3. USART寄存器详解:从SR到CR3,手把手教你配置

好,咱们今天来啃硬骨头——USART的寄存器。说实话,我刚学STM32那会儿,看到寄存器手册里密密麻麻的位定义,头都大了。但后来我发现,只要你搞懂了几个核心寄存器,剩下的都是套路。

USART的寄存器其实不多,真正干活的就是这六个:SR(状态寄存器)、DR(数据寄存器)、BRR(波特率寄存器)、CR1/CR2/CR3(控制寄存器)。咱们一个一个来拆。

核心观点:USART的寄存器配置,说白了就是“设波特率、配模式、查状态、读写数据”这四步。你把这四步刻在脑子里,后面写驱动就是填空。

3.1 USART_SR:状态寄存器——你的“眼线”

SR寄存器,全称Status Register。它就像你的哨兵,告诉你当前USART在干什么。我个人习惯在调试时,先把SR的值读出来看看,心里就有底了。

位号 名称 含义 我常用的场景
9 CTS CTS标志(硬件流控) 用硬件流控时才看,平时忽略
8 LBD LIN断开检测 做LIN总线时用,蓝牙用不到
7 TXE 发送数据寄存器空 发数据前必查!
6 TC 发送完成 发完一帧后等这个位
5 RXNE 接收数据寄存器非空 收数据前必查!
4 IDLE 总线空闲 检测一帧数据结束
3 ORE 溢出错误 数据没来得及读,被覆盖了
2 NF 噪声标志 通信环境差时出现
1 FE 帧错误 波特率不匹配时常见
0 PE 校验错误 开了校验位才会出现

最常用的两个位:TXE和RXNE。 你写驱动时,80%的时间都在跟它们打交道。

我的小技巧:查SR寄存器时,别用if(SR & RXNE)这种写法。我习惯用while(!(USART1->SR & USART_SR_RXNE)),这样代码更紧凑,而且不会漏掉数据。

3.2 USART_DR:数据寄存器——数据的“中转站”

DR寄存器,Data Register。它分两部分:低9位存数据,高7位保留。你写数据到DR,硬件就自动发出去了;你读DR,硬件就把收到的数据给你。

这里有个坑,我踩过:读DR会自动清除RXNE标志位,写DR会自动清除TXE标志位。 所以别手贱去手动清这些标志,除非你想出bug。

// 发送一个字节
USART1->DR = (data & 0x1FF);  // 只取低9位,防止溢出

// 接收一个字节
uint16_t rx_data = USART1->DR;  // 读DR的同时清RXNE

我曾经犯过的错:在中断里读DR后,又手动去清RXNE标志。结果数据丢了,查了两天才发现是标志被清了两次。记住:读DR一次就够了,别画蛇添足。

3.3 USART_BRR:波特率寄存器——通信的“节拍器”

BRR寄存器,Baud Rate Register。它决定了USART的通信速度。公式很简单:

波特率 = 时钟频率 / (16 × USARTDIV)

其中USARTDIV就是BRR寄存器的值。BRR的高4位是整数部分,低4位是小数部分。举个例子:

// 假设时钟频率为84MHz,目标波特率115200
// USARTDIV = 84000000 / (16 * 115200) = 45.5729
// 整数部分 = 45 = 0x2D
// 小数部分 = 0.5729 × 16 = 9.1664 ≈ 9 = 0x9
// BRR = (45 << 4) | 9 = 0x2D9

USART1->BRR = 0x2D9;
时钟频率 目标波特率 BRR值(近似) 实际波特率 误差
84 MHz 115200 0x2D9 115200 0%
84 MHz 9600 0x222B 9600 0%
84 MHz 460800 0x0B6 460800 0%
72 MHz 115200 0x271 115200 0%

避坑指南:我曾经在项目里直接用STM32CubeMX生成的BRR值,结果发现实际波特率差了2%。后来一查,是时钟配置错了。所以我的习惯是:先确认时钟树,再算BRR,最后用示波器量一下TX脚的波形验证。

3.4 USART_CR1/CR2/CR3:控制寄存器——你的“遥控器”

这三个寄存器控制USART的所有行为。CR1管基本功能,CR2管协议细节,CR3管高级特性。

3.4.1 CR1:核心控制

CR1是咱们打交道最多的寄存器。常用的位:

  • UE(位13):USART使能。不设这个,其他都白搭。
  • M(位12):字长选择。0=8位,1=9位。蓝牙模块一般用8位。
  • PCE(位10):校验使能。蓝牙通信一般不开。
  • PS(位9):校验选择。0=偶校验,1=奇校验。
  • TE(位3):发送使能。要发数据就得开。
  • RE(位2):接收使能。要收数据就得开。
  • RXNEIE(位5):接收中断使能。做中断驱动时必开。
  • TCIE(位6):发送完成中断使能。DMA传输时常用。
