FreeModbus学习——读输入寄存器eMBFuncReadInputRegister

FreeModbus版本:1.6

当功能码为04时,也就是读输入寄存器MB_FUNC_READ_INPUT_REGISTER

看一下它是怎么调用读输入寄存器处理函数的

当功能码为04时,调用读输入寄存器处理函数
在这里插入图片描述
这个函数在数组xFuncHandlers中,也就是eMBFuncReadInputRegister这个函数
在这里插入图片描述

来看一下这个函数

eMBException
eMBFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen )//帧   长度
{USHORT          usRegAddress;//寄存器地址USHORT          usRegCount;//寄存器数量UCHAR          *pucFrameCur;//当前帧指针eMBException    eStatus = MB_EX_NONE;//函数返回状态eMBErrorCode    eRegStatus;//寄存器读取状态//检查帧长度if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )//2个地址+2个寄存器数量+1个功能码{//获取寄存器地址usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );//pucFrame[1]   0为功能码usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );//1为寄存器地址高字节  2为寄存器地址低字节usRegAddress++;//地址+1//获取寄存器数量usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 );//pucFrame[3]usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] );//3为寄存器数量高字节  4为寄存器数量低字节/* Check if the number of registers to read is valid. If not* return Modbus illegal data value exception. */if( ( usRegCount >= 1 )&& ( usRegCount < MB_PDU_FUNC_READ_REGCNT_MAX ) )//检查寄存器数量是否在有效范围内{/* Set the current PDU data pointer to the beginning. */pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];//将当前数据帧指针指向数据帧的起始位置,并重置数据帧长度*usLen = MB_PDU_FUNC_OFF;//MB_PDU_FUNC_OFF为功能码偏移  0/* First byte contains the function code. */*pucFrameCur++ = MB_FUNC_READ_INPUT_REGISTER;//在响应帧中添加功能码,长度+1*usLen += 1;/* Second byte in the response contain the number of bytes. */*pucFrameCur++ = ( UCHAR )( usRegCount * 2 );//在响应帧中添加寄存器数量,长度+1。*usLen += 1;//一个寄存器为16位,所以需要乘以2个字节//调用回调函数读取寄存器数据eRegStatus =eMBRegInputCB( pucFrameCur, usRegAddress, usRegCount );/* If an error occured convert it into a Modbus exception. */if( eRegStatus != MB_ENOERR ){eStatus = prveMBError2Exception( eRegStatus );}else{   //调用回调函数读取寄存器数据成功*usLen += usRegCount * 2;//响应数据长度 + 寄存器数量*2个字节}}else{eStatus = MB_EX_ILLEGAL_DATA_VALUE;}}else{/* Can't be a valid read input register request because the length* is incorrect. */eStatus = MB_EX_ILLEGAL_DATA_VALUE;}return eStatus;
}

eMBFuncReadInputRegister这个函数用于打包帧
输入参数是两个指针:
UCHAR * pucFrame, 帧 Modbus PDU 不包括 从机地址 和 CRC
USHORT * usLen帧长度

也就是说经过eMBFuncReadInputRegister这个函数处理之后,打包好的帧已经放在了pucFrame,长度放在了usLen。然后打包好的帧,会在eMBPoll轮询中被发送函数调用。然后通过发送状态机发送给主机。

来看一下eMBFuncReadInputRegister是怎么把帧打包的

首先定义了一些变量
在这里插入图片描述
变量里重要的是这个 UCHAR *pucFrameCur;//当前帧指针
pucFrameCur是指向这一帧 的 指针 ,可能有点绕
就是说这个指针pucFrameCur刚开始指向pucFrame这一帧数据的起始位置,然后打包一个数据,pucFrameCur+一个。

pucFrame是不包括从机地址 和 CRC的,也就是它的长度为5个字节 ,如下
在这里插入图片描述
(图取自安富莱modbus教程,安富莱教程写的非常好)

先检查帧的长度,2个地址+2个寄存器数量+1个功能码
在这里插入图片描述
然后获取寄存器地址和寄存器数量
在这里插入图片描述

然后开始打包数据
在这里插入图片描述
先将当前数据帧指针指向数据帧的起始位置,并重置数据帧长度

先把功能码打包 长度 +1
在把寄存器数量打包 长度+1
在看一下下面该打包啥了
看一下响应帧组成
在这里插入图片描述
(图取自安富莱modbus教程,安富莱教程写的非常好)

读取数据寄存器嘛,下面该打包 读取 到的 数据了

调用回调函数eMBRegInputCB读取寄存器数据,eMBRegInputCB这个函数会把读到的数据继续存放在pucFrameCur 帧里。

如果读取成功 则 usLen += usRegCount * 2; 响应数据长度 + 寄存器数量*2个字节

至此打包完成 。

在回到轮询函数eMBPoll
在这里插入图片描述

