基于Modbus的C#串口调试开发

说明:本文主要研究的是使用C# WinForm开发的串口调试软件(其中包含Modbus协议相关操作)。Modbus相关协议可以查阅百度文库等,可参考: 《http://wenku.baidu.com/link?url=J-QZeQVLfvfZh7_lh8Qf0MdwANZuVjEoTqox6zJYrSnKyfgES2RTb_bjC5ZTn8-xgsuUAyiELRYVA3-3FBkBGywWhQ9YGoavJOzwB0IxTyK 》。

  (1)先测试串口设置,发送和接收数据。

  (2)发送modbus的命令帧数据和使用DataReceived接收缓冲区的数据。

一、简单的串口调试工具

  下图为串口调试工具的界面,主要包括串口基本设置,功能操作,状态框以及发送接收框。由于这里只是简单的初始化数据,所以当需要发送数据的时候需要点击“串口检测”,来测试当前可用的串口,然后输入需要发送的数据,最后点击“发送数据”(由于测试需要,让发送什么数据就返回什么数据,这里的底层硬件做了短接处理,使用短接貌P30-P31,具体操作可以自行百度)

  

  

  1.1 发送数据操作

    (1)点击 串口检测

    (2)输入发送数据

    (3)点击 发送数据

    下面开始时具体代码:

    #1 软件打开时候初始化操作:Form1_Load(),主要初始化操作串口设置的下拉列表。

 1 private void Form1_Load(object sender, EventArgs e)
 2         {
 3
 4             //设置窗口大小固定
 5             this.MaximumSize = this.Size;
 6             this.MinimumSize = this.Size;
 7
 8             //1、设置串口下拉列表
 9             for ( int i = 0; i < 10; i++ )
10             {
11                 cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
12             }
13             cbxCOMPort.SelectedIndex = 2;//默认选项
14
15
16             //2、设置常用波特率
17             int bt = 300;
18             for (int i = 0; i < 8; i++)
19             {
20                 cbxBaudRate.Items.Add(bt.ToString());
21                 bt *= 2;
22             }
23
24             cbxBaudRate.Items.Add("38400");
25             cbxBaudRate.Items.Add("43000");
26             cbxBaudRate.Items.Add("56000");
27             cbxBaudRate.Items.Add("57600");
28             cbxBaudRate.Items.Add("115200");
29             cbxBaudRate.SelectedIndex = 5;
30
31
32             //3、列出停止位
33             cbxStopBits.Items.Add("0");
34             cbxStopBits.Items.Add("1");
35             cbxStopBits.Items.Add("1.5");
36             cbxStopBits.Items.Add("2");
37             cbxStopBits.SelectedIndex = 1;
38
39             //4、设置奇偶检验
40             cbxParity.Items.Add("无");
41             cbxParity.Items.Add("奇校验");
42             cbxParity.Items.Add("偶校验");
43             cbxParity.SelectedIndex = 0;
44
45             //5、设置数据位
46             cbxDataBits.Items.Add("8");
47             cbxDataBits.Items.Add("7");
48             cbxDataBits.Items.Add("6");
49             cbxDataBits.Items.Add("5");
50             cbxDataBits.SelectedIndex = 0;
51
52
53         }

private void Form1_Load(object sender, EventArgs e)

    

    #2 检查串口基本设置的参数:CheckPortSetting(),

 1 /// <summary>
 2         /// 【检测端口设置】
 3         /// </summary>
 4         /// <returns></returns>
 5         private bool CheckPortSetting()
 6         {
 7             //检测端口设置
 8             if (cbxCOMPort.Text.Trim() == "" || cbxBaudRate.Text.Trim() == "" || cbxStopBits.Text.Trim() == "" || cbxParity.Text.Trim() == "" || cbxDataBits.Text.Trim() == "")
 9                 return false;
10             return true;
11         }

private bool CheckPortSetting()

    #3 设置串口属性,创建SerialPort对象  

 1 /// <summary>
 2         /// 【设置串口属性】
 3         /// </summary>
 4         private void SetPortProperty()
 5         {
 6             //1、设置串口的属性
 7             sp = new SerialPort();
 8
 9             sp.ReceivedBytesThreshold = 1;//获取DataReceived事件发生前内部缓存区字节数
10             sp.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);//设置委托
11
12             sp.PortName = cbxCOMPort.Text.Trim();
13
14             //2、设置波特率
15             sp.BaudRate =  Convert.ToInt32( cbxBaudRate.Text.Trim());
16
17             //3、设置停止位
18             float f = Convert.ToSingle( cbxStopBits.Text.Trim());
19
20             if (f == 0)
21             {
22                 sp.StopBits = StopBits.None;//表示不使用停止位
23             }
24             else if (f == 1.5)
25             {
26                 sp.StopBits = StopBits.OnePointFive;//使用1.5个停止位
27             }
28             else if (f == 2)
29             {
30                 sp.StopBits = StopBits.Two;//表示使用两个停止位
31             }
32             else
33             {
34                 sp.StopBits = StopBits.One;//默认使用一个停止位
35             }
36
37             //4、设置数据位
38             sp.DataBits = Convert.ToInt16(cbxDataBits.Text.Trim());
39
40             //5、设置奇偶校验位
41             string s = cbxParity.Text.Trim();
42             if (s.CompareTo("无") == 0)
43             {
44                 sp.Parity = Parity.None;//不发生奇偶校验检查
45             }
46             else if (s.CompareTo("奇校验") == 0)
47             {
48                 sp.Parity = Parity.Odd;//设置奇校验
49             }
50             else if (s.CompareTo("偶校验") == 0)
51             {
52                 sp.Parity = Parity.Even;//设置偶检验
53             }
54             else
55             {
56                 sp.Parity = Parity.None;
57             }
58
59             //6、设置超时读取时间
60             sp.ReadTimeout = -1;
61
62             //7、打开串口
63             try
64             {
65                 sp.Open();
66                 isOpen = true;
67             }
68             catch(Exception)
69             {
70                 lblStatus.Text = "打开串口错误!";
71             }
72
73         }

private void SetPortProperty()

    

    #4 “发送数据”按钮点击事件:btnSend_Click(), 在发送数据需要进行,#2,#3验证,然后开始通过串口对象写入数据

 1     /// <summary>
 2         /// 【发送数据】
 3         /// </summary>
 4         /// <param name="sender"></param>
 5         /// <param name="e"></param>
 6         private void btnSend_Click(object sender, EventArgs e)
 7         {
 8             //发送串口数据
 9
10             //1、检查串口设置
11             if (!CheckPortSetting())
12             {
13                 MessageBox.Show("串口未设置!", "错误提示");
14                 return;
15             }
16
17             //2、检查发送数据是否为空
18             if(tbxSendData.Text.Trim() == ""){
19                  MessageBox.Show("发送数据不能为空");
20                  return;
21             }
22
23             //3、设置
24             if (!isSetProperty)
25             {
26                 SetPortProperty();
27                 isSetProperty = true;
28             }
29
30             //4、写串口数据
31             if (isOpen)
32             {
33                 //写出口数据
34                 try
35                 {
36                     sp.Write(tbxSendData.Text);
37                     tbxStatus.Text = "发送成功!";
38
39
40                     tbxRecvData.Text += sp.ReadLine();//读取发送的数据
41
42                 }
43                 catch
44                 {
45                     tbxStatus.Text = "发送数据错误";
46                 }
47             }
48             else
49             {
50                 MessageBox.Show("串口未打开", "错误提示");
51             }
52
53
54         }                        

  1.2 接受数据操作

    接收数据和发送数据有点类似   

 1 /// <summary>
 2         /// 【读取数据】
 3         /// </summary>
 4         /// <param name="sender"></param>
 5         /// <param name="e"></param>
 6         private void btnRecv_Click(object sender, EventArgs e)
 7         {
 8             if(isOpen) 
 9             { 
10                 try 
11                 {
12                     //读取串口数据
13  
14                     tbxRecvData.Text += sp.ReadLine()+"\r\n"; 
15                 } 
16                 catch(Exception) 
17                 { 
18                     lblStatus.Text = "读取串口时发生错误!"; 
19                     return; 
20                 } 
21             } 
22             else 
23             { 
24               MessageBox.Show("串口未打开!", "错误提示"); 
25               return; 
26
27             } 
28         }

  最后附上该窗体的后台代码:Form1.cs 

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.IO.Ports;
  7 using System.Linq;
  8 using System.Text;
  9 using System.Threading.Tasks;
 10 using System.Windows.Forms;
 11
 12
 13 namespace 串口调试
 14 {
 15     public partial class Form1 : Form
 16     {
 17         SerialPort sp = null;
 18
 19         bool isOpen = false;//是否打开
 20
 21         bool isSetProperty = false;//是否通过串口设置
 22
 23
 24         public Form1()
 25         {
 26             InitializeComponent();
 27         }
 28
 29         private void Form1_Load(object sender, EventArgs e)
 30         {
 31
 32             //设置窗口大小固定
 33             this.MaximumSize = this.Size;
 34             this.MinimumSize = this.Size;
 35
 36             //1、设置串口下拉列表
 37             for ( int i = 0; i < 10; i++ )
 38             {
 39                 cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
 40             }
 41             cbxCOMPort.SelectedIndex = 2;//默认选项
 42
 43
 44             //2、设置常用波特率
 45             int bt = 300;
 46             for (int i = 0; i < 8; i++)
 47             {
 48                 cbxBaudRate.Items.Add(bt.ToString());
 49                 bt *= 2;
 50             }
 51
 52             cbxBaudRate.Items.Add("38400");
 53             cbxBaudRate.Items.Add("43000");
 54             cbxBaudRate.Items.Add("56000");
 55             cbxBaudRate.Items.Add("57600");
 56             cbxBaudRate.Items.Add("115200");
 57             cbxBaudRate.SelectedIndex = 5;
 58
 59
 60             //3、列出停止位
 61             cbxStopBits.Items.Add("0");
 62             cbxStopBits.Items.Add("1");
 63             cbxStopBits.Items.Add("1.5");
 64             cbxStopBits.Items.Add("2");
 65             cbxStopBits.SelectedIndex = 1;
 66
 67             //4、设置奇偶检验
 68             cbxParity.Items.Add("无");
 69             cbxParity.Items.Add("奇校验");
 70             cbxParity.Items.Add("偶校验");
 71             cbxParity.SelectedIndex = 0;
 72
 73             //5、设置数据位
 74             cbxDataBits.Items.Add("8");
 75             cbxDataBits.Items.Add("7");
 76             cbxDataBits.Items.Add("6");
 77             cbxDataBits.Items.Add("5");
 78             cbxDataBits.SelectedIndex = 0;
 79
 80
 81         }
 82
 83
 84         /// <summary>
 85         /// 【串口检测按钮】
 86         /// </summary>
 87         /// <param name="sender"></param>
 88         /// <param name="e"></param>
 89         private void btnCheckCOM_Click(object sender, EventArgs e)
 90         {
 91             //1、检测哪些端口可用
 92             cbxCOMPort.Items.Clear();
 93             cbxCOMPort.Text = "";
 94
 95             lblStatus.Text = "执行中...";
 96             string str = "";
 97             for (int i = 0; i < 10; i++)
 98             {
 99                 try
100                 {
101             ////把所有可能的串口都测试一遍,打开关闭操作,只有可用的串口才可会放到下拉列表中
102                     SerialPort sp = new SerialPort("COM" + (i + 1).ToString());
103                     sp.Open();
104                     sp.Close();
105                     cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
106                 }
107                 catch
108                 {
109                     str += "COM" + (i + 1).ToString() + "、";
110                     continue;
111                 }
112             }
113
114         //如果当前下拉列表有可用的串口,则默认选择第一个
115             if(cbxCOMPort.Items.Count > 0)
116                 cbxCOMPort.SelectedIndex = 0;
117             lblStatus.Text = "完成";
118             tbxStatus.Text = str;
119         }
120
121
122         /// <summary>
123         /// 【检测端口设置】
124         /// </summary>
125         /// <returns></returns>
126         private bool CheckPortSetting()
127         {
128             //检测端口设置
129             if (cbxCOMPort.Text.Trim() == "" || cbxBaudRate.Text.Trim() == "" || cbxStopBits.Text.Trim() == "" || cbxParity.Text.Trim() == "" || cbxDataBits.Text.Trim() == "")
130                 return false;
131             return true;
132         }
133
134         /// <summary>
135         /// 【检测发送数据是否为空】
136         /// </summary>
137         /// <returns></returns>
138         private bool CheckSendData()
139         {
140             if (tbxSendData.Text.Trim() == "")
141                 return false;
142             return true;
143         }
144
145
146         /// <summary>
147         /// 【设置串口属性】
148         /// </summary>
149         private void SetPortProperty()
150         {
151             //1、设置串口的属性
152             sp = new SerialPort();
153
154             sp.PortName = cbxCOMPort.Text.Trim();
155
156             //2、设置波特率
157             sp.BaudRate =  Convert.ToInt32( cbxBaudRate.Text.Trim());
158
159             //3、设置停止位
160             float f = Convert.ToSingle( cbxStopBits.Text.Trim());
161
162             if (f == 0)
163             {
164                 sp.StopBits = StopBits.None;//表示不使用停止位
165             }
166             else if (f == 1.5)
167             {
168                 sp.StopBits = StopBits.OnePointFive;//使用1.5个停止位
169             }
170             else if (f == 2)
171             {
172                 sp.StopBits = StopBits.Two;//表示使用两个停止位
173             }
174             else
175             {
176                 sp.StopBits = StopBits.One;//默认使用一个停止位
177             }
178
179             //4、设置数据位
180             sp.DataBits = Convert.ToInt16(cbxDataBits.Text.Trim());
181
182             //5、设置奇偶校验位
183             string s = cbxParity.Text.Trim();
184             if (s.CompareTo("无") == 0)
185             {
186                 sp.Parity = Parity.None;//不发生奇偶校验检查
187             }
188             else if (s.CompareTo("奇校验") == 0)
189             {
190                 sp.Parity = Parity.Odd;//设置奇校验
191             }
192             else if (s.CompareTo("偶校验") == 0)
193             {
194                 sp.Parity = Parity.Even;//设置偶检验
195             }
196             else
197             {
198                 sp.Parity = Parity.None;
199             }
200
201             //6、设置超时读取时间
202             sp.ReadTimeout = -1;
203
204             //7、打开串口
205             try
206             {
207                 sp.Open();
208                 isOpen = true;
209             }
210             catch(Exception)
211             {
212                 lblStatus.Text = "打开串口错误!";
213             }
214
215         }
216
217
218
219         /// <summary>
220         /// 【发送数据】
221         /// </summary>
222         /// <param name="sender"></param>
223         /// <param name="e"></param>
224         private void btnSend_Click(object sender, EventArgs e)
225         {
226             //发送串口数据
227
228             //1、检查串口设置
229             if (!CheckPortSetting())
230             {
231                 MessageBox.Show("串口未设置!", "错误提示");
232                 return;
233             }
234
235             2、检查发送数据是否为空
236             if (!CheckSendData())
237             {
238                 MessageBox.Show("请输入要发送的数据!", "错误提示");
239                 return;
240             }
241
242             //3、设置
243             if (!isSetProperty)
244             {
245                 SetPortProperty();
246                 isSetProperty = true;
247             }
248
249             //4、写串口数据
250             if (isOpen)
251             {
252                 //写出口数据
253                 try
254                 {
255                     sp.Write(tbxSendData.Text);
256                     tbxStatus.Text = "发送成功!";
257
258                     tbxRecvData.Text += sp.ReadLine()+"\r\n";
259                 }
260                 catch
261                 {
262                     tbxStatus.Text = "发送数据错误";
263                 }
264             }
265             else
266             {
267                 MessageBox.Show("串口未打开", "错误提示");
268             }
269
270
271         }
272
273         /// <summary>
274         /// 【读取数据】
275         /// </summary>
276         /// <param name="sender"></param>
277         /// <param name="e"></param>
278         private void btnRecv_Click(object sender, EventArgs e)
279         {
280             if(isOpen) 
281             { 
282                 try 
283                 {
284                     //读取串口数据
285  
286                     tbxRecvData.Text += sp.ReadLine()+"\r\n"; 
287                 } 
288                 catch(Exception) 
289                 { 
290                     lblStatus.Text = "读取串口时发生错误!"; 
291                     return; 
292                 } 
293             } 
294             else 
295             { 
296               MessageBox.Show("串口未打开!", "错误提示"); 
297               return; 
298
299             } 
300         }
301
302     }
303 }

