0

I am currently having trouble getting the LPUART interrupt to work properly on the NXP FRDM K82f board with MCUXpresso IDE using MCUXpresso SDK.

The LPUART interrupt only fires 6-8 times and never again after that. This behavior is independent from the current position in the code. I tested it by sending the first batch of data long after the program enters the infinite loop and also by sending it right at the start of the program (after lpuart interrupt initialization). Same behavior in both cases.

This is my initialization routine and the interrupt handler.

#include <stdint.h>
#include "fsl_lpuart.h"

#define OSCERCLK_SOURCE 2U
#define ESP_UART LPUART0
#define UART_IRQ LPUART0_IRQn
#define UART_RECEIVE_INTERRUPT LPUART0_IRQHandler
#define BUFFER_SIZE 2048

volatile uint8_t ringBuffer[BUFFER_SIZE] = {0x00};
volatile uint16_t rxIndex = 0;
volatile uint16_t txIndex = 0;


void init(uint32_t baudRate){
    lpuart_config_t config;
    CLOCK_SetLpuartClock(OSCERCLK_SOURCE);
    LPUART_GetDefaultConfig(&config);
    config.baudRate_Bps = baudRate;
    config.enableRx = true;
    config.enableTx = true;
    uint32_t clockFrequency = CLOCK_GetFreq(kCLOCK_Osc0ErClk);
    LPUART_Init(ESP_UART, &config, clockFrequency);
    LPUART_EnableInterrupts(ESP_UART, kLPUART_RxDataRegFullInterruptEnable);
    EnableIRQ(UART_IRQ);
}

void UART_RECEIVE_INTERRUPT(void){
    uint8_t data;
    uint32_t flags = kLPUART_RxDataRegFullFlag & LPUART_GetStatusFlags(ESP_UART);
    if (flags){
        data = LPUART_ReadByte(ESP_UART);
        if((rxIndex + 1) % BUFFER_SIZE != txIndex){
            ringBuffer[rxIndex] = data;
            rxIndex++;
            rxIndex %= BUFFER_SIZE;
        }
    }
}

Has anybody encountered a similar behavior and is able to help?

EDIT: As @Lundin suggested I corrected the non-standard gcc syntax. No success yet, but I was able to track what flags are set when the ISR doesn't fire anymore. The set flags are:

kLPUART_TxDataRegEmptyFlag, 
kLPUART_IdleLineFlag, 
kLPUART_RxOverrunFlag, 
kLPUART_RxFifoEmptyFlag

This looks ambiguous to me, because RX FIFO is empty and also RX is overrun.

