为什么UART发送和接收要使用双寄存器设计? 核心原因是慢速设备和快速CPU的矛盾。
移位寄存器是"慢工出细活"的角色 —— 它要一位一位地发送/接收数据(比如 115200 波特率下,一个字节要 ~87μs)。
数据寄存器是 CPU 的"快速通道" —— CPU 一条指令就能读写完成。
发送
TDR = Transmit Data Register TXE = Transmit Data Register Empty -> 标志位的TX/RX来自通讯标准缩写
从发送端来看,是这样的:
CPU 写入 → TDR(发送数据寄存器) → 移位寄存器 → TX 引脚
↑ 快速 ↑ 慢速(逐位发送)
在STM32F103的UART硬件上,移位寄存器一次只能存储1字节的数据,TDR也是。所以数据是从TDR逐个字节复制到移位寄存器上的,CPU也需要一个一个字节地写入。
当一个字节的数据被复制到移位寄存器后,数据就会通过TX引脚逐个bit地发送出去,这个发送速度和CPU的运行速度来比,是很慢的。
如果没有TDR,CPU每次写入后,都必须要等待一个字节发送完才能继续写入下一个字节,会浪费大量的CPU时间在等待上。
有了TDR后:
- CPU写入TDR瞬间完成
- 硬件会自动将TDR中的数据复制到移位寄存器并发送
- 当TDR的数据被复制到移位寄存器后,TXE标志位就会置1,表示CPU可以继续往TDR写入下一个字节的数据
- 以此循环
效率提升来源何处
整个串口的物理发送速度,最终就是被移位寄存器的发送速度(波特率)卡死的,这是物理上限,永远无法突破! 那么问题来了,双缓冲区既然不能提升最终的发送速度,意义是什么呢? 答案是可以解放CPU的等待时间,CPU不用死等发送,而是可以转头去处理其他任务。等到TXE=1,再回来继续发送。
接收
RDR = Receive Data Register RXNE = Receive Data Register Not Empty
接收端也是类似的逻辑:
RX 引脚 → 移位寄存器 → RDR(接收数据寄存器) → CPU 读取
↑ 慢速(逐位接收) ↑ 快速
移位寄存器在接收下一个字节时,CPU可以去读取RDR,这样接收硬件就不用非要等CPU读完,才开始读下一个字节(再说这也是不现实的)。 同样地,当有数据可读,RXNE标志位就会置1。
但需要小心的是,如果RDR中的数据没有被及时读取,可能会被下一个移位寄存器收到的数据覆盖掉。