High speed Hardware RS485 Support

Has anybody used the half duplex rs485 hardware on the jetson NX? I’m working on a high speed motor control project, our digital servos operate in the 2-4MHz region and require hardware control of the RTS pin. We require hardware turnaround because the servo response time is in the order of 0.5mS after it receives the position command. I’m measuring 4-12mS turnaround time

I have the ‘device tree’ working by adjusting the cpu registers using devmem2.

Original proof of concept rts functionality testing done using
stty -F /dev/ttyTHS0 2000000 crtscts
echo “RTS function test” >> /dev/ttyTHS0

This yielded a ~4-12mS jittered delay on return to listening on the uart RTS line. I have oscilloscope traces I can share next week.

A colleague read the source code for the stty program and found it is setting the c_cflag property with the value CRTSCTS.

I also found documentation from kernel.org describing the usage of TIOCSRS485 https://www.kernel.org/doc/Documentation/serial/serial-rs485.txt

Does the Jetson NX support half duplex RS485 natively with automatic control of the RTS pin using the TIOCSRS485 portions of ioctl?

I have compiled the portion of source code mentioned on kernel.org show below and from memory I got errno 25 “ENOTTY 25 Inappropriate ioctl for device” when trying to run the TIOCSRS485 command with ioctl to set the rs485 settings. Is is TIOCRS485 mode supported. Its a pretty big limitation for my application and can cause heartache such as needing a second processor or micro controller and to design a interface between 2 processors

struct serial_rs485 rs485conf;

/* Enable RS485 mode: */
rs485conf.flags |= SER_RS485_ENABLED;

/* Set logical level for RTS pin equal to 1 when sending: */
rs485conf.flags |= SER_RS485_RTS_ON_SEND;
/* or, set logical level for RTS pin equal to 0 when sending: */
rs485conf.flags &= ~(SER_RS485_RTS_ON_SEND);

/* Set logical level for RTS pin equal to 1 after sending: */
rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
/* or, set logical level for RTS pin equal to 0 after sending: */
rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND);

/* Set rts delay before send, if needed: */
rs485conf.delay_rts_before_send = ...;

/* Set rts delay after send, if needed: */
rs485conf.delay_rts_after_send = ...;

/* Set this flag if you want to receive data even while sending data */
rs485conf.flags |= SER_RS485_RX_DURING_TX;

if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) {
	/* Error handling. See errno. */
}

Driver source code: (unless the NX ships with a different version)

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/tty/serial/serial-tegra.c

I don’t see any clue of RS485 driver enable support in there.

Have you considered using a USB RS485 adaptor?

Thanks for linking me to the relevant driver source code that is really helpful. I think I need to compare that driver against another platform where the TIOCSRS485 ioctl’s are supported.
I can say for a fact the RTS pin is supported as it automatically switches when using stty with the crtscts bit set.

I’ve tested ftdi usb->rs485 adapters, they work, they even worked out of the box with the servo’s example driver code, but they had the same slow bus turn around time and I couldn’t issue commands at a high enough speed to make the system viable. In any case I’m not comfortable using USB in embedded systems. Its my personal opinion USB is a poor choice for industrial interface and I’ve seen it fail time and time again.

Using RTS as an RS485 driver enable is not the normal use of an RTS pin, so the fact that an RTS pin is present and you can turn it on and off doesn’t mean that it is useful.

Am I right in understanding that you need a turnaround (i.e. transmit driver turn off) time of 0.5 ms, at 2 Mbaud? That is 100 bytes times. I would say that should be easy for hardware to do, so if your USB adaptors didn’t achieve that, they probably weren’t doing it in hardware. I’ve had a variety of USB RS485 adaptors and only a few are not bodges.

I mostly agree with your comments about the suitability of USB, except that the alternatives may also have their own problems.

If your baud rate is fixed, the best solution may be to build a circuit with a monostable that turns the driver off after a period of inactivity on the data line.

Hi @nvidiadev1,

A colleague has run with the monostable circuit idea you described and build a test board. We now have the half duplex RS485 working at 2MHz with the jetson and our target hardware.

Thanks for your suggestion!

You already have a solution, but thought I’d throw something in which may be of use in the future for yourself or someone else…

The serial UARTs are actually capable of being used with an older driver (standard, available even in early boot stages), or the DMA-capable version. The device “/dev/ttyS0” is the same hardware as that behind “/dev/ttyTHS0”. The “S0” version is the older driver, the “THS0” is the newer driver. This latter version includes DMA transfer.

Within the device tree each serial UART has a list of drivers which are allowed, the “compatible” entry. This is from a TX2’s “compatible”, but you’ll see a similar entry on every Jetson:
compatible=nvidia,tegra20-uartnvidia,tegra186-hsuart
…and you can remove drivers you are not interested in through the device tree, e.g., the “ttyTHS” entry is the “hsuart” (“high speed UART”, a reference to DMA capability).

I do not know the differences between the IOCTLs of these, but it is possible that one driver will support different IOCTLs than the other. You would not want to use two drivers simultaneously, e.g., you would need to avoid simultaneous use of ttyS0 and ttyTHS0, but if you test an IOCTL against either on separate clean boots, then it is conceivable that you would find one allows IOCTLs not allowed by the other. You could remove one of the device special files by removing the “compatible” entry for that driver.