串口初始化流程总结
使用方法由 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。
- a、使能串口所使用的 GPIO 时钟,比如
- 如果使用中断方式函数
HAL_UART_Transmit_IT()
和HAL_UART_Receive_IT()
需要做如下配置:- a、配置串口中断优先级。
- b、使能串口中断。
- 串口中断的开关是通过函数
__HAL_UART_ENABLE_IT()
和__HAL_UART_DISABLE_IT()
来实现,这两个函数被嵌套到串口的发送和接收函数中调用。 - 如果使用 DMA 方式函数
HAL_UART_Transmit_DMA
和HAL_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
实现的。
/**
* @brief Receives an amount of data in non blocking mode.
* @param huart: pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @param pData: Pointer to data buffer
* @param Size: Amount of data to be received
* @retval HAL status
*/
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
/* Check that a Rx process is not already ongoing */
if(huart->RxState == HAL_UART_STATE_READY)
{
if((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Parity Error Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
具体数据的收发是在中断处理函数 **HAL_UART_IRQHandler()**
里面实现的,它内部通过中断类型来决定调用哪个函数。
例如是接受完成中断(RXNE),那么会调用函数 UART_Receive_IT()
,在 UART_Receive_IT()
中才真正通过 DR 寄存器读取数据。
所以如果想要使用 HAL_UART_Transmit_IT()
和 HAL_UART_Receive_IT()
来收发数据,需要在串口对应的 中断服务程序 USARTx_IRQHandler()
中调用 HAL_UART_IRQHandler()
。
放一张正点原子的串口接收中断执行流程图来帮助理解:
在中断服务函数中,也可以不调用 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()
。