最近在做的項目中涉及到串口通訊編程,隊友在做這一模 塊的時候遇到了一個相信很多人都可能遇到的問題,那就是接受資料的衝突或者丟失。隊友讓我幫他調試的時候,足足花了近兩個小時才把問題給解決了,現覺得有 必要總結一下以給遇到類似問題的朋友一個思路吧~
問題是這樣的,從相應的硬體裝置每隔2秒便發送一個大小為15的位元組資料,存的是16進位資料,每條資料以7E開頭以7E結尾。例如:7E 09 01 1A ... 7E。但是PC端在接受顯示的時候卻出現了問題,如前五個資料接受正常,接下來的都是以00填充,而剩下的10個資料卻出現在下條資料顯示的位置。如下所 示:
7E 09 01 1A 5C 00 00 00 00 00 00 00 00 00 00 /////// 03 06 1A 2C 3D 09 6C 32 12 7E 00 00 00 00 00 00/////// ........
總之感覺錯位了,對了,先把PC端代碼貼一下吧:
public partial class Form1 : Form
{
/// <summary>
/// 接受資料的串口類
/// </summary>
SerialPort spReceive;
delegate void ReceivData(byte[] bytes);
public Form1()
{
Form1.CheckForIllegalCrossThreadCalls = false;
InitializeComponent();
spReceive =new SerialPort("COM5", 57600, Parity.None, 8, StopBits.One);
spReceive.Open();
//設定觸發 DataReceived事件的閥值,在調試中發現這個不起作用,不解~
spReceive.ReceivedBytesThreshold =15;
spReceive.DataReceived +=new System.IO.Ports.SerialDataReceivedEventHandler(spReceive_DataReceived);
}
/// <summary>
/// 更新接受到得資料到UI 介面顯示
/// </summary>
/// <param name="bytes"></param>
public void UpdateReceiveToUI(byte[] bytes)
{
if (txtReceive.InvokeRequired)
{
ReceivData dl = new ReceivData(UpdateReceiveToUI);
object arg = bytes;
txtReceive.Invoke(dl, arg);
}
else
txtReceive.Text += "/////////" + BitConverter.ToString(bytes);
}
/// <summary>
/// 接收到串口資料觸發的事 件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void spReceive_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
byte[] recevied = new byte[15];
int size = spReceive.Read(recevied, 0, recevied.Length);
if (size >0)
UpdateReceiveToUI(recevied);
}
}
在調試的過程中,當我spReceive_DataReceived函數中的byte[] recevied = new byte[15]; 後面在加上個初始化,如下:
for( int i=0; i<recevied.Lengthl;i++)
recevied[i]=11;
這個時候介面顯示的時候,凡是以00顯示的地方都被11替換了。這肯定了這些00是由於傳送的15個位元組數組未被完全填充到我們定義的位元組數組中,即 Read(byte [] bytes,int offset,int size)函數未一次性將位元組數組填充完,而是分了兩次。但是在此過程中我使用的是SerialPort類的DataReceived事件,即沒收到一條 資料便會觸發,而這兒的現象彷彿是事件被觸發了多次。後來自己在SerialPort類的屬性裡面找了下,突然發現了有個 ReceivedBytesThreshold屬性,從這個屬性可以看出我們可以控制接受多少個位元組觸發一次DataReceived事件。於是,我便將 閥值設為15個位元組,滿以為問題解決了,可是結果卻~
囧啊,可是這個屬性也給了我們一些提示,也找到了問題的癥結所在。那就是,DataRecevied事件觸發的閥值,即接受到多少個位元組數觸發一次偏小, 結果導致了我們一條大小為15個位元組的資料觸發了兩次DataReceived事件。要解決這個問題,我們可以沿著這個思路走下去,那就是延遲 DataRecevied事件的觸發直到我們一條資料接受完畢。
所以在spReceive_DataReceived事件函數中,我們做如下修改:
byte[] receiveBuffer=new byte[15];
public void spReceive_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
int bytesRead = 0;
spReceive.ReceivedBytesThreshold = 100000; //延遲DataRecevied事件的再次觸發
while (true)
{
if (bytesRead >= 15) //這兒的15是我一條資料的大小
{
byte[] bytes = new byte[bytesRead];
Array.Copy(receiveBuffer, 0, bytes, 0, bytesRead);
Array.Clear(receiveBuffer, 0, bytesRead);
UpdateReceiveToUI(bytes);
bytesRead = 0;
break;
}
try
{
receiveBuffer[bytesRead] = (byte)spReceive.ReadByte();
++bytesRead;
}
catch (Exception ex1)
{
MessageBox.Show(ex1.Message);
break;
}
bytesRead = 0;
spReceive.ReceivedBytesThreshold = 1; //將延遲改回正常
}
}
經過上述的改動,便可以解決讀取串口資料衝突與丟失的問題了~不周之處,還望各位斧正~