本人写的串口程序读数据经常不完整

.Net技术 码拜 8年前 (2016-02-25) 1628次浏览
软件功能是要读取一个光纤传感器返回的数值,先发送一个6字节的代码确认传感器已经准备好,传感器返回6个字节响应;接着上位机在发送6个字节读取数据,传感器会周期性的返回126个字节来响应。
原因是接收数据的长度有多种,所以本人用一个统一的响应函数里面的一个case语句来通过某一返回字段来判断返回数据,再执行发送的命令的响应函数。
但是问题就出在,当本人发送读数据的6个波长之后,返回的数据经常达不到126个字节,,,希望高手能帮本人解惑,最好能给出帮本人解决问题的语句。下面贴代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using PortsThirdTry.usefulFun;
using System.Threading;
namespace PortsThirdTry
{
    public partial class Form1 : Form
    {
        //全局预处理:创建实例,定义变量
        PortsThirdTry.usefulFun.ClassCrc16 classCrc16 = new PortsThirdTry.usefulFun.ClassCrc16();
        LittleFun littleFun = new LittleFun();
        SerialPort comm = new SerialPort();
        byte crcHi = 0x00;//crc高位
        byte crcLo = 0x00;//crc低位
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            //初始化下拉框参数
            string[] ports = SerialPort.GetPortNames();
            Array.Sort(ports);                      //冒泡程序对串口进行排序
            comboPortName.Items.AddRange(ports);
            comboPortName.SelectedIndex = comboPortName.Items.Count > 0 ? 0 : -1; //默认串口
            comboBaudRate.SelectedIndex = comboBaudRate.Items.IndexOf("115200"); //默认波特率
            comboDataBits.SelectedIndex = comboDataBits.Items.IndexOf("8");//默认数据位数
            comboStopBits.SelectedIndex = comboStopBits.Items.IndexOf("1");//默认停止位数
            comboParityBits.SelectedIndex = comboParityBits.Items.IndexOf("无");//默认校验方式
            comm.ReadTimeout = 2000;//超时时间,2s
            comm.DataReceived += choice;
        }
        #region 串口打开按钮
        private void btnOpenPort_Click(object sender, EventArgs e)
        {
            //根据当前串口对象,来判断操作
            if (comm.IsOpen)
            {
                comm.Close(); //打开时点击,则关闭串口
            }
            else//关闭时点击,则设置好端口,波特率后打开
            {
                comm.PortName = comboPortName.Text;
                comm.BaudRate = int.Parse(comboBaudRate.Text);
                comm.DataBits = int.Parse(comboDataBits.Text);
                switch (comboStopBits.Text)//设置停止位数
                {
                    case "1":
                        comm.StopBits = StopBits.One;
                        break;
                    case "1.5":
                        comm.StopBits = StopBits.OnePointFive;
                        break;
                    case "2":
                        comm.StopBits = StopBits.Two;
                        break;
                    case "无":
                        comm.StopBits = StopBits.None;
                        break;
                    default:
                        break;
                }
                switch (comboParityBits.Text)
                {
                    case "无":
                        comm.Parity = Parity.None;
                        break;
                    case "奇校验":
                        comm.Parity = Parity.Odd;
                        break;
                    case "偶校验":
                        comm.Parity = Parity.Even;
                        break;
                    default:
                        break;
                }
                try
                {
                    comm.Open();
                }
                catch (Exception)
                {
                    //捕获到异常信息,创建一个新的comm对象,之前的不能用了。
                    comm = new SerialPort();
                    //现实异常信息给客户。
                    MessageBox.Show("该串口被占用,请更换其他串口");
                }
            }
            //设置按钮的状态
            btnOpenPort.Text = comm.IsOpen ? "关闭串口" : "打开串口";
            if (btnOpenPort.Text == "关闭串口")
            {
                comboPortName.Enabled = false;
                comboBaudRate.Enabled = false;
                comboDataBits.Enabled = false;
                comboStopBits.Enabled = false;
                comboParityBits.Enabled = false;
            }
            else
            {
                comboPortName.Enabled = true;
                comboBaudRate.Enabled = true;
                comboDataBits.Enabled = true;
                comboStopBits.Enabled = true;
                comboParityBits.Enabled = true;
            }
        }
        #endregion
        #region 主机查询命令
        # region 设置扫描步长
        #region 开始扫描指令
    
