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)?
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 capability on Atmel and Etrax SoCs, but otherwise implementation of these ioctls in Linux UART/USART drivers is very sparse (because it exists to support a somewhat rare hardware feature).
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 RS-485.