3. 数据链路层协议:串口通信的“交通规则”

大家好,我是你们的嵌入式讲师。今天咱们聊聊串口通信里最核心的部分——数据链路层协议。

说白了,串口通信就像两个人打电话。你说话,我听着。但这里有个问题:你怎么知道对方什么时候开始说?什么时候说完?说的内容对不对?

嗯,这就是数据链路层要解决的事。

3.1 异步串行通信的基本框架

先看一张图,这是我用SVG画的异步串行通信时序图。你盯着看30秒,后面讲的内容就全明白了。

异步串行通信时序图(8N1格式) 起始位 (低电平) D0 D1 D2 D3 D4 D5 D6 D7 无校验位 停止位 (高电平) T_bit 空闲(高电平) 空闲(高电平)

这张图展示的是最常用的8N1格式——8个数据位、无校验、1个停止位。你仔细看,空闲时总线是高电平。当要发送数据时,先拉低一个位时间,这就是起始位。然后依次发送8个数据位,最后拉高一个位时间作为停止位。

核心要点:异步串行通信不需要时钟线。收发双方靠约定的波特率来同步。起始位就是“我要开始说话了”的信号。

3.2 起始位:通信的“发令枪”

起始位的作用很简单——告诉接收方:“注意,数据来了!”

它就是一个位时间的低电平。为什么是低电平?因为空闲时总线是高电平。从高到低的跳变,就是一个明显的“开始”信号。

我在项目中遇到过一个问题:有个设备偶尔会丢第一个字节。查了半天,发现是起始位检测太灵敏了,把噪声也当成了起始位。后来加了去抖处理,问题就解决了。

我的建议:在固件里做起始位检测时,最好连续采样3次确认是低电平,再开始接收数据。这样可以滤掉毛刺干扰。

3.3 数据位:真正要传的内容

数据位就是你要发送的字节。通常是5到9位,最常见的是8位。为什么是8位?因为一个字节就是8位,方便和内存数据对应。

发送顺序是LSB优先,也就是最低位先发。比如你要发送0x55(二进制01010101),先发最低位1,再发0,再发1...依次类推。

你想想看,如果发送顺序搞反了,接收方收到的数据就全乱了。我见过一个新手,在自定义协议里用了MSB优先,结果和标准UART不兼容,折腾了两天才发现。

3.4 校验位:数据正确性的“保险”

校验位是可选的。常用的有奇校验和偶校验。

  • 奇校验:数据位中1的个数 + 校验位 = 奇数
  • 偶校验:数据位中1的个数 + 校验位 = 偶数

举个例子:数据0x55(二进制01010101),有4个1。如果用奇校验,校验位就是1(让总1的个数变成5,奇数)。如果用偶校验,校验位就是0(保持4个1,偶数)。

注意:校验位只能检测奇数个位的错误。如果两个位同时翻转,校验位是检测不出来的。所以对可靠性要求高的场景,建议用更高级的CRC校验。

我曾经在一个工业控制项目里,因为省掉了校验位,结果在强电磁干扰环境下,数据错误率高达5%。后来加上奇校验,错误率降到了0.1%以下。嗯,有些钱不能省。

3.5 停止位:通信的“句号”

停止位是1个、1.5个或2个位时间的高电平。它告诉接收方:“这一帧结束了。”

为什么需要停止位?两个原因:

  1. 给接收方时间处理刚收到的数据
  2. 为下一帧的起始位做准备(从高到低的跳变)

大多数场景用1个停止位就够了。如果通信距离远、干扰大,可以考虑用2个停止位。1.5个停止位比较少见,主要在老的终端设备上见过。

3.6 波特率:通信的“节拍器”

波特率就是每秒传输的符号数。在串口通信里,一个符号就是一个位,所以波特率等于比特率。

常用波特率有:9600、19200、38400、115200、921600等。

波特率 位时间(us) 传输1字节时间(us) 典型应用
9600 104.17 1041.7 调试终端、GPS模块
115200 8.68 86.8 常见嵌入式设备
921600 1.09 10.9 高速数据采集

位时间的计算公式很简单:

位时间(us) = 1,000,000 / 波特率

例如:波特率115200
位时间 = 1,000,000 / 115200 ≈ 8.68 us

传输一个字节(8N1格式)需要10个位时间:1个起始位 + 8个数据位 + 1个停止位。所以:

传输1字节时间 = 10 × 位时间 = 10 / 波特率(秒)

波特率115200时:10 / 115200 ≈ 86.8 us

关键点:收发双方的波特率误差必须在±2%以内。误差太大就会导致采样点偏移,出现数据错误。我建议用晶振频率能整除的波特率,比如11.0592MHz晶振配9600波特率,分频后几乎没有误差。

3.7 异步串行通信的时序细节

接收方怎么知道什么时候采样?它靠的是过采样技术。通常用16倍波特率的时钟来采样。

流程是这样的:

  1. 检测到起始位的下降沿
  2. 等待8个采样时钟(半个位时间),在位的中间采样
  3. 之后每16个采样时钟采样一次(一个位时间)
  4. 连续采样8次,得到8个数据位
  5. 再采样停止位,确认是高电平

为什么要采在位的中间?因为信号在跳变时不稳定,中间位置最可靠。这个技巧我在很多项目里都用过,效果很好。

避坑指南:我曾经在一个项目里,因为时钟精度不够,导致采样点逐渐偏移。最后一位的采样点已经靠近跳变沿了,偶尔会采错。后来换了精度更高的晶振,问题就解决了。所以,时钟精度真的很重要。

3.8 常见数据格式总结

串口通信的数据格式用“数据位-校验位-停止位”来表示。常见的有:

  • 8N1:8数据位,无校验,1停止位。最常用。
  • 8E1:8数据位,偶校验,1停止位。
  • 8O1:8数据位,奇校验,1停止位。
  • 7E1:7数据位,偶校验,1停止位。老设备常见。
  • 8N2:8数据位,无校验,2停止位。抗干扰好。

我个人习惯,新项目一律用8N1。除非有特殊要求,比如和旧设备兼容,或者通信环境特别恶劣。

好了,数据链路层协议就讲到这里。这些概念是串口通信的基石,理解了它们,后面讲协议帧、流控制、错误处理就容易多了。

可可精品资料库