C# Modbus-ASCII 超详细完整笔记(协议原理+LRC算法+读写源码全拆解)

发布时间:2026/8/14 16:55:51
C# Modbus-ASCII 超详细完整笔记(协议原理+LRC算法+读写源码全拆解) 一、Modbus 基础体系知识点必背1.1 六大核心功能码含义Modbus 所有通信全部依靠功能码区分操作类型01读取线圈寄存器开关量、状态量只有 0/1可读可写02读取离散输入状态只读开关量03读取保持寄存器数值型、可读可写、最常用04读取输入寄存器传感器采集值、只读06写入单个保持寄存器定点写入1个数值16写入多个保持寄存器批量写入1.2 线圈寄存器存储特点线圈只有0 / 1状态如果一个状态占用一个字节极度浪费空间。Modbus 规则8 个线圈状态 1 个字节用单字节的8个Bit分别存储8个开关状态极致节省带宽。二、Modbus-RTU 与 Modbus-ASCII 核心区别考试高频2.1 Modbus-RTU二进制模式传输格式纯字节二进制校验方式CRC16帧结构从机地址 功能码 起始地址 数据长度 CRC校验固定8字节特点传输快、数据紧凑、工业最常用2.2 Modbus-ASCII字符串模式传输格式ASCII 字符串肉眼可读校验方式LRC 纵向冗余校验帧格式: 有效数据 LRC校验 \r\n特点兼容性强、调试直观、传输慢、字符冗余大2.3 帧格式对比核心记忆点RTU帧01 03 00 00 00 04 CRC低 CRC高ASCII帧:010300000004XXXX\r\nXXXX 为 LRC 校验码三、Modbus-ASCII 通信完整流程程序启动自动打开串口组装 6 字节有效指令数据地址、功能码、起始地址、长度对6字节数据计算LRC 校验码按照 ASCII 协议拼接冒号开头 十六进制字符串 LRC 回车换行串口发送字符串指令接收下位机返回 ASCII 字符串截取数据区、解析4个寄存器数值、打印输出支持按钮触发06功能码单寄存器写入四、核心算法详解LRC纵向冗余校验逐原理拆解4.1 LRC 计算公式固定三步LRC 只对有效指令字节计算不含帧头冒号、不含\r\n将所有参与校验的字节累加求和对总和按8位取反 1补码运算与0xFF做与运算强制截断为8位防止溢出4.2 LRC 完整源码 逐行注释/// summary /// Modbus-ASCII 专属 LRC 校验算法 /// /summary /// param namedata需要校验的有效字节数组6字节指令/param /// returns两位十六进制LRC校验字符串/returns public static string CalcLRC(byte[] data) { // 1.所有字节累加求和 uint sum (uint)data.Sum(x x); // 2.总和8位取反1 得到补码 // 3.0xff 截断高位只保留低8位保证LRC为单字节 uint res (~sum 1) 0xff; // 转2位十六进制大写字符串 return res.ToString(X2); }4.3 LRC 与 CRC16 本质区别CRC1616位校验、多项式移位异或、精度高、RTU使用LRC8位校验、累加取反补码、算法简单、ASCII使用五、ASCII请求帧组装函数核心模块5.1 功能说明传入6字节原始指令数组自动转ASCII字符串、追加LRC、追加协议头尾生成可直接发送的标准Modbus-ASCII帧。5.2 源码逐行解析/// summary /// 生成标准 Modbus-ASCII 完整请求帧 /// 格式规则 : 数据串 LRC \r\n /// /summary /// param namedata6字节有效指令数据/param /// returnsASCII协议完整帧字符串/returns public static string GetRequestFrame(byte[] data) { // 先计算当前指令的LRC校验码 string lrc CalcLRC(data); // 将所有字节转为2位十六进制字符串拼接为长串 string requestData ; foreach (byte b in data) { requestData b.ToString(X2); } // 协议固定格式 // 开头冒号 指令数据 LRC校验 结尾回车换行 string value : requestData lrc \r\n; return value; }5.4 最终生成帧示例输入01 03 00 00 00 04输出:010300000004E0\r\n六、ASCII 响应帧数据解析函数6.1 解析规则Modbus-ASCII 响应帧数据区每一个寄存器数值占 4 个字符2字节十六进制固定起始解析下标76.2 完整源码逐行注释/// summary /// 从ASCII响应字符串中批量解析保持寄存器数值 /// /summary /// param names下位机返回完整ASCII字符串/param /// param namevalueCount需要解析的寄存器个数/param /// param namestartIndex数据区起始下标默认7/param /// returnsushort寄存器数值数组/returns public static ushort[] StringToUshort(string s, int valueCount, int startIndex 7) { // 长度校验防止字符串过短越界报错 if (valueCount * 4 startIndex s.Length) { throw new ArgumentException(响应字符串个数不够); } // 定义数组存储解析后的数值 ushort[] bs new ushort[valueCount]; for (int i 0; i valueCount; i) { // 截取4个字符 1个寄存器2字节数据 string value s.Substring(startIndex, 4); // 下次读取位置后移4位 startIndex 4; // 十六进制字符串转数值 bs[i] Convert.ToUInt16(value); } return bs; }七、串口初始化 自动读取逻辑// 全局串口定义COM1、9600波特率、无校验、8数据位、1停止位 public SerialPort port new SerialPort(COM1, 9600, Parity.None, 8, StopBits.One); public Form1() { InitializeComponent(); // 打开串口 port.Open(); // 绑定数据接收事件 port.DataReceived Port_DataReceived; // 组装读取指令从站1、03功能码、0地址、读取4个寄存器 string s2 GetRequestFrame(new byte[] { 01, 03, 00, 00, 00, 04 }); Console.WriteLine(s2); // 发送ASCII读取指令 port.Write(s2); }八、串口数据接收事件响应帧解析private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e) { // 获取缓冲区数据长度 int readLength port.BytesToRead; byte[] bs new byte[readLength]; // 读取串口数据 port.Read(bs, 0, readLength); // 字节转ASCII字符串去除首尾\r\n string s Encoding.ASCII.GetString(bs).Trim(\r, \n); Console.WriteLine(s); // 判断是否为03功能码读取响应 if (s.Contains(03)) { // 解析4个寄存器数据 ushort[] us StringToUshort(s, 4); Console.WriteLine(us[0] : us[1] : us[2] : us[3]); } }九、06功能码单个寄存器写入功能按钮事件9.1 写入指令规则06功能码帧结构从站地址 06 写入地址高8位 写入地址低8位 写入值高8位 写入值低8位9.2 写入源码逐行解析private void button1_Click(object sender, EventArgs e) { byte add 0x01; // 从站地址 byte func 0x06; // 写入单个寄存器功能码 ushort addres 0x0010;// 目标写入地址 ushort value 0x1234; // 目标写入数值 // 拆分高低字节大端模式 byte[] bs { add, func, (byte)(addres 8), // 地址高8位 (byte)(addres 0xff), // 地址低8位 (byte)(value 8), // 数值高8位 (byte)(value 0xff) // 数值低8位 }; // 生成ASCII写入帧并发送 string s GetRequestFrame(bs); port.Write(s); }十、RTU与ASCII 完整帧对照总结10.1 RTU读取帧8字节01 03 00 00 00 04 CRC低 CRC高10.2 ASCII读取帧字符串:010300000004LRC\r\n10.3 RTU响应帧01 03 08 0001 0002 0003 0004 CRC10.4 ASCII响应帧:0103080001000200030004XX\r\n十一、全套核心考点总结ASCII 独有特征冒号开头、回车换行结尾、LRC校验、字符串传输RTU 独有特征二进制字节、CRC16校验、无多余字符LRC三步公式求和 → 取反1 → 0xff截断高低字节拆分右移8位取高字节与0xff取低字节ASCII解析规则单寄存器4个字符固定偏移7开始03读、06写是工业设备最常用两个功能码十二、项目整体运行流程程序启动 → 初始化串口并打开自动组装03读取指令 → 计算LRC → 生成ASCII标准帧串口下发读取指令下位机返回ASCII字符串响应帧程序接收、清洗字符串、解析4路寄存器数据点击按钮可触发06单个寄存器写入操作