2. 物理层与数据链路层实战:RS232/RS485物理特性、Modbus RTU协议帧解析、CRC校验实现

各位同学,咱们今天聊点实在的。做嵌入式通信,说白了就是跟物理层和数据链路层打交道。很多新手一上来就调应用层,结果死活连不上,最后发现是电平不匹配、线序接反了,或者CRC算错了。嗯,这些坑我都踩过,今天咱们就一次性把它们讲透。

2.1 RS232与RS485:物理层的两个老伙计

先说说RS232。这玩意儿历史久远,我最早接触是在大学实验室,用DB9串口线连单片机。它的特点是全双工,收发各用一根线,再加上地线,总共三根就能干活。但有个致命弱点——传输距离短,也就15米左右,而且抗干扰能力一般。

后来做工业项目,发现现场全是RS485。为什么?因为它是差分信号,抗干扰强,距离能到1200米,还能挂32个节点。我记得有一次在工厂调试,现场电机一启动,RS232就乱码,换成RS485立马稳了。从那以后,但凡涉及工业现场,我首选RS485。

特性 RS232 RS485
传输方式 单端(非平衡) 差分(平衡)
最大距离 约15米 约1200米
节点数 1对1 最多32个(标准)
抗干扰 弱 强
典型电平 ±12V ±5V(差分)
实战小贴士: 做RS485时,记得在总线两端各加一个120Ω终端电阻。我曾经因为漏了这个,总线信号反射严重,数据包错得一塌糊涂。

2.2 Modbus RTU协议帧:拆开看看里面长啥样

物理层搞定了,接下来就是数据链路层。Modbus RTU是工业界用得最广的协议之一。它的帧结构其实很简单,我习惯把它拆成四段来看:

  • 地址码(1字节):从站地址,范围1-247。0是广播地址。
  • 功能码(1字节):告诉从站要干什么。比如03是读寄存器,06是写单个寄存器。
  • 数据区(N字节):具体参数,比如起始地址、数据长度、要写入的值。
  • CRC校验(2字节):低字节在前,高字节在后。这个后面细说。

举个例子,读从站1的寄存器,从地址0x0000开始读2个寄存器,请求帧就是:

01 03 00 00 00 02 C4 0B

你看,01是地址,03是功能码,00 00是起始地址,00 02是寄存器数量,C4 0B就是CRC。是不是很直观?

注意: Modbus RTU帧之间要有至少3.5个字符时间的静默间隔。如果间隔太短,从站会把两帧当成一帧处理。我遇到过有人用USB转485调试,波特率设错了,结果帧间隔不对,怎么调都调不通。

2.3 CRC校验实现:手写一个也不难

CRC校验是Modbus RTU的保命符。它用的是CRC-16(Modbus多项式0x8005),计算过程不复杂,但容易出错。我建议你直接拿现成的查表法,效率高,代码也简洁。

下面是我常用的CRC计算函数,C语言实现,查表法:

// CRC-16 Modbus 查表法
uint16_t crc16_modbus(uint8_t *data, uint16_t len) {
    uint16_t crc = 0xFFFF;
    uint16_t i, j;
    
    for (i = 0; i < len; i++) {
        crc ^= data[i];
        for (j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

注意,Modbus RTU发送时,CRC是低字节在前,高字节在后。比如上面算出来CRC是0x0BC4,发送顺序就是C4 0B。这个顺序搞反了,从站就会回复异常码。我刚开始做的时候就被这个坑过,排查了半天。

避坑指南: 我曾经在STM32上移植Modbus RTU,发现CRC总是算不对。后来发现是字节序问题——MCU是小端模式,但Modbus要求大端序。记得在发送前做字节序转换。

2.4 知识体系总览:一张图看懂

下面这张图把本章的核心逻辑串起来了。从物理层的RS232/RS485,到数据链路层的Modbus RTU帧结构,再到CRC校验,每一步都是环环相扣的。

物理层与数据链路层知识体系 物理层 RS232 RS485 数据链路层 Modbus RTU CRC校验 应用层 读写寄存器 异常处理 核心要点: 1. 物理层决定通信的物理基础:RS232适合短距离点对点,RS485适合长距离多节点。 2. 数据链路层负责帧封装与校验:Modbus RTU帧包含地址、功能码、数据、CRC四部分。 3. CRC校验是数据完整性的最后防线:查表法效率高,注意字节序和发送顺序。 4. 帧间隔时间必须大于3.5个字符时间,否则从站会误判。 5. 实际调试时,先用示波器看物理层波形,再用串口助手看数据帧,最后才调应用层。

2.5 实战调试:从波形到数据帧

最后说说调试方法。我个人的习惯是三步走:

  1. 看波形:用示波器抓RS485的A/B线差分信号,确认电平正常、无毛刺。
  2. 看数据:用USB转485模块接电脑,串口助手看原始数据,确认帧结构正确。
  3. 看CRC:用在线CRC计算工具验证,或者自己写个函数打印出来对比。

你想想看,如果波形不对,调软件有什么用?物理层没搞定,上层全是白费功夫。我曾经在一个项目里,从站老是回复异常码,折腾了两天,最后发现是485芯片的RE/DE引脚控制逻辑写反了。嗯,这种低级错误,犯过一次就记住了。

推荐工具: 调试Modbus RTU,我常用Modbus Poll(主站模拟)和Modbus Slave(从站模拟)。配合串口助手抓原始数据,基本能搞定90%的问题。