问题
I'm trying to setup half duplex communication in my program. I have my RS485 transceiver using the RTS flag (TIOCM_RTS) to toggle back and forth between transmit and receive. And to send/receive data I need to change RTS flag manually:
Set RTS to High.
Send data.
Set RTS to low.
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; }
My question is: shouldn't the linux kernel be able to switch RTS automatically? And how to ensure that data was sent before calling setRTS(0)?
回答1:
shouldn't the linux kernel be able to switch RTS automatically?
Yes, there is kernel framework for this starting in Linux 3.0.
There are two ioctls in include/uapi/asm-generic/ioctls.h:
#define TIOCGRS485 0x542E
#define TIOCSRS485 0x542F
to retrieve and configure a tty serial port driver in RS-485 mode.
These ioctls use the 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 .. */
};
I've used this RS-485 capabilty on Atmel and Etrax SoCs, but otherwise implementation of these ioctls in Linux UART/USART drivers is very sparse.
If your driver doesn't have it, then consider implementing it yourself. You could use the implementation in drivers/tty/serial/atmel_serial.c as a guide. Also read the Linux kernel document for RS485.
回答2:
This can indeed be tricky - to do it pro-actively you need to know when the last byte cleared the UART engine, or at least when it entered (when the buffer went empty) and add a delay calculated from the baud rate and word length. This is indeed something it can be worth implementing in the serial driver itself, where all of that is visible.
However, this problem is most commonly encountered on a shared bus where you also receive everything you transmit. If that is the case, you can use receiving the end of your own transmission (assuming you discover that promptly) as the trigger to disable the driver.
来源:https://stackoverflow.com/questions/25250731/automatically-changing-rts-for-rs-485-communication