STM32 HAL库学习(三):UART

1. USART 与 UART

// 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,而这两个文件使用哪一个是由设备收发类型决定,常用设备都是异步收发。

2. 串口常用函数

2.1 结构体初始化

初始化实例

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

   }

2.2 串口发送/接收函数

三种形式:阻塞式、中断方式和 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模式接收

2.3 串口中断函数

回调函数

HAL_UART_TxHalfCpltCallback();     // 一半数据发送完成时调用

HAL_UART_TxCpltCallback();     // 数据完全发送完成后调用

HAL_UART_RxHalfCpltCallback();     // 一半数据接收完成时调用

HAL_UART_RxCpltCallback();     // 数据完全接受完成后调用

HAL_UART_ErrorCallback();     // 传输出现错误时调用

3. HAL_Msp函数

3.1 HAL_Msp函数的作用

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 入口参数内容。

上面这段话,可以从两个方面进行理解:
我们的任务是初始化串口,初始化的内容可分为两部分:

  • 一部分是与MCU无关的,通信协议;
  • 一部分是与MCU相关的,引脚功能。如上面所说的需要 MCU 来承载,也就是 MSP 函数需要实现的内容。

3.2 HAL_Msp函数实例说明

同样地,以串口为例,

3.2.1 串口协议配置

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

}
3.2.2 串口引脚配置

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 */
  }
}
3.2.3 调用关系与驱动流程

HAL驱动方式的初始化流程就是:AL_USART_Init()—>HAL_USART_MspInit() ,

先初始化与 MCU无关的串口协议,再初始化与 MCU 相关的串口引脚。

在 STM32 的 HAL 驱动中HAL_PPP_MspInit()作为回调,被 AL_PPP_Init()函数所调用。

3.2.4 HAL_Msp函数优点

主要优点是增强了代码的逻辑性,也让代码在不同平台上的移植性更好。

当我们需要移植程序到 STM32F1平台的时候,我们只需要修改 HAL_PPP_MspInit 函数内容而不需要修改 HAL_PPP_Init 入口参数内容。

你可能感兴趣的:(STM32,HAL库)