一 基础知识
地址映射值
Modbus寄存器
Modbus寄存器地址分配
Modbus ASCII消息帧格式
Modbus RTU帧格式
Modbus RTU相邻帧间隔
Modbus寻址范围
PDU与ADU的关系
Modbus TCP/IP ADU与PDU的关系
Modbus TCP/IP与Modbus串行消息构成对比
Modbus TCP/IP协议最大帧数据长度为260字节,其中字节0~6构成MBAP报头,各字段意义如下表所示。
MBAP报头说明
二 查询与响应报文例
对于Modbus TCP消息帧格式,下面举例说明各部分的含义。
• 查询报文:00 00 00 00 00 06 09 03 00 04 00 010
x06:后续还有6个字节
0x09:单元标识符为9
0x03:功能码3,即读保持寄存器的值
0x00 0x04:Modbus起始地址4(即40005)
0x00 0x01:读取寄存器个数为1
• 响应报文:00 00 00 00 00 05 09 03 02 00 05
0x05:表示后续还有5个字节
0x09:同查询报文,单元标识符
0x03:功能码,同查询报文
0x02:返回数据字节数
0x00 0x05:寄存器的值
可见,在Modbus TCP模式下,差错校验字段已不复存在。但在某些特殊场合,例如串行Modbus协议转Modbus TCP的情况下,串行协议数据可以完整地装载到Modbus TCP协议的数据字段,这时CRC或者LRC差错校验字段仍然存在。例如,Modbus RTU Over TCP/IP或Modbus ASCII Over TCP/IP等。
三 Modbus部分功能码
从表可知01、05、15操作相同的数据段,02单独操作一组数据,03、06、16共同操作一组数据。
04操作一组数据,在某些资料上15和16也写作十六进制的0x0f和0x10。
01(0x01)读取线圈/离散量输出状态
该功能码用于读取从设备的线圈或离散量输出的状态,即各DO(Discrete Output,离散输出)的ON/OFF状态。消息帧中指定了需读取的线圈起始地址和线圈数目。需要注意的一点是,在Modbus协议规定的PDU中,规定所有线圈或寄存器地址从0开始计算。
查询报文:
如表所示,查询帧的消息里,定义了从设备地址为3,并读取从设备的Modbus地址00019~00055(线圈地址00020~00056)共计37个状态值。起始线圈地址为0x13(即十进制00019),因为线圈地址从0开始计数。
功能码01查询报文例
Modbus协议规定,起始地址由2个字节构成,取值范围为0x0000~0xFFFF;线圈数量由2个字节构成,取值范围为0x0001~0x07D0(即十进制1~2000)。另外,注意观察ASCII模式和RTU模式的区别,ASCII模式中直接按每4个位拆分为对应的字符表示。
响应报文
响应报文的数据字段中,每一个线圈占用1个位(bit),状态被表示为1=ON和0=OFF两种类型。第1个数据字节的LSB(最低有效位)标识查询报文中的起始地址线圈的状态值,其他线圈依次类推,一直到这个字节的MSB(最高有效位)为止,并在后续字节中按照同样的方式(由低到高)排列。例如,下表中线圈20~27的状态值分别是ON-ON-OFF-OFF-ON-OFF-ON-OFF,表示为二进制则为01010011(0x53),注意观察对应的顺序。一个字节可以表示8个线圈的状态,如果最后的数据字节中不能填满8个线圈的状态,则由0填充。对应于查询报文中需要读取37个线圈的状态,则共需要5个字节保存状态值。
功能码01响应报文例
02(0x02)读取离散量输入值
该功能码用于读取从设备的离散输入即DI(Discrete Input)的ON/OFF状态。消息帧中指定了需读取的离散输入寄存器起始地址和数目,可读取1~2000个连续的离散量输入状态。如果从设备接受主设备的请求则回复功能码02,并返回离散量输入各变量的当前状态。如果返回的离散输入数量的个数不是8的整数倍,将用0填充最后数据字节的剩余位。
03(0x03)读取保持寄存器值
该功能码用于读取从设备保持寄存器的内容,不支持广播模式。消息帧中指定了需读取的保持寄存器的起始地址和数目。而保持寄存器中各地址的具体内容和意义,则由设备开发者自行规定。
函数实现
主机
• 01:Read coil status读线圈状态;
对应函数:
MODBUS_API int modbus_read_bits(modbus_t * ctx, int addr, int nb,uint8_t * dest)
此函数对应于功能码01(0x01)读取线圈/离散量输出状态(Read Coil Status/DOs),其中,所读取的值存放于参数uint8_t * dest指向的数组空间,因此dest指向的空间必须足够大,其大小至少为nb * sizeof(uint8_t)个字节。
• 02:Read input status读输入状态;
MODBUS_API int modbus_read_input_bits(modbus_t * ctx, int addr, int nb, uint8_t *dest)
此函数对应于功能码02(0x02)读取离散量输入值(Read Input Status/DIs),各参数的意义与用法,类似于函数modbus_read_bits()。
• 03:Read holding register读保持寄存器;
MODBUS_API int modbus_read_registers(modbus_t * ctx, int addr, int nb, uint16_t *dest)
此函数对应于功能码03(0x03)读取保持寄存器(Read Holding Register),其中,所读取的值存放于参数uint16_t * dest指向的数组空间,因此dest指向的空间必须足够大,其大小至少为nb *sizeof(uint16_t)个字节。当读取成功后,返回值为读取的寄存器个数;若读取失败,则返回-1。此函数调用依赖关系如下图所示。
函数modbus_read_registers()的调用依赖关系
• 04:Read input registers读输入寄存器;
MODBUS_API int modbus_read_input_registers(modbus_t * ctx, int addr, int nb,uint16_t * dest)
此函数对应于功能码04(0x04)读取输入寄存器(Read Input Register),各参数的意义与用法,类似于函数modbus_read_registers()。此函数的调用依赖关系如下图所示。
函数modbus_read_input_registers()的调用依赖关系
• 05:Force single coil强制写入单线圈;
MODBUS_API int modbus_write_bit(modbus_t * ctx, int coil_addr, int status)
该函数对应于功能码05(0x05)写单个线圈或单个离散输出(Force Single Coil)。其中参数coil_addr代表线圈地址;参数status代表写入值,取值只能是TRUE(1)或FALSE(0)。
• 06:Preset single register预置单寄存器;
MODBUS_API int modbus_write_register(modbus_t * ctx, int reg_addr, int value)
该函数对应于功能码06(0x06)写单个保持寄存器(Preset Single Register)。
• 15:Force multiple coils强制写入多线圈;
MODBUS_API int modbus_write_bits(modbus_t * ctx, int addr, int nb, const uint8_t *data)
该函数对应于功能码15(0x0F)写多个线圈(Force Multiple Coils)。参数addr代表寄存器起始地址,参数nb表示线圈个数,而参数const uint8_t * data表示待写入的数据块。一般情况下,可以使用数组存储写入数据,数组的各元素取值范围只能是TRUE(1)或FALSE(0)。
• 16:Preset multiple registers预置多寄存器;
MODBUS_API int modbus_write_registers(modbus_t * ctx, int addr, int nb, const uint16_t * data)
该函数对应于功能码16(0x10)写多个保持寄存器(Preset Multiple Registers)。参数addr代表寄存器起始地址,参数nb表示寄存器的个数,而参数const uint16_t * data表示待写入的数据块。一般情况下,可以使用数组存储写入数据,数组的各元素取值范围是0~0xFFFF,即数据类型uint16_t的取值范围。
• 17:Report slave ID报告从设备ID;
MODBUS_API int modbus_report_slave_id(modbus_t * ctx, int max_dest, uint8_t *dest)
该函数对应于功能码17(0x11)报告从站ID。参数max_dest代表最大的存储空间,参数dest用于存储返回数据。返回数据可以包括如下内容:从站ID、状态值(0x00 = OFF状态,0xFF=ON状态)以及其他附加信息,具体的各参数意义由开发者指定。
• 22:Mask write register屏蔽写寄存器;
• 23:Read/Write registers读/写寄存器。