2
votes

I know one can enable a UART receive interrupt using

HAL_UART_Receive_IT(&huart2, (uint8_t *)rx_buffer, expectedNumberOfBytes)
  • But once started how does one stop it, "manually"?

We can disable the UART interrupt using HAL_NVIC_DisableIRQ() (ex: HAL_NVIC_DisableIRQ(USART1_IRQn)). This will prevent it from raising an interrupt, but the state set by the function HAL_UART_Receive_IT which is HAL_UART_STATE_BUSY_RX needs to be set back to HAL_UART_STATE_READY for the uart handle to go back to a state that can accept a new HAL_UART_Receive_IT() call.

Question
How do I reset the state of the UART interrupt if I wish to disable a Rx interrupt after some time?

Stack Overflow questions do not address how to reset the state; I have referred to these questions:

  1. Disabling interrupt in interrupt handler STM32F407
  2. https://electronics.stackexchange.com/questions/100073/stm32-usart-rx-interrupts

I could use USART_ClearITPendingBit() or USART_ITConfig() but these are defined as private functions by STM's HAL library. So should I use them?

4
You should probably use clearer terminology. HAL_UART_Receive_IT is a driver function, not an interrupt handler. It sets state for the receive driver. The UART peripheral has interrupt state of its own, but this is different from the state within the software driver, which seems to be what you are stuck on. So I think what you are asking is "How do I reset the STM32 HAL UART driver state?" If that's not what you mean, and you want to reset the peripheral (UART hardware) state, or you want to reset the interrupt handling (NVIC hardware) state, please edit your question to make that clear.Ben Voigt

4 Answers

6
votes

How [do I] reset the state of the UART interrupt if [I] wish to disable a Rx interrupt after some time[?]

