c#:使用Modbus RTU协议
Modbus是一种广泛应用于工业自动化领域的通信协议,支持多种传输方式,如RTU、TCP等。其中,Modbus RTU是一种基于串行通信的协议,具有高效、可靠的特点。本文将详细介绍Modbus RTU协议的基本原理,并重点解析功能码0x03(读取保持寄存器)、0x06(写单个寄存器)和0x10(写多个寄存器)的使用和作用。同时,我们将通过C#代码实现这些功能码的读写操作。
1. Modbus RTU协议简介
1.1 协议概述
Modbus RTU协议是一种主从式通信协议,采用二进制编码,数据帧紧凑,传输效率高。它通过串口(如RS-232、RS-485)进行数据传输,适用于工业现场设备之间的通信。
1.2 数据帧格式
Modbus RTU数据帧由以下部分组成:
从站地址:1字节,表示目标设备的地址。
功能码:1字节,表示操作类型(如读取寄存器、写入寄存器等)。
数据域:可变长度,包含操作所需的数据。
CRC校验:2字节,用于校验数据帧的完整性。
2. 功能码详解
2.1 功能码0x03:读取保持寄存器
作用:读取从站设备中的一个或多个保持寄存器的值。
请求帧格式:
从站地址(1字节)
功能码(0x03,1字节)
起始寄存器地址(2字节)
寄存器数量(2字节)
CRC校验(2字节)
响应帧格式:
从站地址(1字节)
功能码(0x03,1字节)
数据字节数(1字节)
寄存器数据(N字节)
CRC校验(2字节)
2.2 功能码0x06:写单个寄存器
作用:向从站设备中的一个保持寄存器写入数据。
请求帧格式:
从站地址(1字节)
功能码(0x06,1字节)
寄存器地址(2字节)
写入值(2字节)
CRC校验(2字节)
响应帧格式:
从站地址(1字节)
功能码(0x06,1字节)
寄存器地址(2字节)
写入值(2字节)
CRC校验(2字节)
2.3 功能码0x10:写多个寄存器
作用:向从站设备中的多个保持寄存器写入数据。
请求帧格式:
从站地址(1字节)
功能码(0x10,1字节)
起始寄存器地址(2字节)
寄存器数量(2字节)
数据字节数(1字节)
写入数据(N字节)
CRC校验(2字节)
响应帧格式:
从站地址(1字节)
功能码(0x10,1字节)
起始寄存器地址(2字节)
寄存器数量(2字节)
CRC校验(2字节)
3. C#实现Modbus RTU协议
以下是使用C#实现Modbus RTU协议的代码示例,包括功能码0x03、0x06和0x10的读写操作。
3.1 串口初始化
using System.IO.Ports;public class ModbusRTU
{private SerialPort serialPort;public ModbusRTU(string portName, int baudRate){serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);serialPort.Open();}public void Close(){serialPort.Close();}
}
3.2 CRC校验
public static byte[] CalculateCRC(byte[] data)
{ushort crc = 0xFFFF;for (int i = 0; i < data.Length; i++){crc ^= data[i];for (int j = 0; j < 8; j++){if ((crc & 0x0001) != 0){crc >>= 1;crc ^= 0xA001;}else{crc >>= 1;}}}return new byte[] { (byte)(crc & 0xFF), (byte)(crc >> 8) };
}
3.3 功能码0x03:读取保持寄存器
public ushort[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfRegisters)
{byte[] request = new byte[8];request[0] = slaveAddress; // 从站地址request[1] = 0x03; // 功能码request[2] = (byte)(startAddress >> 8); // 起始地址高字节request[3] = (byte)(startAddress & 0xFF); // 起始地址低字节request[4] = (byte)(numberOfRegisters >> 8); // 寄存器数量高字节request[5] = (byte)(numberOfRegisters & 0xFF); // 寄存器数量低字节byte[] crc = CalculateCRC(request.Take(6).ToArray());request[6] = crc[0]; // CRC低字节request[7] = crc[1]; // CRC高字节serialPort.Write(request, 0, request.Length);byte[] response = new byte[5 + 2 * numberOfRegisters];serialPort.Read(response, 0, response.Length);ushort[] registers = new ushort[numberOfRegisters];for (int i = 0; i < numberOfRegisters; i++){registers[i] = (ushort)(response[3 + 2 * i] << 8 | response[4 + 2 * i]);}return registers;
}
3.4 功能码0x06:写单个寄存器
public void WriteSingleRegister(byte slaveAddress, ushort registerAddress, ushort value)
{byte[] request = new byte[8];request[0] = slaveAddress; // 从站地址request[1] = 0x06; // 功能码request[2] = (byte)(registerAddress >> 8); // 寄存器地址高字节request[3] = (byte)(registerAddress & 0xFF); // 寄存器地址低字节request[4] = (byte)(value >> 8); // 写入值高字节request[5] = (byte)(value & 0xFF); // 写入值低字节byte[] crc = CalculateCRC(request.Take(6).ToArray());request[6] = crc[0]; // CRC低字节request[7] = crc[1]; // CRC高字节serialPort.Write(request, 0, request.Length);byte[] response = new byte[8];serialPort.Read(response, 0, response.Length);
}
3.5 功能码0x10:写多个寄存器
public void WriteMultipleRegisters(byte slaveAddress, ushort startAddress, ushort[] values)
{byte[] request = new byte[9 + 2 * values.Length];request[0] = slaveAddress; // 从站地址request[1] = 0x10; // 功能码request[2] = (byte)(startAddress >> 8); // 起始地址高字节request[3] = (byte)(startAddress & 0xFF); // 起始地址低字节request[4] = (byte)(values.Length >> 8); // 寄存器数量高字节request[5] = (byte)(values.Length & 0xFF); // 寄存器数量低字节request[6] = (byte)(2 * values.Length); // 数据字节数for (int i = 0; i < values.Length; i++){request[7 + 2 * i] = (byte)(values[i] >> 8); // 数据高字节request[8 + 2 * i] = (byte)(values[i] & 0xFF); // 数据低字节}byte[] crc = CalculateCRC(request.Take(7 + 2 * values.Length).ToArray());request[7 + 2 * values.Length] = crc[0]; // CRC低字节request[8 + 2 * values.Length] = crc[1]; // CRC高字节serialPort.Write(request, 0, request.Length);byte[] response = new byte[8];serialPort.Read(response, 0, response.Length);
}
4. 总结
本文详细介绍了Modbus RTU协议的基本原理,并通过C#代码实现了功能码0x03、0x06和0x10的读写操作。Modbus RTU协议在工业自动化领域应用广泛,掌握其实现方法对于开发工业控制系统具有重要意义。
希望这篇文章对您有所帮助!如果有任何问题,欢迎在评论区留言讨论。
相关文章:
c#:使用Modbus RTU协议
Modbus是一种广泛应用于工业自动化领域的通信协议,支持多种传输方式,如RTU、TCP等。其中,Modbus RTU是一种基于串行通信的协议,具有高效、可靠的特点。本文将详细介绍Modbus RTU协议的基本原理,并重点解析功能码0x03&a…...
linux系统CentOS 7版本搭建NFS共享存储
一、什么是NFS共享存储方式 NFS共享存储方式 是一种分布式文件系统协议,允许客户端通过网络访问远程服务器上的文件,就像访问本地文件一样。 二、 NFS的基本概念 (1)服务器端:提供共享存储的机器,负责导…...
Matlab 基于SVPWM的VF三电平逆变器异步电机速度控制
1、内容简介 略 Matlab 167-基于SVPWM的VF三电平逆变器异步电机速度控制 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略...
Android Dagger2 框架依赖图构建模块深度剖析(三)
一、引言 在 Android 开发中,依赖注入(Dependency Injection,简称 DI)是一种重要的设计模式,它能够降低代码的耦合度,提高代码的可测试性和可维护性。Dagger 2 作为一款高效的依赖注入框架,在编…...
(一)微服务初见之 Spring Cloud 介绍
微服务架构简介 从单体应用架构发展到SOA架构,再到微服务架构,应用架构经历了多年的不断演进。微服务架构不是凭空产生的,而是技术发展的必然结果,分布式云平台的应用环境使得微服务代替单体应用成为互联网大型系统的架构选择。目…...
python--面试题--基础题
join() 和 split() 函数 join() 函数可以将指定的字符添加到字符串中。 a[my, name, shi, wzngz] print(..join(a)) 输出结果:my.name.shi.wzngz split() 函数可以用指定的字符分割字符串 a"my name shi wzngz " print(a.split()) 输出结果ÿ…...
架构思维:软件建模与架构设计的关键要点
文章目录 1. 软件建模的核心概念2. 七种常用UML图及其应用场景类图时序图组件图部署图用例图状态图活动图 3. 软件设计文档的三阶段结构4. 架构设计的关键实践1. 用例图:核心功能模块2. 部署图:架构演进阶段3. 技术挑战与解决方案4. 关键架构图示例5. 架…...
【RNN神经网络】序列模型与RNN神经网络
前言 清库存。正式切入大模型后,打算把基础知识都梳理一遍,然后写了两篇就发现写不动了,后面就捡重要的记录。RNN知识仅此一篇记录,扫盲记录。 【自然语言处理】 (Natural Language Processing,NLP…...
Python文件管理
目录 一、文本文件读写 1、相关函数 2、读写文件 3、使用readline读取一行 4、读写文件的异常处理 5、添加内容 二、文本文件的编码 1、常见的编码 2、Python程序的编码 3、指定编码 三、文件的路径 1、相对路径 2、绝对路径 3、路径的改变 四、文件夹操作 五、…...
vue3 前端路由权限控制与字典数据缓存实践(附Demo)
目录 前言1. 基本知识2. Demo3. 实战 前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 从实战中出发: 1. 基本知识 Vue3 和 Java 通信时如何进行字典数据管理 需要了解字典数据的结构。通常&#x…...
基于javaweb的SpringBoot精美物流管理系统设计与实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...
【极光 Orbit·STC8x】05. GPIO库函数驱动LED流动
【极光 OrbitSTC8】05. GPIO库函数驱动LED流动 七律 逐光流转 八灯列阵若星河,状态为舟渡长波。 寄存器中藏玄机,Switch语句定山河。 循环往复如潮涌,步骤变量掌沉浮。 单片机前展锋芒,代码织就光之舞。 摘要 本文基于STC8H8K6…...
DeepSeek进阶应用(二):结合Kimi制作PPT(双AI协作教程)
🌟引言: DeepSeek作为国产AI大模型,以强大的逻辑推理和结构化内容生成能力著称,擅长根据用户需求生成PPT大纲或Markdown文本;Kimi的PPT助手则能解析结构化内容并套用模板快速生成美观的PPT,两者结合实现“内…...
【Aioredis实战总结】Aioredis简介
一、Aioredis简介 Aioredis 是一个基于Python asyncio框架的异步Redis客户端库,专为高并发场景设计。它允许开发者在不阻塞主线程的情况下执行Redis操作,显著提升I/O密集型任务(如Web应用的缓存、实时消息队列等)的性能。自4.2.0…...
SpringBoot——Maven篇
Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的工具。它具有许多特性,其中一些重要的特性包括: 1. 自动配置:Spring Boot 提供了自动配置的机制,可以根据应用程序的依赖和环境自动配置应用程序的各种组件ÿ…...
Python中的多态与Java、C#、C++中的多态的区别有哪些?
Python中的多态与Java、C#、C等静态类型语言的主要区别体现在以下几个方面: 1. 类型系统与多态实现方式 Python(动态类型,鸭子类型) 多态基于对象的行为(方法的存在性),而非继承或接口。只要对…...
卷积神经网络(知识点)
一、为了使特征图变小: 由两种方法:1.增大步长:卷积的时候不是一次一步,而是一次多步,类似一张图片,在原来的像素基础上,每隔一个取一个像素点。 其中S就是步长 注意:扩大步长不经…...
Vision Transformer (ViT):将Transformer带入计算机视觉的革命性尝试(代码实现)
Vision Transformer (ViT):将Transformer带入计算机视觉的革命性尝试 作为一名深度学习研究者,如果你对自然语言处理(NLP)领域的Transformer架构了如指掌,那么你一定不会对它在序列建模中的强大能力感到陌生。然而&am…...
特殊 IP 地址
文章目录 特殊IP地址概述受限广播地址(Limited Broadcast Address)直接广播地址(Directed Broadcast Address)多播地址(Multicast Address)环回地址(Loopback Address)本网络本主机&…...
数学——A. K-divisible Sum + D. Exam in MAC
A. K-divisible Sum 题目: 思路: 以下 “[xxx]” 符号均代表向上取整 我们假设总和是sum,那么就有sum k * cnt 要想最大值最小,肯定是要让sum尽可能小,这样每个元素都能变小 最小情况是 sum 恰好等于 n 时&#…...
30天学习Java第五天——数组 字符串
数组 一维数组 定义 int[] anArray;int anOtherArray[];初始化int anOtherArray[] new int[] {1, 2, 3, 4, 5}; 访问 anArray[0] 10;可变数组:void varargsMethod(String... varargs) {} 该方法可以接收任意数量的字符串参数,可以是 0 个或者 N 个…...
【DeepSeek应用】本地部署deepseek模型后,如何在vscode中调用该模型进行代码撰写,检视和优化?
若已成功在本地部署了 DeepSeek 模型(例如通过 vscode-llm、ollama 或私有 API 服务),在 VS Code 中调用本地模型进行代码撰写、检视和优化的完整流程如下: 1. 准备工作:确认本地模型服务状态 模型服务类型: 若使用 HTTP API 服务(如 FastAPI/Flask 封装),假设服务地址…...
2025年【广东省安全员C证第四批(专职安全生产管理人员)】考试及广东省安全员C证第四批(专职安全生产管理人员)模拟试题
安全生产是各行各业不可忽视的重要环节,特别是在广东省这样的经济大省,安全生产的重要性更是不言而喻。为了确保安全生产管理人员具备足够的专业知识和实际操作能力,广东省定期举办安全员C证考试。本文将详细介绍2025年广东省安全员C证第四批…...
网络编程、URI和URL的区别、TCP/IP协议、IP和端口、URLConnection
DAY12.1 Java核心基础 网络编程 在互联网时代,网络在生活中处处可见,javaWeb占据了很大一部分 那如何实现javaWeb编程呢? Web编程就是运行在同一个网络下面的终端,使得它们之间可以进行数据传输 计算机网络基本知识 计算机网络…...
JAVA面试_进阶部分_Java JVM:垃圾回收(GC 在什么时候,对什么东西,做了什么事情)
在什么时候: 首先需要知道,GC又分为minor GC 和 Full GC(major GC)。Java堆内存分为新生代和老年代,新生代 中又分为1个eden区和两个Survior区域。 一般情况下,新创建的对象都会被分配到eden区ÿ…...
自探索大语言模型微调(一)
一、数据 1.1、失败案例 Hugging Face: 根据B站上搜索到的资料,datasets这个库可以直接下载丰富的数据集合和与训练模型,调用也非常的简单,唯一的缺点就是,需要外网(翻墙),用国内的…...
Unity 和 Python 的连接(通过SocketIO)附源码
在游戏或者项目开发中,Unity 通常用于创建前端,而 Python 则因其强大的数据处理能力常被用作后端。通过 Socket.IO,我们可以轻松地实现 Unity 和 Python 的实时通信。本文将介绍如何通过 Socket.IO 连接 Unity 和 Python,并附上完…...
89.HarmonyOS NEXT 应用安全与隐私保护指南:构建安全可靠的应用
温馨提示:本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦! HarmonyOS NEXT 应用安全与隐私保护指南:构建安全可靠的应用 文章目录 HarmonyOS NEXT 应用安全与隐私保护指南:构建安全可…...
浏览器对一个资源设置了缓存,如何清除缓存,且后续请求不命中缓存
方式1、浏览器端强制刷新 方式2、修改资源url eg:如下图,添加了查询参数 <link rel"stylesheet" href"style.css?v1.2.1"> <script src"app.js?t20231010"></script> 原理:1、在资源的…...
spring boot 发送邮件验证码
一、前置需求 1、准备邮箱 2、登录授权码 qq邮箱在–>设置–>账号POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 开启服务 二、发送邮件 1、简单邮件 包含邮件标题、邮件正文 2、引入mail启动器 <dependency><groupId>org.springframework.boot</groupI…...
