C#Modbus专题

1,辅助工具。

1,虚拟串口工具(vspd.exe)
2,Modubus模拟主站(Modbus Poll)
3,Modbus模拟从站(Modbus Slave)
4,OPC服务软件(KEPServerEx V4.0)。

下载链接:https://download.csdn.net/download/lingxiao16888/89516857

2,初识Modbus。

1,Modbus协议是一种应用层报文传输协议,包含RTU,ASCII,TCP(分配的端口号为:502)三种报文类型。2,标准的Modbus协议物理层接口有RS232,RS485,RS422,以太网口。采用主从(Master/Slave)方式通信。3,Modbus主从站协议原理Modubus串行链路协议是一种主从协议。在同一时刻,只有一个主站,一个从站或者多个从站(从站最大编号247)连接在同一串行总线。Modbus通信由主站发起,从站在没有收到主站的请求时是不会发送数据。从站之间互不通信。主站在同一时刻只能发起一个Modbus事务请求。主站有两种模式对从站发起请求:广播,单播。4,Modbus帧结构Modbus串行链路协议是一种主从协议。网络上的每个从站必须有唯一的地址(从1到247)从站地址用于寻址从站设备,由主站发起。地址0用于广播模式,不需要响应。RS-485,RS-232定义了标准的物理端口,提高了操作性。

3,基于ModbusRTU的通信。

3.1,RTU与ASCII模式区别

3.2,Modbus存储区

3.3,报文格式

 

 

 

3.4,异常代码

 

3.5,详细文档连接 。

https://download.csdn.net/download/lingxiao16888/89516928?spm=1001.2014.3001.5503

3.6,代码

目的:通过标准Modbus协议完成对线圈,寄存器的读写。

  • 效果展示:

3.6.1,准备

第1步,使用虚拟串口软件创建虚拟串口。

第2步,使用ModbusSlave软件建立虚拟Modbus从站。

配置通信连接参数

配置功能定义(是进行线圈读写还是寄存器读写)

定义数据显示格式

注明:此截图中的ID=1;F=01表示从站地址为01,功能码为01(即读取线圈,具体参考前面报文格式阐述)

3.6.2,UI部分

