> 技术文档 > Modbus开源库_Libmodbus

Modbus开源库_Libmodbus


1、Libmodbus简介

        在单片机上常用的Modbus库有FreeModbus和Libmodbus,这里介绍Libmodbus。

(1)Libmodbus是一个免费的跨平台支持RTU和TCP的Modbus库,遵循LGPL V2.1+协议。

(2)支持Linux、Mac OsX、FreeBSD、QNX和Windows等操作系统。

(3)支持主、从模式。

(4)稍加修改就可以在裸机、FreeRTOS、RT-Thread上使用。

2、Libmodbus的下载和源码介绍

(1)下载地址:https://github.com/stephane/libmodbus

(2)源码浏览

在目录 “ libmodbus-3.1.10\\libmodbus-3.1.10\\src” 下存在如下文件:

  • modbus.c:modbus的统一接口层。
  • modbus-data.c:一些数据处理的辅助函数
  • modbus-rtu.c:ModbusRtu模式相关程序,一般使用串口。
  • modbus-tcp.c:ModbusTcp相关协议,使用以太网。

(3)源码分层

        libmodbus通过抽象出这些层次,提供了一个跨平台的API,使得在不同硬件和平台上实现Modbus通信变得更加容易。用户在使用libmodbus时,可以根据需要选择不同的后端(如RTU或TCP),而核心层和应用层的代码可以保持不变,从而提高了代码的可移植性和可维护性。

APP 应用层(APP):这一层是用户直接交互的层面,它知道要做什么,比如主设备需要读写哪些寄存器,从设备需要提供或接收什么数据。 modbus.c Modbus核心层:这一层向上提供接口函数,向下调用底层代码构造数据包,发送、接收数据包并解析。它负责Modbus协议的具体实现,包括读写操作的逻辑处理。 modbus-rtu.c、modbus-tcp.c 后端(数据传输层):这一层负责硬件相关的数据封包与发送、接收与解包。它处理与不同网络(如串行RTU或以太网TCP IPv4/IPv6)的通信细节。 物理层 串口或以太网等

3、modbus.c文件核心函数介绍

(1)modbus_read_bits函数

  • 函数原型:int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
  • 函数功能:读布尔量(读数字量输出)
参数 说明 modbus_t *ctx 指向Modbus上下文的指针,包含了与Modbus设备通信所需的所有信息。 int addr 要读取的线圈的起始地址。 int nb 要读取的线圈的起始地址 uint8_t *dest 指向目标数组的指针,用于存储读取的线圈状态。 返回值 -1;读取失败;n:读取线圈的梳理nb

(2)modbus_read_input_bits函数

  • 函数原型:int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
  • 函数功能:读布尔量(读数字量输入/离散输入状态)

(3)modbus_write_bit函数

(4)modbus_write_bits函数

(5)modbus_read_registers函数

  • 函数原型:int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
  • 函数功能:读模拟量(读输出模拟量AO)

(6)modbus_read_input_registers函数

  • 函数原型:int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
  • 函数功能:读模拟量(读输入模拟量/AI)

(7)modbus_write_register函数

  • 函数原型:int modbus_write_register(modbus_t *ctx, int addr, const uint16_t value)
  • 函数功能:写单个输出模拟量
  • 函数调用了write_single,write_single调用了函数send_msg、_modbus_receive_msg、check_confirmation。
    • send_msg:发送消息
    • _modbus_receive_msg:接收消息
    • check_confirmation:检查

(8)modbus_write_registers函数

  • 函数原型:int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src)
  • 功能:写多个输出模拟量
  • modbus_write_registers调用了函数send_msg、_modbus_receive_msg、check_confirmation。
    • send_msg:发送消息
    • _modbus_receive_msg:接收消息
    • check_confirmation:检查

(9)send_msg函数

  • 函数原型:static int send_msg(modbus_t *ctx, uint8_t *msg, int msg_length)
  • 函数功能:发送消息
  • 调用函数ctx->backend->send_msg_pre(msg, msg_length)
    • 发送消息的准备函数
    • prepare:准备;backend:后端
  • 调用函数ctx->backend->send(ctx, msg, msg_length);
    • 发送真正的消息
    • backend是一个非常重要的结构体,该结构体由modbus-rtu.c或者modbus-tcp构造。

