STM32 Uart HalfDuplex keeps looping on USART2_IRQHandler never breaks

91 Views Asked by At

I cant seem to figure out why but when the Interrupt is enabled it just keeps calling USART2_IRQHandler I've checked the ISR register and nothing is changing it all stays the same.

I've tried reconfiguring it to be Full duplex and i encounter the same problem. The uart is supposed to be SingleWire/Half Duplex

void USART2_IRQHandler()
{
    //never stops getting called loops
}
static void MX_USART2_UART_Init(void)
{
    /* USER CODE BEGIN USART2_Init 0 */
    // HAL_UART_Receive_IT(&huart1, &Uart1_Rx, 1);
    /* USER CODE END USART2_Init 0 */

    /* USER CODE BEGIN USART2_Init 1 */

    /* USER CODE END USART2_Init 1 */
    huart2.Instance = DELTA_USART;
    huart2.Init.BaudRate = 57600; // 57.6
    huart2.Init.WordLength = UART_WORDLENGTH_9B;
    huart2.Init.StopBits = UART_STOPBITS_1;
    huart2.Init.Parity = UART_PARITY_ODD;
    huart2.Init.Mode = UART_MODE_TX_RX;
    huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart2.Init.OverSampling = UART_OVERSAMPLING_8;
    huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
    huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
    if (HAL_HalfDuplex_Init(&huart2) != HAL_OK) {
         Error_Handler();
    }
    /* USER CODE BEGIN USART2_Init 2 */

    /* USER CODE END USART2_Init 2 */
}


inline void EnableInterupt()
{
    NVIC_EnableIRQ(irq);
}
inline void DisableInterupt()
{
    NVIC_DisableIRQ(irq);
}

void setupUartInterupt()
{
    huart2.Instance->CR1 |= USART_CR1_RXNEIE //| USART_CR1_TXEIE
                    | USART_CR1_IDLEIE;
    huart2.Instance->CR1 |= USART_CR1_TE | USART_CR1_RE;
        //_huart->CR3 |= USART_CR3_OVRDIS;

    NVIC_SetPriority(USART2_IRQn, 2);
}
2

There are 2 best solutions below

0
Rocky On BEST ANSWER

I figured it out it was something really stupid because i should have clarified earlier it only really happened when i set break points and so all i can figure it would sometime set the Overrun error flag witch is enabled by default and so it was never getting cleared causing it to keep calling repetitively so i just tweaked the setup code and disabled over run.

