您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關利用C# 項目實現(xiàn)一個串口監(jiān)視上位機功能,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
實現(xiàn)上位機和下位機之間的通信,通常使用的是串口通信,接下來實現(xiàn)一個通過上位機和串口調(diào)試助手來完成串口通信測試。
首先創(chuàng)建一個WInfrom窗體應用工程文件,在創(chuàng)建好的工程下面,通過工具箱中已有的控件完成界面的搭建,如下圖所示,為了方便初學者容易看懂程序,下圖將控件的命名一并標注出來:
直接進入正題,將完整的工程代碼黏貼出來:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.IO.Ports; using System.Diagnostics; namespace Tem_Hum_Monitorring { public partial class Form1 : Form { //實例化串口 SerialPort s = new SerialPort(); public Form1() { InitializeComponent(); Control.CheckForIllegalCrossThreadCalls = false; button1.Text = "打開串口"; int[] item = { 9600,115200}; //遍歷 foreach (int a in item) { comboBox2.Items.Add(a.ToString()); } comboBox2.SelectedItem = comboBox2.Items[1]; } private void Form1_Load(object sender, EventArgs e) { portInit(); } /// <summary> /// 串口初始化 /// </summary> private void portInit() { string[] ports = SerialPort.GetPortNames(); comboBox1.Items.AddRange(ports); comboBox1.SelectedItem = comboBox1.Items[0]; } #region 開關串口 private void button1_Click(object sender, EventArgs e) { try { if (!s.IsOpen) { s.PortName = comboBox1.SelectedItem.ToString(); s.BaudRate = Convert.ToInt32(comboBox2.SelectedItem.ToString()); s.Open(); s.DataReceived += s_DataReceived; //"+="代表指定響應事件時要調(diào)用的方法 button1.Text = "關閉串口"; } else { s.Close(); s.DataReceived -= s_DataReceived; button1.Text = "打開串口"; } } catch(Exception ee) { MessageBox.Show(ee.ToString()); } } #endregion #region 串口接收 void s_DataReceived(object sender, SerialDataReceivedEventArgs e) { int count = s.BytesToRead; string str = null; if (count == 8) { //數(shù)據(jù)解析 byte[] buff = new byte[count]; s.Read(buff, 0, count); foreach (byte item in buff) { str += item.ToString("X2") + " "; } richTextBox1.Text = "[" + System.DateTime.Now.ToString() + "] " + str + "\n" + richTextBox1.Text; if (buff[0] == 0x04) { ID.Text = buff[0].ToString(); switch (buff[2]) { case 0x01: { Tem.Text = (buff[5] * 4 + buff[4] * 0.05 - 30).ToString(); Hum.Text = (buff[6] + buff[7]).ToString(); break; } case 0x02: { Light.Text = (buff[6] + buff[7]).ToString(); break; } case 0x04: { Dust.Text = (buff[6] + buff[7]).ToString(); break; } default: break; } } } else { //當接收數(shù)據(jù)不在設定的數(shù)據(jù)位范圍之內(nèi)時,會出現(xiàn)接受到的數(shù)據(jù)一直保存在接收緩存區(qū)之內(nèi),后續(xù)每次接手數(shù)據(jù)都會將上一次的數(shù)據(jù)進行疊加,造成只能通過關閉串口的方法來清除緩沖區(qū)的數(shù)據(jù) s.DiscardInBuffer(); //丟棄來自串行驅(qū)動程序的接收緩沖區(qū)的數(shù)據(jù) } } #endregion #region 串口發(fā)送 private void button3_Click(object sender, EventArgs e) { string[] sendbuff = richTextBox2.Text.Split(); Debug.WriteLine("發(fā)送字節(jié)數(shù):" + sendbuff.Length); foreach (string item in sendbuff) { int count = 1; byte[] buff = new byte[count]; buff[0] = byte.Parse(item, System.Globalization.NumberStyles.HexNumber); s.Write(buff,0,count); } } #endregion private void button2_Click(object sender, EventArgs e) { int count = 1; byte[] buff = new byte[count]; buff[0] = byte.Parse("04", System.Globalization.NumberStyles.HexNumber); s.Write(buff, 0, count); } } }
在Winfrom窗體設計中,實現(xiàn)串口可以通過工具箱中的串口控件來實現(xiàn),不過一般推薦直接通過代碼來實例化串口,實例化串口需使用如下代碼來實現(xiàn):
//實例化串口 SerialPort s = new SerialPort();
串口初始化可以在窗體的Load函數(shù)中實現(xiàn),以下初始化可以自動化取當前設備中的存在的串口,包括真實串口和虛擬串口:
private void Form1_Load(object sender, EventArgs e) { portInit(); } /// <summary> /// 串口初始化 /// </summary> private void portInit() { string[] ports = SerialPort.GetPortNames(); comboBox1.Items.AddRange(ports); comboBox1.SelectedItem = comboBox1.Items[0]; }
通過對開關按鍵button1控件的點擊事件,實現(xiàn)串口的開關,通過對控件的文字修改,可以實現(xiàn)一個控件機能實現(xiàn)開又能實現(xiàn)關串口的作用:
#region 開關串口 private void button1_Click(object sender, EventArgs e) { try { if (!s.IsOpen) { s.PortName = comboBox1.SelectedItem.ToString(); s.BaudRate = Convert.ToInt32(comboBox2.SelectedItem.ToString()); s.Open(); s.DataReceived += s_DataReceived; //"+="代表指定響應事件時要調(diào)用的方法 button1.Text = "關閉串口"; } else { s.Close(); s.DataReceived -= s_DataReceived; button1.Text = "打開串口"; } } catch(Exception ee) { MessageBox.Show(ee.ToString()); } } #endregion
串口數(shù)據(jù)接收和數(shù)據(jù)解析,首先獲取數(shù)據(jù)接收緩存區(qū)數(shù)據(jù)的字節(jié)長度,通過確認長度是否是設定中的長度大小,如果是設定的8位數(shù)據(jù)長度則對接收的數(shù)據(jù)進行解析:
#region 串口接收 void s_DataReceived(object sender, SerialDataReceivedEventArgs e) { int count = s.BytesToRead; string str = null; if (count == 8) { //數(shù)據(jù)解析 byte[] buff = new byte[count]; s.Read(buff, 0, count); foreach (byte item in buff) { str += item.ToString("X2") + " "; } richTextBox1.Text = "[" + System.DateTime.Now.ToString() + "] " + str + "\n" + richTextBox1.Text; if (buff[0] == 0x04) { ID.Text = buff[0].ToString(); switch (buff[2]) { case 0x01: { Tem.Text = (buff[5] * 4 + buff[4] * 0.05 - 30).ToString(); Hum.Text = (buff[6] + buff[7]).ToString(); break; } case 0x02: { Light.Text = (buff[6] + buff[7]).ToString(); break; } case 0x04: { Dust.Text = (buff[6] + buff[7]).ToString(); break; } default: break; } } } else { //當接收數(shù)據(jù)不在設定的數(shù)據(jù)位范圍之內(nèi)時,會出現(xiàn)接受到的數(shù)據(jù)一直保存在接收緩存區(qū)之內(nèi),后續(xù)每次接手數(shù)據(jù)都會將上一次的數(shù)據(jù)進行疊加,造成只能通過關閉串口的方法來清除緩沖區(qū)的數(shù)據(jù) s.DiscardInBuffer(); //丟棄來自串行驅(qū)動程序的接收緩沖區(qū)的數(shù)據(jù) } } #endregion
當接收到的數(shù)據(jù)長度不等于8的時候,將丟棄來自串行驅(qū)動程序的接收緩沖區(qū)的數(shù)據(jù),接下來通過斷點調(diào)試來分析丟棄緩沖區(qū)和不丟棄緩沖區(qū)數(shù)據(jù)兩種情況進行仿真,分析如下幾點。
使用串口助手給上位機發(fā)送數(shù)據(jù)數(shù)據(jù)位長度為8位的數(shù)據(jù),串口調(diào)試助手和上位機的終端的顯示界面如下,發(fā)送端數(shù)據(jù)和接收端數(shù)據(jù)一樣,并未出現(xiàn)異常:
將串口調(diào)試助手發(fā)送數(shù)據(jù)位修改成9位之后,進行發(fā)送,可以發(fā)現(xiàn)上位機并未接收到相關的數(shù)據(jù):
接著修改串口調(diào)試助手的發(fā)送數(shù)據(jù)位,修改成8位,可以發(fā)現(xiàn)上位機尚未能接收到來自串口調(diào)試助手發(fā)來的數(shù)據(jù),這是為什么呢?
接下來將通過斷點逐步進行調(diào)試,來解釋是為啥上位機沒有接收到調(diào)試助手發(fā)來的數(shù)據(jù),當串口調(diào)試助手發(fā)來的數(shù)據(jù)長度位9位時,通過監(jiān)視器可以查看到接收緩沖器中的數(shù)據(jù)長度長度是9
第一次點擊完發(fā)送之后,上位機未能成功接收到數(shù)據(jù),我們就會好奇,并且一般都會點擊第二次、第三次、甚至一直點下去,觀察是否會出現(xiàn)啥異?,F(xiàn)象,當點擊第二次時,通過監(jiān)視窗口,可以觀察到到串口緩沖區(qū)的數(shù)據(jù)長度變成了18,這是因為緩沖區(qū)將上一次接收的數(shù)據(jù)給保留了下來并沒有刪除,就算下次發(fā)送的數(shù)據(jù)長度為8位的時候,也一樣是通過疊加的方式將其保存到緩沖區(qū),這樣就會造成緩沖區(qū)的數(shù)據(jù)位長度會一直大于8;如果不通過s.DiscardInBuffer()方法丟棄來自串行驅(qū)動程序的接收緩沖區(qū)的數(shù)據(jù),就只能通過關閉串口然后重新打開相應的串口來實現(xiàn)緩沖區(qū)的數(shù)據(jù)清除。
使用s.DiscardInBuffer()對不符合長度的數(shù)據(jù)進行丟棄,實現(xiàn)的效果如下所示:
關于利用C# 項目實現(xiàn)一個串口監(jiān)視上位機功能就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。