C#串口通讯,serialPort1_DataReceived接收数据异常。

qhxzgl2006 2017-01-23 10:26:49
serialPort1_DataReceived触发事件:
下位机发送某指令为1s一帧,但是该事件触发后显示在上位机是几毫秒一个,而且上位机对该指令做出的响应未生效,上位机仍在几毫秒一帧的显示。而且当下位机发送间隔3s的某帧时,上位机显示的会丢帧。求大神解救。小白一枚,并不太懂C#,是在别人的软件基础上新增的,因为别人离职了。几位被邀请的朋友是系统推荐的

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
//string FF = serialPort1.ReadTo("FF");
int num = serialPort1.BytesToRead;
byte[] MsgRec = new byte[num];
serialPort1.Read(MsgRec, 0, num);
//builder.Clear();
if (num >=10)
{
MsgRectextBox.AppendText("Recieved:" + "\r\n");
if (MsgRec[0] == 0xFF && MsgRec[2] != 0xFF && MsgRec[num-1] ==0xFF )//判断接收的帧是否符合协议,帧头帧尾
{
int Length = -1;
int i = 0;
MsgRecCount++;
MsgTransCount++;
while (MsgRec[8 + i] != 0xFF)//获取该帧长度
{
i++;
if (8 + i >= num)
break;
}
Length = Length + i + 10;
//byte[] MsgRec1 = new byte[1024 * 1024];
MsgRec = dataAnalyzer.ChangFE012FFFE002FE(MsgRec.Take(Length).ToArray());
string result = dataAnalyzer.ToHexString(MsgRec);
MsgRectextBox.AppendText(result + "\r\n");
//其他处理接收帧代码,及时响应;
}
}
}

...全文
1966 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
qhxzgl2006 2017-02-03
  • 打赏
  • 举报
回复
引用 6 楼 diaodiaop 的回复:
如果你有尾,. 可以这样...(我的串口运行代码)

 private void sp_WT230_DataReceived(byte[] data)
        {
            buffer_wt230.AddRange(data);
            if (buffer_wt230.Find(d => d == 0x0d) != 0)
            {
                byte[] ReceiveBytes = new byte[buffer_wt230.FindIndex(d => d == 0x0d)];
                buffer_wt230.CopyTo(0, ReceiveBytes, 0, ReceiveBytes.Length);
                buffer_wt230.Clear();
        }
一个全局的list<byte> buffer_wt230 每次都累加然后判断 是否有尾.(在上面代码中 我的协议是已0x0d结尾的) 然后最终通过解析 得到一个ReceiveBytes 这个就是 一组合法的数据. 当然这只是其中的一种办法..不过大体都是根据协议来下手
不好意思,再问一下。我现在也建立了list。但是我的数据帧协议是帧头、数据域、校验位、帧尾。其中帧头和帧尾均是“FF”,这里我该如何处理呢?求教。
qhxzgl2006 2017-02-03
  • 打赏
  • 举报
回复
引用 10 楼 vpjian 的回复:
serialPort1_DataReceived 函数里面不要有耗时长的操作,不要有操作界面的操作,这都会导致有一定机率丢失数据并且你根本查不出原因。另外,有可能会每收到一个字节触发一次serialPort1_DataReceived ,所以收到的数据要先存到全局变量。 正确的处理方式之一应该是这样的(这种是异步方案。另一种方案是建一个线程使用同步读取,使用超时异常触发处理,我用这个方案更多一些):

   
        int dataLen = 0;//有效数据长度
        byte[] buff = new byte[256];//分配一个足够大的缓冲区,
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            int num = serialPort1.BytesToRead;
            if (num + dataLen > buff.Length) num = buff.Length - dataLen;//防止数据溢出(其实有点多余,缓冲区已经足够大了,就算是115200的波特率也不可能爆缓冲)
            serialPort1.Read(buff, dataLen, num);
            dataLen += num;
            if (dataLen >= 10)//如果你是定长帧这个是没问题的,否则.....呵呵
            {
                byte[] data = new byte[dataLen];
                Buffer.BlockCopy(buff, 0, data, 0, dataLen);//复制有效数据
                dataLen = 0;
                Action<byte[]> a = new Action<byte[]>(showData);
                a.BeginInvoke(data, null, null);//异步处理数据(显示数据)
            }
        }

        void showData(byte[] data)
        {
            //因为这个函数是异步执行的,因有操作UI控件,所以必须与UI线程同步
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<byte[]>(showData), data);//使用UI线程调用这个函数
                return;
            }

            //下面是处理(显示)过程,

            MsgRectextBox.AppendText("Recieved:" + "\r\n");
            if (data[0] == 0xFF && data[2] != 0xFF && data[data.Length - 1] == 0xFF)//判断接收的帧是否符合协议,帧头帧尾
            {
                int Length = -1;
                int i = 0;
                MsgRecCount++;
                MsgTransCount++;
                while (data[8 + i] != 0xFF)//获取该帧长度
                {
                    i++;
                    if (8 + i >= data.Length)
                        break;
                }
                Length = Length + i + 10;
                //byte[] MsgRec1 = new byte[1024 * 1024];
                data = dataAnalyzer.ChangFE012FFFE002FE(data.Take(Length).ToArray());
                string result = dataAnalyzer.ToHexString(data);
                MsgRectextBox.AppendText(result + "\r\n");
            }
        }

