4、Modbus ASCII模式:与RTU的区别、LRC校验、什么时候该用ASCII?一个老项目的教训。

好,咱们接着聊。上一节我们把RTU模式讲透了,这一节来聊聊它的“老大哥”——ASCII模式。

说实话,现在做农业物联网,90%以上的场景都在用RTU。但ASCII模式并没有死,它只是退到了特定的角落里。我当年在一个老项目里就吃过它的亏,今天拿出来说说,希望能帮你少走弯路。

4.1 ASCII与RTU:到底差在哪?

先看一张对比表,一目了然:

对比项 RTU模式 ASCII模式
数据表示 二进制字节 ASCII字符(十六进制字符串)
帧长度 短(8字节左右) 长(约为RTU的2倍)
校验方式 CRC-16(2字节) LRC(1字节)
帧分隔符 3.5字符静默时间 ':'开头,CR/LF结尾
传输效率 低(约50%效率)
可读性 差(二进制乱码) 好(可直接看字符串)

说白了,ASCII模式就是把每个字节拆成两个ASCII字符来发送。比如0x1A,RTU直接发一个字节,ASCII要发'1'和'A'两个字符。效率直接砍半。

但为什么还要用它?

核心原因:ASCII模式不需要精确的定时控制。

RTU依赖3.5字符时间的静默来识别帧边界,这在低速或不可靠的链路上容易出错。ASCII用明确的字符作为帧头帧尾,容错性更好。

4.2 LRC校验:简单但够用

ASCII模式用的是LRC校验,全称Longitudinal Redundancy Check,纵向冗余校验。

它的算法比CRC简单太多了。我当年第一次手写LRC时,十分钟就调通了。CRC我折腾了一下午。

LRC的计算方法:

// LRC计算函数
uint8_t CalculateLRC(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字节)全部加起来,结果应该是0。

举个例子:

发送数据:0x01 0x03 0x00 0x00 0x00 0x01
累加和:0x01 + 0x03 + 0x00 + 0x00 + 0x00 + 0x01 = 0x05
LRC = (~0x05) + 1 = 0xFA + 1 = 0xFB

完整帧(ASCII格式):
: 0 1 0 3 0 0 0 0 0 0 0 1 F B CR LF

小技巧:LRC只校验数据部分,不包括起始字符':'和结束符CR/LF。这一点和CRC不同,CRC是校验整个帧(包括地址域)。

4.3 什么时候该用ASCII?

我个人总结了三类场景:

  1. 低速链路(1200bps以下):波特率太低时,3.5字符时间会很长,RTU的帧间隔难以保证。ASCII用字符分隔,不受影响。
  2. 无线透传模块:有些廉价的无线模块会吞掉静默时间,导致RTU帧粘连。ASCII模式可以完美避开这个问题。
  3. 调试阶段:ASCII帧可以直接用串口助手看,方便排查问题。我习惯在开发初期先用ASCII,稳定后再切RTU。

警告:ASCII模式不要用于高速数据采集!比如土壤传感器每秒上报一次数据,ASCII的帧长度是RTU的两倍,总线负载会翻倍。我曾经见过一个项目,用ASCII模式跑115200波特率,结果CPU大部分时间都在处理字符转换,得不偿失。

4.4 一个老项目的教训

2018年,我接手过一个温室大棚的改造项目。原来的系统用的是ASCII模式,运行了五六年一直没问题。

新来的工程师觉得ASCII效率低,自作主张改成了RTU。结果一上线就出问题——传感器数据经常丢包,有时候连续几分钟收不到数据。

排查了三天,最后发现是无线模块的问题。那批模块对数据帧之间的静默时间非常敏感,稍微有点抖动就会把两帧数据当成一帧处理。

后来怎么解决的?

我建议他们把波特率从9600降到2400,同时改回ASCII模式。问题立刻消失。

这个教训告诉我:不要为了追求效率而牺牲可靠性。农业现场的环境比实验室复杂得多,有时候“慢一点”反而是最快的。

4.5 实战建议

  • 新项目默认用RTU:效率高,资源占用少,主流方案。
  • 遇到通信不稳定时,试试ASCII:特别是无线场景,ASCII的容错性更好。
  • LRC校验虽然简单,但够用:农业传感器对数据完整性要求没那么苛刻,LRC的漏检率可以接受。
  • 代码里做好模式切换:我习惯在配置文件中留一个开关,方便现场切换。你永远不知道客户会在什么奇葩环境下用你的设备。

好了,ASCII模式就聊这么多。下一节我们讲Modbus的地址映射和寄存器操作,这是写驱动代码的核心。到时候我会分享一个我踩过的坑——关于线圈寄存器和保持寄存器的混淆问题,保证让你印象深刻。