LPC1788: uart TX interrupt problem

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]
formatting link
Reply to
pozz
Loading thread data ...

I can't say I follow some of your description, in particular the FIFO and getting an interrupt when the last character is transmitted completely (meaning the shift register is empty and ready for a new character). The FIFO knows nothing of the transmitter shift register status. Perhaps I did not read carefully enough?

Reply to
Rick C

Which variant of LCP1788? Which UART within the MCU?

Looking at the generic datasheet there are up to 5 UARTs. UART1 is described as supporting RS485 together with modem handshake signals DTR/DSR, RTS/CTS etc which surely could be utilised for Tx/Rx direction.

Another feature is that the register layout for the UARTs are 16C550 standard compliant so re-writing a driver from scratch should not be necessary (assuming plenty of freely available source code?).

Reply to
Chris

Il 17/05/2023 00:22, Rick C ha scritto:

I agree with you, the TX interrupt is a mess on these MCUs. This is what is written in the datasheet:

So you were right, FIFO knows nothing of transmitter shift register. The UART of this MCU implements a delay of one full frame (minus stop bit length), that is exactly the time when the shift register finished its job of sending bits (and it's the right time to enable/disable the driver of external transceiver).

Here a point to stress is about the first interrupt. In uart_putchar(), when the TX is idle (THRE interrupt is disabled), the driver not only needs to enable THRE interrupt, it also needs to push the first byte into hw TX FIFO, otherwise no interrupt will be fired. After the delay above, the THRE interrupt is fired. The driver push another byte in the hw TX FIFO and, after the same delay, the second interrupt is fired and so on. When the last byte interrupt is fired, the THRE interrupt is disabled *and* the direction is set back to RX.

Reply to
pozz

LPC1788FBD208

UART2

UART2 can manage autonomously an external transceiver direction, but it is not used in my driver. I think because of dummy bytes that mustn't be seen on the wire.

I don't know 16C550. Do you think the UART in LPC1788 is compatible with

16C550? Could you suggest any source code out there?
Reply to
pozz

This is why I prefer FPGAs to MCUs. I can design a perfectly functioning UART in less time than it will take you to figure this out.

Reply to
Rick C

16C550 is a more modern version of one of the early, very popular UARTs. The 16C550 will have a FIFO instead of buffer register. The tricky part is in the details of interrupts and status signals. I don't know how standardized they are.

Having compatible registers, does not mean compatible operation when doing things like RS-485.

Reply to
Rick C

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.