串口基本概念和C#串口编程例子

串口是计算机上一种非常通用设备通信的协议(不要与 通用串行总线Universal Serial Bus或者USB混淆)。大多数计算机包含两个基于RS232的串口。串口同时也是仪器仪表设备通用的 通信协议;很多GPIB兼容的设备也带有RS-232口。同时, 串口通信协议也可以用于获取远程采集设备的数据。
串口通信的概念非常简单,串口按位(bit)发送和接收字节。尽管比按 字节(byte)的 并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总常不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。
典型地, 串口用于ASCII码字符的传输。通信使用3根线完成:(1)地线,(2)发送,(3)接收。由于 串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但不是必须的。 串口通信最重要的参数是 波特率、 数据位、停止位和 奇偶校验。对于两个进行通行的端口,这些参数必须匹配:

波特率:

下面内容转自百度百科: http://baike.baidu.com/view/1746248.htm
这是一个衡量通信速度的参数。 它表示每秒钟传送的bit的个数。例如300波特表示每秒钟发送300个bit。当我们提到 时钟周期时,我们就是指波特率例如如果协议需要4800波特率,那么时钟是4800Hz。这意味着 串口通信在数据线上的采样率为4800Hz。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。
【波特率就是指每秒发送的码元个数,只不过这里码元是bit位0或1】

数据位:

这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个 数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和 奇偶校验位。由于实际数据位取决于 通信协议的选取,术语“包”指任何通信的情况。

停止位:

用于 表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台 设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正 时钟同步的机会。适用于停止位的位数越多,不同 时钟同步的容忍程度越大,但是数据传输率同时也越慢。

奇偶校验位:

在 串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和 奇校验的情况,串口会设置 校验位( 数据位 后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是 奇校验,
校验位位1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。

补充:

比特率
在数字信道中, 比特率是数字信号的传输速率,它用单位时间内传输的 二进制代码的有效位(bit)数来表示,其单位为 每秒比特数bit/s(bps)、每秒千比特数(Kbps)或每秒 兆比特数(Mbps)来表示(此处K和M分别为1000和1000000,而不是涉及计算机 存储器容量时的1024和1048576)。
波特率
波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,其单位为波特(Baud)。 波特率与 比特率的关系为:比特率=波特率X单个调制状态对应的二进制位数。
显然,两相调制(单个调制状态对应1个二进制位)的 比特率等于波特率;四相调制(单个调制状态对应2个二进制位)的比特率为波特率的两倍;八相调制(单个调制状态对应3个二进制位)的比特率为波特率的三倍;依次类推。
RS232是要用在近距离传输上最大距离为30M
RS485用在长距离传输最大距离1200M
 
 
C# 串口编程详解
转自: http://chowtrong.blog.163.com/blog/static/193026085201171932139652/

相信很多网友在学习C#编程时,遇到串口通讯的一系列问题。目前我也在学习C#串口编程类的基础知识,在网上也找了一些资料,但都存在一些问题,现在他们基础上再进行一定的修改,且更详细的表达如何实现串口编程,实现串口的发送与接收。本文目的在于:

1)总结C# 编程串口的属性定义、成员变量以及方法等问题;

2)掌握串口的发送与接收,利用虚拟串口来进行通讯,体验串口通讯;

实施步骤如下:

1、程序主界面如下:

 2、启动VS2005编程工具,新建一个项目,并自拟定项目名称和存放路径。

 3、按主界面样式设计好各个控件。

 4、代码实施(简单介绍——具体实验请参见源码,下文可下载):

(1)在Form1.cs程序头文件处添加代码:

         using System.Threading;//用于启用线程类;

         using System.IO.Ports;//用于调用串口类函数

 (2)添加成员变量

       串口的基本参数有:串口号,波特率,数据位,停止位等,如图所示:

 

        public string iPort = "com1"; //默认为串口1

        public int iRate = 9600; //波特率1200,2400,4800,9600
        public byte bSize = 8; //8 bits
        public int iTimeout = 1000; //延时时长
        public SerialPort serialPort1 = new SerialPort();//定义一个串口类的串口变量
        string serialReadString; //用于串口接收数据
        public bool IsCirlce;//判断是否选用循环发送数据
        public Thread Thd_Send; //开辟一个专用于发送数据的线程        
        public byte[] recb;  //用于存放接收数据的数组

 (3)程序开启,串口初始化

        private void Form1_Load(object sender, EventArgs e)
        {
            Microsoft.VisualBasic.Devices.Computer cmbCOM = new Microsoft.VisualBasic.Devices.Computer();

            foreach (string s in cmbCOM.Ports.SerialPortNames)
            {
                cmb_port.Items.Add(s);
            }
            IsCirlce = false;

        }

  (4)打开/关闭串口按钮代码

          //打开/关闭串口
        private void button5_Click(object sender, EventArgs e)
        {

            if (serialPort1.IsOpen)
            {
                serialPort1.Close();
                btnOpenCloseCOM.Text = "开启串口";
                msg.AppendText("串口被关闭……\r\n");
            }
            else
            {
                Parity myParity = Parity.None;  //奇偶校验对象
                StopBits MyStopBits = StopBits.One;  //停止位对象

                serialPort1.PortName = cmb_port.Text;
                serialPort1.BaudRate = iRate;  //波特率
                serialPort1.DataBits = bSize;  //数据位的长度
                serialPort1.Parity = myParity;  //奇偶校验位
                serialPort1.StopBits = MyStopBits;  //停止位
                serialPort1.ReadTimeout = iTimeout;  //读取超时【从有数据开始,读取的时间超过这个设置的值,则引发异常】

                serialPort1.Open();
                btnOpenCloseCOM.Text = "关闭串口";
                msg.AppendText("串口成功开启……\r\n");
            }
        }

        //开串口
        public bool OpenCom()
        {
            try
            {
                if (serialPort1.IsOpen)
                {
                    //mycom1.Close();
                    //mycom1.Open(); //打开串口
                    msg.AppendText("串口已打开\r\n");
                }
                else
                {
                    serialPort1.Open();//打开串口
                }
                return true;
            }
            catch (Exception e)
            {
                MessageBox.Show("错误:" + e.Message);
                return false;
            }

        }

  (5)发送按钮(主程序)

 //发送按钮
        private void button1_Click(object sender, EventArgs e)
        {
            if (t_send.Text == "")
            { MessageBox.Show("发送数据为空!"); return; }


            Thd_Send = new Thread(new ThreadStart(SendData));
            Thd_Send.Start();
        }

 

        //发送数据函数
        public void SendData()  //主要看SubSendData函数,这个函数本身不用太过于关注,无法是控件的异步调用修改控件的属性
        {
            if (System.Convert.ToBoolean(btnSend.Text == "发送"))
            {
                if (IsCirlce)
                {
                    btnSend.Invoke(new MethodInvoker(delegate { btnSend.Text="停止"; })); 

                     //MethodInvoker是无参数的系统定义好的简单方法委托,这里使用了匿名方法,此句执行结果是修改按钮的文本为“停止”

                    while (true)
                    {

                        if (IsCirlce && serialPort1.IsOpen && (System.Convert.ToBoolean(btnSend.Text =="停止")))
                        {
                            SubSendData();
                        }
                        else
                        { goto L1; }
                    }
                }
                else
                {
                    SubSendData();
                }
            }
            else
            {
              btnSend.Invoke(new MethodInvoker(delegate { btnSend.Text = "发送"; }));
            }
        L1:
            btnSend.Invoke(new MethodInvoker(delegate { btnSend.Text = "发送"; }));

        }

 

        //发送数据子函数,实际上是在新开的线程中执行这个函数
        public void SubSendData()
        {

            //byte[] ZhengHeHouData = new byte[mycom1.ReadBufferSize];
            //定义一个无空格二位16进制排列的数组,这样可控制用户输入
            byte[] ZhengHeHouData = NoSpace_2Hex_SendData_ZhengHe(); //这里是整合后的发送的数组,每二位组成一个16进制的数排列【sting转换为Byte类型】
            int SendNumB = 0;

            try
            {
                serialPort1.WriteLine(Dis_Package(ZhengHeHouData));  //Dis_Package函数是Byte类型转换为string类型【通过ASC2码把字符串自动转为字节数组发送】

                SendNumB = serialPort1.BytesToWrite;

                msg.Invoke(new MethodInvoker(delegate { msg.AppendText("\r\n发送数据!(" + serialPort1.BytesToWrite + "):" + Dis_Package(ZhengHeHouData)); }));

                Thread.Sleep(System.Convert.ToInt32(txtTimeGas.Text));
            }

            //mycom1.Read(ZhengHeHouData,1,1);

            //recb = mycom1.BytesToRead;

            //msg.AppendText("\r\n接收到数据包:" + Dis_Package(recb));

            //mycom1.Read(ZhengHeHouData, 0, ZhengHeHouData.Length);

            //if(recb.Length!=0)

            //msg.AppendText("\r\n接收到数据包:" + Dis_Package(recb));

            catch
            {
                //msg.AppendText("\r\n发送失败!");
                msg.Invoke(new MethodInvoker(delegate { msg.AppendText("\r\n发送失败!"); }));

                return;
            }
        }

        //去掉发送数组中的空格
        public string Delspace(string txt_SendData)
        {
            string Infact_SendData = "";
            for (int i = 0; i < txt_SendData.Length; i++)  //txt_SendData.Length为发送框内的数据
            {
                if (txt_SendData[i] != ' ')
                    Infact_SendData += txt_SendData[i];
            }
            return Infact_SendData;
        }
       
        //提取数据包--数组函数,返回整合后的数据,无空格二位16进制排列的数组
        public byte[] NoSpace_2Hex_SendData_ZhengHe()
        {
            string NoSpace_SendData = Delspace(t_send.Text);   //去掉所有空格,整合数据
            byte[] SendData = new byte[50];   
            int j = 0;
            for (int i = 0; i < NoSpace_SendData.Length; i = i + 2, j++) //将无空格发送的数据每二位组成一个数,存放在新的数组SendData中
                SendData[j] = Convert.ToByte(NoSpace_SendData.Substring(i, 2), 16);
            byte[] Last_SendData = new byte[j];  //Last_SendData最终返回的发送数据
            Array.Copy(SendData, Last_SendData, j); //复制整合后的数据,放入最终返回的数组里,以便调用
            return Last_SendData;  //最终返回的发送数据

        }

        //显示包信息,以每二位加一个空格显示数据
        public string Dis_Package(byte[] reb)
        {
            string temp = "";
            foreach (byte b in reb)
                temp += b.ToString("X2") + " "; //以每二位加一个空格显示数据
            return temp;
        }

  (6) 清空列表显示
        private void button3_Click(object sender, EventArgs e)
        {
            t_send.Text = string.Empty;  //清空发送端
            msg.Text = string.Empty;     //清空信息列表框
        }

  (7)应用设置按钮的代码

       //应用设置
        private void button2_Click(object sender, EventArgs e)   //串口初始化,对应图中“应用设置”按钮
        {
            Parity myParity = Parity.None;
            StopBits MyStopBits = StopBits.One;
            if (serialPort1.IsOpen)
            {
                //msg.AppendText("串口处于开启状态,若要重新更换端口,请先关闭!\r\n");
                serialPort1.Close();
            }
            if (cmb_port.Text == "")
            {
                msg.AppendText("串口不能为空,请选择串口!\r\n");
            }
            else
            {
                serialPort1.PortName = Convert.ToString(cmb_port.Text);  //1,2,3,4

                serialPort1.BaudRate = Convert.ToInt16(t_rate.Text); //1200,2400,4800,9600
                serialPort1.DataBits = Convert.ToByte(t_bytesize.Text, 10); //8 bits
                serialPort1.Parity = myParity; // 0-4=no,odd,even,mark,space
                serialPort1.StopBits = MyStopBits;
                //iTimeout=3;
                if (this.OpenCom())
                    msg.AppendText("串口初始化成功……\r\n");
                else
                    msg.AppendText("串口初始化失败!\r\n");
            }
        }

 

 (8)  程序关闭,结束串口
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            serialPort1.Close();
        }

 (9) 用于显示接收数据,这个事件处理程序会自动开一个线程然后执行
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            serialReadString = "";
            serialReadString += serialPort1.ReadExisting();  //读取所有数据,返回字符串【字节数组通过ASC2码翻译成字符串】
            msg.Invoke(new MethodInvoker(delegate { msg.AppendText("\r\n接收数据:" + serialReadString); }));
        }

 (10)控制输入

        //发送文本框内限制输入,须偶数位
        private void t_send_TextChanged(object sender, EventArgs e)
        {
            btnSend.Enabled = false;
            string SendDataLength = Delspace(t_send.Text);   //去掉所有空格,整合数据

            if (SendDataLength.Length % 2 != 0)
            {
                t_send.Focus();
            }
            else
            {
                btnSend.Enabled = true;
            }
        }

  (11)复选框程序(是否启用无限循环)

        private void chkCircle_CheckedChanged(object sender, EventArgs e)
        {
            if (IsCirlce)
            {
                IsCirlce = false;
            }
            else
            {
                IsCirlce = true;
            }
        }

 5、虚拟串口配置(可下载此软件,按照下载后的软件进行配置说明即可使用)

串口基本概念和C#串口编程例子_第1张图片

 

6、打开UartAssist.exe软件,运行如下所示:

串口基本概念和C#串口编程例子_第2张图片

 7、在配置好VSPM后,虚拟串口有3、4、5、6共四个串口,启动VSPM后,再运行串口通讯选择串口4,此时串口通讯程序与上面的 UartAssist.exe软件串口3进行了互联,如下图所示。选择串口4后,点击[应用设置]按钮,此时文本框显示:串口初始化成功....

串口基本概念和C#串口编程例子_第3张图片

  8、发送测试,在设置数据包文本框内输入:12345678

串口基本概念和C#串口编程例子_第4张图片

  9、接收数据,如图:

串口基本概念和C#串口编程例子_第5张图片

    10、源码下载。见原文 

你可能感兴趣的:(串口编程)