第4章:Modbus ASCII模式——与RTU模式的区别,LRC校验算法,ASCII帧结构
好,聊完了RTU模式,咱们来看看它的“老大哥”——ASCII模式。说实话,现在新项目里用ASCII模式的已经不多了,但你要是去维护一些老设备,尤其是90年代到2000年初的进口仪表、PLC,十有八九会遇到它。我个人习惯是,遇到这种老设备,先看它支持不支持RTU,不支持再切ASCII。
4.1 为什么要有ASCII模式?
你想想看,RTU模式用的是二进制传输,对硬件和时序要求比较高。早期的单片机性能弱,串口中断处理不及时,很容易丢帧。ASCII模式说白了就是把数据变成人能读懂的字母和数字,每个字节拆成两个ASCII字符发送。这样对硬件要求低,哪怕主站处理慢一点,从站也能从容接收。
我记得有一次去一个化工厂调试,现场用的还是80年代的进口流量计,只支持ASCII模式。当时我带的转换器默认是RTU,折腾了半天才发现问题。嗯,从那以后我包里常备一个支持双模式的转换器。
4.2 ASCII帧结构——看得见的报文
ASCII模式的帧结构比RTU“啰嗦”多了。咱们直接看对比:
| 字段 | RTU模式 | ASCII模式 |
|---|---|---|
| 起始符 | 3.5字符静默时间 | ':' (0x3A) |
| 地址码 | 1字节 (0x01) | 2字符 ('0','1') |
| 功能码 | 1字节 (0x03) | 2字符 ('0','3') |
| 数据区 | N字节 | 2N字符 |
| 校验 | CRC-16 (2字节) | LRC (2字符) |
| 结束符 | 3.5字符静默时间 | CR+LF (0x0D 0x0A) |
看到没?ASCII模式最大的特点就是:起始符是冒号':',结束符是回车换行。整个报文里所有数据都是可见的ASCII字符,调试的时候用串口助手直接看,一目了然。
ASCII帧结构示例(读取从站1,起始地址0x0000,读取2个寄存器):
发送:: 01 03 00 00 00 02 FB CR LF
接收:: 01 03 04 00 01 00 02 3A CR LF
注意:实际传输时没有空格,这里是为了方便阅读。每个十六进制数对应两个ASCII字符。
4.3 LRC校验算法——比CRC简单多了
ASCII模式用的校验叫LRC(纵向冗余校验)。说实话,这玩意儿比CRC简单太多了,我当年第一次手写LRC代码,十分钟就搞定了。
LRC的计算规则:
- 把地址码、功能码、数据区所有字节加起来(注意是字节,不是ASCII字符)
- 取和的补码(也就是0xFF - 和 + 1)
- 结果就是LRC值
说白了就是:所有字节求和,取反,加1。或者更简单:0x100 - (和 % 0x100)。
我的小技巧: 实际编程时,LRC计算可以这样写:
uint8_t calcLRC(uint8_t *data, uint16_t len)
{
uint8_t lrc = 0;
for(uint16_t i = 0; i < len; i++)
{
lrc += data[i]; // 累加所有字节
}
lrc = (~lrc) + 1; // 取反加1
return lrc;
}
注意:LRC只校验地址码、功能码和数据区,不包含起始符和结束符。
4.4 实战:手算LRC校验
咱们拿上面的例子来算一下:
发送报文(去掉起始符和结束符):01 03 00 00 00 02
计算过程:
0x01 + 0x03 + 0x00 + 0x00 + 0x00 + 0x02 = 0x06
0x06 取反:0xF9
0xF9 + 1 = 0xFA
所以LRC = 0xFA,对应ASCII字符就是 'F' 和 'A'。
我曾经踩过的坑: 有一次写ASCII模式驱动,怎么调都不对。后来发现LRC计算时,我把起始符':'也算进去了。记住:LRC只计算起始符之后、结束符之前的部分。另外,LRC在帧里是以两个ASCII字符形式存在的,接收端要先转成字节再校验。
4.5 ASCII vs RTU:到底选哪个?
我个人的经验总结:
| 对比项 | ASCII模式 | RTU模式 |
|---|---|---|
| 传输效率 | 低(数据量翻倍) | 高(二进制传输) |
| 硬件要求 | 低(时序宽松) | 高(需要精确计时) |
| 调试难度 | 低(肉眼可读) | 高(需要工具解析) |
| 校验强度 | 中(LRC) | 高(CRC-16) |
| 适用场景 | 老设备、低速链路 | 新设备、高速链路 |
你想想看,同样读取10个寄存器,RTU只需要发送8个字节,ASCII要发送16个字符(还不算起始结束符)。效率差了一倍多。所以现在新设备基本都用RTU了。
但话说回来,ASCII模式有个RTU永远比不了的优势:调试友好。我当年用串口助手调试ASCII设备,直接看文本就能知道报文对不对。RTU的话,你还得开个十六进制显示,或者用专门的抓包工具。
4.6 避坑指南
最后分享几个我实际项目中遇到的坑:
- 字符大小写问题: 有些老设备只认大写字母,有些大小写通吃。我建议统一用大写,省得出问题。
- 结束符缺失: 有些设备发送的结束符只有CR没有LF,或者反过来。写程序时最好兼容两种。
- 起始符延迟: 发送冒号后,如果紧接着发送数据太快,有些老设备会丢第一个字符。我习惯在冒号后加1ms延迟。
- LRC计算范围: 再次强调,只算地址码到数据区,不算起始结束符。
好了,ASCII模式就讲这么多。下一章咱们开始抓包分析,用Wireshark和串口助手实际看看RTU和ASCII报文长什么样。到时候你就知道,纸上得来终觉浅,绝知此事要躬行啊。