pozz
2023-05-16 21:47:58 UTC
This issue is very difficult to understand when you see it, so it's
quite impossible to find the solution here in a newsgroup. However,
after scratching my head for long days, I want to try. Maybe some guy
here could enlighten me on the right direction.
I have a critical issue with a board based on LPC1788, specifically with
UART peripheral. The issue is very annoying because it brings to a non
funcional device and moreover because it happens rarely, maybe after
many months. So it's very difficult to see it in lab and try some debug.
This device works usually without supply interruption (it is powered by
mains and a lead-acid battery in case of black-out).
The interesting parts of the UART driver (written by others) is here[1].
It's an interrupt-based driver with two circular buffers: one for RX and
one for TX (the uart can be configured to use callbacks, but I use
circular buffers). When the application wants to write, data is pushed
in a TX FIFO in RAM and TX interrupt is enabled (sending the first byte,
see below). In the TX interrupt the next data is popped up from the TX
FIFO and written to the TX register of UART peripheral.
There are some complications.
The driver adds a few dummy bytes before real transmission. TX and RX
signals are connected to an external RS485 transceiver. The direction is
connected to a GPIO.
If the TX is in idle and the application writes the first byte to
transmit (uart_putchar), some dummy bytes are sent first and the real
data pushed in TX FIFO in RAM. After the last dummy byte is transmitted,
the direction toggles to enable transmission over RS485 bus. In this way
the author implemented a small delay that is useful when the receiver
device on the bus is slow changing its direction from TX to RX.
Another point is the use of TX interrupt. This MCU has an hw TX FIFO
that is enabled, but *not* used. Indeed, only a byte at a time is pushed
in hw FIFO. In this case, the TX interrupt is triggered only when the
data is shifted out completely. This way, we can enable RS485
transmitter after the last dummy byte at the exact timing
(txstart_callback). After the last byte was transmitted, the UART TX
interrupt is disabled *and* the RS485 transitter is disabled
(txend_callback).
In many MCUs, if the TX FIFO (or single register) is empty and the TX
interrupt is enabled, the interrupt is triggered immediately. This isn't
the case in this MCU, where you need to send at least the first byte to
trigger the interrupt (after it's shifted out).
When the issue happens (as I said, even after many months), the RS485
direction is stuck high (transmission enabled), but I didn't see any
activity on TX UART signal. This should mean that the TX interrupt is
disabled.
Consequently, the device can't receive nothing fro the bus (the
transceiver is half-duplex). Other functions of the device are ok even
in this situation. For example, I can see the status LED blinking as usual.
The uart data structure is configured in the following way:
* rx_callback=NULL
* tx_callback=NULL
* txstart_callback: set the GPIO of RS485 direction high
* txend_callback: set the GPIO of RS485 direction low
* txdummy_num=1
* txdummy_value=0xFF (it isn't important here)
I can't understand how the MCU reaches this incorrect state (RS485
transmitter always enabled). With two consecutive instructions the
driver disables the TX interrupt after last byte was transmitted
completely and the direction is set low.
I know I could refactor the UART driver, but at the moment I'd like to
fix this issue without rewriting so low-level part of the firmware.
Any idea?
[1] https://justpaste.it/8swct
quite impossible to find the solution here in a newsgroup. However,
after scratching my head for long days, I want to try. Maybe some guy
here could enlighten me on the right direction.
I have a critical issue with a board based on LPC1788, specifically with
UART peripheral. The issue is very annoying because it brings to a non
funcional device and moreover because it happens rarely, maybe after
many months. So it's very difficult to see it in lab and try some debug.
This device works usually without supply interruption (it is powered by
mains and a lead-acid battery in case of black-out).
The interesting parts of the UART driver (written by others) is here[1].
It's an interrupt-based driver with two circular buffers: one for RX and
one for TX (the uart can be configured to use callbacks, but I use
circular buffers). When the application wants to write, data is pushed
in a TX FIFO in RAM and TX interrupt is enabled (sending the first byte,
see below). In the TX interrupt the next data is popped up from the TX
FIFO and written to the TX register of UART peripheral.
There are some complications.
The driver adds a few dummy bytes before real transmission. TX and RX
signals are connected to an external RS485 transceiver. The direction is
connected to a GPIO.
If the TX is in idle and the application writes the first byte to
transmit (uart_putchar), some dummy bytes are sent first and the real
data pushed in TX FIFO in RAM. After the last dummy byte is transmitted,
the direction toggles to enable transmission over RS485 bus. In this way
the author implemented a small delay that is useful when the receiver
device on the bus is slow changing its direction from TX to RX.
Another point is the use of TX interrupt. This MCU has an hw TX FIFO
that is enabled, but *not* used. Indeed, only a byte at a time is pushed
in hw FIFO. In this case, the TX interrupt is triggered only when the
data is shifted out completely. This way, we can enable RS485
transmitter after the last dummy byte at the exact timing
(txstart_callback). After the last byte was transmitted, the UART TX
interrupt is disabled *and* the RS485 transitter is disabled
(txend_callback).
In many MCUs, if the TX FIFO (or single register) is empty and the TX
interrupt is enabled, the interrupt is triggered immediately. This isn't
the case in this MCU, where you need to send at least the first byte to
trigger the interrupt (after it's shifted out).
When the issue happens (as I said, even after many months), the RS485
direction is stuck high (transmission enabled), but I didn't see any
activity on TX UART signal. This should mean that the TX interrupt is
disabled.
Consequently, the device can't receive nothing fro the bus (the
transceiver is half-duplex). Other functions of the device are ok even
in this situation. For example, I can see the status LED blinking as usual.
The uart data structure is configured in the following way:
* rx_callback=NULL
* tx_callback=NULL
* txstart_callback: set the GPIO of RS485 direction high
* txend_callback: set the GPIO of RS485 direction low
* txdummy_num=1
* txdummy_value=0xFF (it isn't important here)
I can't understand how the MCU reaches this incorrect state (RS485
transmitter always enabled). With two consecutive instructions the
driver disables the TX interrupt after last byte was transmitted
completely and the direction is set low.
I know I could refactor the UART driver, but at the moment I'd like to
fix this issue without rewriting so low-level part of the firmware.
Any idea?
[1] https://justpaste.it/8swct