Modbus工具建立,
MODBUS协议:
1、读输出线圈(功能码:01)
Tx:01 01 00 00 00 0A BC 0D
从站地址(01)+功能码(01)+起始地址(00 00)+线圈数10(00 0A)+CRC(BC 0D)
Rx:01 01 02 0F 00 BC 0C
从站地址(01)+功能码(01)+字节数 2(02)+线圈状态(0F 00)+CRC(BC 0C)
2、强制单线圈(功能码05)
Tx:01 05 00 03 00 00 3D CA
从站地址(01)+功能码(05)+线圈地址(00 03)+操作数(00 00)+CRC(3D CA)
Rx:01 05 00 03 00 00 3D CA
原报文返回
3、读保持型寄存器(功能码03)
Tx:01 03 00 00 00 0A C5 CD
从站地址(01)+功能码(03)+起始地址(00 00)+寄存器数量(00 0A)+CRC(C5 CD)
Rx:01 03 14 00 37 00 2C 00 21 00 0A 00 00 00 00 00 00 00 00 00 00 00 00 7C CC
从站地址(01)+功能码(03)+字节数(14)+字节状态(00 37 00 2C 00 21 00 0A 00 00 00 00 00 00 00 00 00 00 00 00)+CRC(7C CC)
4、预置多寄存器(功能码10【10进制16】)
Tx:01 10 00 00 00 02 04 42 C8 00 00 66 29
从站地址(01)+功能码(10)+起始地址(00 00)+寄存器数量(00 02)+字节数量(04)+预置数(42 C8 00 00)+CRC(66 29)
Rx:01 10 00 00 00 02 41 C8
从站地址(01)+功能码(10)+起始地址(00 00)+寄存器数量(00 02)+CRC(41 C8)
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace DAL
{
public class Modbus_
{
private SerialPort _myCom;
private byte ucCRCHi = 0xFF;
private byte ucCRCLo = 0xFF;
//定义接收字节数组
byte[] bData = new byte[1024];
byte mReceiveByte; //接收字节用
int mReceiveByteCount = 0; //接收的字节数量
//定义设备地址
int CurrentAddr;
int iMWordLen;
int iMBitLen;
//定义返回报文
string strUpData;
//属性
public SerialPort MyCom { get => _myCom; set => _myCom = value; }
public Modbus_()//构造函数
{
this.MyCom = new SerialPort();
}
#region 打开关闭串口方法
/// <summary>
/// 打开串口方法 [9600 N 8 1] 8:无效验 1:停止位
/// </summary>
/// <param name="iBaudRate">波特率</param>
/// <param name="IPortNo">端口号</param>
/// <param name="iDataBits">数据位</param>
/// <param name="iParity">奇偶校验位</param>
/// <param name="iStopBits">停止位</param>
/// <returns></returns>
//波特率, 端口号, 数据位, 奇偶校验, 停止位
public bool OpenMyComm(int iBaudRate, string IPortNo, int iDataBits, Parity iParity, StopBits iStopBits)
{
try
{
if (MyCom.IsOpen)
{
MyCom.Close();
}
//设置串口属性;
MyCom.BaudRate = iBaudRate;
MyCom.PortName = IPortNo;
MyCom.DataBits = iDataBits;
MyCom.Parity = iParity;
MyCom.StopBits = iStopBits;
MyCom.ReceivedBytesThreshold = 1;//设置输入缓存字节数
MyCom.DataReceived += MyCom_DataReceived; //绑定事件
MyCom.Open();
return true;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
/// <summary>
/// 关闭串口
/// </summary>
/// <returns></returns>
private bool ClosePort()
{
if (MyCom.IsOpen == true)
{
MyCom.Close();
return true;
}
else
return false;
}
#endregion
private void MyCom_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//解析
while (MyCom.BytesToRead>0)
{
mReceiveByte = (byte)MyCom.ReadByte();
bData[mReceiveByteCount] = mReceiveByte;//如果读入,则索引自增加
mReceiveByteCount += 1;
if (mReceiveByteCount>=1024)
{
mReceiveByteCount = 0;
MyCom.DiscardInBuffer();//清除输入缓存区
return;
}
//读取保持型寄存器 功能码0x03. 如果第一个字节位从站地址且第二个字节长度
// iMWordLen 寄存器长度为8*2 字节长度, 加上前面被占用的5个位
if (bData[0] == (byte)CurrentAddr && bData[1] == 0x03 && mReceiveByteCount >= (iMWordLen * 2 + 5))
{
strUpData = "";
for (int i = 0; i < iMWordLen * 2 + 5; i++)
{
strUpData = strUpData + " " + bData[i].ToString("X2");
}
MyCom.DiscardInBuffer();
}
};
}
#region 读保持寄存器 功能码03
// MODBUS读保持寄存器 iAddress 开始地址(0开始),iLength 寄存器数量
//主站请求:01 03 00 00 00 06 70 08
//地址 1字节
//功能码 1字节 0x03
//起始寄存器地址 2字节 0x0000~0x0005
//寄存器数量 2字节 0x01~0x06
//CRC校验 2字节
/*Tx:01 03 00 00 00 0A C5 CD
从站地址(01)+功能码(03)+起始地址(00 00)+寄存器数量(00 0A)+CRC(C5 CD)
Rx:01 03 14 00 37 00 2C 00 21 00 0A 00 00 00 00 00 00 00 00 00 00 00 00 7C CC
从站地址(01)+功能码(03)+字节数(14)+字节状态(00 37 00 2C 00 21 00 0A 00 00 00 00 00 00 00 00 00 00 00 00)+CRC(7C CC)
*/
public byte[] ReadKeepReg(int iDevAdd,int iAddress,int iLength)
{
byte[] ResByte = null;
CurrentAddr = iDevAdd;
iMWordLen = iLength;
//if (comBusying == true) Thread.Sleep(250);
byte[] SendCommand = new byte[8];
SendCommand[0] = (byte)iDevAdd; //从站地址
SendCommand[1] = 0x03; //功能码(03)
SendCommand[2] = (byte)((iAddress-iAddress%256)/256);//地址高位
SendCommand[3] = (byte)((iAddress % 256) / 256); //地址低位
SendCommand[4] = (byte)((iLength - iLength % 256) / 256);//长度高位
SendCommand[5] = (byte)(iLength % 256); //长度低位
Crc16(SendCommand, 6);
SendCommand[6] = ucCRCLo;
SendCommand[7] = ucCRCHi;
//第二步, 发送报文
try
{
//发送指令
MyCom.Write(SendCommand, 0, 8);//偏移0, 长度8
}
catch
{
return null;
}
//第三步:解析报文
mReceiveByteCount = 0;
Thread.Sleep(100);
ResByte = HexStringToByteArray(this.strUpData);
return ResByte;
//1,拼接报文
}
#endregion
#region LRC校验
private string LRC(string strLRC)
{
int d_lrc = 0;
string h_lrc = "";
int l = strLRC.Length;
for (int c = 0; c < l; c = c + 2)
{
string c_data = strLRC.Substring(c, 2);
d_lrc = d_lrc + (Int32)Convert.ToByte(c_data, 16);
//d_lrc = d_lrc + Convert.ToInt32(c_data);
}
if (d_lrc >= 255)
d_lrc = d_lrc % 0x100;
h_lrc = Convert.ToInt32(~d_lrc + 1).ToString("X");
if (h_lrc.Length > 2)
h_lrc = h_lrc.Substring(h_lrc.Length - 2, 2);
return h_lrc;
}
#endregion
#region CRC校验
private static readonly byte[] aucCRCHi = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40
};
private static readonly byte[] aucCRCLo = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
0x41, 0x81, 0x80, 0x40
};
private void Crc16(byte[] pucFrame, int usLen)
{
int i = 0;
ucCRCHi = 0xFF;
ucCRCLo = 0xFF;
UInt16 iIndex = 0x0000;
while (usLen-- > 0)
{
iIndex = (UInt16)(ucCRCLo ^ pucFrame[i++]);
ucCRCLo = (byte)(ucCRCHi ^ aucCRCHi[iIndex]);
ucCRCHi = aucCRCLo[iIndex];
}
}
#endregion
#region 报文去除包头和校验,转换字节数组
/// <summary>
/// 报文转换成字节数组
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
private byte[] HexStringToByteArray(string s)
{
string Result = string.Empty;
byte[] buffer = null;
if (s != null && s.Length > 1)
{
string[] str = s.Trim().Split(' ');
string[] Res = new string[str.Length - 5];
for (int i = 0; i < str.Length - 5; i++)
{
Res[i] = str[i + 3];
}
for (int i = 0; i < Res.Length; i++)
{
Result += Res[i] + " ";
}
string[] strArr = Result.Trim().Split(' ');
buffer = new byte[strArr.Length];
for (int i = 0; i < strArr.Length; i++)
{
buffer[i] = Convert.ToByte(strArr[i], 16);
}
}
return buffer;
}
#endregion
}
}
建立winform测试
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DAL;
using System.IO.Ports;
namespace Modbus
{
public partial class FrmModbus : Form
{
public FrmModbus()
{
InitializeComponent();
}
Modbus_ objMbus = new Modbus_();
private void btn_Connect_Click(object sender, EventArgs e)
{
if (objMbus.OpenMyComm(9600, "COM4", 8, Parity.None, StopBits.One))
{
MessageBox.Show("打开成功");
}
else
MessageBox.Show("打开失败");
}
private void btn_ReadReg_Click(object sender, EventArgs e)
{
byte[] Res = objMbus.ReadKeepReg(1, 0, 10);//从站号,偏移量,长度
if (Res.Length>0)
{
for (int i = 0; i < Res.Length; i++)
{
this.listBox1.Items.Add(Res[i].ToString());
}
}
}
}
}
本文详细介绍了如何使用Modbus协议进行设备通信,包括读输出线圈、强制单线圈、读保持型寄存器和预置多寄存器的操作步骤,以及关键函数如CRC校验和LRC校验的实现。通过实例展示了如何在Windows Forms中创建串口连接并执行这些功能。
957

被折叠的 条评论
为什么被折叠?