// 典型配置:8位数据、无校验、使能收发
USART1->CR1 |= USART_CR1_UE;      // 使能USART
USART1->CR1 &= ~USART_CR1_M;      // 8位数据模式
USART1->CR1 &= ~USART_CR1_PCE;    // 无校验
USART1->CR1 |= USART_CR1_TE;      // 使能发送
USART1->CR1 |= USART_CR1_RE;      // 使能接收

3.4.2 CR2:协议细节

CR2主要管停止位和同步模式。蓝牙通信一般用异步模式,所以关注停止位就行。

  • STOP[1:0](位13:12):停止位长度。00=1位,10=2位,01=0.5位,11=1.5位。蓝牙模块默认1位停止位。
  • CLKEN(位11):时钟输出使能。同步模式才用,异步模式关掉。
// 配置1位停止位
USART1->CR2 &= ~USART_CR2_STOP;   // 默认就是00,1位停止位

3.4.3 CR3:高级特性

CR3管流控、DMA、红外等高级功能。蓝牙通信中,最常用的是DMA相关位。

  • DMAT(位7):发送DMA使能。用DMA发数据时开。
  • DMAR(位6):接收DMA使能。用DMA收数据时开。
  • RTSE(位8):RTS硬件流控使能。蓝牙模块一般不用。
  • CTSE(位9):CTS硬件流控使能。同上。
// 使能DMA发送和接收
USART1->CR3 |= USART_CR3_DMAT;    // 使能DMA发送
USART1->CR3 |= USART_CR3_DMAR;    // 使能DMA接收

注意:开DMA之前,一定要先配好DMA控制器。我见过有人只开了CR3的DMAT位,但DMA根本没初始化,结果数据发不出去还找不到原因。

3.5 寄存器配置流程图

下面这张图是我自己总结的USART寄存器配置流程,你照着这个顺序来,基本不会出错。

USART寄存器配置流程图 USART寄存器配置流程 1. 使能USART时钟 2. 配置GPIO复用功能 3. 设置BRR波特率 4. 配置CR2停止位 5. 配置CR1(字长/校验/使能) 6. 配置CR3(DMA/流控) ✅ 配置完成 💡 配置顺序很重要 先时钟→再GPIO →再BRR→最后CR1 UE位最后使能

3.6 实战:完整的USART初始化代码

好了,理论说完了,咱们来写点真东西。下面是我在蓝牙项目中用的初始化代码,你直接拿去改改就能用。

void USART1_Init(uint32_t baudrate)
{
    // 1. 使能时钟
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    
    // 2. 配置GPIO:PA9=TX, PA10=RX
    GPIOA->MODER &= ~(GPIO_MODER_MODER9 | GPIO_MODER_MODER10);
    GPIOA->MODER |= (GPIO_MODER_MODER9_1 | GPIO_MODER_MODER10_1);  // 复用功能
    GPIOA->AFR[1] |= (7 << 4) | (7 << 8);  // AF7 = USART1
    
    // 3. 计算并设置BRR
    uint32_t clock = 84000000;  // 假设APB2时钟84MHz
    uint32_t usartdiv = (clock + (baudrate * 8)) / (baudrate * 16);
    USART1->BRR = usartdiv;
    
    // 4. 配置CR2:1位停止位
    USART1->CR2 &= ~USART_CR2_STOP;
    
    // 5. 配置CR1:8位数据、无校验、使能收发
    USART1->CR1 &= ~USART_CR1_M;      // 8位
    USART1->CR1 &= ~USART_CR1_PCE;    // 无校验
    USART1->CR1 |= USART_CR1_TE;      // 使能发送
    USART1->CR1 |= USART_CR1_RE;      // 使能接收
    USART1->CR1 |= USART_CR1_UE;      // 最后使能USART
}

我的习惯:UE位一定要最后设。因为前面配置还没完成就使能USART,可能会产生意外的中断或错误。这个顺序我吃过亏,后来就记住了。

3.7 常见问题与避坑

  • 波特率不对:先查时钟树,再算BRR,最后用示波器量。我遇到过有人把APB2时钟记成72MHz,结果算出来的波特率全错。
  • 数据收不到:检查RE位有没有开,GPIO有没有配成复用功能。我刚开始学的时候,经常忘了配GPIO。
  • 数据发不出:检查TE位和TXE标志。有时候TXE一直为0,说明上次数据还没发完。
  • 中断不触发:检查NVIC有没有使能,中断优先级有没有配。我见过有人只开了RXNEIE,但没在NVIC里使能USART中断。

嗯,寄存器这块就讲这么多。你把这些搞懂了,后面写驱动就是水到渠成的事。记住:SR看状态,DR传数据,BRR定速度,CR1/CR2/CR3管配置。这四句话,够你用一辈子。