// USART 是对 UART 的扩展。它除了支持异步传输之外,也支持同步传输。但目前较少应用。
UART(Universal Asynchronous Receiver/Transmitter),即通用异步收发传输器;
USART(Universal Synchronous Asynchronous Receiver Transmitter),通用同步/异步串行接收/发送器
在库中提供了stm32l0xx_hal_uart.c 与 stm32l0xx_hal_usart.c,在这里说明一下这两个文件的使用,比如UART是异步收发器,USART是同步异步收发器。
一般情况下都是使用stm32l0xx_hal_uart.c,而这两个文件使用哪一个是由设备收发类型决定,常用设备都是异步收发。
初始化实例
UART_HandleTypeDef UartHandle; // 定义串口句柄
UartHandle.Instance = USART1; // 初始化串口实例 USART1
UartHandle.Init.BaudRate = 9600; // 设置波特率
UartHandle.Init.WordLength = UART_WORDLENGTH_8B; // 8位数据位
UartHandle.Init.StopBits = UART_STOPBITS_1; // 一个停止位
UartHandle.Init.Parity = UART_PARITY_NONE; // 奇偶校验位无
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 硬件控制流无
UartHandle.Init.Mode = UART_MODE_TX_RX; // RX和TX模式
if(HAL_UART_Init(&UartHandle) != HAL_OK) //使能结构体变量
{
Error_Handler();
}
三种形式:阻塞式、中断方式和 DMA 模式
HAL_UART_Transmit(); // 阻塞式发送,串口轮询模式发送,使用超时管理机制
HAL_UART_Receive(); // 阻塞式接收,串口轮询模式接收,使用超时管理机制
HAL_UART_Transmit_IT(); // 串口中断模式发送
HAL_UART_Receive_IT(); // 串口中断模式接收
HAL_UART_Transmit_DMA(); // 串口DMA模式发送
HAL_UART_Transmit_DMA(); // 串口DMA模式接收
回调函数
HAL_UART_TxHalfCpltCallback(); // 一半数据发送完成时调用
HAL_UART_TxCpltCallback(); // 数据完全发送完成后调用
HAL_UART_RxHalfCpltCallback(); // 一半数据接收完成时调用
HAL_UART_RxCpltCallback(); // 数据完全接受完成后调用
HAL_UART_ErrorCallback(); // 传输出现错误时调用
MSP 函数是 HAL 库与标准库三大不同之一(另外两个:句柄和回调函数见这里)。
MSP(MCU Specific Package,单片机的具体方案),是指和MCU相关的初始化,这里引用正点原子的话进行理解。
我们要初始化一个串口,首先要设置和 MCU 无关的东西,例如波特率,奇偶校验,停止位等,这些参数设置和 MCU 没有任何关系,可以使用 STM32F1,也可以是 STM32F2/F3/F4/F7上的串口。而一个串口设备它需要一个 MCU 来承载,例如用 STM32F4 来做承载,PA9 做为发送,PA10 做为接收,MSP 就是要初始化 STM32F4 的 PA9,PA10,配置这两个引脚。所以 HAL驱动方式的初始化流程就是:AL_USART_Init()—>HAL_USART_MspInit() ,先初始化与 MCU无关的串口协议,再初始化与 MCU 相关的串口引脚。在 STM32 的 HAL 驱动中HAL_PPP_MspInit()作为回调,被 AL_PPP_Init()函数所调用。当我们需要移植程序到 STM32F1平台的时候,我们只需要修改 HAL_PPP_MspInit 函数内容而不需要修改 HAL_PPP_Init 入口参数内容。
上面这段话,可以从两个方面进行理解:
我们的任务是初始化串口,初始化的内容可分为两部分:
同样地,以串口为例,
在 MX_USART1_UART_Init(void) 函数中初始化串口的波特率、停止位、奇偶校验等,这部分代码是与 MCU 无关的串口通信协议,是抽象的。
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
}
在 stm32f0xx_hal_msp.c 文件中的 HAL_UART_MspInit(UART_HandleTypeDef huart)* 函数中将串口所用的 MCU 引脚模式进行配置,这部分是与 MCU 具体相关的。
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
// 关于 GPIO 引脚初始化的流程与上面介绍的“四步”相同
GPIO_InitTypeDef GPIO_InitStruct;
if(huart->Instance==USART2) // USART 2
{
/* USER CODE BEGIN USART2_MspInit 0 */
/* USER CODE END USART2_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_USART2_CLK_ENABLE(); // 使能串口时钟
/**USART2 GPIO Configuration
PA2 ------> USART2_TX
PA3 ------> USART2_RX
*/
GPIO_InitStruct.Pin = USART2_TX_Pin|USART2_RX_Pin; // 收发引脚
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF1_USART2; // 复用功能配置
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 结构体初始化
/* USART2 DMA Init */ // DMA 方式收发初始化
/* USART2_RX Init */
hdma_usart2_rx.Instance = DMA1_Channel3;
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_rx.Init.Mode = DMA_NORMAL;
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_DMA1_REMAP(HAL_DMA1_CH3_USART2_RX);
__HAL_LINKDMA(huart,hdmarx,hdma_usart2_rx);
/* USART2_TX Init */
hdma_usart2_tx.Instance = DMA1_Channel4;
hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_tx.Init.Mode = DMA_NORMAL;
hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_DMA1_REMAP(HAL_DMA1_CH4_USART2_TX);
__HAL_LINKDMA(huart,hdmatx,hdma_usart2_tx);
/* USART2 interrupt Init */
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_MspInit 1 */
/* USER CODE END USART2_MspInit 1 */
}
}
HAL驱动方式的初始化流程就是:AL_USART_Init()—>HAL_USART_MspInit() ,
先初始化与 MCU无关的串口协议,再初始化与 MCU 相关的串口引脚。
在 STM32 的 HAL 驱动中HAL_PPP_MspInit()作为回调,被 AL_PPP_Init()函数所调用。
主要优点是增强了代码的逻辑性,也让代码在不同平台上的移植性更好。
当我们需要移植程序到 STM32F1平台的时候,我们只需要修改 HAL_PPP_MspInit 函数内容而不需要修改 HAL_PPP_Init 入口参数内容。