2、通信协议基础(上):串口通信RS-232/RS-485原理、Modbus RTU协议帧结构、功能码详解、CRC校验计算
各位工程师朋友,咱们今天聊聊通信协议。说实话,搞SCADA系统集成,通信协议就是咱们的“通用语言”。语言不通,设备之间就没办法对话。我见过太多项目因为通信问题调试好几天,最后发现是协议配置错了。所以这一章,咱们把最基础的串口通信和Modbus RTU协议彻底搞明白。
2.1 串口通信:RS-232与RS-485
串口通信,说白了就是设备之间通过一根或两根线,一位一位地传数据。虽然现在以太网很普及,但在工业现场,串口通信依然占据半壁江山。为什么?因为它简单、可靠、成本低。
2.1.1 RS-232:点对点的老将
RS-232是最早的串口标准之一。我记得刚入行时,调试PLC用的就是RS-232接口。它的特点是:
- 传输距离短:一般不超过15米。我在一个项目中试过用20米的线,结果数据全是乱码。
- 点对点通信:一台设备只能和另一台设备通信,不能组网。
- 电压信号:用-15V到-3V表示逻辑“1”,+3V到+15V表示逻辑“0”。
2.1.2 RS-485:工业现场的主力
RS-485才是工业现场的主角。它解决了RS-232的很多痛点:
- 差分信号传输:用两根线(A和B)的电压差来表示数据。抗干扰能力强,传输距离可达1200米。
- 支持多节点:一条总线上可以挂32个设备(理论上更多)。
- 半双工通信:同一时刻只能发送或接收,不能同时进行。
我个人习惯,在SCADA项目中,只要距离超过30米,一律用RS-485。你想想看,现场电机一启动,电磁干扰多强,RS-232根本扛不住。
2.2 Modbus RTU协议帧结构
Modbus协议是工业通信的事实标准。它有三种模式:RTU、ASCII和TCP。咱们重点讲RTU,因为它在串口通信中用得最多。
Modbus RTU的帧结构非常简洁,就四个部分:
| 字段 | 长度 | 说明 |
|---|---|---|
| 地址码 | 1字节 | 从站地址,范围1-247 |
| 功能码 | 1字节 | 告诉从站要做什么操作 |
| 数据区 | N字节 | 具体的数据内容 |
| CRC校验 | 2字节 | 低字节在前,高字节在后 |
举个例子,我要读取1号从站的3个寄存器,起始地址是0x0000。那么发送的报文就是:
01 03 00 00 00 03 05 CB
拆开来看:
01:从站地址03:功能码,读取保持寄存器00 00:起始地址00 03:读取3个寄存器05 CB:CRC校验码
嗯,这里要注意:Modbus RTU的报文是连续的,帧与帧之间必须有3.5个字符以上的空闲时间。否则从站会认为是一帧数据,导致解析错误。
2.3 功能码详解
功能码是Modbus协议的核心。它告诉从站:你要干什么?常用的功能码就那么几个,我列个表:
| 功能码 | 名称 | 作用 | 我常用的场景 |
|---|---|---|---|
| 01 | 读取线圈状态 | 读取DO(数字量输出) | 读取继电器状态 |
| 02 | 读取离散输入 | 读取DI(数字量输入) | 读取按钮、限位开关 |
| 03 | 读取保持寄存器 | 读取模拟量输出或参数 | 读取变频器频率、温度值 |
| 04 | 读取输入寄存器 | 读取模拟量输入 | 读取压力传感器、液位计 |
| 05 | 写单个线圈 | 控制单个DO | 启动/停止电机 |
| 06 | 写单个寄存器 | 设置单个参数 | 设定PID目标值 |
| 15 | 写多个线圈 | 批量控制DO | 同时控制多个阀门 |
| 16 | 写多个寄存器 | 批量设置参数 | 下载配方数据 |
我个人最常用的是03和06功能码。为什么?因为保持寄存器既能读又能写,灵活性最高。你想想看,一个变频器的频率,既可以通过03读取当前值,又可以通过06设定目标值,多方便。
2.4 CRC校验计算
CRC校验是Modbus RTU的“安全锁”。它确保数据在传输过程中没有被干扰。计算方法是:
- 预置一个16位寄存器为0xFFFF
- 取第一个字节与寄存器低8位异或
- 右移一位,高位补0
- 如果移出的位是1,则与0xA001异或
- 重复步骤3-4,直到8位全部处理完
- 取下一个字节,重复步骤2-5
- 最后得到的寄存器值取反,就是CRC码
听起来有点复杂?我给你一个C语言实现的代码片段,直接复制就能用:
unsigned int crc16_modbus(unsigned char *buf, int len) {
unsigned int crc = 0xFFFF;
int i, j;
for (i = 0; i < len; i++) {
crc ^= buf[i];
for (j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc = crc >> 1;
}
}
}
return crc;
}
调用的时候,把报文地址码、功能码、数据区传进去,返回的就是CRC校验码。注意:Modbus RTU的CRC是低字节在前,高字节在后。比如计算结果是0xCB05,发送时要先发0x05,再发0xCB。
好了,这一章的内容就到这里。串口通信和Modbus RTU是SCADA系统集成的基石。下一章咱们继续聊Modbus TCP和DNP3协议,到时候再分享一些我在项目中遇到的坑。记住:通信协议搞懂了,集成工作就成功了一半。