打包完之后 响应帧就已经存在ucMBFrame这里面了。

如果打包过程顺利的话

下面会调用peMBFrameSendCur开始发送响应帧(并不是这个函数发送,这个函数补充打包了地址+CRC)

如果万一打包不顺利,发生了错误。

则会将功能码 + 0x80 ,
在加一个异常码,
长度变为2。
如下图所示
在这里插入图片描述
(图取自安富莱modbus教程,安富莱教程写的非常好)

打包数据过程中的 读寄存器数据 那个回调函数怎么实现的呢

继续看一下eMBRegInputCB

eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{eMBErrorCode    eStatus = MB_ENOERR;int             iRegIndex;//寄存器数组索引usAddress = usAddress - 1;//传进来的地址+1了,这里要减1//判断地址是否在输入寄存器范围内if( ( usAddress >= REG_INPUT_START ) && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) ){iRegIndex = ( int )( usAddress - usRegInputStart );//地址 - 开始地址  =  索引while( usNRegs > 0 ){*pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );//寄存器值高位*pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );//寄存器值低位iRegIndex++;usNRegs--;}}else{eStatus = MB_ENOREG;}return eStatus;
}

输入参数:
pucRegBuffer: 读到的寄存器数据
usAddress: 寄存器地址
usNRegs: 要读取的寄存器个数

这个函数并不复杂
读寄存器数据,其实就是读数组嘛,寄存器是16位的,所以定义数组也是16位的SHORT型。

读数组自然得有数组索引

索引 = 参数地址 - 开始地址
iRegIndex = ( int )( usAddress - usRegInputStart );获取索引
然后将数组中的值赋值给 * pucRegBuffer就好了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/49648.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实战:MyBatis适配多种数据库:MySQL、Oracle、PostGresql等

概叙 很多时候&#xff0c;一套代码要适配多种数据库&#xff0c;主流的三种库&#xff1a;MySQL、Oracle、PostGresql&#xff0c;刚好mybatis支持这种扩展&#xff0c;如下图所示&#xff0c;在一个“namespace”&#xff0c;判断唯一的标志是iddatabaseId&#xff0c;刚好写…

RuoYi-Vue-Plus (HiKariCp JDBC连接池)

一、简介 HikariCP 是一个高性能的 JDBC 连接池实现。它以其高性能、轻量级和自动管理的特点而广受 Java 开发者的青睐。以下是 HikariCP 的主要特点: 高性能:HikariCP 通过使用低开销的并发访问机制、有效地利用线程池、采用轻量级的对象池等技术,确保在高负载情况下依然能…

mysql索引结构

多种数据结构 在数据库索引领域&#xff0c;特别是MySQL的InnoDB存储引擎中&#xff0c;聚簇索引&#xff08;Clustered Index&#xff09;和非聚簇索引&#xff08;也称为二级索引&#xff0c;Secondary Index&#xff09;是两种主要的索引类型。这些索引类型在数据结构的选择…

最优化原理(笔记)

内积是线性代数运算的一个结果&#xff0c;一行*一列。 内积的性质&#xff01; 什么是范数&#xff1f;&#xff1f;&#xff1f; 对称矩阵&#xff1a;关于主对角线对称&#xff01; 正定对称矩阵&#xff1a; 二阶导是正定的&#xff0c;f(x)就是严格的凸函数&#xff01;&a…

spring部分源码分析及Bean的生命周期理解

前言&#xff1a; 本文整体框架是通过refresh方法这个入口进入分析&#xff1a;分析IOC容器的创建及一些Bean的生命周期的知识点&#xff0c;写得确实一般般&#xff0c;感觉自己的有些前置知识并没有理解的很到位&#xff0c;所以&#xff0c;这篇文件先记录一下&#xff0c;…

推荐一款开箱即用、开源、免费的中后台管理系统模版

项目介绍 vue-pure-admin 是推荐一款开箱即用、开源&#xff08;遵循MIT License开源协议&#xff09;、免费的中后台管理系统模版&#xff0c;完全采用 ECMAScript 模块&#xff08;ESM&#xff09;规范来编写和组织代码&#xff0c;使用了最新的 Vue3、 Vite、Element-Plus、…

无人机图像目标检测技术详解

当前研究领域的热点之一。无人机搭载的高清摄像头能够实时捕获大量图像数据&#xff0c;对这些数据进行有效的目标检测对于军事侦察、环境监测、灾害救援等领域具有重要意义。本文将对无人机图像目标检测技术进行详解&#xff0c;包括图像处理技术、目标检测算法、关键技术应用…

pdf2docx - pdf 提取内容转 docx

