SerialPort常用属性方法和事件

SerialPort 常用属性方法和事件

  一.概述

    Visual Studio 6.0 中编写串口通讯程序,一般都使用Microsoft Communication

Control (简称MSComm )的通讯控件,只要通 过对此控件的属性和事件进行相应编程操

作,就可以轻松地实现串口通讯。但在Microsoft.Net 技术广泛应用的今天,Visual

Studio.Net 没有将此控件加入控件库,所以人们采用了许多方法在Visual Studio.Net

编写串口通讯程序:第一种方法是通过采用Visual Studio 6.0 中原来的MSComm 控件这是

最简单的,最方便的方法,但需要注册;第二种方法是采用微软在.NET 推出了一个串口

控件,基于.NETP/Invoke 调用方法实现;第三种方法是自己用API 写串口通信,虽然难

度高,但可以方便实现自己想要的各种功能。

    现在微软推出了最新版本的Visual Studio 2005 开发工具,可以不再采用第三方控

件的方法来设计串口通讯程序。NET Framework 2.0 类库包含了SerialPort , 方便地

实现了所需要串口通讯的多种功能,为了使MSComm 编程方法快速转换到以SerialPort

为核心的串口通讯的设计方法,本文着重讨论了Visual Studio 6.0MSComm 控件和

SerialPort 类设计方法的异同点。

    二.SerialPort 常用属性、方法和事件

    1 .命名空间
    System.IO.Ports
命名空间包含了控制串口重要的SerialPort 类,该类提供了同步

I/O 和事件驱动的 I/O 、对管脚和中断状态的访问以及对串行驱动程序属性的访问,所

以在程序代码起始位置需加入Using System.IO.Ports
    2
.串口的通讯参数
   
串口通讯最常用的参数就是通讯端口号及通讯格式( 波特率、数据位、停止位和校验

) ,在MSComm 中相关的属性是CommPortSettingsSerialPort 类与MSComm 有一些区别

:
    a.
通讯端口号
    [PortName]
属性获取或设置通信端口,包括但不限于所有可用的 COM 端口,请注意

该属性返回类型为String ,不是Mscomm.CommPortshort 类型。通常情况下,PortName

正常返回的值为COM1COM2……SerialPort 类最大支持的端口数突破了CommPort 控件

CommPort 属性不能超过16 的限止,大大方便了用户串口设备的配置。
    b.
通讯格式
    SerialPort
类对分别用[BaudRate][Parity][DataBits][StopBits] 属性设置

通讯格式中的波特率、数据位、停止位和校验位,其中[Parity][StopBits] 分别是枚

举类型ParityStopBitsParity 类型中枚举了Odd()Even()MarkNone

Space Parity 枚举了NoneOneOnePointFiveTwo
SerialPort
类提供了七个重载的构造函数,既可以对已经实例化的SerialPort 对象设置

上述相关属性的值,也可以使用指定的端口名称、波特率和奇偶校验位数据位和停止位

直接初始化 SerialPort 类的新实例。
    3
.串口的打开和关闭
    SerialPort
类没有采用MSComm.PortOpen=True/False 设置属性值打开关闭串口,相

应的是调用类的Open()Close() 方法。
    4.
数据的发送和读取
    Serial
类调用重载的WriteWriteLine 方法发送数据,其中WriteLine 可发送字符串

并在字符串末尾加入换行符,读取串口缓冲区的方法有许多,其中除了ReadExisting

ReadTo, 其余的方法都是同步调用,线程被阻塞直到缓冲区有相应的数据或大于

ReadTimeOut 属性设定的时间值后,引发ReadExisting 异常。
    5.DataReceived
事件
   
该事件类似于MSComm 控件中的OnComm 事件,DataReceived 事件在接收到了

[ReceivedBytesThreshold] 设置的字符个数或接收到了文件结束字符并将其放入了输入

缓冲区时被触发。其中[ReceivedBytesThreshold] 相当于MSComm 控件的[Rthreshold]

性,该事件的用法与MsComm 控件的OnComm 事件在CommEventcomEvSendcomEvEof 时是

一致的。

    三.SerialPort 的使用

    对于熟悉MSComm 控件的程序设计者,SerialPort 类是相当容易上手的。在进行串口

通讯时,一般的流程是设置通讯端口号及波特率、数据位、停止位和校验位,再打开端

口连接,发送数据,接收数据,最后关闭端口连接这样几个步骤。

    数据接收的设计方法在这里比较重要,采用轮询的方法比较浪费时间,在Visual

