STM32串口DMA与空闲中断实现不定长数据接收 - 第3节

3.1 STM32 串口模块内部结构

STM32 的 USART(通用同步异步收发器)是一个高度可配置的串行通信外设。理解其内部数据路径和控制逻辑,是正确配置 DMA 与空闲中断的基础。下图展示了 USART 模块的核心数据流与关键寄存器接口。

STM32 USART 模块内部结构图 STM32 USART 模块内部结构图 发送数据寄存器 (TDR) 发送移位寄存器 TX 引脚 接收数据寄存器 (RDR) 接收移位寄存器 RX 引脚 控制寄存器 (CR1/CR2/CR3) 状态寄存器 (SR/ISR) 波特率发生器 (BRR) 中断控制 / DMA 请求 AHB/APB 总线接口 说明 TDR/RDR 与 DMA 直接交互 空闲中断由 RX 线电平检测触发

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”的步骤。下图展示了完整的初始化流程。

STM32 USART 初始化流程图 STM32 USART 初始化流程图 1. 使能 USART 与 GPIO 时钟 2. 配置 TX/RX 引脚为复用功能 3. 设置波特率、字长、停止位 4. 使能空闲中断 (IDLEIE) 5. 配置接收 DMA (DMAR) 6. 使能 USART (UE=1) 7. 使能 DMA 流/通道 注意:步骤 4 和 5 的顺序可互换,但必须在使能 USART 之前完成配置 使能 DMA 流应在 USART 使能之后,避免 DMA 请求丢失

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)是避免中断风暴的关键。