Just clarity the code sample before was a simplification to the project that its apart of it was just a lot of different pieces to try and include so i simplified it as best as i could. (Thanks again for the help it did point me in the right direction because i started monitoring the ISR register real time by making a crude struct to decode the ISR and i noticed the ORE was getting called when it would start looping.

DeltaUARTHL::ISRBitField isrTest = DeltaUARTHL::ISRBitField();
void USART2_IRQHandler(void) //<<In main CPP
{
    isrTest = DeltaUARTHL::ISRBitField(&huart2.Instance->ISR);
    delta.DeltaUartEventCallBack();
}


inline void DeltaInitialize(uint16_t _bufferSize, volatile uint32_t *microTickCounter, USART_TypeDef *_huart, IRQn_Type _irq)//<<In Library H
{
    huartHandle = _huart;            //Set internal refrence to Uart Handle
    deltaTickPtr = microTickCounter; //Sets internal counter to refrence some external counter
    ClearReceiverStats();            //Clears receiver stats to default
    if (_bufferSize > 0)             //Checks if wanting to creat buffer
        CreateBuffer(_bufferSize);   //Creates a buffer space

    localDeviceAddress = 0; //Sets localDevice address to 0 (To Be Determained)
    irq = _irq;             //Sets the internal Interrupt number.

    _huart->CR1 &= ~USART_CR1_UE;                       //have to disable uart inorder to set control bits
    _huart->CR1 |= USART_CR1_RXNEIE | USART_CR1_IDLEIE; //Enables  idle interupt and receive interupt
    _huart->CR1 |= USART_CR1_TE | USART_CR1_RE;         //Enables Transmitter and Receiver
    _huart->CR3 |= USART_CR3_OVRDIS;                    //Disable over run
    _huart->CR1 |= USART_CR1_UE;                        //Then re-enable uart
    NVIC_SetPriority(_irq, 2);                          //Setup interupt priority
    NVIC_EnableIRQ(irq);                                //Enable interupt
}



void DeltaUARTHL::DeltaUartEventCallBack(void)
{

    if (huartHandle->ISR & USART_ISR_IDLE) // handle idle line event<<<<<<<<<<<<<<<<<<<<<
    {
        huartHandle->ICR |= USART_ICR_IDLECF;
        DeltaHandleIDLE_CallBack();
    }

    if (huartHandle->ISR & USART_ISR_RXNE) // handle received data event<<<<<<<<<<<<<<<<<
    {
        // deltaHuart->RQR |= USART_RQR_RXFRQ;
        DeltaHandleRX_CallBack();
    }
}


struct ISRBitField//<<Also in Library H
{
    ISRBitField() {}
    ISRBitField(volatile uint32_t *isrReg)
    {
        uint32_t isr = *isrReg;//Dereference
        REACK = isr & USART_ISR_REACK;
        TEACK = isr & USART_ISR_TEACK;
        WUF = isr & USART_ISR_WUF;
        RWU = isr & USART_ISR_RWU;
        SBKF = isr & USART_ISR_SBKF;
        CMF = isr & USART_ISR_CMF;
        BUSY = isr & USART_ISR_BUSY;
        ABRF = isr & USART_ISR_ABRF;
        ABRE = isr & USART_ISR_ABRE;
        EOBF = isr & USART_ISR_EOBF;
        RTOF = isr & USART_ISR_RTOF;
        CTS = isr & USART_ISR_CTS;
        CTSIF = isr & USART_ISR_CTSIF;
        LBDF = isr & USART_ISR_LBDF;
        TXE = isr & USART_ISR_TXE;
        TC = isr & USART_ISR_TC;
        RXNE = isr & USART_ISR_RXNE;
        IDLE = isr & USART_ISR_IDLE;
        ORE = isr & USART_ISR_ORE;
        NF = isr & USART_ISR_NE;
        FE = isr & USART_ISR_FE;
        PE = isr & USART_ISR_PE;
    }
    bool REACK = false;
    bool TEACK = false;
    bool WUF = false;
    bool RWU = false;
    bool SBKF = false;
    bool CMF = false;
    bool BUSY = false;
    bool ABRF = false;
    bool ABRE = false;

    bool EOBF = false;
    bool RTOF = false;
    bool CTS = false;
    bool CTSIF = false;
    bool LBDF = false;
    bool TXE = false;
    bool TC = false;
    bool RXNE = false;
    bool IDLE = false;
    bool ORE = false;
    bool NF = false;
    bool FE = false;
    bool PE = false;
};
3
Ilya On

Before answering your question, some context about how things work internally:

Let's go over the logic of how interrupts in Cortex-M MCUs (such as STM32) happen.

There are three devices involved in makeing interrupt happen in STM32 (at least Cortex-M cores): the source of interrupt event (UART peripheral device), NVIC (core peripheral device), and the core itself. I will use the word "core" a little loosely, one can always go deeper, but it would be irrelevant for this discussion.

I drew an illustration, if you excuse its crudity:

enter image description here

Several important points are:
-Your peripheral is not connected directly to the core, it doesn't trigger core interrupts by itself
-The core has one and only interrupt signal line. All exceptions and interrupt events are triggered using this one and only line.
-NVIC sits on the core interrupt line and triggers it.
-NVIC receives interrupt event signals from other peripherals as interrupt requests (IRQs), and then, if that interrupt is enabled, NVIC in turn triggers the core interrupt. In a way, NVIC passes peripheral interrupt signal through to the core, when that interrupt is enabled.

NVIC is basically an interrupt manager for the core. It controls priorities, it controls what interrupts are enabled and not, but it also keeps track of interrupt handling status - is the interrput handler still being executed? Is it over? It automatically sets and clears interrupt handler status flags in itself (but not in peripherals). Such statuses include "interrupt handler waiting to be started", or "interrupt handler being executed". You can check NVIC registers in a document called Programming Manual (for the core of your MCU).


Answering the question:

Why doesn't your implementation work the way you intend it to?
Because you never removed the UART event trigger (see picture). The event bit in the peripheral is set. This signal is sent to NVIC constantly, not as a pulse. For as long as event bit in the peripheral is set, the peripheral triggers its IRQ in NVIC, and even though NVIC has just executed the event handler, and marked that event handler as finished, it constantly gets another interrupt request from the peripheral.

Solution:
Interrupt handler needs to clear event flag in the peripheral, so the peripheral doesn't constantly trigger NVIC. You don't need to clear event flags in NVIC, it does that automatically, but the peripherals are outside the core, they're literally external devices for the core, so you need to clear event flags in the peripherals manually.