TXE & RXNE
1. TXE (Transmit Empty) —— 发送寄存器“空”了
- 全称:Transmit Data Register Empty
- 人话:“老板(CPU),我的盘子空了,你可以把下一道菜(数据)端给我了!”
- 状态:
- 0 (Reset):盘子里有菜,正在忙着往外运,千万别放新菜,否则会把旧的压烂。
- 1 (Set):盘子空了,赶紧放新菜,不然我就闲着了。
2. RXNE (Receive Not Empty) —— 接收寄存器“不空”
- 全称:Receive Data Register Not Empty
- 人话:“老板(CPU),信箱里有信(数据)来了,赶紧拿走!”
- 状态:
- 0 (Reset):信箱是空的,别来拿,拿了也是空气(乱码)。
- 1 (Set):信箱里有货,快读走!如果你不读走,下一封信来了就会把这封信挤爆(发生 Overrun 错误)。
二、 核心原理:为什么需要它们?(双缓冲机制)
新手容易以为:单片机发送数据就是 CPU 直接把数据扔到电线上。 错! 其实中间隔着两个“房间”。
我们以 发送 (TX) 为例,内部其实有两个寄存器:
- TDR (发送数据寄存器):相当于备餐区。
- Shift Register (移位寄存器):相当于传菜员(真正干活把数据一位一位挤出去的地方)。
TXE 的工作流程:
- 你写代码
SPI_SendData(0x55)。 - 数据
0x55掉进 TDR (备餐区)。此时 TXE = 0(忙)。 - 硬件自动检查:传菜员(移位寄存器)有空吗?
- 如果有空,硬件瞬间把
0x55从 TDR 挪到 移位寄存器。 - 重点来了! 此时数据还在移位寄存器里慢慢发,但 TDR 已经空了!
- TXE 变回 1。
- CPU 看到 TXE=1,虽然上一个字节还没发完(还在传菜员手里),但 CPU 已经可以把下一个字节放入 TDR 了。
这就是“流水线”作业,效率极高!
三、 代码怎么写?(标准套路)
在写“轮询模式”(Polling)的驱动时,这两个标志位是必须判断的。
1. 发送数据(标准姿势)
C
// 这里的 waiting 就是在等 TXE 变 1
// 意思:只要 TXE 不是 1 (还没空),我就死等,不往里写
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
// 终于空了,写入数据,标志位会自动清除
SPI_I2S_SendData(SPI1, byte);2. 接收数据(标准姿势)
C
// 这里的 waiting 就是在等 RXNE 变 1
// 意思:只要 RXNE 不是 1 (还没收到),我就死等,不读
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
// 终于有数据了,读出来,RXNE 会自动变回 0
data = SPI_I2S_ReceiveData(SPI1);四、 资深工程师的“避坑指南” (如果你用 STM32,必看!)
这里有两个新手一定会掉进去的深坑,请务必记在小本本上:
坑 1:TXE 不等于“发送完成”!(最痛的领悟)
- 误区:很多新手在 SPI 发送完最后一个字节后,看到
TXE == 1,就立马拉高 CS 引脚(结束通信)。 - 后果:数据发丢了!
- 原因:回顾上面的原理,
TXE=1只是代表 TDR(备餐区) 空了,但数据还在 移位寄存器(传菜员) 里正在发呢!你这时候拉高 CS,传菜员干到一半就被你掐断了。 - 解决:发送最后一个字节并想结束传输时,不能只看 TXE,必须看 TC (Transmission Complete) 标志!
Wait TXE=1-> 发送数据Wait TXE=1(表示放入TDR成功)Wait TC=1(表示移位寄存器也发完了,彻底空闲) -> 拉高 CS。
坑 2:RXNE 如果不读,后果很严重 (Overrun 错误)
- 如果你开了接收中断,但因为某些原因(比如中断里处理太慢,或者逻辑写错了)没有把数据读走(即没有读取 DR 寄存器)。
- 硬件会发现:“卧槽,新数据来了,老数据还在!”
- 这时候会触发 ORE (Overrun Error)。
- 后果:一旦触发 ORE,很多单片机(特别是 STM32)的串口或 SPI 会锁死,再也收不到数据,直到你手动清除 ORE 标志。
坑 3:如何清除这两个标志?
- 不像有些标志位需要你写
ClearFlag()函数。 - TXE:只要你往数据寄存器里写(Write DR),TXE 自动清零。
- RXNE:只要你从数据寄存器里读(Read DR),RXNE 自动清零。
- 警告:如果你调试时在 Watch 窗口里查看了 DR 寄存器的值,调试器会把这个值“读”走,导致代码里的 RXNE 突然消失,让你查不出 Bug。
总结
- TXE = 能不能填新数据?(1=能,0=不能)
- RXNE = 有没有收到新数据?(1=有,0=没有)
- 大坑:TXE 变 1 不代表物理发送结束,断开连接前一定要查 TC。