乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      用C#一步步寫串口通信

       華燈初放l 2013-08-29

      最近在公司讓用C#寫一個串口調(diào)試的工具,要求向串口中輸入16進制數(shù)據(jù)或字符串。下面我將這次遇到的問題和解決方法奉獻出來,目的是和同行交流,回饋網(wǎng)友們提供的幫助,也是為了自己對知識加深一下鞏固。

      我們來看具體的實現(xiàn)步驟。

      公司要求實現(xiàn)以下幾個功能:

      1):實現(xiàn)兩臺計算機之前的串口通信,以16進制形式和字符串兩種形式傳送和接收。

      2):根據(jù)需要設(shè)置串口通信的必要參數(shù)。

      3):定時發(fā)送數(shù)據(jù)。

      4):保存串口設(shè)置。

      看著好像挺復(fù)雜,其實都是紙老虎,一戳就破,前提是你敢去戳。我盡量講的詳細一些,爭取說到每個知識點。

      在編寫程序前,需要將你要測試的COM口短接,就是收發(fā)信息都在本地計算機,短接的方式是將COM口的2、3號針接起來。COM口各針的具體作用,度娘是這么說的:COM口。記住2、3針連接一定要連接牢固,我就是因為接觸不良,導(dǎo)致本身就不通,白白花掉了一大半天時間調(diào)試代碼。

      下面給出主要的操作界面,如下:

      順便,我將所有控件對應(yīng)的代碼名字也附上了,相信對初學(xué)者來說,再看下面的代碼會輕松很多。控件名字命名的方法是“控件名+作用”的形式,例如“打開串口”的開關(guān)按鈕,其名字是btnSwitch  (btn就是button的簡寫了)。我認為這種命名控件的方式比較好,建議大家使用,如果你有好的命名方式,希望你能告訴我!

      下面我們將各個功能按照從主到次的順序逐個實現(xiàn)。(我分塊給出代碼實現(xiàn),詳細代碼見鏈接:《C#串口通信工具》)

      一、獲取計算機的COM口總個數(shù),將它們列為控件cbSerial的候選項,并將第一個設(shè)為cbSerial的默認選項。

      這部分是在窗體加載時完成的。請看代碼:

      (很多信息代碼的注釋里講的很清楚,我就不贅述了。)

      [csharp] view plaincopyprint?

      //檢查是否含有串口  

      string[] str = SerialPort.GetPortNames();  

      if (str == null)  

      MessageBox.Show("本機沒有串口!", "Error");  

      return;  

      //添加串口項目  

      foreach (string s in System.IO.Ports.SerialPort.GetPortNames())  

      //獲取有多少個COM口  

      cbSerial.Items.Add(s);  

      //串口設(shè)置默認選擇項  

      cbSerial.SelectedIndex = 0;         //設(shè)置cbSerial的默認選項  

      二、“串口設(shè)置”

      這面我沒代碼編程,直接從窗體上按照串口信息設(shè)置就行。我們僅設(shè)置它們的默認選項,但這里我用到了ini文件,暫時不講,我們先以下面形式設(shè)置默認。

      [csharp] view plaincopyprint?

      cbBaudRate.SelectedIndex = 5;  

      cbDataBits.SelectedIndex = 3;  

      cbStop.SelectedIndex = 0;  

      cbParity.SelectedIndex = 0;  

      radio1.Checked = true;  //發(fā)送數(shù)據(jù)的“16進制”單選按鈕(這里我忘了改名,現(xiàn)在看著很不舒服?。?nbsp; 

      rbRcvStr.Checked = true;  

      三、打開串口

      在發(fā)送信息之前,我們需要根據(jù)選中的選項設(shè)置串口信息,并設(shè)置一些控件的屬性,最后將串口打開。

      [csharp] view plaincopyprint?

      private void btnSwitch_Click(object sender, EventArgs e)  

      //sp1是全局變量。 SerialPort sp1 = new SerialPort();  

      if (!sp1.IsOpen)  

      try  

      //設(shè)置串口號  

      string serialName = cbSerial.SelectedItem.ToString();  

      sp1.PortName = serialName;  

      //設(shè)置各“串口設(shè)置”  

      string strBaudRate = cbBaudRate.Text;  

      string strDateBits = cbDataBits.Text;  

      string strStopBits = cbStop.Text;  

      Int32 iBaudRate = Convert.ToInt32(strBaudRate);  

      Int32 iDateBits = Convert.ToInt32(strDateBits);  

      sp1.BaudRate = iBaudRate;       //波特率  

      sp1.DataBits = iDateBits;       //數(shù)據(jù)位  

      switch (cbStop.Text)            //停止位  

      case "1":  

      sp1.StopBits = StopBits.One;  

      break;  

      case "1.5":  

      sp1.StopBits = StopBits.OnePointFive;  

      break;  

      case "2":  

      sp1.StopBits = StopBits.Two;  

      break;  

      default:  

      MessageBox.Show("Error:參數(shù)不正確!", "Error");  

      break;  

      switch (cbParity.Text)             //校驗位  

      case "無":  

      sp1.Parity = Parity.None;  

      break;  

      case "奇校驗":  

      sp1.Parity = Parity.Odd;  

      break;  

      case "偶校驗":  

      sp1.Parity = Parity.Even;  

      break;  

      default:  

      MessageBox.Show("Error:參數(shù)不正確!", "Error");  

      break;  

      if (sp1.IsOpen == true)//如果打開狀態(tài),則先關(guān)閉一下  

      sp1.Close();  

      //狀態(tài)欄設(shè)置  

      tsSpNum.Text = "串口號:" + sp1.PortName + "|";  

      tsBaudRate.Text = "波特率:" + sp1.BaudRate + "|";  

      tsDataBits.Text = "數(shù)據(jù)位:" + sp1.DataBits + "|";  

      tsStopBits.Text = "停止位:" + sp1.StopBits + "|";  

      tsParity.Text = "校驗位:" + sp1.Parity + "|";  

      //設(shè)置必要控件不可用  

      cbSerial.Enabled = false;  

      cbBaudRate.Enabled = false;  

      cbDataBits.Enabled = false;  

      cbStop.Enabled = false;  

      cbParity.Enabled = false;  

      sp1.Open();     //打開串口  

      btnSwitch.Text = "關(guān)閉串口";  

      catch (System.Exception ex)  

      MessageBox.Show("Error:" + ex.Message, "Error");  

      return;  

      else  

      //狀態(tài)欄設(shè)置  

      tsSpNum.Text = "串口號:未指定|";  

      tsBaudRate.Text = "波特率:未指定|";  

      tsDataBits.Text = "數(shù)據(jù)位:未指定|";  

      tsStopBits.Text = "停止位:未指定|";  

      tsParity.Text = "校驗位:未指定|";  

      //恢復(fù)控件功能  

      //設(shè)置必要控件不可用  

      cbSerial.Enabled = true;  

      cbBaudRate.Enabled = true;  

      cbDataBits.Enabled = true;  

      cbStop.Enabled = true;  

      cbParity.Enabled = true;  

      sp1.Close();    //關(guān)閉串口  

      btnSwitch.Text = "打開串口";  

      四、發(fā)送信息

      因為這里涉及到字符的轉(zhuǎn)換,難點在于,在發(fā)送16進制數(shù)據(jù)時,如何將文本框中的字符數(shù)據(jù)在內(nèi)存中以同樣的形式表現(xiàn)出來,例如我們輸入16進制的“eb 90”顯示到內(nèi)存中,也就是如下形式:

      或輸入我們想要的任何字節(jié),如上面的“12 34 56 78 90”.

      內(nèi)存中的數(shù)據(jù)時16進制顯示的,而我們輸入的數(shù)據(jù)時字符串,我們需要將字符串轉(zhuǎn)換為對應(yīng)的16進制數(shù)據(jù),然后將這個16進制數(shù)據(jù)轉(zhuǎn)換為字節(jié)數(shù)據(jù),用到的主要方法是:

      Convert.ToInt32  (String, Int32);

      Convert.ToByte  (Int32);

      這是我想到的,如果你有好的方法,希望你能告訴我。下面看代碼:

      [csharp] view plaincopyprint?

      private void btnSend_Click(object sender, EventArgs e)  

      if (!sp1.IsOpen) //如果沒打開  

      MessageBox.Show("請先打開串口!", "Error");  

      return;  

      String strSend = txtSend.Text;  

      if (radio1.Checked == true) //“16進制發(fā)送” 按鈕   

      //處理數(shù)字轉(zhuǎn)換,目的是將輸入的字符按空格、“,”等分組,以便發(fā)送數(shù)據(jù)時的方便(此處轉(zhuǎn)的比較麻煩,有高見者,請指點!)  

      string sendBuf = strSend;  

      string sendnoNull = sendBuf.Trim();  

      string sendNOComma = sendnoNull.Replace(',', ' ');    //去掉英文逗號  

      string sendNOComma1 = sendNOComma.Replace(',', ' '); //去掉中文逗號  

      string strSendNoComma2 = sendNOComma1.Replace("0x", "");   //去掉0x  

      strSendNoComma2.Replace("0X", "");   //去掉0X  

      string[] strArray = strSendNoComma2.Split(' ');  

      //strArray數(shù)組中會出現(xiàn)“”空字符的情況,影響下面的賦值操作,故將byteBufferLength相應(yīng)減小  

      int byteBufferLength = strArray.Length;  

      for (int i = 0; i <strarray.length; i++ )  < p="">

      if (strArray[i]=="")  

      byteBufferLength--;  

      byte[] byteBuffer = new byte[byteBufferLength];  

      int ii = 0;   //用于給byteBuffer賦值  

      for (int i = 0; i < strArray.Length; i++)        //對獲取的字符做相加運算  

      Byte[] bytesOfStr = Encoding.Default.GetBytes(strArray[i]);  

      int decNum = 0;  

      if (strArray[i] == "")  

      continue;  

      else  

      decNum = Convert.ToInt32(strArray[i], 16); //atrArray[i] == 12時,temp == 18   

      try    //防止輸錯,使其只能輸入一個字節(jié)的字符,即只能在txtSend里輸入 “eb 90”等字符串,不能輸入“123 2345”等超出字節(jié)范圍的數(shù)字  

      byteBuffer[ii] = Convert.ToByte(decNum);          

      catch (System.Exception ex)  

      MessageBox.Show("字節(jié)越界,請逐個字節(jié)輸入!", "Error");  

      return;  

      ii++;      

      sp1.Write(byteBuffer, 0, byteBuffer.Length);  

      else        //以字符串形式發(fā)送時   

      sp1.WriteLine(txtSend.Text);    //寫入數(shù)據(jù)  

      五、數(shù)據(jù)的接收

      亮點來了,看到這里,如果你還沒吐(可能是我的代碼比較拙劣!),那么下面的知識點對你也不成問題。

      這里需要用到 委托 的知識,我是搞C/C++出身,剛碰到這個知識點還真有點不適應(yīng)。為了不偏離主題,關(guān)于委托,我僅給出兩條比較好的鏈接,需要的網(wǎng)友可以去加深學(xué)習(xí):C#委托、訂閱委托事件。

      在窗體加載時就訂閱上委托是比較好的,所以在Form1_Load中添加以下代碼:

      [csharp] view plaincopyprint?

      Control.CheckForIllegalCrossThreadCalls = false;    //意圖見解釋  

      sp1.DataReceived += new SerialDataReceivedEventHandler(sp1_DataReceived); //訂閱委托        注意,因為自.net 2.0以后加強了安全機制,,不允許在winform中直接跨線程(事件觸發(fā)需要產(chǎn)生一個線程處理)訪問控件的屬性,第一條代碼的意圖是說在這個類中我們強制不檢查跨線程的調(diào)用是否合法。處理這種問題的解決方案有很多,具體可參閱以下內(nèi)容:解決方案。

      好了,訂閱委托之后,我們就可以處理接收數(shù)據(jù)的事件了。

      [csharp] view plaincopyprint?

      void sp1_DataReceived(object sender, SerialDataReceivedEventArgs e)  

      if (sp1.IsOpen)     //此處可能沒有必要判斷是否打開串口,但為了嚴謹性,我還是加上了  

      byte[] byteRead = new byte[sp1.BytesToRead];    //BytesToRead:sp1接收的字符個數(shù)  

      if (rdSendStr.Checked)                          //'發(fā)送字符串'單選按鈕  

      txtReceive.Text += sp1.ReadLine() + "\r\n"; //注意:回車換行必須這樣寫,單獨使用"\r"和"\n"都不會有效果  

      sp1.DiscardInBuffer();                      //清空SerialPort控件的Buffer   

      else                                            //'發(fā)送16進制按鈕'  

      try  

      Byte[] receivedData = new Byte[sp1.BytesToRead];        //創(chuàng)建接收字節(jié)數(shù)組  

      sp1.Read(receivedData, 0, receivedData.Length);         //讀取數(shù)據(jù)                         

      sp1.DiscardInBuffer();                                  //清空SerialPort控件的Buffer  

      string strRcv = null;  

      for (int i = 0; i < receivedData.Length; i++) //窗體顯示  

      strRcv += receivedData[i].ToString("X2");  //16進制顯示  

      txtReceive.Text += strRcv + "\r\n";  

      catch (System.Exception ex)  

      MessageBox.Show(ex.Message, "出錯提示");  

      txtSend.Text = "";  

      else  

      MessageBox.Show("請打開某個串口", "錯誤提示");  

      為了友好和美觀,我將當前時間也顯示出來,又將顯示字體的顏色做了修改:

      [csharp] view plaincopyprint?

      //輸出當前時間  

      DateTime dt = DateTime.Now;  

      txtReceive.Text += dt.GetDateTimeFormats('f')[0].ToString() + "\r\n";  

      txtReceive.SelectAll();  

      txtReceive.SelectionColor = Color.Blue;         //改變字體的顏色  

      做到這里,大部分功能就已實現(xiàn)了,剩下的工作就是些簡單的操作設(shè)置了,有保存設(shè)置、定時發(fā)送信息、控制文本框輸入內(nèi)容等。

      六、保存設(shè)置

      這部分相對簡單,但當時我沒接觸過,也花了點時間,現(xiàn)在想想,也不過如此。

      保存用戶設(shè)置用ini文件是個不錯的選擇,雖然大部分都用注冊表實現(xiàn),但ini文件保存還是有比較廣泛的使用。

      .ini 文件是Initialization File的縮寫,也就是初始化文件。

      為了不偏離正題,也不過多說明,可參考相關(guān)內(nèi)容(網(wǎng)上資源都不錯,因人而異,就不加鏈接了)。

      使用Inifile讀寫ini文件,這里我用到了兩個主要方法:

      [csharp] view plaincopyprint?

      //讀出ini文件  

      a:=inifile.Readstring('節(jié)點','關(guān)鍵字',缺省值);// string類型   

      b:=inifile.Readinteger('節(jié)點','關(guān)鍵字',缺省值);// integer類型   

      c:=inifile.Readbool('節(jié)點','關(guān)鍵字',缺省值);// boolean類型   

      其中[缺省值]為該INI文件不存在該關(guān)鍵字時返回的缺省值。   

      //寫入INI文件:   

      inifile.writestring('節(jié)點','關(guān)鍵字',變量或字符串值);   

      inifile.writeinteger('節(jié)點','關(guān)鍵字',變量或整型值);   

      inifile.writebool('節(jié)點','關(guān)鍵字',變量或True或False);   

      請看代碼:

      [csharp] view plaincopyprint?

      //using 省寫了  

      namespace INIFILE  

      class Profile  

      public static void LoadProfile()  

      string strPath = AppDomain.CurrentDomain.BaseDirectory;  

      _file = new IniFile(strPath + "Cfg.ini");  

      G_BAUDRATE = _file.ReadString("CONFIG", "BaudRate", "4800");    //讀數(shù)據(jù),下同  

      G_DATABITS = _file.ReadString("CONFIG", "DataBits", "8");  

      G_STOP = _file.ReadString("CONFIG", "StopBits", "1");  

      G_PARITY = _file.ReadString("CONFIG", "Parity", "NONE");  

      public static void SaveProfile()  

      string strPath = AppDomain.CurrentDomain.BaseDirectory;  

      _file = new IniFile(strPath + "Cfg.ini");  

      _file.WriteString("CONFIG", "BaudRate", G_BAUDRATE);            //寫數(shù)據(jù),下同  

      _file.WriteString("CONFIG", "DataBits", G_DATABITS);  

      _file.WriteString("CONFIG", "StopBits", G_STOP);  

      _file.WriteString("CONFIG", "G_PARITY", G_PARITY);  

      private static IniFile _file;//內(nèi)置了一個對象  

      public static string G_BAUDRATE = "1200";//給ini文件賦新值,并且影響界面下拉框的顯示  

      public static string G_DATABITS = "8";  

      public static string G_STOP = "1";  

      public static string G_PARITY = "NONE";      

      _file聲明成了內(nèi)置對象,可以方便各函數(shù)的調(diào)用。

      下面是“保存設(shè)置”的部分代碼:

      [csharp] view plaincopyprint?

      private void btnSave_Click(object sender, EventArgs e)  

      //設(shè)置各“串口設(shè)置”  

      string strBaudRate = cbBaudRate.Text;  

      string strDateBits = cbDataBits.Text;  

      string strStopBits = cbStop.Text;  

      Int32 iBaudRate = Convert.ToInt32(strBaudRate);  

      Int32 iDateBits = Convert.ToInt32(strDateBits);  

      Profile.G_BAUDRATE = iBaudRate+"";       //波特率  

      Profile.G_DATABITS = iDateBits+"";       //數(shù)據(jù)位  

      switch (cbStop.Text)            //停止位  

      case "1":  

      Profile.G_STOP = "1";  

      break;  

      case "1.5":  

      Profile.G_STOP = "1.5";  

      break;  

      //防止過多刷屏,下面省寫了  

      ……  

      switch (cbParity.Text)             //校驗位  

      case "無":  

      Profile.G_PARITY = "NONE";  

      break;  

      …………  

      Profile.SaveProfile();  //保存設(shè)置  

      讀取ini文件主要在加載窗體時執(zhí)行:

      INIFILE.Profile.LoadProfile();//加載所有

      七、控制文本輸入這里倒挺簡單,只是注意一點。當我們控制輸入非法字符時,可通過控制e.Handed的屬性值實現(xiàn),注意這里的Handed屬性是“操作過”的含義,而非“執(zhí)行此處操作”之意,Handled是過去式,看字面意思,"操作過的=是;",將這個操作的狀態(tài)設(shè)為已處理過,自然就不會再處理了。具體參見MSDN:Handed

      [csharp] view plaincopyprint?

      private void txtSend_KeyPress(object sender, KeyPressEventArgs e)  

      if (radio1.Checked== true)  

      //正則匹配  

      string patten = "[0-9a-fA-F]|\b|0x|0X| "; //“\b”:退格鍵  

      Regex r = new Regex(patten);  

      Match m = r.Match(e.KeyChar.ToString());  

      if (m.Success )//&&(txtSend.Text.LastIndexOf(" ") != txtSend.Text.Length-1))  

      e.Handled = false;  

      else  

      e.Handled = true;  

      //end of radio1  

      八、定時發(fā)送信息

      這邊看似很簡單,但也有一點需要注意,當定時器生效時,我們要間隔訪問“發(fā)送”按鍵的內(nèi)容,怎么實現(xiàn)?還好MS給我們提供了必要的支持,使用Button的 PerformClick可以輕松做到,  PerformClick參見MSDN:PerformClick

      [csharp] view plaincopyprint?

      private void tmSend_Tick(object sender, EventArgs e)  

      //轉(zhuǎn)換時間間隔  

      string strSecond = txtSecond.Text;  

      try  

      int isecond = int.Parse(strSecond) * 1000;//Interval以微秒為單位  

      tmSend.Interval = isecond;  

      if (tmSend.Enabled == true)  

      btnSend.PerformClick(); //產(chǎn)生“發(fā)送”的click事件  

      catch (System.Exception ex)  

      MessageBox.Show("錯誤的定時輸入!", "Error");  

      注意在一些情況下不要忘了讓定時器失效,如在取消“定時發(fā)送數(shù)據(jù)"和“關(guān)閉串口”時等。

      好了,主要內(nèi)容就是這些,希望以上內(nèi)容對大家有所幫助,如你有好的想法,還請不吝賜教!

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多