GEIO控制包PLC程序结构化控制卡
-
¥40000.00
. Modbus协议源代码简介
2.1 关于modbus中的常见两种寄存器区别
保持寄存器:指可以通过通信命令读或者写的寄存器;通常是一些功能控制寄存器或者输出寄存器等。不同的设计中,有些保持寄存器是掉电保持;有些则不然。
输入寄存器:指只能读不能写的寄存器,通常是状态寄存器或者是输入结果寄存器等。
线圈寄存器,可以类比为开关量,每一个bit都对应一个信号的开关状态。所以一个byte就可以同时控制8路的信号。
离散输入寄存器:相当于线圈寄存器的只读模式,每个bit表示一个开关量,而他的开关量只能读取输入的开关信号,无法写入。
2.2 Modbus开源库常用配置接口
1)modbus_t* modbus_new_rtu(const char *device,
int baud, char parity, int data_bit,
int stop_bit)
modbus_new_rtu函数用于生成Modbus的句柄,在本函数中可以设置通
信协议中的波特率、校验位、数据长度以及停止位,其返回值为通过设置后生成的句柄,用于在读写数据时使用,每个句柄可以执行一个modbus指令。如果这些配置参数有误,就会返回一个空指针。
2)static int _modbus_rtu_connect(modbus_t *ctx)
本函数主要功能是将通信串口设置为rtu模式。
3)int modbus_set_slave(modbus_t *ctx, int slave)
本函数设置本句柄的从机号。
2.3 Modbus主机通信常用接口
1)int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src)
本函数为将数组中的数据写入到远端设备(从机)的寄存器中,写入的地址位addr,长度为nb个寄存器。
2)int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
本函数将远端设备(从机)保持寄存器中的数据复制到数组dest中。
3)int modbus_read_input_registers(modbus_t *ctx, int addr, int nb,
uint16_t *dest)
本函数读取远端设备(从机)地址为addr输入寄存器中的数据,数据长度为nb。
2.4 Modbus从机通信主要接口
1)int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
本函数可以用于处理来自主机的请求,返回接受到的字符的数量,如果成功,则返回uint8_t数组中的消息(即主机发送的命令),否则返回-1。
2)int modbus_reply(modbus_t *ctx, const uint8_t *req,
int req_length, modbus_mapping_t *mb_mapping)
本函数负责在接受到请求后,分析请求并生成响应消息,并且发送到主机。如果请求属性为广播,那么不发送响应消息。
三、 调试问题分享
在调试中,从机的Server进程会经常出现崩溃,后发现在Server经常每次处理配置变更时,都会重新new出新的modbus句柄,但却不释放原有句柄,这种处理会导致多次修改Modbus通信配置时,从机Server进程崩溃。
解决方案:在程序中判断,当modbus句柄已经存在时,此时更新配置后,不再new出新的句柄,而是调用接口 modbus_close(), modbus_free()释放句柄中的配置,然后用更新后的配置重新设置句柄参数。
注释:需要管理超时,以便明确地等待可能不会出现的应答。
串行链路上个MODBUS 执行的长度约束限制了MODBUS PDU 大小(大RS485ADU=256字节)。
因此,对串行链路通信来说,MODBUS PDU=256-服务器地址(1 字节)-CRC(2 字节)=253字节。
从而:
RS232 / RS485 ADU = 253 字节+服务器地址(1字节) + CRC (2 字节) = 256 字节。
TCP MODBUS ADU = 249 字节+ MBAP (7 字节) = 256 字节。
MODBUS 协议定义了三种 PDU。它们是:
MODBUS 请求 PDU,mb_req_pdu
MODBUS 响应 PDU,mb_rsp_pdu
MODBUS 异常响应 PDU,mb_excep_rsp_pdu
定义 mb_req_pdu 为:
mb_req_pdu = { function_code, request_data},其中
function_code - [1 个字节] MODBUS 功能码
request_data - [n 个字节],这个域与功能码有关,并且通常包括诸如可变参考、变量、数据偏移量、子功能码等信息。
定义 mb_rsp_pdu 为:
mb_rsp_pdu = { function_code, response_ data},其中
function_code - [1 个字节] MODBUS 功能码
response_data - [n 个字节],这个域与功能码有关,并且通常包括诸如可变参考、变量、数据偏移量、子功能码等信息。
定义 mb_excep_rsp_pdu 为:
mb_excep_rsp_pdu = { function_code, request_data},其中
function_code - [1 个字节] MODBUS 功能码 + 0x80
exception_code - [1 个字节],在下表中定义了 MODBUS 异常码。
4.2 数据编码
MODBUS 使用一个‘big-Endian’ 表示地址和数据项。这意味着当发射多个字节时,发送高有效位。例如:
寄存器大小 值
16 – 比特 0x1234 发送的字节为 0x12 然后 0x34
输入与输出之间以及比特寻址的和字寻址的数据项之间的区别并没有暗示任何应用操作。如果这是对可疑对象核心部分自然的解释,那么这种区别是可完全接受的,而且很普通,以便认为四个表格全部覆盖了另外一个表格。
对于基本表格中任何一项,协议都允许单个地选择 65536 个数据项,而且设计那些项的读写操作可以越过多个连续数据项直到数据大小规格限制,这个数据大小规格限制与事务处理功能码有关。很显然,将通过 MODBUS 处理的所有数据放置在设备应用存储器中。但是,存储器的物理地址不应该与数据参考混淆。要求仅仅是数据参考与物理地址的链接。
MODBUS 功能码中使用的 MODBUS 逻辑参考数字是以 0 开始的无符号整数索引。
MODBUS 模型实现的实例
下例实例示出了两种在设备中构造数据的方法。可能有不同的结构,这个文件中没有全部描述出来。每个设备根据其应用都有它自己的数据结构。
实例 1:有 4 个立块的设备
下例实例示出了设备中的数据结构,这个设备含有数字量和模拟量、输入量和输出量。由于不同块中的数据不相关,每个块是相互立。按不同MODBUS 功能码访问每个块。