Form1.cs

二、基于Modbus协议的数据发送和接收

  这里主要是在前面的基础上,把发送和接收的数据进行格式化(符合Modbus的数据帧格式),如下左图所示,右图为加入Modbus协议的窗体,主要添加了命令帧的输入框组:

  

  

  2.1 获取字节的的高位和低位:WORD_LO()、WORD_HI()   

 1 /// <summary>
 2         /// 【获取低位字节】
 3         /// </summary>
 4         /// <param name="crcCLo"></param>
 5         /// <returns></returns>
 6         public static byte WORD_LO(ushort crcCLo)
 7         {
 8             crcCLo = (ushort)(crcCLo & 0X00FF);
 9             return (byte)crcCLo;
10         }
11
12         /// <summary>
13         /// 【获取高位字节】
14         /// </summary>
15         /// <param name="crcHI"></param>
16         /// <returns></returns>
17         public static byte WORD_HI(ushort crcHI)
18         {
19             crcHI = (ushort)(crcHI >> 8 & 0X00FF);
20             return (byte)crcHI;
21         }

WORD_LO() WORD_HI()

  

  2.2 CRC高位表和低位表 

 1  #region CRC高位表 byte[] _auchCRCHi
 2         private static readonly byte[] _auchCRCHi = new byte[]//crc高位表
 3         {
 4             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
 5             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
 6             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
 7             0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
 8             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
 9             0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
10             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
11             0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
12             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
13             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
14             0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
15             0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
16             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
17             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
18             0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
19             0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
20             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
21             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
22             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
23             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
24             0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
25             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
26             0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
27             0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
28             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
29             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
30         };
31         #endregion
32
33         #region CRC低位表 byte[] _auchCRCLo
34         private static readonly byte[] _auchCRCLo = new byte[]//crc低位表
35         {
36             0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
37             0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
38             0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
39             0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
40             0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
41             0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
42             0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
43             0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
44             0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
45             0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
46             0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
47             0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
48             0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
49             0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
50             0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
51             0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
52             0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
53             0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
54             0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
55             0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
56             0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
57             0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
58             0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
59             0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
60             0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
61             0x43, 0x83, 0x41, 0x81, 0x80, 0x40
62         };
63         #endregion

