串口初始化流程总结

使用方法由 HAL 库提供:

  • 第 1 步: 定义 UART_HandleTypeDef 类型串口结构体变量,比如 UART_HandleTypeDef huart
  • 第 2 步: 使用函数 HAL_UART_MspInit() 初始化串口相关底层配置,这个函数是弱定义的, 允许用户在工程其它源文件里面重新实现此函数。不限制一定要用此函数里面初始化,用户也可以自己实现。HAL_UART_MspInit()HAL_UART_Init() 中会被调用,不需要用户自己调用。在HAL_UART_MspInit() 中主要做以下工作:
    • 使能串口时钟,比如 __HAL_RCC_USART1_CLK_ENABLE()
    • 引脚配置。
      • a、使能串口所使用的 GPIO 时钟,比如 __HAL_RCC_GPIOA_CLK_ENABLE()
      • b、配置 GPIO 的复用模式,TX 引脚配置为 AF_PP,RX 引脚配置为 AF_INPUT。
    • 如果使用中断方式函数 HAL_UART_Transmit_IT()HAL_UART_Receive_IT() 需要做如下配置:
      • a、配置串口中断优先级。
      • b、使能串口中断。
    • 串口中断的开关是通过函数 __HAL_UART_ENABLE_IT()__HAL_UART_DISABLE_IT() 来实现,这两个函数被嵌套到串口的发送和接收函数中调用。
    • 如果使用 DMA 方式函数 HAL_UART_Transmit_DMAHAL_UART_Receive_DMA 需要做如下配置:
      • a、声明串口的发送和接收 DMA 结构体变量,注意发送和接收是独立的,如果都使用,那就都需要配置。
      • b、使能 DMA 接口时钟。
      • c、配置串口的发送和接收 DMA 结构体变量。
      • d、配置 DMA 发送和接收通道。
      • e、关联 DMA 和串口的句柄。
      • f、配置发送 DMA 和接收 DMA 的传输完成中断和中断优先级。
  • 第 3 步: 配置串口的波特率,位长,停止位,奇偶校验位,流控制和发送接收模式。
  • 第 4 步: 如果需要,可以编程高级特性,比如 TX/RX 交换引脚,自动波特率检测。 通过第 1 步串口结构体变量 huart 的结构体成员 AdvancedInit 来设置。
  • 第 5 步: 串口初始化调用的函数 HAL_UART_Init() 初始化。

    中断模式收发数据

    当配置了 NVIC 并使能了串口相关的中断时,当发生对应中断时,会跳转到对应串口的中断服务程序 USARTx_IRQHandler() 中去。

HAL_UART_Transmit_IT()HAL_UART_Receive_IT()不负责具体数据的收发,通过阅读源码可以知道这两个函数只负责开启接收中断,并且初始化串口句柄的缓存等相关参数。开启特定中断是通过 __HAL_UART_ENABLE_IT 实现的。

  1. /**
  2. * @brief Receives an amount of data in non blocking mode.
  3. * @param huart: pointer to a UART_HandleTypeDef structure that contains
  4. * the configuration information for the specified UART module.
  5. * @param pData: Pointer to data buffer
  6. * @param Size: Amount of data to be received
  7. * @retval HAL status
  8. */
  9. HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
  10. {
  11. /* Check that a Rx process is not already ongoing */
  12. if(huart->RxState == HAL_UART_STATE_READY)
  13. {
  14. if((pData == NULL) || (Size == 0U))
  15. {
  16. return HAL_ERROR;
  17. }
  18. /* Process Locked */
  19. __HAL_LOCK(huart);
  20. huart->pRxBuffPtr = pData;
  21. huart->RxXferSize = Size;
  22. huart->RxXferCount = Size;
  23. huart->ErrorCode = HAL_UART_ERROR_NONE;
  24. huart->RxState = HAL_UART_STATE_BUSY_RX;
  25. /* Process Unlocked */
  26. __HAL_UNLOCK(huart);
  27. /* Enable the UART Parity Error Interrupt */
  28. __HAL_UART_ENABLE_IT(huart, UART_IT_PE);
  29. /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
  30. __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
  31. /* Enable the UART Data Register not empty Interrupt */
  32. __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
  33. return HAL_OK;
  34. }
  35. else
  36. {
  37. return HAL_BUSY;
  38. }

具体数据的收发是在中断处理函数 **HAL_UART_IRQHandler()** 里面实现的,它内部通过中断类型来决定调用哪个函数。
image.png
例如是接受完成中断(RXNE),那么会调用函数 UART_Receive_IT() ,在 UART_Receive_IT() 中才真正通过 DR 寄存器读取数据。
image.png
所以如果想要使用 HAL_UART_Transmit_IT()HAL_UART_Receive_IT() 来收发数据,需要在串口对应的 中断服务程序 USARTx_IRQHandler() 中调用 HAL_UART_IRQHandler()

放一张正点原子的串口接收中断执行流程图来帮助理解:
image.png

在中断服务函数中,也可以不调用 HAL_UART_IRQHandler 函数,而是直接编写自己的中断服务函数。如果我们不用中断处理回调函数,那么就不用初始化串口句柄的中断接收缓存,所以 HAL_UART_Receive_IT 函数就不用出现在初始化函数 uart_init 中,而是直接在要开启中断的地方通过调用__HAL_UART_ENABLE_IT 单独开启中断即可。然后在串口的中断服务程序 USARTx_IRQHandler() 中通过 __HAL_UART_GET_FLAG 判断中断类型,进而实现相关逻辑。

阻塞模式收发数据

阻塞模式使用的 HAL 函数是 HAL_UART_Receive()HAL_UART_Transmit()