4、Modbus ASCII协议帧结构:与RTU的区别、LRC校验计算

好,咱们接着聊。上一节我们把RTU模式讲透了,这一节轮到ASCII模式了。说实话,我在楼宇自控项目里,用ASCII模式的场景其实不多。但为什么还要讲?因为有些老设备,尤其是欧洲过来的暖通控制器,就认ASCII。你不懂它,连上去了数据也是乱码。

ASCII模式,说白了就是把每个字节拆成两个ASCII字符来发送。你想想看,一个字节是8位,拆成两个十六进制字符,每个字符再转成对应的ASCII码。嗯,这样传输效率确实低了,但好处是抗干扰能力强。我在一个老旧的水冷机组项目里遇到过,现场变频器干扰特别大,RTU模式死活通讯不上,换成ASCII模式就稳了。

4.1 ASCII帧结构长什么样?

先看帧结构,我直接给你列出来:

起始符 | 地址码 | 功能码 | 数据区 | LRC校验码 | 结束符
  ':'   |  2字节 |  2字节 |  N字节  |   2字节   | CR+LF

注意看,这里每个字段都是ASCII字符。比如地址码0x01,在ASCII帧里不是发一个字节0x01,而是发两个字符'0'和'1',对应的ASCII码是0x30和0x31。

我刚开始接触ASCII模式时,犯过一个低级错误。我用串口调试助手直接发十六进制数据,结果设备没反应。后来才反应过来——ASCII模式要求你发的是字符,不是原始字节。这个坑,我替你们踩过了。

4.2 与RTU模式的核心区别

咱们直接上对比表,这样更清楚:

对比项 RTU模式 ASCII模式
字节表示 直接发送二进制字节 每个字节拆成两个ASCII字符
起始标识 3.5字符静默时间 字符':' (0x3A)
结束标识 3.5字符静默时间 CR+LF (0x0D 0x0A)
校验方式 CRC-16 (2字节) LRC (1字节,转2字符)
传输效率 高(数据量小) 低(数据量翻倍)
可读性 差(二进制,人眼看不懂) 好(直接显示字符,调试方便)
适用场景 高速、大数据量通讯 干扰大、低速、调试阶段

我个人习惯是:项目调试阶段先用ASCII模式,因为你能直接在串口工具里看到':'、'0'、'1'这些字符,一眼就能判断报文对不对。等调试通了,再切回RTU模式跑正式数据。

4.3 LRC校验计算——其实很简单

LRC校验,全称是纵向冗余校验。听起来挺唬人,但算法比CRC简单太多了。我当年第一次算LRC时,手算都能算出来。

计算步骤就三步:

  1. 累加:把地址码、功能码、数据区所有字节加起来(注意,是原始字节值,不是ASCII字符)
  2. 取补:对累加和取补码(也就是256减去累加和,或者按位取反再加1)
  3. 取低8位:只保留结果的最低8位

举个例子,假设我们要发送一条报文:读取地址0x01设备的寄存器,功能码0x03,起始地址0x0001,读取2个寄存器。

原始数据字节:

地址码: 0x01
功能码: 0x03
起始地址高: 0x00
起始地址低: 0x01
寄存器数量高: 0x00
寄存器数量低: 0x02

第一步,累加:

0x01 + 0x03 + 0x00 + 0x01 + 0x00 + 0x02 = 0x07

第二步,取补码:

0xFF - 0x07 + 1 = 0xF9

或者更简单:256 - 7 = 249,转十六进制就是0xF9。

第三步,LRC校验码就是0xF9。在ASCII帧里,它要转成两个字符'F'和'9'发送。

完整ASCII报文示例:

: 0 1 0 3 0 0 0 1 0 0 0 2 F 9 CR LF

实际发送的十六进制数据:

3A 30 31 30 33 30 30 30 31 30 30 30 32 46 39 0D 0A

我的调试小技巧:

写代码算LRC时,我习惯用这个C语言片段,简单粗暴:

unsigned char calcLRC(unsigned char *data, int len)
{
    unsigned char lrc = 0;
    for(int i = 0; i < len; i++)
    {
        lrc += data[i];
    }
    lrc = (~lrc) + 1;  // 取补码
    return lrc;
}

注意,累加时用unsigned char,溢出自动截断,最后取补就行。

4.4 避坑指南——我踩过的几个坑

坑一:LRC计算范围搞错

我曾经在项目里,把起始符':'也算进LRC计算了。结果设备一直回复异常码。查了半天手册才发现,LRC只计算地址码到数据区,起始符和结束符都不参与校验。这个错误,我印象特别深,因为那天加班到晚上十点才找到原因。

坑二:ASCII字符大小写问题

LRC算出来是0xF9,你发'F'还是'f'?有些设备只认大写,有些大小写都认。我建议统一用大写,兼容性最好。我在一个德国品牌的冷水机组上就遇到过,发小写字母它直接忽略。

坑三:结束符不能少

ASCII帧的结束符是CR+LF,也就是回车换行。有些工程师只发了CR,没发LF,结果设备一直等后续数据,超时后才报错。记住,CR和LF一个都不能少。

重要提醒:

ASCII模式下,帧与帧之间的间隔没有严格的时间要求。不像RTU模式有3.5字符的静默时间限制。你发完一帧,等多久发下一帧都行。这也是ASCII模式在干扰环境下更稳定的原因之一——不怕时间抖动。

4.5 什么时候该用ASCII?

我个人总结了三类场景:

  • 调试阶段:用ASCII模式,报文可读性强,一眼就能看出问题
  • 强干扰环境:变频器、大功率电机附近,ASCII模式更抗造
  • 低速通讯:9600bps以下,ASCII模式效率损失可以接受

但如果你做的是大型楼宇自控系统,几百个点位轮询,我建议还是老老实实用RTU。ASCII模式数据量翻倍,轮询周期会拉长,实时性就差了。

好了,ASCII协议的帧结构和LRC校验就讲到这里。下一节我们聊聊Modbus的异常处理——设备返回异常码时,你该怎么排查?