• C#串口开发之SerialPort类封装


    SerialPort类#

    微软在.NET中对串口通讯进行了封装,我们可以在.net2.0及以上版本开发时直接使用SerialPort类对串口进行读写操作。
    为操作方便,本文对SerialPort类做了一些封装,暂时取名为SerialPortClient

    SerialPort类的属性主要包括:

    • 串口名称(PortName)
    • 波特率(BaudRate)
    • 数据位 DataBits
    • 停止位 StopBits
    • 奇偶校验 Parity

    SerialPort类的事件主要包括:

    • DataReceived:用于异步接收串口数据事件
    • ErrorReceived:错误处理事件

    SerialPort类的方法主要包括:Open();Close();Read();Write()、DiscardInBuffer()、DiscardOutBuffer()等

    参数封装#

    波特率、数据位这些参数不是系统内置的枚举类型,为方便实际操作需构造波特率、数据位这两个枚举对象。

        #region 波特率、数据位的枚举
        /// <summary>
        /// 串口数据位列表(5,6,7,8)
        /// </summary>
        public enum DataBits : int
        {        
            Five = 5,        
            Six = 6,        
            Sevent = 7,        
            Eight = 8
        }
    
        /// <summary>
        /// 串口波特率列表。
        /// 75,110,150,300,600,1200,2400,4800,9600,14400,19200,28800,38400,56000,57600,
        /// 115200,128000,230400,256000
        /// </summary>
        public enum BaudRates : int
        {
            BR_75 = 75,
            BR_110 = 110,
            BR_150 = 150,
            BR_300 = 300,
            BR_600 = 600,
            BR_1200 = 1200,
            BR_2400 = 2400,
            BR_4800 = 4800,
            BR_9600 = 9600,
            BR_14400 = 14400,
            BR_19200 = 19200,
            BR_28800 = 28800,
            BR_38400 = 38400,
            BR_56000 = 56000,
            BR_57600 = 57600,
            BR_115200 = 115200,
            BR_128000 = 128000,
            BR_230400 = 230400,
            BR_256000 = 256000
        } 
        #endregion
    

    控件操作封装#

    对常用的参数下拉列表绑定做一个封装,该操作为封装类的静态方法。
    为兼容第三方控件使用IList接口,不直接使用ComboBox类型作为参数

            #region 静态方法
            /// <summary>
            /// 设置串口号
            /// </summary>
            /// <param name="obj">需要绑定的项的集合(如:ComboBox中项的集合ComboBox.Items)</param>
            public static void SetPortNameValues(IList obj)
            {
                obj.Clear();            
                try
                {                
                    foreach (string str in SerialPort.GetPortNames())
                    {
                        obj.Add(str);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine("未能查询到串口名称——"+e.ToString());                
                }
                
            }
    
            /// <summary>
            /// 设置波特率
            /// </summary>
            /// <param name="obj">需要绑定的项的集合(如:ComboBox中项的集合ComboBox.Items)</param>
            public static void SetBauRateValues(IList obj)
            {
                obj.Clear();
                foreach (BaudRates rate in Enum.GetValues(typeof(BaudRates)))
                {
                    obj.Add(((int)rate).ToString());
                }
            }
    
            /// <summary>
            /// 设置数据位
            /// </summary>
            /// <param name="obj">需要绑定的项的集合(如:ComboBox中项的集合ComboBox.Items)</param>
            public static void SetDataBitsValues(IList obj)
            {
                obj.Clear();
                foreach (DataBits databit in Enum.GetValues(typeof(DataBits)))
                {
                    obj.Add(((int)databit).ToString());
                }
            }
    
            /// <summary>
            /// 设置校验位列表
            /// </summary>
            /// <param name="obj">需要绑定的项的集合(如:ComboBox中项的集合ComboBox.Items)</param>
            public static void SetParityValues(IList obj)
            {
                obj.Clear();
                foreach (string str in Enum.GetNames(typeof(Parity)))
                {
                    obj.Add(str);
                }
            }
    
            /// <summary>
            /// 设置停止位
            /// </summary>
            /// <param name="obj">需要绑定的项的集合(如:ComboBox中项的集合ComboBox.Items)</param>
            public static void SetStopBitValues(IList obj)
            {
                obj.Clear();
                foreach (string str in Enum.GetNames(typeof(StopBits)))
                {
                    obj.Add(str);
                }
            }
            #endregion
    

    SerialPortClient类实现#

    该部分代码是SerialPortClient类非静态操作部分的实现,完整的类实现需要加上上面的控件操作封装的实现代码,具体请参照Demo源码。

            #region 变量属性
            public event Action<List<byte>> DataReceived;
            public event SerialErrorReceivedEventHandler ErrorReceived; 
    
            private SerialPort comPort = new SerialPort();
            private string portName = "COM1";//串口号,默认COM1
            private BaudRates baudRate = BaudRates.BR_9600;//波特率
            private Parity parity = Parity.None;//校验位
            private StopBits stopBits = StopBits.One;//停止位
            private DataBits dataBits = DataBits.Eight;//数据位        
    
            /// <summary>
            /// 串口号
            /// </summary>
            public string PortName
            {
                get { return portName; }
                set { portName = value; }
            }
    
            /// <summary>
            /// 波特率
            /// </summary>
            public BaudRates BaudRate
            {
                get { return baudRate; }
                set { baudRate = value; }
            }
    
            /// <summary>
            /// 奇偶校验位
            /// </summary>
            public Parity Parity
            {
                get { return parity; }
                set { parity = value; }
            }
    
            /// <summary>
            /// 数据位
            /// </summary>
            public DataBits DataBits
            {
                get { return dataBits; }
                set { dataBits = value; }
            }
    
            /// <summary>
            /// 停止位
            /// </summary>
            public StopBits StopBits
            {
                get { return stopBits; }
                set { stopBits = value; }
            }             
            #endregion
    
            #region 构造函数
            /// <summary>
            /// 无参构造函数
            /// </summary>
            public SerialPortClient()
            {
                BoundEvents();
            }
    
            void BoundEvents()
            {
                comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
                comPort.ErrorReceived += new SerialErrorReceivedEventHandler(comPort_ErrorReceived);
            }
    
            /// <summary>
            /// 参数构造函数(使用枚举参数构造)
            /// </summary>
            /// <param name="baud">波特率</param>
            /// <param name="par">奇偶校验位</param>
            /// <param name="sBits">停止位</param>
            /// <param name="dBits">数据位</param>
            /// <param name="name">串口号</param>
            public SerialPortClient(string name, BaudRates baud, Parity par, DataBits dBits, StopBits sBits)
            {
                this.portName = name;
                this.baudRate = baud;
                this.parity = par;
                this.dataBits = dBits;
                this.stopBits = sBits;
                BoundEvents();
            }
    
            /// <summary>
            /// 参数构造函数(使用字符串参数构造)
            /// </summary>
            /// <param name="baud">波特率</param>
            /// <param name="par">奇偶校验位</param>
            /// <param name="sBits">停止位</param>
            /// <param name="dBits">数据位</param>
            /// <param name="name">串口号</param>
            public SerialPortClient(string name, string baud, string par, string dBits, string sBits)
            {
                this.portName = name;
                this.baudRate = (BaudRates)Enum.Parse(typeof(BaudRates), baud);
                this.parity = (Parity)Enum.Parse(typeof(Parity), par);
                this.dataBits = (DataBits)Enum.Parse(typeof(DataBits), dBits);
                this.stopBits = (StopBits)Enum.Parse(typeof(StopBits), sBits);
                BoundEvents();
           }
            #endregion
    
            #region 事件处理函数
            /// <summary>
            /// 数据仓库
            /// </summary>
            List<byte> datapool = new List<byte>();//存放接收的所有字节
            /// <summary>
            /// 数据接收处理
            /// </summary>
            void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
            {
                if (comPort.IsOpen)     //判断是否打开串口
                {
    
                    Byte[] receivedData = new Byte[comPort.BytesToRead];        //创建接收字节数组
                    comPort.Read(receivedData, 0, receivedData.Length);         //读取数据
    
                    //触发整条记录的处理
                    if (DataReceived != null)
                    {
                        datapool.AddRange(receivedData);                
                        DataReceived(datapool);
                    }
                    
                }
                else
                {
                    Console.WriteLine("请打开某个串口", "Error");
                } 
            }
            /// <summary>
            /// 错误处理函数
            /// </summary>
            void comPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
            {
                if (ErrorReceived != null)
                {
                    ErrorReceived(sender, e);
                }
            }
            #endregion
    
            #region 串口关闭/打开
            /// <summary>
            /// 端口是否已经打开
            /// </summary>
            public bool IsOpen
            {
                get
                {
                    return comPort.IsOpen;
                }
            }
    
            /// <summary>
            /// 打开端口
            /// </summary>
            /// <returns></returns>
            public void Open()
            {
                if (comPort.IsOpen) comPort.Close();
    
                comPort.PortName = portName;
                comPort.BaudRate = (int)baudRate;
                comPort.Parity = parity;
                comPort.DataBits = (int)dataBits;
                comPort.StopBits = stopBits;
    
                comPort.Open();
            }
    
            /// <summary>
            /// 关闭端口
            /// </summary>
            public void Close()
            {
                if (comPort.IsOpen) comPort.Close();
            }
    
            /// <summary>
            /// 丢弃来自串行驱动程序的接收和发送缓冲区的数据
            /// </summary>
            public void DiscardBuffer()
            {
                comPort.DiscardInBuffer();
                comPort.DiscardOutBuffer();
            }
            #endregion
    
            #region 写入数据
            /// <summary>
            /// 写入数据
            /// </summary>
            /// <param name="buffer"></param>
            public void Write(byte[] buffer, int offset, int count) 
            {
                if (!(comPort.IsOpen)) comPort.Open();
                comPort.Write(buffer,offset, count);
            }
    
            /// <summary>
            /// 写入数据
            /// </summary>
            /// <param name="buffer">写入端口的字节数组</param>
            public void Write(byte[] buffer)
            {
                if (!(comPort.IsOpen)) comPort.Open();
                comPort.Write(buffer, 0, buffer.Length);
            }
            #endregion
    

    SerialPortClient类使用#

    SerialPortClient类使用很简单,主要分为初始化下拉列表、初始化串口(绑定数据接收、错误处理事件)两步,在窗体加载事件中调用即可。代码如下:

            //全局变量
            SerialPortClient portClient = new SerialPortClient();
            private void InitPort() 
            {
                portClient.DataReceived+=new Action<List<byte>>(datapool=>
                {
                    //匹配数据帧,此处以0xea 0xeb为帧头、0xfa 0xfb为帧尾
                    //实际通讯协议应该更复杂
                    while (true)
                    {
                        if (datapool.Count <4) break;
                        if ((datapool[0] == 0xea) && (datapool[0] == 0xeb)) 
                        {
                            datapool.RemoveAt(0);
                            continue;
                        }
                        int index = -1;//正确作法是记住上次最末尾的索引,避免重复匹配
                        for (int i = 0; i < datapool.Count-1; i++)
                        {
                            if (datapool[i] == 0xfa && datapool[i + 1] == 0xfb) 
                            { 
                                index = i;
                                break;
                            }
                        }
                        if (index != -1)
                        {
                            byte[] data = datapool.GetRange(0, index + 2).ToArray();
                            datapool.RemoveRange(0, index + 2);
                            string msg = Encoding.UTF8.GetString(data,2,data.Length-4);
                            this.BeginInvoke(new Action(() => { textBox_Recive.Text = msg; }));
                        }
                        else break;
                    }
    
                });
            }
            private void InitCmb()
            {
                //绑定端口号
                SerialPortClient.SetPortNameValues(comboBox_Com.Items);
                comboBox_Com.DropDownStyle = ComboBoxStyle.DropDownList;
                comboBox_Com.SelectedIndexChanged += new EventHandler((obj, e) => 
                { 
                    if (comboBox_Com.SelectedItem!=null) 
                    {
                        portClient.PortName = comboBox_Com.SelectedItem.ToString(); 
                    }
                });
                comboBox_Com.SelectedIndex = 0;
    
                //波特率
                SerialPortClient.SetBauRateValues(comboBox_BaudRates.Items);
                comboBox_BaudRates.DropDownStyle = ComboBoxStyle.DropDownList;
                comboBox_BaudRates.SelectedIndexChanged += new EventHandler((obj, e) => 
                {
                    if (comboBox_BaudRates.SelectedItem != null)
                    {
                        portClient.BaudRate = (BaudRates)Enum.Parse(typeof(BaudRates), comboBox_BaudRates.SelectedItem.ToString());                    
                    }
                });
                comboBox_BaudRates.SelectedIndex = 9;
    
                //数据位
                SerialPortClient.SetDataBitsValues(comboBox_DataBits.Items);
                comboBox_DataBits.DropDownStyle = ComboBoxStyle.DropDownList;
                comboBox_DataBits.SelectedIndexChanged += new EventHandler((obj, e) =>
                {
                    if (comboBox_DataBits.SelectedItem != null)
                    {
                        portClient.DataBits = (DataBits)Enum.Parse(typeof(DataBits), comboBox_DataBits.SelectedItem.ToString());
                    }
                });
                comboBox_DataBits.SelectedIndex = 3;
    
                //校验位
                SerialPortClient.SetParityValues(comboBox_Parity.Items);
                comboBox_Parity.DropDownStyle = ComboBoxStyle.DropDownList;
                comboBox_Parity.SelectedIndexChanged += new EventHandler((obj, e) =>
                {
                    if (comboBox_Parity.SelectedItem != null)
                    {
                        portClient.Parity = (Parity)Enum.Parse(typeof(Parity), comboBox_Parity.SelectedItem.ToString());
                    }
                });
                comboBox_Parity.SelectedIndex = 0;
    
                //停止位
                SerialPortClient.SetStopBitValues(comboBox_StopBits.Items);
                comboBox_StopBits.DropDownStyle = ComboBoxStyle.DropDownList;
                comboBox_StopBits.SelectedIndexChanged += new EventHandler((obj, e) =>
                {
                    if (comboBox_StopBits.SelectedItem != null)
                    {
                        portClient.StopBits = (StopBits)Enum.Parse(typeof(StopBits), comboBox_StopBits.SelectedItem.ToString());
                    }
                });
                comboBox_StopBits.SelectedIndex = 1;
            }
    

    串口的打开、关闭、状态显示等操作请查看文章末尾的Demo源码。

    测试Demo#


    Demo下载:https://files-cdn.cnblogs.com/files/timefiles/SerialPortDemo.rar

    参考文章#

    C#开发串口总结,并提炼串口辅助类到公用类库中——博客园

    出处:https://www.cnblogs.com/timefiles/p/SerialPortClient.html

    您的资助是我最大的动力!
    金额随意,欢迎来赏!
    款后有任何问题请给我留言。

    如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的推荐按钮。
    如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的关注我。(●'◡'●)

    如果你觉得本篇文章对你有所帮助,请给予我更多的鼓励,求打             付款后有任何问题请给我留言!!!

    因为,我的写作热情也离不开您的肯定支持,感谢您的阅读,我是【Jack_孟】!

  • 相关阅读:
    DailyRollingFileAppender的使用
    CSS自动换行
    MAC与WIN10电脑共享文件
    C++常用STL
    Python机器学习及实践 课后小题
    Mac上利用VScode配置c/c++开发环境
    pycharm+anaconda在Mac上的配置方法 2019.11.29
    二分查找 python实现
    python3相关
    算法图解 各部分回忆
  • 原文地址:https://www.cnblogs.com/mq0036/p/14758561.html
Copyright © 2020-2023  润新知