UART 帧结构与16倍超采样机制

波特率和比特时间

假设波特率是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,并数够对应的脉冲数即可。

如果接收方性能较低,可以给多一点停止位,留够处理时间。