modbus 协议编程 C++

2023-11-10

MODBUS通讯协议及编程

  ModBus通讯协议分为RTU协议和ASCII协议,我公司的多种仪表都采用ModBus RTU通讯协议,如:CH2000智能电力监测仪、CH2000M电力参数采集模块、巡检表、数显表、光柱数显表等。下面就ModBus RTU协议简要介绍如下:

一、通讯协议

(一)、通讯传送方式
   通讯传送分为独立的信息头,和发送的编码数据。以下的通讯传送方式定义也与MODBUS RTU通讯规约相兼容:

编 码
8位二进制
起始位
1位
数据位
8位
奇偶校验位
1位(偶校验位)
停止位
1位
错误校检
CRC(冗余循环码)

初始结构 = ≥4字节的时间
地址码 = 1 字节
功能码 = 1 字节
数据区 = N 字节
错误校检 = 16位CRC码
结束结构 = ≥4字节的时间


  地址码 :地址码为通讯传送的第一个字节。这个字节表明由用户设定地址码的从机将接收由主机发送来的信息。并且每个从机都有具有唯一的地址码,并且响应回送均以各自的地址码开始。主机发送的地址码表明将发送到的从机地址,而从机发送的地址码表明回送的从机地址。

  功能码 :通讯传送的第二个字节。ModBus通讯规约定 义功能号为1到127。本仪表只利用其中的一部分功能码。作为主机请求发送,通过功能码告诉从机执行什么动作。作为从机响应,从机发送的功能码与从主机发 送来的功能码一样,并表明从机已响应主机进行操作。如果从机发送的功能码的最高位为1(比如功能码大与此同时127),则表明从机没有响应操作或发送出 错。

  数据区 :数据区是根据不同的功能码而不同。数据区可以是实际数值、设置点、主机发送给从机或从机发送给主机的地址。

   CRC码 :二字节的错误检测码。

(二)、通讯规约:

   当通讯命令发送至仪器时,符合相应地址码的设备接通讯命令,并除去地址码,读取信息,如果没有出错,则执行相应的任务;然后把执行结果返送给发送者。返送的信息中包括地址码、执行动作的功能码、执行动作后结果的数据以及错误校验码。如果出错就不发送任何信息。

1.信息帧结构

地址码
功能码
数据区
错误校验码
8位
8位
N × 8位
16位

  地址码 :地址码是信息帧的第一字节(8位),从0到255。这个字节表明由用户设置地址的从机将接收由主机发送来的信息。每个从机都必须有唯一的地址码,并且只有符合地址码的从机才能响应回送。当从机回送信息时,相当的地址码表明该信息来自于何处。

   功能码 :主机发送的功能码告诉从机执行什么任务。表1-1列出的功能码都有具体的含义及操作。

代码
含义
操作
03 读取数据 读取当前寄存器内一个或多个二进制值
06 重置单一寄存器 把设置的二进制值写入单一寄存器

  数据区 :数据区包含需要从机执行什么动作或由从机采集的返送信息。这些信息可以是数值、参考地址等等。例如,功能码告诉从机读取寄存器的值,则数据区必需包含要读取寄存器的起始地址及读取长度。对于不同的从机,地址和数据信息都不相同。

  错误校验码 :主机或从机可用校验码进行判别接收信息是否出错。有时,由于电子噪声或其它一些干扰,信息在传输过程中会发生细微的变化,错误校验码保证了主机或从机对在传送过程中出错的信息不起作用。这样增加了系统的安全和效率。错误校验采用CRC-16校验方法。

注:信息帧的格式都基本相同:地址码、功能码、数据区和错误校验码。

