嵌入式:深刻理解UART与USART串口通信的流程和原理结构
2026/8/23 18:42:32 网站建设 项目流程

前言

文章合集:

嵌入式系列https://blog.csdn.net/2401_88433210/category_13196870.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=13196870&sharerefer=PC&sharesource=2401_88433210&sharefrom=from_link

文章以STM32F103ZET6芯片为例来介绍

建议先看一下这一篇博客了解UART与USART串口通信的波特率与帧格式

嵌入式:深刻理解UART与USART串口通信的波特率与帧格式-CSDN博客本文以STM32F103ZET6为例,详细解析UART/USART串口通信原理,详细介绍了波特率,USART帧格式之间的关系https://blog.csdn.net/2401_88433210/article/details/163637426?spm=1011.2415.3001.10575&sharefrom=mp_manage_link

一、USART 简化原理结构图

前面若干章,讨论的都是 USART 的通信协议特性,下面把重点放在 STM32 单片机中,关于 USART 的功能结构中,即重点放在 USART 硬件功能构成和如何发出 USART 数据帧上。

  • STM32 的 USART 完全兼容之前讲解的 UART 的全部特性。
  • 除此之外它还支持同步单向通信和半双工单线通信,也支持 LIN(局部互连网),智能卡协议和 IrDA(红外数据组织)SIR‑ENDEC 规范,以及调制解调器(CTS / RTS)操作,它还允许多处理器通信的能力。总之 STM32 的 USART 有丰富的扩展功能

如果我们要看到 USART 功能框图,在《STM32F10xxx 参考手册》通用同步异步收发器 (USART) 章节可以看到。

如上图所示是 STM32 的 USART 的功能框图,其中各部分的主要功能如下:

  1. 图中 ① 标识区域是 USART 相关的管脚;
  2. 图中 ② 标识区域是 USART 波特率控制相关;
  3. 图中 ③ 标识区域是 USART 控制寄存器相关;
  4. 图中 ④ 标识区域是 USART 状态寄存器相关;
  5. 图中 ⑤ 标识区域是 USART 数据收、发相关核心功能框图;
  6. 图中 ⑥ 标识区域在 IrDA(红外模式)和智能卡模式下才会使用。

该图所示的功能模块,对于萌新而言太过庞杂。对于仅使用 USART 标准通信功能而言最核心的就是图中 ⑤ 标识的区域部分,我们只把 ⑤ 标识的区域部分拿出来进行简化。

简化结构图:

  1. 当用户将待发送的字节数据写入到数据寄存器(USART_DR / TDR)中,USART 控制器会首先把 TDR 的数据平移至发送移位寄存器,然后 USART 的发送控制器,就会将发送移位寄存器中的数据,自动按照数据位通过 Tx 管脚依次发送出去。

  2. USART 控制器会通过 Rx 管脚依次接收收到的数据位到接收移位寄存器,当接收完一个数据段(8 / 9 位)时,会自动将接收移位寄存器中的数据,平移拷贝到数据寄存器(USART_DR / RDR)中。后续用户从数据寄存器(USART_DR / RDR)读取数据,就能得到最近一次接收到的数据字节。

在这里,我们正式认识两个 USART 寄存器,分别是:

  • 发送寄存器 TDR(Transmit Data Register)
  • 接收寄存器 RDR(Receive Data Register)

其中,发送和接收移位寄存器,在 USART 硬件单元内部,对用户不可见。

❓注意: 这里除了 TDR,和 RDR,我们把 USART 的其他模块当成黑盒的,整个拷贝移动,移位发送和移位接收,都是 USART 模块自己完成的

二、理解 CPU 读写 TDR 和 RDR

这两个寄存器属于 USART 模块内部,而其中 STM32F103ZET6 有 5 个串口模块,每一个都有一套独立的寄存器结构,我们以 USART1 为例。

在 STM32F103ZET6 当中,片上外设寄存器全部映射至 CPU 内存地址空间、统一编址。 CPU 可以直接操作地址,读写 TDR 发送寄存器、RDR 接收寄存器,之后由 USART 硬件自主完成收发工作。

上图可知,USART1 的地址范围是:0x4001 3800 − 0x4001 3BFF

该地址范围是 0x4001 3800 到 0x4001 3BFF。

  • USART1 拥有的范围大小:结束地址 − 起始地址 + 1
  • 0x4001 3BFF − 0x4001 3800 + 1 = 0x3FF + 1 = 0x400(十六进制)
  • 将 0x400 转换为十进制:1024 字节。

所以,这个地址空间一共是 1024 字节,也就是 1KB。

  • 注意:虽然 CPU 内核只为 USART1 分配了 1KB 的地址空间,但实际使用的只有最开头的很少一部分。后续的地址是保留的,访问它们可能产生不可预知的行为。

