3、Modbus串行链路:RTU与ASCII两种传输模式详解

好,咱们今天聊聊Modbus串行链路上最核心的一个选择题——RTU和ASCII,到底选哪个?

我记得刚入行那会儿,第一次调试一个供水系统的PLC,现场工程师扔给我一本手册,说“你看着配”。我一看,通讯参数里有个“Mode:RTU/ASCII”,当时就懵了。后来踩了不少坑,才把这两个模式彻底搞明白。今天我把这些经验掰开了揉碎了讲给你听。

3.1 两种模式的根本区别

说白了,RTU和ASCII就是两种不同的“打包方式”。

你想想看,Modbus协议要传输的数据,本质上是一串二进制数。RTU模式直接把这些二进制数“原封不动”地发出去。而ASCII模式呢?它先把每个字节拆成两个ASCII字符,再发出去。

举个例子:我要发送一个字节 0x1A

  • RTU模式:直接发 0x1A(8个bit)
  • ASCII模式:先转成字符 '1''A',也就是 0x310x41(16个bit)

看到没?同样的信息,ASCII模式要多发一倍的字节。这就是它们最本质的区别。

核心结论:RTU是“二进制裸传”,ASCII是“字符编码传”。RTU效率高,ASCII可读性强。

3.2 RTU模式详解

RTU模式,全称Remote Terminal Unit,但咱们别被名字唬住。它就是最常用的那种模式,工业现场90%以上的Modbus设备都在用RTU。

3.2.1 报文结构

RTU的报文结构很紧凑:

| 地址码(1字节) | 功能码(1字节) | 数据区(N字节) | CRC校验(2字节) |

嗯,这里要注意,RTU没有起始位和结束位的特殊标记。它靠什么判断一帧报文结束?靠“静默时间”。

具体规则是这样的:

  • 报文开始前,总线必须静默至少3.5个字符时间
  • 报文结束后,总线也必须静默至少3.5个字符时间
  • 帧内两个字节的间隔不能超过1.5个字符时间

我曾经在一个项目中遇到过,因为PLC的扫描周期太长,导致帧内字节间隔超过了1.5个字符时间,从站直接丢弃了报文。排查了整整一下午,最后发现是程序里一个延时函数搞的鬼。

避坑指南:我曾经在调试一个变频器时,发现RTU通讯时好时坏。后来用示波器一抓,发现主站发送完报文后,总线上的静默时间只有2.8个字符时间。从站要求3.5,差那么一点点,就是不认。所以,静默时间一定要留足余量,我一般会设到5个字符时间以上。

3.2.2 CRC校验

RTU用CRC-16校验,多项式是 0x8005。这个算法挺成熟,我就不展开讲了。直接给一个C语言实现,我在项目里一直用这个:

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

注意,CRC校验码是低字节在前,高字节在后。这个顺序搞反了,通讯肯定不通。我刚开始写程序时就犯过这个错,查了半天资料才发现。

3.3 ASCII模式详解

ASCII模式,说白了就是“给人看的”。每个字节都转成两个ASCII字符,然后用特殊的字符标记帧的开始和结束。

3.3.1 报文结构

ASCII模式的报文长这样:

| ':' (0x3A) | 地址码(2字符) | 功能码(2字符) | 数据区(2N字符) | LRC校验(2字符) | CRLF (0x0D 0x0A) |

看到没?开头有个冒号 ':',结尾有回车换行 CRLF。这就好办了,不用靠静默时间判断帧边界了。

举个例子,我要读取从站地址为1的设备的保持寄存器,起始地址0,读取2个寄存器:

RTU模式:01 03 00 00 00 02 C4 0B
ASCII模式:3A 30 31 30 33 30 30 30 30 30 30 30 32 46 42 0D 0A
          :  0  1  0  3  0  0  0  0  0  0  0  2  F  B  CR LF

ASCII模式比RTU模式多了将近一倍的字节数。所以,同样的波特率下,ASCII模式的通讯效率只有RTU的一半左右

3.3.2 LRC校验

ASCII模式用的是LRC校验,比CRC简单多了。就是把所有字节加起来,取补码:

uint8_t lrc_modbus(uint8_t *data, uint16_t len) {
    uint8_t lrc = 0;
    for (uint16_t i = 0; i < len; i++) {
        lrc += data[i];
    }
    lrc = (~lrc) + 1;  // 取补码
    return lrc;
}

注意,LRC校验的是原始字节,不是ASCII字符。比如地址码 0x01,LRC计算时用的是 0x01,不是 '0''1'

我的习惯:ASCII模式虽然效率低,但调试时特别好用。我经常在开发阶段先用ASCII模式,等通讯调通了,再切回RTU模式。因为ASCII报文可以直接用串口助手看,一眼就能看出数据对不对。

3.4 两种模式的对比

咱们用一张表总结一下:

对比项 RTU模式 ASCII模式
数据编码 二进制 ASCII字符
帧长度 短(约1/2) 长(约2倍)
校验方式 CRC-16(强) LRC(弱)
帧边界 静默时间 ':' 和 CRLF
通讯效率 低(约50%)
可读性
适用场景 绝大多数工业现场 调试、低速链路

3.5 如何选择?

我个人习惯是:能用RTU就用RTU。原因很简单:

  • 效率高,同样的波特率能传更多数据
  • CRC校验比LRC可靠得多
  • 绝大多数设备都支持RTU

那什么时候用ASCII呢?

  • 调试阶段:方便看数据,快速定位问题
  • 通讯链路质量很差:ASCII模式有明确的帧边界,不容易被干扰搞乱
  • 设备只支持ASCII:有些老设备确实只支持ASCII,没办法

注意:同一个总线上,所有设备必须使用同一种模式。不能有的设备用RTU,有的用ASCII。否则,从站根本解析不了报文。

3.6 知识体系图

下面这张图,把两种模式的核心逻辑串起来了:

Modbus串行链路传输模式 传输模式 RTU模式 ASCII模式 二进制编码 CRC-16校验 静默时间判帧 效率高 ASCII字符编码 LRC校验 ':'和CRLF判帧 可读性好 选择建议: 工业现场首选RTU,调试或低速链路用ASCII

3.7 实际项目中的选择

最后,说说我个人的经验。在绝大多数项目中,我都会选择RTU模式。原因很简单:

  • 效率高,同样的波特率能传更多数据
  • CRC校验比LRC可靠得多
  • 绝大多数设备都支持RTU

但是,如果遇到以下情况,我会考虑用ASCII:

  • 调试阶段:方便看数据,快速定位问题
  • 通讯链路质量很差:ASCII模式有明确的帧边界,不容易被干扰搞乱
  • 设备只支持ASCII:有些老设备确实只支持ASCII,没办法

我的一个小技巧:在开发阶段,我会先用ASCII模式把通讯调通,然后用串口助手抓一份完整的报文。等切换到RTU模式后,再用这份报文做对比,确保数据一致。这样能省不少排查时间。

好了,关于RTU和ASCII两种模式,咱们就聊到这儿。记住一句话:RTU是效率之王,ASCII是调试之友。根据你的实际场景,选对模式,能少踩很多坑。


专注资料整理