• 转 C# 串口编程遇到的问题以及解决方法 武胜


    最近在做的项目中涉及到串口通信编程,队友在做这一模 块的时候遇到了一个相信很多人都可能遇到的问题,那就是接受数据的冲突或者丢失。队友让我帮他调试的时候,足足花了近两个小时才把问题给解决了,现觉得有 必要总结一下以给遇到类似问题的朋友一个思路吧~

          问题是这样的,从相应的硬件设备每隔2秒便发送一个大小为15的字节数据,存的是16进制数据,每条数据以7E开头以7E结尾。例如:7E 09 01 1A ... 7E。但是PC端在接受显示的时候却出现了问题,如前五个数据接受正常,接下来的都是以00填充,而剩下的10个数据却出现在下条数据显示的位置。如下所 示: 

          7E 09 01 1A 5C 00 00 00 00 00 00 00 00 00 00 /////// 03 06 1A 2C 3D 09 6C 32 12 7E 00 00 00 00 00 00/////// ........

          总之感觉错位了,对了,先把PC端代码贴一下吧:      

         


    public partial class Form1 : Form
        {
            
    /// <summary>
            
    /// 接受数据的串口类 
            
    /// </summary>
            SerialPort spReceive;
            
    delegate void ReceivData(byte[] bytes);

            
    public Form1()
            {
                Form1.CheckForIllegalCrossThreadCalls 
    = false;
                InitializeComponent();
              
                spReceive 
    =new SerialPort("COM5"57600, Parity.None, 8, StopBits.One);             
                spReceive.Open();
                
                
    //设置触发 DataReceived事件的阀值,在调试中发现这个不起作用,不解~      
                spReceive.ReceivedBytesThreshold =15;            
                spReceive.DataReceived 
    +=new System.IO.Ports.SerialDataReceivedEventHandler(spReceive_DataReceived);            
            }

            
    /// <summary>
            
    /// 更新接受到得数据到UI 界面显示
            
    /// </summary>
            
    /// <param name="bytes"></param>
            public void UpdateReceiveToUI(byte[] bytes)
            {
                
    if (txtReceive.InvokeRequired)
                {
                    ReceivData dl 
    = new ReceivData(UpdateReceiveToUI);
                    
    object arg = bytes;
                    txtReceive.Invoke(dl, arg);
                }
                
    else
                    txtReceive.Text 
    += "/////////" + BitConverter.ToString(bytes);
            }

            
    /// <summary>
            
    /// 接收到串口数据触发的事 件
            
    /// </summary>
            
    /// <param name="sender"></param>
            
    /// <param name="e"></param>
            public void spReceive_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
            {
                
    byte[] recevied = new byte[15];
                
    int size = spReceive.Read(recevied, 0, recevied.Length);

                if (size >0)

                    UpdateReceiveToUI(recevied);

                

            }

        }

           在调试的过程中,当我spReceive_DataReceived函数中的byte[] recevied = new byte[15]; 后面在加上个初始化,如下:

           for( int i=0; i<recevied.Lengthl;i++)

                 recevied[i]=11;

        

           这个时候界面显示的时候,凡是以00显示的地方都被11替换了。这肯定了这些00是由于传送的15个字节数组未被完全填充到我们定义的字节数组中,即 Read(byte [] bytes,int offset,int size)函数未一次性将字节数组填充完,而是分了两次。但是在此过程中我使用的是SerialPort类的DataReceived事件,即没收到一条 数据便会触发,而这儿的现象仿佛是事件被触发了多次。后来自己在SerialPort类的属性里面找了下,突然发现了有个 ReceivedBytesThreshold属性,从这个属性可以看出我们可以控制接受多少个字节触发一次DataReceived事件。于是,我便将 阀值设为15个字节,满以为问题解决了,可是结果却~

           囧啊,可是这个属性也给了我们一些提示,也找到了问题的症结所在。那就是,DataRecevied事件触发的阀值,即接受到多少个字节数触发一次偏小, 结果导致了我们一条大小为15个字节的数据触发了两次DataReceived事件。要解决这个问题,我们可以沿着这个思路走下去,那就是延迟 DataRecevied事件的触发直到我们一条数据接受完毕。

           所以在spReceive_DataReceived事件函数中,我们做如下修改:

           byte[]  receiveBuffer=new byte[15];  

           public void spReceive_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)

           {

                  int bytesRead = 0;  

                  spReceive.ReceivedBytesThreshold = 100000;     //延迟DataRecevied事件的再次触发

                  

                 while (true)

                 { 

                       if (bytesRead >= 15)      //这儿的15是我一条数据的大小

                      { 

                            byte[] bytes = new byte[bytesRead]; 

                            Array.Copy(receiveBuffer, 0, bytes, 0, bytesRead);       

                            Array.Clear(receiveBuffer, 0, bytesRead); 

                            UpdateReceiveToUI(bytes);

                            bytesRead = 0; 

                            break; 

                      } 

                      try

                     {

                        receiveBuffer[bytesRead] = (byte)spReceive.ReadByte();

                        ++bytesRead;

                     }

                    catch (Exception ex1)

                    {

                        MessageBox.Show(ex1.Message);

                        break;

                    } 

     

                    bytesRead = 0;

                    spReceive.ReceivedBytesThreshold = 1;            //将延迟改回正常

                 } 

           } 

         

           经过上述的改动,便可以解决读取串口数据冲突与丢失的问题了~不周之处,还望各位斧正~

  • 相关阅读:
    初来乍到
    OpenGl基础篇(1.0)
    说说结构化方法和面向对象方法
    浅谈软件项目管理
    测试篇
    结构化与面向对象化之应用比较
    敏捷软件开发VS传统软件开发
    SOSO街景地图 API (Javascript)开发教程(1)- 街景
    阿里实习内推面经
    Android工程开发笔记<一>
  • 原文地址:https://www.cnblogs.com/zeroone/p/1700227.html
Copyright © 2020-2023  润新知