LPC1788: a serious problem with UART TX interrupts

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC1788: a serious problem with UART TX interrupts

547 Views
giusloq
Contributor III

I wrote a custom UART interrupt-driven driver for LPC1788 MCU. It works... most of the time.

In the field, many customers complained about a lock in the product that happen at random times. This event is very very rare, it could be one time in one year or in a couple of weeks.

At the moment, I'm quite sure the issue is related to the UART management, but I couldn't understand why. The UART is connected to an external RS485 half-duplex transceiver. The direction control is driven by a GPIO (not internal UART RS485 direction control feature).

When the issue happens, it seems the external RS485 transceiver direction is fixed to transmission. At this moment, the MCU will not be able to receive new data, because the receiver of the transceiver is disabled.

The user is forced to power off and on again the device to let it work again.

Only one time I was able to see this issue in my lab and I noticed a few strange things:

  • THREIE is normally enabled (THRE interrupt is enabled)
  • THRE is set (THR is empty)
  • IIR=0xC1 (no pending interrupt)

Why could this happen? THRE is empy and its interrupt is enabled, why the THRE interrupt isn't triggered?

This is a skectup of my driver:

 

void UARTx_IRQ_Tx(LPC_USART_T *hw)
{
  int new_byte = next_byte();
  if (new_byte >= 0) {
    Chip_UART_SendByte(hw, (unsigned char)new_byte);
  } else {
    if (uart->txend_callback)
      uart->txend_callback(uart);
    Chip_UART_IntDisable(uart->hw, UART_IER_THREINT);
  }
}

static void
UARTx_IRQHandler(LPC_USART_T *hw)
{
  uint32_t iir = hw->IIR;

  if ((iir & UART_IIR_INTID_MASK) == UART_IIR_INTID_RDA) {
    UARTx_IRQ_Rx(hw);

  } else if ((iir & UART_IIR_INTID_MASK) == UART_IIR_INTID_THRE) {
    UARTx_IRQ_Tx(hw);
  }
}

void 
uart_putchar(MYUART *uart, unsigned char c)
{
  uint32_t ier = Chip_UART_GetIntsEnabled(uart->hw);
  size_t i = ROLLOVER(uart->tx_in, uart->tx_size);

  /* Wait for ring-buffer availability */
  while(i == uart->tx_out)
    ;

  if ((ier & UART_IER_THREINT) == 0) {
    if (uart->txstart_callback)
      uart->txstart_callback(uart);
    Chip_UART_SendByte(uart->hw, c);
    Chip_UART_IntEnable(uart->hw, UART_IER_THREINT);
  } else {										 
    uart->tx_buff[uart->tx_in] = c;  /* Add new byte in ring-buffer */
    uart->tx_in = i;
    if ((Chip_UART_GetIntsEnabled(uart->hw) & UART_IER_THREINT) == 0) {
      Chip_UART_SendByte(uart->hw, next_byte(uart));
    }
    Chip_UART_IntEnable(uart->hw, UART_IER_THREINT);
  }
}

 

txstart_callback() and tx_end_callback() are functions that set and reset the GPIO output level connected to the direction of external transceiver.

 

 

0 Kudos
Reply
0 Replies