2.错误校验

   冗余循环码(CRC)包含2个字节,即16位二进制。CRC码由发送设备计算,放置于发送信息的尾部。接收信息的设备再重新计算接收到信息的 CRC码,比较计算得到的CRC码是否与接收到的相符,如果两者不相符,则表明出错。

  CRC码的计算方法是,先预置16位寄存器全为1。再逐步把每8位数据信息进行处理。在进行CRC码计算时只用8位数据位,起始位及停止位,如有奇偶校验位的话也包括奇偶校验位,都不参与CRC码计算。

   在计算CRC码时,8位数据与寄存器的数据相异或,得到的结果向低位移一字节,用0填补最高位。再检查最低位,如果最低位为1,把寄存器的内容与预置数相异或,如果最低位为0,不进行异或运算。

   这个过程一直重复8次。第8次移位后,下一个8位再与现在寄存器的内容相相异或,这个过程与以上一样重复8次。当所有的数据信息处理完后,最后寄存器的内容即为CRC码值。CRC码中的数据发送、接收时低字节在前。

   计算CRC码的步骤为:

  • 预置16位寄存器为十六进制FFFF(即全为1)。称此寄存器为CRC寄存器;
  • 把第一个8位数据与16位CRC寄存器的低位相异或,把结果放于CRC寄存器;
  • 把寄存器的内容右移一位(朝低位),用0填补最高位,检查最低位;
  • 如果最低位为0:重复第3步(再次移位); 如果最低位为1:CRC寄存器与多项式A001(1010 0000 0000 0001)进行异或;
  • 重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理;
  • 重复步骤2到步骤5,进行下一个8位数据的处理;
  • 最后得到的CRC寄存器即为CRC码。

3.功能码03,读取点和返回值:

  仪表采用Modbus RTU通讯规约,利用通讯命令,可以进行读取点(“保持寄存器”) 或返回值(“输入寄存器” )的操作。保持和输入寄存器都是16位(2字节)值,并且高位在前。这样用于仪表的读取点和返回值都是2字节。一次最多可读取寄存器数是60。由于一些可 编程控制器不用功能码03,所以功能码03被用作读取点和返回值。从机响应的命令格式是从机地址、功能码、数据区及CRC码。数据区中的寄存器数据都是每 两个字节高字节在前。

4.功能码06,单点保存

  主机利用这条命令把单点数据保存到仪表的存储器。从机也用这个功能码向主机返送信息。

二、编程举例

  下面是一个用VC编写的ModBus RTU通讯的例子

(一)、通讯口设置

DCB dcb;
hCom=CreateFile("COM1",
     GENERIC_READ|GENERIC_WRITE,
     0,
     NULL,
     OPEN_EXISTING,
     0,
     NULL);
if(hCom==INVALID_HANDLE_VALUE)
{
  MessageBox("createfile error,error");
}
BOOL error=SetupComm(hCom,1024,1024);
if(!error)
  MessageBox("setupcomm error");
error=GetCommState(hCom,&dcb);
if(!error)
  MessageBox("getcommstate,error");
dcb.BaudRate=2400;
dcb.ByteSize=8;

dcb.Parity=EVENPARITY;//NOPARITY;
dcb.StopBits=ONESTOPBIT;

error=SetCommState(hCom,&dcb);

(二)、CRC校验码计算

UINT crc
void calccrc(BYTE crcbuf)
{
BYTE i;

crc=crc ^ crcbuf;
for(i=0;i<8;i++)
{
BYTE TT;
TT=crc&1;
crc=crc>>1;
crc=crc&0x7fff;
if (TT==1)
crc=crc^0xa001;
crc=crc&0xffff;
}
}

(三)、数据发送

zxaddr=11;//读取地址为11的巡检表数据
zxnum=10;//读取十个通道的数据

writebuf2[0]=zxaddr;
writebuf2[1]=3;
writebuf2[2]=0;
writebuf2[3]=0;
writebuf2[4]=0;
writebuf2[5]=zxnum;
crc=0xffff;
calccrc(writebuf2[0]);
calccrc(writebuf2[1]);
calccrc(writebuf2[2]);
calccrc(writebuf2[3]);
calccrc(writebuf2[4]);
calccrc(writebuf2[5]);

writebuf2[6]=crc & 0xff;
writebuf2[7]=crc/0x100;
WriteFile(hCom,writebuf2,8,&comnum,NULL);

