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 }
View Code

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 }
View Code

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//打开串口
244                 {
245                     sp.Close();
246                     isOpen=false;
247                     isSetProperty=false;
248                     openPort.Text="打开串口";//关闭串口后,串口设置选项便可以继续使用
249                     protBox.Enabled=true;
250                     BaudRate.Enabled=true;
251                     dataBits.Enabled=true;
252                     parity.Enabled=true;
253                     stopBits.Enabled=true;
254                     rbnChar.Enabled=true;
255                     rbnHex.Enabled=true;
256                 }
257                 catch(Exception){
258                     lblStatus.Text="关闭串口时发生错误";
259                 }
260             }
261 }
262         private void sp_DataReceived(object sender,SerialDataReceivedEventArgs e)
263         {
264             System.Threading.Thread.Sleep(100);//延时100ms等待接收完数据//this.Invoke就是跨线程访问ui的方法,也是本文的范例
265             this.Invoke((EventHandler)(delegate{
266 
267                 if(isHex==false){
268 
269                     tbxRec.Text+=sp.ReadLine();
270                 }else{
271 
272                     Byte[]ReceivedData=new Byte[sp.BytesToRead+1];//创建接收字节数组
273                     sp.Read(ReceivedData,0,ReceivedData.Length);//读取所接收到的数据
274                     String RecvDataText=null;
275 
276                     for( int i=0;i<ReceivedData.Length-1; i++){
277 
278                         RecvDataText+=(ReceivedData[i].ToString("X2")+" ");
279                     }
280                          tbxRec.Text+=RecvDataText;
281                 }
282                  sp.DiscardInBuffer();//丢弃接收缓冲区数据
283             }));
284             }
285 
286 
287        
288             private void button1_Click_1(object sender, EventArgs e)
289             {
290                 
291                
292                 if (isOpen)//写串口数据
293                 {
294                     try
295                     {
296                         byte[] send_data;
297                         string str = this.tbxSend.Text;
298                         string strc = str + " " + CRC.CRCCheck(this.tbxSend.Text);
299                         //int iLen = 0;
300                         send_data = ByteHexHelper.HexToByte(strc);
301 
302                        // send_data = HexStringToByteArray(strc);
303                         //iLen = send_data.GetLength(0);
304                         //this.tbxRec = byteToHexStr(send_data);
305                        // byte[] acc_data = new byte[] { 0x30, 0x31 };
306                         //acc_data = send_data;
307                         //String acc_str = System.Text.Encoding.Default.GetString(acc_data);
308                         ////ModBus comm = new ModBus();
309                         this.tbxRec.Text = str + " "+ CRC.CRCCheck(str);
310                         //String rec = str + " " + CRC.CRCCheck(acc_str);
311                         //sp.WriteLine(strs);
312                         sp.Write(send_data,0,send_data.Length);
313                         
314                         //this.tbxRec.Text = sp.BaudRate + " " + sp.BytesToWrite + "kong";
315                         
316                         
317                     }
318                     catch (Exception)
319                     {
320 
321                         MessageBox.Show("发送数据时发生错误!", "错误提示");
322                         return;
323                     }
324                 }
325                 else
326                 {
327                     MessageBox.Show("串口未打开!", "错误提示");
328                     return;
329                 }
330                 if (!checkSendData())//检测要发送的数据
331                 {
332                     MessageBox.Show("请输入要发送的数据!", "错误提示");
333                     return;
334                 }
335             }
336 
337             private void button2_Click(object sender, EventArgs e)
338             {
339                 byte[] acc_data;
340                 String str2 = this.tbxRec.Text;
341                 int iLen = 0;
342                 acc_data = System.Text.Encoding.ASCII.GetBytes(str2);
343                 iLen = acc_data.GetLength(0);
344 
345                 byte[] send_data = new byte[] { 0x30, 0x31 };
346                 send_data = acc_data;
347                 String acc_str = System.Text.Encoding.ASCII.GetString(acc_data);
348                 //ModBus comm = new ModBus();
349 
350                 this.tbxSend.Text = str2 + " " + CRC.CRCCheck(acc_str);
351             }
352 
353             private void button3_Click(object sender, EventArgs e)
354             {
355                 tbxRec.Text = "";
356                 tbxSend.Text = "";
357                
358             }
359 
360             
361 
362        
363 
364     }
365 }
View Code

4、窗体

posted on 2016-06-06 19:21  随笔一书,心存一世  阅读(1961)  评论(0编辑  收藏  举报

导航