How do I reset the STM32 HAL UART driver (HAL) state?

18,587

Solution 1

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

Solution 2

You can use HAL_UART_Abort_IT.

Share:
18,587

Related videos on Youtube

clamentjohn
Author by

clamentjohn

Apparently, this user prefers to keep an air of mystery about them. Or maybe not. Look behind you!

Updated on June 04, 2022

Comments

  • clamentjohn
    clamentjohn about 2 years

    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?

    • Ben Voigt
      Ben Voigt about 5 years
      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.
  • clamentjohn
    clamentjohn about 5 years
    How do I read from the data register? In the fn HAL_UART_Receive data is read from huart->Instance->DR. So I tried reading from it. But huart->State won't be reset by anyone/anything, I had to manually reset it. Is this the right approach? (It works, as in, the next Rx_interrupt works as expected.)
  • John Burger
    John Burger about 5 years
    Updating the huart->State variable manually sounds like a pain, but the alternative would be worse. If you read the Data Register before you disabled interrupts, then the State variable would (probably) be updated - but a new character could arrive before the disable happened, which would put you back in the original state.
  • Ben Voigt
    Ben Voigt about 5 years
    I like the way you think. IMO the HAL drivers are a clunky undocumented layer wrapping the well-documented hardware. Whatever portability and reuse advantages might exist from using the vendor library are totally negated by lack of documentation and hidden interactions with peripherals other than the one the driver is supposed to be for. You can't hope to understand and use the HAL layer without thoroughly reading the manual for the register-level interface, and once you do that you no longer need the HAL.
  • clamentjohn
    clamentjohn about 5 years
    Woah! Thank you for taking the time to answer. So, as you said, Status is just a HAL flag and doesn't do much w.r.t the hardware. I'll check out your suggestion 2 and get back. And please do write 3,4 and 5 when you get the time. Thanks again.
  • Gabriel Staples
    Gabriel Staples about 5 years
    @BenVoigt, yeah there's definitely a lot of truth to what you're saying. The HAL layer can be helpful, but it could be much better I think, and you are absolutely right about having to dig and dig and dig to understand what it's really doing. Even knowing just how to properly use it requires looking at example code, then also reading the STM32 Reference Manuals and scouring through the HAL source code, as its usage and structure isn't very well documented, unfortunately.
  • mjuarez
    mjuarez over 4 years
    Hi. Can you expand on your answer please?
  • Rainb
    Rainb about 3 years
    Waitung for those TODOS ;)