第5章 STM32F4串口简介

第五章 STM32F4串口简介

1. 导入

STM32F4 的串口资源相当丰富的,功能也相当强劲。 ALIENTEK 探索者 STM32F4 开发板所使用的 STM32F407ZGT6 最多可提供 6 路串口,有分数波特率发生器、支持同步单线通信和半双工单线通讯、支持 LIN、 支持调制解调器操作、 智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA 等。

接下来我们将主要从库函数操作层面结合寄存器的描述,告诉你如何设置串口,以达到我们最基本的通信功能。

我们将实现利用串口 1 不停的打印信息到电脑上,同时接收从串口发过来的数据,把发送过来的数据直接送回给电脑。 探索者 STM32F4 开发板板载了 1 个 USB 串口和 2 个 RS232 串口,我们本章介绍的是通过 USB 串口和电脑通信。

端口复用功能已经讲解过,对于复用功能的 IO,我们首先要使能 GPIO 时钟,然后使能相应的外设时钟,同时要把 GPIO 模式设置为复用。这些准备工作做完之后,剩下的当然是串口参数的初始化设置,包括波特率,停止位等等参数。在设置完成只能接下来就是使能串口,这很容易理解。

2. 串口使用基本步骤

  1. 串口时钟使能, GPIO 时钟使能。

  2. 设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。

  3. GPIO 初始化设置:要设置模式为复用功能。

  4. 串口参数初始化:设置波特率,字长,奇偶校验等参数。

  5. 开启中断并且初始化 NVIC,使能中断(如果需要开启中断才需要这个步骤)。

  6. 使能串口。

  7. 编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)。

下面, 我们就简单介绍下这几个与串口基本配置直接相关的几个 HAL 库函数。这些函数和定义主要分布在 stm32f4xx_hal_usart.h 和 stm32f4xx_hal_usart.c 文件中。

3. 相关HAL库函数

3.1 串口时钟和GPIO时钟使能

串口作为 STM32 的一个外设, HAL 库为其配置了串口初始化函数。接下来我们看看串口初始化函数 HAL_UART_Init 相关知识,定义如下:

HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);

该函数只有一个入口参数 huart,为 UART_HandleTypeDef 结构体指针类型,我们俗称其为串口句柄,它的使用会贯穿整个串口程序。

一般情况下,我们会定义一个 UART_HandleTypeDef结构体类型全局变量,然后初始化各个成员变量。接下来我们看看结构体 UART_HandleTypeDef的定义:

typedef struct
{            
    USART_TypeDef *Instance;
    UART_InitTypeDef Init;
    uint8_t *pTxBuffPtr;
    uint16_t TxXferSize;
    uint16_t TxXferCount;
    uint8_t *pRxBuffPtr;
    uint16_t RxXferSize;
    uint16_t RxXferCount;
    DMA_HandleTypeDef *hdmatx;
    DMA_HandleTypeDef *hdmarx;
    HAL_LockTypeDef Lock;
    __IO HAL_UART_StateTypeDef State;
    __IO uint32_t ErrorCode;
}UART_HandleTypeDef;

  1. USART_TypeDef *Instance:
  • 指向具体的 UART 外设实例,例如 USART1USART2 等,用于指明你要使用哪个 UART 硬件。
  1. UART_InitTypeDef Init:
  • 包含 UART 初始化参数的结构体,通常包括波特率、数据位、停止位、校验位等配置。这些参数在调用初始化函数时会被使用。
  1. uint8_t *pTxBuffPtr:
  • 指向用于发送数据的缓冲区。在 UART 发送数据时,UART 驱动会从这个缓冲区读取数据。
  1. uint16_t TxXferSize:
  • 需要发送的数据字节总数,表示准备发送的字节长度。
  1. uint16_t TxXferCount:
  • 当前已发送的数据字节数,通常在发送过程中更新,以便跟踪发送进度。
  1. uint8_t *pRxBuffPtr:
  • 指向用于接收数据的缓冲区。UART 驱动将把接收到的数据存储在这个缓冲区中。
  1. uint16_t RxXferSize:
  • 需要接收的数据字节总数,表示期望接收到的字节长度。
  1. uint16_t RxXferCount:
  • 当前已接收的数据字节数,通常在接收过程中更新,以便跟踪接收进度。
  1. DMA_HandleTypeDef *hdmatx:
  • 指向与 UART 发送相关的 DMA 句柄,如果使用 DMA 模式进行发送,相关的 DMA 配置将通过这个句柄来管理。
  1. DMA_HandleTypeDef *hdmarx:
  • 指向与 UART 接收相关的 DMA 句柄,如果使用 DMA 模式进行接收,相关的 DMA 配置将通过这个句柄来管理。
  1. HAL_LockTypeDef Lock:
  • 用于保护结构体的并发访问,确保在多个任务或中断情况下对 UART 资源的安全访问。
  1. __IO HAL_UART_StateTypeDef State:
  • 表示 UART 的当前状态,如空闲、忙碌、错误等。通过这个状态可以判断 UART 目前的工作情况。
  1. __IO uint32_t ErrorCode:
  • 用于存储 UART 操作中的错误代码,方便调试和错误处理。