CRC高低位表

  2.3 CRC校验方法:CRC16()

 1 /// <summary>
 2         /// 【CRC校验】
 3         /// </summary>
 4         /// <param name="buffer">命令帧合适前6字节</param>
 5         /// <param name="Sset">开始位</param>
 6         /// <param name="Eset">结束位</param>
 7         /// <returns>CRC校验码</returns>
 8         public static ushort CRC16(Byte[] buffer, int Sset, int Eset)
 9         {
10             byte crcHi = 0xff;  // 高位初始化
11
12             byte crcLo = 0xff;  // 低位初始化
13
14             for (int i = Sset; i <= Eset; i++)
15             {
16                 int crcIndex = crcHi ^ buffer[i]; //查找crc表值
17
18                 crcHi = (byte)(crcLo ^ _auchCRCHi[crcIndex]);
19                 crcLo = _auchCRCLo[crcIndex];
20             }
21
22             return (ushort)(crcHi << 8 | crcLo);
23         }

public static ushort CRC16(Byte[] buffer, int Sset, int Eset)

  

  2.4 获取数据帧,把需要发送的数据格式化成Modbus协议数据帧

  

 1 /// <summary>
 2         /// 【获取读数据命令,返回命令帧】
 3         /// </summary>
 4         /// <param name="mdaddr">地址码</param>
 5         /// <param name="R_CMD">功能码</param>
 6         /// <param name="min_reg">寄存器地址</param>
 7         /// <param name="data_len">寄存器个数</param>
 8         /// <param name="R_CMD_LEN">命令长度</param>
 9         /// <returns></returns>
10         public byte[] GetReadFrame(byte mdaddr, byte R_CMD, ushort min_reg, ushort data_len, int R_CMD_LEN)
11         {
12             //主机命令帧格式
13             //  字节    功能描述            例子
14             //
15             //  1         地址码             0x01
16             //  2         功能码             0x03
17             //  3         寄存器地址高       0x00
18             //  4         寄存器地址低       0x00
19             //  5         寄存器个数高       0x00
20             //  6         寄存器个数低       0x02
21             //  7         CRC检验码低        0xC4
22             //  8         CRC校验码高        0x0B
23
24             ushort crc;
25             byte[] message = new byte[8];
26
27             //设置模块号
28             message[0] = mdaddr;
29             //设置命令字
30             message[1] = R_CMD;
31
32             //设置开始寄存器
33             message[2] = WORD_HI(min_reg);
34             message[3] = WORD_LO(min_reg);
35
36             //设置数据长度
37             message[4] = WORD_HI(data_len);
38             message[5] = WORD_LO(data_len);
39
40             //设置 CRC
41             crc = CRC16(message, 0, R_CMD_LEN - 3);
42
43             message[6] = WORD_HI(crc);//CRC校验码高位
44             message[7] = WORD_LO(crc);//CRC校验码低位
45
46
47             return message;
48         }

  2.6 对于DataReceived的使用

    #1 设置委托和方法

       1 private delegate void myDelegate(byte[] readBuffer);     

1 /// <summary>
2         /// 【显示接收返回的数据】
3         /// </summary>
4         /// <param name="resbuffer"></param>
5         public void ShowRst(byte[] resbuffer)
6         {
7             MyModbus modbus = new MyModbus();
8             tbxRecvData.Text += "Recv:" + modbus.SetText(resbuffer) + "\r\n";
9         }

    #2 设置属性:ReceivedBytesThreshold = 1

