波特率和比特时间
假设波特率是9600,就意味着发送一个bit数据用到的时间是 1 / 9600 s ≈ 104 µs,我称之为比特时间。
下面的讨论我们都用 1bit的起始位、1bit的停止位、8bit的数据位、9600的波特率作为前提,这也是非常常用的配置。
如果是 1bit的起始位、1bit的停止位、8bit的数据位,则发送一个Byte的数据,需要消耗10个比特时间,也就是说,在9600的波特率下,1秒最多发送960Byte的数据。
发送方
空闲的时候,一直输出高电平,如果要开始发送数据了,则先输出一个比特时间的低电平,然后开始发送8bit的数据,最后输出一个比特时间的高电平作为停止位。
比如要发 0b00000000:
空闲: 1
起始位: 0 (第1bit)
D0: 0 (第2bit)
D1: 0 (第3bit)
D2: 0
D3: 0
D4: 0
D5: 0
D6: 0
D7: 0
停止位: 1
如果要发 0b11110000:
空闲: 1
起始位: 0 (第1bit)
D0: 0 (第2bit)
D1: 0 (第3bit)
D2: 0
D3: 0
D4: 1
D5: 1
D6: 1
D7: 1
停止位: 1
接收端
接收端会在比特时间的中间进行采样,为了减少抖动带来的干扰,比如一个数据位占104 µs,接收方会在 52 µs的时候采样。 为了能够稳定接收数据,接收端一般会过采样,UART常用的是16倍超采样,也就是一个比特时间内,会采样16次。
下降沿检测
起始位到来的时候,电平会拉低,当硬件监测到电平下降,会触发下降沿检测。 为了防止误触发,硬件采用"少数服从多数"的逻辑进行二次确认:
- 第一次校验:在3、5、7个采样周期,如果三个点中有至少两个是0,说明这确实是一个信号的开始
- 第二次校验:在8、9、10个采样周期,再次确认是否至少有两个点为0
这两个校验都通过了,硬件才会正式认为这是一个起始位,然后开始接收数据位。
这样做有两个好处:第一个是可以抗干扰;第二个是可以对准时钟,通过多次采样,硬件能找到信号的中心点(通常是第8个采样点的附近),从而在后续数据传输中可以在信号最稳定的中间位置读取电平值(这里的最中间位置也是会采样的,不是只有一个Tick)。
硬件时钟并不是完全没有误差的,假设接收方的时钟比发送方快2%,那么到了停止位,采样点可能已经偏移到了位的边缘。这就是为什么UART一次只能传很少的数据位,然后利用停止位和起始位的下降沿来重新校准计数器。
下面照旧来做个示例拆解:每个Bit对应16个Tick。
起始位:目标是确认是信号的开始而不是噪声,然后同步时钟
| 刻度 (Tick) | 硬件动作 | 逻辑判断 |
|---|---|---|
| 0 | 检测到 1 → 0 下降沿 | 同步点:立即重置 16 倍频计数器。 |
| 1 ~ 2 | 保持沉默 | 等待信号从跳变沿稳定下来。 |
| 3, 5, 7 | 第一次采样 | 初步验证:如果这三个点中有 ≥2 个是 0,继续;否则判定为噪声,重置回空闲状态。 |
| 8, 9, 10 | 第二次采样 | 最终确认:如果这三个点中有 ≥2 个是 0,正式确认起始位有效。 |
| 11 ~ 16 | 强制等待 | 已经确认了身份,这段时间硬件"闭眼"直到这个 Bit 彻底结束,防止干扰。 |
数据位:目标是在信号最稳定的时候读取电平
| 刻度 (Tick) | 硬件动作 | 逻辑判断 |
|---|---|---|
| 1 ~ 7 | 跳过(盲区) | 绝对不采样。这是为了避开位与位之间可能的电平过渡抖动。 |
| 8, 9, 10 | 核心采样点 | 读取数据:对这三个点进行"多数表决"。≥2 个为 1 → 该位记录为 1;≥2 个为 0 → 该位记录为 0 |
| 11 ~ 16 | 强制等待 | 完成当前位的读取,等待计数器数满 16 下,然后立即无缝切换到下一个 Bit 的 Tick 1。 |
“对准"和"采样"是相辅相成的,对准是为了找到tick8的位置,采样是在tick8的基础上,对 tick 8、9、10 进行多数表决。
校验位
校验位是加在最后一位数据位(D7)之后、停止位(Stop Bit)之前的一个可选位。
校验位的核心逻辑:数 1 的个数
- 偶校验的目标是使得数据位+校验位中1的总数为偶数,所以如果数据里已经有偶数个1,校验位是0,反之是1。
- 奇校验的目标是使得数据位+校验位中1的总数为奇数,所以数据里如果已经有偶数个1,校验位是1,反之是0。
停止位
在UART中,常见的停止位有 1 位、1.5 位、2 位 这三种。
为什么可以有 1.5位呢?是因为硬件是不会数小数的,它只会数脉冲,对 16倍频的硬件逻辑来说:
- 1.0 位:占用 $1 \times 16 = 16$ 个采样刻度(Ticks)。
- 1.5 位:占用 $1.5 \times 16 = 24$ 个采样刻度。
- 2.0 位:占用 $2 \times 16 = 32$ 个采样刻度。
硬件只需在发送完数据位后,强行让电平保持在1,并数够对应的脉冲数即可。
如果接收方性能较低,可以给多一点停止位,留够处理时间。