如何重置STM32 HAL UART驱动程序(HAL)的状态?

3

我知道可以使用以下方法启用UART接收中断:

HAL_UART_Receive_IT(&huart2, (uint8_t *)rx_buffer, expectedNumberOfBytes)
  • 但一旦启动,如何“手动”停止它?

我们可以使用HAL_NVIC_DisableIRQ()(例如:HAL_NVIC_DisableIRQ(USART1_IRQn))禁用UART中断。这将防止它引发中断,但由 HAL_UART_Receive_IT 函数设置的状态(即 HAL_UART_STATE_BUSY_RX )需要设置回 HAL_UART_STATE_READY 状态,以使uart句柄返回到可以接受新的 HAL_UART_Receive_IT()调用的状态。

问题:
如果我希望在一段时间后禁用Rx中断,如何重置UART中断的状态?

Stack Overflow的问题并没有解决如何重置状态;我已经参考了以下问题:

  1. 在STM32F407中禁用中断
  2. https://electronics.stackexchange.com/questions/100073/stm32-usart-rx-interrupts

我可以使用USART_ClearITPendingBit()USART_ITConfig(),但这些在STM的HAL库中被定义为私有函数。所以我应该使用它们吗?


2
你应该使用更清晰的术语。HAL_UART_Receive_IT是一个驱动程序函数,而不是中断处理程序。它为接收驱动程序设置状态。UART外设具有其自己的中断状态,但这与软件驱动程序内部的状态不同,这似乎是你卡住的地方。所以我认为你要问的是“如何重置STM32 HAL UART驱动程序状态?”如果这不是你的意思,而你想重置外设(UART硬件)状态,或者你想重置中断处理(NVIC硬件)状态,请编辑你的问题以明确这一点。 - Ben Voigt
4个回答

7
如果我想在一段时间后禁用Rx中断,应该如何重置UART中断的状态?(例如在“stm32f4xx_hal_uart.c”中看到)“huart-> RxState”成员只在HAL库内部使用,例如“HAL_UART_Receive()”,“HAL_UART_Receive_IT()”,“HAL_UART_Receive_DMA()”等等。但是,如果您手动实现基于中断和环形缓冲区的UART Tx和Rx调用,这是首选方法,此成员完全没有意义,而且无论您如何处理都无关紧要,因为它仅用于HAL库函数调用和HAL ISR处理程序(两者都不需要使用),并且与寄存器级别的中断和直接相关的事物毫无关系。 通过查看“stm32f4xx_hal_uart.c”等源代码,下面是几个可以使用的有效选项:
1. 如何重置“huart-> RxState”为“HAL_UART_STATE_READY”: 1. 调用“HAL_UART_Init()”。通过检查其源代码,您会发现它在返回之前调用了“huart-> RxState = HAL_UART_STATE_READY;”。 2. 只需手动设置“huart-> RxState = HAL_UART_STATE_READY;”。只要您知道已正确停止正在进行处理的基于中断的接收,这是完全有效的。
然而,让我们继续深入。假设您使用的是STM32F4上的UART7。在您的“stm32f4xx_it.c”中断处理程序文件中,您将看到由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 */
}

让我们来看一些禁用/启用中断的层次。

2. 从最宽泛到最具体的范围,以下是禁用/启用USART Rx中断的几种方法:

  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. 如何(笨拙地)使用HAL_UART_Receive_IT()进行持续的基于中断的接收。

待办事项

4. 为什么HAL_UART_Receive_IT()实际上并不是一个非常有用的函数。

待办事项

5. 如何手动配置自己的基于中断的UART Tx和Rx ISRs和函数。

待办事项


2
我喜欢你的思维方式。在我看来,HAL驱动程序是一种笨重的未经记录的层,包装了良好记录的硬件。使用供应商库可能存在的任何可移植性和重用优势都被缺乏文档和与除了驱动程序应该用于的外设的隐藏交互所抵消。没有彻底阅读寄存器级接口手册,你不能希望理解和使用HAL层,而一旦你这样做了,你就不再需要HAL了。 - Ben Voigt
1
哇!谢谢您抽出时间来回答。正如您所说,“状态”只是一个“HAL标志”,在硬件方面并没有太多作用。我会尝试您的建议2,并会再次联系您。当您有时间时,请务必编写3、4和5。再次感谢。 - clamentjohn
1
@BenVoigt,是的,你说的很有道理。HAL层可能会有所帮助,但我认为它可以做得更好,而且你绝对正确,需要不断挖掘才能真正理解它在做什么。即使只是知道如何正确使用它也需要查看示例代码,然后还要阅读STM32参考手册并搜索HAL源代码,因为它的用法和结构文档化程度不高,不幸的是。 - Gabriel Staples
等待那些待办事项 ;) - Rainb

1
您可以使用HAL_UART_Abort_IT

1
你好。你能详细解释一下你的答案吗? - mjuarez

0
大多数UART在程序从保持寄存器读取数据时会清除任何待处理的接收中断。因此,我的答案是:在禁用中断后简单地读取数据寄存器并忽略结果。
我还没有机会在我的STM32上尝试这个方法,但是...

我该如何从数据寄存器中读取数据?在函数HAL_UART_Receive中,数据是从huart->Instance->DR中读取的。所以我尝试从中读取数据。但是huart->State不会被任何人/任何事情重置,我必须手动重置它。这是正确的方法吗?(它可以工作,也就是说,下一个Rx_interrupt按预期工作。) - clamentjohn
手动更新huart->State变量听起来很麻烦,但另一种选择会更糟。如果在禁用中断之前读取数据寄存器,则State变量可能会被更新 - 但是在禁用发生之前可能会到达新字符,这将使您回到原始状态。 - John Burger

0

HAL库中有一个函数static void UART_EndRxTransfer(UART_HandleTypeDef *huart),它执行以下操作:

  • 禁用RXNE、PE和ERR中断
  • huart->RxState恢复为Ready

我在stm32f7xx_hal_uart.c文件中找到了这个函数。然而,它被定义为static,所以我只是将定义复制到了我使用它的文件中。这可能有点hacky,但对我来说有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接