该结构体成员变量非常多,一般情况下载调用函数 HAL_UART_Init 对串口进行初始化的时候,我们只需要先设置 Instance 和 Init 两个成员变量的值。接下来我们依次解释一下各个成员变量的含义。

3.1.1 INSTANCE

Instance 是 USART_TypeDef 结构体指针类型变量,它是执行寄存器基地址,实际上这个基地址 HAL 库已经定义好了,如果是串口 1,取值为 USART1 即可。

3.1.2 INIT

Init 是 UART_InitTypeDef 结构体类型变量,它是用来设置串口的各个参数,包括波特率,停止位等,它的使用方法非常简单。 UART_InitTypeDef 结构体定义如下:

typedef struct
{    
    uint32_t BaudRate;    // 波特率
    uint32_t WordLength;  // 字长
    uint32_t StopBits;    // 停止位
    uint32_t Parity;      // 奇偶校验
    uint32_t Mode;        // 收/发模式设置
    uint32_t HwFlowCtl;   // 硬件流设置
    uint32_t OverSampling;// 过采样设置
}UART_InitTypeDef
  1. BaudRate (波特率)
  • 意义:通信的速度,通常以比特每秒 (bps) 表示。常见值包括 9600, 115200 等。
  • 示例:如果设置 BaudRate = 115200,则串口通信速度为 115200 bps。
  1. WordLength (字长)
  • 意义:每个数据字节的位数。常见选项包括 8 位和 9 位。
  • 示例:如果设置 WordLength = 8,则每个字节将由 8 位组成。
  1. StopBits (停止位)
  • 意义:数据传输结束后发送的位数,用于指示一帧数据的结束。可以设置为 1、1.5 或 2。
  • 示例:如果设置 StopBits = 1,则每帧数据结束时会有一个停止位。
  1. Parity (奇偶校验)
  • 意义:用于检测传输错误的一种方法。选项通常为 None (无)、Even (偶校验) 和 Odd (奇校验)。
  • 示例:如果设置 Parity = None,则不使用奇偶校验。
  1. Mode (收/发模式设置)
  • 意义:设置 UART 的工作模式,通常包括接收 (RX)、发送 (TX) 或双向 (TX/RX)。
  • 示例:如果设置 Mode = TX_RX,则 UART 可以同时发送和接收数据。
  1. HwFlowCtl (硬件流控制)
  • 意义:用于流控制的硬件支持,常见选项包括 None (无)、RTS/CTS (请求发送/清除发送)。
  • 示例:如果设置 HwFlowCtl = RTS_CTS,则启用 RTS/CTS 硬件流控制功能。
  1. OverSampling (过采样设置)
  • 意义:选择数据过采样的方式,通常可以设置为 16 或 8,以提高抗干扰能力。
  • 示例:如果设置 OverSampling = 16,则表示使用 16 倍过采样。

以下是一个使用 UART_InitTypeDef 结构体初始化 UART 的示例代码:

UART_InitTypeDef UART_InitStruct;
// 设置波特率为 115200
UART_InitStruct.BaudRate = 115200;
// 设置字长为 8 位
UART_InitStruct.WordLength = 8;
// 设置停止位为 1 位
UART_InitStruct.StopBits = 1;
// 不使用奇偶校验
UART_InitStruct.Parity = 0; // 0 表示 None
// 设置为双向模式
UART_InitStruct.Mode = TX_RX; 
// 使用 RTS/CTS 硬件流控制
UART_InitStruct.HwFlowCtl = RTS_CTS;
// 设置过采样为 16
UART_InitStruct.OverSampling = 16;
// 调用 UART 初始化函数
UART_Init(&UART_InitStruct);

3.1.3 接发数据相关结构体成员

pTxBuffPtr, TxXferSize 和 TxXferCount 三个变量分别用来设置串口发送的数据缓存指针,发送的数据量和还剩余的要发送的数据量。而接下来的三个变量 pRxBuffPtr, RxXferSize 和RxXferCount 则是用来设置接收的数据缓存指针,接收的最大数据量以及还剩余的要接收的数据量。

3.1.4 HDMATX & HDMARX