3.6.3,UI代码部分
 public partial class Form1 : Form{ModbusSerial modbus;int slaveStation = 10;Timer timer = new Timer();public Form1(){InitializeComponent();UIControlStatusChange(false);timer.Interval = 1000;foreach (Control item in groupBox1.Controls){if (item is GroupBox){foreach (Control c in item.Controls){if (c is Button){(c as Button).Click += Button_Click;}}}}InitDataLoad();}void InitDataLoad(){//获取串口号comboPortName.DataSource = SerialPort.GetPortNames();//波特率comboBaudRate.DataSource = new int[] { 4800, 9600, 19200 };comboBaudRate.SelectedIndex = 1;//数据位comboDataBits.DataSource = new int[] { 5, 6, 7, 8 };comboDataBits.SelectedIndex = 3;//校验位comboParity.DataSource = Enum.GetNames(typeof(Parity));//停止位----不支持None所以应该None去除comboStopBits.DataSource = Enum.GetNames(typeof(StopBits)).Where(item => !item.Equals("None", StringComparison.CurrentCultureIgnoreCase)).ToArray();modbus = new ModbusSerial();modbus.ErrorMessage += Modbus_ErrorMessage;}private void Button_Click(object sender, EventArgs e){lblErrorMsg.Text = "";}private void Modbus_ErrorMessage(int arg1, string arg2){this.Invoke(new Action(() =>{lblErrorMsg.Text = $"从站[{arg1}]出现异常,原因:{arg2}";}));}/// <summary>/// 变更UI控件状态/// </summary>/// <param name="status">True:已连接,False:连接断开</param>void UIControlStatusChange(bool status){if (status){gbParam.Enabled = false;btnConnect.BackColor = Color.LightGreen;btnConnect.Text = "断开连接";groupBox1.Enabled = true;}else{gbParam.Enabled = true;btnConnect.BackColor = Color.Red;btnConnect.Text = "连接";groupBox1.Enabled = false;}}private void btnConnect_Click(object sender, EventArgs e){if (btnConnect.Text.Equals("连接")){if (modbus.IsOpen){modbus.Close();}//更新端口的设置if (comboPortName.DataSource == null || comboPortName.Items.Count==0){MessageBox.Show("没有可用的串口!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);return;}string portName = comboPortName.Text;int baudRate = Convert.ToInt32(comboBaudRate.Text);int dataBits = Convert.ToInt32(comboDataBits.Text);Parity parity = (Parity)Enum.Parse(typeof(Parity), comboParity.Text);StopBits stopBits = (StopBits)Enum.Parse(typeof(StopBits), comboStopBits.Text);modbus.BaudRate = baudRate;modbus.DataBits = dataBits;modbus.Parity = parity;modbus.StopBits = stopBits;modbus.Open(portName);}else{if (modbus.IsOpen){modbus.Close();}}UIControlStatusChange(modbus.IsOpen);}//读取多个线圈private void btnReadCoils_Click(object sender, EventArgs e){int address; int len;if (!int.TryParse(txtCoilsAddress.Text.Trim(), out address)){txtCoilsAddress.Focus();MessageBox.Show("线圈地址错误!");return;}if (!int.TryParse(txtCoilsCount.Text.Trim(), out len)){txtCoilsCount.Focus();MessageBox.Show("线圈数量只能为正整数!");return;}bool[] reuslt = modbus.ReadCoils(slaveStation, address, len);if (reuslt == null) return;StringBuilder sb = new StringBuilder();foreach (var item in reuslt){if (item){sb.Append(" 1");}else{sb.Append(" 0");}}lblCoilsResult.Text = sb.ToString();}private void txtSlave_TextChanged(object sender, EventArgs e){if (!int.TryParse(txtSlave.Text.Trim(), out slaveStation)){slaveStation = 10;}}private void btnForced_Click(object sender, EventArgs e){int address;if (!int.TryParse(txtCoilsWriteAdress.Text.Trim(), out address)){txtCoilsWriteAdress.Focus();MessageBox.Show("线圈写入地址错误!");return;}if (btnForced.Text.Equals("ON")){bool result = modbus.ForcedCoil(slaveStation, address, false);if (result){btnForced.Text = "OFF";btnForced.BackColor = Color.Red;}}else{bool result = modbus.ForcedCoil(slaveStation, address, true);if (result){btnForced.Text = "ON";btnForced.BackColor = Color.LightGreen;}}}private void btnRegistersRead_Click(object sender, EventArgs e){int address; int len;if (!int.TryParse(txtRegistersAdress.Text.Trim(), out address)){txtRegistersAdress.Focus();MessageBox.Show("寄存器地址错误!");return;}if (!int.TryParse(txtRegistersCount.Text.Trim(), out len)){txtRegistersCount.Focus();MessageBox.Show("寄存器数量只能为正整数!");return;}short[] result = modbus.ReadHoldingRegisters(slaveStation, address, len);if (result == null) return;StringBuilder sb = new StringBuilder();foreach (var item in result){sb.Append(" " + item.ToString());}lblRegistersResult.Text = sb.ToString();}private void btnReadFloat_Click(object sender, EventArgs e){int address; int len;if (!int.TryParse(txtRegistersAdress.Text.Trim(), out address)){txtRegistersAdress.Focus();MessageBox.Show("寄存器地址错误!");return;}if (!int.TryParse(txtRegistersCount.Text.Trim(), out len)){txtRegistersCount.Focus();MessageBox.Show("寄存器数量只能为正整数!");return;}float[] result = modbus.ReadFloatRegisters(slaveStation, address, len);if (result == null) return;StringBuilder sb = new StringBuilder();foreach (var item in result){sb.Append(" " + item.ToString());}lblRegistersResult.Text = sb.ToString();}private void btnWriteShort_Click(object sender, EventArgs e){int address; short val;if (!int.TryParse(txtWriteShortAdress.Text.Trim(), out address)){txtWriteShortAdress.Focus();MessageBox.Show("寄存器地址错误!");return;}if (!short.TryParse(txtWriteShortVal.Text.Trim(), out val)){txtWriteShortVal.Focus();MessageBox.Show("写入的值只能为正整数!");return;}if (modbus.WriteSingleRegister(slaveStation, address, val)){MessageBox.Show("Short数已成功写入");}else{MessageBox.Show("Short数写入失败");}}private void btnWriteFloat_Click(object sender, EventArgs e){int address; float val;if (!int.TryParse(txtFloatWriteAddress.Text.Trim(), out address)){txtFloatWriteAddress.Focus();MessageBox.Show("寄存器地址错误!");return;}if (!float.TryParse(txtWriteFloatVal.Text.Trim(), out val)){txtWriteFloatVal.Focus();MessageBox.Show("写入的值只能为正整数!");return;}if (modbus.WriteFloatRegister(slaveStation, address, val)){MessageBox.Show("Float数已成功写入");}else{MessageBox.Show("Float数写入失败");}}//给线圈批量赋值private void btnBatch_Click(object sender, EventArgs e){int address; short val;if (!int.TryParse(txtCoils.Text.Trim(), out address)){txtCoils.Focus();MessageBox.Show("线圈地址错误!");return;}if (!short.TryParse(txtCoilsVal.Text.Trim(), out val)){txtCoilsVal.Focus();MessageBox.Show("写入的值只能为正整数!");return;}bool result = modbus.WriteMultipleCoils(slaveStation, address, 16, new short[] { val });if (result){MessageBox.Show("线圈已批量写入");}else{MessageBox.Show("线圈批量失败");}}private void btnMulShortWrite_Click(object sender, EventArgs e){int address; short val; int len;if (!int.TryParse(txtMulAdress.Text.Trim(), out address)){txtMulAdress.Focus();MessageBox.Show("寄存器地址错误!");return;}if (!short.TryParse(txtMulShortVal.Text.Trim(), out val)){txtMulShortVal.Focus();MessageBox.Show("写入的值只能为正整数!");return;}if (!int.TryParse(txtMulCount.Text.Trim(), out len)){txtMulCount.Focus();MessageBox.Show("寄存器数量只能为正整数!");return;}if (modbus.WriteMultipleRegisters(slaveStation, address, len, Enumerable.Repeat(val, len).ToArray())){MessageBox.Show("寄存器已批量写入");}else{MessageBox.Show("寄存器批量写入失败");}}private void comboPortName_DropDown(object sender, EventArgs e){comboPortName.DataSource = SerialPort.GetPortNames();}}
3.6.4,Modbus类
 public class ModbusSerial : IDisposable{SerialPort serialPortObject = null;/// <summary>/// 目标站点号/// </summary>int targetStation = 0;/// <summary>/// 消息接收到标志位/// </summary>bool msgRecFlag = false;/// <summary>/// 接收到有效的数据/// </summary>byte[] RecBytes = null;AutoResetEvent slim = new AutoResetEvent(true);/// <summary>/// 返回的异常信息/// </summary>public event Action<int, string> ErrorMessage;/// <summary>/// 构建基于SerialPort的Modbus/// </summary>/// <param name="baudRate">波特率</param>/// <param name="dataBits">数据位</param>/// <param name="parity">校验位</param>/// <param name="stopBits">停止位</param>public ModbusSerial(int baudRate, int dataBits, Parity parity, StopBits stopBits){serialPortObject = new SerialPort();serialPortObject.BaudRate = baudRate;serialPortObject.DataBits = dataBits;serialPortObject.Parity = parity;serialPortObject.StopBits = stopBits;serialPortObject.DataReceived += SerialPortObject_DataReceived;}/// <summary>/// 该构造方法需通过属性设置串口参数/// </summary>public ModbusSerial(){serialPortObject = new SerialPort();serialPortObject.DataReceived += SerialPortObject_DataReceived;}ModbusSerial(SerialPort port){serialPortObject = port;serialPortObject.DataReceived += SerialPortObject_DataReceived;}static ModbusSerial modbus;/// <summary>/// 获取Modbus/// </summary>/// <param name="port"></param>/// <returns></returns>public static ModbusSerial CreateModbus(SerialPort port){if (modbus == null){modbus = new ModbusSerial(port);}return modbus;}private void SerialPortObject_DataReceived(object sender, SerialDataReceivedEventArgs e){int num = serialPortObject.BytesToRead;if (num == 0) return;byte[] bytes = new byte[num];//需要通过事件监听不可采用循环阻塞接收,如果采用循环阻塞将抛出异常serialPortObject.Read(bytes, 0, num);//对消息进行解析//检查报文是否正确msgRecFlag = true;byte[] checkSum = Checkhelper.CrcModbus(bytes, 0, bytes.Length - 2);if (checkSum[0] == bytes[bytes.Length - 1] && checkSum[1] == bytes[bytes.Length - 2]){//验证通过byte stationNo = bytes[0];targetStation = stationNo;byte commandCode = bytes[1];if (commandCode > 80){//异常信息ErrorCode errorCode = (ErrorCode)bytes[2];//这里使用异步调用如果使用同步调用将大大增加阻塞时间导致后面的AutoResetEvent等待超时ErrorMessage?.BeginInvoke(stationNo, errorCode.ToString(), null, null);}else{FunctionCode function = (FunctionCode)commandCode;byte length = bytes[2];switch (function){case FunctionCode.ReadCoils:case FunctionCode.ReadDiscreteInputs://返回字节长度RecBytes = new byte[length];Array.Copy(bytes, 3, RecBytes, 0, length);break;case FunctionCode.ReadHoldingRegisters:case FunctionCode.ReadInputRegisters://读取寄存器数据RecBytes = new byte[length];Array.Copy(bytes, 3, RecBytes, 0, length);break;case FunctionCode.WriteSingleCoil:case FunctionCode.WriteSingleRegister:case FunctionCode.WriteMultipleCoils:case FunctionCode.WriteMultipleRegisters:RecBytes = new byte[4];Array.Copy(bytes, 2, RecBytes, 0, RecBytes.Length);break;case FunctionCode.ReportSlaveID:break;case FunctionCode.MaskWriteRegister:break;case FunctionCode.ReadWriteRegister:break;default:break;}}}else{ErrorMessage?.BeginInvoke(0, "报文校验未通过,可能传送中收到干扰!", null, null);}//接收完成,取消主线程阻塞slim.Set();}/// <summary>/// 串口波特率/// </summary>public int BaudRate{get{return serialPortObject.BaudRate;}set{serialPortObject.BaudRate = value;}}/// <summary>/// 数据位长度/// </summary>public int DataBits{get{return serialPortObject.DataBits;}set{serialPortObject.DataBits = value;}}/// <summary>/// 奇偶校验协议/// </summary>public Parity Parity{get{return serialPortObject.Parity;}set{serialPortObject.Parity = value;}}/// <summary>/// 停止位/// </summary>public StopBits StopBits{get{return serialPortObject.StopBits;}set{serialPortObject.StopBits = value;}}/// <summary>/// 当前的SerialPort对象/// </summary>public SerialPort SerialPortObject{get{return serialPortObject;}}/// <summary>/// 获取计算机所有的串行端口/// </summary>public string[] SerialPorts{get{return SerialPort.GetPortNames();}}/// <summary>/// 打开指定的串口/// </summary>/// <param name="serialPortName"></param>public void Open(string serialPortName){if (serialPortObject.IsOpen){serialPortObject.Close();}serialPortObject.PortName = serialPortName;serialPortObject.Open();}/// <summary>/// 关闭串口/// </summary>public void Close(){if (serialPortObject.IsOpen){serialPortObject.Close();}}/// <summary>/// 串口是否打开/// </summary>public bool IsOpen{get{return serialPortObject.IsOpen;}}/// <summary>///发送报文/// </summary>/// <param name="bytes"></param>/// <param name="protocol">报文协议</param>void Send(string sendMsg){//对消息进行判断是否合规if (!Regex.IsMatch(sendMsg, @"^[0-9a-fA-F]+$")){throw new Exception("报文错误,存在非16进制字符");}byte[] bytes = new byte[sendMsg.Length / 2];for (int i = 0; i < sendMsg.Length / 2; i++){bytes[i] = Convert.ToByte(sendMsg.Substring(i * 2, 2), 16);}//添加校验码,并发送byte[] checkSum = Checkhelper.CrcModbus(bytes, 0, bytes.Length);var checkarr = checkSum.Reverse().ToArray();byte[] msgBytes = new byte[bytes.Length + checkSum.Length];Array.Copy(bytes, msgBytes, bytes.Length);Array.Copy(checkarr, 0, msgBytes, bytes.Length, checkarr.Length);serialPortObject.Write(msgBytes, 0, msgBytes.Length);}/// <summary>/// 编制报文/// </summary>/// <param name="slaveStation">从站站号</param>/// <param name="function">功能代码</param>/// <param name="devAdress">起始地址</param>/// <param name="devLenght">数量</param>/// <param name="values">写入的值</param>///  /// <param name="fromBase">如果写入的数值类型所占用的byte,例如short类型为2,int类型为4,flost类型为4,double类型为8</param>/// <param name="set">强制线圈通断,true:ON ;false:OFF</param>void ComposeMessage(int slaveStation, FunctionCode function, int devAdress, int devLenght = 1, byte[] values = null, int fromBase = 2, bool set = false){lock (this){slim.Reset();StringBuilder sb = new StringBuilder();//添加从站点,功能代码,起始位置sb.AppendFormat("{0}{1}{2}", slaveStation.ToString("x2"), ((int)function).ToString("X2"), devAdress.ToString("X4"));switch (function){case FunctionCode.ReadCoils:case FunctionCode.ReadDiscreteInputs:case FunctionCode.ReadHoldingRegisters:case FunctionCode.ReadInputRegisters://读取的数量,占用2bytesb.Append(devLenght.ToString("X4"));break;case FunctionCode.WriteSingleCoil:if (set){//线圈置ONsb.Append("FF00");}else{//线圈置OFFsb.Append("0000");}break;case FunctionCode.WriteSingleRegister://向单个寄存器写入值,占用两个byteint curVal = BitConverter.ToInt16(values, 0);sb.Append(curVal.ToString("X4"));break;case FunctionCode.WriteMultipleCoils://向多个线圈写入值//1,需操作的线圈数量,数值占用两个bytesb.Append(devLenght.ToString("X4"));//2,需要操作的寄存器占用的字节数,数值占用1个bytesb.Append(((int)Math.Ceiling(devLenght * 1.0 / 8)).ToString("X2"));//3,写入线圈状态值foreach (var item in values){sb.Append(item.ToString("X2"));}break;case FunctionCode.WriteMultipleRegisters://向多个寄存器写入值//1,需操作的寄存器数量,数值占用两个bytesb.Append((values.Length * 2 / fromBase).ToString("X4"));//2,需要操作的寄存器占用的字节数,数值占用1个bytesb.Append((values.Length).ToString("X2"));//3,写入寄存器的值,根据frombase判定值//此时姑且以占2个Byte计算for (int i = 0; i < values.Length; i++){sb.Append(values[i].ToString("X2"));}break;case FunctionCode.ReportSlaveID:break;case FunctionCode.MaskWriteRegister:break;case FunctionCode.ReadWriteRegister:break;default:break;}RecBytes = null;Send(sb.ToString().Trim());serialPortObject.DiscardInBuffer();//100ms等待消息回复msgRecFlag = slim.WaitOne(3000);System.Threading.Thread.Sleep(20);if (!msgRecFlag){ErrorMessage?.Invoke(slaveStation, $"从站:{slaveStation},回复超时");}}}/// <summary>/// 强制指定单个线圈动作/// </summary>/// <param name="slaveStationNo">从站站号</param>/// <param name="devAdress">线圈地址</param>/// <param name="onOrOff">True为ON,false为Off</param>/// <returns></returns>public bool ForcedCoil(int slaveStationNo, int devAdress, bool set){ComposeMessage(slaveStationNo, FunctionCode.WriteSingleCoil, devAdress, set: set);if (slaveStationNo == targetStation && msgRecFlag && RecBytes != null){if (RecBytes[2] == 0xff && RecBytes[3] == 0 && set){return true;}if (RecBytes[3] == 0 && RecBytes[2] == 0 && !set){return true;}}return false;}/// <summary>/// 读取多个线圈状态/// </summary>/// <param name="slaveStationNo"></param>/// <param name="devAdress"></param>/// <param name="iLenght"></param>public bool[] ReadCoils(int slaveStationNo, int devAdress, int iLenght){ComposeMessage(slaveStationNo, FunctionCode.ReadCoils, devAdress, iLenght);if (slaveStationNo == targetStation && msgRecFlag && RecBytes != null){BitArray array = new BitArray(RecBytes);if (iLenght > array.Count){throw new Exception("线圈查询结果异常!");}bool[] coils = new bool[iLenght];for (int i = 0; i < iLenght; i++){coils[i] = array[i];}return coils;}else{return null;}}/// <summary>/// 向多个线圈写入值/// </summary>/// <param name="slaveStationNo">从站站号</param>/// <param name="devAdress"></param>/// <param name="iLenght">地址长度</param>/// <param name="arr">写入的值</param>public bool WriteMultipleCoils(int slaveStationNo, int devAdress, int iLenght, short[] arr){if ((int)Math.Ceiling(iLenght * 1.0 / 16) != arr.Length){throw new Exception("线圈数量与所赋值数量不匹配");}byte[] bytes = new byte[arr.Length * 2];int index = 0;foreach (var item in arr){byte[] temBytes = BitConverter.GetBytes(item);Array.Copy(temBytes, 0, bytes, index, temBytes.Length);index += temBytes.Length;}ComposeMessage(slaveStationNo, FunctionCode.WriteMultipleCoils, devAdress, iLenght, bytes);if (slaveStationNo == targetStation && msgRecFlag && RecBytes != null && RecBytes.Length == 4){int startAddress = BitConverter.ToInt16(new byte[] { RecBytes[1], RecBytes[0] }, 0);int addressLen = BitConverter.ToInt16(new byte[] { RecBytes[3], RecBytes[2] }, 0);if (startAddress == devAdress && addressLen == iLenght){return true;}else{return false;}}else{return false;}}/// <summary>/// 读取从站多个寄存器的值/// </summary>/// <param name="slaveStationNo"></param>/// <param name="devAdress">起始寄存器</param>/// <param name="iLenght">寄存器数量</param>public short[] ReadHoldingRegisters(int slaveStationNo, int devAdress, int iLenght){ComposeMessage(slaveStationNo, FunctionCode.ReadHoldingRegisters, devAdress, iLenght);if (slaveStationNo == targetStation && msgRecFlag && RecBytes != null){List<short> shortList = new List<short>();for (int i = 0; i < RecBytes.Length; i += 2){shortList.Add(BitConverter.ToInt16(new byte[] { RecBytes[i + 1], RecBytes[i] }, 0));}return shortList.ToArray();}else{return null;}}/// <summary>/// 读取单精度浮点数/// </summary>/// <param name="slaveStationNo">从站站点</param>/// <param name="devAdress">起始地址</param>/// <param name="iLenght">连续读取浮点数的个数</param>/// <returns></returns>public float[] ReadFloatRegisters(int slaveStationNo, int devAdress, int iLenght){ComposeMessage(slaveStationNo, FunctionCode.ReadHoldingRegisters, devAdress, iLenght * 2);if (slaveStationNo == targetStation && msgRecFlag && RecBytes != null){List<float> floatList = new List<float>();for (int i = 0; i < RecBytes.Length; i += 4){floatList.Add(BitConverter.ToSingle(new byte[] { RecBytes[i + 1], RecBytes[i], RecBytes[i + 3], RecBytes[i + 2] }, 0));}return floatList.ToArray();}else{return null;}}/// <summary>/// 向从站单个寄存器写入值/// </summary>/// <param name="slaveStationNo"></param>/// <param name="devAdress"></param>/// <param name="value"></param>public bool WriteSingleRegister(int slaveStationNo, int devAdress, short value){ComposeMessage(slaveStationNo, FunctionCode.WriteSingleRegister, devAdress, values: BitConverter.GetBytes(value));if (slaveStationNo == targetStation && msgRecFlag && RecBytes != null && RecBytes.Length == 4){int startAddress = BitConverter.ToInt16(new byte[] { RecBytes[1], RecBytes[0] }, 0);int val = BitConverter.ToInt16(new byte[] { RecBytes[3], RecBytes[2] }, 0);if (startAddress == devAdress && val == value){return true;}else{return false;}}else{return false;}}/// <summary>/// 写入单精度浮点数/// </summary>/// <param name="slaveStationNo">从站站点</param>/// <param name="devAdress">寄存器地址</param>/// <param name="value">值</param>/// <returns>true写入成功,false写入失败</returns>public bool WriteFloatRegister(int slaveStationNo, int devAdress, float value){//进行拆解组合byte[] bytes = BitConverter.GetBytes(value);byte[] valBytes = new byte[] { bytes[1], bytes[0], bytes[3], bytes[2] };ComposeMessage(slaveStationNo, FunctionCode.WriteMultipleRegisters, devAdress, values: valBytes, fromBase: 4);if (slaveStationNo == targetStation && msgRecFlag && RecBytes != null){short adress = BitConverter.ToInt16(new byte[] { RecBytes[1], RecBytes[0] }, 0);short registerCount = BitConverter.ToInt16(new byte[] { RecBytes[3], RecBytes[2] }, 0);if (adress == devAdress && registerCount == 2){return true;}else{return false;}}else{return false;}}/// <summary>/// 向从站多个寄存器写入值/// </summary>/// <param name="slaveStationNo"></param>/// <param name="devAdress"></param>/// <param name="iLenght"></param>/// <param name="arr"></param>public bool WriteMultipleRegisters(int slaveStationNo, int devAdress, int iLenght, short[] arr){if (iLenght != arr.Length){throw new Exception("寄存器数量与所赋值数量不匹配");}byte[] bytes = new byte[arr.Length * 2];int index = 0;foreach (var item in arr){byte[] temBytes = BitConverter.GetBytes(item);byte[] temBytes2 = new byte[] { temBytes[1], temBytes[0] };Array.Copy(temBytes2, 0, bytes, index, temBytes2.Length);index += temBytes.Length;}ComposeMessage(slaveStationNo, FunctionCode.WriteMultipleRegisters, devAdress, iLenght, bytes, fromBase: 4);if (slaveStationNo == targetStation && msgRecFlag && RecBytes != null && RecBytes.Length == 4){int startAddress = BitConverter.ToInt16(new byte[] { RecBytes[1], RecBytes[0] }, 0);int addressLen = BitConverter.ToInt16(new byte[] { RecBytes[3], RecBytes[2] }, 0);if (startAddress == devAdress && addressLen == iLenght){return true;}else{return false;}}else{return false;}}public void Dispose(){serialPortObject?.Dispose();}
}
3.6.5,校验码类
/// <summary>/// 数据校验帮助类,计算常见的CRC校验;LRC校验;BCC校验;累加和校验/// </summary>public class Checkhelper{#region CRC校验说明// CRC即循环冗余校验码(Cyclic Redundancy Check):是数据通信领域中最常用的一种查错校验码,//其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,//并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。// CRC算法参数模型解释:// NAME:参数模型名称。//WIDTH:宽度,即CRC比特数。// POLY:生成项的简写,以16进制表示。例如:CRC-32即是0x04C11DB7,忽略了最高位的"1",即完整的生成项是0x104C11DB7。//INIT:这是算法开始时寄存器(crc)的初始化预置值,十六进制表示。//REFIN:待测数据的每个字节是否按位反转,True或False。// REFOUT:在计算后之后,异或输出之前,整个数据是否按位反转,True或False。// XOROUT:计算结果与此参数异或后得到最终的CRC值。#endregion// **********************************************************************// Name: CRC-4/ITU    x4+x+1// Poly: 0x03// Init: 0x00// Refin: true// Refout: true// Xorout: 0x00//*************************************************************************/// <summary>/// CRC-4/ITU/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc1(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte crc = 0;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (byte)((crc >> 1) ^ 0x0C);//0x0C = (reverse 0x03)>>(8-4)elsecrc = (byte)(crc >> 1);}}return new byte[] { crc };}// **********************************************************************// Name: CRC-5/EPC    x5+x3+1// Poly: 0x09// Init: 0x09// Refin: false// Refout: false// Xorout: 0x00//*************************************************************************/// <summary>/// CRC-5/EPC/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc2(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte crc = 0x48;// Initial value: 0x48 = 0x09<<(8-5)for (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 0x80) > 0)crc = (byte)((crc << 1) ^ 0x48);// 0x48 = 0x09<<(8-5)elsecrc = (byte)(crc << 1);}}return new byte[] { (byte)(crc >> 3) };}// **********************************************************************// Name: CRC-5/ITU    x5+x4+x2+1// Poly: 0x15// Init: 0x00// Refin: true// Refout: true// Xorout: 0x00//*************************************************************************/// <summary>/// CRC-5/ITU /// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc3(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte crc = 0;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (byte)((crc >> 1) ^ 0x15);// 0x15 = (reverse 0x15)>>(8-5)elsecrc = (byte)(crc >> 1);}}return new byte[] { crc };}// **********************************************************************// Name: CRC-5/USB    x5+x2+1// Poly: 0x05// Init: 0x1F// Refin: true// Refout: true// Xorout: 0x1F//*************************************************************************/// <summary>/// CRC-5/USB/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc4(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte crc = 0x1F;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (byte)((crc >> 1) ^ 0x14);// 0x14 = (reverse 0x05)>>(8-5)elsecrc = (byte)(crc >> 1);}}return new byte[] { (byte)(crc ^ 0x1F) };}// **********************************************************************// Name: CRC-6/ITU    x6+x+1// Poly: 0x03// Init: 0x00// Refin: true// Refout: true// Xorout: 0x00//*************************************************************************/// <summary>/// CRC-6/ITU/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc5(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte crc = 0;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (byte)((crc >> 1) ^ 0x30);// 0x30 = (reverse 0x03)>>(8-6)elsecrc = (byte)(crc >> 1);}}return new byte[] { crc };}// **********************************************************************// Name: CRC-7/MMC    x7+x3+1// Poly: 0x09// Init: 0x00// Refin: false// Refout: false// Xorout: 0x00//*************************************************************************/// <summary>/// CRC-7/MMC/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc6(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte crc = 0;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 0x80) > 0)crc = (byte)((crc << 1) ^ 0x12);// 0x12 = 0x09<<(8-7)elsecrc = (byte)(crc << 1);}}return new byte[] { (byte)(crc >> 1) };}// **********************************************************************// Name: CRC8    x8+x2+x+1// Poly: 0x07// Init: 0x00// Refin: false// Refout: false// Xorout: 0x00//*************************************************************************/// <summary>/// CRC8 /// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc7(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte crc = 0;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 0x80) > 0)crc = (byte)((crc << 1) ^ 0x07);elsecrc = (byte)(crc << 1);}}return new byte[] { crc };}// **********************************************************************// Name: CRC-8/ITU    x8+x2+x+1// Poly: 0x07// Init: 0x00// Refin: false// Refout: false// Xorout: 0x55//*************************************************************************/// <summary>/// CRC-8/ITU/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc8(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte crc = 0;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 0x80) > 0)crc = (byte)((crc << 1) ^ 0x07);elsecrc = (byte)(crc << 1);}}return new byte[] { (byte)(crc ^ 0x55) };}// **********************************************************************// Name: CRC-8/MAXIM    x8+x5+x4+1// Poly: 0x31// Init: 0x00// Refin: true// Refout: true// Xorout: 0x00//*************************************************************************/// <summary>/// CRC-8/MAXIM /// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc9(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte crc = 0;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (byte)((crc >> 1) ^ 0x8C);// 0x8C = reverse 0x31elsecrc = (byte)(crc >> 1);}}return new byte[] { crc };}// **********************************************************************// Name: CRC-8/ROHC    x8+x2+x+1// Poly: 0x07// Init: 0xFF// Refin: true// Refout: true// Xorout: 0x00//*************************************************************************/// <summary>/// CRC-8/ROHC/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc10(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte crc = 0xFF;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (byte)((crc >> 1) ^ 0xE0);// 0xE0 = reverse 0x07elsecrc = (byte)(crc >> 1);}}return new byte[] { crc };}/// Z1协议校验码计算static byte[] table = { 0x00, 0x1C, 0x38, 0x24, 0x70, 0x6C, 0x48, 0x54, 0xE0, 0xFC,0xD8, 0xC4, 0x90, 0x8C, 0xA8, 0xB4, 0xDC, 0xC0, 0xE4, 0xF8,0xAC, 0xB0, 0x94, 0x88, 0x3C, 0x20, 0x04, 0x18, 0x4C, 0x50,0x74, 0x68, 0xA4, 0xB8, 0x9C, 0x80, 0xD4, 0xC8, 0xEC, 0xF0,0x44, 0x58, 0x7C, 0x60, 0x34, 0x28, 0x0C, 0x10, 0x78, 0x64,0x40, 0x5C, 0x08, 0x14, 0x30, 0x2C, 0x98, 0x84, 0xA0, 0xBC,0xE8, 0xF4, 0xD0, 0xCC, 0x54, 0x48, 0x6C, 0x70, 0x24, 0x38,0x1C, 0x00, 0xB4, 0xA8, 0x8C, 0x90, 0xC4, 0xD8, 0xFC, 0xE0,0x88, 0x94, 0xB0, 0xAC, 0xF8, 0xE4, 0xC0, 0xDC, 0x68, 0x74,0x50, 0x4C, 0x18, 0x04, 0x20, 0x3C, 0xF0, 0xEC, 0xC8, 0xD4,0x80, 0x9C, 0xB8, 0xA4, 0x10, 0x0C, 0x28, 0x34, 0x60, 0x7C,0x58, 0x44, 0x2C, 0x30, 0x14, 0x08, 0x5C, 0x40, 0x64, 0x78,0xCC, 0xD0, 0xF4, 0xE8, 0xBC, 0xA0, 0x84, 0x98, 0xA8, 0xB4,0x90, 0x8C, 0xD8, 0xC4, 0xE0, 0xFC, 0x48, 0x54, 0x70, 0x6C,0x38, 0x24, 0x00, 0x1C, 0x74, 0x68, 0x4C, 0x50, 0x04, 0x18,0x3C, 0x20, 0x94, 0x88, 0xAC, 0xB0, 0xE4, 0xF8, 0xDC, 0xC0,0x0C, 0x10, 0x34, 0x28, 0x7C, 0x60, 0x44, 0x58, 0xEC, 0xF0,0xD4, 0xC8, 0x9C, 0x80, 0xA4, 0xB8, 0xD0, 0xCC, 0xE8, 0xF4,0xA0, 0xBC, 0x98, 0x84, 0x30, 0x2C, 0x08, 0x14, 0x40, 0x5C,0x78, 0x64, 0xFC, 0xE0, 0xC4, 0xD8, 0x8C, 0x90, 0xB4, 0xA8,0x1C, 0x00, 0x24, 0x38, 0x6C, 0x70, 0x54, 0x48, 0x20, 0x3C,0x18, 0x04, 0x50, 0x4C, 0x68, 0x74, 0xC0, 0xDC, 0xF8, 0xE4,0xB0, 0xAC, 0x88, 0x94, 0x58, 0x44, 0x60, 0x7C, 0x28, 0x34,0x10, 0x0C, 0xB8, 0xA4, 0x80, 0x9C, 0xC8, 0xD4, 0xF0, 0xEC,0x84, 0x98, 0xBC, 0xA0, 0xF4, 0xE8, 0xCC, 0xD0, 0x64, 0x78,0x5C, 0x40, 0x14, 0x08, 0x2C, 0x30};public static byte[] Crc11(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;int i;byte crc = 0x00;int tableIndex;for (i = start; i < length; i++){tableIndex = crc ^ (buffer[i] & 0xFF);crc = table[tableIndex];}return new byte[] { crc };}// **********************************************************************// Name: CRC-12    x16+x12+x5+1// Poly: 0x80// Init: 0x0000// Refin: true// Refout: true// Xorout: 0x0000//*************************************************************************/// <summary>/// CRC-12 /// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc12(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;ushort crc = 0;// Initial valueshort iQ = 0, iR = 0;for (int i = start; i < length; i++){// 多项式除法// 如果该位为1if ((buffer[i] & (0x80 >> iR)) > 0){// 则在余数尾部添1否则添0crc |= 0x01;}// 如果12位除数中的最高位为1,则够除if (crc >= 0x1000){crc ^= 0x180D;}crc <<= 1;iR++;if (8 == iR){iR = 0;iQ++;}}// 对后面添加的12个0做处理for (int i = 0; i < 12; i++){if (crc >= 0x1000){crc ^= 0x180D;}crc <<= 1;}crc >>= 1;byte[] ret = BitConverter.GetBytes(crc);Array.Reverse(ret);return ret;}// **********************************************************************// Name: CRC-16/CCITT    x16+x12+x5+1// Poly: 0x1021// Init: 0x0000// Refin: true// Refout: true// Xorout: 0x0000//*************************************************************************/// <summary>/// CRC-16/CCITT/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc13(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;ushort crc = 0;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (ushort)((crc >> 1) ^ 0x8408);// 0x8408 = reverse 0x1021elsecrc = (ushort)(crc >> 1);}}byte[] ret = BitConverter.GetBytes(crc);Array.Reverse(ret);return ret;}// **********************************************************************// Name: CRC-16/CCITT FALSE    x16+x12+x5+1// Poly: 0x1021// Init: 0xFFFF// Refin: false// Refout: false// Xorout: 0x0000//*************************************************************************/// <summary>/// CRC-16/CCITT FALSE/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc14(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;ushort crc = 0xFFFF;// Initial valuefor (int i = start; i < length; i++){crc ^= (ushort)(buffer[i] << 8);for (int j = 0; j < 8; j++){if ((crc & 0x8000) > 0)crc = (ushort)((crc << 1) ^ 0x1021);elsecrc = (ushort)(crc << 1);}}byte[] ret = BitConverter.GetBytes(crc);Array.Reverse(ret);return ret;}// **********************************************************************// Name: CRC-16/DNP    x16+x13+x12+x11+x10+x8+x6+x5+x2+1// Poly: 0x3D65// Init: 0x0000// Refin: true// Refout: true// Xorout: 0xFFFF//*************************************************************************/// <summary>/// CRC-16/DNP /// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc15(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;ushort crc = 0;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (ushort)((crc >> 1) ^ 0xA6BC);// 0xA6BC = reverse 0x3D65elsecrc = (ushort)(crc >> 1);}}byte[] ret = BitConverter.GetBytes((ushort)~crc);Array.Reverse(ret);return ret;}// **********************************************************************// Name: CRC-16/IBM    x16+x15+x2+1// Poly: 0x8005// Init: 0x0000// Refin: true// Refout: true// Xorout: 0x0000//*************************************************************************/// <summary>/// CRC-16/IBM/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc16(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;ushort crc = 0;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (ushort)((crc >> 1) ^ 0xA001);// 0xA001 = reverse 0x8005elsecrc = (ushort)(crc >> 1);}}byte[] ret = BitConverter.GetBytes(crc);Array.Reverse(ret);return ret;}// **********************************************************************// Name: CRC-16/MAXIM    x16+x15+x2+1// Poly: 0x8005// Init: 0x0000// Refin: true// Refout: true// Xorout: 0xFFFF//*************************************************************************/// <summary>/// CRC-16/MAXIM/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc17(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;ushort crc = 0;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (ushort)((crc >> 1) ^ 0xA001);// 0xA001 = reverse 0x8005elsecrc = (ushort)(crc >> 1);}}byte[] ret = BitConverter.GetBytes((ushort)~crc);Array.Reverse(ret);return ret;}// **********************************************************************// Name: CRC-16/MODBUS    x16+x15+x2+1// Poly: 0x8005// Init: 0xFFFF// Refin: true// Refout: true// Xorout: 0x0000//*************************************************************************/// <summary>/// CRC-16/MODBUS/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] CrcModbus(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;ushort crc = 0xFFFF;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (ushort)((crc >> 1) ^ 0xA001);// 0xA001 = reverse 0x8005elsecrc = (ushort)(crc >> 1);}}byte[] ret = BitConverter.GetBytes(crc);Array.Reverse(ret);return ret;}// **********************************************************************// Name: CRC-16/USB    x16+x15+x2+1// Poly: 0x8005// Init: 0xFFFF// Refin: true// Refout: true// Xorout: 0xFFFF//*************************************************************************/// <summary>/// CRC-16/USB/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc19(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;ushort crc = 0xFFFF;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (ushort)((crc >> 1) ^ 0xA001);// 0xA001 = reverse 0x8005elsecrc = (ushort)(crc >> 1);}}byte[] ret = BitConverter.GetBytes((ushort)~crc);Array.Reverse(ret);return ret;}// **********************************************************************// Name: CRC-16/X25    x16+x12+x5+1// Poly: 0x1021// Init: 0xFFFF// Refin: true// Refout: true// Xorout: 0xFFFF//*************************************************************************/// <summary>/// CRC-16/X25/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc20(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;ushort crc = 0xFFFF;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (ushort)((crc >> 1) ^ 0x8408);// 0x8408 = reverse 0x1021elsecrc = (ushort)(crc >> 1);}}byte[] ret = BitConverter.GetBytes((ushort)~crc);Array.Reverse(ret);return ret;}// **********************************************************************// Name: CRC-16/XMODEM    x16+x12+x5+1// Poly: 0x1021// Init: 0x0000// Refin: false// Refout: false// Xorout: 0x0000//*************************************************************************/// <summary>/// CRC-16/XMODEM/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc21(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;ushort crc = 0;// Initial valuefor (int i = start; i < length; i++){crc ^= (ushort)(buffer[i] << 8);for (int j = 0; j < 8; j++){if ((crc & 0x8000) > 0)crc = (ushort)((crc << 1) ^ 0x1021);elsecrc = (ushort)(crc << 1);}}byte[] ret = BitConverter.GetBytes(crc);Array.Reverse(ret);return ret;}// **********************************************************************// Name: CRC32    x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1// Poly: 0x04C11DB7// Init: 0xFFFFFFFF// Refin: true// Refout: true// Xorout: 0xFFFFFFFF//*************************************************************************/// <summary>/// CRC32/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc22(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;uint crc = 0xFFFFFFFF;// Initial valuefor (int i = start; i < length; i++){crc ^= buffer[i];for (int j = 0; j < 8; j++){if ((crc & 1) > 0)crc = (crc >> 1) ^ 0xEDB88320;// 0xEDB88320= reverse 0x04C11DB7elsecrc = crc >> 1;}}byte[] ret = BitConverter.GetBytes(~crc);Array.Reverse(ret);return ret;}// **********************************************************************// Name: CRC32/MPEG-2    x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1// Poly: 0x04C11DB7// Init: 0xFFFFFFFF// Refin: false// Refout: false// Xorout: 0x00000000//*************************************************************************/// <summary>/// CRC32/MPEG-2/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Crc23(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;uint crc = 0xFFFFFFFF;// Initial valuefor (int i = start; i < length; i++){crc ^= (uint)(buffer[i] << 24);for (int j = 0; j < 8; j++){if ((crc & 0x80000000) > 0)crc = (crc << 1) ^ 0x04C11DB7;elsecrc = crc << 1;}}byte[] ret = BitConverter.GetBytes(crc);Array.Reverse(ret);return ret;}//***************************************************************/* 纵向冗余校验(Longitudinal Redundancy Check,简称:LRC)是通信中常用的一种校验形式,也称LRC校验或纵向校验。 它是一种从纵向通道上的特定比特串产生校验比特的错误检测方法。在行列格式中(如磁带),LRC经常是与VRC一起使用,这样就会为每个字符校验码。在工业领域Modbus协议Ascii模式采用该算法。 LRC计算校验码,具体算法如下:*/// 1、对需要校验的数据(2n个字符)两两组成一个16进制的数值求和。// 2、将求和结果与256求模。// 3、用256减去所得模值得到校验结果(另一种方法:将模值按位取反然后加1)。/// <summary>/// LRC校验(纵向冗余校验)工业领域Modbus协议Ascii模式采用该算法/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Lrc(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte lrc = 0;// Initial valuefor (int i = start; i < len; i++){lrc += buffer[i];}lrc = (byte)((lrc ^ 0xFF) + 1);return new byte[] { lrc };}/// <summary>/// BCC(Block Check Character/信息组校验码),因校验码是将所有数据异或得出,故俗称异或校验。具体算法是:将每一个字节的数据(一般是两个16进制的字符)进行异或后即得到校验码。/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] Bcc(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte bcc = 0;// Initial valuefor (int i = start; i < len; i++){bcc ^= buffer[i];}return new byte[] { bcc };}/// <summary>///  检验和(checksum),在数据处理和数据通信领域中,用于校验目的地一组数据项的和。它通常是以十六进制为数制表示的形式。如果校验和的数值超过十六进制的FF,也就是255. 就要求其补码作为校验和。通常用来在通信中,尤其是远距离通信中保证数据的完整性和准确性。/// </summary>/// <param name="buffer"></param>/// <param name="start"></param>/// <param name="len"></param>/// <returns></returns>public static byte[] allAdd(byte[] buffer, int start = 0, int len = 0){if (buffer == null || buffer.Length == 0) return null;if (start < 0) return null;if (len == 0) len = buffer.Length - start;int length = start + len;if (length > buffer.Length) return null;byte bcc = 0;// Initial valuefor (int i = start; i < len; i++){bcc ^= buffer[i];}return new byte[] { bcc };}}
    /// <summary>/// 操作功能码/// </summary>public enum FunctionCode{/// <summary>/// 读写从站输出线圈状态0XXXX状态/// </summary>ReadCoils = 1,/// <summary>/// 只读从站输入线圈状态1XXXX状态/// </summary>ReadDiscreteInputs = 2,/// <summary>/// 读写从站保存寄存器4XXXX状态/// </summary>ReadHoldingRegisters = 3,/// <summary>/// 只读从站输入寄存器3XXXX值/// </summary>ReadInputRegisters = 4,/// <summary>/// 强制从站单个线圈状态/// </summary>WriteSingleCoil = 5,/// <summary>/// 向从站单个寄存器写入值/// </summary>WriteSingleRegister = 6,/// <summary>/// 强制从站多个线圈状态/// </summary>WriteMultipleCoils = 15,/// <summary>/// 向从站多个寄存器写入值/// </summary>WriteMultipleRegisters = 16,ReportSlaveID = 17,MaskWriteRegister = 22,ReadWriteRegister = 23}/// <summary>/// 从站返回异常代码/// </summary>public enum ErrorCode{/// <summary>/// 非法功能/// </summary>IllegalFunction,/// <summary>/// 非法数据地址/// </summary>IllegalDataAddress,/// <summary>/// 非法数据/// </summary>IllegalData,/// <summary>/// 相关设备故障/// </summary>RelatedEquipmentFailure,/// <summary>/// 确认/// </summary>Verify,/// <summary>/// 忙碌、拒绝执行/// </summary>Busy,}

4,基于ModbusTCP通信。

4.1,ModbusTCP报文格式参考链接

https://www.cnblogs.com/Kirito-Asuna-Yoyi/p/ModbusTCP.html

注明:ModbusTCP没有校验码。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redisson框架

1. Redisson锁与Redis订阅与发布模式的联系&#xff1a; Redisson锁中&#xff0c;使用订阅发布模式去通知等待锁的客户端&#xff1a;锁已经释放&#xff0c;可以进行抢锁。 publish channel_name message&#xff1a;将消息发送到指定频道 解锁时&#xff0c;在Lua解锁脚本…

算法思想总结:优先级队列

一、最后一块石头的重量 . - 力扣&#xff08;LeetCode&#xff09; 我们每次都要快速找到前两个最大的石头进行抵消&#xff0c;这个时候用优先级队列&#xff08;建大堆&#xff09;,不断取堆顶元素是最好的&#xff01;每次删除堆顶元素后&#xff0c;可以自动调整&#xf…

[C++][CMake][CMake基础]详细讲解

目录 1.CMake简介2.大小写&#xff1f;3.注释1.注释行2.注释块 4.日志 1.CMake简介 CMake是一个项目构建工具&#xff0c;并且是跨平台的 问题 – 解决 如果自己动手写Makefile&#xff0c;会发现&#xff0c;Makefile通常依赖于当前的编译平台&#xff0c;而且编写Makefile的…

【动态规划】动态规划一

动态规划一 1.第 N 个泰波那契数2.面试题 08.01. 三步问题3.使用最小花费爬楼梯4.解码方法 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.…

分布式限流:Spring Cloud Gateway 限流

分布式限流&#xff1a;Spring Cloud Gateway 限流 在现代微服务架构中&#xff0c;流量控制是一个至关重要的部分。分布式限流作为一种有效的流量控制手段&#xff0c;能够帮助我们保护系统不被突发的流量冲垮。Spring Cloud Gateway支持多种限流方式。 什么是分布式限流 分…

模拟5亿年自然进化史,全新蛋白质大模型ESM3诞生!前Meta老将力作LeCun转赞

模拟5亿年自然进化史&#xff0c;全新蛋白质大模型ESM3诞生&#xff01;前Meta老将力作LeCun转赞。 能抗衡AlphaFold 3的生命科学大模型终于出现了。初创公司Evolutionary Scale AI发布了他们最新的98B参数蛋白质语言模型ESM3。不仅支持序列、结构、功能的all-to-all推理&#…

Unreal Engine@Jetson Orin Nano尚不支持

Unreal EngineJetson Orin Nano尚不支持 1. 源由2. Unreal Engine介绍3. 问题4. 编译方法5. 补充6. 其他 1. 源由 最近在看SC-Explorer方面的内容&#xff0c;在模拟方面采用了Unreal Engine。 本打算跑下模拟&#xff0c;因此打算在JetsonOrin的板子上试试看。 2. Unreal En…

opencv实现目标检测功能----20240704

早在 2017 年 8 月,OpenCV 3.3 正式发布,带来了高度改进的“深度神经网络”(dnn)模块。 该模块支持多种深度学习框架,包括 Caffe、TensorFlow 和 Torch/PyTorch。这次我们使用Opencv深度学习的功能实现目标检测的功能,模型选用MobileNetSSD_deploy.caffemodel。 模型加载…

mac视频压缩简单办法,mac如何把视频压缩到指定大小内存

在数字时代&#xff0c;视频已成为我们日常生活和工作的重要交流工具。然而&#xff0c;视频文件体积庞大&#xff0c;给存储和分享带来了不少困扰。本文将为你揭秘视频压缩的秘密&#xff0c;让你轻松减小视频文件体积&#xff0c;提升分享效率&#xff01; 方法一下载文件压缩…

Python爬虫教程第0篇-写在前面

为什么写这个系列 最近开发了个Python爬虫的脚本&#xff0c;去抢一个名额&#xff0c;结果是程序失败了&#xff0c;中间有各种原因&#xff0c;终究还是准备不足的问题。我想失败的经验或许也可贵&#xff0c;便总结一下当初从0开始学Python&#xff0c;一步步去写Python脚本…

【SpringCloud】Ribbon源码解析

ribbon是一个负载均衡组件&#xff0c;它可以将请求分散到多个服务提供者实例中&#xff0c;提高系统的性能和可用性。本章分析ribbon是如何实现负载均衡的 1、LoadBalanced 消费者在引入ribbon组件后&#xff0c;给http客户端添加LoadBalanced注解就可以启用负载均衡功能。Lo…

Github 2024-07-01开源项目月报 Top15

根据Github Trendings的统计,本月(2024-07-01统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目6JavaScript项目3C++项目2PHP项目1Blade项目1非开发语言项目1C#项目1Lua项目1Go项目1MDX项目1Jupyter Notebook项目1从零开始构建你喜…

Ubuntu 24.04-自动安装-Nvidia驱动

教程 但在安全启动模式下可能会报错。 先在Nvidia官网找到GPU对应的驱动版&#xff0c; 1. 在软件与更新中选择合适的驱动 2. ubuntu自动安装驱动 sudo ubuntu-drivers autoinstall显示驱动 ubuntu-drivers devices3. 安装你想要的驱动 sudo apt install nvidia-driver-ve…

99. 岛屿数量

题目描述&#xff1a;给定一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的矩阵&#xff0c;你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成&#xff0c;并且四周都是水域。你可以假设矩阵外均被水包围。 输入描述&#xff1a…

@amap/amap-jsapi-loader实现高德地图嵌入React项目中,并且做到点击地图任意一处,获得它的经纬度

1.第一步要加入项目package.json中或者直接yarn install它都可以 想必大家应该都会 "amap/amap-jsapi-loader": "0.0.7"2.加入项目中 关于接口获取key的接口 大家改成自己对应的项目请求方法 import React, { PureComponent } from react; import { Input…

GEE计算遥感生态指数RSEI

目录 RESI湿度绿度热度干度源代码归一化函数代码解释整体的代码功能解释:导出RSEI计算结果参考文献RESI RSEI = f (Greenness,Wetness,Heat,Dryness)其遥感定义为: RSEI = f (VI,Wet,LST,SI)式中:Greenness 为绿度;Wetness 为湿度;Thermal为热度;Dryness 为干度;VI 为植被指数…

【CT】LeetCode手撕—4. 寻找两个正序数组的中位数

目录 题目1- 思路2- 实现⭐4. 寻找两个正序数组的中位数——题解思路 3- ACM 实现 题目 原题连接&#xff1a;4. 寻找两个正序数组的中位数 1- 思路 思路 将寻找中位数 ——> 寻找两个合并数组的第 K 大 &#xff08;K代表中位数&#xff09; 实现 ① 遍历两个数组 &am…

如何利用AI撰写短文案获客?分享6大平台和3大步骤!

从去年开始&#xff0c;很多大厂都在裁员&#xff0c;原因就是因为AI的火爆&#xff0c;替代了很多机械式的劳动力。以前很多人可以通过机械式的工作来摸鱼&#xff0c;现在AI完成的效率比人工的要高很多倍。 国内好用的AI平台非常多&#xff0c;有时候也可以使用几个AI平台结合…

如何提高内容生产效率

在当前数字化和信息爆炸的时代&#xff0c;内容生产的需求不断增加&#xff0c;如何提高内容生产效率成为企业面临的重要课题。本文将介绍一种有效的内容生产协同工具——【可瓜】&#xff0c;并详细探讨其在提高内容生产效率方面的优势和实际应用。 内容生产任务进度追踪 传统…

【BUUCTF-PWN】7-[第五空间2019 决赛]PWN5

参考&#xff1a;BUU pwn [第五空间2019 决赛]PWN5 //格式化字符串漏洞 - Nemuzuki - 博客园 (cnblogs.com) 格式化字符串漏洞原理详解_printf 任意内存读取-CSDN博客 32位小端排序&#xff0c;有栈溢出保护 运行效果&#xff1a; 查看main函数 存在格式化字符串漏洞 输…