Basic 中的延时方法中一般会调用API 并用DOEvents 方法来处理,但程序不易控制,建议

采用DataReceived 事件触发的方法,合理的设置ReceivedBytesThreshold 的值,若接收

的是定长的数据,则将ReceivedBytesThreshold 设为接收数据的长度,若接收数据的结

尾是固定的字符或字符串则可采用ReadTo 的方法或在DataReceived 事件中判断接收的字

符是否满足条件。

    SerialPort 类读取数据的许多方法是同步阻塞调用,尽量避免在主线程中调用,可

以使用异步处理或线程间处理调用这些读取数据的方法。

    由于DataReceived 事件在辅线程被引发,当收到完整的一条数据,返回主线程处理

或在窗体上显示时,请注意跨线程的处理,C# 可采用控件异步委托的方法

Control.BeginInvoke 及同步委托的方法Invoke

    四.结束语

    .NET 平台下熟练使用SerialPort 类,可以很好地开发出串口通讯类程序,对于过

去使用MSComm 控件设计了一些通讯程序,也可以将MSComm 控件替换为SerialPort 类,当

然为了避免对以前的项目做大的改动,可以使用SerialPort 类设计一些与MSComm 控件具

有相同接口的类,在今后工业控制中,SerialPort 类将广泛地应用于串口通讯程序的设

计中,发挥着与MSComm 控件一样的作用。

 

 

 

 

C#2.0 中,SerialPort 运行方式

 SerialPort 中串口数据的读取与写入有较大的不同。由于串口不知道数据何时到达,因此有两种方法可以实现串口数据的读取。一、线程实时读串口;二、事件触发方式实现。
由于线程实时读串口的效率不是十分高效,因此比较好的方法是事件触发的方式。在SerialPort 类中有DataReceived 事件,当串口的读缓存 有数据到达时则触发DataReceived 事件,其中SerialPort.ReceivedBytesThreshold 属性决定了当串口读缓存中数 据多少个时才触发DataReceived 事件,默认为1
另外,SerialPort.DataReceived 事件运行比较特殊,其运行在辅线程,不能与主线程中的显示数据控件直接进行数据传输,必须用间接的方式实现。如下:

SerialPort spSend;  //spSend,spReceive
用虚拟串口连接,它们之间可以相互传输数据。spSend 发送数据
SerialPort spReceive;  //spReceive
接受数据
TextBox txtSend;  //
发送区
TextBox txtReceive;  //
接受区
Button btnSend;  //
数据发送按钮
delegate void HandleInterfaceUpdateDelegate(string text);  //
委托,此为重点
HandleInterfaceUpdateDelegate interfaceUpdateHandle;

public void InitClient()  //
窗体控件已在初始化
{
  interfaceUpdateHandle = new HandleInterfaceUpdateDelegate(UpdateTextBox);  //
实例化委托对象
  spSend.Open();  //SerialPort
对象在程序结束前必须关闭,在此说明
  spReceive.DataReceived += Ports.SerialDataReceivedEventHandler(spReceive_DataReceived);
  spReceive.ReceivedBytesThreshold = 1;
  spReceive.Open();
}

public void btnSend_Click(object sender,EventArgs e)
{
  spSend.WriteLine(txtSend.Text);
}

public void spReceive_DataReceived(object sender,Ports.SerialDataReceivedEventArgs e)
{
  byte[] readBuffer = new byte[spReceive.ReadBufferSize];
  spReceive.Read(readBuffer, 0, readBuffer.Length);
  this.Invoke(interfaceUpdateHandle, new string[] { Encoding.Unicode.GetString(readBuffer) });
}

private void UpdateTextBox(string text)
{
  txtReceive.Text = text;
}

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;

