4. UART 寄存器编程:数据寄存器(DR)、状态寄存器(SR)、控制寄存器(CR)、波特率分频寄存器(BRR)
UART 外设的寄存器是驱动开发者与硬件交互的唯一接口。本章深入剖析 STM32 系列(以 USART2 为例)最核心的四个寄存器:数据寄存器(DR)、状态寄存器(SR)、控制寄存器(CR) 和 波特率分频寄存器(BRR)。理解每个寄存器的位域含义、读写时序和配置顺序,是编写稳定、高效 UART 驱动的基础。
寄存器编程原则: 所有寄存器操作应遵循“读-改-写”模式,避免破坏未使用的位域。对于 CR 类寄存器,建议在修改前先读取当前值,再按位或/与后写回。
4.1 寄存器概览与地址映射
下表列出了 USART2 的基地址(0x40004400)及四个关键寄存器的偏移地址。不同系列 MCU 的基地址可能不同,但寄存器偏移和位域定义高度兼容。
| 寄存器名称 | 偏移地址 | 全称 | 复位值 | 宽度 |
|---|---|---|---|---|
| SR | 0x00 | Status Register | 0xC0 | 32 位 |
| DR | 0x04 | Data Register | 0xXXXX | 32 位 |
| BRR | 0x08 | Baud Rate Register | 0x0000 | 32 位 |
| CR1 | 0x0C | Control Register 1 | 0x0000 | 32 位 |
| CR2 | 0x10 | Control Register 2 | 0x0000 | 32 位 |
| CR3 | 0x14 | Control Register 3 | 0x0000 | 32 位 |
关键提示: 虽然 DR 寄存器物理宽度为 32 位,但实际有效数据位只有低 9 位(8 位数据 + 1 位校验/第9位)。写入时高 23 位被硬件忽略,读取时高 23 位返回 0。
4.2 数据寄存器(DR)—— 收发数据的核心通道
DR 寄存器是 CPU 与 UART 移位器之间的数据缓冲区。发送时,CPU 将待发送字节写入 DR;接收时,CPU 从 DR 读取已接收字节。DR 内部实际由两个物理缓冲区组成:发送数据寄存器(TDR)和接收数据寄存器(RDR),但软件层面共用同一个地址。
4.2.1 DR 位域定义
| 位域 | 名称 | 类型 | 描述 |
|---|---|---|---|
| [31:9] | Reserved | RO | 保留位,读出为 0 |
| [8:0] | DR[8:0] | RW | 数据值。8 位模式使用 [7:0];9 位模式使用 [8:0] |
4.2.2 发送与接收操作流程
- 发送流程: 等待 SR.TXE(发送数据寄存器空)置 1 → 写入 DR → 硬件自动将数据移入移位寄存器 → TXE 再次置 1。
- 接收流程: 等待 SR.RXNE(接收数据寄存器非空)置 1 → 读取 DR → 硬件自动清除 RXNE 标志。
// 发送一个字节(轮询模式)
void uart_send_byte(USART_TypeDef *USARTx, uint8_t data) {
// 等待发送缓冲区空
while (!(USARTx->SR & USART_SR_TXE));
// 写入数据寄存器
USARTx->DR = (data & 0xFF);
}
// 接收一个字节(轮询模式)
uint8_t uart_receive_byte(USART_TypeDef *USARTx) {
// 等待接收数据就绪
while (!(USARTx->SR & USART_SR_RXNE));
// 读取数据寄存器
return (uint8_t)(USARTx->DR & 0xFF);
}
常见陷阱: 读取 DR 后,RXNE 标志由硬件自动清除。但如果连续读取 DR 两次,第二次读取可能得到旧数据或 0。务必在每次 RXNE 置位后只读取一次 DR。
4.3 状态寄存器(SR)—— 通信状态的实时窗口
SR 寄存器提供 UART 模块的实时状态,包括发送/接收缓冲区状态、帧错误、溢出错误、校验错误等。驱动开发中,SR 是轮询模式和中断模式的核心判断依据。
4.3.1 SR 关键位域
| 位 | 名称 | 描述 | 清除方式 |
|---|---|---|---|
| 7 | TXE | 发送数据寄存器空。置 1 表示 TDR 已空,可写入新数据 | 写入 DR 自动清除 |
| 6 | TC | 发送完成。置 1 表示当前帧已全部发送完毕(包括停止位) | 读 SR 后写 DR,或写 CR1.TC 位 |
| 5 | RXNE | 接收数据寄存器非空。置 1 表示 RDR 中有新数据可读 | 读取 DR 自动清除 |
| 4 | IDLE | 空闲线路检测。置 1 表示检测到空闲帧 | 读 SR 后读 DR |
| 3 | ORE | 溢出错误。RXNE 为 1 时新数据到达导致覆盖 | 读 SR 后读 DR |
| 2 | NF | 噪声标志。接收帧检测到噪声 | 读 SR 后读 DR |
| 1 | FE | 帧错误。停止位无效(通常为 0) | 读 SR 后读 DR |
| 0 | PE | 校验错误。接收校验位与计算值不匹配 | 读 SR 后读 DR |
4.3.2 错误处理示例
// 带错误检查的接收
uint8_t uart_receive_with_error_check(USART_TypeDef *USARTx, uint8_t *data) {
uint32_t status = USARTx->SR;
// 检查错误标志
if (status & (USART_SR_ORE | USART_SR_FE | USART_SR_NE)) {
// 读取 DR 以清除错误标志
(void)USARTx->DR;
return 1; // 返回错误
}
if (status & USART_SR_RXNE) {
*data = (uint8_t)(USARTx->DR & 0xFF);
return 0; // 成功
}
return 2; // 无数据
}
最佳实践: 在中断服务函数中,建议先读取 SR 再读取 DR,确保错误标志与数据在同一帧内被捕获。读取顺序不可颠倒。
4.4 控制寄存器(CR1/CR2/CR3)—— 配置 UART 工作模式
控制寄存器组(CR1、CR2、CR3)负责配置 UART 的使能、数据位长度、停止位、校验、中断使能、DMA 使能等。其中 CR1 最为常用,CR2 主要配置停止位和同步模式,CR3 配置流控制和 DMA 方向。
4.4.1 CR1 关键位域
| 位 | 名称 | 描述 |
|---|---|---|
| 13 | UE | UART 使能。置 1 开启 UART,清 0 关闭并复位状态 |
| 12 | M | 字长选择。0=8 位,1=9 位 |
| 11 | WAKE | 唤醒方式。0=空闲线路唤醒,1=地址标记唤醒 |
| 10 | PCE | 校验使能。置 1 使能校验位发送/检查 |
| 9 | PS | 校验选择。0=偶校验,1=奇校验 |
| 8 | PEIE | 校验错误中断使能 |
| 7 | TXEIE | 发送缓冲区空中断使能 |
| 6 | TCIE | 发送完成中断使能 |
| 5 | RXNEIE | 接收缓冲区非空中断使能 |
| 4 | IDLEIE | 空闲线路中断使能 |
| 3 | TE | 发送使能。置 1 使能发送器 |
| 2 | RE | 接收使能。置 1 使能接收器 |
| 0 | SBK | 发送间隔帧。置 1 发送间隔符 |
4.4.2 典型配置序列
// 配置 USART2:115200-8-N-1
void uart_init(USART_TypeDef *USARTx, uint32_t baud) {
// 1. 使能 USART 时钟(以 STM32F4 为例)
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
// 2. 配置 GPIO 为复用功能(TX=PA2, RX=PA3)
// ... GPIO 初始化代码 ...
// 3. 配置 CR1:8 位数据、无校验、使能发送和接收
USARTx->CR1 &= ~(USART_CR1_M | USART_CR1_PCE | USART_CR1_PS);
USARTx->CR1 |= (USART_CR1_TE | USART_CR1_RE);
// 4. 配置 CR2:1 位停止位
USARTx->CR2 &= ~USART_CR2_STOP;
// 5. 配置波特率
uart_set_baudrate(USARTx, baud);
// 6. 使能 UART
USARTx->CR1 |= USART_CR1_UE;
}
配置顺序要求: UE 位必须在所有其他配置(M、PCE、PS、TE、RE)设置完成后再置 1。如果在配置过程中 UE 为 1,某些位域的修改可能被硬件忽略或导致不可预测行为。
4.5 波特率分频寄存器(BRR)—— 精确时钟分频
BRR 寄存器决定 UART 通信速率。其值由系统时钟频率(f_CK)和目标波特率计算得出。STM32 支持两种分频模式:标准模式(OVER8=0)和过采样模式(OVER8=1)。
4.5.1 BRR 位域结构
| 位域 | 名称 | 描述 |
|---|---|---|
| [15:4] | DIV_Mantissa | 分频系数整数部分(12 位) |
| [3:0] | DIV_Fraction | 分频系数小数部分(4 位,OVER8=0 时有效) |
4.5.2 波特率计算公式
- 标准模式(OVER8=0): BRR = f_CK / (16 × 目标波特率)
- 过采样模式(OVER8=1): BRR = f_CK / (8 × 目标波特率)
计算出的 BRR 值需拆分为整数部分和小数部分:
- 整数部分 = BRR 右移 4 位(DIV_Mantissa)
- 小数部分 = BRR 低 4 位(DIV_Fraction)
// 设置波特率(标准模式,OVER8=0)
void uart_set_baudrate(USART_TypeDef *USARTx, uint32_t baud) {
uint32_t apb_clock = 84000000; // 假设 APB1 时钟为 84 MHz
uint32_t brr_value;
// 计算 BRR 值
brr_value = (apb_clock + (baud * 8)) / (baud * 16); // 四舍五入
USARTx->BRR = brr_value;
}
// 更精确的实现(含小数舍入)
void uart_set_baudrate_precise(USART_TypeDef *USARTx, uint32_t baud) {
uint32_t apb_clock = 84000000;
uint32_t mantissa, fraction;
uint32_t brr;
// 计算 16 倍分频系数
brr = (apb_clock * 10) / (baud * 16); // 放大 10 倍提高精度
mantissa = brr / 10;
fraction = ((brr % 10) * 16 + 5) / 10; // 小数部分转换为 4 位
USARTx->BRR = (mantissa << 4) | (fraction & 0x0F);
}
4.5.3 常见波特率与 BRR 值对照表(f_CK=84MHz,OVER8=0)
| 目标波特率 | 理论 BRR | DIV_Mantissa | DIV_Fraction | 实际波特率 | 误差 |
|---|---|---|---|---|---|
| 9600 | 546.875 | 546 | 14 | 9598.6 | 0.015% |
| 19200 | 273.4375 | 273 | 7 | 19207.7 | 0.04% |
| 115200 | 45.5729 | 45 | 9 | 115226 | 0.02% |
| 921600 | 5.6966 | 5 | 11 | 922509 | 0.10% |
误差容忍度: 大多数 UART 接收器能容忍 ±2% 的波特率误差。上表中所有常见波特率的误差均远低于此阈值。如果误差超过 1%,建议调整系统时钟或使用过采样模式。
4.6 寄存器编程综合流程图
下图展示了 UART 初始化过程中四个寄存器的配置顺序和依赖关系。箭头表示数据流或控制流方向。
4.7 寄存器编程避坑指南
- UE 位操作: 修改 CR1 中除 UE 外的其他位时,建议先清 UE,配置完成后再置位。部分 MCU 在 UE=1 时修改 M 位无效。
- DR 写入时机: 必须在 TXE=1 时写入 DR。如果在 TXE=0 时写入,数据可能丢失或覆盖未发送的数据。
- 错误标志清除: ORE、NF、FE、PE 等错误标志必须通过“读 SR 后读 DR”序列清除。仅读 SR 不会清除这些标志。
- BRR 更新时机: 建议在 UE=0 时修改 BRR。如果在通信过程中修改 BRR,可能导致当前帧波特率错乱。
- 保留位处理: 写寄存器时,保留位必须写 0。读寄存器时,保留位应被忽略(不依赖其值)。
实战建议: 在驱动开发初期,建议将每个寄存器的配置值打印或保存到调试变量中,与参考手册逐位对比。一个位域的误配置可能导致整个 UART 无法工作,而这类问题往往难以通过逻辑分析仪直接发现。