automatically changing RTS for RS-485 communication
Asked Answered
L

2

2

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:

  1. Set RTS to High.

  2. Send data.

  3. 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)?

Lie answered 11/8, 2014 at 19:17 Comment(0)
R
5

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.

Refrigerant answered 11/8, 2014 at 21:50 Comment(5)
Do you understand why do I need both delay_rts_before_send and delay_rts_after_send? The only reason I can see, is to always enable the transmitter as for RS422.Ketron
The RTS control line is typically used to enable/disable the RS485 transmitter/line-driver. Normally a short delay is added after the the send to ensure that all bits get on the line before the transmitter is turned off.Refrigerant
I understand this. But why do I need both delay_rts_before_send and delay_rts_after_send? OMAP serial driver checks only delay_rts_before_send and inverts the value for the second phase automatically. Having both delay_rts_before_send and delay_rts_after_send gives me an opportunity to set both phases to the same level, so no change occurs, i.e. transmitter is either enabled all the time or disabled.Ketron
"But why do I need both..." -- You seem to be complaining about too much capability/flexibility in an interface. If you decide you don't need a delay in your situation, then simply assign a zero when configuring. See lxr.free-electrons.com/source/Documentation/serial/… The delays are to be used "if needed".Refrigerant
Or I see now, that I described the flags, but copied variables ;-) I was actually complaining about logical levels SER_RS485_RTS_ON_SEND and SER_RS485_RTS_AFTER_SEND. For RS485 half-duplex they are actually mutually exclusive. That's why I asked, why both. So the only purpose I see is RS422, where you always want to have the transmitter enabled and you don't want to set RTS flag manually, but via serial_rs485 facility.Ketron
H
1

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.

Hibernicism answered 11/8, 2014 at 19:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.