c#中,確保數據接收完整的 串口處理程序
https://msdn.microsoft.com/zh-tw/library/system.io.ports.serialport.getportnames.aspx
C# 串口通信總結
http://www.cnblogs.com/binfire/archive/2011/10/08/2201973.html
如果一些廠家比較懶的話,沒有提供相應的dll,我們只能對它進行串口通信編程了。以前從沒接觸過串口編程,最近在一個項目中有幾個地方都需要采用串口通信,跟公司一個老手請教后,感覺學到了很多東西,特在此做個總結:
一:首先我們來認識下什么是串口:
我們可以看到該串口的屬性,在C#中我們使用SerialPort類來表示串口
二:串口調試工具:
在對串口進行編程時候,我們要向串口發送指令,然后我們解析串口返回的指令。在這里向大家推薦一款工具。
void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { //接收數據 string str = ""; do { int count = serialPort.BytesToRead; if (count <= 0) break; byte[] readBuffer = new byte[count]; Application.DoEvents(); serialPort.Read(readBuffer, 0, count); str += System.Text.Encoding.Default.GetString(readBuffer); } while (serialPort.BytesToRead > 0); listBox1.Items.Add(str); }
C# 編寫的串口通信程序
http://www.cnblogs.com/zhaoming510/p/3965061.html
如果,翻看我之前的博客,會找到一篇用I/O模擬IIC通信的程序文章。好吧,如果找不到可以點擊這里,這里就不在贅述了,系統也已經完全調試通過了。
今天的任務是,把測試得到的數據在上位機的界面上顯示出來,於是鍵盤手花了兩天的時間模仿着巨人的肩膀通過了用C#編寫的界面程序,界面很簡單就像下面顯示的一樣。
c#串口系列文章:
http://bbs.csdn.net/topics/350038794
第一個 串口打開的時候你是要TRY CATCH的 不然會異常
第二個 盡量使用READ和WRITE不要用WRITELINE和READLINE,后兩個是要讀換行符的
第三個 查看你的串口初始化是否初始化好了
為什么我按發送界面就卡住了啊,
程序就不往下執行了。
為什么啊?
沒弄明白,請高手指點
如果你只知道編寫同步操作代碼,就不可能做出專業的產品,而只是知道.net有那些語句而已。
C# 串口操作系列(1) -- 入門篇,一個標准的,簡陋的串口例子。
http://blog.csdn.net/wuyazhe/article/details/5598945
C# 串口操作系列(2) -- 入門篇,為什么我的串口程序在關閉串口時候會死鎖 ?
http://blog.csdn.net/wuyazhe/article/details/5606276
http://blog.csdn.net/wuyazhe/article/details/5627253
http://blog.csdn.net/wuyazhe/article/details/5657188
http://blog.csdn.net/wuyazhe/article/details/5797673
GSM Modem
一直在csdn上回關於gsm modem方面的AT指令問題,之前花了不少時間,想想還是補上整理后的內容
http://blog.nonocast.cn/post/4082
在C#中使用SerialPort類實現串口通信 遇到多線程問題
http://bbs.21ic.com/blog-357347-66360.html
4...添加數據接收的事件 private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) 使用中的一些常見問題 C#中SerialPort類中DataReceived事件GUI實時處理方法(來自wanglei_wan@yahoo.com.cn 的看法) MSDN:從 SerialPort 對象接收數據時,將在輔助線程上引發 DataReceived 事件。由於此事件在輔助線程而非主線程上引發,因此嘗試修改主線程中的一些元素(如 UI 元素)時會引發線程異常。如果有必要修改主 Form 或 Control 中的元素,必須使用 Invoke 回發更改請求,這將在正確的線程上執行.進而要想將輔助線程中所讀到的數據顯示到主線程的Form控件上時,只有通過Invoke方法來實現 下面是代碼實例: private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { int SDateTemp = this.serialPort1.ReadByte(); //讀取串口中一個字節的數據 this.tB_ReceiveDate.Invoke( //在擁有此控件的基礎窗口句柄的線程上執行委托Invoke(Delegate) //即在textBox_ReceiveDate控件的父窗口form中執行委托. new MethodInvoker( /*表示一個委托,該委托可執行托管代碼中聲明為 void 且不接受任何參數的任何方法。 在對控件的 Invoke 方法進行調用時或需要一個簡單委托又不想自己定義時可以使用該委托。*/ delegate{ /*匿名方法,C#2.0的新功能,這是一種允許程序員將一段完整代碼區塊當成參數傳遞的程序代碼編寫技術,通過此種方法可 以直接使用委托來設計事件響應程序以下就是你要在主線程上實現的功能但是有一點要注意,這里不適宜處理過多的方法,因為C#消息機制是消息流水線響應機制,如果這里在主線程上處理語句的時間過長會導致主UI線程阻塞,停止響應或響應不順暢,這時你的主form界面會延遲或卡死 */ this.tB_ReceiveDate.AppendText(SDateTemp.ToString());//輸出到主窗口文本控件 this.tB_ReceiveDate.Text += " ";} ) ); } 如何知道當前電腦有哪個串口 在窗體上添加一個comboBox控件。 然后使用comboBox1.Items.AddRange(System.IO.Ports.SerialPort.GetPortNames()); 或者 string[] portList = System.IO.Ports.SerialPort.GetPortNames(); for (int i = 0; i < portList.Length; ++i) { string name = portList; comboBox1.Items.Add(name); }
例子講解與源碼:
Serial Communication using C# and Whidbey
http://www.codeproject.com/Articles/8605/Serial-Communication-using-C-and-Whidbey
[轉載]C#中串口通信編程:總結了 串口類,
http://blog.pfan.cn/sword2008/38218.html
C#: 1) 添加引用 using System.IO.Ports; 2) 定義SerialPort類實例 private SerialPort SpCom2 = new SpCom ("COM2", 9600,Parity.None, 8, StopBits.One); 3) 設置通訊端口號及波特率、數據位、停止位和校驗位。 SpCom.PortName = "COM1"; SpCom.BaudRate = 9600; SpCom.Parity = IO.Ports.Parity.None; SpCom.DataBits = 8; SpCom.StopBits = IO.Ports.StopBits.One; 或是定義時直接初始化 private SerialPort SpCom2 = new SpCom ("COM2", 9600,Parity.None, 8, StopBits.One); 4) 發送數據 SpCom.Write(TextSendData.Text); 5) 添加接受事件 a) 在運行時將事件與事件處理程序相關聯(通過委托實現) SpCom.DataReceived += new SerialDataReceivedEventHandler(SpCom2_DataReceived); 說明: SerialDataReceivedEventHandler 委托 表示將處理 SerialPort 對象的 DataReceived 事件的方法 b) 添加事件處理程序(簽名一定要一致) private void SpCom_DataReceived(object sender, SerialDataReceivedEventArgs e) 6) 讀取數據 string data = SpCom .ReadExisting();
c#中,確保數據接收完整的 串口處理程序:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { /* int n = comm.BytesToRead;//先記錄下來,避免某種原因,人為的原因,操作幾次之間時間長,緩存不一致 byte[] buf = new byte[n];//聲明一個臨時數組存儲當前來的串口數據 received_count += n;//增加接收計數 comm.Read(buf, 0, n);//讀取緩沖數據 builder.Clear();//清除字符串構造器的內容 //因為要訪問ui資源,所以需要使用invoke方式同步ui。 this.Invoke((EventHandler)(delegate { //判斷是否是顯示為16禁止 if (checkBoxHexView.Checked) { //依次的拼接出16進制字符串 foreach (byte b in buf) { builder.Append(b.ToString("X2") + " "); } } else { //直接按ASCII規則轉換成字符串 builder.Append(Encoding.ASCII.GetString(buf)); } //追加的形式添加到文本框末端,並滾動到最后。 * * //要解析字符串,根據長度定位,根據特殊字符定位,來解析數據。(還是要確保一個完整的數據包接收完整,並處理完整) * this.txGet.AppendText(builder.ToString()); //修改接收計數 labelGetCount.Text = "Get:" + received_count.ToString(); })); */ int n = serialPort1.BytesToRead;//待讀字節個數 byte[] buf = new byte[n];//創建n個字節的緩存 serialPort1.Read(buf, 0, n);//讀到在數據存儲到buf //1.緩存數據 buffer.AddRange(buf);//不斷地將接收到的數據加入到buffer鏈表中 //2.完整性判斷 while (buffer.Count >= 4) //至少包含幀頭(2字節)、長度(1字節)、功能位(1字節);根據設計不同而不同 { //2.1 查找數據頭 if (buffer[0] == 0x0AA) //傳輸數據有幀頭,用於判斷. 找到幀頭 AA AA 0A { int len = buffer[2]; //int len = 79; if (buffer.Count < len + 4) //數據區尚未接收完整, { break;//跳出接收函數后之后繼續接收數據 } //得到完整的數據,復制到ReceiveBytes中進行校驗 buffer.CopyTo(0, ReceiveBytes, 0, len + 4);// byte jiaoyan; //開始校驗 jiaoyan = 0x01;//jiaoyan = this.JY(ReceiveBytes); if (jiaoyan != ReceiveBytes[3]) //驗證功能位失敗 if (jiaoyan != ReceiveBytes[len+3]) { buffer.RemoveRange(0, len + 4);//從鏈表中移除接收到的校驗失敗的數據, //MessageBox.Show("數據包不正確!");//顯示數據包不正確, continue;//繼續執行while循環程序, } buffer.RemoveRange(0, len + 4); //執行其他代碼,對數據進行處理。 //解析5 6, 7 8字節的經緯度. DataProgress(); } else //幀頭不正確時,記得清除 { buffer.RemoveAt(0);//清除第一個字節,繼續檢測下一個。 } } }
對串口而言,不存在完整數據長度,都是以Byte為單位;一般來說,通常是透過時間跟固定數量來進行接收動作。(可能要看各PC的OS或Driver的設置情況)
通常在串口處理上,要確認接收數據完整,是在PC軟件上進行接收、保存跟判斷的動作,在完整收到後,才進行顯示或處理。
我現在的處理方法是根據串口接收事件的實際效果來的:接收的數量到達了一個數量后,我才開始處理,這樣比原來的簡單的判斷效果好多了。
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { try { int byteNumber = SerialPort.BytesToRead; Common.Delay(20); //延時等待數據接收完畢。 while ((byteNumber < SerialPort.BytesToRead) && (SerialPort.BytesToRead < 4800)) { byteNumber = SerialPort.BytesToRead; Common.Delay(20); } int n = SerialPort.BytesToRead; //記錄下緩沖區的字節個數 byte[] buf = new byte[n]; //聲明一個臨時數組存儲當前來的串口數據 SerialPort.Read(buf, 0, n); //讀取緩沖數據到buf中,同時將這串數據從緩沖區移除 //設置文字顯示 Control.CheckForIllegalCrossThreadCalls = false; StringBuilder sb = new StringBuilder(); for (int i = 0; i < n; i++) { string s; if (buf[i] < 16) s = "0" + Convert.ToString(buf[i], 16).ToUpper() + " "; else s = Convert.ToString(buf[i], 16).ToUpper() + " "; sb.Append(s); } textBox1.Text = sb.ToString(); } catch (Exception ee) { } }