Electronic – STM32: UART Interrupt is triggering without any flags getting set

interruptsmicrocontrollerstm32stm32f10xuart

I am using the UART4 module of an STM32F105. I'm using the RXNE ("RX Buffer Not Empty") interrupt to grab data as it comes in. It's working as expected.

When the RXNE interrupt is enabled it also enables the Overrun interrupt (ORE). This also appears to work as expected.

The ORE status flag is mapped as USART_FLAG_ORE. The corresponding interrupt flag is USART_IT_ORE. If the RXNE interrupt is enabled, when the Flag bit gets set it should cause the IT bit to also get set. Copied from the User Manual:

ore

I'm handling the interrupt like so:

void usartISR(void)
{
    // Did we receive data?
    if(USART_GetITStatus(UART4, USART_IT_RXNE) == SET)
    {
        // Add it to the active buffer
    }

    // Did the receiver overrun?
    else if(USART_GetITStatus(UART4, USART_IT_ORE) == SET)
    {
        // Clean up and clear the Overrun condition
    }

    // No other triggers have been enabled
    else
    {
        ErrorHandler(ERR_BAD_INTERRUPT);
    }
}

The problem happens if the ORE flag is already set when I enable the interrupt:

{
    // The USART_FLAG_ORE bit is already set

    // The following command causes an immediate vector to the ISR:
    USART_IT_Config(UART4, USART_IT_RXNE, ENABLE);
}

The code vectors to the ISR, but the USART_IT_ORE flag doesn't seem to get set. The ISR merrily skips to the end and calls the error handler. If I look into the USART_CR registers, none of the other interrupt events are enabled. If I bypass the error handler, the code repeatedly vectors to the ISR even though no IT flags are set.

Why is the ISR getting triggered without the USART_IT_ORE flag getting set? Am I missing something obvious?

Best Answer

Having gone through the definitions in stm32f10x_usart.h and Get_ITStatus() in the source, its evident that there are no actual UART 'interrupt flags'. There are only status flags in the status register. As you know, when an event occurs, the corresponding flag bit is set, whether interrupts are enabled or not. If the associated interrupt is enabled, then the MCU jumps to the relevant ISR, where the event may be handled.

UART interrupts

The image above shows that if the RXNE interrupt is enabled and the ORE flag is set, an interrupt is triggered, as it should be. In your code, you attempted to get the status of the 'interrupt flag' with Get_ITStatus(). However, the only thing Get_ITStatus() does is: check if the associated status flag is SET && if the associated interrupt is enabled.

Now, we know the first condition is true, since you SET the flag yourself and you have verified that it is indeed set. The second condition should be true as well--if the interrupt being checked were RXNEIE (since you enabled it). But the actual interrupt enable bit being checked, when USART_IT_ORE is passed to Get_ITStatus(), is EIE (Error Interrupt Enable). So since you didnt enable this interrupt, Get_ITStatus() will always return RESET (0) for an Overrun error.

Get_ITStatus() is not completely incorrect though. The diagram above shows that when ORE, EIE and DMAR are set, an interrupt will be generated. According to the manual at the link you provided:

EIE defs

This snippet provides additional confirmation. So it seems that the library authors only coded for this second possibility (though not correctly since DMAR isnt checked). Your work-around checks the status flag directly which is okay as long as you dont set EIE with RXNEIE, since you wouldnt know which source generated the Overrun Error.