软件功能是要读取一个光纤传感器返回的数值,先发送一个6字节的代码确认传感器已经准备好,传感器返回6个字节响应;接着上位机在发送6个字节读取数据,传感器会周期性的返回126个字节来响应。
原因是接收数据的长度有多种,所以本人用一个统一的响应函数里面的一个case语句来通过某一返回字段来判断返回数据,再执行发送的命令的响应函数。
但是问题就出在,当本人发送读数据的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
List<byte> sendData = new List<byte>(); 这句代码放在方法的外面
参考下这个你就明白了。
http://bbs.csdn.net/topics/391975233#post-401292623
5
你这个问题问的有歧义..
是他发不到126还是你收不到126?..
假如是前者 可能这个模块有问题.
假如是后者 为啥你不判断126之后 组成一个”合法”的数据呢
10
即便是串口通讯,双方也应该定义一个通信协议,怎么能随随便便说收几个就收几个呢?
本人建议你先用串口调试助手先确定一下,是下位机没有发送预期的数据,还是人家发了你没有收到。假如是后者,你就观察一下下位机什么时候把126个字节全部发给你的,你可以等待这么久时间后一次性收上来,当然这很非常不专业的做法。正确的做法是在协议中指定一个剩余字节长度,然后按照这个协议中指定的长度去接受剩下的内容,还需要设计一个超时,这取决于上下位机之间的最长响应时延。接着说,假如你按照协议指定长度接收完毕,那么你收到的内容必定是双方约定的预期的内容,假如没收完就超时,说明下位机没法或超时时间太短了。
本人建议你先用串口调试助手先确定一下,是下位机没有发送预期的数据,还是人家发了你没有收到。假如是后者,你就观察一下下位机什么时候把126个字节全部发给你的,你可以等待这么久时间后一次性收上来,当然这很非常不专业的做法。正确的做法是在协议中指定一个剩余字节长度,然后按照这个协议中指定的长度去接受剩下的内容,还需要设计一个超时,这取决于上下位机之间的最长响应时延。接着说,假如你按照协议指定长度接收完毕,那么你收到的内容必定是双方约定的预期的内容,假如没收完就超时,说明下位机没法或超时时间太短了。
10
本人也遇到相似于你的问题,假设本人一次发送一帧十字节的数据,接受到的也的确是十字节 但是他们可能不是一帧了 而是在大于一帧小于十帧之间,字节和字节之间被断开单独算一帧了,而且单独算一帧的字节数还不确定