1  //1、设置串口的属性
2             sp = new SerialPort();
3
4             sp.ReceivedBytesThreshold = 1;//获取DataReceived事件发生前内部缓存区字节数
5             sp.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);//设置委托

    #3 点击“发送数据”按钮的事件如下:  

 1         /// <summary>
 2         /// 【发送数据】
 3         /// </summary>
 4         /// <param name="sender"></param>
 5         /// <param name="e"></param>
 6         private void btnSend_Click(object sender, EventArgs e)
 7         {
 8             //发送串口数据
 9
10             //1、检查串口设置
11             if (!CheckPortSetting())
12             {
13                 MessageBox.Show("串口未设置!", "错误提示");
14                 return;
15             }
16
17             //2、检查发送数据是否为空
18             //if (!CheckSendData())
19             //{
20             //    MessageBox.Show("请输入要发送的数据!", "错误提示");
21             //    return;
22             //}
23
24             //3、设置
25             if (!isSetProperty)
26             {
27                 SetPortProperty();
28                 isSetProperty = true;
29             }
30
31             //4、写串口数据
32             if (isOpen)
33             {
34                 //写出口数据
35                 try
36                 {
37                     //sp.Write(tbxSendData.Text);
38                     tbxStatus.Text = "发送成功!";
39                     //tbxSendData.Text += tbxAddress.Text;
40
41
42                     byte address = Convert.ToByte( tbxAddress.Text.Trim(), 16);//地址码
43                     byte cmd = Convert.ToByte(tbxCmd.Text.Trim(),16);//命令帧
44                     byte regAddr = Convert.ToByte(tbxRegAddr.Text.Trim(), 16);//寄存器地址
45                     byte regNum = Convert.ToByte(tbxRegNum.Text.Trim(), 16);//寄存器数量
46
47
48                     //Modbus相关处理对象
49                     MyModbus modbus = new MyModbus();
50                     byte[] text = modbus.GetReadFrame(address, cmd, regAddr, regNum, 8);
51
52                     sp.Write(text, 0, 8);
53                     tbxRecvData.Text += "Send:" + BitConverter.ToString(text)+ "\r\n";
54
55                 }
56                 catch
57                 {
58                     tbxStatus.Text = "发送数据错误";
59                 }
60             }
61             else
62             {
63                 MessageBox.Show("串口未打开", "错误提示");
64             }
65
66
67         }

    

  2.7 附加代码

    #1 这里的MyModbus主要为Modbus相关一些操作,包括把发送数据封装成Modbus数据帧等。   

  1 using System;
  2 using System.Collections.Generic;
  3 using System.IO.Ports;
  4 using System.Linq;
  5 using System.Text;
  6 using System.Threading.Tasks;
  7
  8 namespace 串口调试
  9 {
 10     class MyModbus
 11     {
 12
 13         #region CRC高位表 byte[] _auchCRCHi
 14         private static readonly byte[] _auchCRCHi = new byte[]//crc高位表
 15         {
 16             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
 17             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
 18             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
 19             0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
 20             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
 21             0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
 22             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
 23             0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
 24             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
 25             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
 26             0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
 27             0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
 28             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
 29             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
 30             0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
 31             0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
 32             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
 33             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
 34             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
 35             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
 36             0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
 37             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
 38             0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
 39             0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
 40             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
 41             0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
 42         };
 43         #endregion
 44
 45         #region CRC低位表 byte[] _auchCRCLo
 46         private static readonly byte[] _auchCRCLo = new byte[]//crc低位表
 47         {
 48             0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
 49             0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
 50             0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
 51             0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
 52             0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
 53             0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
 54             0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
 55             0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
 56             0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
 57             0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
 58             0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
 59             0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
 60             0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
 61             0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
 62             0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
 63             0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
 64             0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
 65             0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
 66             0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
 67             0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
 68             0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
 69             0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
 70             0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
 71             0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
 72             0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
 73             0x43, 0x83, 0x41, 0x81, 0x80, 0x40
 74         };
 75         #endregion
 76
 77         /// <summary>
 78         /// 【获取读数据命令,返回命令帧】
 79         /// </summary>
 80         /// <param name="mdaddr">地址码</param>
 81         /// <param name="R_CMD">功能码</param>
 82         /// <param name="min_reg">寄存器地址</param>
 83         /// <param name="data_len">寄存器个数</param>
 84         /// <param name="R_CMD_LEN">命令长度</param>
 85         /// <returns></returns>
 86         public byte[] GetReadFrame(byte mdaddr, byte R_CMD, ushort min_reg, ushort data_len, int R_CMD_LEN)
 87         {
 88             //主机命令帧格式
 89             //  字节    功能描述            例子
 90             //
 91             //  1         地址码             0x01
 92             //  2         功能码             0x03
 93             //  3         寄存器地址高       0x00
 94             //  4         寄存器地址低       0x00
 95             //  5         寄存器个数高       0x00
 96             //  6         寄存器个数低       0x02
 97             //  7         CRC检验码低        0xC4
 98             //  8         CRC校验码高        0x0B
 99
100             ushort crc;
101             byte[] message = new byte[8];
102
103             //设置模块号
104             message[0] = mdaddr;
105             //设置命令字
106             message[1] = R_CMD;
107
108             //设置开始寄存器
109             message[2] = WORD_HI(min_reg);
110             message[3] = WORD_LO(min_reg);
111
112             //设置数据长度
113             message[4] = WORD_HI(data_len);
114             message[5] = WORD_LO(data_len);
115
116             //设置 CRC
117             crc = CRC16(message, 0, R_CMD_LEN - 3);
118
119             message[6] = WORD_HI(crc);//CRC校验码高位
120             message[7] = WORD_LO(crc);//CRC校验码低位
121
122
123             return message;
124         }
125
126         /// <summary>
127         /// 【格式化输出,校验读取的数据】
128         /// </summary>
129         /// <param name="readBuffer"></param>
130         /// <returns></returns>
131         public string SetText(byte[] readBuffer)
132         {
133             //将byte 转换成string 用于显示
134             //string readstr = string.Empty;
135             if (readBuffer != null)
136             {
137                 ushort crc = CRC16(readBuffer, 0, readBuffer.Length - 3);
138                 if (readBuffer[readBuffer.Length - 2] == WORD_HI(crc) && readBuffer[readBuffer.Length - 1] == WORD_LO(crc))//crc校验
139                 {
140                     return ToHexString(readBuffer);
141                 }
142                 else
143                 {
144                     return "CRC校验错误";
145                 }
146             }
147
148             return "程序出错";
149         }
150
151
152         /// <summary>
153         /// 【CRC校验】
154         /// </summary>
155         /// <param name="buffer">命令帧合适前6字节</param>
156         /// <param name="Sset">开始位</param>
157         /// <param name="Eset">结束位</param>
158         /// <returns>CRC校验码</returns>
159         public static ushort CRC16(Byte[] buffer, int Sset, int Eset)
160         {
161             byte crcHi = 0xff;  // 高位初始化
162
163             byte crcLo = 0xff;  // 低位初始化
164
165             for (int i = Sset; i <= Eset; i++)
166             {
167                 int crcIndex = crcHi ^ buffer[i]; //查找crc表值
168
169                 crcHi = (byte)(crcLo ^ _auchCRCHi[crcIndex]);
170                 crcLo = _auchCRCLo[crcIndex];
171             }
172
173             return (ushort)(crcHi << 8 | crcLo);
174         }
175
176         /// <summary>
177         /// 【获取大写字母】
178         /// </summary>
179         /// <param name="bytes"></param>
180         /// <returns></returns>
181         public static string ToHexString(byte[] bytes) // 0xae00cf => "AE00CF "
182         {
183             string hexString = string.Empty;
184
185             if (bytes != null)
186             {
187
188                 StringBuilder strB = new StringBuilder();
189
190                 for (int i = 0; i < bytes.Length; i++)
191                 {
192
193                     strB.Append(bytes[i].ToString("X2"));
194
195                 }
196
197                 hexString = strB.ToString();
198
199             } return hexString;
200
201         }
202
203         //取Word变量的高位字节、低位字节
204         /// <summary>
205         /// 【获取低位字节】
206         /// </summary>
207         /// <param name="crcCLo"></param>
208         /// <returns></returns>
209         public static byte WORD_LO(ushort crcCLo)
210         {
211             crcCLo = (ushort)(crcCLo & 0X00FF);
212             return (byte)crcCLo;
213         }
214
215         /// <summary>
216         /// 【获取高位字节】
217         /// </summary>
218         /// <param name="crcHI"></param>
219         /// <returns></returns>
220         public static byte WORD_HI(ushort crcHI)
221         {
222             crcHI = (ushort)(crcHI >> 8 & 0X00FF);
223             return (byte)crcHI;
224         }
225     }
226 }