您好。关于那个呵呵那里,我想问一下。下位机上传的帧中最小长度是10的。这里还可以这样用么?
vpjian 2017-01-30
  • 打赏
  • 举报
回复
serialPort1_DataReceived 函数里面不要有耗时长的操作,不要有操作界面的操作,这都会导致有一定机率丢失数据并且你根本查不出原因。另外,有可能会每收到一个字节触发一次serialPort1_DataReceived ,所以收到的数据要先存到全局变量。 正确的处理方式之一应该是这样的(这种是异步方案。另一种方案是建一个线程使用同步读取,使用超时异常触发处理,我用这个方案更多一些):

   
        int dataLen = 0;//有效数据长度
        byte[] buff = new byte[256];//分配一个足够大的缓冲区,
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            int num = serialPort1.BytesToRead;
            if (num + dataLen > buff.Length) num = buff.Length - dataLen;//防止数据溢出(其实有点多余,缓冲区已经足够大了,就算是115200的波特率也不可能爆缓冲)
            serialPort1.Read(buff, dataLen, num);
            dataLen += num;
            if (dataLen >= 10)//如果你是定长帧这个是没问题的,否则.....呵呵
            {
                byte[] data = new byte[dataLen];
                Buffer.BlockCopy(buff, 0, data, 0, dataLen);//复制有效数据
                dataLen = 0;
                Action<byte[]> a = new Action<byte[]>(showData);
                a.BeginInvoke(data, null, null);//异步处理数据(显示数据)
            }
        }

        void showData(byte[] data)
        {
            //因为这个函数是异步执行的,因有操作UI控件,所以必须与UI线程同步
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<byte[]>(showData), data);//使用UI线程调用这个函数
                return;
            }

            //下面是处理(显示)过程,

            MsgRectextBox.AppendText("Recieved:" + "\r\n");
            if (data[0] == 0xFF && data[2] != 0xFF && data[data.Length - 1] == 0xFF)//判断接收的帧是否符合协议,帧头帧尾
            {
                int Length = -1;
                int i = 0;
                MsgRecCount++;
                MsgTransCount++;
                while (data[8 + i] != 0xFF)//获取该帧长度
                {
                    i++;
                    if (8 + i >= data.Length)
                        break;
                }
                Length = Length + i + 10;
                //byte[] MsgRec1 = new byte[1024 * 1024];
                data = dataAnalyzer.ChangFE012FFFE002FE(data.Take(Length).ToArray());
                string result = dataAnalyzer.ToHexString(data);
                MsgRectextBox.AppendText(result + "\r\n");
            }
        }

by_封爱 版主 2017-01-23
  • 打赏
  • 举报