(四)、数据读取

ReadFile(hCom,writebuf,5+zxnum*2,&comnum,NULL);//读取zxnum个通道数据
可增加错误处理程序,如地址码错误、CRC码错误判断、通讯故障处理等。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

modbus 协议编程 C++ 的相关文章

  • 仅当值既不为 null 也不未定义时才调用函数

    单击按钮时 我检查本地存储键中是否存在某些内容 如下所示 var a localStorage getItem foo if typeof a undefined Function 但如果该键根本不存在 则返回 null 我怎样才能打电话如
  • Ruby - 如何将消息长度表示为 2 个二进制字节

    我正在使用 Ruby 并且正在与一个网络端点进行通信 该端点需要在发送消息本身之前格式化 标头 标头中的第一个字段必须是消息长度 它被定义为网络字节顺序中的 2 个二进制字节消息长度 例如 我的消息长度是1024 如何将 1024 表示为二
  • 为什么java没有byte类型后缀? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何检查 params[:some][:field] 是否为零?

    我尝试了代码 这让很多人受益匪浅 如何测试rails中是否存在参数 https stackoverflow com questions 5629402 how to test if parameters exist in rails 但它不
  • 何时使用字节数组&何时使用字节缓冲区?

    字节数组和字节缓冲区有什么区别 另外 在什么情况下应该优先选择其中之一 我的用例是用 java 开发的 Web 应用程序 实际上有多种处理字节的方法 我同意 选择最好的并不总是那么容易 the byte the java nio ByteB
  • 如何避免在 Java 中检查空值?

    I use x null避免NullPointerException https docs oracle com javase 9 docs api java lang NullPointerException html 还有其他选择吗 i
  • 从空整数到逗号列表中的指针的转换

    我知道在我们的现代世界中 NULL 和 0 并不是指针操作的最佳实践 根据 cppreference 指针转换 空指针常量 参见 NULL 可以是 转换为任意指针类型 结果为空指针 该类型的值 这种转换 称为空指针转换 允许作为单个转换转换
  • 如何消除SQL中的NULL字段

    我正在为 SQL Server 2008 R2 开发 TSQL 查询 我正在尝试开发此查询来识别一条记录 客户 由于其中一些值为 NULL 因此我目前正在对大多数表执行 LEFT JOINS 但 LEFT JOIN 的问题是 现在我为某些客
  • 如何将可以为 null 的值或数组隐式包装到 Scala 选项中

    我在 Jar 文件中包含这个 Java 类 作为 Scala 程序的依赖项 如 Axis jar class MyClass private String someStrings public String getSomeStrings r
  • 为什么 is.null 对于不存在的列表元素返回 false?

    我正在尝试测试列表是否包含对象 github源码 https raw github com PecanProject pecan 073235d9117faf8719038e095ba589bdaca1e402 settings R rea
  • 如何在 SQL 中替换 PIVOT 中的 Null 值

    我有以下代码 我试图用零替换使用枢轴时出现的 Null 我执行了以下操作 但它说 ISNULL 附近的语法不正确 我不确定我做错了什么 有什么建议请 select from tempfinaltable pivot ISNULL sum T
  • Mysql AVG 忽略零

    我需要对一列执行平均值 但我知道该列中的大多数值都为零 在所有可能的行中 只有两行可能具有正值 我如何告诉 mySQL 忽略零并仅平均实际值 假设您可能不想完全排除此类行 也许它们在您想要聚合的其他列中具有值 SELECT AVG NULL
  • 在C语言中,NULL指针和指向0的指针有区别吗?如果是这样,那又怎样?

    在C语言中 NULL指针和指向0的指针有什么区别 ISO IEC 9899 TC2 中规定6 3 2 3 Pointers 3 值为 0 的整型常量表达式 或这样的表达式 强制转换为 void 类型 称为空指针常量 55 如果 null 指
  • 如何检查 GAS 中是否存在文件(通过 id)

    我知道有关如何检查文件是否存在的问答by name using hasnext 不过我需要检查一下按文件 ID 最好没有高级 Drive API 披露 我写了一个基于错误处理的解决方案 function ifFileExists id tr
  • 如何将字节数组转换为任何类型

    好的 我看到有人问如何将字节数组转换为int string Stream等等 答案各不相同 我个人还没有找到任何令人满意的答案 这里有一些我们想要将字节数组转换成的类型 UnityEngine Font可以吸收ttf data UnityE
  • 在 C# 中追加到空字符串是如何工作的?

    我很惊讶地看到一个字符串被初始化为 null 然后在生产环境中附加一些内容的示例 只是闻起来不对劲 我确信它会抛出空对象异常 但这个大大简化的示例也有效 string sample null sample test sample equal
  • 如何在 jQuery 中检查 null 对象

    我正在使用 jQuery 我想检查页面中是否存在某个元素 我写了以下代码 但它不起作用 if btext i null alert btext i text btext i text Branch i 如何检查元素是否存在 检查jQuery
  • 如何通过列名检查MySqlDataReader中的NULL?

    我怎样才能检查NULL开放的价值MySqlDataReader 以下不起作用 它总是击中else if rdr GetString timeOut null queryResult Egresstime Logged in else que
  • PySpark 用数组替换 Null

    通过 ID 连接后 我的数据框如下所示 ID Features Vector 1 50 Array 1 1 2 3 2 50 Null 我最终得到 向量 列中某些 ID 的空值 我想用 300 维的零数组替换这些 Null 值 与非空向量条
  • 如何在c中的某个位置终止字符指针?

    我试图通过设置空终止符来终止 c 中的字符指针 在特定位置 例如 如果我有一个 char 指针 char hi hello 我希望它是 hell 通过设置o为空 我尝试过使用 strcpy 来执行此操作 例如 strcpy hi 4 0 但