(See it's usage in "stm32f4xx_hal_uart.c", for example.)

The huart->RxState member of a uart handle struct is really only used internally by the HAL library when doing things such as HAL_UART_Receive(), HAL_UART_Receive_IT(), HAL_UART_Receive_DMA(), (and many other internal functions like this), etc. If you manually implement your own interrupt-based and ring-buffer-based UART Tx and Rx calls, however, which is the preferred way to do it, this member is completely meaningless and it doesn't matter what you do with it, as it is used only inside HAL library function calls and HAL ISR handlers (neither of which you have to use), and really has nothing to do with the register-level interrupts and things directly.

By digging around the source code in stm32f4xx_hal_uart.c (for example), however, here are a couple valid options you can use:

1. How to reset huart->RxState to HAL_UART_STATE_READY:

  1. Call HAL_UART_Init(). By inspecting its source code, you'll see it calls huart->RxState= HAL_UART_STATE_READY; before returning.
  2. Just manually set huart->RxState = HAL_UART_STATE_READY; So long as you know you have properly stopped the interrupt-based receive in the middle of its processing, this is perfectly valid.

Let's take this further, however.

Imagine you are using UART7 on an STM32F4. Therefore, in your stm32f4xx_it.c interrupt handler file, you'll see the following code auto-generated by STM32CubeMX:

/**
* @brief This function handles UART7 global interrupt.
*/
void UART7_IRQHandler(void)
{
  /* USER CODE BEGIN UART7_IRQn 0 */

  /* USER CODE END UART7_IRQn 0 */
  HAL_UART_IRQHandler(&huart7);
  /* USER CODE BEGIN UART7_IRQn 1 */

  /* USER CODE END UART7_IRQn 1 */
}

Let's go over some layers of disabling/enabling interrupts.

2. From broadest to narrowest scope, here are several ways to disable/enable the USART Rx interrupt:

  1. You can disable/enable ALL interrupts, including this UART7_IRQHandler(), using these ARM-core CMSIS calls:

    __disable_irq();
    __enable_irq();
    

    Source: https://stm32f4-discovery.net/2015/06/how-to-properly-enabledisable-interrupts-in-arm-cortex-m/

    So, you could do the following to disable the interrupt, reset the RxState, and then start up the interrupt-based receive again when ready:

    __disable_irq();
    huart7->RxState= HAL_UART_STATE_READY;
    
    __enable_irq();
    HAL_UART_Receive_IT(&huart7, (uint8_t *)rx_buffer, expectedNumberOfBytes);
    
  2. You can disable/enable ONLY the UART7_IRQHandler() interrupts (all 10 types of uart7 interrupts connected to this interrupt vector, including Tx-related, Rx-related, error-related, etc), using these STM32 HAL calls:

    HAL_NVIC_DisableIRQ(UART7_IRQn);
    HAL_NVIC_EnableIRQ(UART7_IRQn);
    

    Then, do the same as just above except use these calls to disable/enable the interrupts instead.

  3. If you dig down into the implementation of HAL_UART_IRQHandler(), however, which is called by UART7_IRQHandler(), you'll see that it only calls the interrupt-based receive handler, UART_Receive_IT(), if both the USART_SR_RXNE bit ("Receive Not Empty", inside the USART Status Register) and the USART_CR1_RXNEIE bit ("Receive Not Empty Interrupt Enable", inside the USART Control Register 1), are both set. The RXNE bit is set whenever a byte comes in, and is cleared whenever you read the data register or write a zero to it. The interrupt-enable bit is something you have full control over to disable this UART receive interrupt, and if you clear this bit manually, you will disable the receive interrupt withOUT disabling any other type of interrupt associated with this USART. This is the best way to do it, as there are 10 interrupt sources associated with this UART. In other words, clearing this bit not only causes the check inside HAL_UART_IRQHandler() to fail, but it also prevents the receive interrupt from happening in the first place! Refer to the Reference Manual RM0090 Rev 16, for example:

    p969: enter image description here

    p1009: enter image description here

    p1011: enter image description here

    p1015: enter image description here

    p1013: enter image description here

    So, to disable/enable the USART Receive Not Empty interrupt only, do the following. Refer to the Control Register (USART_CR1) on p1013, shown just above.

    // Disable the USART Receive Not Empty interrupt
    CLEAR_BIT(huart7.Instance.CR1, USART_CR1_RXNEIE);
    
    // Enable the USART Receive Not Empty interrupt
    SET_BIT(huart7.Instance.CR1, USART_CR1_RXNEIE);
    

    Now, you could do the following to disable the USART Receive interrupt, reset the HAL RxState, and then start up the interrupt-based receive again when ready:

    CLEAR_BIT(huart7.Instance.CR1, USART_CR1_RXNEIE);
    huart7->RxState= HAL_UART_STATE_READY;
    
    SET_BIT(huart7.Instance.CR1, USART_CR1_RXNEIE); // This call isn't actually necessary, as this bit is set inside `HAL_UART_Receive_IT()` as well
    HAL_UART_Receive_IT(&huart7, (uint8_t *)rx_buffer, expectedNumberOfBytes);
    

3. How to (awkwardly) use HAL_UART_Receive_IT() for continual interrupt-based receiving.

TODO

4. Why HAL_UART_Receive_IT() really isn't a very useful function after-all.

TODO

5. How to manually configure your own interrupt-based UART Tx and Rx ISRs and functions.

TODO

0
votes

Most UARTs clear any pending Receive interrupt when the program reads from the holding register. So my answer would be: simply read the data register after disabling interrupts, and ignore the result.

I haven't had a chance to try this on my STM32 yet, but...

0
votes

You can use HAL_UART_Abort_IT.

0
votes

There is a function static void UART_EndRxTransfer(UART_HandleTypeDef *huart) in the HAL library that does the following:

  • Disable RXNE, PE and ERR interrupts
  • restore huart->RxState to Ready

I found that function in the stm32f7xx_hal_uart.c file. However, it is defined as static, so I just copied over the definition into the file where I used it. It might be a bit hacky but it worked for me.