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

Posted

技术标签:

【中文标题】如何重置 STM32 HAL UART 驱动程序 (HAL) 状态?【英文标题】:How do I reset the STM32 HAL UART driver (HAL) state? 【发布时间】:2019-08-19 01:12:33 【问题描述】:

我知道可以使用 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句柄返回到可以接受新@987654329的状态@调用。

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

堆栈溢出问题没有解决如何重置状态;我已经提到了这些问题:

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

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

【问题讨论】:

您应该使用更清晰的术语。 HAL_UART_Receive_IT 是一个驱动函数,而不是一个中断处理程序。它为接收驱动程序设置状态。 UART 外围设备有自己的中断状态,但这与软件驱动程序中的状态不同,这似乎是您所坚持的。所以我认为您要问的是“如何重置 STM32 HAL UART 驱动程序状态?”如果这不是您的意思,并且您想要重置外围设备(UART 硬件)状态,或者您想要重置中断处理(NVIC 硬件)状态,请编辑您的问题以明确说明。 【参考方案1】:

如果[我]希望在一段时间后禁用 Rx 中断[?]

,如何[我]重置 UART 中断的状态

(例如,参见“stm32f4xx_hal_uart.c”中的用法。)

uart 句柄结构的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

    致电HAL_UART_Init()。通过检查它的源代码,您会看到它在返回之前调用了huart->RxState= HAL_UART_STATE_READY;。 只需手动设置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 中断的几种方法:

    您可以使用这些 ARM 核心 CMSIS 调用禁用/启用所有中断,包括此 UART7_IRQHandler()

    __disable_irq();
    __enable_irq();
    

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

    因此,您可以执行以下操作来禁用中断,重置RxState,然后在准备好时再次启动基于中断的接收:

    __disable_irq();
    huart7->RxState= HAL_UART_STATE_READY;
    
    __enable_irq();
    HAL_UART_Receive_IT(&huart7, (uint8_t *)rx_buffer, expectedNumberOfBytes);
    

    您可以使用这些 STM32 HAL 调用仅禁用/启用UART7_IRQHandler() 中断(连接到此中断向量的所有 10 种类型的 uart7 中断,包括 Tx 相关、Rx 相关、错误相关等):

    HAL_NVIC_DisableIRQ(UART7_IRQn);
    HAL_NVIC_EnableIRQ(UART7_IRQn);
    

    然后,除了使用这些调用来禁用/启用中断之外,执行与上面相同的操作。

    如果你深入研究HAL_UART_IRQHandler() 的实现,它被UART7_IRQHandler() 调用,你会发现它只调用基于中断的接收处理程序UART_Receive_IT(),如果USART_SR_RXNE位(USART 状态寄存器内的“接收非空”)和USART_CR1_RXNEIE 位(USART 控制寄存器 1 内的“接收非空中断启用”)设置。只要有一个字节进入,RXNE 位就会被设置,而当您读取数据寄存器或向其写入零时,它就会被清除。 您可以完全控制中断启用位来禁用此 UART 接收中断,如果您手动清除该位,您将禁用接收中断,而不会禁用与此 USART 相关的任何其他类型的中断。这是最好的方法,因为有 10 个中断源与这个 UART 相关联。 换句话说,清除该位不仅会导致 HAL_UART_IRQHandler() 内部的检查失败,而且还会阻止接收中断从一开始就发生!请参阅参考手册 RM0090 Rev 16,例如:

    p969:

    p1009:

    p1011:

    p1015:

    p1013:

    因此,要仅禁用/启用 USART Receive Not Empty 中断,请执行以下操作。 请参阅 p1013 上的控制寄存器 (USART_CR1),如上所示。

    // 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);
    

    现在,您可以执行以下操作来禁用 USART 接收中断,重置 HAL RxState,然后在准备好时再次启动基于中断的接收:

    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 ISR 和功能。

待办事项

【讨论】:

我喜欢你的想法。 IMO HAL 驱动程序是一个笨重的无证层,包裹着有据可查的硬件。使用供应商库可能存在的任何可移植性和重用优势都完全被缺乏文档和与驱动程序应该用于的外围设备之外的外围设备的隐藏交互所抵消。如果不彻底阅读寄存器级接口的手册,你就无法理解和使用 HAL 层,一旦你这样做了,你就不再需要 HAL。 哇!感谢您花时间回答。所以,正如你所说,Status 只是一个HAL flag,并没有做太多的硬件工作。我会检查你的建议 2 并回来。当你有时间时,请务必写下 3,4 和 5。再次感谢。 @BenVoigt,是的,你所说的肯定有很多道理。 HAL 层可能会有所帮助,但我认为它可能会更好,而且您必须挖掘、挖掘和挖掘以了解它真正在做什么是绝对正确的。即使只知道如何正确使用它,也需要查看示例代码,然后阅读 STM32 参考手册并仔细阅读 HAL 源代码,因为它的用法和结构不是不幸的是,没有很好的记录。 等待那些 TODOS ;)【参考方案2】:

当程序从保持寄存器中读取数据时,大多数 UART 会清除任何挂起的接收中断。所以我的回答是:在禁用中断后简单地读取数据寄存器,而忽略结果。

我还没有机会在我的 STM32 上尝试这个,但是...

【讨论】:

如何读取数据寄存器?在 fn HAL_UART_Receive 中,数据从 huart->Instance->DR 读取。所以我试着从中阅读。但是huart->State 不会被任何人/任何东西重置,我必须手动重置它。这是正确的方法吗? (它的工作原理是,下一个 Rx_interrupt 按预期工作。) 手动更新huart->State 变量听起来很痛苦,但替代方案会更糟。如果您禁用中断之前读取数据寄存器,那么State 变量将(可能)被更新 - 但一个新字符可能在禁用发生之前到达,这会让你回到原来的状态。【参考方案3】:

您可以使用HAL_UART_Abort_IT

【讨论】:

嗨。你能扩展你的答案吗?【参考方案4】:

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

禁用 RXNE、PE 和 ERR 中断 将huart->RxState 恢复为就绪

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

【讨论】:

以上是关于如何重置 STM32 HAL UART 驱动程序 (HAL) 状态?的主要内容,如果未能解决你的问题,请参考以下文章

STM32F4 UART HAL 驱动程序

带有 STM32 HAL 驱动程序的 FreeRTOS 中的 UART 中断

STM32 HAL USART驱动程序:此语法如何工作?

如何在 STM32F1 上使用 HAL 库永久启用 uart RX 中断

STM32F4 UART HAL驱动程序'将字符串保存在变量缓冲区中'

STM32-HAL库-UART学习