文章目录 一、关于 pdf2docx主要功能限制 二、安装1、 PyPI2、从remote安装3、从源码安装4、卸载 三、转化 PDF例 1: convert all pages例 2: 转换指定页面例 3: multi-Processing例 4: 转换加密的pdf 四、提取表格五、命令行交互1、按页面范围2、按页码3、Multi-Processing 六…

【算法】有效的括号字符串

难度&#xff1a;中等 题目&#xff1a; 给你一个只包含三种字符的字符串&#xff0c;支持的字符类型分别是 ‘(’、‘)’ 和 ‘*’。请你检验这个字符串是否为有效字符串&#xff0c;如果是 有效 字符串返回 true 。 有效 字符串符合如下规则&#xff1a; 任何左括号 ‘(’…

gitee设置ssh公钥密码频繁密码验证

gitee中可以创建私有项目&#xff0c;但是在clone或者push都需要输入密码&#xff0c; 比较繁琐。 公钥则可以解决该问题&#xff0c;将私钥放在本地&#xff0c;公钥放在gitee上&#xff0c;当对项目进行操作时带有的私钥会在gitee和公钥进行验证&#xff0c;避免了手动输入密…

C语言数据结构课设:基于EasyX前端界面的飞机订票系统

数据结构课程设计说明书 学 院、系&#xff1a; 软件学院 专 业&#xff1a; 软件工程 班 级&#xff1a; 学 生 姓 名&#xff1a; 范 学 号&#xff1a; 设 计 题 目&#xff1a; 飞机订票系统 起 迄 日 期: 2024年6月18日~ 20…

【测试能力提升-AI】AI介绍

注释&#xff1a; 搞python的最终梦想&#xff0c;搞机器&#xff0c;玩深度&#xff0c;通网络&#xff0c;知模型&#xff0c;拿下AI技术&#xff0c;尽管只是测试&#xff0c;但是也是有梦想的 1. 目标 完成AI任务 ---- 掌握成熟、标准的任务解决方法掌握AI工具 ---- 完成…

2022 年中高职组“网络安全”赛项-海南省省竞赛任务书-1-B模块-B-4Web渗透测试

前言 本章节我将尝试操作B-4模块的渗透测试&#xff0c;搭建环境很难&#xff0c;还望大家点点赞多多支持&#xff01; 任务概览 最后4、5、6有一定的难度。 环境要求 kali Linux192.168.41.2Web服务器&#xff08;假设为PYsystem 2020 模拟平台&#xff09;192.168.41.7交换…

postman接口测试实战篇

击杀小游戏接口测试 接口测试简单介绍击杀小游戏代码下载单接口测试(postman)接口关联并参数化接口测试简单介绍 首先思考两个问题:1.接口是什么?2.接口测试是什么? 1.我们总是把接口想的很复杂,其实呢,它就是一个有特定输入和输出参数的交互逻辑处理单元,它不需要知…

【实战】Spring Cloud Stream3.0 整合RocketMq

文章目录 前言技术积累Spring Cloud Stream3.0新特性RocketMq简介 实战演示引入Maven依赖增加application配置消息生产者消息消费者 前言 相信很多同学用使用过rocketmq消息中间件&#xff0c;且大多情况下是使用原生的rocketmq-spring-boot-starter 进行集成然后创建一个rock…

Three 三维矩阵(Matrix3)、四维矩阵(Matrix4)

三维矩阵&#xff08;Matrix3&#xff09; var matrix3 new THREE.Matrix3().set( 1,2,3,4,5,6,7,8,9); //而其内部elements则展示为&#xff1a; matrix3.elements [1,4,7,2,5,8,3,6,9]; 属性&#xff08;Properties&#xff09; # .elements : Array 矩阵列优先column-…

Spring中Bean的循环依赖

目录 定义&#xff1a; 循环依赖的后果&#xff1a; 一&#xff1a;三级缓存 1、大概的思路&#xff1a; 注意&#xff1a; 2、执行过程&#xff1a; A半完成&#xff1a; B完成&#xff1a; A完成&#xff1a; 注&#xff1a; 二&#xff1a;Lazy 定义&#xff1a; …

入门C语言只需一个星期(星期三)

点击上方"蓝字"关注我们 01、基本数据类型 char 1 字节 −128 ~ 127 单个字符/字母/数字/ASCIIsigned char 1 字节 −128 ~ 127 -unsigned char 1 字节 0 ~ 255 -int…

Vue数组操作之sort详解

在 Vue.js 中&#xff0c;sort() 方法用于对数组进行排序。它会改变原数组&#xff0c;并返回排序后的数组。默认情况下&#xff0c;sort() 方法按照字母顺序&#xff08;Unicode 编码顺序&#xff09;对数组中的元素进行排序。如果需要按照其他规则排序&#xff0c;可以传递一…

【SpringCloud】微服务远程调用OpenFeign

工作原理流程图 上代码 common中添加依赖&#xff1a; <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency><groupId>org.spri…