libmodbus源码分析(3)从机(服务端)功能源码分析

2023-10-10 05:40

本文主要是介绍libmodbus源码分析(3)从机(服务端)功能源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

    在上一篇文章《libmodbus源码分析(2)主机(客户端)功能源码分析》 从 主机的角度 分析了 源码,本文以 从机(服务器)的角度分析一下源码。同样的,我们以 modbus rtu 协议的 4x区保持寄存器功能进行举例说明。

   我们简单的写一下 modbus rtu 下 响应客户端(主机)读4x 区保持寄存器的伪代码流程:

int main(void)
{modbus_t *ctx;modbus_mapping_t *mb_mapping;uint8_t *query;/* 创建并初始化 modbus_t 指针 */ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1);/* 设定本机设备地址 */modbus_set_slave(ctx, SERVER_ID);/* 用于接收主机消息的 缓存申请 */query = malloc(MODBUS_RTU_MAX_ADU_LENGTH);/* 0x、1x、3x、4x共4个区 寄存器的 缓存申请 */mb_mapping = modbus_mapping_new_start_address(UT_BITS_ADDRESS, UT_BITS_NB,UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB,UT_REGISTERS_ADDRESS, UT_REGISTERS_NB_MAX,UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB);/* 根据自己需要 填充 4个区 寄存器内容,这一部分可以在另外一个单独线程中,循环刷新寄存器值*/while(1){/* 等待接收主机读,直到 读到 指令,会阻塞*/do{rc = modbus_receive(ctx, query);}while(rc == 0);/* 根据接收到的主机 指令 query 内容,自动的回复 主机想要的 数据包 */rc = modbus_reply(ctx, query, rc, mb_mapping);}/* libmodbus 退出后,释放、关闭相关资源 */modbus_mapping_free(mb_mapping);free(query);/* For RTU */modbus_close(ctx);modbus_free(ctx);
}

     上述代码中,大部分都是很容易理解的,modbus_receive 函数就是在上一篇文章中已经进行了分析,这里就不再赘述了,它最终是调用 _modbus_receive_msg 函数, 它采用 select 接收机制,而且当作为 从机使用时, select 的超时时间设定为 空,

 if (msg_type == MSG_INDICATION) {/* Wait for a message, we don't know when the message will be* received */p_tv = NULL;}

  这就意味着该函数会“阻塞“等待接收,直到有数据可接收,所以在写程序的时候,需要注意,可以考虑在一个单独线程中使用。

 接下来,我们就分析一下 modbus_repley 函数的实现:

 

这里,我们再看看一下,libmodbus 是如何 知道主机要读那些寄存器,并且如何将主机想读的寄存器内容筛选打包的,源码如下:

case MODBUS_FC_READ_HOLDING_REGISTERS:case MODBUS_FC_READ_INPUT_REGISTERS: {/* 保持寄存器 or 输入寄存器判断 */unsigned int is_input = (function == MODBUS_FC_READ_INPUT_REGISTERS);/* modbus 寄存器区 首地址 获取 */int start_registers = is_input ? mb_mapping->start_input_registers : mb_mapping->start_registers;/* modbus 寄存器区 寄存器总数量,比如 4x区寄存器数量 */int nb_registers = is_input ? mb_mapping->nb_input_registers : mb_mapping->nb_registers;/* 对应区 寄存器 缓存 首地址 */uint16_t *tab_registers = is_input ? mb_mapping->tab_input_registers : mb_mapping->tab_registers;/* 调试,没用 */const char * const name = is_input ? "read_input_registers" : "read_registers";/* 筛选出 主机想要读的 寄存器 数量 */int nb = (req[offset + 3] << 8) + req[offset + 4];/* The mapping can be shifted to reduce memory consumption and itdoesn't always start at address zero. *//* 计算出 主机要读的 寄存器起始地址 在 modbus 寄存器缓存中的 偏移地址 */int mapping_address = address - start_registers;/* 主机发送命令中的 首地址、寄存器数量大小 合法性判断 */if (nb < 1 || MODBUS_MAX_READ_REGISTERS < nb) {rsp_length = response_exception(ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,"Illegal nb of values %d in %s (max %d)\n",nb, name, MODBUS_MAX_READ_REGISTERS);} else if (mapping_address < 0 || (mapping_address + nb) > nb_registers) {rsp_length = response_exception(ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,"Illegal data address 0x%0X in %s\n",mapping_address < 0 ? address : address + nb, name);} else {/* 根据前面的 计算,将对应区的寄存器 数据从 modbus 缓存 拷贝到 rsp(回复给主机的数* 据帧包) */int i;rsp_length = ctx->backend->build_response_basis(&sft, rsp);rsp[rsp_length++] = nb << 1;for (i = mapping_address; i < mapping_address + nb; i++) {rsp[rsp_length++] = tab_registers[i] >> 8;rsp[rsp_length++] = tab_registers[i] & 0xFF;}}}break;

 

 