namespace TestSerialPort
{
    public partial class frmTESTSerialPort : Form
    {
        public frmTESTSerialPort()
        {
            InitializeComponent();
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        private Button button1;
        private TextBox txtSend;
        private TextBox txtReceive;
        private Label label1;
        private Label label2;

        /// <summary>
        ///
必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        ///
清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">
如果应释放托管资源,为 true ;否则为 false</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows
窗体设计器生成的代码

        /// <summary>
        ///
设计器支持所需的方法 - 不要
        ///
使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.txtSend = new System.Windows.Forms.TextBox();
            this.txtReceive = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            //
            // button1
            //
            this.button1.Location = new System.Drawing.Point(440, 379);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 0;
            this.button1.Text = "
发送";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            //
            // txtSend
            //
            this.txtSend.Location = new System.Drawing.Point(59, 12);
            this.txtSend.Multiline = true;
            this.txtSend.Name = "txtSend";
            this.txtSend.Size = new System.Drawing.Size(456, 164);
            this.txtSend.TabIndex = 2;
            //
            // txtReceive
            //
            this.txtReceive.Location = new System.Drawing.Point(59, 200);
            this.txtReceive.Multiline = true;
            this.txtReceive.Name = "txtReceive";
            this.txtReceive.Size = new System.Drawing.Size(456, 164);
            this.txtReceive.TabIndex = 2;
            //
            // label1
            //
            this.label1.Location = new System.Drawing.Point(13, 15);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(41, 12);
            this.label1.TabIndex = 0;
            this.label1.Text = "
发送";
            //
            // label2
            //
            this.label2.Location = new System.Drawing.Point(13, 213);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(41, 12);
            this.label2.TabIndex = 0;
            this.label2.Text = "
接收";
            //
            // frmTESTSerialPort
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(546, 434);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.txtReceive);
            this.Controls.Add(this.txtSend);
            this.Controls.Add(this.button1);
            this.Name = "frmTESTSerialPort";
            this.Text = "
串口试验";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

private void button1_Click(object sender, EventArgs e)
        {
            //
实例化串口对象( 默认:COMM1,9600,e,8,1)            
            SerialPort serialPort1 = new SerialPort();
            //
更改参数
            serialPort1.PortName = "COM1";
            serialPort1.BaudRate = 19200;
            serialPort1.Parity = Parity.Odd;
            serialPort1.StopBits = StopBits.Two;

            //
上述步骤可以用在实例化时调用SerialPort 类的重载构造函数
            //SerialPort serialPort = new SerialPort("COM1", 19200, Parity.Odd, StopBits.Two);

            //
打开串口( 打开串口后不能修改端口名, 波特率等参数, 修改参数要在串口关闭后修改)
            serialPort1.Open();

            //
发送数据
            SendStringData(serialPort1);
            //
也可用字节的形式发送数据
            //SendBytesData(serialPort1);
            
            //
开启接收数据线程
            ReceiveData(serialPort1);
            
        }

        

        //
发送字符串数据
        private void SendStringData(SerialPort serialPort)
        {
            serialPort.Write(txtSend.Text);
        }

        /// <summary>
        ///
开启接收数据线程
        /// </summary>
        private void ReceiveData(SerialPort serialPort)
        {
            //
同步阻塞接收数据线程
            Thread threadReceive=new Thread(new ParameterizedThreadStart(SynReceiveData));
            threadReceive.Start(serialPort);
            
             //
也可用异步接收数据线程
            //Thread threadReceiveSub = new Thread(new ParameterizedThreadStart(AsyReceiveData));
            //threadReceiveSub.Start(serialPort);
            
        }

        //
发送二进制数据
        private void SendBytesData(SerialPort serialPort)
        {
            byte[] bytesSend=System.Text.Encoding.Default.GetBytes(txtSend.Text );
            serialPort.Write(bytesSend, 0, bytesSend.Length);
        }

        //
同步阻塞读取
        private void SynReceiveData(object serialPortobj)
        {
            SerialPort serialPort = (SerialPort)serialPortobj;
            System.Threading.Thread.Sleep(0);
            serialPort.ReadTimeout = 1000;
            try
            {
                //
阻塞到读取数据或超时( 这里为2)
                byte firstByte=Convert.ToByte(serialPort.ReadByte());
                int bytesRead=serialPort.BytesToRead ;               
                byte[] bytesData=new byte[bytesRead+1];
                bytesData[0] = firstByte;
                for (int i = 1; i <=bytesRead; i++)
                    bytesData = Convert.ToByte( serialPort.ReadByte());
                txtReceive.Text = System.Text.Encoding.Default.GetString(bytesData);
            }
            catch(Exception e)
            {
                MessageBox.Show(e.Message);
                //
处理超时错误
            }
            
            serialPort.Close();

        }

        //
异步读取
        private void AsyReceiveData(object serialPortobj)
        {
            SerialPort serialPort = (SerialPort)serialPortobj;
            System.Threading.Thread.Sleep(500);
            try
            {
                 txtReceive.Text =   serialPort.ReadExisting();
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
                //
处理错误
            }
            serialPort.Close();
        }

    }


    static class Program
    {
        /// <summary>
        ///
应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new frmTESTSerialPort());
        }
    }

}

 

阅读更多
换一批

没有更多推荐了,返回首页