elhe
  • 122
  • 10
  • Your ring buffer needs to be declared as `volatile`. It needs to be protected against race conditions. Your current implementation is broken and will never work properly, see [this](https://stackoverflow.com/a/48517862/584518). That being said, if your interrupts don't trigger at all, it sounds like a hardware flag clearing problem. – Lundin Feb 02 '18 at 14:50
  • Btw is there a reason why you are using non-standard GCC extensions instead of just `uint8_t ringBuffer[BUFFER_SIZE] = {0x00};`? – Lundin Feb 02 '18 at 14:52
  • `kLPUART_RxDataRegFullFlag` is a suspicious mask name. I'd expect a mask that checks for any data, not necessarily just a full condition. – chux - Reinstate Monica Feb 02 '18 at 15:04
  • @Lundin I accidentally copied it wrong from my source, it is declared volatile in my program. I will edit it here. And there is no specific reason why I use non-standard GCC extension. – elhe Feb 02 '18 at 16:18
  • @chux I took this flag from the provided example code for uart interrupt. All other available masks are `kLPUART_RxActiveEdgeInterruptEnable, kLPUART_TxDataRegEmptyInterruptEnable,kLPUART_TransmissionCompleteInterruptEnable, kLPUART_RxDataRegFullInterruptEnable, kLPUART_IdleLineInterruptEnable, kLPUART_RxOverrunInterruptEnable, kLPUART_NoiseErrorInterruptEnable, kLPUART_FramingErrorInterruptEnable, kLPUART_ParityErrorInterruptEnable, kLPUART_TxFifoOverflowInterruptEnable, kLPUART_RxFifoUnderflowInterruptEnable` – elhe Feb 02 '18 at 16:18
  • @chux My bad, can't edit it anymore. The correct masks are: ` kLPUART_TxDataRegEmptyFlag, kLPUART_TransmissionCompleteFlag, kLPUART_RxDataRegFullFlag, kLPUART_IdleLineFlag, kLPUART_RxOverrunFlag, kLPUART_NoiseErrorFlag, kLPUART_FramingErrorFlag, kLPUART_ParityErrorFlag, kLPUART_LinBreakFlag, kLPUART_RxActiveEdgeFlag, kLPUART_RxActiveFlag, kLPUART_DataMatch2Flag, kLPUART_DataMatch1Flag, kLPUART_NoiseErrorInRxDataRegFlag, kLPUART_ParityErrorInRxDataRegFlag, kLPUART_TxFifoEmptyFlag, kLPUART_RxFifoEmptyFlag, kLPUART_TxFifoOverflowFlag, kLPUART_RxFifoUnderflowFlag ` – elhe Feb 02 '18 at 17:01
  • Are you sure that "interrupt doesn't fire any more" is the case and not "new characters cease to arrive in my buffer"? Which is a different thing. – tofro Feb 02 '18 at 17:21
  • @tofro If you mean whether I tested the UART sender with a logic analyzer: Yes I did. It sends 11 bytes every 250 milliseconds. I hope this is what you ask, otherwise let me know. – elhe Feb 02 '18 at 17:27
  • Concerning `{[ 0 ... sizeof(BUFFER_SIZE)-1 ] = 0x00};`, is not `sizeof(BUFFER_SIZE)` something like 8,4, or 2? The size of the integer 2048? Sorry I can not help more. – chux - Reinstate Monica Feb 02 '18 at 17:31
  • That's not what I meant. You ISR could be called for another reason but a received character - Overrun, for example. And as long as you don't clear this error condition, it will stop receiving characters. – tofro Feb 02 '18 at 17:50
  • @tofro I put a breakpoint into the ISR to check whether it gets called and it only gets called when I receive a character. – elhe Feb 02 '18 at 17:55
  • 1
    You should still check whether the UART is in any sort of error condition you have to reset. – tofro Feb 03 '18 at 13:18
  • Ok I have now captured the flags that are set, when the ISR doesn't fire anymore: `kLPUART_TxDataRegEmptyFlag , kLPUART_IdleLineFlag, kLPUART_RxOverrunFlag, kLPUART_RxFifoEmptyFlag, ` Which to me looks ambidous, because how can RX be overrun when at the same time the RX FIFO is empty. – elhe Feb 03 '18 at 19:56

2 Answers2

2

One of the first things you need to figure out is if you are using the FIFO, or simply dealing with the single-character receive data register. If you are not using the FIFO, then the FIFO flag is irrelevant, and it is unsurprising that it shows empty.

As to the overrun flag, the programmer's manual is unsurprisingly explanatory:

Receiver Overrun Flag

OR is set when software fails to prevent the receive data register from overflowing with data. The OR bit is set immediately after the stop bit has been completely received for the dataword that overflows the buffer and all the other error flags (FE, NF, and PF) are prevented from setting. The data in the shift register is lost, but the data already in the LPUART data registers is not affected....

This suggests the distinct possibility that at some point in the past you failed to claim data before it would have been overwritten, setting the overrun flag. But if you also had a delayed response to the original receive data interrupt, you could have read the original data without awareness of the problem. However, if you did so, and left the overrun flag on, that would be the last data you ever received.

It seems like there are at least three things you need to do:

  1. Fully implement or fully ignore the optional FIFO mode

  2. Check the overrun flag, clear it to see if it makes a difference, but also find a way to indicate (set a sticky volatile software flag, toggle a GPIO watched by a scope in one time trigger mode, whatever) to indicate that a problem has occurred, so that you can take steps to investigate that.

  3. Analyze and test your program overall for design faults which might yield to failure to respond to serial data quickly enough. This could include things like toggling a GPIO in the ISR and watching that and the serial data line on a scope, putting timer checks into the code, and auditing all other ISRs and any foreground options which must disable interrupts. Also try stripping your program down until you simply have something that receives and echos characters and experiment with that, both hitting keys one at a time in a terminal program, and having a program inject strings at a rate which keeps the serial line 100% busy.

Also keep in mind that while break point debugging can be very powerful, with any program that must respond to external events, it is quite likely that operation will no longer be normal after the first time the breakpoint is hit, so to approach a problem in that way you often need to design tests which end at the point where the breakpoint is hit and you analyze only the state collected up to that point. Sometimes this means you need to an an "if" condition so that you can put a breakpoint inside of it.

Community
  • 1
  • 1
Chris Stratton
  • 39,853
  • 6
  • 84
  • 117
  • I fully ignored the FIFO mode (clearing RX FIFO ENABLE bit) and used a logic analyzer to analyze when the overrun happens. And you were correct, it was a self made problem, the breakpoint debugging caused the overrun and after clearing it everything was fine. I also stripped down the code and built it back up to incrementally in order to verify the correct function of the program. So all in all you helped me a lot, this answer fixed my problem (and I also learned something). – elhe Feb 07 '18 at 22:41
  • Sounds like you had a long but satisfying debug session! Glad you hear you figured out and solved the root problem. – Chris Stratton Feb 08 '18 at 02:06
0

The problem is most probably about Receiver Overrun flag. As it is stated in reference manual of the processor, after overrun flag has set, no more new data has come to receiver buffer/FIFO even if sufficient room exists.

Don't forget that even if you don't call the LPUART_ReadByte() function, the characters entered to your UART port has saved into UART buffer and after you call ReadByte it first reads the previous entered characters. If you overcommit to UART port and fill its buffer, it sets the Overrun flag and refuses to take more input characters from that port.

The thing you should do is, clearing the Overrun flag after it is anyhow set. It would be better if you check overrun flag and if it is set, clear it by yourself manually.

if(kLPUART_RxOverrunFlag & LPUART_GetStatusFlags(ESP_UART))
{LPUART_ClearStatusFlags(ESP_UART,kLPUART_RxOverrunFlag);}

You can check for this flag and clear it every before you want to read a data from your UART.

Onur
  • 43
  • 8