4、modbus-rtu.c核心函数介绍

(1)主要工作就是构造这个结构体。

// clang-format offconst modbus_backend_t _modbus_rtu_backend = { _MODBUS_BACKEND_TYPE_RTU, _MODBUS_RTU_HEADER_LENGTH, _MODBUS_RTU_CHECKSUM_LENGTH, MODBUS_RTU_MAX_ADU_LENGTH, _modbus_set_slave, _modbus_rtu_build_request_basis, _modbus_rtu_build_response_basis, _modbus_rtu_prepare_response_tid, _modbus_rtu_send_msg_pre, /* 发送 */ _modbus_rtu_send, _modbus_rtu_receive, /* 接收 */ _modbus_rtu_recv, _modbus_rtu_check_integrity, _modbus_rtu_pre_check_confirmation, _modbus_rtu_connect, _modbus_rtu_is_connected, _modbus_rtu_close, _modbus_rtu_flush, _modbus_rtu_select, _modbus_rtu_free};

5、mosbus-tcp核心函数介绍

(1)主要工作就是构造这个结构体。

const modbus_backend_t _modbus_tcp_pi_backend = { _MODBUS_BACKEND_TYPE_TCP, _MODBUS_TCP_HEADER_LENGTH, _MODBUS_TCP_CHECKSUM_LENGTH, MODBUS_TCP_MAX_ADU_LENGTH, _modbus_set_slave, _modbus_tcp_build_request_basis, _modbus_tcp_build_response_basis, _modbus_tcp_prepare_response_tid, _modbus_tcp_send_msg_pre, /* 网络发送 */ _modbus_tcp_send, _modbus_tcp_receive, /* 网络接收 */ _modbus_tcp_recv, _modbus_tcp_check_integrity, _modbus_tcp_pre_check_confirmation, _modbus_tcp_pi_connect, _modbus_tcp_is_connected, _modbus_tcp_close, _modbus_tcp_flush, _modbus_tcp_select, _modbus_tcp_pi_free};

6、modbus-data.c文件核心函数介绍

(1)bswap_16函数:实现16位无符号整数的字节交换。

  • 将数据从大端序(big-endian)转换为小端序(little-endian),或者从小端序转换为大端序。
#if !defined(bswap_16)/* 如果bswap_16没有被定义,编译器会发出一个警告信息,提示用户正在使用C函数作为字节交换的后备方案。 */# warning \"Fallback on C functions for bswap_16\"static inline uint16_t bswap_16(uint16_t x){ return (x >> 8) | (x << 8);}#endif

(2)bswap_16函数:实现32位无符号整数的字节交换。

  • 将数据从大端序(big-endian)转换为小端序(little-endian),或者从小端序转换为大端序。
# warning \"Fallback on C functions for bswap_32\"static inline uint32_t bswap_32(uint32_t x){ return (bswap_16(x & 0xffff) <> 16));}#endif

7、modbus_backend结构体

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); /* 连接相关,TCP中需要建立连接 */ int (*connect)(modbus_t *ctx); unsigned int (*is_connected)(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;

(1)涉及硬件操作的函数

  • ssize_t (*send)(modbus_t *ctx, const uint8_t *req, int req_length);
  • ssize_t (*recv)(modbus_t *ctx, uint8_t *rsp, int rsp_length);

8、unit-test-client.c文件

(1)单元测试客户端程序,客户端主动发送消息,然后接收服务器的应答。

(2)根据use_backend确认modbus的通信方式。

if (use_backend == TCP) { ctx = modbus_new_tcp(ip_or_device, 1502);} else if (use_backend == TCP_PI) { ctx = modbus_new_tcp_pi(ip_or_device, \"1502\");} else { ctx = modbus_new_rtu(ip_or_device, 115200, \'N\', 8, 1);}if (ctx == NULL) { fprintf(stderr, \"Unable to allocate libmodbus context\\n\"); return -1;}

(3)modbus_new_rtu(ip_or_device, 115200, \'N\', 8, 1);

  • ip_or_device:串口设备号
  • 115200:串口波特率
  • \'N\': 校验相关
  • 8:数据位
  • 1:停止位