当前位置 博文首页 > 不积跬步,无以至千里:STM32F4 HAL库开发 -- 串口

    不积跬步,无以至千里:STM32F4 HAL库开发 -- 串口

    作者:[db:作者] 时间:2021-08-28 16:28

    一、串口介绍

    串口设置包含:开启串口时钟、设置响应的IO口模式、设置波特率、数据位长度、奇偶校验位、DMA等信息。
    具体参看:STM32开发 – 串口详解

    二、函数

    1、串口参数初始化,并使能串口。

    HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
    

    该函数只有一个入口参数 huart,为UART_HandleTypeDef 结构体指针类型,我们俗称其为串口句柄,它的使用会贯穿整个串口程序。一般情况下,我们会定义一个UART_HandleTypeDef 结构体类型全局变量,然后初始化各个成员变量。
    结构体UART_HandleTypeDef 定义:

    /**
      * @brief  UART handle Structure definition
      */
    typedef struct __UART_HandleTypeDef
    {
      USART_TypeDef                 *Instance;        /*!< UART registers base address        */
    
      UART_InitTypeDef              Init;             /*!< UART communication parameters      */
    
      uint8_t                       *pTxBuffPtr;      /*!< Pointer to UART Tx transfer Buffer */
    
      uint16_t                      TxXferSize;       /*!< UART Tx Transfer size              */
    
      __IO uint16_t                 TxXferCount;      /*!< UART Tx Transfer Counter           */
    
      uint8_t                       *pRxBuffPtr;      /*!< Pointer to UART Rx transfer Buffer */
    
      uint16_t                      RxXferSize;       /*!< UART Rx Transfer size              */
    
      __IO uint16_t                 RxXferCount;      /*!< UART Rx Transfer Counter           */
    
      DMA_HandleTypeDef             *hdmatx;          /*!< UART Tx DMA Handle parameters      */
    
      DMA_HandleTypeDef             *hdmarx;          /*!< UART Rx DMA Handle parameters      */
    
      HAL_LockTypeDef               Lock;             /*!< Locking object                     */
    
      __IO HAL_UART_StateTypeDef    gState;           /*!< UART state information related to global Handle management
                                                           and also related to Tx operations.
                                                           This parameter can be a value of @ref HAL_UART_StateTypeDef */
    
      __IO HAL_UART_StateTypeDef    RxState;          /*!< UART state information related to Rx operations.
                                                           This parameter can be a value of @ref HAL_UART_StateTypeDef */
    
      __IO uint32_t                 ErrorCode;        /*!< UART Error code                    */
    } UART_HandleTypeDef;
    
    

    该结构体成员变量非常多,一般情况下下载调用函数HAL_UART_Init对串口进行初始化的时候,我们只需要先设置Instance和Init两个成员变量的值。
    Instance 是 USART_TypeDef 结构体指针类型的变量,它是执行寄存器基地址,实际上这个基地址HAL库已经定义好了,如果是串口1,取值为USART1即可。
    Init是UART_InitTypeDef 结构体类型变量,它是用来设置串口的各个参数,包括波特率、停止位等。
    UART_InitTypeDef 结构体定义如下:

    typedef struct
    {
      uint32_t BaudRate;                  /*!< This member configures the UART communication baud rate.
                                               The baud rate is computed using the following formula:
                                               - IntegerDivider = ((PCLKx) / (8 * (OVR8+1) * (huart->Init.BaudRate)))
                                               - FractionalDivider = ((IntegerDivider - ((uint32_t) IntegerDivider)) * 8 * (OVR8+1)) + 0.5
                                               Where OVR8 is the "oversampling by 8 mode" configuration bit in the CR1 register. */
    
      uint32_t WordLength;                /*!< Specifies the number of data bits transmitted or received in a frame.
                                               This parameter can be a value of @ref UART_Word_Length */
    
      uint32_t StopBits;                  /*!< Specifies the number of stop bits transmitted.
                                               This parameter can be a value of @ref UART_Stop_Bits */
    
      uint32_t Parity;                    /*!< Specifies the parity mode.
                                               This parameter can be a value of @ref UART_Parity
                                               @note When parity is enabled, the computed parity is inserted
                                                     at the MSB position of the transmitted data (9th bit when
                                                     the word length is set to 9 data bits; 8th bit when the
                                                     word length is set to 8 data bits). */
    
      uint32_t Mode;                      /*!< Specifies whether the Receive or Transmit mode is enabled or disabled.
                                               This parameter can be a value of @ref UART_Mode */
    
      uint32_t HwFlowCtl;                 /*!< Specifies whether the hardware flow control mode is enabled or disabled.
                                               This parameter can be a value of @ref UART_Hardware_Flow_Control */
    
      uint32_t OverSampling;              /*!< Specifies whether the Over sampling 8 is enabled or disabled, to achieve higher speed (up to fPCLK/8).
                                               This parameter can be a value of @ref UART_Over_Sampling */
    } UART_InitTypeDef;
    

    BaudRate: 为串口波特率,用来确定串口通信的速率 。
    WordLength: 为字长,可以设置8位字长或者9位字长。我们设置为 8 位字长数据格式 UART_WORDLENGTH_8B。
    StopBits: 为停止位,可以设置为1个停止位或者2个停止位。我们设置为1个停止位 UART_STOPBITS_1。
    Parity: 为是否需要奇偶校验,我们设定为无奇偶校 UART_PARITY_NONE。
    Mode: 为串口模式,可以设置为只收模式、只发模式或者收发模式。我们设置为全双工收发模式 UART_MODE_TX_RX。
    HwFlowCtl: 为是否支持硬件流控制,我们设置为无硬件流控制 UART_HWCONTROL_NONE。
    pTxBuffPtr, TxXferSize 和 TxXferCount 三个变量分别用来设置串口发送的数据缓存指针,发送的数据量和还剩余的要发送的数据量。
    pRxBuffPtr, RxXferSize 和RxXferCount 三个变量则是用来设置接收的数据缓存指针,接收的最大数据量以及还剩余的要接收的数据量。
    hdmatx 和 hdmarx 是串口 DMA 相关的变量,指向 DMA 句柄。

    函数 HAL_UART_Init 使用的一般格式为:

    UART_HandleTypeDef UART1_Handler; //UART 句柄
    UART1_Handler.Instance=USART1; //USART1
    UART1_Handler.Init.BaudRate=115200; //波特率
    UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为 8 位格式
    UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
    UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
    UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
    UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
    HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能 UART1
    

    需要说明的是,函数HAL_UART_Init内部会调用串口使能函数使能相应串口,所以调用了该函数之后我们就不需要重复使能串口了。当然,HAL库也提供了具体的串口使能和失能的方法,具体使用方法如下:

    __HAL_UART_ENABLE(handler); //使能句柄 handler 指定的串口
    __HAL_UART_DISABLE(handler); //关闭句柄 handler 指定的串口
    

    这里还需要注意,串口作为一个重要的外设,在调用的初始化函数HAL_UART_Init内部,会先调用MSP初始化回调函数进行MCU相关的初始化,函数为:

    void HAL_UART_MspInit(UART_HandleTypeDef *huart);
    

    我们的程序中,只需要重写该函数即可。一般情况下,该函数内部用来编写IO初始化,时钟使能以及NVIC配置。

    2、使能串口和GPIO时钟

    我们要使用串口,所以我们必须使能串口时钟和使用到的GPIO口的时钟。具体方法:

    __HAL_RCC_USART1_CLK_ENABLE(); //使能 USART1 时钟
    __HAL_RCC_GPIOA_CLK_ENABLE(); //使能 GPIOA 时钟
    

    3、GPIO口初始化设置,以及复位映射配置

    参看:STM32F4 HAL库开发 – GPIO

    4、开启串口相关中断,配置串口中断优先级

    __HAL_UART_ENABLE_IT 使能串口中断的标识符。
    示例:

    __HAL_UART_ENABLE_IT(huart,UART_IT_RXNE); //开启接收完成中断
    

    第一个参数:为串口句柄,类型为UART_HandleTypeDef 结构体类型。
    第二个参数:为我们要开启的中断类型值。

    #define UART_IT_PE                       ((uint32_t)(UART_CR1_REG_INDEX << 28U | USART_CR1_PEIE))
    #define UART_IT_TXE                      ((uint32_t)(UART_CR1_REG_INDEX << 28U | USART_CR1_TXEIE))
    #define UART_IT_TC                       ((uint32_t)(UART_CR1_REG_INDEX << 28U | USART_CR1_TCIE))
    #define UART_IT_RXNE                     ((uint32_t)(UART_CR1_REG_INDEX << 28U | USART_CR1_RXNEIE))
    #define UART_IT_IDLE                     ((uint32_t)(UART_CR1_REG_INDEX << 28U | USART_CR1_IDLEIE))
    

    有开启中断就有关闭中断,操作方法为:

    __HAL_UART_DISABLE_IT(huart,UART_IT_RXNE); //关闭接收完成中断
    

    对于中断优先级配置,参考方法为:

    HAL_NVIC_EnableIRQ(USART1_IRQn); //使能 USART1 中断通道
    HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级 3,子优先级 3
    

    5、编写中断服务函数

    串口1中断服务函数为:

    void USART1_IRQHandler(void) ;
    

    当发生中断的时候,程序就会执行中断服务函数。然后我们在中断服务函数中编写相应的逻辑代码即可。

    6、串口数据接收和发送

    HAL 库操作 USART_DR 寄存器发送数据的函数是:

    HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart,
    uint8_t *pData, uint16_t Size, uint32_t Timeout);
    

    通过该函数向串口寄存器 USART_DR 写入一个数据。

    HAL 库操作 USART_DR 寄存器读取串口接收到的数据的函数是:

    HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart,
    uint8_t *pData, uint16_t Size, uint32_t Timeout);
    

    通过该函数可以读取串口接受到的数据。

    7、DMA配置

    参看:STM32F4 HAL库开发 – DMA

    三、STM32CubeMX 配置串口

    1、配置

    打开 Pinout选项卡界面,左侧依次进入 Categories->Connectivity->USART1 配置栏。
    在这里插入图片描述
    配置栏有2个选项
    **Mode:**用来设置串口的模式或者关闭串口
    在这里插入图片描述
    Asynchronous:异步
    Synchronous:同步
    Single Wire(Half-Duplex):单线(半双工)
    Multiprocessor Communication:多处理器通信

    这里我们要开启串口的异步模式,选择Mode值为 Asynchronous。

    Hardware Flow Control(RS232): 用来开启/关闭串口的硬件控制流。
    该选项只有在Mode选项值为 Asynchronous(异步通信)模式的前提下才有效。

    选择收发引脚:
    在这里插入图片描述
    在GPIO->USART里就可以看到新添加的USART1_TX和USART1_RX。
    在这里插入图片描述
    配置栏有一下几个选项:
    GPIO mode:
    Altemate Fuction Push Pull (复用推挽)
    GPIO Pull-up/Pull-down:
    Pull-up (上拉)
    Maximum output speed:
    Very High(速度)
    在这里插入图片描述
    再次打开进入 Categories->Connectivity->USART1 配置栏
    在这里插入图片描述
    下半部分可以看到:
    Parameter Settings:
    包含波特率、数据位、奇偶校验位、停止位、使能收发模式、过采样等设置。
    我们将 USART1 配置为:波特率 115200, 8 位字长模式,无奇偶校验位, 1 个停止位,发送/接收均开启。
    在这里插入图片描述
    DMA settings:
    可以点击Add,添加USART1_RX 和 USART1_TX。
    可以选择,Priority 优先级。
    在 DMA Request Settings,也可以进行设置。
    在这里插入图片描述
    NVIC Settings:
    配置完DMA后,在打开NVIC Settings,就可以看到 DMA的使能是选中的。
    这里我们勾选 USART1 global interrupt 的中断使能。
    在这里插入图片描述

    2、生成源码

    MX_USART1_UART_Init 函数

    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;
      if (HAL_UART_Init(&huart1) != HAL_OK)
      {
        Error_Handler();
      }
    
    }
    

    HAL_UART_MspInit 函数

    void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
    {
      GPIO_InitTypeDef GPIO_InitStruct = {0};
      if(uartHandle->Instance==USART1)
      {
      /* USER CODE BEGIN USART1_MspInit 0 */
    
      /* USER CODE END USART1_MspInit 0 */
        /* USART1 clock enable */
        __HAL_RCC_USART1_CLK_ENABLE();
    
        __HAL_RCC_GPIOB_CLK_ENABLE();
        /**USART1 GPIO Configuration
        PB6     ------> USART1_TX
        PB7     ------> USART1_RX
        */
        GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
        /* USART1 DMA Init */
        /* USART1_RX Init */
        hdma_usart1_rx.Instance = DMA2_Stream2;
        hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;
        hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;