小言_互联网的博客

libmodbus 源码分析(1)基本框架、关键数据结构、接口

488人阅读  评论(0)

在之前的文章:

《 libmodbus协议栈1——Linux下详细移植步骤(配置、生成) 》

《 libmodbus协议栈2—— Linux下 modbus RTU master 开发案例 》

《 libmodbus协议栈3—— Linux下 modbus RTU 从机 开发案例 》

《 libmodbus协议栈4—— 总结 》

  我们是从 应用的角度了解到了 libmodbus 的使用方法,经过最近一段时间的测试,我发现该库文件 还是比较稳定的,尤其是其提供了 故障恢复 功能,大大的提高了其稳定性。下面我们就从源码的角度,分析一下 libmodbus的基本原理,这里我们以最常用的modbus rtu 来分析,也就是 用于串口。

一、关键数据结构、接口函数

1、modbus_t 

 该类型是libmodbus的最基本的 数据结构,它是一个结构体,源码定义如下:


  
  1. struct _modbus {
  2. /* Slave address */
  3. int slave;
  4. /* Socket or file descriptor */
  5. int s;
  6. int debug;
  7. int error_recovery;
  8. struct timeval response_timeout;
  9. struct timeval byte_timeout;
  10. const modbus_backend_t *backend;
  11. void *backend_data;
  12. };

  其中:


  
  1. /*
  2. * 设备地址,如果我们使用libmodbus作为主机,则slave是 我们想要访问的从机地址
  3. * 如果我们使用libmodbus作为从机(服务器),则slave是 本机地址
  4. */
  5. int slave;
  6. /*
  7. * 文件描述符,如果是modbus rtu 则是串口的 文件描述符,如果是modbus tcp 则是 socket 文件描述符
  8. */
  9. int s;
  10. /*
  11. * debug 功能使能标志
  12. */
  13. int debug;
  14. /*
  15. * 错误恢复标志,这个很重要,是保证 libmodbus运行稳定的 关键,设置相应的 错误 异常恢复功能。
  16. */
  17. int error_recovery;
  18. /*
  19. * 响应超时 参数
  20. */
  21. struct timeval response_timeout;
  22. /*
  23. * 数据帧 字节 延时,这个是modbus 协议中定义的参数,如果那个 3.5 字节长度时间
  24. */
  25. struct timeval byte_timeout;
  26. /*
  27. * 这个有多种翻译解释,我把它称作 回调函数 集,作者将代码做了很好的封装接口,modbus rtu 和
  28. * modbus tcp 的访问接口基本相同,但是在初始化时,赋值不同的函数指针。
  29. */
  30. const modbus_backend_t *backend;
  31. /*
  32. * 回调 参数,这个一般存放 通信参数,比如modbus rtu 下,就是串口的通信参数。
  33. */
  34. void *backend_data;

2、modbus_backend_t

   该数据结构 为modbus 的 回调函数集 结构体,源码如下:


  
  1. typedef struct _modbus_backend {
  2. unsigned int backend_type;
  3. unsigned int header_length;
  4. unsigned int checksum_length;
  5. unsigned int max_adu_length;
  6. int (*set_slave) (modbus_t *ctx, int slave);
  7. int (*build_request_basis) (modbus_t *ctx, int function, int addr,
  8. int nb, uint8_t *req);
  9. int (*build_response_basis) (sft_t *sft, uint8_t *rsp);
  10. int (*prepare_response_tid) ( const uint8_t *req, int *req_length);
  11. int (*send_msg_pre) (uint8_t *req, int req_length);
  12. ssize_t (*send) (modbus_t *ctx, const uint8_t *req, int req_length);
  13. int (*receive) (modbus_t *ctx, uint8_t *req);
  14. ssize_t (*recv) (modbus_t *ctx, uint8_t *rsp, int rsp_length);
  15. int (*check_integrity) (modbus_t *ctx, uint8_t *msg,
  16. const int msg_length);
  17. int (*pre_check_confirmation) (modbus_t *ctx, const uint8_t *req,
  18. const uint8_t *rsp, int rsp_length);
  19. int (*connect) (modbus_t *ctx);
  20. void (*close) (modbus_t *ctx);
  21. int (*flush) (modbus_t *ctx);
  22. int (*select) (modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length);
  23. void (*free) (modbus_t *ctx);
  24. } modbus_backend_t;

  作者将代码做了很好的封装接口,modbus rtu 和  modbus tcp 的访问接口是相同,但是在初始化时,赋值不同协议的函数指针,就是modbus 的各种 操作接口函数。

3、modbus_mapping_t 

   该结构是用于libmodbus 作为从机使用时,modbus 各区 寄存器 集合的指针,源码如下:


  
  1. typedef struct {
  2. int nb_bits;
  3. int start_bits;
  4. int nb_input_bits;
  5. int start_input_bits;
  6. int nb_input_registers;
  7. int start_input_registers;
  8. int nb_registers;
  9. int start_registers;
  10. uint8_t *tab_bits;
  11. uint8_t *tab_input_bits;
  12. uint16_t *tab_input_registers;
  13. uint16_t *tab_registers;
  14. } 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 结构指针,然后 赋值 不同的  回调函数。

示例代码:


  
  1. if (use_backend == TCP) {
  2. ctx = modbus_new_tcp( "127.0.0.1", 1502);
  3. } else if (use_backend == TCP_PI) {
  4. ctx = modbus_new_tcp_pi( "::1", "1502");
  5. } else {
  6. ctx = modbus_new_rtu( "/dev/ttyUSB1", 115200, 'N', 8, 1);
  7. }

5、modbus_set_error_recovery

    该函数 用于设定 libmodbus 运行过程中,发生错误时的 如何恢复,该函数非常重要,因为程序的运行 少不了出现各种异常,异常不怕,只要 有应对,程序还是稳定的,示例代码如下:


  
  1. modbus_set_error_recovery(ctx,
  2. MODBUS_ERROR_RECOVERY_LINK |
  3. 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
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场