基于Modbus的C#串口调试开发
說(shuō)明:本文主要研究的是使用C# WinForm開發(fā)的串口調(diào)試軟件(其中包含Modbus協(xié)議相關(guān)操作)。Modbus相關(guān)協(xié)議可以查閱百度文庫(kù)等,可參考: 《http://wenku.baidu.com/link?url=J-QZeQVLfvfZh7_lh8Qf0MdwANZuVjEoTqox6zJYrSnKyfgES2RTb_bjC5ZTn8-xgsuUAyiELRYVA3-3FBkBGywWhQ9YGoavJOzwB0IxTyK 》。
(1)先測(cè)試串口設(shè)置,發(fā)送和接收數(shù)據(jù)。
(2)發(fā)送modbus的命令幀數(shù)據(jù)和使用DataReceived接收緩沖區(qū)的數(shù)據(jù)。
一、簡(jiǎn)單的串口調(diào)試工具
下圖為串口調(diào)試工具的界面,主要包括串口基本設(shè)置,功能操作,狀態(tài)框以及發(fā)送接收框。由于這里只是簡(jiǎn)單的初始化數(shù)據(jù),所以當(dāng)需要發(fā)送數(shù)據(jù)的時(shí)候需要點(diǎn)擊“串口檢測(cè)”,來(lái)測(cè)試當(dāng)前可用的串口,然后輸入需要發(fā)送的數(shù)據(jù),最后點(diǎn)擊“發(fā)送數(shù)據(jù)”(由于測(cè)試需要,讓發(fā)送什么數(shù)據(jù)就返回什么數(shù)據(jù),這里的底層硬件做了短接處理,使用短接貌P30-P31,具體操作可以自行百度)
1.1 發(fā)送數(shù)據(jù)操作
(1)點(diǎn)擊 串口檢測(cè)
(2)輸入發(fā)送數(shù)據(jù)
(3)點(diǎn)擊 發(fā)送數(shù)據(jù)
下面開始時(shí)具體代碼:
#1 軟件打開時(shí)候初始化操作:Form1_Load(),主要初始化操作串口設(shè)置的下拉列表。
1 private void Form1_Load(object sender, EventArgs e)
2 {
3
4 //設(shè)置窗口大小固定
5 this.MaximumSize = this.Size;
6 this.MinimumSize = this.Size;
7
8 //1、設(shè)置串口下拉列表
9 for ( int i = 0; i < 10; i++ )
10 {
11 cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
12 }
13 cbxCOMPort.SelectedIndex = 2;//默認(rèn)選項(xiàng)
14
15
16 //2、設(shè)置常用波特率
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、設(shè)置奇偶檢驗(yàn)
40 cbxParity.Items.Add("無(wú)");
41 cbxParity.Items.Add("奇校驗(yàn)");
42 cbxParity.Items.Add("偶校驗(yàn)");
43 cbxParity.SelectedIndex = 0;
44
45 //5、設(shè)置數(shù)據(jù)位
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 檢查串口基本設(shè)置的參數(shù):CheckPortSetting(),
1 /// <summary>
2 /// 【檢測(cè)端口設(shè)置】
3 /// </summary>
4 /// <returns></returns>
5 private bool CheckPortSetting()
6 {
7 //檢測(cè)端口設(shè)置
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 設(shè)置串口屬性,創(chuàng)建SerialPort對(duì)象
1 /// <summary>
2 /// 【設(shè)置串口屬性】
3 /// </summary>
4 private void SetPortProperty()
5 {
6 //1、設(shè)置串口的屬性
7 sp = new SerialPort();
8
9 sp.ReceivedBytesThreshold = 1;//獲取DataReceived事件發(fā)生前內(nèi)部緩存區(qū)字節(jié)數(shù)
10 sp.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);//設(shè)置委托
11
12 sp.PortName = cbxCOMPort.Text.Trim();
13
14 //2、設(shè)置波特率
15 sp.BaudRate = Convert.ToInt32( cbxBaudRate.Text.Trim());
16
17 //3、設(shè)置停止位
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個(gè)停止位
27 }
28 else if (f == 2)
29 {
30 sp.StopBits = StopBits.Two;//表示使用兩個(gè)停止位
31 }
32 else
33 {
34 sp.StopBits = StopBits.One;//默認(rèn)使用一個(gè)停止位
35 }
36
37 //4、設(shè)置數(shù)據(jù)位
38 sp.DataBits = Convert.ToInt16(cbxDataBits.Text.Trim());
39
40 //5、設(shè)置奇偶校驗(yàn)位
41 string s = cbxParity.Text.Trim();
42 if (s.CompareTo("無(wú)") == 0)
43 {
44 sp.Parity = Parity.None;//不發(fā)生奇偶校驗(yàn)檢查
45 }
46 else if (s.CompareTo("奇校驗(yàn)") == 0)
47 {
48 sp.Parity = Parity.Odd;//設(shè)置奇校驗(yàn)
49 }
50 else if (s.CompareTo("偶校驗(yàn)") == 0)
51 {
52 sp.Parity = Parity.Even;//設(shè)置偶檢驗(yàn)
53 }
54 else
55 {
56 sp.Parity = Parity.None;
57 }
58
59 //6、設(shè)置超時(shí)讀取時(shí)間
60 sp.ReadTimeout = -1;
61
62 //7、打開串口
63 try
64 {
65 sp.Open();
66 isOpen = true;
67 }
68 catch(Exception)
69 {
70 lblStatus.Text = "打開串口錯(cuò)誤!";
71 }
72
73 }
private void SetPortProperty()
#4 “發(fā)送數(shù)據(jù)”按鈕點(diǎn)擊事件:btnSend_Click(), 在發(fā)送數(shù)據(jù)需要進(jìn)行,#2,#3驗(yàn)證,然后開始通過(guò)串口對(duì)象寫入數(shù)據(jù)
1 /// <summary>
2 /// 【發(fā)送數(shù)據(jù)】
3 /// </summary>
4 /// <param name="sender"></param>
5 /// <param name="e"></param>
6 private void btnSend_Click(object sender, EventArgs e)
7 {
8 //發(fā)送串口數(shù)據(jù)
9
10 //1、檢查串口設(shè)置
11 if (!CheckPortSetting())
12 {
13 MessageBox.Show("串口未設(shè)置!", "錯(cuò)誤提示");
14 return;
15 }
16
17 //2、檢查發(fā)送數(shù)據(jù)是否為空
18 if(tbxSendData.Text.Trim() == ""){
19 MessageBox.Show("發(fā)送數(shù)據(jù)不能為空");
20 return;
21 }
22
23 //3、設(shè)置
24 if (!isSetProperty)
25 {
26 SetPortProperty();
27 isSetProperty = true;
28 }
29
30 //4、寫串口數(shù)據(jù)
31 if (isOpen)
32 {
33 //寫出口數(shù)據(jù)
34 try
35 {
36 sp.Write(tbxSendData.Text);
37 tbxStatus.Text = "發(fā)送成功!";
38
39
40 tbxRecvData.Text += sp.ReadLine();//讀取發(fā)送的數(shù)據(jù)
41
42 }
43 catch
44 {
45 tbxStatus.Text = "發(fā)送數(shù)據(jù)錯(cuò)誤";
46 }
47 }
48 else
49 {
50 MessageBox.Show("串口未打開", "錯(cuò)誤提示");
51 }
52
53
54 }
1.2 接受數(shù)據(jù)操作
接收數(shù)據(jù)和發(fā)送數(shù)據(jù)有點(diǎn)類似
1 /// <summary>
2 /// 【讀取數(shù)據(jù)】
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 //讀取串口數(shù)據(jù)
13
14 tbxRecvData.Text+=sp.ReadLine()+"
";
15 }
16 catch(Exception)
17 {
18 lblStatus.Text="讀取串口時(shí)發(fā)生錯(cuò)誤!";
19 return;
20 }
21 }
22 else
23 {
24 MessageBox.Show("串口未打開!","錯(cuò)誤提示");
25 return;
26
27 }
28 }
最后附上該窗體的后臺(tái)代碼: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 串口調(diào)試
14 {
15 public partial class Form1 : Form
16 {
17 SerialPort sp = null;
18
19 bool isOpen = false;//是否打開
20
21 bool isSetProperty = false;//是否通過(guò)串口設(shè)置
22
23
24 public Form1()
25 {
26 InitializeComponent();
27 }
28
29 private void Form1_Load(object sender, EventArgs e)
30 {
31
32 //設(shè)置窗口大小固定
33 this.MaximumSize = this.Size;
34 this.MinimumSize = this.Size;
35
36 //1、設(shè)置串口下拉列表
37 for ( int i = 0; i < 10; i++ )
38 {
39 cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
40 }
41 cbxCOMPort.SelectedIndex = 2;//默認(rèn)選項(xiàng)
42
43
44 //2、設(shè)置常用波特率
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、設(shè)置奇偶檢驗(yàn)
68 cbxParity.Items.Add("無(wú)");
69 cbxParity.Items.Add("奇校驗(yàn)");
70 cbxParity.Items.Add("偶校驗(yàn)");
71 cbxParity.SelectedIndex = 0;
72
73 //5、設(shè)置數(shù)據(jù)位
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 /// 【串口檢測(cè)按鈕】
86 /// </summary>
87 /// <param name="sender"></param>
88 /// <param name="e"></param>
89 private void btnCheckCOM_Click(object sender, EventArgs e)
90 {
91 //1、檢測(cè)哪些端口可用
92 cbxCOMPort.Items.Clear();
93 cbxCOMPort.Text = "";
94
95 lblStatus.Text = "執(zhí)行中...";
96 string str = "";
97 for (int i = 0; i < 10; i++)
98 {
99 try
100 {
101 ////把所有可能的串口都測(cè)試一遍,打開關(guān)閉操作,只有可用的串口才可會(huì)放到下拉列表中
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 //如果當(dāng)前下拉列表有可用的串口,則默認(rèn)選擇第一個(gè)
115 if(cbxCOMPort.Items.Count > 0)
116 cbxCOMPort.SelectedIndex = 0;
117 lblStatus.Text = "完成";
118 tbxStatus.Text = str;
119 }
120
121
122 /// <summary>
123 /// 【檢測(cè)端口設(shè)置】
124 /// </summary>
125 /// <returns></returns>
126 private bool CheckPortSetting()
127 {
128 //檢測(cè)端口設(shè)置
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 /// 【檢測(cè)發(fā)送數(shù)據(jù)是否為空】
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 /// 【設(shè)置串口屬性】
148 /// </summary>
149 private void SetPortProperty()
150 {
151 //1、設(shè)置串口的屬性
152 sp = new SerialPort();
153
154 sp.PortName = cbxCOMPort.Text.Trim();
155
156 //2、設(shè)置波特率
157 sp.BaudRate = Convert.ToInt32( cbxBaudRate.Text.Trim());
158
159 //3、設(shè)置停止位
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個(gè)停止位
169 }
170 else if (f == 2)
171 {
172 sp.StopBits = StopBits.Two;//表示使用兩個(gè)停止位
173 }
174 else
175 {
176 sp.StopBits = StopBits.One;//默認(rèn)使用一個(gè)停止位
177 }
178
179 //4、設(shè)置數(shù)據(jù)位
180 sp.DataBits = Convert.ToInt16(cbxDataBits.Text.Trim());
181
182 //5、設(shè)置奇偶校驗(yàn)位
183 string s = cbxParity.Text.Trim();
184 if (s.CompareTo("無(wú)") == 0)
185 {
186 sp.Parity = Parity.None;//不發(fā)生奇偶校驗(yàn)檢查
187 }
188 else if (s.CompareTo("奇校驗(yàn)") == 0)
189 {
190 sp.Parity = Parity.Odd;//設(shè)置奇校驗(yàn)
191 }
192 else if (s.CompareTo("偶校驗(yàn)") == 0)
193 {
194 sp.Parity = Parity.Even;//設(shè)置偶檢驗(yàn)
195 }
196 else
197 {
198 sp.Parity = Parity.None;
199 }
200
201 //6、設(shè)置超時(shí)讀取時(shí)間
202 sp.ReadTimeout = -1;
203
204 //7、打開串口
205 try
206 {
207 sp.Open();
208 isOpen = true;
209 }
210 catch(Exception)
211 {
212 lblStatus.Text = "打開串口錯(cuò)誤!";
213 }
214
215 }
216
217
218
219 /// <summary>
220 /// 【發(fā)送數(shù)據(jù)】
221 /// </summary>
222 /// <param name="sender"></param>
223 /// <param name="e"></param>
224 private void btnSend_Click(object sender, EventArgs e)
225 {
226 //發(fā)送串口數(shù)據(jù)
227
228 //1、檢查串口設(shè)置
229 if (!CheckPortSetting())
230 {
231 MessageBox.Show("串口未設(shè)置!", "錯(cuò)誤提示");
232 return;
233 }
234
235 2、檢查發(fā)送數(shù)據(jù)是否為空
236 if (!CheckSendData())
237 {
238 MessageBox.Show("請(qǐng)輸入要發(fā)送的數(shù)據(jù)!", "錯(cuò)誤提示");
239 return;
240 }
241
242 //3、設(shè)置
243 if (!isSetProperty)
244 {
245 SetPortProperty();
246 isSetProperty = true;
247 }
248
249 //4、寫串口數(shù)據(jù)
250 if (isOpen)
251 {
252 //寫出口數(shù)據(jù)
253 try
254 {
255 sp.Write(tbxSendData.Text);
256 tbxStatus.Text = "發(fā)送成功!";
257
258 tbxRecvData.Text += sp.ReadLine()+"
";
259 }
260 catch
261 {
262 tbxStatus.Text = "發(fā)送數(shù)據(jù)錯(cuò)誤";
263 }
264 }
265 else
266 {
267 MessageBox.Show("串口未打開", "錯(cuò)誤提示");
268 }
269
270
271 }
272
273 /// <summary>
274 /// 【讀取數(shù)據(jù)】
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 //讀取串口數(shù)據(jù)
285
286 tbxRecvData.Text+=sp.ReadLine()+"
";
287 }
288 catch(Exception)
289 {
290 lblStatus.Text="讀取串口時(shí)發(fā)生錯(cuò)誤!";
291 return;
292 }
293 }
294 else
295 {
296 MessageBox.Show("串口未打開!","錯(cuò)誤提示");
297 return;
298
299 }
300 }
301
302 }
303 }
Form1.cs
二、基于Modbus協(xié)議的數(shù)據(jù)發(fā)送和接收
這里主要是在前面的基礎(chǔ)上,把發(fā)送和接收的數(shù)據(jù)進(jìn)行格式化(符合Modbus的數(shù)據(jù)幀格式),如下左圖所示,右圖為加入Modbus協(xié)議的窗體,主要添加了命令幀的輸入框組:
2.1 獲取字節(jié)的的高位和低位:WORD_LO()、WORD_HI()
1 /// <summary>
2 /// 【獲取低位字節(jié)】
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 /// 【獲取高位字節(jié)】
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校驗(yàn)方法:CRC16()
1 /// <summary>
2 /// 【CRC校驗(yàn)】
3 /// </summary>
4 /// <param name="buffer">命令幀合適前6字節(jié)</param>
5 /// <param name="Sset">開始位</param>
6 /// <param name="Eset">結(jié)束位</param>
7 /// <returns>CRC校驗(yàn)碼</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 獲取數(shù)據(jù)幀,把需要發(fā)送的數(shù)據(jù)格式化成Modbus協(xié)議數(shù)據(jù)幀
1 /// <summary>
2 /// 【獲取讀數(shù)據(jù)命令,返回命令幀】
3 /// </summary>
4 /// <param name="mdaddr">地址碼</param>
5 /// <param name="R_CMD">功能碼</param>
6 /// <param name="min_reg">寄存器地址</param>
7 /// <param name="data_len">寄存器個(gè)數(shù)</param>
8 /// <param name="R_CMD_LEN">命令長(zhǎng)度</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 //主機(jī)命令幀格式
13 // 字節(jié) 功能描述 例子
14 //
15 // 1 地址碼 0x01
16 // 2 功能碼 0x03
17 // 3 寄存器地址高 0x00
18 // 4 寄存器地址低 0x00
19 // 5 寄存器個(gè)數(shù)高 0x00
20 // 6 寄存器個(gè)數(shù)低 0x02
21 // 7 CRC檢驗(yàn)碼低 0xC4
22 // 8 CRC校驗(yàn)碼高 0x0B
23
24 ushort crc;
25 byte[] message = new byte[8];
26
27 //設(shè)置模塊號(hào)
28 message[0] = mdaddr;
29 //設(shè)置命令字
30 message[1] = R_CMD;
31
32 //設(shè)置開始寄存器
33 message[2] = WORD_HI(min_reg);
34 message[3] = WORD_LO(min_reg);
35
36 //設(shè)置數(shù)據(jù)長(zhǎng)度
37 message[4] = WORD_HI(data_len);
38 message[5] = WORD_LO(data_len);
39
40 //設(shè)置 CRC
41 crc = CRC16(message, 0, R_CMD_LEN - 3);
42
43 message[6] = WORD_HI(crc);//CRC校驗(yàn)碼高位
44 message[7] = WORD_LO(crc);//CRC校驗(yàn)碼低位
45
46
47 return message;
48 }
2.6 對(duì)于DataReceived的使用
#1 設(shè)置委托和方法
1 private delegate void myDelegate(byte[] readBuffer);
1 /// <summary>
2 /// 【顯示接收返回的數(shù)據(jù)】
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) + "
";
9 }
#2 設(shè)置屬性:ReceivedBytesThreshold = 1
1 //1、設(shè)置串口的屬性 2 sp = new SerialPort(); 3 4 sp.ReceivedBytesThreshold = 1;//獲取DataReceived事件發(fā)生前內(nèi)部緩存區(qū)字節(jié)數(shù) 5 sp.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);//設(shè)置委托
#3 點(diǎn)擊“發(fā)送數(shù)據(jù)”按鈕的事件如下:
1 /// <summary>
2 /// 【發(fā)送數(shù)據(jù)】
3 /// </summary>
4 /// <param name="sender"></param>
5 /// <param name="e"></param>
6 private void btnSend_Click(object sender, EventArgs e)
7 {
8 //發(fā)送串口數(shù)據(jù)
9
10 //1、檢查串口設(shè)置
11 if (!CheckPortSetting())
12 {
13 MessageBox.Show("串口未設(shè)置!", "錯(cuò)誤提示");
14 return;
15 }
16
17 //2、檢查發(fā)送數(shù)據(jù)是否為空
18 //if (!CheckSendData())
19 //{
20 // MessageBox.Show("請(qǐng)輸入要發(fā)送的數(shù)據(jù)!", "錯(cuò)誤提示");
21 // return;
22 //}
23
24 //3、設(shè)置
25 if (!isSetProperty)
26 {
27 SetPortProperty();
28 isSetProperty = true;
29 }
30
31 //4、寫串口數(shù)據(jù)
32 if (isOpen)
33 {
34 //寫出口數(shù)據(jù)
35 try
36 {
37 //sp.Write(tbxSendData.Text);
38 tbxStatus.Text = "發(fā)送成功!";
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);//寄存器數(shù)量
46
47
48 //Modbus相關(guān)處理對(duì)象
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)+ "
";
54
55 }
56 catch
57 {
58 tbxStatus.Text = "發(fā)送數(shù)據(jù)錯(cuò)誤";
59 }
60 }
61 else
62 {
63 MessageBox.Show("串口未打開", "錯(cuò)誤提示");
64 }
65
66
67 }
2.7 附加代碼
#1 這里的MyModbus主要為Modbus相關(guān)一些操作,包括把發(fā)送數(shù)據(jù)封裝成Modbus數(shù)據(jù)幀等。
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 串口調(diào)試
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 /// 【獲取讀數(shù)據(jù)命令,返回命令幀】
79 /// </summary>
80 /// <param name="mdaddr">地址碼</param>
81 /// <param name="R_CMD">功能碼</param>
82 /// <param name="min_reg">寄存器地址</param>
83 /// <param name="data_len">寄存器個(gè)數(shù)</param>
84 /// <param name="R_CMD_LEN">命令長(zhǎng)度</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 //主機(jī)命令幀格式
89 // 字節(jié) 功能描述 例子
90 //
91 // 1 地址碼 0x01
92 // 2 功能碼 0x03
93 // 3 寄存器地址高 0x00
94 // 4 寄存器地址低 0x00
95 // 5 寄存器個(gè)數(shù)高 0x00
96 // 6 寄存器個(gè)數(shù)低 0x02
97 // 7 CRC檢驗(yàn)碼低 0xC4
98 // 8 CRC校驗(yàn)碼高 0x0B
99
100 ushort crc;
101 byte[] message = new byte[8];
102
103 //設(shè)置模塊號(hào)
104 message[0] = mdaddr;
105 //設(shè)置命令字
106 message[1] = R_CMD;
107
108 //設(shè)置開始寄存器
109 message[2] = WORD_HI(min_reg);
110 message[3] = WORD_LO(min_reg);
111
112 //設(shè)置數(shù)據(jù)長(zhǎng)度
113 message[4] = WORD_HI(data_len);
114 message[5] = WORD_LO(data_len);
115
116 //設(shè)置 CRC
117 crc = CRC16(message, 0, R_CMD_LEN - 3);
118
119 message[6] = WORD_HI(crc);//CRC校驗(yàn)碼高位
120 message[7] = WORD_LO(crc);//CRC校驗(yàn)碼低位
121
122
123 return message;
124 }
125
126 /// <summary>
127 /// 【格式化輸出,校驗(yàn)讀取的數(shù)據(jù)】
128 /// </summary>
129 /// <param name="readBuffer"></param>
130 /// <returns></returns>
131 public string SetText(byte[] readBuffer)
132 {
133 //將byte 轉(zhuǎn)換成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校驗(yàn)
139 {
140 return ToHexString(readBuffer);
141 }
142 else
143 {
144 return "CRC校驗(yàn)錯(cuò)誤";
145 }
146 }
147
148 return "程序出錯(cuò)";
149 }
150
151
152 /// <summary>
153 /// 【CRC校驗(yàn)】
154 /// </summary>
155 /// <param name="buffer">命令幀合適前6字節(jié)</param>
156 /// <param name="Sset">開始位</param>
157 /// <param name="Eset">結(jié)束位</param>
158 /// <returns>CRC校驗(yàn)碼</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變量的高位字節(jié)、低位字節(jié)
204 /// <summary>
205 /// 【獲取低位字節(jié)】
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 /// 【獲取高位字節(jié)】
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 串口調(diào)試
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 //數(shù)據(jù)接收使用的代理
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 //設(shè)置窗口大小固定
36 this.MaximumSize = this.Size;
37 this.MinimumSize = this.Size;
38
39 //1、設(shè)置串口下拉列表
40 for ( int i = 0; i < 10; i++ )
41 {
42 cbxCOMPort.Items.Add("COM" + (i + 1).ToString());
43 }
44 cbxCOMPort.SelectedIndex = 2;//默認(rèn)選項(xiàng)
45
46
47 //2、設(shè)置常用波特率
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、設(shè)置奇偶檢驗(yàn)
71 cbxParity.Items.Add("無(wú)");
72 cbxParity.Items.Add("奇校驗(yàn)");
73 cbxParity.Items.Add("偶校驗(yàn)");
74 cbxParity.SelectedIndex = 0;
75
76 //5、設(shè)置數(shù)據(jù)位
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 /// 【串口檢測(cè)按鈕】
89 /// </summary>
90 /// <param name="sender"></param>
91 /// <param name="e"></param>
92 private void btnCheckCOM_Click(object sender, EventArgs e)
93 {
94 //1、檢測(cè)哪些端口可用
95 cbxCOMPort.Items.Clear();
96 cbxCOMPort.Text = "";
97
98 lblStatus.Text = "執(zhí)行中...";
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);//讀取串口數(shù)據(jù)
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 /// 【顯示接收返回的數(shù)據(jù)】
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) + "
";
155 tbxRecvData.Text += "
";
156 }
157
158 /// <summary>
159 /// 【檢測(cè)端口設(shè)置】
160 /// </summary>
161 /// <returns></returns>
162 private bool CheckPortSetting()
163 {
164 //檢測(cè)端口設(shè)置
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 /// 【檢測(cè)發(fā)送數(shù)據(jù)是否為空】
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 /// 【設(shè)置串口屬性】
184 /// </summary>
185 private void SetPortProperty()
186 {
187 //1、設(shè)置串口的屬性
188 sp = new SerialPort();
189
190 sp.ReceivedBytesThreshold = 1;//獲取DataReceived事件發(fā)生前內(nèi)部緩存區(qū)字節(jié)數(shù)
191 sp.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);//設(shè)置委托
192
193 sp.PortName = cbxCOMPort.Text.Trim();
194
195 //2、設(shè)置波特率
196 sp.BaudRate = Convert.ToInt32( cbxBaudRate.Text.Trim());
197
198 //3、設(shè)置停止位
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個(gè)停止位
208 }
209 else if (f == 2)
210 {
211 sp.StopBits = StopBits.Two;//表示使用兩個(gè)停止位
212 }
213 else
214 {
215 sp.StopBits = StopBits.One;//默認(rèn)使用一個(gè)停止位
216 }
217
218 //4、設(shè)置數(shù)據(jù)位
219 sp.DataBits = Convert.ToInt16(cbxDataBits.Text.Trim());
220
221 //5、設(shè)置奇偶校驗(yàn)位
222 string s = cbxParity.Text.Trim();
223 if (s.CompareTo("無(wú)") == 0)
224 {
225 sp.Parity = Parity.None;//不發(fā)生奇偶校驗(yàn)檢查
226 }
227 else if (s.CompareTo("奇校驗(yàn)") == 0)
228 {
229 sp.Parity = Parity.Odd;//設(shè)置奇校驗(yàn)
230 }
231 else if (s.CompareTo("偶校驗(yàn)") == 0)
232 {
233 sp.Parity = Parity.Even;//設(shè)置偶檢驗(yàn)
234 }
235 else
236 {
237 sp.Parity = Parity.None;
238 }
239
240 //6、設(shè)置超時(shí)讀取時(shí)間
241 sp.ReadTimeout = -1;
242
243 //7、打開串口
244 try
245 {
246 sp.Open();
247 isOpen = true;
248 }
249 catch(Exception)
250 {
251 lblStatus.Text = "打開串口錯(cuò)誤!";
252 }
253
254 }
255
256
257
258 /// <summary>
259 /// 【發(fā)送數(shù)據(jù)】
260 /// </summary>
261 /// <param name="sender"></param>
262 /// <param name="e"></param>
263 private void btnSend_Click(object sender, EventArgs e)
264 {
265 //發(fā)送串口數(shù)據(jù)
266
267 //1、檢查串口設(shè)置
268 if (!CheckPortSetting())
269 {
270 MessageBox.Show("串口未設(shè)置!", "錯(cuò)誤提示");
271 return;
272 }
273
274 //2、檢查發(fā)送數(shù)據(jù)是否為空
275 //if (!CheckSendData())
276 //{
277 // MessageBox.Show("請(qǐng)輸入要發(fā)送的數(shù)據(jù)!", "錯(cuò)誤提示");
278 // return;
279 //}
280
281 //3、設(shè)置
282 if (!isSetProperty)
283 {
284 SetPortProperty();
285 isSetProperty = true;
286 }
287
288 //4、寫串口數(shù)據(jù)
289 if (isOpen)
290 {
291 //寫出口數(shù)據(jù)
292 try
293 {
294 //sp.Write(tbxSendData.Text);
295 tbxStatus.Text = "發(fā)送成功!";
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);//寄存器數(shù)量
303
304
305 //Modbus相關(guān)處理對(duì)象
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)+ "
";
311
312 }
313 catch
314 {
315 tbxStatus.Text = "發(fā)送數(shù)據(jù)錯(cuò)誤";
316 }
317 }
318 else
319 {
320 MessageBox.Show("串口未打開", "錯(cuò)誤提示");
321 }
322
323
324 }
325
326 /// <summary>
327 /// 【讀取數(shù)據(jù)】
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 //讀取串口數(shù)據(jù)
338
339 tbxRecvData.Text+=sp.ReadLine()+"
";
340 }
341 catch(Exception)
342 {
343 lblStatus.Text="讀取串口時(shí)發(fā)生錯(cuò)誤!";
344 return;
345 }
346 }
347 else
348 {
349 MessageBox.Show("串口未打開!","錯(cuò)誤提示");
350 return;
351
352 }
353 }
354
355
356 }
357 }
Form1.cs
三、最后說(shuō)一句
由于需要查詢大量資料,有些代碼是引用別人的,所以這里要感謝那些分享的人,秉著分享精神,希望可以幫助更多的人。
如果發(fā)現(xiàn)有什么錯(cuò)誤,或者建議,請(qǐng)留言,謝謝!
總結(jié)
以上是生活随笔為你收集整理的基于Modbus的C#串口调试开发的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 腰酸疼的原因(女人腰酸痛是妇科病吗)
- 下一篇: 团队名称的英文缩写(有意义的英文缩写团队