c#:使用Modbus RTU协议

news2025/3/17 20:34:37

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协议在工业自动化领域应用广泛,掌握其实现方法对于开发工业控制系统具有重要意义。

希望这篇文章对您有所帮助!如果有任何问题,欢迎在评论区留言讨论。

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

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

相关文章

linux系统CentOS 7版本搭建NFS共享存储

一、什么是NFS共享存储方式 NFS共享存储方式 是一种分布式文件系统协议&#xff0c;允许客户端通过网络访问远程服务器上的文件&#xff0c;就像访问本地文件一样。 二、 NFS的基本概念 &#xff08;1&#xff09;服务器端&#xff1a;提供共享存储的机器&#xff0c;负责导…

Matlab 基于SVPWM的VF三电平逆变器异步电机速度控制

1、内容简介 略 Matlab 167-基于SVPWM的VF三电平逆变器异步电机速度控制 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略

(一)微服务初见之 Spring Cloud 介绍

微服务架构简介 从单体应用架构发展到SOA架构&#xff0c;再到微服务架构&#xff0c;应用架构经历了多年的不断演进。微服务架构不是凭空产生的&#xff0c;而是技术发展的必然结果&#xff0c;分布式云平台的应用环境使得微服务代替单体应用成为互联网大型系统的架构选择。目…

架构思维:软件建模与架构设计的关键要点

文章目录 1. 软件建模的核心概念2. 七种常用UML图及其应用场景类图时序图组件图部署图用例图状态图活动图 3. 软件设计文档的三阶段结构4. 架构设计的关键实践1. 用例图&#xff1a;核心功能模块2. 部署图&#xff1a;架构演进阶段3. 技术挑战与解决方案4. 关键架构图示例5. 架…

【RNN神经网络】序列模型与RNN神经网络

前言 清库存。正式切入大模型后&#xff0c;打算把基础知识都梳理一遍&#xff0c;然后写了两篇就发现写不动了&#xff0c;后面就捡重要的记录。RNN知识仅此一篇记录&#xff0c;扫盲记录。 【自然语言处理】 &#xff08;Natural Language Processing&#xff0c;NLP&#xf…

Python文件管理

目录 一、文本文件读写 1、相关函数 2、读写文件 3、使用readline读取一行 4、读写文件的异常处理 5、添加内容 二、文本文件的编码 1、常见的编码 2、Python程序的编码 3、指定编码 三、文件的路径 1、相对路径 2、绝对路径 3、路径的改变 四、文件夹操作 五、…

vue3 前端路由权限控制与字典数据缓存实践(附Demo)

目录 前言1. 基本知识2. Demo3. 实战 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 从实战中出发&#xff1a; 1. 基本知识 Vue3 和 Java 通信时如何进行字典数据管理 需要了解字典数据的结构。通常&#x…

基于javaweb的SpringBoot精美物流管理系统设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

DeepSeek进阶应用(二):结合Kimi制作PPT(双AI协作教程)

&#x1f31f;引言&#xff1a; DeepSeek作为国产AI大模型&#xff0c;以强大的逻辑推理和结构化内容生成能力著称&#xff0c;擅长根据用户需求生成PPT大纲或Markdown文本&#xff1b;Kimi的PPT助手则能解析结构化内容并套用模板快速生成美观的PPT&#xff0c;两者结合实现“内…

SpringBoot——Maven篇

Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的工具。它具有许多特性&#xff0c;其中一些重要的特性包括&#xff1a; 1. 自动配置&#xff1a;Spring Boot 提供了自动配置的机制&#xff0c;可以根据应用程序的依赖和环境自动配置应用程序的各种组件&#xff…

卷积神经网络(知识点)

一、为了使特征图变小&#xff1a; 由两种方法&#xff1a;1.增大步长&#xff1a;卷积的时候不是一次一步&#xff0c;而是一次多步&#xff0c;类似一张图片&#xff0c;在原来的像素基础上&#xff0c;每隔一个取一个像素点。 其中S就是步长 注意&#xff1a;扩大步长不经…

Vision Transformer (ViT):将Transformer带入计算机视觉的革命性尝试(代码实现)

Vision Transformer (ViT)&#xff1a;将Transformer带入计算机视觉的革命性尝试 作为一名深度学习研究者&#xff0c;如果你对自然语言处理&#xff08;NLP&#xff09;领域的Transformer架构了如指掌&#xff0c;那么你一定不会对它在序列建模中的强大能力感到陌生。然而&am…

特殊 IP 地址

文章目录 特殊IP地址概述受限广播地址&#xff08;Limited Broadcast Address&#xff09;直接广播地址&#xff08;Directed Broadcast Address&#xff09;多播地址&#xff08;Multicast Address&#xff09;环回地址&#xff08;Loopback Address&#xff09;本网络本主机&…

数学——A. K-divisible Sum + D. Exam in MAC

A. K-divisible Sum 题目&#xff1a; 思路&#xff1a; 以下 “[xxx]” 符号均代表向上取整 我们假设总和是sum&#xff0c;那么就有sum k * cnt 要想最大值最小&#xff0c;肯定是要让sum尽可能小&#xff0c;这样每个元素都能变小 最小情况是 sum 恰好等于 n 时&#…

【DeepSeek应用】本地部署deepseek模型后,如何在vscode中调用该模型进行代码撰写,检视和优化?

若已成功在本地部署了 DeepSeek 模型(例如通过 vscode-llm、ollama 或私有 API 服务),在 VS Code 中调用本地模型进行代码撰写、检视和优化的完整流程如下: 1. 准备工作:确认本地模型服务状态 模型服务类型: 若使用 HTTP API 服务(如 FastAPI/Flask 封装),假设服务地址…

网络编程、URI和URL的区别、TCP/IP协议、IP和端口、URLConnection

DAY12.1 Java核心基础 网络编程 在互联网时代&#xff0c;网络在生活中处处可见&#xff0c;javaWeb占据了很大一部分 那如何实现javaWeb编程呢&#xff1f; Web编程就是运行在同一个网络下面的终端&#xff0c;使得它们之间可以进行数据传输 计算机网络基本知识 计算机网络…

自探索大语言模型微调(一)

一、数据 1.1、失败案例 Hugging Face&#xff1a; 根据B站上搜索到的资料&#xff0c;datasets这个库可以直接下载丰富的数据集合和与训练模型&#xff0c;调用也非常的简单&#xff0c;唯一的缺点就是&#xff0c;需要外网&#xff08;翻墙&#xff09;&#xff0c;用国内的…

Unity 和 Python 的连接(通过SocketIO)附源码

在游戏或者项目开发中&#xff0c;Unity 通常用于创建前端&#xff0c;而 Python 则因其强大的数据处理能力常被用作后端。通过 Socket.IO&#xff0c;我们可以轻松地实现 Unity 和 Python 的实时通信。本文将介绍如何通过 Socket.IO 连接 Unity 和 Python&#xff0c;并附上完…

89.HarmonyOS NEXT 应用安全与隐私保护指南:构建安全可靠的应用

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; HarmonyOS NEXT 应用安全与隐私保护指南&#xff1a;构建安全可靠的应用 文章目录 HarmonyOS NEXT 应用安全与隐私保护指南&#xff1a;构建安全可…

spring boot 发送邮件验证码

一、前置需求 1、准备邮箱 2、登录授权码 qq邮箱在–>设置–>账号POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 开启服务 二、发送邮件 1、简单邮件 包含邮件标题、邮件正文 2、引入mail启动器 <dependency><groupId>org.springframework.boot</groupI…