C#實現簡單的串口通訊

前言

    本着學習研究的態度,用c#語言實現簡單的串口通訊工具。c#

1、串口通訊原理

串口通訊

串口通訊(Serial Communications)的概念很是簡單,串口按位(bit)發送和接收字節。儘管比按字節(byte)的並行通訊慢,可是串口能夠在使用一根線發送數據的同時用另外一根線接收數據。它很簡單而且可以實現遠距離通訊。因爲串口通訊是異步的,端口可以在一根線上發送數據同時在另外一根線上接收數據。其餘線用於握手,但不是必須的。串口通訊最重要的參數是波特率、數據位、中止位和奇偶校驗。對於兩個進行通訊的端口,這些參數必須匹配。數組

經常使用術語介紹

波特率:這是一個衡量符號傳輸速率的參數。指的是信號被調製之後在單位時間內的變化,即單位時間內載波參數變化的次數,如每秒鐘傳送240個字符,而每一個字符格式包含10位(1個起始位,1箇中止位,8個數據位),這時的波特率爲240Bd,比特率爲10位*240個/秒=2400bps。通常調製速率大於波特率,好比曼徹斯特編碼)。一般電話線的波特率爲14400,28800和36600。波特率能夠遠遠大於這些值,可是波特率和距離成反比。高波特率經常用於放置的很近的儀器間的通訊,典型的例子就是GPIB設備的通訊。框架

數據位這是衡量通訊中實際數據位的參數。當計算機發送一個信息包,實際的數據每每不會是8位的,標準的值是六、7和8位。如何設置取決於你想傳送的信息。好比,標準的ASCII碼是0~127(7位)。擴展的ASCII碼是0~255(8位)。若是數據使用簡單的文本(標準 ASCII碼),那麼每一個數據包使用7位數據。每一個包是指一個字節,包括開始/中止位,數據位和奇偶校驗位。因爲實際數據位取決於通訊協議的選取,術語"包"指任何通訊的狀況。異步

中止位:用於表示單個包的最後一位。典型的值爲1,1.5和2位。因爲數據是在傳輸線上定時的,而且每個設備有其本身的時鐘,極可能在通訊中兩臺設備間出現了小小的不一樣步。所以中止位不只僅是表示傳輸的結束,而且提供計算機校訂時鐘同步的機會。適用於中止位的位數越多,不一樣時鐘同步的容忍程度越大,可是數據傳輸率同時也越慢。ide

奇偶校驗位:串口通訊中一種簡單的檢錯方式。有四種檢錯方式:偶、奇、高和低。固然沒有校驗位也是能夠的。對於偶和奇校驗的狀況,串口會設置校驗位(數據位後面的一位),用一個值確保傳輸的數據有偶個或者奇個邏輯高位。例如,若是數據是011,那麼對於偶校驗,校驗位爲0,保證邏輯高的位數是偶數個。若是是奇校驗,校驗位爲1,這樣就有3個邏輯高位。高位和低位不真正的檢查數據,簡單置位邏輯高或者邏輯低校驗。這樣使得接收設備可以知道一個位的狀態,有機會判斷是否有噪聲干擾了通訊或者是否傳輸和接收數據是否不一樣步。工具

串口引腳圖解

1 載波檢測(DCD) 2 接受數據(RXD) 3 發出數據(TXD) 4 數據終端準備好(DTR) 學習

5 信號地線(SG) 6 數據準備好(DSR) 7 請求發送(RTS) 8 清除發送(CTS) 9 振鈴指示(RI) this

      

2、使用System.IO.Port.SerialPort類實現串口通訊

System.IO.Port.SerialPort類介紹

    System.IO.Port.SerialPort.NET Framework提供的操做串行端口的類,裏面提供了一些方法、屬性和和事件供開發者調用操做串口。編碼

調用流程

1. 直接調用SerialPort的靜態方法GetPortNames()獲取當前計算機的串行端口名稱數組

2.根據串口名稱,初始化SerialPort對象,設置參數,調用Open()方法打開串口

3.調用Write()方法發送數據

4.註冊接收數據的監聽,獲取數據(或者另起線程循環讀取接收數據,本文使用註冊監聽方式接收數據)    

 

具體代碼實現

