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没有校验码。