我正在尝试在我的程序中设置半双工通信。我的 RS485 收发器使用 RTS 标志 (TIOCM_RTS) 在发送和接收之间来回切换。要发送/接收数据,我需要手动更改 RTS 标志:
将 RTS 设置为高。
发送数据。
将RTS设置为低。
int setRTS(int level) {
int status;
ioctl(ser_port, TIOCMGET, &status);
if(level) {
status |= TIOCM_RTS;
} else {
status &= ~TIOCM_RTS;
}
ioctl(ser_port, TIOCMSET, &status);
return 1;
}
我的问题是:linux内核不应该可以自动切换RTS吗? 以及如何确保在调用 setRTS(0) 之前发送了数据?
linux内核不是应该可以自动切换RTS吗?
是的,从 Linux 3.0 开始就有内核框架。
include/uapi/asm-generic/ioctls.h中有两个ioctl:
#define TIOCGRS485 0x542E
#define TIOCSRS485 0x542F
在 RS-485 模式下检索和配置 tty 串行端口驱动程序。
这些 ioctl 使用
struct serial_rs485
:
/*
* Serial interface for controlling RS485 settings on chips with suitable
* support. Set with TIOCSRS485 and get with TIOCGRS485 if supported by your
* platform. The set function returns the new state, with any unsupported bits
* reverted appropriately.
*/
struct serial_rs485 {
__u32 flags; /* RS485 feature flags */
#define SER_RS485_ENABLED (1 << 0) /* If enabled */
#define SER_RS485_RTS_ON_SEND (1 << 1) /* Logical level for
RTS pin when
sending */
#define SER_RS485_RTS_AFTER_SEND (1 << 2) /* Logical level for
RTS pin after sent*/
#define SER_RS485_RX_DURING_TX (1 << 4)
__u32 delay_rts_before_send; /* Delay before send (milliseconds) */
__u32 delay_rts_after_send; /* Delay after send (milliseconds) */
__u32 padding[5]; /* Memory is cheap, new structs
are a royal PITA .. */
};
我已经在 Atmel 和 Etrax SoC 上使用了这种 RS-485 功能,但是在 Linux UART/USART 驱动程序中这些 ioctl 的实现非常稀少(因为它的存在是为了支持某种罕见的硬件功能)。
如果您的驱动程序没有,请考虑自己实现。您可以使用 drivers/tty/serial/atmel_serial.c 中的实现作为指南。
另请阅读 RS-485 的 Linux 内核文档。
这确实很棘手 - 要主动执行此操作,您需要知道最后一个字节何时清除 UART 引擎,或者至少知道它何时进入(缓冲区变空时)并添加根据波特率和字计算的延迟长度。这确实值得在串行驱动程序本身中实现,所有这些都是可见的。
但是,此问题最常在共享总线上遇到,您还可以接收传输的所有内容。如果是这种情况,您可以使用 receiving 您自己的传输结束(假设您及时发现)作为禁用驱动程序的触发器。