随机推荐

  • DARTS代码分析(Pytorch)

    最近在看DARTS的代码 有一个operations py的文件 里面是对各类点与点之间操作的方法 OPS none lambda C stride affine Zero stride avg pool 3x3 lambda C stri
  • YOLOv7和YOLOv5对比

    YOLO You Only Look Once 是一种基于深度学习的目标检测框架 它通过在单次前向传播中检测所有对象来实现高效目标检测 YOLOv7和YOLOv5是YOLO的不同版本 其中YOLOv7是较新的版本 在计算效率和精度方面 YO
  • IDA调试安卓应用

    先安装好IDA工具 找到需要放到android上的android server文件 我这里是64位的 将android server64放入到手机 data local tmp adb push d as data local tmp as
  • 前端面试题集锦(7)

    目录 1 常见的跨域方式 2 对象的遍历方法 3 数组扁平化 4 typeof 原理 5 介绍类型转化 6 闭包的问题和优化 7 浏览器和Node事件循环的区别 1 常见的跨域方式 JSONP JSONP 是利用外链脚本 没有跨源限制的特点
  • Java算法基础:使用递归算法实现,平方相加1^2 + 2^2 + 3^2 +...+ n^2。

    package algorithm recursion public class RecursionTest public static void main String args int m 5 int sumOfSquares sumO
  • CMake的安装(超级详细)

    安装路径 CMake 官方下载网址 https cmake org download 这里以 win10 64 位系统为例 点击下载 cmake 3 17 2 win64 x64 ms i 安装步骤 找到安装包 双击打开 打开界面如下 勾选
  • 高德API实现地理逆编码

    阅读官网 API 文档 查看各参数含义 当我们想获取北京市 麦当劳 全部餐饮地址时 请求的地址 https restapi amap com v3 place text keywords 麦当劳 city 北京 offset 20 page
  • G2Plot(antV)柱状图demo

    1 npm下载G2Plot antV 并引入项目 G2Plot antV G2Plot 开箱即用的图表库 AntV 也可以在线或下载到本地引入 2 基础柱状图 单个的 图例目前没有找到怎么显示 3 图表代码
  • 华为OD机试 - 免单统计(Java)

    题目描述 华为商城举办了一个促销活动 如果某顾客是某一秒内最早时刻下单的顾客 可能是多个人 则可以获取免单 请你编程计算有多少顾客可以获取免单 输入描述 输入为 n 行数据 每一行表示一位顾客的下单时间 以 年 月 日时 分 秒 毫秒 yy
  • java进制转换工具类

    本文主要写进制转换的工具类 具体的各个方法讲解见上一篇 进制转换 进制转换工具类及算法 1 工具类 2 二进制相加 主页传送门 传送 1 工具类 进制转换是人们利用符号来计数的方法 进制转换由一组数码符号和两个基本因素 基数 与 位权 构成
  • 从传导骚扰测试实质分析来解决传导骚扰问题

    从传导骚扰测试实质分析来解决传导骚扰问题 1前言 2测试目的 3测试示意图 4电源端口传导骚扰测试实质 5实际案例分析 1前言 传导骚扰 主要针对电源端口 测试是电磁兼容测试中不容易通过的测试项目之一 本文从电源端口传导测试实质开始 结合实
  • 路由追踪命令:tracert、pathping 值得收藏

    对于网络工程师来说 需要熟练掌握的Windows路由追踪命令有两个 tracert和pathping 其中pathping是tracert和ping命令的结合 不但可以追踪目标IP地址的路由 还可以测试经过的每一跳的时延和丢包率 trace
  • 华为OD机试,C语言实现:IP地址整数表示

    描述 原理 ip地址的每段可以看成是一个0 255的整数 把每段拆分成一个二进制形式组合起来 然后把这个二进制数转变成 一个长整数 举例 一个ip地址为10 0 3 193 每段数字 相对应的二进制数 10 00001010 0 00000
  • 总结: C++ 中如何把输出结果写入到文件中

    文是我在网上搜到额一些经验汇总 C 把输出结果写入到文件中 文件 I O 在C 中比烤蛋糕简单多了 在这篇文章里 我会详细解释ASCII和二进制文件的输入输出的每个细节 值得注意的是 所有这些都是用C 完成的 一 ASCII 输出 为了使用
  • vue实战之Filter传入多个参数

    文章目录 前言 一 Filter多参数 1 创建全局过滤器 2 引用实现 总结 前言 vue过滤器Filter用于格式化文本 即我们日常的为数字添加 或 符号等简单操作 但在现实场景中除了 往往会有略微复杂的情况出现 如 给多个不同的循环表
  • batch normalization详解

    1 引入BN的原因 1 加快模型的收敛速度 2 在一定程度上缓解了深度网络中的 梯度弥散 问题 从而使得训练深层网络模型更加容易和稳定 3 对每一批数据进行归一化 这个数据是可以输入也可以是网络中间的某一层输出 4 网络一旦train起来
  • 改变单选按钮radio圆点的颜色

    2019独角兽企业重金招聘Python工程师标准 gt gt gt
  • 实例化和初始化的区别

    一 实例化和初始化的区别 实例化 只是单纯的把对象 new 一下就行了 例 Student st new Student 这样就行了 初始化 是在实例化之后完成的 先实例化对象 只给对象属性赋值 然后初始化这个对象
  • mybatis拦截器使用及原理

    使用拦截器 Web开发中我们经常会碰到分页操作 一个项目中或许有多处使用到分页 这时如果Java后台使用MyBatis作为持久层 我们就可以使用MyBatis的拦截器功能来完成整个项目中多处的分页操作 减少代码的冗余 拦截器代码 拦截Sta
  • modbus 协议编程 C++

    MODBUS通讯协议及编程 ModBus通讯协议分为RTU协议和ASCII协议 我公司的多种仪表都采用ModBus RTU通讯协议 如 CH2000智能电力监测仪 CH2000M电力参数采集模块 巡检表 数显表 光柱数显表等 下面就ModB