using System;  
using System.IO.Ports;  
using System.Text;  
    
namespace PortControlDemo  
{  
    public class PortControlHelper  
    {  
        #region 字段/屬性/委託  
        /// <summary>  
        /// 串行端口對象  
        /// </summary>  
        private SerialPort sp;  
    
        /// <summary>  
        /// 串口接收數據委託  
        /// </summary>  
        public delegate void ComReceiveDataHandler(string data);  
    
        public ComReceiveDataHandler OnComReceiveDataHandler = null;  
    
        /// <summary>  
        /// 端口名稱數組  
        /// </summary>  
        public string[] PortNameArr { get; set; }  
    
        /// <summary>  
        /// 串口通訊開啓狀態  
        /// </summary>  
        public bool PortState { get; set; } = false;  
            
        /// <summary>  
        /// 編碼類型  
        /// </summary>  
        public Encoding EncodingType { get; set; } = Encoding.ASCII;  
        #endregion  
   
        #region 方法  
        public PortControlHelper()  
        {  
            PortNameArr = SerialPort.GetPortNames();  
            sp = new SerialPort();  
            sp.DataReceived += new SerialDataReceivedEventHandler(DataReceived);  
        }  
    
        /// <summary>  
        /// 打開端口  
        /// </summary>  
        /// <param name="portName">端口名稱</param>  
        /// <param name="boudRate">波特率</param>  
        /// <param name="dataBit">數據位</param>  
        /// <param name="stopBit">中止位</param>  
        /// <param name="timeout">超時時間</param>  
        public void OpenPort(string portName , int boudRate = 115200, int dataBit = 8, int stopBit = 1, int timeout = 5000)  
        {  
            try  
            {  
                sp.PortName = portName;  
                sp.BaudRate = boudRate;  
                sp.DataBits = dataBit;  
                sp.StopBits = (StopBits)stopBit;  
                sp.ReadTimeout = timeout;  
                sp.Open();  
                PortState = true;  
            }  
            catch (Exception e)  
            {  
                throw e;  
            }  
        }  
    
        /// <summary>  
        /// 關閉端口  
        /// </summary>  
        public void ClosePort()  
        {  
            try  
            {  
                sp.Close();  
                PortState = false;  
            }  
            catch (Exception e)  
            {  
                throw e;  
            }  
        }  
    
        /// <summary>  
        /// 發送數據  
        /// </summary>  
        /// <param name="sendData"></param>  
        public void SendData(string sendData)  
        {  
            try  
            {  
                sp.Encoding = EncodingType;  
                sp.Write(sendData);  
            }  
            catch (Exception e)  
            {  
                throw e;  
            }  
        }  
    
        /// <summary>  
        /// 接收數據回調用  
        /// </summary>  
        /// <param name="sender"></param>  
        /// <param name="e"></param>  
        private void DataReceived(object sender, SerialDataReceivedEventArgs e)  
        {  
            byte[] buffer = new byte[sp.BytesToRead];  
            sp.Read(buffer, 0, buffer.Length);  
            string str = EncodingType.GetString(buffer);  
            if (OnComReceiveDataHandler != null)  
            {  
                OnComReceiveDataHandler(str);  
            }  
        }  
        #endregion  
    }  
}
View Code

 

3、編寫Winform串口通訊工具界面

界面預覽

操做介紹

        本界面主要功能是操做兩個串口,一個發送數據另外一個接收數據。左側設置兩串口的一些參數,設置完成後點擊"打開發送接收串口",如兩串口成功打開,右側即可操做發送和接受數據。spa

具體代碼實現

using System;
using System.Windows.Forms;

namespace PortControlDemo
{
    public partial class FrmPortControl : Form
    {
        #region 字段/屬性
        int[] BaudRateArr = new int[] { 110, 300, 1200, 2400, 4800, 115200 };
        int[] DataBitArr = new int[] { 6, 7, 8 };
        int[] StopBitArr = new int[] { 1, 2, 3};
        int[] TimeoutArr = new int[] { 500, 1000, 2000, 5000, 10000 };
        object[] CheckBitArr = new object[] { "None"};
        private bool ReceiveState = false;
        private PortControlHelper pchSend;
        private PortControlHelper pchReceive;
        #endregion

