前言
文章合集:
嵌入式系列
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 的功能框图,其中各部分的主要功能如下:
- 图中 ① 标识区域是 USART 相关的管脚;
- 图中 ② 标识区域是 USART 波特率控制相关;
- 图中 ③ 标识区域是 USART 控制寄存器相关;
- 图中 ④ 标识区域是 USART 状态寄存器相关;
- 图中 ⑤ 标识区域是 USART 数据收、发相关核心功能框图;
- 图中 ⑥ 标识区域在 IrDA(红外模式)和智能卡模式下才会使用。
该图所示的功能模块,对于萌新而言太过庞杂。对于仅使用 USART 标准通信功能而言最核心的就是图中 ⑤ 标识的区域部分,我们只把 ⑤ 标识的区域部分拿出来进行简化。
简化结构图:
当用户将待发送的字节数据写入到数据寄存器(USART_DR / TDR)中,USART 控制器会首先把 TDR 的数据平移至发送移位寄存器,然后 USART 的发送控制器,就会将发送移位寄存器中的数据,自动按照数据位通过 Tx 管脚依次发送出去。
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_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 |
有了这些地址,CPU 就可以直接访问任意一个寄存器,包括我们的数据寄存器 DR。
而的 STM32F103ZET6 系列单片机,内部包含 5 个串口模块,每一个都有自己的地址范围:
串口模块详细地址清单
| 外设 | 所在总线 | 起始地址 | 结束地址 | 地址空间大小 | 类型 |
|---|---|---|---|---|---|
| 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 | 通用异步串行接口 |
主要差别
- 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_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 |
可是:
用户怎么知道 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 标志,主要逻辑如下:
- RXNE 标志被置 0,表示暂时没有数据,要求用户轮训检测该标志位,标志位为 0,就有一直阻塞在这里。
- 收到数据,RXNE 标志被置 1,表示接收到 1 字节数据,用户将读到的数据,保存到自己定义内存区域,读取的时候,会自动触发清除 RXNE 标志,然后继续检测 RXNE ,等待接收下一个数据。
- 达到期望接收的数据个数或等待数据超时,退出本次阻塞模式接收流程。