clinuxserial-portrs485

automatically changing RTS for RS-485 communication


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


Solution

  • 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.