回复
如果你有尾,. 可以这样...(我的串口运行代码)

 private void sp_WT230_DataReceived(byte[] data)
        {
            buffer_wt230.AddRange(data);
            if (buffer_wt230.Find(d => d == 0x0d) != 0)
            {
                byte[] ReceiveBytes = new byte[buffer_wt230.FindIndex(d => d == 0x0d)];
                buffer_wt230.CopyTo(0, ReceiveBytes, 0, ReceiveBytes.Length);
                buffer_wt230.Clear();
        }
一个全局的list<byte> buffer_wt230 每次都累加然后判断 是否有尾.(在上面代码中 我的协议是已0x0d结尾的) 然后最终通过解析 得到一个ReceiveBytes 这个就是 一组合法的数据. 当然这只是其中的一种办法..不过大体都是根据协议来下手
巴士上的邂逅 2017-01-23
  • 打赏
  • 举报
回复
如果有帧头帧尾,定义一个全局的byte[],长度要大于通讯数据最大长度,serialPort1_DataReceived里一个字节一个字节的读,读到帧头清空全局变量,开始存入全局变量,并记录收到的长度,收到帧尾就可以处理数据了
ArmStronger 2017-01-23
  • 打赏
  • 举报
回复
你先把读到的数据保存下来,然后再做判断,完整了就处理掉,不完整就继续接收,如此即可
qhxzgl2006 2017-01-23
  • 打赏
  • 举报
回复
引用 1 楼 yuankaiwsl 的回复:
通过SerialPort1.BytesToRead不能断定要接受的就是你需要的完整的数据, 你要通过数据帧头、帧尾或者帧头、数据长度来接收、截取一帧数据
我也是这样想的。因为SerialPort1.BytesToRead读取回来的长度每次都不是固定长度,所以我在后面进行了一个判断。如果在前面使用帧头、帧尾来判断应该怎么写呢。实在是白的厉害,不好意思。
qhxzgl2006 2017-01-23
  • 打赏
  • 举报
回复
我也是这样想的。因为SerialPort1.BytesToRead读取回来的长度每次都不是固定长度,所以我在后面进行了一个判断。如果在前面使用帧头、帧尾来判断应该怎么写呢。实在是白的厉害,不好意思。
巴士上的邂逅 2017-01-23
  • 打赏
  • 举报
回复
通过SerialPort1.BytesToRead不能断定要接受的就是你需要的完整的数据, 你要通过数据帧头、帧尾或者帧头、数据长度来接收、截取一帧数据
巴士上的邂逅 2017-01-23
  • 打赏
  • 举报
回复
好的,可以结贴了
qhxzgl2006 2017-01-23
  • 打赏
  • 举报
回复
引用 5 楼 yuankaiwsl 的回复:
如果有帧头帧尾,定义一个全局的byte[],长度要大于通讯数据最大长度,serialPort1_DataReceived里一个字节一个字节的读,读到帧头清空全局变量,开始存入全局变量,并记录收到的长度,收到帧尾就可以处理数据了
谢谢解答。试了一下丢帧是因为我那个num-1的判断多余,多帧是因为下位机固件对我给他做出响应的那个帧没识别。
qhxzgl2006 2017-01-23
  • 打赏
  • 举报
回复
引用 6 楼 diaodiaop 的回复:
如果你有尾,. 可以这样...(我的串口运行代码)

 private void sp_WT230_DataReceived(byte[] data)
        {
            buffer_wt230.AddRange(data);
            if (buffer_wt230.Find(d => d == 0x0d) != 0)
            {
                byte[] ReceiveBytes = new byte[buffer_wt230.FindIndex(d => d == 0x0d)];
                buffer_wt230.CopyTo(0, ReceiveBytes, 0, ReceiveBytes.Length);
                buffer_wt230.Clear();
        }
一个全局的list<byte> buffer_wt230 每次都累加然后判断 是否有尾.(在上面代码中 我的协议是已0x0d结尾的) 然后最终通过解析 得到一个ReceiveBytes 这个就是 一组合法的数据. 当然这只是其中的一种办法..不过大体都是根据协议来下手
我的帧头和帧尾都是FF。不过问题已经解决了。

110,571

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