这篇关于libmodbus源码分析(3)从机(服务端)功能源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/178349

相关文章

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

mysql表操作与查询功能详解

《mysql表操作与查询功能详解》本文系统讲解MySQL表操作与查询,涵盖创建、修改、复制表语法,基本查询结构及WHERE、GROUPBY等子句,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随... 目录01.表的操作1.1表操作概览1.2创建表1.3修改表1.4复制表02.基本查询操作2.1 SE

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

Golang如何用gorm实现分页的功能

《Golang如何用gorm实现分页的功能》:本文主要介绍Golang如何用gorm实现分页的功能方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录背景go库下载初始化数据【1】建表【2】插入数据【3】查看数据4、代码示例【1】gorm结构体定义【2】分页结构体

python中Hash使用场景分析

《python中Hash使用场景分析》Python的hash()函数用于获取对象哈希值,常用于字典和集合,不可变类型可哈希,可变类型不可,常见算法包括除法、乘法、平方取中和随机数哈希,各有优缺点,需根... 目录python中的 Hash除法哈希算法乘法哈希算法平方取中法随机数哈希算法小结在Python中,

Java Stream的distinct去重原理分析

《JavaStream的distinct去重原理分析》Javastream中的distinct方法用于去除流中的重复元素,它返回一个包含过滤后唯一元素的新流,该方法会根据元素的hashcode和eq... 目录一、distinct 的基础用法与核心特性二、distinct 的底层实现原理1. 顺序流中的去重

Java Web实现类似Excel表格锁定功能实战教程

《JavaWeb实现类似Excel表格锁定功能实战教程》本文将详细介绍通过创建特定div元素并利用CSS布局和JavaScript事件监听来实现类似Excel的锁定行和列效果的方法,感兴趣的朋友跟随... 目录1. 模拟Excel表格锁定功能2. 创建3个div元素实现表格锁定2.1 div元素布局设计2.

HTML5实现的移动端购物车自动结算功能示例代码

《HTML5实现的移动端购物车自动结算功能示例代码》本文介绍HTML5实现移动端购物车自动结算,通过WebStorage、事件监听、DOM操作等技术,确保实时更新与数据同步,优化性能及无障碍性,提升用... 目录1. 移动端购物车自动结算概述2. 数据存储与状态保存机制2.1 浏览器端的数据存储方式2.1.

基于 HTML5 Canvas 实现图片旋转与下载功能(完整代码展示)

《基于HTML5Canvas实现图片旋转与下载功能(完整代码展示)》本文将深入剖析一段基于HTML5Canvas的代码,该代码实现了图片的旋转(90度和180度)以及旋转后图片的下载... 目录一、引言二、html 结构分析三、css 样式分析四、JavaScript 功能实现一、引言在 Web 开发中,

关于MyISAM和InnoDB对比分析

《关于MyISAM和InnoDB对比分析》:本文主要介绍关于MyISAM和InnoDB对比分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录开篇:从交通规则看存储引擎选择理解存储引擎的基本概念技术原理对比1. 事务支持:ACID的守护者2. 锁机制:并发控制的艺