RT685 USART Driver

キャンセル
次の結果を表示 
表示  限定  | 次の代わりに検索 
もしかして: 

RT685 USART Driver

1,881件の閲覧回数
jwprice100
Contributor II

I'm using a RT685 with the FreeRTOS USART driver provided in the SDK. The driver seems to work most of the time. However I end up somehow getting into a weird state after an indeterminate period of time.  It seems the base USART driver's IRQ Handler is called repeatedly over and over again. Digging into the reason it seems the tx level FIFO interrupt is firing. However there is no data to send. 

Digging into the code I find:

 /* Send data */
        if (sendEnabled && ((base->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK) != 0U))
        {
            base->FIFOWR = *handle->txData;
            handle->txDataSize--;
            handle->txData++;
            sendEnabled = handle->txDataSize != 0U;
            if (!sendEnabled)
            {
                base->FIFOINTENCLR = USART_FIFOINTENCLR_TXLVL_MASK;

                base->INTENSET = USART_INTENSET_TXIDLEEN_MASK;
            }
        }

When tx data runs out it clearly disabled that interrupt. Yet I find from a breakpoint that txDataSize is zero! So it's like it ran out of data without executing the code path that would disable tx lvl interrupts.

To confirm I added the following code:

    if (!sendEnabled && (base->FIFOINTENSET & USART_FIFOINTENCLR_TXLVL_MASK) )
    {
        base->FIFOINTENCLR = USART_FIFOINTENCLR_TXLVL_MASK;
        base->INTENSET = USART_INTENSET_TXIDLEEN_MASK;
    }

This code does indeed fire. This seems impossible! Can anyone shed any light?

Thanks!

0 件の賞賛
返信
7 返答(返信)

1,815件の閲覧回数
jwprice100
Contributor II

The issue does appear to be that when two writes to the FIFOINTENCLR register that are spaced closely in time, the second write may not work. I've confirmed via debugging that the register was written but did not clear. My solution is as follows:

In the USART_TransferHandleIRQ function replace:

base->FIFOINTENCLR = USART_FIFOINTENCLR_RXLVL_MASK | USART_FIFOINTENSET_RXERR_MASK;

with 

while((base->FIFOINTENSET & USART_FIFOINTENCLR_TXLVL_MASK) || (base->FIFOINTENSET & USART_FIFOINTENCLR_TXLVL_MASK))
{
  base->FIFOINTENCLR = USART_FIFOINTENCLR_RXLVL_MASK | USART_FIFOINTENSET_RXERR_MASK;
 }

 

and replace 

base->FIFOINTENCLR = USART_FIFOINTENCLR_TXLVL_MASK;

 

with

//This is necesary in case the previous loop iteration just wrote it or the receive logic just wrote it.
while(base->FIFOINTENSET & USART_FIFOINTENCLR_TXLVL_MASK)
{
  base->FIFOINTENCLR = USART_FIFOINTENCLR_TXLVL_MASK;
}

 

Hopefully this helps anyone else encountering this issue.

0 件の賞賛
返信

1,786件の閲覧回数
jwprice100
Contributor II

This wasn't the source of the problem afterall. I just got really lucky and ran for many hours without getting stuck in the ISR. I think I know the real problem/solution, I'll report that if I am right.

0 件の賞賛
返信

1,724件の閲覧回数
gusarambula
NXP TechSupport
NXP TechSupport

Hello @jwprice100 ,

Did you find the root cause of this problem or are you still requiring support on this matter?

If you did find the root cause, we would appreciate it if you would please share it so that it may benefit other community members.

Regards,
Gustavo

0 件の賞賛
返信

1,468件の閲覧回数
jwprice100
Contributor II

I believe I identified the root cause of this matter. It's a fairly significant bug in the UART driver but it would only manifest if transmitting and receiving at the same time. I suspect most applications are not doing this which is why it may have remained hidden.

To understand the issue, the interrupt handler for the fsl_uart code has a snippet as follows:

 

 

        if (sendEnabled && ((base->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK) != 0U))
        {
            base->FIFOWR = *handle->txData;
            handle->txDataSize--;
            handle->txData++;
            sendEnabled = handle->txDataSize != 0U;
            if (!sendEnabled)
            {
                base->FIFOINTENCLR = USART_FIFOINTENCLR_TXLVL_MASK;

                base->INTENSET = USART_INTENSET_TXIDLEEN_MASK;
            }
        }

 

 

The important thing to note is that it disables the tx interrupt sources in the interrupt handler itself. 

In the USART_TransferSendNonBlocking function there are two lines:

 

 

uint32_t interruptMask = USART_GetEnabledInterrupts(base);
USART_DisableInterrupts(base, interruptMask);

 

 

 Followed later by:

 

 

USART_EnableInterrupts(base, interruptMask | (uint32_t)kUSART_TxLevelInterruptEnable);

 

 

If the interrupt happens to fire in between those first tw0o lines, and happened to clear the Tx interrupts in that call, the tx interrupts would then be re-enabled.

As one of those tx interrupt sources are the Tx FIFO level threshold the interrupt fires immediately. There's no code path to disable the tx interrupts in this case (they are only cleared if there was something to send and then all the bytes were drained), so the interrupt exists and fires again immediately. This hangs the processor.

Hopefully that all made sense, let me know if there are any questions about it. It's a very nasty bug so it should definitely get fixed. 

 

1,457件の閲覧回数
gregburlingame
Contributor I

Thank you @jwprice100 for sharing your findings!  

タグ(1)
0 件の賞賛
返信

1,484件の閲覧回数
gregburlingame
Contributor I

I am also interested to hear how @jwprice100 made out.  Thanks in advance for sharing your results! 

0 件の賞賛
返信

1,836件の閲覧回数
jwprice100
Contributor II

Does anyone know if writing to the FIFOINTENCLR register twice, very close in time together, would cause the second write to not take? That appears to be what's happening. 

0 件の賞賛
返信