而后续 USART1 中,寄存器一共有 7 个 (暂时不管是什么,我们也不需要全部熟悉,需要的时候查就行了),其中每一个寄存器都有一个偏移量地址,最终访问任何一个寄存器,访问方式都是: 0x4001 3800 + 寄存器偏移。

这样,每个寄存器的地址清单就是:

寄存器偏移量 (Offset)地址计算过程最终地址
USART_SR0x000x4001 3800 + 0x000x4001 3800
USART_DR0x040x4001 3800 + 0x040x4001 3804
USART_BRR0x080x4001 3800 + 0x080x4001 3808
USART_CR10x0C0x4001 3800 + 0x0C0x4001 380C
USART_CR20x100x4001 3800 + 0x100x4001 3810
USART_CR30x140x4001 3800 + 0x140x4001 3814
USART_GTPR0x180x4001 3800 + 0x180x4001 3818

有了这些地址,CPU 就可以直接访问任意一个寄存器,包括我们的数据寄存器 DR。

而的 STM32F103ZET6 系列单片机,内部包含 5 个串口模块,每一个都有自己的地址范围:

串口模块详细地址清单

外设所在总线起始地址结束地址地址空间大小类型
USART1APB20x4001 38000x4001 3BFF1 KB高级同步 / 异步串行接口
USART2APB10x4000 44000x4000 47FF1 KB通用同步 / 异步串行接口
USART3APB10x4000 48000x4000 4BFF1 KB通用同步 / 异步串行接口
UART4APB10x4000 4C000x4000 4FFF1 KB通用异步串行接口
UART5APB10x4000 50000x4000 53FF1 KB通用异步串行接口

主要差别

  • APB2:高速外设总线,USART1 挂载于此,通常运行在最高 72MHz
  • APB1:低速外设总线,其余串口挂载于此,最高频率 36MHz
  • 有的是 USART(支持同步、异步)有的是 UART(仅异步)注意区分

TDR 与 RDR

OK,现在我们理清了 USART 寄存器方面的思路,知道 CPU 核心可以直接通过物理地址访问任意一个 USART 寄存器。

⚠️注意:

  • CPU 能访问 USART 内部的寄存器,那么是谁让 CPU 访问这些寄存器的呢?
  • 答案是:CPU 执行的是你未来写的代码,你的代码写了要访问寄存器的指令,那么 CPU 才去访问的,换句话说:CPU 访问 USART 寄存器,就是程序员让 CPU 访问的,CPU 控制 USART,人控制 CPU,编程的本质之一,就是指导 CPU 工作。

  • 在物理上,USART_DR 寄存器有两个,一个是 TDR,一个是 RDR 分别让用户来发和收数据

  • 这两个寄存器被映射到了同一个物理地址,但是官方没有披露具体是怎么做到访问同一个地址,从而做到访问不同寄存器,我们可以推测通过操作类型 (rw) 来区分要访问哪一个寄存器。

  • rw : 寄存器中特定的比特位具有 read 和 write 权限

  • 地址偏移,结合上面的内容,该字段很容易理解。

  • 虽然 USART_DR 寄存器是 32 位的,但是实际使用的只有 9 位,因为 USART 可编程数据位最多是 9 位。

三、USART发送标志位介绍

当发送方要发送数据,主要过程分为:

  • 用户把数据拷贝到 TDR
  • 然后你就别管了,USART 自己移动到移位寄存器,然后再时钟的驱使下,开始进行发送,也就是 USART 发送对用户来讲是黑盒的

可是: USART 发送不是一蹴而就的,而是一步一步发的,那么万一在发送期间,用户又往 TDR 写数据了,会发生什么问题?

场景 1:只要 TDR 有数据,USART 就要移动到移位寄存器,如果没发完,数据覆盖,错误

场景 2:USART 把自己的数据发完 && TDR 还有数据,USART 再移动到移位寄存器,再发。USART 真实采用的是这种情况。但是,潜台词是:用户的数据,会因为 USART 在忙,而暂时保存在 TDR 中,可是,这个过程对用户来讲都是透明的,用户又不知道!用户写完到 TDR,如果再写,就自己把自己的数据覆盖了。

可是,无论是场景 1,还是场景 2,用户不管啊,用户又不知道 USART 有没有发完,如果覆盖了,这个锅谁背啊?肯定是 USART,要是 TDR 我不能写,USART 你得告诉我啊。 所以,USART 在自己的收发过程中,必须要提供自己收发状态的信息,这部分信息,叫做 USART 的状态寄存器 (USART_SR:USART Status Register),可以预言,该寄存器内部,一定有标志位来表明用户是否可以写入 TDR 的寄存器。

USART 发送状态信息,由这 10 个标志位标识,《STM32F103xx 参考手册》看到所有标志位信息。但是现在没必要,需要再查,现在我们只需要关心的是两个标志位:

