目录
- 前言
- 一、USART模块地址空间
- 1.1 STM32统一编址机制
- 1.2 USART1地址范围详解
- 1.3 各串口模块地址清单
- 二、USART寄存器结构
- 2.1 寄存器列表与偏移量
- 2.2 寄存器权限标识说明
- 三、数据寄存器 USART_DR
- 3.1 TDR与RDR的双重角色
- 3.2 DR寄存器的位结构
- 四、数据帧格式
- 4.1 数据帧结构组成
- 4.2 以发送0x55为例的波形分析
- 4.3 起始位与停止位的电平本质
- 五、USART发送机制
- 5.1 从TDR到发送移位寄存器的数据流转
- 5.2 数据覆盖问题——为什么不能盲目连续写入
- 5.3 TXE与TC——衡量发送状态的两个核心标志位
- 5.3.1 TXE(Transmit Data Register Empty,发送数据寄存器空)
- 5.3.2 TC(Transmission Complete,发送完成)
- 5.3.3 TXE与TC的区别
- 5.4 USART_SR状态寄存器全景
- 六、USART带标志位的数据发送原理
- 6.1 用户视角:什么时候可以写入数据
- 6.2 USART硬件视角:TXE标志的翻转时机
- 七、三字节完整发送流程剖析(0x00 → 0xFF → 0x55)
- 7.1 第一阶段:发送 0x00
- 7.2 第二阶段:发送 0xFF
- 7.3 第三阶段:发送 0x55
- 7.4 发送完毕:TC标志位的翻转
- 八、阻塞式发送模式
- 九、时钟域的并行操作
- 十、USART 详细接收过程
- 10.1 数据帧格式回顾
- 10.2 接收过程的逐步拆解
- 10.2.1 第一阶段:检测起始位
- 10.2.2 第二阶段:逐位接收数据位
- 10.2.3 第三阶段:接收校验位
- 10.2.4 第四阶段:检测停止位
- 10.2.5 第五阶段:校验与数据平移
- 10.3 RXNE 标志位——接收就绪的信号
- 10.3.1 RXNE 位的定义
- 10.3.2 RXNE 的硬件置位机制
- 10.3.3 RXNE 的清除机制
- 10.4 阻塞式读取机制
- 10.4.1 轮询检测 RXNE
- 10.4.2 形象比喻
- 10.4.3 责任边界
- 10.4.4 读取后自动清零
- 10.5 连续接收的时序关系
- 结语
前言
大家好啊,我是云泽Q,欢迎阅读我的文章,一名热爱计算机技术的在校大学生,喜欢在课余时间做一些计算机技术的总结性文章,希望我的文章能为你解答困惑~
一、USART模块地址空间
1.1 STM32统一编址机制
在STM32F103ZET6这款MCU内部,所有片上外设——包括SRAM、Flash以及各类外设模块——都处于CPU的统一编址空间之下,完成地址映射。换句话说,每一个外设、每一个寄存器,最终都对应着一个明确的物理地址。CPU通过对这些物理地址的读写操作,来实现对外设的控制和数据交互。
具体到USART模块,STM32F103ZET6内部总共包含了5个串口接口:USART1、USART2、USART3、UART4、UART5。每一个串口都是一个独立的硬件单元,拥有自己专属的地址空间。只要搞清楚其中一个串口的硬件结构和寄存器组,其余所有串口的原理完全一致,可以直接举一反三。
1.2 USART1地址范围详解
以USART1为例,它的物理地址范围是:
0x4001 3800 ~ 0x4001 3BFF
这个地址空间的大小可以通过公式计算:
结束地址 - 起始地址 + 1 = 0x4001 3BFF - 0x4001 3800 + 1 = 0x3FF + 1 = 0x400(十六进制)将0x400转换为十进制:1024字节,即1KB。
也就是说,CPU内核为USART1分配了1KB的地址空间。但实际使用中,只有最开头的很少一部分地址被真正使用到了,后续的大部分地址是保留的,访问它们可能产生不可预知的行为。
USART1中一共有7个寄存器,每一个寄存器都有一个偏移量地址。访问任何一个寄存器的方式都是:
USART1基地址(0x4001 3800) + 寄存器偏移量1.3 各串口模块地址清单
| 外设 | 所在总线 | 起始地址 | 结束地址 | 地址空间大小 | 类型 |
|---|---|---|---|---|---|
| USART1 | APB2 | 0x4001 3800 | 0x4001 3BFF | 1 KB | 高级同步/异步串行接口 |
| USART2 | APB1 | 0x4000 4400 | 0x4000 47FF | 1 KB | 通用同步/异步串行接口 |
| USART3 | APB1 | 0x4000 4800 | 0x4000 4BFF | 1 KB | 通用同步/异步串行接口 |
| UART4 | APB1 | 0x4000 4C00 | 0x4000 4FFF | 1 KB | 通用异步串行接口 |
| UART5 | APB1 | 0x4000 5000 | 0x4000 53FF | 1 KB | 通用异步串行接口 |
可以看到,USART1挂载在APB2总线上,而其余串口均挂载在APB1总线上。这也说明USART1的性能和时钟频率与其他串口有所不同。
二、USART寄存器结构
2.1 寄存器列表与偏移量
USART1的7个寄存器及其偏移量如下:
| 寄存器 | 偏移量(Offset) | 地址计算过程 | 最终地址 |
|---|---|---|---|
| USART_SR | 0x00 | 0x4001 3800 + 0x00 | 0x4001 3800 |
| USART_DR | 0x04 | 0x4001 3800 + 0x04 | 0x4001 3804 |
| USART_BRR | 0x08 | 0x4001 3800 + 0x08 | 0x4001 3808 |
| USART_CR1 | 0x0C | 0x4001 3800 + 0x0C | 0x4001 380C |
| USART_CR2 | 0x10 | 0x4001 3800 + 0x10 | 0x4001 3810 |
| USART_CR3 | 0x14 | 0x4001 3800 + 0x14 | 0x4001 3814 |
| USART_GTPR | 0x18 | 0x4001 3800 + 0x18 | 0x4001 3818 |
这里我们重点关注前几个核心寄存器:
- USART_SR(状态寄存器):偏移量0x00,用于存放各种状态标志位,包括我们后面重点讲解的TXE和TC。
- USART_DR(数据寄存器):偏移量0x04,是发送和接收数据的核心寄存器。
2.2 寄存器权限标识说明
在参考手册的寄存器位描述中,你会看到类似rc、w0、r这样的标识,这些代表了每一位的读写权限:
- r(Read Only,只读):该位只能读取,不能写入。从编程角度,你可以直接读取寄存器的值来获取状态。
- rc(Read Clear,读后清零):该标志位由硬件自动置位,当你在软件中读取该寄存器(或对应位)后,硬件会自动将该位清零。这是一个典型的"读即清"机制。
- w0(Write 0 to Clear,写0清零):该位可以通过软件写0来清零。写入1通常不影响该位的状态。
这些权限标识反映了硬件对各个标志位的管理方式。很多情况下,标准库或HAL库已经帮我们处理了这些底层的读写操作,不需要我们手动去操作。但了解这些底层机制,有助于在遇到异常时进行深度调试。
三、数据寄存器 USART_DR
3.1 TDR与RDR的双重角色
USART_DR是串口通信中最核心的寄存器之一,它的偏移量为0x04,对于USART1来说,其物理地址为0x4001 3804。
从数据手册的描述来看,USART_DR被标注为一个寄存器,但实际上它由两个逻辑寄存器组成:
- TDR(Transmit Data Register,发送数据寄存器):用于发送数据。当CPU向USART_DR写入数据时,数据被写入TDR。
- RDR(Receive Data Register,接收数据寄存器):用于接收数据。当CPU从USART_DR读取数据时,数据来自RDR。
TDR和RDR共享同一个物理地址(0x4001 3804),通过总线控制信号中的读写方向来区分:
- 写操作→ 访问TDR(发送方向)
- 读操作→ 访问RDR(接收方向)
TDR寄存器提供了内部总线(APBx)和输出移位寄存器之间的并行接口;RDR寄存器提供了输入移位寄存器和内部总线之间的并行接口。
3.2 DR寄存器的位结构
USART_DR是一个32位寄存器,但实际只使用了低9位:
| 位段 | 名称 | 说明 |
|---|---|---|
| 位31:9 | 保留位 | 硬件强制为0 |
| 位8:0 | DR[8:0] | 数据值(Data value),共9位 |
DR[8:0]共9位,意味着USART最多支持9位数据的传输。
- 如果配置为9位数据位,则无法启用奇偶校验功能。
- 工程中更常见的配置是8位数据 + 1位奇偶校验(如8N1、8E1、8O1)。
当使能校验位(USART_CR1中PCE位被置位)进行发送时,写到MSB的值(根据数据长度不同,MSB是第7位或第8位)会被后来的校验位所取代。也就是说,硬件会自动用计算出的校验位覆盖最高数据位,用户无需手动计算校验值。
四、数据帧格式
4.1 数据帧结构组成
串口通信的数据是以**帧(Frame)**为单位进行传输的。一个完整的数据帧由以下部分组成:
[起始位] + [数据位] + [校验位(可选)] + [停止位]以最常见的配置为例:
- 起始位:1位,固定为低电平(0),用于通知接收方"数据开始传输"
- 数据位:8位(或9位),实际要传输的数据,LSB(最低有效位)先发
- 校验位:1位(奇校验或偶校验),由硬件自动生成
- 停止位:1位(或1.5位、2位),固定为高电平(1),用于标识一帧结束
所以一帧完整的帧包含:1 + 8 + 1 + 1 =11位。
4.2 以发送0x55为例的波形分析
以发送数据0x55为例来直观理解数据帧的传输过程:
0x55 的二进制 = 0101 0101由于串口通信是LSB先发(最低有效位优先),所以实际发送顺序为:
起始位 → 数据位(LSB→MSB) → 校验位 → 停止位 0 → 1 0 1 0 1 0 1 0 → 奇校验位 → 1完整帧为:0(起始)+ 10101010(数据,LSB先发)+ 1(奇校验)+ 1(停止)= 11位
波形图上各阶段的含义:
| 阶段 | 名称 | 电平 | 说明 |
|---|---|---|---|
| ① | 空闲状态 | 高电平 | 无数据传输时,TX线保持高电平 |
| ② | 起始位 | 低电平 | 拉低电平,触发接收方开始采样 |
| ③ | 数据位 | 高低交替 | 8位数据逐位发送,LSB先发 |
| ④ | 校验位 | 由数据决定 | 奇校验位,硬件自动填充 |
| ⑤ | 停止位 | 高电平 | 拉高电平,标识一帧结束,回到空闲状态 |
4.3 起始位与停止位的电平本质
起始位和停止位虽然可以用逻辑0和逻辑1来理解,但更好的理解方式是从电平角度:
- 起始位:本质上就是USART硬件发现"有数据要发送"时,主动拉低TX引脚的电平,形成低电平触发信号,通知接收端"准备接收数据"。
- 停止位:本质上就是USART硬件发现"数据发送完成"时,主动拉高TX引脚的电平,形成高电平恢复信号,表示"这一帧结束了,回到空闲状态"。
接收端收到的是一串高低电平交替的方波,最终形成的波形就是我们在示波器上看到的样子。起始位可以理解为逻辑0,但更好的方式是理解成低电平;停止位可以理解为逻辑1,但更好的方式是理解成高电平。
五、USART发送机制
5.1 从TDR到发送移位寄存器的数据流转
整个发送过程涉及两个关键的寄存器层次:
CPU(通过APB总线)→ 写入 → TDR(发送数据寄存器)→ 硬件转移 → 发送移位寄存器 → 逐位输出到TX引脚具体流程如下:
- 用户程序将要发送的数据写入USART_DR(实际写入TDR)。
- USART硬件检测到TDR中有数据后,将TDR中的数据一次性并行转移到发送移位寄存器中。
- 发送移位寄存器在USART时钟驱动下,将数据逐位串行输出到TX引脚,LSB先发。
- 硬件自动在数据帧前后添加起始位和停止位,并根据配置自动填充校验位。
注意:发送移位寄存器对用户是不可见的,用户只能通过TDR来操作发送数据。
5.2 数据覆盖问题——为什么不能盲目连续写入
假设USART底层正在发送数据,发送移位寄存器中还有数据未发完。此时如果用户再次向TDR写入新数据,会发生什么?
while(cnt){TDR=cnt;cnt--;}如果第一次写入时USART正在忙,数据暂存在TDR中等待转移到移位寄存器。如果在USART还没完成转移和发送之前,用户第二次又往TDR写入数据,那么新数据会直接覆盖TDR中尚未发送的旧数据,导致数据丢失。
这就好比你一边在说话,别人一边往你嘴里塞新的台词——你根本来不及把之前的话说完。
因此,在发送数据之前,必须确认TDR是否为空,即上一次的数据是否已经被转移到移位寄存器中。这正是TXE标志位存在的意义。
5.3 TXE与TC——衡量发送状态的两个核心标志位
USART_SR状态寄存器中有两个极其重要的标志位,专门用来衡量发送过程的状态:
5.3.1 TXE(Transmit Data Register Empty,发送数据寄存器空)
- 位置:USART_SR的位7
- 含义:当TDR中的数据被硬件转移到发送移位寄存器后,该位被硬件置1。
- 取值说明:
0:数据还没有被转移到移位寄存器,TDR非空,此时不能写入新数据。1:数据已经被转移到移位寄存器,TDR已空,可以写入下一帧数据。
- 清零方式:对USART_DR进行写操作时,该位自动清零(因为写入新数据后TDR不再为空)。
- 应用场景:主要用于单缓冲器传输中,判断是否可以写入下一字节数据。
5.3.2 TC(Transmission Complete,发送完成)
- 位置:USART_SR的位6
- 含义:当包含有数据的一帧发送完成后,并且TXE=1时,由硬件将该位置1。
- 取值说明:
0:发送还未完成。1:发送已完成,整个数据帧(包括停止位)已全部从TX引脚发出。
- 清零方式:
- 软件序列清除:先读USART_SR,然后写入USART_DR。
- 或者直接写入’0’来清除(仅在多缓冲器通讯中推荐)。
- 应用场景:用于判断整帧数据是否彻底发送完毕。
5.3.3 TXE与TC的区别
这两个标志位经常被混淆,它们的本质区别在于衡量的阶段不同:
| 标志位 | 衡量的是什么 | 何时置1 |
|---|---|---|
| TXE | TDR寄存器是否为空 | 数据从TDR转移到移位寄存器后立即置1 |
| TC | 整帧数据是否发送完毕 | 移位寄存器中最后一位(含停止位)发完后置1 |
简单来说:
- TXE=1意味着:TDR空了,你可以写下一字节了,但当前这一帧可能还在移位寄存器中慢慢发。
- TC=1意味着:不仅TDR空了,移位寄存器也空了,整个数据帧已经全部从TX引脚发出去了。
5.4 USART_SR状态寄存器全景
USART_SR(状态寄存器)位于偏移量0x00处,复位值为0x00C0。其关键位段如下:
| 位 | 名称 | 权限 | 说明 |
|---|---|---|---|
| 位7 | TXE | r | 发送数据寄存器空 |
| 位6 | TC | rc w0 | 发送完成 |
| 位5 | RXNE | rc w0 | 读数据寄存器非空(接收数据就绪) |
| 位4 | IDLE | rc w0 | 空闲线检测 |
| 位3 | ORE | r | 溢出错误 |
| 位2 | NE | r | 噪声错误标志 |
| 位1 | FE | r | 帧错误 |
| 位0 | PE | r | 校验错误 |
其中我们当前阶段重点关注的是**TXE(位7)和TC(位6)**这两个发送相关的标志位。后续进入接收阶段后,RXNE(位5)将成为核心关注点。
六、USART带标志位的数据发送原理
当我们把两个核心标志位——TXE和TC——引入到USART的发送流程中后,串口发送就进入了真正意义上的实战阶段。在用户代码层面进行数据帧发送时,真实情况下的每一次发送都是要携带标志位来判断状态的。
我们要发送三个字节的数据,分别是0x00、0xFF和0x55。这一次不只是一个数据,而是连续三个。数据帧的格式设定为:1位起始位 + 8位数据位 + 1位奇校验位 + 1位停止位,总共11位。这个帧格式是我们之前一直沿用的,此处不再赘述。
6.1 用户视角:什么时候可以写入数据
站在用户的角度,理解TXE和TC的状态变化逻辑至关重要:
- 当TXE == 1时,说明TDR是空的。此时如果用户还有数据要发送,就可以放心地继续写入下一个字节。
- 如果用户的数据已经全部写完了,那就什么都不用做,静等USART硬件把剩余数据发完。当TC变成1时,意味着
TXE == 1 && TC == 1,表示本轮数据全部发送完毕。 - 如果数据没发完,只要TXE == 1,用户就可以继续往TDR里写新数据。
用户判断逻辑就是:TXE == 1 && 自己还有数据要发,那么就继续发送。
6.2 USART硬件视角:TXE标志的翻转时机
从USART硬件模块的角度来看,它的运作逻辑是这样的:
- USART必须先把当前移位寄存器里的数据全部串行发送完毕。
- 发送完成后,USART才会把TDR寄存器中的数据平移到发送移位寄存器。
- 只有TDR中的数据成功平移到发送移位寄存器之后,USART才会将TXE设置为1,表示TDR已空,允许用户再次写入。
这就是TXE的本质——USART硬件模块用来指导用户写入数据的指挥棒。它告诉用户:现在TDR空了,你可以写了;或者TDR还被占着,你必须等。
如果用户无视TXE标志位,在TXE == 0(TDR不空)时强行写入数据,就会导致自己覆盖了自己的数据。这时候锅要用户自己背,因为USART已经通过TXE标志告诉你TDR不为空了,你还要往里写。
所以,用户要写TDR,必须先对TXE进行判定,只有TXE == 1,用户才能再写入。
七、三字节完整发送流程剖析(0x00 → 0xFF → 0x55)
下面我们以发送0x00、0xFF、0x55这三个字节为例,从时序角度逐步拆解整个发送过程。
7.1 第一阶段:发送 0x00
初始状态下,TDR为空,TXE = 1,TC = 1。
用户将第一个数据0x00写入TDR。此时:
- TDR被占用,TXE被USART自动设置为0。
- TC也被设置为0(因为发送尚未完成)。
- USART硬件自动将TDR中的数据(
1-0-0-0-0-0-0-0-0,含起始位和校验位)平移到发送移位寄存器。 - TDR重新变为空,TXE恢复为1。
- 发送移位寄存器开始按位串行输出数据到TX引脚。
此时TC仍为0,因为移位寄存器中还有数据正在发送。
7.2 第二阶段:发送 0xFF
USART硬件在自身时钟驱动下,继续从移位寄存器逐位输出0x00的数据帧。
同时,由于上一阶段TDR已经变空(TXE = 1),用户代码检测到TXE == 1后,将第二个数据0xFF写入TDR。此时:
- TDR再次被占用,TXE被设置为0。
- 注意:如果此时USART硬件中上一次
0x00的数据还没有从移位寄存器发送完毕,那么TDR内部的0xFF就无法被平移到移位寄存器。 - 因此TDR一直被占用,TXE保持为0。
用户必须等待TXE变回1,才能写入下一个数据。
当USART把0x00的所有位(11位)全部发送完毕后,硬件会将TDR中的0xFF平移到移位寄存器。平移完成后:
- TDR重新变空,TXE被USART设置为1。
- 移位寄存器开始串行输出
0xFF(加上奇校验位后为1-1-1-1-1-1-1-1-1)。
7.3 第三阶段:发送 0x55
此时TXE = 1,用户检测到后,将第三个数据0x55写入TDR。
- TDR被占用,TXE被设置为0。
- USART继续从移位寄存器输出
0xFF的数据帧。
当0xFF的数据帧全部输出完毕后:
- USART将TDR中的
0x55平移到移位寄存器。 - TDR变空,TXE被设置为1。
- 移位寄存器开始输出
0x55的数据帧。
此时用户已经没有更多数据要发送了。TDR保持为空(TXE = 1)。
7.4 发送完毕:TC标志位的翻转
当最后一个数据0x55的所有位也从移位寄存器全部串行输出到TX引脚后:
- TDR为空(TXE = 1)
- 移位寄存器也为空
两个条件同时满足,USART硬件将TC标志位置为1,宣告本次多字节数据发送全部完成。
此时TXE == 1 && TC == 1,整个发送流程结束。
八、阻塞式发送模式
在实际编程中,用户发送数据时必须对TXE标志位进行检测:
- 如果TXE == 0,表示TDR中的数据还没有被平移到发送移位寄存器(TDR被占用)。此时用户必须阻塞在检测TXE标志位的位置进行等待,直到TXE变为1。
这种模式的写入方式就叫做阻塞式发送。
在时序图上可以清晰地看到:
- 数据寄存器TDR中依次装入数据1、数据2、数据3。
- 每当一个数据从TDR平移到发送移位寄存器时,TXE标志从低电平跳变为高电平(标记为时刻①、②)。
- 用户只有在TXE变高的时刻才能写入下一个数据。
- 当最后一个数据全部发送完毕,TDR和发送移位寄存器都为空时,TC标志被置为1(标记为时刻③)。
所以,如果使用阻塞模式进行数据发送(一个或多个数据之后),要知道USART是否彻底把数据全部发送出去了,就看TC标志位。当TC == 1时,表示要发送的数据已全部发送完成。
九、时钟域的并行操作
需要特别注意的是,CPU和USART模块是在各自的时钟域下独立运行的:
- CPU通过APBx总线时钟向TDR写入数据。
- USART模块通过自身的波特率时钟从移位寄存器逐位串行输出数据。
这意味着,在时间维度上,用户向TDR写入下一个数据和USART从移位寄存器发送当前数据是可以并行进行的。只要TDR是空的(TXE == 1),用户就可以提前把下一个数据写好放进去,等USART发完当前数据后,硬件会自动把TDR中的新数据搬运到移位寄存器继续发送。
这种硬件层面的自动平移机制,使得串口发送在效率上远高于CPU逐位模拟的方式。
十、USART 详细接收过程
前面我们完成了 STM32 USART 发送的详细过程,数据帧格式和标志位都已经搞定了。接下来进入下一个话题——USART 详细的接收过程。
之前我们聊接收的时候,只是站在 8 位数据位的角度,并没有谈任何数据帧格式。所以这一次,我们要带着完整的帧格式来理解接收的全貌。
10.1 数据帧格式回顾
以发送端发送0x55为例,对应的二进制为0101 0101。
我们约定的帧格式为:
| 组成部分 | 位数 | 说明 |
|---|---|---|
| 起始位 | 1 位 | 低电平(拉低),标记一帧数据的开始 |
| 数据位 | 8 位 | 实际有效数据 |
| 校验位 | 1 位 | 奇校验位 |
| 停止位 | 1 位 | 高电平(拉高),标记一帧数据的结束 |
总共 11 位比特位构成一帧完整的数据。
需要特别注意的是:发送端是从低位向高位逐位发送的。0x55的二进制0101 0101,我们人眼阅读时是从高位到低位读的,但在物理线路上,发送端先发最低位的1,再发0、1、0……依此类推。所以线路上实际传输的比特流顺序为:
起始位(0) → 1 → 0 → 1 → 0 → 1 → 0 → 1 → 0 → 校验位(1) → 停止位(1)接收端按这个顺序依次接收到自己的接收移位寄存器中。
10.2 接收过程的逐步拆解
10.2.1 第一阶段:检测起始位
接收端的 RX 引脚平时处于空闲的高电平状态。当接收控制器检测到一个拉低的电平(低电平)时,就知道对方开始发送数据了。
这个低电平仅仅用于 USART 内部判断"对方开始发了",不需要保存,更不需要被用户读取。起始位由硬件自动识别和处理。
收到这个拉低的电平后,接收移位寄存器准备好接收对面的数据,第一个进入接收移位寄存器的就是起始位。
10.2.2 第二阶段:逐位接收数据位
起始位之后,发送端开始逐位发送 8 位数据。接收端按照约定的波特率(时钟节拍),一位一位地把数据从 RX 引脚读入接收移位寄存器中。
以0x55为例,实际进入接收移位寄存器的顺序为:
第1位进入:1 第2位进入:0 第3位进入:1 第4位进入:0 第5位进入:1 第6位进入:0 第7位进入:1 第8位进入:0数据在接收移位寄存器中逐位右移填充,最终填满 8 个有效数据位。
10.2.3 第三阶段:接收校验位
8 位数据位之后,接下来进入的是 1 位校验位。在我们的奇校验配置下,0x55的二进制0101 0101中"1"的个数为 4 个(偶数),为了满足奇校验(总"1"个数为奇数),校验位为1。
这个校验位同样被接收移位寄存器接收进来。
10.2.4 第四阶段:检测停止位
在收到约定的 9 位数据(8 位数据位 + 1 位校验位)之后,接收端继续检测线路电平。当读到拉高的电平时,就说明对方发完了,这就是停止位。
这个高电平同样仅仅是 USART 内部判断"对方发完了"的信号,不需要保存,更不需要被用户读取。停止位由硬件自动识别。
10.2.5 第五阶段:校验与数据平移
当 11 位完整帧全部接收完毕后,USART 模块自己进行校验:
- 校验通过 → 有效数据(8 位)从接收移位寄存器平移到接收数据寄存器(RDR)。
- 校验不通过 → 数据无效,可能触发错误标志位。
在这个过程中,起始位和停止位已经被硬件自动丢弃,它们只起到帧边界的标记作用,不会进入 RDR。
10.3 RXNE 标志位——接收就绪的信号
数据从接收移位寄存器平移到 RDR 的过程中,状态寄存器(USART_SR)中的RXNE 位会被硬件自动置 1。
10.3.1 RXNE 位的定义
RXNE 全称为Read Data Register Not Empty(读数据寄存器非空),位于 USART_SR 状态寄存器的位 5:
| 位编号 | 名称 | 含义 |
|---|---|---|
| 位 5 | RXNE | 读数据寄存器非空 |
- RXNE = 0:数据没有收到,RDR 为空,不可读取。
- RXNE = 1:收到数据,RDR 非空,可以读出。
10.3.2 RXNE 的硬件置位机制
当 RDR 移位寄存器中的数据被转移到 USART_DR 寄存器中时,RXNE 位被硬件自动置位。
如果 USART_CR1 寄存器中的 RXNEIE(接收中断使能位)为 1,则同时会产生中断。
10.3.3 RXNE 的清除机制
- 正常读取清除:对 USART_DR(即 RDR)执行读操作,RXNE 位会被硬件自动清零。
- 手动清除:RXNE 位也可以通过写入 0 来清除,但只有在多缓存通讯中才推荐这种清除方式。
10.4 阻塞式读取机制
有了 RXNE 标志位之后,用户就可以安全地读取数据了。具体做法是:
10.4.1 轮询检测 RXNE
在读取 RDR 之前,必须检测 RXNE 标志位:
while((USART_SR&0x0020)==0);// 轮询等待 RXNE 置 1// RXNE 为 1 后,安全读取数据uint8_treceived_data=USART_DR;// 读取数据,同时硬件清零 RXNE标志位为 0 表示没有数据,程序通过while循环不断轮询检测。只要标志位一直为 0,程序就卡在那里等待。
10.4.2 形象比喻
这个等待过程就像去食堂打饭:阿姨还没把饭菜端上来的时候,你只能站在点菜的位置上等待;阿姨把菜放上来了(RXNE 变 1),你这个时候才能拿到菜。
10.4.3 责任边界
这种机制明确了硬件和软件之间的责任边界:
- USART 模块的责任:负责接收数据、校验数据、将有效数据放入 RDR,并置位 RXNE 通知用户"数据准备好了"。
- 用户的责任:在 RXNE 置 1 之后去读取数据。只要标志位为 1,用户读数据读出错了就和 USART 模块没关系了——“我把我该做的工作做了,要后面读数据出错,这个锅我不背。”
10.4.4 读取后自动清零
只要用户把数据读走了,RXNE 标志位就会被 USART 硬件自动清零,回到 0 状态,准备接收下一帧数据。
10.5 连续接收的时序关系
当连续接收多帧数据时,时序关系如下:
每一帧数据接收完成并校验通过后,RXNE 被置 1(图中①②③标记处),用户读取后 RXNE 自动清零,等待下一帧。