MODBUS协议相关代码(CRC验证 客户端程序)
Posted SunShine。smile
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MODBUS协议相关代码(CRC验证 客户端程序)相关的知识,希望对你有一定的参考价值。
Modbus协议是一种已广泛应用于当今工业控制领域的通用通讯协议。通过此协议,控制器相互之间、或控制器经由网络(如以太网)可以和其它设备之间进行通信。Modbus协议使用的是主从通讯技术,即由主设备主动查询和操作从设备。一般将主控设备方所使用的协议称为Modbus Master,从设备方使用的协议称为Modbus Slave。典型的主设备包括工控机和工业控制器等;典型的从设备如PLC可编程控制器等。Modbus通讯物理接口可以选用串口(包括RS232和RS485),也可以选择以太网口。
1、十六进制字符串的CRC验证
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace modbustest 7 { 8 public static class ByteHexHelper 9 { 10 private static char[] _buffedChars = null; 11 private static byte[] _buffedBytes = null; 12 13 static ByteHexHelper() 14 { 15 _buffedChars = new char[(byte.MaxValue - byte.MinValue + 1) * 2]; 16 int idx = 0; 17 byte b = byte.MinValue; 18 while (true) 19 { 20 string hexs = b.ToString("X2"); 21 _buffedChars[idx++] = hexs[0]; 22 _buffedChars[idx++] = hexs[1]; 23 24 if (b == byte.MaxValue) break; 25 ++b; 26 } 27 28 _buffedBytes = new byte[0x67]; 29 idx = _buffedBytes.Length; 30 while (--idx >= 0) 31 { 32 if ((0x30 <= idx) && (idx <= 0x39)) 33 { 34 _buffedBytes[idx] = (byte)(idx - 0x30); 35 } 36 else 37 { 38 if ((0x61 <= idx) && (idx <= 0x66)) 39 { 40 _buffedBytes[idx] = (byte)((idx - 0x61) + 10); 41 continue; 42 } 43 if ((0x41 <= idx) && (idx <= 70)) 44 { 45 _buffedBytes[idx] = (byte)((idx - 0x41) + 10); 46 } 47 } 48 } 49 } 50 51 /// <summary> 52 /// 字节数组转16进制字符串 53 /// </summary> 54 /// <param name="bytes"></param> 55 /// <returns></returns> 56 public static string ByteToHex(byte[] bytes) 57 { 58 if (bytes == null) 59 { 60 return null; 61 } 62 63 char[] result = new char[bytes.Length * 2]; 64 for (int i = 0; i < bytes.Length; ++i) 65 { 66 int startIndex = (bytes[i] - byte.MinValue) * 2; 67 result[i * 2] = _buffedChars[startIndex]; 68 result[i * 2 + 1] = _buffedChars[startIndex + 1]; 69 } 70 71 return new string(result); 72 } 73 74 /// <summary> 75 /// 16进制字符串转字节数组 76 /// </summary> 77 /// <param name="str"></param> 78 /// <returns></returns> 79 public static byte[] HexToByte(string str) 80 { 81 str = str.Replace(" ",""); 82 if (str == null || (str.Length & 1) == 1) 83 { 84 return null; 85 } 86 87 byte[] result = new byte[str.Length / 2]; 88 int charIndex = 0; 89 int byteIndex = 0; 90 int length = result.Length; 91 while (--length >= 0) 92 { 93 int first = 0; 94 int second = 0; 95 try 96 { 97 first = _buffedBytes[str[charIndex++]]; 98 second = _buffedBytes[str[charIndex++]]; 99 } 100 catch 101 { 102 return null; 103 } 104 result[byteIndex++] = (byte)((first << 4) + second); 105 } 106 return result; 107 } 108 } 109 }
2、字符串转十六进制|十六进制转字符串
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 namespace modbustest 6 { 7 class CRC 8 { 9 10 public static string CRCCheck(string val) 11 { 12 val = val.TrimEnd(‘ ‘); 13 string[] spva = val.Split(‘ ‘); 14 byte[] bufData = new byte[spva.Length + 2]; 15 bufData = ToBytesCRC(val); 16 ushort CRC = 0xffff; 17 ushort POLYNOMIAL = 0xa001; 18 for (int i = 0; i < bufData.Length - 2; i++) 19 { 20 CRC ^= bufData[i]; 21 for (int j = 0; j < 8; j++) 22 { 23 if ((CRC & 0x0001) != 0) 24 { 25 CRC >>= 1; 26 CRC ^= POLYNOMIAL; 27 } 28 else 29 { 30 CRC >>= 1; 31 } 32 } 33 } 34 35 return ToHex(System.BitConverter.GetBytes(CRC)); 36 } 37 /// <summary> 38 /// 例如把如下字符串转换成字节数组 39 /// AA AA AA AA 0A 00 68 00 06 03 04 54 21 28 22 E5 F3 16 BB BB BB BB 转换为字节数组 40 /// </summary> 41 /// <param name="hex">十六进制字符串</param> 42 /// <returns></returns> 43 public static byte[] ToBytesCRC(string hex) 44 { 45 string[] temp = hex.Split(‘ ‘); 46 byte[] b = new byte[temp.Length + 2]; 47 48 for (int i = 0; i < temp.Length; i++) 49 { 50 b[i] = Convert.ToByte(temp[i], 16); 51 } 52 53 return b; 54 } 55 /// <summary> 56 /// 将字节数据转换为十六进制字符串,中间用 “ ”分割 如:AA AA AA AA 0A 00 68 00 06 03 04 54 21 28 22 E5 F3 16 BB BB BB BB 57 /// </summary> 58 /// <param name="vars">要转换的字节数组</param> 59 /// <returns></returns> 60 public static String ToHex(byte[] vars) 61 { 62 return BitConverter.ToString(vars).Replace(‘-‘, ‘ ‘).Trim(); 63 } 64 } 65 }
3、窗体主程序
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.IO.Ports; 10 using System.Text.RegularExpressions; 11 12 namespace modbustest 13 { 14 public partial class Form1 : Form 15 { 16 17 SerialPort sp = null;//申明一个串口类 18 bool isOpen = false;//打开串口标识位 19 bool isSetProperty = false;//属性设置标志位 20 bool isHex = false;//十六进制显示标志位 21 22 public Form1() 23 { 24 InitializeComponent(); 25 } 26 //窗体初始化 27 private void Form1_Load(object sender, EventArgs e) 28 { 29 this.MaximumSize = this.Size; 30 this.MinimumSize = this.Size; 31 this.MaximizeBox = false; 32 //最大支持10个串口 33 for (int i = 0; i < 10; i++) 34 { 35 protBox.Items.Add("COM" + (i + 1).ToString()); 36 } 37 protBox.SelectedIndex = 0; 38 39 //初始化波特率 40 BaudRate.Items.Add("1200"); 41 BaudRate.Items.Add("2400"); 42 BaudRate.Items.Add("4800"); 43 BaudRate.Items.Add("9600"); 44 BaudRate.Items.Add("19200"); 45 BaudRate.Items.Add("38400"); 46 BaudRate.Items.Add("43000"); 47 BaudRate.Items.Add("56000"); 48 BaudRate.Items.Add("57600"); 49 BaudRate.Items.Add("115200"); 50 51 BaudRate.SelectedIndex = 5; 52 53 //初始化停止位 54 55 stopBits.Items.Add("0"); 56 stopBits.Items.Add("1"); 57 stopBits.Items.Add("1.5"); 58 stopBits.Items.Add("2"); 59 stopBits.SelectedIndex = 1; 60 61 //初始化数据位 62 dataBits.Items.Add("8"); 63 dataBits.Items.Add("7"); 64 dataBits.Items.Add("6"); 65 dataBits.Items.Add("5"); 66 dataBits.SelectedIndex = 0; 67 68 //初始化奇偶校验位 69 parity.Items.Add("无"); 70 parity.Items.Add("Odd"); 71 parity.Items.Add("Even"); 72 parity.SelectedIndex = 2; 73 74 //默认显示hex 75 rbnHex.Checked = true; 76 } 77 78 //检查哪些串口可用 79 private void checkCOM_Click(object sender, EventArgs e) 80 { 81 bool comExistence = false;//有可用的串口标志位 82 protBox.Items.Clear();//清除当前串口号中的所有串口名称 83 for (int i = 0; i < 10;i++ ) 84 { 85 try{ 86 87 SerialPort sp = new SerialPort("COM"+(i+1).ToString()); 88 sp.Open(); 89 sp.Close(); 90 protBox.Items.Add("COM" + (i + 1).ToString()); 91 comExistence = true; 92 93 } 94 catch { 95 96 continue; 97 } 98 } 99 if (comExistence) 100 { 101 102 protBox.SelectedIndex = 0; 103 } 104 else { 105 106 MessageBox.Show("没有找到可用串口","错误提示"); 107 } 108 109 } 110 //检查串口是否设置 111 private bool checkPortSetting() 112 { 113 if (protBox.Text.Trim() == "") return false; 114 115 if (BaudRate.Text.Trim() == "") return false; 116 if (dataBits.Text.Trim() == "") return false; 117 if (parity.Text.Trim() == "") return false; 118 if (stopBits.Text.Trim() == "") return false; 119 120 return true; 121 122 } 123 124 private bool checkSendData() 125 { 126 if (tbxSend.Text.Trim() == "") return false; 127 return true; 128 129 } 130 131 private void SetPortProperty()//设置串口的属性 132 { 133 sp=new SerialPort(); 134 sp.PortName=protBox.Text.Trim();//设置串口名 135 sp.BaudRate=Convert.ToInt32(BaudRate.Text.Trim());//设置串口的波特率 136 float f=Convert.ToSingle(stopBits.Text.Trim());//设置停止位 137 if(f==0){ 138 139 sp.StopBits=StopBits.None; 140 } 141 else if(f==1.5){ 142 143 sp.StopBits=StopBits.OnePointFive; 144 } 145 else if(f==1){ 146 147 sp.StopBits=StopBits.One; 148 } 149 else if(f==2){ 150 151 sp.StopBits=StopBits.Two; 152 } 153 else{ 154 sp.StopBits=StopBits.One; 155 } 156 sp.DataBits=Convert.ToInt16(dataBits.Text.Trim());//设置数据位 157 string s=parity.Text.Trim();//设置奇偶校验位 158 if(s.CompareTo("None")==0){ 159 sp.Parity=Parity.None; 160 } 161 else if(s.CompareTo("Odd")==0){ 162 163 sp.Parity=Parity.Odd; 164 } 165 else if(s.CompareTo("Even")==0){ 166 167 sp.Parity=Parity.Even; 168 } 169 else{ 170 171 sp.Parity=Parity.None; 172 } 173 sp.ReadTimeout=-1;//设置超时读取时间 174 sp.RtsEnable=true; 175 //定义DataReceived事件,当串口收到数据后触发事件 176 sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived); 177 if(rbnHex.Checked){ 178 isHex=true; 179 }else{ 180 isHex=false; 181 } 182 } 183 private void btnSend_Click(object sender,EventArgs e)//发送串口数据 184 { 185 if(isOpen)//写串口数据 186 { 187 try{ 188 189 sp.WriteLine(tbxSend.Text); 190 } 191 catch(Exception){ 192 193 MessageBox.Show("发送数据时发生错误!","错误提示"); 194 return; 195 } 196 }else{ 197 MessageBox.Show("串口未打开!","错误提示"); 198 return; 199 } 200 if(!checkSendData())//检测要发送的数据 201 { 202 MessageBox.Show("请输入要发送的数据!","错误提示"); 203 return; 204 } 205 } 206 207 //打开串口 208 private void openPort_Click(object sender, EventArgs e) 209 { 210 if(isOpen==false){ 211 if(!checkPortSetting())//检测串口设置 212 { 213 MessageBox.Show("串口未设置!","错误提示"); 214 return; 215 } 216 if(!isSetProperty)//串口未设置则设置串口 217 { 218 SetPortProperty(); 219 isSetProperty=true; 220 } 221 try//打开串口 222 { 223 //sp = new SerialPort("COM1",19200,Parity.Even,8,StopBits.One); 224 sp.Open(); 225 isOpen=true; 226 openPort.Text="关闭串口";//串口打开后则相关的串口设置按钮便不可再用 227 228 protBox.Enabled=false; 229 BaudRate.Enabled=false; 230 dataBits.Enabled=false; 231 parity.Enabled=false; 232 stopBits.Enabled=false; 233 rbnChar.Enabled=false; 234 rbnHex.Enabled=false; 235 } 236 catch(Exception){ 237 //打开串口失败后,相应标志位取消 238 isSetProperty=false; 239 isOpen=false; 240 MessageBox.Show("串口无效或已被占用!","错误提示"); 241 } 242 }else{ 243 try//
以上是关于MODBUS协议相关代码(CRC验证 客户端程序)的主要内容,如果未能解决你的问题,请参考以下文章