        #region 方法
        /// <summary>
        /// 初始化控件
        /// </summary>
        private void InitView()
        {
            cb_portNameSend.DataSource = pchSend.PortNameArr;
            cb_portNameReceive.DataSource = pchReceive.PortNameArr;
            cb_baudRate.DataSource = BaudRateArr;
            cb_dataBit.DataSource = DataBitArr;
            cb_stopBit.DataSource = StopBitArr;
            cb_checkBit.DataSource = CheckBitArr;
            cb_timeout.DataSource = TimeoutArr;
            FreshBtnState(pchSend.PortState && pchReceive.PortState);
        }

        /// <summary>
        /// 刷新按鈕狀態
        /// </summary>
        /// <param name="state"></param>
        private void FreshBtnState(bool state)
        {
            if (state)
            {
                Btn_open.Text = "關閉發送接收串口";
                Btn_send.Enabled = true;
                Btn_receive.Enabled = true;
            }
            else
            {
                Btn_open.Text = "打開發送接收串口";
                Btn_send.Enabled = false;
                Btn_receive.Enabled = false;
            }
        }
        #endregion

        #region 事件
        public FrmPortControl()
        {
            InitializeComponent();
            pchSend = new PortControlHelper();
            pchReceive = new PortControlHelper();
            InitView();
        }

        /// <summary>
        /// 點擊 發送數據 按鈕,發送文本內數據
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Btn_send_Click(object sender, EventArgs e)
        {
            pchSend.SendData(tb_send.Text);
        }

        /// <summary>
        /// 點擊 開始接收 按鈕,開始監聽串口接收入口數據
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Btn_receive_Click(object sender, EventArgs e)
        {
            if (ReceiveState)
            {
                pchReceive.OnComReceiveDataHandler -= new PortControlHelper.ComReceiveDataHandler(ComReceiveData);
                Btn_receive.Text = "開始接收";
                ReceiveState = false;
            }
            else
            {

                pchReceive.OnComReceiveDataHandler += new PortControlHelper.ComReceiveDataHandler(ComReceiveData);
                Btn_receive.Text = "中止接收";
                ReceiveState = true;
            }
        }

        /// <summary>
        /// 開啓或關閉 兩個通訊的串口,刷新按鈕狀態
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Btn_open_Click(object sender, EventArgs e)
        {
            if (pchSend.PortState)
            {
                pchSend.ClosePort();
                pchReceive.ClosePort();
            }
            else
            {
                pchSend.OpenPort(cb_portNameSend.Text, int.Parse(cb_baudRate.Text),
                    int.Parse(cb_dataBit.Text), int.Parse(cb_stopBit.Text),
                    int.Parse(cb_timeout.Text));
                pchReceive.OpenPort(cb_portNameReceive.Text, int.Parse(cb_baudRate.Text),
                   int.Parse(cb_dataBit.Text), int.Parse(cb_stopBit.Text),
                   int.Parse(cb_timeout.Text));

            }
            FreshBtnState(pchSend.PortState && pchReceive.PortState);
            pchReceive.OnComReceiveDataHandler += new PortControlHelper.ComReceiveDataHandler(ComReceiveData);
            Btn_receive.Text = "中止接收";
            ReceiveState = true;
        }

        /// <summary>
        /// 接收到的數據,寫入文本框內
        /// </summary>
        /// <param name="data"></param>
        private void ComReceiveData(string data)
        {
            this.Invoke(new EventHandler(delegate
            {
                tb_receive.AppendText($"接收:{data}\n");
            }));
        }
        #endregion
    }
}
View Code

 

總結    

    有框架提供的幫助類,咱們實現串口通訊雖然不難,可是仍是有不少細節要注意的。寫好任何同樣東西都不容易,一塊兒加油吧!

附錄

搭建一個串口調試環境的工具和本文源碼地址,有須要的筒靴自提吧 ( •̀_•́)

串口調試Demo源碼地址:https://files.cnblogs.com/files/ElijahZeng/PortControlDemo.rar

虛擬串口工具vspd:https://files.cnblogs.com/files/ElijahZeng/VSPD%E8%99%9A%E6%8B%9F%E4%B8%B2%E5%8F%A3%E5%B7%A5%E5%85%B7.rar

相關文章
相關標籤/搜索