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]