news 2026/4/23 22:24:21

libmodbus笔记

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
libmodbus笔记

libmodbus笔记

@@ date : 2026.4.20

@@ ps : 一些开源工程libmodbus的源码学习笔记

网址:https://github.com/stephane/libmodbus

参考书籍:《modbus软件开发实战指南》

modbus初始化

这里主从都差不多。

主函数中首先确定使用的modbus类型,并根据类型确定使用的设备相关特征,对于modbus-tcp就是ip

if(strcmp(argv[1],"tcp")==0){use_backend=TCP;}elseif(strcmp(argv[1],"tcppi")==0){use_backend=TCP_PI;}elseif(strcmp(argv[1],"rtu")==0){use_backend=RTU;......switch(use_backend){caseTCP:ip_or_device="127.0.0.1";break;caseTCP_PI:ip_or_device="::1";break;caseRTU:ip_or_device="/dev/ttyUSB1";break;}

根据使用的ip_or_device创建modbus总线,并设置要访问的从机ID

if(use_backend==TCP){ctx=modbus_new_tcp(ip_or_device,1502);}elseif(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(use_backend==RTU){modbus_set_slave(ctx,SERVER_ID);}

然后就是连接了。

其中有一个关键结构体就是函数modbus_new_rtu()创建的modbus_t *ctx,这个结构体中包含了所有需要使用到的东西,其modbus_backend_t是一堆函数指针,也就是完成各类modbus协议操作的函数。

struct_modbus{/* Slave address */intslave;/* Socket or file descriptor */ints;intdebug;interror_recovery;intquirks;structtimevalresponse_timeout;structtimevalbyte_timeout;structtimevalindication_timeout;constmodbus_backend_t*backend;void*backend_data;};typedefstruct_modbus_backend{unsignedintbackend_type;unsignedintheader_length;unsignedintchecksum_length;unsignedintmax_adu_length;int(*set_slave)(modbus_t*ctx,intslave);int(*build_request_basis)(modbus_t*ctx,intfunction,intaddr,intnb,uint8_t*req);int(*build_response_basis)(sft_t*sft,uint8_t*rsp);int(*prepare_response_tid)(constuint8_t*req,int*req_length);int(*send_msg_pre)(uint8_t*req,intreq_length);ssize_t(*send)(modbus_t*ctx,constuint8_t*req,intreq_length);int(*receive)(modbus_t*ctx,uint8_t*req);ssize_t(*recv)(modbus_t*ctx,uint8_t*rsp,intrsp_length);int(*check_integrity)(modbus_t*ctx,uint8_t*msg,constintmsg_length);int(*pre_check_confirmation)(modbus_t*ctx,constuint8_t*req,constuint8_t*rsp,intrsp_length);int(*connect)(modbus_t*ctx);unsignedint(*is_connected)(modbus_t*ctx);void(*close)(modbus_t*ctx);int(*flush)(modbus_t*ctx);int(*select)(modbus_t*ctx,fd_set*rset,structtimeval*tv,intmsg_length);void(*free)(modbus_t*ctx);}modbus_backend_t;

寄存器读写

对于写数据,文件modbus.c中给出了各类寄存器的写函数,如下:

进入这些函数就会发现,所有的函数都调用了send_msg()函数,在这个函数中调用了上面backend中的各类接口函数。

对于MODBUS-RTU就是这些函数了:

const 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 };

关键函数

来看一下modbus_new_rtu()函数中具体干了什么,其中只调用了一个函数_modbus_init_common,这个函数中对ctx中的一些基本成员进行初始化,剩下的都是直接对ctx结构体成员进行初始化,比如下面这些

ctx_rtu->baud=baud;if(parity=='N'||parity=='E'||parity=='O'){ctx_rtu->parity=parity;}else{modbus_free(ctx);errno=EINVAL;returnNULL;}ctx_rtu->data_bit=data_bit;ctx_rtu->stop_bit=stop_bit;#ifHAVE_DECL_TIOCSRS485/* The RS232 mode has been set by default */ctx_rtu->serial_mode=MODBUS_RTU_RS232;#endif#ifHAVE_DECL_TIOCM_RTS/* The RTS use has been set by default */ctx_rtu->rts=MODBUS_RTU_RTS_NONE;/* Calculate estimated time in micro second to send one byte */ctx_rtu->onebyte_time=1000000*(1+data_bit+(parity=='N'?0:1)+stop_bit)/baud;/* The internal function is used by default to set RTS */ctx_rtu->set_rts=_modbus_rtu_ioctl_rts;/* The delay before and after transmission when toggling the RTS pin */ctx_rtu->rts_delay=ctx_rtu->onebyte_time;#endifctx_rtu->confirmation_to_ignore=FALSE;

最关键的一部就是对backend进行设置ctx->backend = &_modbus_rtu_backend;.

举个例子,当我们设置从设备ID时调用下面的函数,其实就是调用了backend中的set_slave函数指针指向的函数,该函数就是上面的_modbus_rtu_backend中的_modbus_set_slave函数。

intmodbus_set_slave(modbus_t*ctx,intslave){if(ctx==NULL){errno=EINVAL;return-1;}returnctx->backend->set_slave(ctx,slave);}

以从设备的接收为例,其使用下面这个函数进行接收:

intmodbus_receive(modbus_t*ctx,uint8_t*req){if(ctx==NULL){errno=EINVAL;return-1;}returnctx->backend->receive(ctx,req);}

可以看到,仍然使用的是backend中的receive函数,也就是下面这个函数,其真正的主体调用了_modbus_receive_msg函数

staticint_modbus_rtu_receive(modbus_t*ctx,uint8_t*req){intrc;modbus_rtu_t*ctx_rtu=ctx->backend_data;if(ctx_rtu->confirmation_to_ignore){_modbus_receive_msg(ctx,req,MSG_CONFIRMATION);/* Ignore errors and reset the flag */ctx_rtu->confirmation_to_ignore=FALSE;rc=0;if(ctx->debug){printf("Confirmation to ignore\n");}}else{rc=_modbus_receive_msg(ctx,req,MSG_INDICATION);if(rc==0){/* The next expected message is a confirmation to ignore */ctx_rtu->confirmation_to_ignore=TRUE;}}returnrc;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 22:14:29

UNIT3D多语言支持:50+语言包与本地化配置完整指南

UNIT3D多语言支持:50语言包与本地化配置完整指南 【免费下载链接】UNIT3D-Community-Edition UNIT3D is a private torrent tracker built using Laravel, Livewire and AlpineJS. 项目地址: https://gitcode.com/gh_mirrors/un/UNIT3D-Community-Edition UN…

作者头像 李华
网站建设 2026/4/23 22:14:21

Go语言的sync.RWMutex读写锁竞争分析与性能优化策略

Go语言中的sync.RWMutex是一种高效的读写锁机制,广泛应用于高并发场景中。随着并发量的增加,读写锁的竞争问题逐渐显现,可能导致性能瓶颈。本文将从竞争分析入手,探讨如何优化RWMutex的性能,帮助开发者更好地利用这一工…

作者头像 李华
网站建设 2026/4/23 22:14:20

语音信号处理实战:5种窗函数对比与Python代码实现(附避坑指南)

语音信号处理实战:5种窗函数对比与Python代码实现(附避坑指南) 在数字信号处理领域,窗函数的选择往往决定了频谱分析的精度与可靠性。当我们截取一段语音信号进行傅里叶变换时,窗函数就像一扇"观察窗口"&…

作者头像 李华
网站建设 2026/4/23 22:13:25

LucidGloves与SteamVR集成指南:通过OpenGloves实现完美兼容

LucidGloves与SteamVR集成指南:通过OpenGloves实现完美兼容 【免费下载链接】lucidgloves Arduino/ESP32 based DIY VR Haptic gloves. Compatible with SteamVR via OpenGloves. 项目地址: https://gitcode.com/gh_mirrors/lu/lucidgloves LucidGloves是一款…

作者头像 李华
网站建设 2026/4/23 22:07:18

Python如何解析ini文件

文章目录1. INI文件格式简介2. 使用configparser模块安装3. 常用方法读取操作写入操作4. 处理默认值5. 高级配置6. 示例代码读取并打印配置修改并保存配置7. 注意事项在Python中解析INI文件通常使用标准库中的 configparser模块。以下是如何使用该模块的详细介绍:1.…

作者头像 李华