3. STM32串口外设回顾:USART/UART配置

在实现串口DMA双缓冲连续采样之前,必须彻底理解STM32的USART/UART外设核心配置。串口不仅是数据收发的物理通道,更是DMA传输的触发源和中断控制点。本节将系统回顾波特率、数据位、停止位等基础参数,并重点分析中断与DMA使能的协同关系。

关键认知: USART(通用同步异步收发器)与UART(通用异步收发器)的区别在于同步时钟引脚。在DMA双缓冲场景中,我们通常使用UART模式(异步),仅需TX/RX两根信号线。

3.1 USART/UART核心参数配置

STM32的串口外设通过一组寄存器控制通信格式。以下是最关键的三个参数组:

参数 寄存器位域 典型值 说明
波特率 USART_BRR (DIV_Mantissa + DIV_Fraction) 115200 / 921600 由外设时钟和分频系数计算:
Tx/Rx时钟 = f_CLK / (16 × USARTDIV)
数据位 USART_CR1 (M位) 8位 / 9位 M=0 → 8位数据;M=1 → 9位数据。DMA传输通常使用8位
停止位 USART_CR2 (STOP位) 1位 / 0.5位 / 2位 1位停止位最常用,兼容性好
波特率计算示例: 若外设时钟为72MHz,目标波特率115200,则USARTDIV = 72000000 / (16 × 115200) ≈ 39.0625。整数部分39写入DIV_Mantissa,小数部分0.0625 × 16 = 1写入DIV_Fraction。
USART数据帧结构与参数配置 数据帧结构(异步模式) 起始位(1) | 数据位(8/9) | 校验位(可选) | 停止位(0.5/1/2) 波特率 USART_BRR寄存器 f_CLK / (16 × USARTDIV) 数据位/停止位 CR1.M / CR2.STOP 8N1 / 8E1 / 9N1 中断/DMA使能 CR1.RXNEIE / CR3.DMAT CR3.DMAR / CR1.TCIE 配置顺序要求 先配置波特率、数据位、停止位 → 再使能DMA请求 → 最后使能USART

3.2 中断使能:逐字节处理与DMA的边界

USART中断是串口通信的“事件通知机制”。在DMA双缓冲方案中,中断的角色从“逐字节搬运”转变为“缓冲区切换与错误处理”。

  • RXNEIE(接收缓冲区非空中断使能): 每收到一个字节触发一次中断。在纯中断模式中用于逐字节读取,在DMA模式中通常关闭,避免与DMA传输冲突。
  • TCIE(发送完成中断使能): 当数据帧完全发送(包括停止位)后触发。DMA发送时可用于判断最后一帧是否发送完毕。
  • IDLEIE(空闲总线中断使能): 检测到RX线空闲超过一个帧长度时触发。这是DMA双缓冲方案中最关键的接收中断,用于判断一帧数据接收完成。
  • PE/NE/FE/ORE(错误中断): 校验错误、噪声错误、帧错误、溢出错误。DMA高速传输时必须使能,防止错误累积导致数据错乱。
常见陷阱: 在使能USART的DMA接收(DMAR)后,如果同时使能RXNEIE,则每个字节仍会触发中断,导致CPU频繁响应中断,完全失去DMA的“解放CPU”优势。正确的做法是:DMA接收时关闭RXNEIE,使用IDLE中断或DMA传输完成中断来通知数据就绪。

3.3 DMA使能:USART与DMA控制器的握手

USART外设内部有独立的DMA请求线路,通过USART_CR3寄存器控制:

CR3位域 功能 DMA通道映射 双缓冲中的作用
DMAT (位7) 发送DMA使能 USART1_TX → DMA2_Stream7 (STM32F4) 自动从内存搬运数据到DR寄存器发送
DMAR (位6) 接收DMA使能 USART1_RX → DMA2_Stream5 (STM32F4) 自动从DR寄存器搬运数据到内存缓冲区

当DMAR位被置1后,USART接收数据寄存器(DR)每收到一个有效字节,会自动向DMA控制器发送一个请求信号。DMA控制器响应请求,将DR中的数据搬运到预先配置的内存地址,整个过程无需CPU干预。

USART与DMA握手流程(接收方向) USART外设 RX引脚接收数据 DR寄存器存储 CR3.DMAR = 1 DMA控制器 接收DMA请求 从DR读取数据 写入内存缓冲区 内存缓冲区 Buffer A / Buffer B 双缓冲交替写入 DMA_MEM2MEM = 0 DMA请求 数据搬运 DMA传输完成中断 / USART IDLE中断 → 通知CPU切换缓冲区

3.4 典型配置代码框架(HAL库)

以下代码展示了USART+DMA接收的初始化流程,注意中断与DMA使能的配合:

// USART与DMA接收初始化(HAL库风格)
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;
    HAL_UART_Init(&huart1);

    // 使能IDLE中断(用于帧结束判断)
    __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
    // 关闭RXNE中断(避免与DMA冲突)
    __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);
    // 使能错误中断
    __HAL_UART_ENABLE_IT(&huart1, UART_IT_ERR);
}

void MX_DMA_Init(void)
{
    // DMA接收流配置:外设到内存,循环模式
    hdma_usart1_rx.Instance = DMA2_Stream5;
    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;
    hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;        // 循环模式支持双缓冲
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    HAL_DMA_Init(&hdma_usart1_rx);

    // 关联DMA到USART句柄
    __HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx);

    // 使能DMA传输完成中断(用于缓冲区切换)
    HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
}
配置要点:
  1. DMA模式必须设置为CIRCULAR(循环模式),这是双缓冲连续采样的基础。
  2. USART的DMAR位在HAL_UART_Init()内部自动使能,无需手动操作CR3寄存器。
  3. IDLE中断和DMA传输完成中断是双缓冲切换的“双触发源”,后续章节会详细讲解。

3.5 常见配置错误与排查

错误现象 可能原因 解决方案
DMA不搬运数据 DMAR位未使能;DMA通道映射错误 检查CR3寄存器;核对参考手册DMA映射表
数据错位/丢字节 数据位/停止位不匹配;波特率误差过大 使用示波器测量波特率;检查双方配置一致
CPU占用率仍然很高 RXNEIE未关闭;DMA未配置为循环模式 确认关闭RXNE中断;设置DMA_MODE_CIRCULAR
IDLE中断不触发 IDLEIE未使能;RX线持续有数据 使能IDLE中断;检查发送端是否发送完整帧