这两个标志位与发送有关。

  • TXE (Transmit data registerempty) 目前需要关注的是:当 TDR 寄存器中的数据被 USART 硬件模块转移到移位寄存器的时候,该为被硬件设置为 1,潜台词就是,用户需要检测这个比特位,当 TXE 是 1 的时候,表明 TDR 空了,用户才可以写。这样 USART 数据覆盖的锅,甩给了用户,从此丰俭由人。 对 USART_DR 进行写入,TXE 会自动清 0。这句话的潜台词就是,用户发现 TXE 是 1,表明 TDR 空了,用户可以写,用户写入,TXE 清零,表明 TDR 正在被占用,请勿在写。

  • TC (Transmissioncomplete) 目前需要关注的是:当包含有数据的一帧发送完成之后并且 TXE = 1(TDR 为空),USART 硬件模块会自动将该位设置为 1。

  • rc: 通常代表 "Read‑Clear"。这表示是一个 “读取并清除” 的操作

  • wz: 代表 "Write Zero"。这表示向某个寄存器写入 0。向该位写 0 来清除标志;写 1 则无影响(这是 STM32 的标准做法)

  • r: 代表 "Read"。这表示一个读取操作

阻塞模式发送

之前我们讲述的发送过程,就是要求用户,发送数据的时候,必须对 TXE 进行检测,如果TXE==0,表示 TDR 数据,并没有平移到发送移位寄存器,那么用户就必须阻塞在检测 TXE 标志位的位置处,进行等待,这种模式就叫做阻塞式发送。

在用户角度,如果使用阻塞模式进行数据发送一个或者多个数据之后,怎么知道 USART 彻底把数据发送出去(发送移位寄存器和 TDR 都为空)?如下图中 ③ 标识的区域是 TC 标志被置 1 时,表示要发送的数据已全部发送完成,这时如果还有待发送的数据就可以进行发送。

四、USART接收标志位介绍

当接收方要接收数据,主要过程分为:

  • 在时钟的驱使下 USART 自己识别起始位,接受数据,移动数据到移位寄存器,识别结束位,判定接受一个数据帧结束,自动校验,校验成功自动将数据位平移到 RDR 寄存器
  • 用户只需要读取 RDR 寄存器中的数据即可

注意:

  • 根据之前的内容我们知道,用户是可以直接用物理地址读取 RDR 的,所以读取 RDR 并不困难
寄存器偏移量 (Offset)地址计算过程最终地址
USART_SR0x000x4001 3800 + 0x000x4001 3800
USART_DR0x040x4001 3800 + 0x040x4001 3804
USART_BRR0x080x4001 3800 + 0x080x4001 3808
USART_CR10x0C0x4001 3800 + 0x0C0x4001 380C
USART_CR20x100x4001 3800 + 0x100x4001 3810
USART_CR30x140x4001 3800 + 0x140x4001 3814
USART_GTPR0x180x4001 3800 + 0x180x4001 3818

可是:

用户怎么知道 RDR 内部,已经有数据了呢? USART 需要有方式,能表示 RDR 寄存器的状态,类似发送,接收的时候,也是状态寄存器 USART_SR 来标识接收状态,其中,RXNE 标志位就是完成该项工作的。

在 UART 数据接收过程中:

状态寄存器 (USART_SR)

  • 当 RDR 寄存器中没有数据的时候,RXNE 标志位被设置为 0
  • 当 RDR 寄存器中有数据的时候,RXNE 标志位被设置为 1
  • RXNE: Read data register not empty

如下图中 ①②③ 标识的区域是 RXNE 标志,表示读数据寄存器非空 (Read data register not empty)。当接收移位寄存器中的数据被转移到 USART_DR 寄存器中,该位被硬件自动置位。对 USART_DR 寄存器的读操作可以将该标志位清零。

所以,当 USART 在接受数据期间,那么用户就应该一直检测 RXNE 标志位,只要该标志位一直是 0,那么用户就应该一直阻塞在检测的位置,直到 RXNE == 1 表示有数据了,才能读取。

阻塞模式接收

如果使用阻塞模式进行数据接收,程序需要轮询判断如下图所示的 ①②③ 的 RXNE 标志,主要逻辑如下:

  1. RXNE 标志被置 0,表示暂时没有数据,要求用户轮训检测该标志位,标志位为 0,就有一直阻塞在这里。
  2. 收到数据,RXNE 标志被置 1,表示接收到 1 字节数据,用户将读到的数据,保存到自己定义内存区域,读取的时候,会自动触发清除 RXNE 标志,然后继续检测 RXNE ,等待接收下一个数据。
  3. 达到期望接收的数据个数或等待数据超时,退出本次阻塞模式接收流程。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询