SerialPort接收数据并解析格式化数据问题,内有详解?
我现在有个读卡设备,设备是不停向计算机串口发送数据,但发送的数据不固定,因为有刷卡记录时就发刷卡记录,没有记录就会隔1ms左右发一次设备状态等消息。
我用串口调试器可以取到数据,但现在主要是取到数据向解析格式化数据问题,如下:
02 00 0C 03 03 01 48 50 B3 A5 04 00 0B 12 03
为一个完整的包,规定以02开头,03结尾。
从第二个字节开始 00 0C为从00开始到12之前的数据长度,并且高位在前。
倒数第2个数 12 为校验位。
现在的目的就是想接到一个完整的包后然后执行另一个程序(如解析各数据作用),问题就是每次传过来的字节数并不完整,如
02 00
0C 03 03 01
48 50 B3 A5 04 00 0B 12 03
我设置SerilPort.ReceivedBytesThreshold 事件也不行,因为我不知道这个包有多少个字节,只能等到第 2 和 3 字节(00 0C)后才知道这个包的长度,然后才接收,到倒数第2位时再计算一下前面校验数是否等于 12 ,这些条件都符合执行解析方法,否则丢弃此包。
我本想声明一个类内变量byte[] bArray;
在serialPort_DataReceived事件中首先:
int bytes = serialPort.BytesToRead;
这个方法中思维特别忙,请各位串口方面有过开发经验的朋友给予指点。本人开发C#应用软件方面比较多,特别对于byte,二进制数头疼。不管您能不能帮忙,给出建议,均非常感谢!
[解决办法]
楼主是不是用的serialport控件来实现串口通信的,如果是,请注意以下几点:
如果串口数据比较频繁,建议使用多线程和队列来操作,这样不会造成串口数据丢失,我看了楼主从取到的数据,发觉很有可能是因为通信过快所以数据丢失了。
serialport的datarecevie事件中将收到的数据及时写入到一个数组缓存中,然后另开一个线程专门处理该数组中数据内容,这样可以确保万无一失。
[解决办法]
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Data.SqlClient;
using System.Configuration;
using System.IO;
namespace UDMS200.showDig
{
public partial class setPara : Form
{
//存放输入数据的变量
string inputData = string.Empty;
//this delegate enables asynchronous calls for setting
//the text property on a TextBox control;
delegate void SetTextCallback(string text);
public setPara()
{
InitializeComponent();
//获取当前PC机上的所有串口 nice methods to browse all available ports;
string[] ports = SerialPort.GetPortNames();
//将获得的所有串口绑定到combobox控件 add all port names to the comboBox;
foreach (string port in ports)
{
ComboBox_ports.Items.Add(port);
}
}
private void setPara_Load(object sender, EventArgs e)
{
bind();
}
//初始化页面
protected void bind()
{
}
//选择串口后,下拉列表关闭后触发的事件
private void ComboBox_ports_SelectionChangeCommitted(object sender, EventArgs e)
{
if (SpCom.IsOpen) SpCom.Close();
SpCom.PortName = ComboBox_ports.SelectedItem.ToString();
label_motion.Text = "串口信息:" + SpCom.PortName + ":9600,8n1";
//try to open the selected port;
try
{
SpCom.Open();
}
//give a message,if the port is not available;
catch
{
MessageBox.Show("Serial port" + SpCom.PortName + "cannot be opened!", "RS232 tester", MessageBoxButtons.OK, MessageBoxIcon.Warning);
ComboBox_ports.Text = "";
label_motion.Text = "Select serial port!";
}
}
//接收读入的数据
private void SpCom_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//System.Threading.Thread.Sleep(500);
inputData = SpCom.ReadExisting();
try
{
if (inputData != string.Empty)
{
SetText(inputData);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
//读出数据
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox_answer.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
textBox_answer.Text += text;
if (textBox_answer.Text.Trim().EndsWith("03")) //判断接收一串字符是否结束,
{
//这里可以放你想要做的字符串的解析操作
}
}
}
}
}
你可以试一下,我也遇到过你的问题,就是这样解决的,是看的网上的一些资料,不知道是不是你想要的结果。
好像说是C#的这个控件是每次只读8个字节还是8个字符的,还是什么的,所以>8个字符,好像就拆开读了,具体我也没太看细,你自己再找找网上的资源看看吧。
[解决办法]
一次只接受一个数据
如果第一个是02 计数器置0;
通过第二\三个数据得到数据包的长度,只提供一个思路.下面我的代码已aa aa 开头第三个为数据包长度.其中只有0x0d长度为0x16外,其余都是6
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { byte r_byte; while (serialPort1.BytesToRead != 0) { r_byte = Convert.ToByte(serialPort1.ReadByte()); switch (r_flag) { case 0: if (r_byte == 0xaa) { r_flag += 1; serial_input[0] = r_byte; } break; case 1: if (r_byte == 0xaa) { r_flag += 1; serial_input[1] = r_byte; r_count = 0; } else { r_flag = 0; r_count = 0; } break; case 2: serial_input[r_count + 2] = r_byte; r_count += 1; if (serial_input[2] != 0xd0) { if (r_count == 4) { r_flag = 0; r_count = 0; rs_cl();//调用接收处理函数 } } else { if (r_count == 0x14) { r_flag = 0; r_count = 0; rs_cl();//调用接收处理函数 } } break; } } }