[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RS485 ninth bit works



I appreciate the help with the RS485 code, but I have
a fairly unique problem. I need to use 9th bit messages,
where the very first byte is the address being sent to
the slave, and has the 9th bit set high. All subsequent
bytes in the message have the 9th bit set low. Many of
the older embedded controllers had all this built in,
but the newer ones don't. Because of this, I couldn't
use the built-in code. What follows is from serial.c,
the serial driver.

static int
e100_write_rs485(struct tty_struct *tty, int from_user,
                 const unsigned char *buf, int count) {
      struct e100_serial * info = (struct e100_serial *)tty->driver_data;

#ifdef CONFIG_ERS_STANDARDS
    static uint ii;
      unsigned long flags;

    // OK, we're doing 485
    // Set for ninth bit high, addr byte only
    info->tx_ctrl |= 0x0E;
    info->port[REG_TR_CTRL] = info->tx_ctrl;

    // Turn on (low) RTS
    info->rx_ctrl |= 0xAE;
    info->port[REG_REC_CTRL] = info->rx_ctrl;

    // Send first byte
    for (ii = 0; ii < count; ii++) {

      // wait on transmit shift register empty
      do { } while (!(info->port[REG_STATUS] & 0x20));

      info->port[REG_DATA] = buf[ii];

      if (ii == (count - 2)) {
        save_flags(flags);
        cli();
        }
      if (!ii) {
        udelay(2000);
        // Now set ninth bit low for data bytes
        info->tx_ctrl &= 0xFA;
        info->port[REG_TR_CTRL] = info->tx_ctrl;
      }
    }
    udelay(2500);

    // Finally, kill RTS
    info->rx_ctrl &= 0xDF;
    info->port[REG_REC_CTRL] = info->rx_ctrl;
    restore_flags(flags);

    // If not 485, just send it
#else
      int old_enabled = info->rs485.enabled;
      /* rs485 is always implicitly enabled if we're using the ioctl()
       * but it doesn't have to be set in the rs485_control
       * (to be backward compatible with old apps)
       * So we store, set and restore it.
       */
      info->rs485.enabled = 1;
      /* rs_write now deals with RS485 if enabled */
      count = rs_write(tty, from_user, buf, count);
      info->rs485.enabled = old_enabled;
#endif
      return count;
}

A couple of notes:

1. The stick parity and parity odd are set before hitting
the data.

2. The delay built-in after the first byte is to be sure the
character is fully sent before switching the parity back. It
appears the parity flag is only used as the last data bit
shifts out of the shift register, so it's impossible to catch
it between two characters. That's ok, tho since this is async,
anyway.

3. The timing after the last byte is tight, also. Have about
two milliseconds after the last byte sent to get the line
turned around. Want to kill the interrupts two characters
prior to the end, so it won't hold up the base, but so no
unwanted int can draw out our time sequence. This is now
working with my analog input boards fine.

4. The real problem is that my axis was just set up for RS422,
not for RS485, and I had to add a jumper.

Thanks

Frank Brown
Phoenix Consulting
for Emerson Climate Technology
fcb3@xxxxxxx.net