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?
我个人总结了三类场景:
- 低速链路(1200bps以下):波特率太低时,3.5字符时间会很长,RTU的帧间隔难以保证。ASCII用字符分隔,不受影响。
- 无线透传模块:有些廉价的无线模块会吞掉静默时间,导致RTU帧粘连。ASCII模式可以完美避开这个问题。
- 调试阶段: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的地址映射和寄存器操作,这是写驱动代码的核心。到时候我会分享一个我踩过的坑——关于线圈寄存器和保持寄存器的混淆问题,保证让你印象深刻。