Modbus | Modbus协议回顾
为什么最多247个从站?
这是因为Modbus协议中的设备地址字段是1个字节(8位),但有一些地址是保留的,不能分配给普通的从站设备。
具体解释如下:
- 地址范围:地址字段为1个字节,可以表示0~255(十进制),即256个地址。
- 地址 0:地址0是广播地址,主站发出的消息所有从站都能接收,但不会回应,因此不能分配给具体设备。
- 地址 1~247:实际分配给从站设备的地址是1~247,也就是247个从站地址。这是Modbus协议标准所规定的。
- 地址 248~255:这些地址在标准协议中是保留的,暂未用作普通从站地址。
标准Modbus中数据是如何存储的?
信息在从设备中存储于四种不同的表中。
有两张表用于存储开/关(离散)值(线圈),另外两张表用于存储数值(寄存器)。
线圈和寄存器各自都有只读表和读写表。
每张表有9999个数值。 每个线圈或接点占1位,并被分配一个从0000到270E之间的数据地址。 每个寄存器占1字=16位=2字节,也同样有从0000到270E之间的数据地址。
线圈/寄存器编号 | 数据地址 | 类型 | 表名 |
---|---|---|---|
1-9999 | 0000 到 270E | 读写 | 离散输出线圈 Discrete Output Coils |
10001-19999 | 0000 到 270E | 只读 | 离散输入接点 Discrete Input Contacts |
30001-39999 | 0000 到 270E | 只读 | 模拟量输入寄存器 Analog Input Registers |
40001-49999 | 0000 到 270E | 读写 | 模拟量输出保持寄存器 Analog Output Holding Registers |
线圈/寄存器编号可以视为位置名称,因为它们不会出现在实际报文中。数据地址才会被用在消息里。
例如,第一个保持寄存器,编号为40001,对应的数据地址是0000。
这两个值之间的差值称为偏移量(offset)。
每张表有不同的偏移量:1、10001、30001和40001。
四个表明的解释
1. 离散输出线圈 (Discrete Output Coils)
- 定义:又称“线圈”或“Coil”,代表可控制的开关量输出,通常是开/关(ON/OFF)状态。
- 典型用途:用于驱动继电器、指示灯、打开/关闭机械设备等。
- 读/写属性:通常可由主机读取其状态,也可以写入改变其状态(例如,让某个线圈通电或断电)。
- 数据类型:每个线圈占1位(二进制),值为0或1。
- 例子:你可以通过写线圈打开或关闭一个电磁阀。
2. 离散输入接点 (Discrete Input Contacts)
- 定义:又称“输入接点”或“Discrete Inputs”,反映现场设备的开关量输入信号。
- 典型用途:用于检测按钮、限位开关、传感器等的开/关状态。
- 读/写属性:只读。主机只能读取其状态,不能控制它们。
- 数据类型:每个接点占1位(二进制),值为0或1。
- 例子:检测一个安全门是否被打开或关闭。
3. 模拟量输入寄存器 (Analog Input Registers)
- 定义:用于存储现场设备的模拟量输入值(如电压、电流、温度等)。
- 典型用途:读取模拟传感器(如温度传感器、压力传感器等)的数据。
- 读/写属性:只读。主机只能读取其值,不能写入。
- 数据类型:每个寄存器通常占16位(2字节),表示一个整数值。
- 例子:读取温度传感器采集到的当前温度。
4. 模拟量输出保持寄存器 (Analog Output Holding Registers)
- 定义:用于存储需要输出到现场设备的模拟量控制值,也可用来保存设备的一些配置参数。
- 典型用途:用来控制变频器的转速、阀门的开度、输出电流/电压等。
- 读/写属性:可读可写。主机可以读取当前值,也可以写入新值来改变控制输出。
- 数据类型:每个寄存器通常占16位(2字节)。
- 例子:向变频器的保持寄存器写一个新速度设定值,从而控制电机转速。
总结对比
名称 | 英文 | 典型功能 | 读写属性 | 数据类型 |
---|---|---|---|---|
离散输出线圈 | Discrete Output Coils | 控制输出设备开关 | 读写 | 1位 |
离散输入接点 | Discrete Input Contacts | 采集输入设备状态 | 只读 | 1位 |
模拟量输入寄存器 | Analog Input Registers | 采集模拟量输入 | 只读 | 16位 |
模拟量输出保持寄存器 | Analog Output Holding Registers | 输出模拟量或参数 | 读写 | 16位 |
为什么输出是控制,输入是采集?
1. 为什么输出是控制?
- 离散输出线圈(Discrete Output Coils)和模拟量输出保持寄存器(Analog Output Holding Registers)之所以称为“输出”,是因为它们用来控制现场设备,是主控系统输出到现场的控制信号。
- 你通过Modbus写这些“输出”,其实是在远程控制设备,比如打开/关闭继电器、设置阀门的开度、调节电机速度等。
- 这些动作都是“输出”到现场,所以叫输出。
2. 为什么输入是采集?
- 离散输入接点(Discrete Input Contacts)和模拟量输入寄存器(Analog Input Registers)称为“输入”,是因为它们用来采集现场设备的状态,输入到主控系统。
- 比如检测按钮是否按下、传感器是否有信号、温度是多少等。
- 现场设备的状态“输入”到系统里,所以叫输入。
- 比如:你读输入寄存器,其实就是在“采集”现场的数据。
3. 为什么容易误解?
很多初学者会本能地把“输入”理解为“我要输入参数”(往设备里写),把“输出”理解为“我要输出数据”(从设备里读)。但在工业自动化和Modbus规范里,输入输出是站在主控系统(如PLC或上位机)对现场设备的视角来定义的:
- “输出(Output)”:主控系统控制外部设备,“我输出一个动作/信号给现场”。
- “输入(Input)”:主控系统采集外部信号,“我采集到一个来自现场的数值”。
4. 举例区分
- 离散输出线圈(Output Coils): 你通过主机指令让某个设备“动作”或“打开/关闭”,这就是输出控制。
- 离散输入接点(Input Contacts): 你通过主机读取按钮、开关等现场设备的状态,这就是输入采集。
5. 现实生活类比
比如:
- 你用遥控器(主机)控制电视(现场设备)的电源开关,这个“开关”就是输出线圈(你控制电视)。
- 你用遥控器(主机)读取电视屏幕上当前的频道号,这个“频道号”就是输入寄存器(你采集、读取电视的信息)。
总结:
在Modbus和大部分工业协议中,“输出”通常指你控制外部设备的部分,“输入”通常是你采集外部设备状态的部分。这跟一般日常生活的“输入输出”说法确实不同,但这就是自动化行业和Modbus约定的命名方式。
离散(Discrete)与模拟量(Analog)含义
1. 离散(Discrete)
- “离散”在自动化和电气专业里,一般特指只有两种状态的信号,也称为数字信号,例如“开/关”、“有/无”、“1/0”等。
- 线圈(Coils)和输入接点(Discrete Inputs)实际就是用**一位(bit)**来表示“要不要通电”、“这个按钮是否按下”——没有中间态。
- 在Modbus里,这些只表示0和1,不能存储中间的数值,所以叫离散。
举例:
- 电机是否启动?(离散信号:1=启动,0=未启动)
- 灯泡是否亮?(离散信号)
2. 模拟量(Analog)
- “模拟量”指的是可以连续变化的物理量或数值,比如温度、压力、电压、流量等。
- 在Modbus里,所有需要存储这类“连续”数值的地方,实际上都用寄存器(Register)表示;每个寄存器可以存储0~65535(2字节/16位)的值。
- 这些寄存器的内容通常代表的是某种模拟测量值数据,不是开或关这么简单,就是一个范围内的某个具体数值。
举例:
- 温度是23.7℃(模拟量),压力是5.2 bar(模拟量)
- 你需要寄存器来存储和表示这些数值
为什么线圈叫“离散”,寄存器叫“模拟量”?
- 线圈(Coil)和输入接点:
- 只关心“开/关”,
- 只占1位(Bit),
- 没有中间数值,
- 所以叫“离散”(Discrete)。
- 输入寄存器和输出保持寄存器:
- 每个寄存器可以存储一个具体的数值(16位整数),
- 可用于表示类似电流、温度、速度等信息,
- 这些是“模拟量”(Analog),可连续变化。
表格总结
名称 | 英文 | 传输内容 | 数据类型 | 作用举例 |
---|---|---|---|---|
离散输出线圈 | Discrete Output Coils | 开关状态 | 1位 | 打开/关闭一个某设备 |
离散输入接点 | Discrete Input Contacts | 开关状态 | 1位 | 检测按钮是否被按下 |
模拟量输入寄存器 | Analog Input Registers | 连续数值 | 16位 | 读取温度、压力传感器 |
模拟量输出保持寄存器 | Analog Output Holding Registers | 连续数值 | 16位 | 控制阀门的开度、设置电机转速 |
什么是功能码?
主机发送的第二个字节就是功能码(Function code)。这个数字告诉从机要访问哪一个表,以及是读取还是写入表中的数据。
功能码 | 操作 | 表名 |
---|---|---|
01(01 hex) | 读取 | 离散输出线圈(Discrete Output Coils) |
05(05 hex) | 单点写入 | 离散输出线圈(Discrete Output Coil) |
15(0F hex) | 多点写入 | 离散输出线圈(Discrete Output Coils) |
02(02 hex) | 读取 | 离散输入接点(Discrete Input Contacts) |
04(04 hex) | 读取 | 模拟量输入寄存器(Analog Input Registers) |
03(03 hex) | 读取 | 模拟量输出保持寄存器(Analog Output Holding Registers) |
06(06 hex) | 单点写入 | 模拟量输出保持寄存器(Analog Output Holding Register) |
16(10 hex) | 多点写入 | 模拟量输出保持寄存器(Analog Output Holding Registers) |
单播和广播
1. Modbus RTU(串口,RS-485等)
单播
- 单播是Modbus最常见的通信方式。
- 主站(Master)指令中SlaveID(从站地址)为具体设备地址(1~247之间的某个数),只有地址匹配的从站才会响应并返回数据,其他从站忽略此请求。
广播
- 通过SlaveID地址设置为0(0x00),意味着广播。
- 所有从站设备都必须接受并处理这条指令,但不会有任何从站发回响应(以避免总线冲突)。
- 常用于如“全部清零”、“全部写入寄存器”等一对多操作(比如初始化所有从站)。
2. Modbus TCP(以太网)
- 标准Modbus TCP理论上是基于IP网络的点对点通信(单播,服务器/客户端结构),通常每台设备有唯一IP,所以广播使用极少。
TCP下的“广播”?
- 大多数Modbus TCP实现并不支持广播(即,不会有标准从站响应UDP广播或IP组播)。
- Modbus TCP只保留了UnitID字段来兼容网关场景,但实际数据传输依赖于唯一的IP地址(主机名),所以常规都是单播。
- 个别厂家在特定应用下做了扩展(比如通过UDP广播向所有设备刷写配置),但这不是协议层面标准,只适用于特定产品。
3. 总结对比
单播 | 广播 | |
---|---|---|
Modbus RTU | 支持(常用) | 支持(地址0) |
Modbus TCP | 支持(常用) | 基本不支持,协议无定义 |
常见场景举例
- 单播:主站向1号从站请求状态,SlaveID=1→1号从站响应。
- 广播:主站发送全体写入,SlaveID=0→所有从站执行,无人响应。
结论:
Modbus确实有单播和广播,RTU支持广播(SlaveID=0),TCP标准下基本只用单播。
Modbus TCP
ADU 与 PDU
以 Modbus RTU 消息为例,将消息开头的 SlaveID(从站地址)和结尾的 CRC 校验去掉,剩下的就是 PDU(协议数据单元,Protocol Data Unit)。
下面是一个 Modbus RTU 请求的例子,请求地址为 17 的从设备的模拟输出保持寄存器(Holding Registers)#40108 到 40110 的内容。
11 03 006B 0003 7687
- 11:SlaveID(从站地址)(17 = 0x11 16进制)
- 03:功能码(读取模拟输出保持寄存器)
- 006B:请求的第一个寄存器地址(40108-40001=107=0x6B 16进制)
- 0003:请求的寄存器数量(读取3个寄存器,从40108到40110)
- 7687:CRC(循环冗余校验),用于错误检测
去掉SlaveID和CRC后,就得到了PDU:
03 006B 0003
说明:
ADU(应用数据单元)= 协议头(比如SlaveID或者MBAP头部)+ PDU + 校验。
在Modbus RTU中,ADU由[SlaveID][PDU][CRC]组成;而在Modbus TCP中,ADU由[MBAP Header][PDU]组成(MBAP头里含UnitID,没有CRC)。
Modbus RTU和Modbus TCP调试
调试工具指令“逻辑”一致,但“封装和通讯方式”不同
1. 指令内容(功能码、寄存器号、数据等)是通用的
- 比如你在调试工具里设置“读保持寄存器(功能码03)”,起始地址40001,读取2个寄存器,这个“命令内容”逻辑RTU、TCP下是一致的。
- 举个例子,调试工具一般会让你选择:
- 功能码(如03)
- 地址(如40001或0偏移)
- 数据长度
- 填好后点击发送
2. 封装方式不同(工具会帮你处理)
- Modbus RTU:
- 工具会把你的指令包装成串口协议的二进制帧(末尾加CRC校验),通过串口(COM口)发出去。
- Modbus TCP:
- 工具会把你的指令包装成以太网TCP协议的报文(加MBAP头,不需要加CRC),走网络端口(通常502)发出去。
3. 使用方式基本一样,只需注意如下差别
- 选择通讯方式
- RTU → 需要选择串口、波特率、校验、站号等
- TCP → 需要输入IP地址、端口号、站号
- 指令设置界面基本一模一样(寄存器、功能码、数据等)
- 只能用在对应设备上:RTU工具发串口、TCP工具发网线。不能串口连着,工具选TCP,也不能反过来。
4. 底层数据格式完全由调试工具自动组包
- 大部分Modbus调试工具(比如Modbus Poll、Modscan、Simply Modbus等)都支持RTU和TCP两种模式,你只需要选对通讯方式,填对参数即可。
举例
- 你在调试工具选“读03寄存器,站号为1,地址为40001,长度为1”,
- RTU模式下,工具帮你通过串口发出
01 03 00 00 00 01 CRC
- TCP模式下,工具帮你通过网口发出
MBAP头 01 03 00 00 00 01
- RTU模式下,工具帮你通过串口发出
- 你只需要关心功能和参数,不用自己手动封包!
总结
- 逻辑通用:你会用一种Modbus调试工具,切换RTU和TCP,参数设置基本一样。
- 物理层和底层数据帧不通用,硬件连接方式&通讯设置要选对。
你要做的只是一句话
“选对通讯方式(串口还是TCP),填好常规参数,其他都是通用的。”
- https://www.modbustools.com/modbus.html
- https://www.simplymodbus.ca/
- GPT-4.1