现在的位置: 首页 > 综合 > 正文

C# 串口通讯

2017年11月29日 ⁄ 综合 ⁄ 共 4227字 ⁄ 字号 评论关闭

 

本人做的一个C#串口上位机程序,最近有空就写了点感悟,见笑大方了。

 

一,软件概述

本上位机采用Visual C# 2010编写,用于与单片机通信,发送并接收固定格式的数据包。

上位机每次点击“发送”按钮后将发送18字节给下位机,发送包的格式为:

包头:0xAA;命令号:0x01;帧长:0x0D;帧数据13字节,由界面上的选项决定(如发射频率6MHz,代表0x000600三字节),和校验1字节,包尾:0xa5;共183+13+2)字节。此18字节将在发送区中显示。具体的包格式就不说了,都是自己定义的。

下位机接收到数据后,将发送5字节的数据,上位机接收到数据后可以判定数据是否正确,校验是否正确等。下位机发送的数据由上位机接收后将在接收区中显示

二,具体实现核心代码

1)。运行软件后,将自动显示本机的串口(如果有的话),并打开。FormLoad代码为:

//列出本机所有串口

            string[] ports = SerialPort.GetPortNames();

            Array.Sort(ports);

            comboBoxPortSelect.Items.AddRange(ports);

//默认选中第一个

            comboBoxPortSelect.SelectedIndex = 0;

//打开本计算机的串口            

            if (mycomm.IsOpen) {

                mycomm.Close();

            }

mycomm.PortName = comboBoxPortSelect.Text;

             mycomm.ReadTimeout = 32;

            try  {

                mycomm.Open();

                btnOpen.Text = "关闭串口";//按钮打开

                lblToolStripStatus.Text = "串口打开成功!";//状态栏

            }catch{

                btnOpen.Text = "打开串口";

                MessageBox.Show("没有发现串口或串口已被占用!");

                lblToolStripStatus.Text = "串口打开失败!";

            }

            //添加事件注册

            mycomm.DataReceived += comm_DataReceived;          

2)。点击发送按钮,发送18字节数据,代码为:

//存放待发送的一包数据(包括帧头,命令号,帧长,帧数据,校验,帧尾)

            byte[] package = new byte[18]; int j = 0; int k = 3;

            package[0] = 0xAA;//帧头

            package[1] = 0x01;//命令号

            package[2] = 0x0D;//帧长            

            byte parity = 0x01+0x0d;//和校验:(命令号+帧长+帧数据)&7f。 

            //存放帧数据(13字节)

            byte[] realData = new byte[13];

            string data = getMode().Append(getTxFre(txtTxFre.Text).Append(getDuoPuLe(txtDuoFre.Text)).Append(    getBand()).Append(getTxPower()).Append(getRxFre(txtRxFre.Text)).Append(getRxBandAndDelay()).Append(getAGC())).ToString();//待发送的二进制数据,略。。。            

            for (int i = 0; i < data.Length; i += 8)

            {

                //每八位截取,转为十进制的byte,再放入byte数组中 

                realData[j++] = (byte)Convert.ToInt32(data.Substring(i, 8), 2);               

            }            

            foreach (byte b in realData) {           

                 parity += b;//校验码计算      

                 package[k++] = b; }

            parity &= 0x7F; //校验码计算完成          

            package[16] = parity;//和校验(1字节)

            package[17] = 0xa5;//帧尾

            if (!mycomm.IsOpen)

            {

                MessageBox.Show("串口没有打开,请打开串口!");

                return;

            }

            mycomm.Write(package, 0, 18);//向串口发送一包(18字节)的数据 

3)。 接收数据的事件代码:

private void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)

        {           

            int n = mycomm.BytesToRead;

            byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据        

            mycomm.Read(buf, 0, n);//读取缓冲数据

            builder.Clear();//清除字符串构造器的内容

            //因为要访问ui资源,所以需要使用invoke方式同步ui

            this.Invoke((EventHandler)(delegate

            {    //依次的拼接出16进制字符串

                foreach (byte b in buf)

                {

                    builder.Append(b.ToString("X2") + " ");                   

                }

                //直接按ASCII规则转换成字符串

                //builder.Append(Encoding.ASCII.GetString(buf));   

                txtGet.Text = "";

                txtGet.AppendText(builder.ToString());

            }));           

            //判断返回正确与否

            if (buf[3] == 0x00){

                lblToolStripRxStatus.Text = "返回值正确!";

            }                             

            else if(buf[3] == 0x01){

                lblToolStripRxStatus.Text = "返回值错误!";

            } 

            //判断校验是否正确

            byte rxParity;//接收校验和 :( 命令号+帧长+帧数据)&7f

            rxParity = 0x01+0x01;

            rxParity += buf[3];

            rxParity &= 0x7F;

            if (rxParity != buf[4]) {

                lblToolStripRxStatus.Text += "校验错误!";

            }  

        }

三,总结

  总体上说,C#串口通信比较简单,发送数据用:

   byte[] package = new byte[18]{0};

   mycomm.Write(package, 0, 18);

接收数据在串口的

 private void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)

  { 

int n = mycomm.BytesToRead; 

byte[] buf = new byte[n]; 

comm.Read(buf, 0, n);

 }事件中。主要是Read/Write两个方法。

  需要注意的地方是每次接收到的数据未必是一个完整的包(尤其是包较长时),例如刚开始本人本机调试(本地收发)时收到的18字节经常是先收到9字节,再收到9字节,后者先2,再8,再8字节。导致接手区的数据不对(没有18字节,只有最开始的92字节),此时可以用一个缓冲数组保存接收到的数据,直到接收的长度正确或到包尾了再进行处理。

抱歉!评论已关闭.