溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

利用C# 項目實現(xiàn)一個串口監(jiān)視上位機功能

發(fā)布時間:2020-11-21 14:42:48 來源:億速云 閱讀:179 作者:Leah 欄目:開發(fā)技術(shù)

這篇文章將為大家詳細講解有關利用C# 項目實現(xiàn)一個串口監(jiān)視上位機功能,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

  實現(xiàn)上位機和下位機之間的通信,通常使用的是串口通信,接下來實現(xiàn)一個通過上位機和串口調(diào)試助手來完成串口通信測試。

  首先創(chuàng)建一個WInfrom窗體應用工程文件,在創(chuàng)建好的工程下面,通過工具箱中已有的控件完成界面的搭建,如下圖所示,為了方便初學者容易看懂程序,下圖將控件的命名一并標注出來:

利用C# 項目實現(xiàn)一個串口監(jiān)視上位機功能

  直接進入正題,將完整的工程代碼黏貼出來:

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)異常:

利用C# 項目實現(xiàn)一個串口監(jiān)視上位機功能

將串口調(diào)試助手發(fā)送數(shù)據(jù)位修改成9位之后,進行發(fā)送,可以發(fā)現(xiàn)上位機并未接收到相關的數(shù)據(jù):

利用C# 項目實現(xiàn)一個串口監(jiān)視上位機功能

接著修改串口調(diào)試助手的發(fā)送數(shù)據(jù)位,修改成8位,可以發(fā)現(xiàn)上位機尚未能接收到來自串口調(diào)試助手發(fā)來的數(shù)據(jù),這是為什么呢?

利用C# 項目實現(xiàn)一個串口監(jiān)視上位機功能

接下來將通過斷點逐步進行調(diào)試,來解釋是為啥上位機沒有接收到調(diào)試助手發(fā)來的數(shù)據(jù),當串口調(diào)試助手發(fā)來的數(shù)據(jù)長度位9位時,通過監(jiān)視器可以查看到接收緩沖器中的數(shù)據(jù)長度長度是9

利用C# 項目實現(xiàn)一個串口監(jiān)視上位機功能

第一次點擊完發(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ù)清除。

利用C# 項目實現(xiàn)一個串口監(jiān)視上位機功能

使用s.DiscardInBuffer()對不符合長度的數(shù)據(jù)進行丟棄,實現(xiàn)的效果如下所示:

利用C# 項目實現(xiàn)一個串口監(jiān)視上位機功能

關于利用C# 項目實現(xiàn)一個串口監(jiān)視上位機功能就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI