libmodbus编程笔记

一 基础知识

地址映射值 libmodbus编程笔记_第1张图片

Modbus寄存器

libmodbus编程笔记_第2张图片

 Modbus寄存器地址分配

libmodbus编程笔记_第3张图片

 Modbus ASCII消息帧格式

Modbus RTU帧格式

libmodbus编程笔记_第4张图片

 Modbus RTU相邻帧间隔

libmodbus编程笔记_第5张图片

 Modbus寻址范围

  PDU与ADU的关系

 Modbus TCP/IP ADU与PDU的关系

libmodbus编程笔记_第6张图片

 Modbus TCP/IP与Modbus串行消息构成对比

libmodbus编程笔记_第7张图片

Modbus TCP/IP协议最大帧数据长度为260字节,其中字节0~6构成MBAP报头,各字段意义如下表所示。 

MBAP报头说明

libmodbus编程笔记_第8张图片

 二 查询与响应报文例

对于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部分功能码

libmodbus编程笔记_第9张图片

 从表可知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查询报文例 

libmodbus编程笔记_第10张图片

 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响应报文例

libmodbus编程笔记_第11张图片

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。此函数调用依赖关系如下图所示。 

libmodbus编程笔记_第12张图片

 函数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()。此函数的调用依赖关系如下图所示。 

libmodbus编程笔记_第13张图片

 函数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读/写寄存器。

小结

你可能感兴趣的:(网络,tcp/ip,网络协议,网络)