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

jinminbox 2016-06-28 11:16:46
软件功能是要读取一个光纤传感器返回的数值,先发送一个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;
}
}
}
}
...全文
1078 12 打赏 收藏 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
龍过鸡年 2016-06-28
  • 打赏
  • 举报
回复
引用 楼主 jinming122919 的回复:
软件功能是要读取一个光纤传感器返回的数值,先发送一个6字节的代码确认传感器已经准备好,传感器返回6个字节响应;接着上位机在发送6个字节读取数据,传感器会周期性的返回126个字节来响应。。。。 因为接收数据的长度有多种,所以我用一个统一的响应函数里面的一个case语句来通过某一返回字段来判断返回数据,再执行发送的命令的响应函数。。。。 但是问题就出在,当我发送读数据的6个波长之后,返回的数据经常达不到126个字节,,,希望大神能帮我解惑,最好能给出帮
List<byte> sendData = new List<byte>(); 这句代码放在方法的外面 参考下这个你就明白了。 http://bbs.csdn.net/topics/391975233#post-401292623
jinminbox 2016-06-28
  • 打赏
  • 举报
回复
引用 3 楼 sp1234 的回复:
你若想“提高成功率”,你为什么不写 Thread.Sleep(5000) 呢?为什么要写 Sleep(50) 呢? 写 50 也太长,而且你给了这么长这么无意义的的延时,也还是不能准确接收数据。接收数据本来就不是这样“卡死”的流程,本来就是“有几个字节就接收几个字节”,然后接收完毕判断时候接收到消息结束标志,如果消息尚未没有结束那么就不要解析消息,如果接收到消息结束标志(此时可能已经有下一个消息的开始数据了)那么就把之前n次接收到的内容取出来进行消息解析。 编程中绝不可以有 Thread.Sleep(...) 这么坑爹的代码。
我也是迫不得已才用sleep的,因为暂时没有找到合适的办法。可是怎么知道接收完毕了呢?我既不知道接收的数据的长度,接收的数据的后面部分也都是随机的校验码
  • 打赏
  • 举报
回复
引用 4 楼 jinming122919 的回复:
这个是我后面加的,,还没测试,因为长度经常不对
我知道你会有这个借口,之所以你能找借口来说,本身就正好说明你那个地方是关键地不会设计程序的地方。所以重点就在于这一点点简单的设计啊。
  • 打赏
  • 举报
回复
如果消息尚未没有结束那么就不要解析消息 --> 如果消息尚没有结束那么就不要解析消息 把握重点重构你的代码。精力应该放到重点的流程设计上,不要把精力放到抄来的比较普通的代码上。
jinminbox 2016-06-28
  • 打赏
  • 举报
回复
引用 1 楼 sp1234 的回复:
基本上,看到
            while (n < 126)
            {
                Thread.Sleep(50);
            }
这类代码,你就应该清理这类垃圾。重新理解和设计流程。 之所以“死循环+阻塞”,而且是给出了一个既不足够长也不足够短的数值——50,这个完全瞎猜的数值,之所以这样设计,就是因为这类通讯程序设计根本的理念就错误了。
引用 1 楼 sp1234 的回复:
基本上,看到
            while (n < 126)
            {
                Thread.Sleep(50);
            }
这类代码,你就应该清理这类垃圾。重新理解和设计流程。 之所以“死循环+阻塞”,而且是给出了一个既不足够长也不足够短的数值——50,这个完全瞎猜的数值,之所以这样设计,就是因为这类通讯程序设计根本的理念就错误了。
这个是我后面加的,,还没测试,因为长度经常不对
  • 打赏
  • 举报
回复
你若想“提高成功率”,你为什么不写 Thread.Sleep(5000) 呢?为什么要写 Sleep(50) 呢? 写 50 也太长,而且你给了这么长这么无意义的的延时,也还是不能准确接收数据。接收数据本来就不是这样“卡死”的流程,本来就是“有几个字节就接收几个字节”,然后接收完毕判断时候接收到消息结束标志,如果消息尚未没有结束那么就不要解析消息,如果接收到消息结束标志(此时可能已经有下一个消息的开始数据了)那么就把之前n次接收到的内容取出来进行消息解析。 编程中绝不可以有 Thread.Sleep(...) 这么坑爹的代码。
xian_wwq 2016-06-28
  • 打赏
  • 举报
回复 1
有一点建议,先把数据接收和数据解析分离 数据解析会影响数据接收的
  • 打赏
  • 举报
回复
基本上,看到
            while (n < 126)
            {
                Thread.Sleep(50);
            }
这类代码,你就应该清理这类垃圾。重新理解和设计流程。 之所以“死循环+阻塞”,而且是给出了一个既不足够长也不足够短的数值——50,这个完全瞎猜的数值,之所以这样设计,就是因为这类通讯程序设计根本的理念就错误了。
jinminbox 2016-06-28
  • 打赏
  • 举报
回复
引用 9 楼 diaodiaop 的回复:
引用
返回的数据经常达不到126个字节
你这个问题问的有歧义.. 是他发不到126还是你收不到126?.. 如果是前者 可能这个模块有问题. 如果是后者 为啥你不判断126之后 组成一个"合法"的数据呢
嗯,我在收到数据后加了一个100ms的等待,,就顺利收到了数据
591320277 2016-06-28
  • 打赏
  • 举报
回复
我也遇到类似于你的问题,假设我一次发送一帧十字节的数据,接受到的也的确是十字节 但是他们可能不是一帧了 而是在大于一帧小于十帧之间,字节和字节之间被断开单独算一帧了,而且单独算一帧的字节数还不确定
黑娃 2016-06-28
  • 打赏
  • 举报
回复
即便是串口通讯,双方也应该定义一个通信协议,怎么能随随便便说收几个就收几个呢? 我建议你先用串口调试助手先确定一下,是下位机没有发送预期的数据,还是人家发了你没有收到。如果是后者,你就观察一下下位机什么时候把126个字节全部发给你的,你可以等待这么久时间后一次性收上来,当然这很非常不专业的做法。正确的做法是在协议中指定一个剩余字节长度,然后按照这个协议中指定的长度去接受剩下的内容,还需要设计一个超时,这取决于上下位机之间的最长响应时延。接着说,如果你按照协议指定长度接收完毕,那么你收到的内容必定是双方约定的预期的内容,如果没收完就超时,说明下位机没法或者超时时间太短了。
by_封爱 版主 2016-06-28
  • 打赏
  • 举报
回复
引用
返回的数据经常达不到126个字节
你这个问题问的有歧义.. 是他发不到126还是你收不到126?.. 如果是前者 可能这个模块有问题. 如果是后者 为啥你不判断126之后 组成一个"合法"的数据呢

111,128

社区成员

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

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

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