Modbus ASCII 基础:从零开始学习工业通信协议
本文详细介绍了Modbus ASCII协议的基础知识,包括协议原理、数据格式、功能码使用、异常处理和数据类型转换等,帮助初学者快速掌握这一重要的工业通信协议,并提供了丰富的实践建议。
Modbus ASCII 基础教程:从零开始学习工业通信协议
Modbus ASCII 是 Modbus 协议家族中的一种重要传输模式,尽管在效率上不如其兄弟协议 Modbus RTU,但它凭借其人类可读的特性,在某些特定场景下(如需要人工调试或通过字符型设备传输时)依然占有一席之地。本教程旨在从零开始,系统地带你全面了解并掌握 Modbus ASCII 协议的核心概念、工作原理和实际应用技巧。
无论是初学者还是希望拓宽知识面的工程师,本文都将为您提供一份详尽的指南。
1. 什么是 Modbus ASCII?
Modbus ASCII 是一种在串行通信线路上,使用 ASCII(美国信息交换标准代码)字符来交换数据的传输模式。与 RTU 模式发送紧凑的二进制数据不同,ASCII 模式将每一个字节的数据都转换为两个可打印的 ASCII 字符进行传输。例如,一个十六进制字节 0x8A 会被转换为 ASCII 字符串 "8A" ("8" 的 ASCII 码是 0x38,"A" 的 ASCII 码是 0x41)。
Modbus ASCII 同样遵循主从(Master-Slave)通信模型:一个主站(Master)设备发起请求,一个或多个从站(Slave)设备响应该请求。
Modbus ASCII 的核心特点在于:
- 人类可读性:由于传输的是 ASCII 字符,可以直接通过串口监视工具查看和理解报文内容,极大地方便了调试和故障排查。
- 宽松的时间要求:与 RTU 模式严格的 3.5 个字符静默时间要求不同,ASCII 模式允许字符之间有长达 1 秒的间隔,这使得它能更好地适应一些较慢的或非实时的通信介质,如某些调制解调器(Modem)或无线电台。
- 明确的帧边界:使用特定的起始符(
:)和结束符(回车符CR和换行符LF)来界定一帧数据,使得帧的识别非常清晰。 - 兼容性:虽然效率较低,但在某些只能处理文本数据的系统中,Modbus ASCII 提供了可靠的通信解决方案。
2. 物理层:Modbus ASCII 的载体
与 Modbus RTU 类似,Modbus ASCII 协议本身也只定义了报文结构,其物理传输依赖于标准的串行通信接口。
2.1 RS-485
RS-485 是工业应用中最常见的选择,因为它支持多点通信,允许一个主站连接多个从站设备。其差分信号传输方式也使其具有很强的抗干扰能力和较远的传输距离(可达1200米)。
2.2 RS-232
RS-232 主要用于点对点通信,即一个主站连接一个从站。 它的传输距离较短(通常在15米以内),抗干扰能力也较弱,常用于设备与本地计算机的短距离连接或调试。
2.3 其他介质
由于 Modbus ASCII 对字符间的时间间隔要求宽松,它也适用于一些特殊的通信介质,例如通过电话线调制解调器或无线数传电台进行远距离传输。
3. Modbus 数据模型:理解数据存储结构
Modbus ASCII 与 Modbus RTU 共享完全相同的数据模型。这意味着设备内部的数据同样被抽象为四个逻辑上独立的存储区,每个区有不同的数据类型和访问权限。
| 数据表 | 类型 | 访问权限 | Modbus协议地址范围 | PLC地址范围 | 典型用途 |
|---|---|---|---|---|---|
| 线圈 (Coils) | 单个位 (Bit) | 读/写 | 0x0000-0xFFFF | 00001-09999 | 控制继电器、开关、指示灯、电机启停等布尔量输出 |
| 离散量输入 (Discrete Inputs) | 单个位 (Bit) | 只读 | 0x0000-0xFFFF | 10001-19999 | 读取数字输入信号、传感器状态(门开/关)、按钮状态等 |
| 保持寄存器 (Holding Registers) | 16位字 (Word) | 读/写 | 0x0000-0xFFFF | 40001-49999 | 存储配置参数、设定值、控制变量、模拟量输出、PLC内部变量 |
| 输入寄存器 (Input Registers) | 16位字 (Word) | 只读 | 0x0000-0xFFFF | 30001-39999 | 读取测量值(温度、压力)、设备状态、传感器数据、模拟量输入 |
重要提示:
- PLC 地址是工业领域常用的基于 1 的编址方法,而 Modbus 协议地址是通信时实际使用的基于 0 的编址。转换关系为:
Modbus协议地址 = PLC地址 - 基础地址。例如,PLC 地址40003对应 Modbus 协议中的偏移地址0x0002(40003 - 40001)。 - 数据模型的划分与传输模式(ASCII 或 RTU)无关,它是 Modbus 协议的核心规范之一。
4. 协议基础结构:Modbus ASCII 数据帧
一个标准的 Modbus ASCII 数据帧是可读的文本字符串,其结构和 RTU 帧有显著区别,但承载的核心信息是相同的。
4.1 数据帧格式概览
[起始符] [从站地址] [功能码] [数据字段] [LRC校验] [结束符]
1字符 2字符 2字符 N字符 2字符 2字符(CR LF)
4.1.1 起始符 (Start Character) - 1个字符
- 作用:明确标识一帧数据的开始。
- 内容:固定为冒号(
:),其 ASCII 码为0x3A。
4.1.2 从站地址 (Slave Address) - 2个字符
- 作用:唯一标识网络上的目标从站设备。
- 格式:一个 8 位的从站地址(例如
0x0B,即十进制的 11)被转换为两个 ASCII 字符("0"和"B")进行传输。 - 范围:地址范围为 1-247。 地址
0是广播地址。
4.1.3 功能码 (Function Code) - 2个字符
- 作用:定义主站请求的操作类型。
- 格式:与从站地址一样,一个 8 位的功能码(例如
0x03)被转换为两个 ASCII 字符("0"和"3")传输。 - 常用功能码列表:与 Modbus RTU 完全相同,例如
01读线圈,03读保持寄存器,06写单个寄存器,16(十六进制为0x10) 写多个寄存器等。
4.1.4 数据字段 (Data Field) - 变长
- 作用:包含执行特定功能所需的具体信息。
- 格式:数据字段中的每一个字节都被转换为两个 ASCII 字符。因此,数据字段的长度是实际数据字节数的两倍。例如,要写入 16 位数据
0x12AB,需要传输 4 个 ASCII 字符:"1"、"2"、"A"、"B"。
4.1.5 LRC 校验 (Longitudinal Redundancy Check) - 2个字符
- 作用:提供数据的完整性校验,用于检测传输错误。这是 Modbus ASCII 与 RTU 在错误校验上的主要区别。
- 算法:
- 将从从站地址到数据字段末尾的所有 8 位字节值相加,忽略任何进位。
- 对结果进行二进制补码(取反加一),得到一个 8 位的 LRC 值。
- 最后,将这个 8 位的 LRC 值转换为两个 ASCII 字符进行传输。
- 与 CRC 的区别:LRC 是一种相对简单的校验方式,其检错能力不如 RTU 模式中使用的 CRC-16 校验。
- 示例代码
/**
* 计算Modbus ASCII帧的LRC(纵向冗余校验)校验码
* LRC算法:将从站地址到数据字段的所有字节相加,忽略进位,然后取二进制补码(取反加一)
*
* @param data 指向待计算数据的指针
* @param length 数据的字节长度(不包括起始符和结束符)
* @return 计算得到的8位LRC校验值
*/
unsigned char calculateLRC(const unsigned char *data, int length) {
unsigned char lrc = 0; // 初始化LRC值为0
int i;
// 遍历数据中的所有字节,将它们相加(忽略进位)
for (i = 0; i < length; i++) {
lrc += data[i];
}
// 对结果取二进制补码(取反加一)得到最终的LRC值
return (unsigned char)(-((char)lrc));
}
4.1.6 结束符 (End Characters) - 2个字符
- 作用:明确标识一帧数据的结束。
- 内容:固定为回车符(CR)和换行符(LF)的组合,其 ASCII 码分别为
0x0D和0x0A。
5. 异常响应:错误处理机制
当从站接收到一个有效但无法处理的请求时(如请求的地址不存在),它会返回一个异常响应帧。Modbus ASCII 的异常响应机制与 RTU 类似。
5.1 异常响应帧的格式
[起始符] [从站地址] [功能码+0x80] [异常码] [LRC校验] [结束符]
1字符 2字符 2字符 2字符 2字符 2字符(CR LF)
- 功能码+0x80:异常响应的标志。从站将原始功能码的最高位置 1(即与
0x80进行“或”运算),然后将结果转换为两个 ASCII 字符。例如,对于功能码0x03的请求,异常响应中的功能码字段将是0x83(ASCII 字符为"83")。 - 异常码:一个字节,指示错误的具体类型,同样被转换为两个 ASCII 字符。
5.2 常见异常码及其含义
| 异常码 (十六进制) | 名称 | 说明 |
|---|---|---|
| 01 (0x01) | 非法功能码 (Illegal Function) | 从站不支持该功能码。 |
| 02 (0x02) | 非法数据地址 (Illegal Data Address) | 请求的数据地址在从站中不存在或超出范围。 |
| 03 (0x03) | 非法数据值 (Illegal Data Value) | 请求中包含的数据值对从站无效(例如,写入值超出设备限制)。 |
| 04 (0x04) | 从站设备故障 (Slave Device Failure) | 从站在执行操作时发生不可恢复的内部错误。 |
| 06 (0x06) | 从站忙 (Slave Device Busy) | 从站正忙于处理其他任务,无法响应请求。 |
6. 实际应用示例:深入理解通信流程
通过具体的命令和响应示例,可以更清晰地理解 Modbus ASCII 的工作方式。
6.1 示例1:读取温度传感器数据(功能码 0x03 - 读取保持寄存器)
假设有一个地址为 1 的温度传感器,其当前温度值存储在保持寄存器地址 40001 (协议偏移地址 0x0000)。我们要读取这一个寄存器的值。
发送命令(主站 -> 从站):
:010300000001FB\r\n
命令解析:
::起始符。01:从站地址为 1 (ASCII"01"对应十六进制0x01)。03:功能码为 3 (ASCII"03"对应十六进制0x03),表示读取保持寄存器。0000:起始寄存器地址为 0x0000。0001:请求读取 1 个寄存器。FB:LRC 校验码。- 计算过程:
0x01 + 0x03 + 0x00 + 0x00 + 0x00 + 0x01 = 0x05。 - 二进制补码:
(NOT 0x05) + 1 = 0xFA + 1 = 0xFB。
- 计算过程:
\r\n:结束符 (CR LF)。
设备响应(从站 -> 主站):
:010302006496\r\n
响应解析:
::起始符。01:从站地址为 1。03:功能码为 3。02:数据字节数为 2(因为读取了 1 个 16 位寄存器)。0064:实际读取到的数据。ASCII 字符串"0064"对应十六进制0x0064,转换为十进制是100。96:LRC 校验码。- 计算过程:
0x01 + 0x03 + 0x02 + 0x00 + 0x64 = 0x6A。 - 二进制补码:
(NOT 0x6A) + 1 = 0x95 + 1 = 0x96。
- 计算过程:
\r\n:结束符。
结果:主站成功读取到温度值为 100。
7. 数据类型和字节序:处理复杂数据
Modbus ASCII 与 RTU 同样使用 16 位寄存器作为基本数据单元。因此,在处理超过 16 位的数据类型(如 32 位整数或浮点数)时,也会面临字节序(Byte Order)和字序(Word Order)的问题。
7.1 常见数据类型
| 数据类型 | 长度 (位) | 占用寄存器数 | 描述 |
|---|---|---|---|
| UINT16 | 16 | 1 | 16位无符号整数 (0 到 65535) |
| INT16 | 16 | 1 | 16位有符号整数 (-32768 到 32767) |
| UINT32 | 32 | 2 | 32位无符号整数 (0 到 4,294,967,295) |
| INT32 | 32 | 2 | 32位有符号整数 (-2,147,483,648 到 2,147,483,647) |
| FLOAT32 | 32 | 2 | 32位单精度浮点数 (遵循 IEEE 754 标准) |
7.2 字节序和字序问题
当一个数据占用两个寄存器时(如 32 位浮点数),设备如何排列这两个寄存器(字序)以及每个寄存器内部的两个字节(字节序)至关重要。这通常是导致数据解析错误的主要原因。
常见的组合包括:
- ABCD (大端序 Big-Endian):高位字在前,每个字内部高字节在前。
- DCBA (小端序 Little-Endian):低位字在前,每个字内部低字节在前。
- BADC (字序交换的大端序):高位字在前,每个字内部低字节在前。
- CDAB (字序交换的小端序):低位字在前,每个字内部高字节在前。
解决方案:
- 查阅设备手册:这是解决字节序问题的首要和最佳途径。设备的技术文档通常会明确说明其数据格式。
- 使用调试工具:利用支持多种字节序解析的 Modbus 软件工具,尝试不同的组合,直到解析出正确的数据。
- 编程处理:在主站应用程序中,根据从站的规范,对接收到的数据进行相应的字节或字交换操作。
8. Modbus ASCII vs. Modbus RTU
选择哪种传输模式取决于具体的应用需求。
| 特性 | Modbus ASCII | Modbus RTU |
|---|---|---|
| 数据格式 | 人类可读的 ASCII 字符 | 紧凑的二进制数据 |
| 传输效率 | 较低,每个 8 位数据需要传输 2 个字符 (14-20 位) | 较高,每个 8 位数据传输 8 位 (10-11 位) |
| 帧定界 | 使用起始符 : 和结束符 CR LF | 使用至少 3.5 个字符的静默时间 |
| 字符间隔 | 允许长达 1 秒的间隔 | 要求字符连续传输,间隔不能超过 1.5 字符时间 |
| 错误校验 | LRC (纵向冗余校验) | CRC-16 (循环冗余校验) |
| 检错能力 | 相对较弱 | 更强 |
| 调试难度 | 较低 | 相对较高 |
| 适用场景 | 需要人工监控、通过调制解调器传输、对效率要求不高的场合 | 大多数工业现场、对通信效率和可靠性要求高的场合 |
9. 常见问题和解决方案
9.1 通信无响应
现象:主站发送请求后收不到从站的任何响应。
常见原因:
- 串口参数不匹配:波特率、数据位、停止位、奇偶校验设置错误。Modbus ASCII 通常使用 7 位数据位和偶/奇校验,或 8 位数据位无校验。
- 从站地址错误:请求的地址与从站实际配置的地址不符。
- 接线问题:RS-485 的 A/B 线接反或接触不良。
- 帧格式错误:起始符、结束符或 LRC 校验不正确。
解决方案:
- 仔细核对主从双方的所有串口参数和 Modbus 地址。
- 检查物理连接,确保接线牢固且正确。
- 使用串口调试工具捕获并分析发送的报文,确认其格式完全符合 Modbus ASCII 规范。
9.2 LRC 校验失败
现象:接收到响应,但计算出的 LRC 与报文中包含的 LRC 不符。
常见原因:
- 线路干扰:电磁噪声导致数据在传输过程中发生位错误。
- LRC 计算错误:主站或从站程序中的 LRC 算法实现有误。
- 数据帧不完整:帧的起始或结束部分被截断。
解决方案:
- 改善线路质量:使用屏蔽双绞线,并确保屏蔽层良好接地。
- 验证 LRC 算法:使用在线工具或标准代码库来验证你的 LRC 计算逻辑是否正确。
- 降低通信速率:降低波特率可以增强通信的稳定性。
9.3 数据解析错误
现象:通信正常,但解析出的数值不正确。
常见原因:
- 字节序/字序错误:在处理 32 位或更长数据时最常见的问题。
- 数据类型不匹配:将浮点数按整数解析,或将有符号数按无符号数解析。
- 地址错误:读取的寄存器地址与设备手册中定义的地址不符。
解决方案:
- 严格参考设备手册,确认数据类型、字节序和地址。
- 使用支持多种字节序的调试软件反复测试,直到找到正确的组合。
- 核对地址映射,确保请求的地址与目标数据完全对应。
10. 总结
Modbus ASCII 以其直观易读的特性,在工业通信领域中扮演着一个独特的角色。虽然在性能上不及 Modbus RTU,但其在调试便利性和对某些特殊通信介质的适应性上具有不可替代的优势。通过本教程的学习,你应该已经:
- 理解了 Modbus ASCII 的基本概念和主从通信模型。
- 掌握了 Modbus 共享的数据模型。
- 深入剖析了 Modbus ASCII 数据帧的独特构成,包括起始符、结束符和 LRC 校验。
- 学习了如何识别和处理异常响应。
- 认识到数据类型和字节序的重要性,并了解了解决方案。
- 明确了 Modbus ASCII 和 RTU 之间的关键区别及其各自的适用场景。