在之前的文章:
《 libmodbus协议栈1——Linux下详细移植步骤(配置、生成) 》
《 libmodbus协议栈2—— Linux下 modbus RTU master 开发案例 》
《 libmodbus协议栈3—— Linux下 modbus RTU 从机 开发案例 》
我们是从 应用的角度了解到了 libmodbus 的使用方法,经过最近一段时间的测试,我发现该库文件 还是比较稳定的,尤其是其提供了 故障恢复 功能,大大的提高了其稳定性。下面我们就从源码的角度,分析一下 libmodbus的基本原理,这里我们以最常用的modbus rtu 来分析,也就是 用于串口。
一、关键数据结构、接口函数
1、modbus_t
该类型是libmodbus的最基本的 数据结构,它是一个结构体,源码定义如下:
-
struct _modbus {
-
/* Slave address */
-
int slave;
-
/* Socket or file descriptor */
-
int s;
-
int debug;
-
int error_recovery;
-
struct timeval response_timeout;
-
struct timeval byte_timeout;
-
const modbus_backend_t *backend;
-
void *backend_data;
-
};
其中:
-
/*
-
* 设备地址,如果我们使用libmodbus作为主机,则slave是 我们想要访问的从机地址
-
* 如果我们使用libmodbus作为从机(服务器),则slave是 本机地址
-
*/
-
int slave;
-
-
/*
-
* 文件描述符,如果是modbus rtu 则是串口的 文件描述符,如果是modbus tcp 则是 socket 文件描述符
-
*/
-
int s;
-
-
/*
-
* debug 功能使能标志
-
*/
-
int debug;
-
-
/*
-
* 错误恢复标志,这个很重要,是保证 libmodbus运行稳定的 关键,设置相应的 错误 异常恢复功能。
-
*/
-
int error_recovery;
-
-
/*
-
* 响应超时 参数
-
*/
-
struct timeval response_timeout;
-
-
/*
-
* 数据帧 字节 延时,这个是modbus 协议中定义的参数,如果那个 3.5 字节长度时间
-
*/
-
struct timeval byte_timeout;
-
-
/*
-
* 这个有多种翻译解释,我把它称作 回调函数 集,作者将代码做了很好的封装接口,modbus rtu 和
-
* modbus tcp 的访问接口基本相同,但是在初始化时,赋值不同的函数指针。
-
*/
-
const modbus_backend_t *backend;
-
-
/*
-
* 回调 参数,这个一般存放 通信参数,比如modbus rtu 下,就是串口的通信参数。
-
*/
-
void *backend_data;
2、modbus_backend_t
该数据结构 为modbus 的 回调函数集 结构体,源码如下:
-
typedef
struct _modbus_backend {
-
unsigned
int backend_type;
-
unsigned
int header_length;
-
unsigned
int checksum_length;
-
unsigned
int max_adu_length;
-
int (*set_slave) (modbus_t *ctx,
int slave);
-
int (*build_request_basis) (modbus_t *ctx,
int function,
int addr,
-
int nb, uint8_t *req);
-
int (*build_response_basis) (sft_t *sft, uint8_t *rsp);
-
int (*prepare_response_tid) (
const uint8_t *req,
int *req_length);
-
int (*send_msg_pre) (uint8_t *req,
int req_length);
-
ssize_t (*send) (modbus_t *ctx,
const uint8_t *req,
int req_length);
-
int (*receive) (modbus_t *ctx, uint8_t *req);
-
ssize_t (*recv) (modbus_t *ctx, uint8_t *rsp,
int rsp_length);
-
int (*check_integrity) (modbus_t *ctx, uint8_t *msg,
-
const
int msg_length);
-
int (*pre_check_confirmation) (modbus_t *ctx,
const uint8_t *req,
-
const uint8_t *rsp,
int rsp_length);
-
int (*connect) (modbus_t *ctx);
-
void (*close) (modbus_t *ctx);
-
int (*flush) (modbus_t *ctx);
-
int (*select) (modbus_t *ctx, fd_set *rset,
struct timeval *tv,
int msg_length);
-
void (*free) (modbus_t *ctx);
-
} modbus_backend_t;
作者将代码做了很好的封装接口,modbus rtu 和 modbus tcp 的访问接口是相同,但是在初始化时,赋值不同协议的函数指针,就是modbus 的各种 操作接口函数。
3、modbus_mapping_t
该结构是用于libmodbus 作为从机使用时,modbus 各区 寄存器 集合的指针,源码如下:
-
typedef
struct {
-
int nb_bits;
-
int start_bits;
-
int nb_input_bits;
-
int start_input_bits;
-
int nb_input_registers;
-
int start_input_registers;
-
int nb_registers;
-
int start_registers;
-
uint8_t *tab_bits;
-
uint8_t *tab_input_bits;
-
uint16_t *tab_input_registers;
-
uint16_t *tab_registers;
-
} modbus_mapping_t;
从上面的定义,也可以看出,就是对 0x、1x、3x、4x 共 4个区寄存器参数的定义。
4、modbus_new_tcp 、 modbus_new_rtu
这两个函数的功能是一样的,申请并初始化一个 modbus_t *ctx 结构指针,后面所有的modbus操作,都是基于这个ctx,所不同的是modbus_new_tcp 用于 modbus tcp, 而modbus_new_rtu 用于modbus rtu,具体的功能就是 申请 modbus_t 结构指针,然后 赋值 不同的 回调函数。
示例代码:
-
if (use_backend == TCP) {
-
ctx = modbus_new_tcp(
"127.0.0.1",
1502);
-
}
else
if (use_backend == TCP_PI) {
-
ctx = modbus_new_tcp_pi(
"::1",
"1502");
-
}
else {
-
ctx = modbus_new_rtu(
"/dev/ttyUSB1",
115200,
'N',
8,
1);
-
}
5、modbus_set_error_recovery
该函数 用于设定 libmodbus 运行过程中,发生错误时的 如何恢复,该函数非常重要,因为程序的运行 少不了出现各种异常,异常不怕,只要 有应对,程序还是稳定的,示例代码如下:
-
modbus_set_error_recovery(ctx,
-
MODBUS_ERROR_RECOVERY_LINK |
-
MODBUS_ERROR_RECOVERY_PROTOCOL);
代码中,MODBUS_ERROR_RECOVERY_LINK 表示 断开连接后,自动恢复。
MODBUS_ERROR_RECOVERY_PROTOCOL 表示协议层面 出现故障时,自动恢复,比如从设备超时等。
6、modbus_set_slave
设定 modbus 设备地址,如果我们的设备是主机,则该函数 设定要读的从机地址,反之,如果我们的设备是从机,则该函数设定本机地址,示例代码如下:
modbus_set_slave(ctx, SERVER_ID);
7、modbus_connect
该函数字面意思上可以看出来,是 实现 “连接”功能,只有调用该函数成功后,才能进行通信,前面的函数都是申请、 初始化各种参数,对硬件设备(串口、网口)并没有实际的 动作,该函数就是对底层硬件接口的动作,在modbus rtu模式下,就是设置并打开串口, 而 在modbus tcp模式下则是 创建socket连接。
8、modbus_read_xxxx
该函数 是libmodbus 用于主机时,读 各区(0x、1x、3x、4x) 的接口函数。需要说明的是,这个函数不仅仅 包含“读”功能,它包含了“读”功能的所有步骤:
① 创建并打包 读命令。
② 发送 步骤①中的读指令。
③ 接收从机返回数据。
④ 对返回数据 进行各种校验,比如设备地址,地址、数量、CRC校验等。
⑤ 将正确的返回 有效数据 存放到 我们指定的 缓存中。
9、modbus_write_xxxx
当libmodbus 用作主机时,写 0x、4x区 的接口函数,类似于 modbus_read_xxxx 包含了所有的功能。
10、modbus_receive
libmodbus 无论是作为主机还是从机使用,都会使用该函数,也就是接收功能,该函数 在linux下采用select 机制,所不同的是,当libmodbus 作为 主机时,select 中有 超时,不会一直阻塞,而作为从机时,select中的超时为 0, 也就是会阻塞,这也是必然的,而且该函数 还不是 一下子接收完毕,往往是分三步接收,这一点在后面的文章会详细介绍。
11、modbus_mapping_new 、 modbus_mapping_new_start_address
这两个函数功能 本质上是一样的,前者也是通过调用后者来实现的的。当libmodbus作为 从机(服务器)使用时,通过该函数申请并初始化4个区寄存器 缓存。
12、modbus_reply
从函数字面意思上看,当libmodbus 作为 从机(服务器)使用时,用于对主机访问的 回复。该函数的的实现代码还是比较多的,毕竟要 相应 主机的一切需求,不仅正确的,还有非法的需求。一般只要我们 调用 modbus_receive 接收数据完成后,直接调用 该函数,libmodbus就会自动的 组建打包 对应的 数据包,然后返回给 主机。
13、modbus_close
关闭libmodbus。
14、modbus_mapping_free
当libmodbus用作 从机(服务器)时,释放 4个区寄存器的缓存。
15、modbus_free
释放 libmodbus 的资源,也就是 最开始 初始化 申请的 modbus_t *ctx;
转载:https://blog.csdn.net/u012351051/article/details/104088091