STM32串口DMA与空闲中断实现不定长数据接收 - 第3节
3.1 STM32 串口模块内部结构
STM32 的 USART(通用同步异步收发器)是一个高度可配置的串行通信外设。理解其内部数据路径和控制逻辑,是正确配置 DMA 与空闲中断的基础。下图展示了 USART 模块的核心数据流与关键寄存器接口。
3.1.1 核心数据路径
- 发送路径:CPU 或 DMA 将数据写入 TDR(发送数据寄存器),硬件自动将数据转移到 发送移位寄存器,按设定的波特率从 TX 引脚逐位发出。
- 接收路径:RX 引脚上的串行数据被 接收移位寄存器 逐位采样,完成一帧后并行转移到 RDR(接收数据寄存器),等待 CPU 或 DMA 读取。
- 双缓冲机制:TDR 和 RDR 均为双缓冲结构,允许在移位寄存器工作时,下一个数据已在数据寄存器中准备就绪,提高吞吐量。
3.1.2 控制与状态单元
- 控制寄存器组 (CR1/CR2/CR3):配置字长、停止位、奇偶校验、使能 USART、使能中断、使能 DMA 等。
- 状态寄存器 (SR/ISR):反映发送完成 (TC)、发送数据寄存器空 (TXE)、接收数据寄存器非空 (RXNE)、空闲总线 (IDLE) 等标志。
- 波特率发生器 (BRR):根据系统时钟和 USARTDIV 分频系数,产生精确的位时钟。
关键点:DMA 传输直接与 TDR/RDR 交互,无需 CPU 逐字节搬运;空闲中断 (IDLE) 由 RX 线电平状态检测,是判断不定长数据帧结束的核心机制。
3.2 串口相关寄存器概述
掌握以下关键寄存器是编写串口驱动的基础。下表列出了最常用的寄存器及其功能描述。
| 寄存器名称 | 偏移地址 | 核心功能 | 关键位/域 |
|---|---|---|---|
| USART_SR | 0x00 | 状态寄存器 | RXNE(5)、TC(6)、TXE(7)、IDLE(4) |
| USART_DR | 0x04 | 数据寄存器 | DR[8:0](9位字长时) |
| USART_BRR | 0x08 | 波特率寄存器 | DIV_Mantissa[15:4]、DIV_Fraction[3:0] |
| USART_CR1 | 0x0C | 控制寄存器 1 | UE(13)、M(12)、PCE(10)、PS(9)、TE(3)、RE(2)、RXNEIE(5)、IDLEIE(4) |
| USART_CR2 | 0x10 | 控制寄存器 2 | STOP[13:12]、CLKEN(11)、CPOL(10)、CPHA(9) |
| USART_CR3 | 0x14 | 控制寄存器 3 | DMAT(7)、DMAR(6)、CTSIE(10)、CTSE(9) |
3.2.1 关键位详解
- RXNE (Read Data Register Not Empty):接收数据寄存器非空,读取 DR 后硬件自动清零。可触发中断或 DMA 请求。
- IDLE (Idle Line Detected):检测到空闲总线(RX 线保持高电平超过一帧时间),是本次课程实现不定长接收的核心标志。
- TC (Transmission Complete):发送完成,表示移位寄存器与 TDR 均空,常用于 RS485 方向切换。
- DMAT/DMAR:分别使能发送 DMA 和接收 DMA,开启后 USART 自动向 DMA 控制器发出请求。
常见误区:IDLE 标志在置位后不会自动清零,必须在中断服务程序中通过软件序列清零(先读 SR,再读 DR)。否则将导致空闲中断反复触发。
3.3 串口初始化流程
标准串口初始化遵循“时钟使能 → GPIO 配置 → USART 参数设置 → 中断/DMA 配置 → 使能 USART”的步骤。下图展示了完整的初始化流程。
3.3.1 初始化代码示例
以下代码基于 STM32 HAL 库,展示了标准串口初始化流程,包含空闲中断使能与 DMA 配置。
void MX_USART1_UART_Init(void)
{
// 1. 使能时钟
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
// 2. GPIO 配置:PA9-TX, PA10-RX
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 3. USART 参数配置
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;
HAL_UART_Init(&huart1);
// 4. 使能空闲中断 (HAL 库需直接操作寄存器)
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
// 5. 配置接收 DMA (DMAR 位在 HAL_UART_Init 中已设置)
// 此处省略 DMA 句柄配置,将在 DMA 章节详述
// 6. 使能 USART (HAL_UART_Init 已自动设置 UE=1)
// 7. 使能 DMA 流 (在 DMA 初始化中完成)
}
3.3.2 初始化步骤说明
| 步骤 | 操作 | 注意事项 |
|---|---|---|
| 1 | 使能 USART、GPIO、DMA 时钟 | 遗漏时钟使能将导致外设无响应 |
| 2 | 配置 TX/RX 为复用推挽输出/输入 | 需查阅数据手册确认复用功能编号 (AF7 等) |
| 3 | 设置波特率、字长、停止位、校验 | 波特率误差应 < 3%,否则通信可能失败 |
| 4 | 使能空闲中断 (IDLEIE=1) | 必须在使能 USART 之前设置,否则可能漏掉首个空闲帧 |
| 5 | 使能接收 DMA (DMAR=1) | 使能后 USART 自动向 DMA 发出请求 |
| 6 | 使能 USART (UE=1) | 所有配置完成后最后使能 |
| 7 | 使能 DMA 流/通道 | USART 使能后再启动 DMA,避免请求丢失 |
初始化顺序原则:先配置外设参数,再使能中断/DMA 功能,最后使能外设本身。违反此顺序可能导致意外中断触发或 DMA 请求丢失。
3.4 本章小结
- STM32 USART 模块包含独立的数据路径(TDR/RDR + 移位寄存器)和统一的控制状态单元。
- 空闲中断 (IDLE) 和 DMA 请求是本次课程实现不定长接收的两大硬件基础。
- 初始化流程必须遵循“时钟 → GPIO → 参数 → 中断/DMA → 使能”的固定顺序。
- IDLE 标志的软件清零序列(读 SR → 读 DR)是避免中断风暴的关键。