MyModbus.cs

    

    #2 下面为Form1.cs的代码,主要包括窗体一些基本操作。   

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.IO.Ports;
  7 using System.Linq;
  8 using System.Text;
  9 using System.Threading.Tasks;
 10 using System.Windows.Forms;
 11
 12
 13 namespace 串口调试
 14 {
 15     public partial class Form1 : Form
 16     {
 17         SerialPort sp = null;
 18
 19         bool isOpen = false;//是否打开
 20
 21         bool isSetProperty = false;
 22
 23
 24         //数据接收使用的代理
 25         private delegate void myDelegate(byte[] readBuffer);
 26
 27         public Form1()
 28         {
 29             InitializeComponent();
 30         }
 31
 32         private void Form1_Load(object sender, EventArgs e)
 33         {
 34
 35             //设置窗口大小固定
 36             this.MaximumSize = this.Size;
 37             this.MinimumSize = this.Size;
 38
 39             //1、设置串口下拉列表
 40             for ( int i = 0; i < 10; i++ )
 41             {
 42                 cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
 43             }
 44             cbxCOMPort.SelectedIndex = 2;//默认选项
 45
 46
 47             //2、设置常用波特率
 48             int bt = 300;
 49             for (int i = 0; i < 8; i++)
 50             {
 51                 cbxBaudRate.Items.Add(bt.ToString());
 52                 bt *= 2;
 53             }
 54
 55             cbxBaudRate.Items.Add("38400");
 56             cbxBaudRate.Items.Add("43000");
 57             cbxBaudRate.Items.Add("56000");
 58             cbxBaudRate.Items.Add("57600");
 59             cbxBaudRate.Items.Add("115200");
 60             cbxBaudRate.SelectedIndex = 5;
 61
 62
 63             //3、列出停止位
 64             cbxStopBits.Items.Add("0");
 65             cbxStopBits.Items.Add("1");
 66             cbxStopBits.Items.Add("1.5");
 67             cbxStopBits.Items.Add("2");
 68             cbxStopBits.SelectedIndex = 1;
 69
 70             //4、设置奇偶检验
 71             cbxParity.Items.Add("无");
 72             cbxParity.Items.Add("奇校验");
 73             cbxParity.Items.Add("偶校验");
 74             cbxParity.SelectedIndex = 0;
 75
 76             //5、设置数据位
 77             cbxDataBits.Items.Add("8");
 78             cbxDataBits.Items.Add("7");
 79             cbxDataBits.Items.Add("6");
 80             cbxDataBits.Items.Add("5");
 81             cbxDataBits.SelectedIndex = 0;
 82
 83
 84         }
 85
 86
 87         /// <summary>
 88         /// 【串口检测按钮】
 89         /// </summary>
 90         /// <param name="sender"></param>
 91         /// <param name="e"></param>
 92         private void btnCheckCOM_Click(object sender, EventArgs e)
 93         {
 94             //1、检测哪些端口可用
 95             cbxCOMPort.Items.Clear();
 96             cbxCOMPort.Text = "";
 97
 98             lblStatus.Text = "执行中...";
 99             string str = "";
100             for (int i = 0; i < 10; i++)
101             {
102                 try
103                 {
104                     SerialPort sp = new SerialPort("COM" + (i + 1).ToString());
105                     sp.Open();
106                     sp.Close();
107                     cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
108                 }
109                 catch
110                 {
111                     str += "COM" + (i + 1).ToString() + "、";
112                     continue;
113                 }
114             }
115
116             if(cbxCOMPort.Items.Count > 0)
117                 cbxCOMPort.SelectedIndex = 0;
118             lblStatus.Text = "完成";
119             tbxStatus.Text = str;
120         }
121
122
123         public void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
124         {
125             myDelegate md = new myDelegate(ShowRst);
126             try
127             {
128                 if (sp.IsOpen)
129                 {
130                     int count = sp.BytesToRead;
131                     if (count > 0)
132                     {
133                         byte[] readBuffer = new byte[count];
134                         sp.Read(readBuffer, 0, count);//读取串口数据
135                         //     serialPort1.Write(readBuffer, 0, count);
136                         Invoke(md, readBuffer);
137                     }
138                 }
139             }
140             catch (Exception err)
141             {
142                 throw err;
143             }
144
145         }
146
147         /// <summary>
148         /// 【显示接收返回的数据】
149         /// </summary>
150         /// <param name="resbuffer"></param>
151         public void ShowRst(byte[] resbuffer)
152         {
153             MyModbus modbus = new MyModbus();
154             tbxRecvData.Text += "Recv:" + modbus.SetText(resbuffer) + "\r\n";
155             tbxRecvData.Text += "\r\n";
156         }
157
158         /// <summary>
159         /// 【检测端口设置】
160         /// </summary>
161         /// <returns></returns>
162         private bool CheckPortSetting()
163         {
164             //检测端口设置
165             if (cbxCOMPort.Text.Trim() == "" || cbxBaudRate.Text.Trim() == "" || cbxStopBits.Text.Trim() == "" || cbxParity.Text.Trim() == "" || cbxDataBits.Text.Trim() == "")
166                 return false;
167             return true;
168         }
169
170         /// <summary>
171         /// 【检测发送数据是否为空】
172         /// </summary>
173         /// <returns></returns>
174         private bool CheckSendData()
175         {
176             if (tbxSendData.Text.Trim() == "")
177                 return false;
178             return true;
179         }
180
181
182         /// <summary>
183         /// 【设置串口属性】
184         /// </summary>
185         private void SetPortProperty()
186         {
187             //1、设置串口的属性
188             sp = new SerialPort();
189
190             sp.ReceivedBytesThreshold = 1;//获取DataReceived事件发生前内部缓存区字节数
191             sp.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);//设置委托
192
193             sp.PortName = cbxCOMPort.Text.Trim();
194
195             //2、设置波特率
196             sp.BaudRate =  Convert.ToInt32( cbxBaudRate.Text.Trim());
197
198             //3、设置停止位
199             float f = Convert.ToSingle( cbxStopBits.Text.Trim());
200
201             if (f == 0)
202             {
203                 sp.StopBits = StopBits.None;//表示不使用停止位
204             }
205             else if (f == 1.5)
206             {
207                 sp.StopBits = StopBits.OnePointFive;//使用1.5个停止位
208             }
209             else if (f == 2)
210             {
211                 sp.StopBits = StopBits.Two;//表示使用两个停止位
212             }
213             else
214             {
215                 sp.StopBits = StopBits.One;//默认使用一个停止位
216             }
217
218             //4、设置数据位
219             sp.DataBits = Convert.ToInt16(cbxDataBits.Text.Trim());
220
221             //5、设置奇偶校验位
222             string s = cbxParity.Text.Trim();
223             if (s.CompareTo("无") == 0)
224             {
225                 sp.Parity = Parity.None;//不发生奇偶校验检查
226             }
227             else if (s.CompareTo("奇校验") == 0)
228             {
229                 sp.Parity = Parity.Odd;//设置奇校验
230             }
231             else if (s.CompareTo("偶校验") == 0)
232             {
233                 sp.Parity = Parity.Even;//设置偶检验
234             }
235             else
236             {
237                 sp.Parity = Parity.None;
238             }
239
240             //6、设置超时读取时间
241             sp.ReadTimeout = -1;
242
243             //7、打开串口
244             try
245             {
246                 sp.Open();
247                 isOpen = true;
248             }
249             catch(Exception)
250             {
251                 lblStatus.Text = "打开串口错误!";
252             }
253
254         }
255
256
257
258         /// <summary>
259         /// 【发送数据】
260         /// </summary>
261         /// <param name="sender"></param>
262         /// <param name="e"></param>
263         private void btnSend_Click(object sender, EventArgs e)
264         {
265             //发送串口数据
266
267             //1、检查串口设置
268             if (!CheckPortSetting())
269             {
270                 MessageBox.Show("串口未设置!", "错误提示");
271                 return;
272             }
273
274             //2、检查发送数据是否为空
275             //if (!CheckSendData())
276             //{
277             //    MessageBox.Show("请输入要发送的数据!", "错误提示");
278             //    return;
279             //}
280
281             //3、设置
282             if (!isSetProperty)
283             {
284                 SetPortProperty();
285                 isSetProperty = true;
286             }
287
288             //4、写串口数据
289             if (isOpen)
290             {
291                 //写出口数据
292                 try
293                 {
294                     //sp.Write(tbxSendData.Text);
295                     tbxStatus.Text = "发送成功!";
296                     //tbxSendData.Text += tbxAddress.Text;
297
298
299                     byte address = Convert.ToByte( tbxAddress.Text.Trim(), 16);//地址码
300                     byte cmd = Convert.ToByte(tbxCmd.Text.Trim(),16);//命令帧
301                     byte regAddr = Convert.ToByte(tbxRegAddr.Text.Trim(), 16);//寄存器地址
302                     byte regNum = Convert.ToByte(tbxRegNum.Text.Trim(), 16);//寄存器数量
303
304
305                     //Modbus相关处理对象
306                     MyModbus modbus = new MyModbus();
307                     byte[] text = modbus.GetReadFrame(address, cmd, regAddr, regNum, 8);
308
309                     sp.Write(text, 0, 8);
310                     tbxRecvData.Text += "Send:" + BitConverter.ToString(text)+ "\r\n";
311
312                 }
313                 catch
314                 {
315                     tbxStatus.Text = "发送数据错误";
316                 }
317             }
318             else
319             {
320                 MessageBox.Show("串口未打开", "错误提示");
321             }
322
323
324         }
325
326         /// <summary>
327         /// 【读取数据】
328         /// </summary>
329         /// <param name="sender"></param>
330         /// <param name="e"></param>
331         private void btnRecv_Click(object sender, EventArgs e)
332         {
333             if(isOpen) 
334             { 
335                 try 
336                 {
337                     //读取串口数据
338  
339                     tbxRecvData.Text += sp.ReadLine()+"\r\n"; 
340                 } 
341                 catch(Exception) 
342                 { 
343                     lblStatus.Text = "读取串口时发生错误!"; 
344                     return; 
345                 } 
346             } 
347             else 
348             { 
349               MessageBox.Show("串口未打开!", "错误提示"); 
350               return; 
351
352             } 
353         }
354
355
356     }
357 }

