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寄存器配置流程,你照着这个顺序来,基本不会出错。
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管配置。这四句话,够你用一辈子。