-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RTU over RS485 timing issues, need to poll UART LSR for TEMT flag #668
Comments
why manually handle RTS down after Tx queue sent empty? |
It really depends on the hardware. My setup was with an old and dumb RS232/RS485 converter, RTS required to switch direction - connected to an ISA serial port card in a Pentium Pro 200MHz machine, back then there was no USB yet. Some better converters switch direction automatically, based on TXD line. Other solutions are possible, some good USB UARTs have a pin to control the RS485 driver automagically. |
Hmmm, The i8250, i16550 and i16650 and NS16550 do in fact have TXRDY interrupt. The typical failure is to assume the interrupt comes after last byte left the Transmitter Shift Register (TSR). But this is wrong, if documented somewhere. The IRQ occurs after the last Byte was put from Transmitter Hold Register (THR) into TSR, or from the Tx-FiFo into TSR. It's often said the wide spread TI Quad-16550 Derivate has problem with this IRQ, but on e2e ti.com you'll find details how to implement it right. The main pain with some (or all?) multi-channel TI UARTs is the common IRQ for any of the channels, so the typical legacy code or setup expecting IRQ4=ttyS0, IRQ3=ttyS1 does not work with. Win32 flow control flags can be set to toggle RTS on TXRD. And I am damn sure, this toggling always happens after TSR did it. Sure, because we used it with a modulator which had /CE connected to RTS and never lost even the stop bit of the last Tx byte (or to /RTS, don't remember). At 200 MHz single core x86. Linux also supports this IRQ, a helpful discussion may be found here stackoverflow questions interrupts-in-uart-16550-and-linux-kernel. A former team mate of me (rest in peace, CSCH!) implemented such sender even on pre-3.xx Linux kernel, without fiddling with the IRQs, just configured it via setserial, IIRC. Yes, you're right, some UARTS don't work well with this. For instance recent Xilinx Zync as well as mid 90s super-cheap port extender cards. Who cares the >15 years old hardware? So, finally, libmodbus could get problematic on exotic hardware with Linux on Microcontrollers. |
"TX ready" is when more data can be written to the UART (there is enough free space in the FIFO) and that works fine of course. |
To work well with some dumb hardware, it is necessary to poll the "Tx empty" flag of the UART. Unfortunately, the typical 16550 UART doesn't support interrupts on this event, and serial port write() call can return early when data is still in the queue to be sent, and even tcdrain() alone doesn't guarantee it is sent completely by the hardware. In my own code (written ~20 years ago, now trying to port to libmodbus mainly to get TCP support) I use the code like below, after sending the frame with write() and before disabling RTS, and I'd suggest to do something similar in libmodbus (if supported by the OS - that ioctl could be Linux-specific). Switching RS485 direction is really timing critical, you either lose the end of request of the start of response if you miss the correct timing. BTW, one rare example of UART where they got it right is part of Atmel (now Microchip) AVR 8-bit MCUs, they implement a separate interrupt for "Tx empty" event in addition to the usual "Tx ready".
The text was updated successfully, but these errors were encountered: