c#中,确保数据接收完整的 串口处理程序

 SerialPort 方法

https://msdn.microsoft.com/zh-tw/library/system.io.ports.serialport.getportnames.aspx

C# 串口通信总结

http://www.cnblogs.com/binfire/archive/2011/10/08/2201973.html

如果一些厂家比较懒的话,没有提供相应的dll,我们只能对它进行串口通信编程了。以前从没接触过串口编程,最近在一个项目中有几个地方都需要采用串口通信,跟公司一个老手请教后,感觉学到了很多东西,特在此做个总结:

     

     一:首先我们来认识下什么是串口:

我们可以看到该串口的属性,在C#中我们使用SerialPort类来表示串口

    二:串口调试工具:

在对串口进行编程时候,我们要向串口发送指令,然后我们解析串口返回的指令。在这里向大家推荐一款工具。

        串口调试助手.exe

void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{

//接收数据
string str = "";
do
{
int count = serialPort.BytesToRead;
if (count <= 0)
break;
byte[] readBuffer = new byte[count];

Application.DoEvents();
serialPort.Read(readBuffer, 0, count);
str += System.Text.Encoding.Default.GetString(readBuffer);

} while (serialPort.BytesToRead > 0);
listBox1.Items.Add(str);
}

 

 

C# 编写的串口通信程序

http://www.cnblogs.com/zhaoming510/p/3965061.html

如果,翻看我之前的博客,会找到一篇用I/O模拟IIC通信的程序文章。好吧,如果找不到可以点击这里,这里就不在赘述了,系统也已经完全调试通过了。

今天的任务是,把测试得到的数据在上位机的界面上显示出来,于是键盘手花了两天的时间模仿着巨人的肩膀通过了用C#编写的界面程序,界面很简单就像下面显示的一样。

 

c#串口系列文章:

http://bbs.csdn.net/topics/350038794

第一个 串口打开的时候你是要TRY CATCH的 不然会异常
第二个 尽量使用READ和WRITE不要用WRITELINE和READLINE,后两个是要读换行符的
第三个 查看你的串口初始化是否初始化好了

 

为什么我按发送界面就卡住了啊,
程序就不往下执行了。
为什么啊?
没弄明白,请高手指点



如果你只知道编写同步操作代码,就不可能做出专业的产品,而只是知道.net有那些语句而已。

C# 串口操作系列(1) -- 入门篇,一个标准的,简陋的串口例子。

http://blog.csdn.net/wuyazhe/article/details/5598945

C# 串口操作系列(2) -- 入门篇,为什么我的串口程序在关闭串口时候会死锁 ?

http://blog.csdn.net/wuyazhe/article/details/5606276

C# 串口操作系列(3) -- 协议篇,二进制协议数据解析

http://blog.csdn.net/wuyazhe/article/details/5627253

C# 串口操作系列(4) -- 协议篇,文本协议数据解析

http://blog.csdn.net/wuyazhe/article/details/5657188

C# 串口操作系列(5)--通讯库雏形

http://blog.csdn.net/wuyazhe/article/details/5797673

GSM Modem

一直在csdn上回关于gsm modem方面的AT指令问题,之前花了不少时间,想想还是补上整理后的内容

http://blog.nonocast.cn/post/4082

在C#中使用SerialPort类实现串口通信 遇到多线程问题

http://bbs.21ic.com/blog-357347-66360.html

 

4...添加数据接收的事件

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)

使用中的一些常见问题

C#中SerialPort类中DataReceived事件GUI实时处理方法(来自wanglei_wan@yahoo.com.cn 的看法)
MSDN:从 SerialPort 对象接收数据时,将在辅助线程上引发 DataReceived 事件。由于此事件在辅助线程而非主线程上引发,因此尝试修改主线程中的一些元素(如 UI 元素)时会引发线程异常。如果有必要修改主 Form 或 Control 中的元素,必须使用 Invoke 回发更改请求,这将在正确的线程上执行.进而要想将辅助线程中所读到的数据显示到主线程的Form控件上时,只有通过Invoke方法来实现 
下面是代码实例: 
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
   int SDateTemp = this.serialPort1.ReadByte();
   //读取串口中一个字节的数据
   this.tB_ReceiveDate.Invoke(   
//在拥有此控件的基础窗口句柄的线程上执行委托Invoke(Delegate)
//即在textBox_ReceiveDate控件的父窗口form中执行委托.
new MethodInvoker(            
/*表示一个委托,该委托可执行托管代码中声明为 void 且不接受任何参数的任何方法。 在对控件的 Invoke 方法进行调用时或需要一个简单委托又不想自己定义时可以使用该委托。*/
delegate{                 
    /*匿名方法,C#2.0的新功能,这是一种允许程序员将一段完整代码区块当成参数传递的程序代码编写技术,通过此种方法可 以直接使用委托来设计事件响应程序以下就是你要在主线程上实现的功能但是有一点要注意,这里不适宜处理过多的方法,因为C#消息机制是消息流水线响应机制,如果这里在主线程上处理语句的时间过长会导致主UI线程阻塞,停止响应或响应不顺畅,这时你的主form界面会延迟或卡死      */                   
this.tB_ReceiveDate.AppendText(SDateTemp.ToString());//输出到主窗口文本控件
this.tB_ReceiveDate.Text += " ";}
    )
    );
}

如何知道当前电脑有哪个串口

在窗体上添加一个comboBox控件。

然后使用comboBox1.Items.AddRange(System.IO.Ports.SerialPort.GetPortNames()); 或者

string[] portList = System.IO.Ports.SerialPort.GetPortNames();
            for (int i = 0; i < portList.Length; ++i)
            {
                string name = portList;
                comboBox1.Items.Add(name);
            }

例子讲解与源码:

Serial Communication using C# and Whidbey

http://www.codeproject.com/Articles/8605/Serial-Communication-using-C-and-Whidbey

[转载]C#中串口通信编程:总结了 串口类,

http://blog.pfan.cn/sword2008/38218.html

 

C#:
1)         添加引用
     using System.IO.Ports;
2)         定义SerialPort类实例
      private SerialPort SpCom2 = new SpCom ("COM2", 9600,Parity.None, 8, StopBits.One);
3)         设置通讯端口号及波特率、数据位、停止位和校验位。
        SpCom.PortName = "COM1";
        SpCom.BaudRate = 9600;
        SpCom.Parity = IO.Ports.Parity.None;
        SpCom.DataBits = 8;
        SpCom.StopBits = IO.Ports.StopBits.One;
        或是定义时直接初始化
         private SerialPort SpCom2 = new SpCom ("COM2", 9600,Parity.None, 8, StopBits.One);
4)         发送数据
     SpCom.Write(TextSendData.Text);
5)         添加接受事件
a)        在运行时将事件与事件处理程序相关联(通过委托实现)
SpCom.DataReceived += new SerialDataReceivedEventHandler(SpCom2_DataReceived);
说明:
SerialDataReceivedEventHandler 委托 表示将处理 SerialPort 对象的 DataReceived 事件的方法
b)        添加事件处理程序(签名一定要一致)
              private void SpCom_DataReceived(object sender, SerialDataReceivedEventArgs e)
6)         读取数据
        string data = SpCom .ReadExisting();

 

