Search code examples
carmembeddedinterruptuart

FRDM K82f uart interrupt only fires about 8 times


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.


Solution

  • 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.