hdmatx 和 hdmarx 是串口 DMA 相关的变量,指向 DMA 句柄,等到后面学习到DMA再讲解

3.1.5 错误处理结构体成员

其他的三个变量就是一些 HAL 库处理过程状态标志位和串口通信的错误码。

3.1.6 完整配置示例

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 配置。

3.2 GPIO初始化设置(速度,上下拉等)以及复用映射配置

我们在跑马灯实验中讲解过,在 HAL 库中 IO 口初始化参数设置和复用映射配置是在函数HAL_GPIO_Init 中一次性完成的。这里大家只需要注意,我们要复用 PA9 和 PA10 为串口发送接收相关引脚,我们需要配置 IO 口为复用,同时复用映射到串口 1。配置源码如下:

GPIO_InitTypeDef GPIO_Initure;
GPIO_Initure.Pin = GPIO_PIN_9|GPIO_PIN_10; // PA9/PA10
GPIO_Initure.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_Initure.Pull = GPIO_PULLUP; // 上拉
GPIO_Initure.Speed = GPIO_SPEED_FAST; // 高速
GPIO_Initure.Alternate = GPIO_AF7_USART1; // 复用为 USART1
HAL_GPIO_Init(GPIOA, &GPIO_Initure); // 初始化 PA9/PA10

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

HAL 库中定义了一个使能串口中断的标识符__HAL_UART_ENABLE_IT,大家可以把它当一个函数来使用,具体定义请参考 HAL 库文件 stm32f4xx_hal_uart.h 中该标识符定义。例如我们要使能接收完成中断,方法如下:

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

第一个参数为我们讲解的串口句柄,类型为 UART_HandleTypeDef 结构体类型。第二个参数为我们要开启的中断类型值,可选值在头文件 stm32f4xx_hal_uart.h 中有宏定义。

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

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

对于中断优先级配置,方法就非常简单

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

3.4 编写中断服务函数

串口1中断服务函数为:

void USART1_IRQHandler(void)
{
 // 用户实现   
}

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

3.5 串口数据接收和发送

STM32F4 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是一个双寄存器,包含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。 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);

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

完整的配置如下:

#include "main.h"
// 定义串口句柄
UART_HandleTypeDef huart2; // 根据你选择的 USART 修改
// 函数声明
void SystemClock_Config(void); // 系统时钟配置函数
static void MX_GPIO_Init(void); // GPIO初始化函数
static void MX_USART2_UART_Init(void); // USART2初始化函数

int main(void) 
{
    // 初始化硬件抽象层库
    HAL_Init();    
    // 配置系统时钟
    SystemClock_Config();
    // 初始化GPIO
    MX_GPIO_Init();
    // 初始化USART2
    MX_USART2_UART_Init();
    // 示例数据,用于发送
    uint8_t txData[] = "Hello, STM32!";
    // 接收缓冲区
    uint8_t rxData[20];

    while (1) 
    {
        // 发送数据,通过 USART2 发送 txData
        HAL_UART_Transmit(&huart2, txData, sizeof(txData), HAL_MAX_DELAY);
        // 接收数据,通过 USART2 接收并存储到 rxData 中
        HAL_UART_Receive(&huart2, rxData, sizeof(rxData), HAL_MAX_DELAY);    
        // 可以在此处理接收到的数据,比如将其打印到串口等
    }
}

// USART2 初始化函数
static void MX_USART2_UART_Init(void) 
{
    // 配置 USART2 的实例
    huart2.Instance = USART2;
    // 设置波特率为 115200
    huart2.Init.BaudRate = 115200;
    // 设置数据位为 8 位
    huart2.Init.WordLength = UART_WORDLENGTH_8B;
    // 设置停止位为 1 位
    huart2.Init.StopBits = UART_STOPBITS_1;
    // 设置无校验位
    huart2.Init.Parity = UART_PARITY_NONE;
    // 设置模式为发送和接收
    huart2.Init.Mode = UART_MODE_TX_RX;
    // 设置硬件流控制为无
    huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    // 设置过采样为 16
    huart2.Init.OverSampling = UART_OVERSAMPLING_16;
    // 初始化 USART2
    HAL_UART_Init(&huart2);
}

// GPIO 初始化函数(如果需要可配置引脚)
static void MX_GPIO_Init(void) {
    // 此处根据具体需要配置 GPIO 引脚,如 LED、按钮等
}

// 系统时钟配置函数(根据你的系统需求配置时钟)
void SystemClock_Config(void) {
    // 此处添加配置代码,根据具体使用的时钟源进行配置
}

2024.9.27 第一次修订,后期不再维护

2024.12.21 简化内容

posted @ 2024-09-27 22:46  hazy1k  阅读(61)  评论(0编辑  收藏  举报