c#中,确保数据接收完整的 串口处理程序:

    private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {

            /*
             int n = comm.BytesToRead;//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致  
            byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据  
            received_count += n;//增加接收计数  
            comm.Read(buf, 0, n);//读取缓冲数据  
            builder.Clear();//清除字符串构造器的内容  
            //因为要访问ui资源,所以需要使用invoke方式同步ui。  
            this.Invoke((EventHandler)(delegate  
            {  
                //判断是否是显示为16禁止  
                if (checkBoxHexView.Checked)  
                {  
                    //依次的拼接出16进制字符串  
                    foreach (byte b in buf)  
                    {  
                        builder.Append(b.ToString("X2") + " ");  
                    }  
                }  
                else  
                {  
                    //直接按ASCII规则转换成字符串  
                    builder.Append(Encoding.ASCII.GetString(buf));  
                }  
                //追加的形式添加到文本框末端,并滚动到最后。  
             * 
             * //要解析字符串,根据长度定位,根据特殊字符定位,来解析数据。(还是要确保一个完整的数据包接收完整,并处理完整)
             * 
                this.txGet.AppendText(builder.ToString());  
                //修改接收计数  
                labelGetCount.Text = "Get:" + received_count.ToString();  
            }));  
             */

            int n = serialPort1.BytesToRead;//待读字节个数
            byte[] buf = new byte[n];//创建n个字节的缓存
            serialPort1.Read(buf, 0, n);//读到在数据存储到buf

            //1.缓存数据
            buffer.AddRange(buf);//不断地将接收到的数据加入到buffer链表中
            //2.完整性判断
            while (buffer.Count >= 4) //至少包含帧头(2字节)、长度(1字节)、功能位(1字节);根据设计不同而不同
            {
                //2.1 查找数据头
                if (buffer[0] == 0x0AA) //传输数据有帧头,用于判断. 找到帧头  AA AA 0A 
                {
                    int len = buffer[2];
                    //int len = 79;
                    if (buffer.Count < len + 4) //数据区尚未接收完整,
                    {
                        break;//跳出接收函数后之后继续接收数据
                    }
                    //得到完整的数据,复制到ReceiveBytes中进行校验
                    buffer.CopyTo(0, ReceiveBytes, 0, len + 4);//
                    byte jiaoyan; //开始校验
                    jiaoyan = 0x01;//jiaoyan = this.JY(ReceiveBytes);

                    if (jiaoyan != ReceiveBytes[3]) //验证功能位失败    if (jiaoyan != ReceiveBytes[len+3])
                    {
                        buffer.RemoveRange(0, len + 4);//从链表中移除接收到的校验失败的数据,
                        //MessageBox.Show("数据包不正确!");//显示数据包不正确,
                        continue;//继续执行while循环程序,
                    }
                    buffer.RemoveRange(0, len + 4);
                    //执行其他代码,对数据进行处理。
                    //解析5 6, 7 8字节的经纬度.
                    DataProgress();
                }
                else //帧头不正确时,记得清除
                {
                    buffer.RemoveAt(0);//清除第一个字节,继续检测下一个。
                }
            }
        }

 

对串口而言,不存在完整数据长度,都是以Byte为单位;一般来说,通常是透过时间跟固定数量来进行接收动作。(可能要看各PC的OS或Driver的设置情况)

通常在串口处理上,要确认接收数据完整,是在PC软件上进行接收、保存跟判断的动作,在完整收到後,才进行显示或处理。



我现在的处理方法是根据串口接收事件的实际效果来的:接收的数量到达了一个数量后,我才开始处理,这样比原来的简单的判断效果好多了。

private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
int byteNumber = SerialPort.BytesToRead;

Common.Delay(20);


//延时等待数据接收完毕。
while ((byteNumber < SerialPort.BytesToRead) && (SerialPort.BytesToRead < 4800))
{
byteNumber = SerialPort.BytesToRead;
Common.Delay(20);
}


int n = SerialPort.BytesToRead; //记录下缓冲区的字节个数 
byte[] buf = new byte[n]; //声明一个临时数组存储当前来的串口数据 
SerialPort.Read(buf, 0, n); //读取缓冲数据到buf中,同时将这串数据从缓冲区移除 


//设置文字显示
Control.CheckForIllegalCrossThreadCalls = false;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++)
{
string s;
if (buf[i] < 16)
s = "0" + Convert.ToString(buf[i], 16).ToUpper() + " ";
else
s = Convert.ToString(buf[i], 16).ToUpper() + " ";


sb.Append(s);
}
textBox1.Text = sb.ToString();


}
catch (Exception ee)
{
}
}

 

posted @ 2016-01-13 09:38  星雷热忱  阅读(54989)  评论(2编辑  收藏  举报