        #region 读取波长数据
        int[] allWave = new int[30];//定义一个数组来存储波长数据
        private void btnGetWaveLength_Click(object sender, EventArgs e)
        {
            Byte ScanLength = Convert.ToByte(comboGetChannels.Text);
            byte[] sendGetWaveLengthData = new byte[4] { 0x10, 0x01, 0x06, ScanLength };//将要发送的数据组
            byte[] sendGetWaveLengthCrc = classCrc16.Crc16(sendGetWaveLengthData, out crcHi, out crcLo);//将要发送的crc
            byte[] sendGetWaveLength = new byte[sendGetWaveLengthData.Length + sendGetWaveLengthCrc.Length];//将要发送的完整数组
            sendGetWaveLengthData.CopyTo(sendGetWaveLength, 0);
            sendGetWaveLengthCrc.CopyTo(sendGetWaveLength, sendGetWaveLengthData.Length);
            comm.Write(sendGetWaveLength, 0, sendGetWaveLength.Length);//发送扫描数据
            //comm.DataReceived += comm_GetWaveLength;
            //comm.ReceivedBytesThreshold = 126;//设置接收的缓冲字节数
        }
        void comm_GetWaveLength(List<byte> a)//
        {
            byte[] receiveGetWaveLength = (byte[]) a.ToArray();
            int n = receiveGetWaveLength.Length;//comm.Read(receiveGetWaveLength, 0, n);
            while (n < 126)
            {
                Thread.Sleep(50);
            }
            byte[] receiveGetWaveLengthData = new byte[n-2];//将收到的数据中的crc以外的部分去掉,用于校验对比
            byte[] receiveGetWaveLengthCrc = new byte[2];
            Array.Copy(receiveGetWaveLength, 0, receiveGetWaveLengthData, 0, n-2);
            Array.Copy(receiveGetWaveLength, n-2, receiveGetWaveLengthCrc, 0, 2);
            byte[] receiveGetWaveLengthCrc2 = classCrc16.Crc16(receiveGetWaveLengthData, out crcHi, out crcLo);
            this.Invoke((EventHandler)(delegate             //用invoke更新界面
            {
                 if (!littleFun.equalOrNot(receiveGetWaveLengthCrc2, receiveGetWaveLengthCrc))
                {
                    MessageBox.Show("扫描波长数据错误,请重新设置");
                }
                else
                {
                    //写一个循环程序迭代光栅编号
                    for (int j = 3; j < 126; j++)
                    {
                        txtDataMessage.Text = "波长数据为:" + " ";
                        if ((j - 4) % 4 == 0)
                        {
                            int text = ((int)receiveGetWaveLength[j + 1] * 65536 + (int)receiveGetWaveLength[j + 2] * 256 + (int)receiveGetWaveLength[j + 3]) / 10000;
                            int x = (j - 4) / 4;
                            allWave[x] = text;
                            txtDataMessage.Text += allWave[x];
                        }
                        else
                        {
                        }
                    }
                }
            }));
        }
        #endregion
        #region 峰值功率数据
        
        #region 读取AD数据
        
        #region 查询当前阈值
        
        private void btnClose_Click(object sender, EventArgs e)
        {
            Close();
        }
        #region 手动设定阈值
        void choice(object sender, SerialDataReceivedEventArgs e)
        {
            int n = comm.BytesToRead;
            byte[] receiveData = new byte[n];
            comm.Read(receiveData, 0, n);
            int Data = receiveData[1];
            List<byte> sendData = new List<byte>();
            foreach (byte i in receiveData)
            {
                sendData.Add(i);
            }
            switch (Data)
            {
                case 3:
                    comm_receiveMachineSearchFun(sendData);
                    break;
                case 4:
                    comm_ScanLengthFun(sendData);
                    break;
                case 32:
                    comm_receiveBeginScanFun(sendData);
                    break;
                case 1://表示需要执行读波长函数
                    comm_GetWaveLength(sendData);
                    break;
                case 2:
                    comm_PeakRate(sendData);
                    break;
                case 5:
                    break;
                case 6:
                    if ((receiveData[6] & receiveData[4] & receiveData[5] & receiveData[7]) == 0)
                    {
                        comm_receiveGetThreshold(sendData);
                    }
                    else
                    {
                        comm_SetThreshold(sendData);
                    }
                    break;
            }
        }
    }
}
解决方案

15

基本上,看到

            while (n < 126)
            {
                Thread.Sleep(50);
            }

这类代码,你就应该清理这类垃圾。重新理解和设计流程。
之所以“死循环+阻塞”,而且是给出了一个既不足够长也不足够短的数值——50,这个完全瞎猜的数值,之所以这样设计,就是原因是这类通讯程序设计根本的理念就错误了。

10

有一点建议,先把数据接收和数据解析分离
数据解析会影响数据接收的

5

引用 LZ jinming122919 的回复:

软件功能是要读取一个光纤传感器返回的数值,先发送一个6字节的代码确认传感器已经准备好,传感器返回6个字节响应;接着上位机在发送6个字节读取数据,传感器会周期性的返回126个字节来响应。
原因是接收数据的长度有多种,所以本人用一个统一的响应函数里面的一个case语句来通过某一返回字段来判断返回数据,再执行发送的命令的响应函数。
但是问题就出在,当本人发送读数据的6个波长之后,返回的数据经常达不到126个字节,,,希望高手能帮本人解惑,最好能给出帮

List<byte> sendData = new List<byte>(); 这句代码放在方法的外面
参考下这个你就明白了。
http://bbs.csdn.net/topics/391975233#post-401292623

5

引用

返回的数据经常达不到126个字节

你这个问题问的有歧义..
是他发不到126还是你收不到126?..
假如是前者 可能这个模块有问题.
假如是后者 为啥你不判断126之后 组成一个”合法”的数据呢

10

即便是串口通讯,双方也应该定义一个通信协议,怎么能随随便便说收几个就收几个呢?
本人建议你先用串口调试助手先确定一下,是下位机没有发送预期的数据,还是人家发了你没有收到。假如是后者,你就观察一下下位机什么时候把126个字节全部发给你的,你可以等待这么久时间后一次性收上来,当然这很非常不专业的做法。正确的做法是在协议中指定一个剩余字节长度,然后按照这个协议中指定的长度去接受剩下的内容,还需要设计一个超时,这取决于上下位机之间的最长响应时延。接着说,假如你按照协议指定长度接收完毕,那么你收到的内容必定是双方约定的预期的内容,假如没收完就超时,说明下位机没法或超时时间太短了。

10

本人也遇到相似于你的问题,假设本人一次发送一帧十字节的数据,接受到的也的确是十字节     但是他们可能不是一帧了 而是在大于一帧小于十帧之间,字节和字节之间被断开单独算一帧了,而且单独算一帧的字节数还不确定

CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明本人写的串口程序读数据经常不完整
喜欢 (0)
[1034331897@qq.com]
分享 (0)