上位機至關於一個軟件系統,能夠用於接收數據、控制數據。便可以對接收到的數據直接發送操控命令來操做數據。上位機能夠接收下位機的信號。下位機是一個控制器,是直接控制設備獲取設備情況的計算機。上位機發出的命令首先給下位機,下位機再根據此命令解釋成相應時序信號直接控制相應設備。下位機不時讀取設備狀態數據(通常爲模擬量),轉換成數字信號反饋給上位機。上位機不能夠單獨使用,而下位機能夠單獨使用。前端
串口至關於硬件類型的接口。好比無線傳感節點發送信號到匯聚節點,匯聚節點經過串口將數據傳到計算機中的上位機中,上位機接收信息,並處理。編程
串口是按位(bit)發送和接收字節。串口通訊最重要的參數是波特率、數據位、中止位和奇偶校驗。對於兩個進行通訊的端口,這些參數必須匹配。c#
a,波特率:這是一個衡量符號傳輸速率的參數。後端
b,數據位:這是衡量通訊中實際數據位的參數。數組
c,中止位:用於表示單個包的最後一位。典型的值爲1,1.5和2位。函數
d,奇偶校驗位:在串口通訊中一種簡單的檢錯方式。測試
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 serial2 { public partial class Form1 : Form { SerialPort s = new SerialPort(); //實例化一個串口對象,在前端控件中能夠直接拖過來,但最好是在後端代碼中寫代碼,這樣複製到其餘地方不會出錯。s是一個串口的句柄 public Form1() { InitializeComponent(); Control.CheckForIllegalCrossThreadCalls = false; //防止跨線程訪問出錯,好多地方會用到 button1.Text = "打開串口"; int[] item = { 9600,115200}; //定義一個Item數組,遍歷item中每個變量a,增長到comboBox2的列表中 foreach (int a in item) { comboBox2.Items.Add(a.ToString()); } comboBox2.SelectedItem = comboBox2.Items[1]; //默認爲列表第二個變量 } private void Form1_Load(object sender, EventArgs e) //窗體事件要先配置端口信息。 { string[] ports = SerialPort.GetPortNames(); comboBox1.Items.AddRange(ports); comboBox1.SelectedItem=comboBox1.Items[0]; //Array.Sort(ports); } 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; button1.Text = "關閉串口"; //MessageBox.Show("串口已打開"); } else { s.Close(); s.DataReceived -= s_DataReceived; button1.Text = "打開串口"; } } catch (Exception ee) { MessageBox.Show(ee.ToString()); } } void s_DataReceived(object sender, SerialDataReceivedEventArgs e) //數據接收事件,讀到數據的長度賦值給count,若是是8位(節點內部編程規定好的),就申請一個byte類型的buff數組,s句柄來讀數據 { int count =s.BytesToRead; string str=null ; if (count == 8) { byte[] buff = new byte[count]; s.Read(buff, 0, count); foreach (byte item in buff) //讀取Buff中存的數據,轉換成顯示的十六進制數 { str += item.ToString("X2")+" "; } richTextBox1.Text =System.DateTime.Now.ToString()+": "+ str + "\n" + richTextBox1.Text; //這是跨線程訪問richtextbox,原程序和DataReceived事件是兩個不一樣的線程同時在執行 if (buff[0] == 0x04) //若是節點是04發來的數據 { ID.Text = buff[0].ToString(); //這下面是上位機右邊那一段,用來顯示處理好的數據的溫度、溼度、光照、灰塵、ID信息的。buff【0】中存的是數據的ID信息,顯示在ID的Label上面 switch (buff[2]) //判斷數據類型 buff【0】和buff【1】表明ID的低位和高位,同理2和3表明數據類型的低位和高位,當2和3的值爲1時,4和5表明溫度,6和7表明溼度; { case 0x01: //當2和3的值爲1,4和5是溫度,6和7是溼度 { Tem.Text = (buff[5] * 4 + buff[4] * 0.05 - 30).ToString(); Hum.Text = (buff[6] + buff[7]).ToString(); break; } case 0x02://6和7是光照 { Light.Text = (buff[6] + buff[7]).ToString(); break; } case 0x04://6和7是灰塵 { Dust.Text = (buff[6] + buff[7]).ToString(); break; } default: break; } } } } private void button3_Click(object sender, EventArgs e) //每次發一個字節 { string[] sendbuff = richTextBox2.Text.Split(); //分割輸入的字符串,判斷有多少個字節須要發送 Debug.WriteLine("發送字節數:"+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); } } 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);//這裏只顯示04節點的信息 s.Write(buff, 0, count); } } }
(以上規則均是本實驗室節點內部自定義規則,測試的,外面的相應要改)spa
1)在程序可能會遇到錯誤的地方,用try+兩個Tab鍵,將代碼寫入try中。好比本例子中的代碼:線程
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; button1.Text = "關閉串口"; //MessageBox.Show("串口已打開"); } else { s.Close(); s.DataReceived -= s_DataReceived; button1.Text = "打開串口"; } } catch (Exception ee) { MessageBox.Show(ee.ToString()); } }
若是代碼沒有寫入try中,則可能出現的一種狀況是好比有兩個上位機,同時佔用同一個串口,則就會衝突,會出錯。程序就會終止,整個進程結束。而若是寫入try中,而且把拋出異常的catch代碼實例化,即捕獲異常要實例化一個句柄,這樣程序遇到error就不會終止,而會出現報錯的緣由。以下圖,個人這個上位機和網上下載的一個上位機同時佔用COM3串口(網上下載的先佔用COM3),這時個人上位機在打開串口時會出現報錯。code
2)就我這個上位機而言,須要有打開串口和關閉串口兩個button按鈕,可是考慮到佔地方,固然最重要的仍是若是用兩個按鈕來表示,當你按下打開串口,若是忘了是否打開,則是看不出來是否是打開的,因此能夠合併爲一個button控件。(代碼仍是用上面那一段的代碼)。(感受很神奇啊)。在button1_Click事件中,先點擊button,若是串口是關閉的,則打開串口,而後把button1.Text的值賦值爲「關閉串口」,若是串口原本是關閉的,則點擊按鈕會把button1.Text的值賦值爲「打開串口」,同時把接收的數據清空。感受這個方法真的很不錯!嘿嘿
3)當輸入一個變量或方法什麼的,它全部有的會自動出如今一個列表,這時,「正方體」表明「方法」,「小鉗子」表明「變量」,「閃電」表明「事件」。
4) 產生對象的事件時
好比輸入s.自動會出現DataReceived事件,再輸入「+=」就會有如上圖提示,按Tab鍵。而後又會以下圖提示
再次按tab鍵,就會自動生成DataReceived事件處理函數。