Form1.cs

 三、最后说一句

  由于需要查询大量资料,有些代码是引用别人的,所以这里要感谢那些分享的人,秉着分享精神,希望可以帮助更多的人。

  如果发现有什么错误,或者建议,请留言,谢谢!

时间: 2024-07-30 18:25:43

基于Modbus的C#串口调试开发的相关文章

VELT-0.1.5开发: gdb串口调试内核时信息丢失的问题

快乐虾 http://blog.csdn.net/lights_joy/(QQ群:Visual EmbedLinux Tools 375515651) 欢迎转载,但请保留作者信息 本文仅适用于vs2013 + velt-0.1.5 VELT的全称是Visual EmbedLinuxTools,它是一个与visual gdb类似的visual studio插件,用以辅助完成Linux开发.利用这个插件,将可以在visual studio的IDE中进行Linux应用程序的开发(包括编译和调试),也可

Delphi 使用串口模拟工具进行串口程序开发调试

版权声明:本文为博主原创文章,如需转载请注明出处及作者. 本文由小李专栏原创,转载需注明出处:[http://blog.csdn.net/softwave/article/details/8907599] 最近在做一个项目,要求使用Java对磅房的小磅数据进行读取,这就要求使用Java与串口进行通信.串口的Java代码已经开发完毕,但又不能总在生产现场调试程序,所以如何在自己的笔记本电脑上模拟串口数据就成了当务之急.托Google的福,找到了两个小工具:VSPD和串口调试助手.下面简单介绍一下如

工业串口触摸屏开发制作mp3播放器,单片机或PLC通过串口控制播放MP3音频方法

在工业控制系统中将音乐或语音MP3的美和工控结合在一起,给人以赏心悦目的感受.随着工业控制的发展,对工业控制的控制要求也越来越高,使得越来越多的控制部分不是指简单的现场控制,还需要增加音乐播放或语音提示,使得控制系统更加人性化. 这里介绍广州易显的工业串口触摸屏或者ARM工控机连接单片机或者PLC,使用工业串口触摸屏开发制作mp3的方法.跟电脑的播放器一样,具有播放,暂停,停止,控制播放进度,上一首,下一首等功能.可以在人机界面上控制播放MP3音乐或者语音提示.也可以使用单片机或PLC控制播放的

ok6410[002] ubuntu1604系统下搭配ckermit和dnw基于RAM的裸机程序调试环境

ubuntu1604系统下搭配ckermit和dnw基于RAM的裸机程序调试环境 系统:  ubuntu16.04 裸板: 飞凌公司OK6410开发板 目标:搭建基于ubuntu1604系统和基于RAM的裸机程序开发环境 1.安装配置ckermit 在ubuntu1604下一般做裸板开发时都是使用ckermit作为远程通讯终端,下面我们就开始讲解下其安装与配置. 1.1.安装ckermit 在ubuntu系统下安装ckermit,使用 sudo  apt-get  install  ckermi

keil中的串口调试:

keil中串口的虚拟调试信息在通过View-serial windows-#usart1/2/3/4/debug(printf)可以看到.当然也可以通过虚拟串口VSPD+串口调试助手在外部实现,方法如下: 虚拟 串口使用:步骤 1 下载虚拟串口软件,虚拟2个连接的串口COMA/COMB,这两个串口与PC机的真实物理串口没关系.两边的设置相同 2 打开串口通讯助手,将A分配给串口通讯助手,则B就分配给下面的COMx 2 在MDK中输入命令行或者将下面的做成debug.ini文件加载 MODE CO

基于Eclipse的Go语言可视化开发环境

http://jingyan.baidu.com/article/d7130635032e2f13fdf475b8.html 基于Eclipse的Go语言可视化开发环境 | 浏览:2924 | 更新:2013-12-21 18:24 | 标签:eclipse 1 2 3 4 5 6 7 分步阅读 搭建与使用一个基于Eclipse的Go语言可视化开发环境. 注意: 在阅读此片经验前,先确保必要工具已经安装或打开 工具/原料 搭建好Go语言开发环境,详细步骤见经验: http://jingyan.b

java串口助手开发记录

转载:http://blog.sina.com.cn/s/blog_ad0672d601017qjs.html 一.首先搭建平台,我用的是eclipse+rxtx+SWT. 安装eclipse就是安装java包后,然后下载eclipse即可.因为eclipse是绿色的,不用安装,下载即可用. 下载rxtx.在网上下载rxtx包含串口开发的常用函数,是开源社区的一个产物,与sun公司的comm包相同,只是调用前的导入语句由import javax.comm.*变为import gnu.io.*而已

Linux下使用putty进行UART串口调试【转】

本文转载自:http://blog.csdn.net/xzongyuan/article/details/11593101 版权声明:本文为博主原创文章,未经博主允许不得转载. 使用putty进行串口调试,非常简单.只需要设置好端口(Serial Line),Linux中默认接上串口后,端口为ttyS0,或者ttyUSB0(对应USB转串口),如果再多接一个,就会显示ttyS1或者ttyUSB1:Window中,普通串口对应的是COM1.COM2,USB对应COM3(需要安装驱动,在设备管理器查

STM32F407串口调试总结

一.背景 最近项目开发需要用到3路串口,分别为U1,U5,U6,其中U1原子哥的例程已经提供,拿过来就可用,本文主要讲解U5,U6串口调试. 二.正文 1.硬件描述 三个串口分别对应的发送引脚和接收引脚如下表所示: 串口号 接收引脚 发送引脚 USART1 PA9 PA10 UART5 PC12 PD2 USART6 PG14 PG9 2.串口介绍 USART为通用同步异步接收器,能够灵活地与外部设备进行全双工数据交换,满足外部设备对工业标准NRZ异步穿行数据格式的要求. STM32F407ZG