none
Serial port 接收資料的問題 RRS feed

  • 問題

  • 各位先進前輩好, 小弟請教個問題

    小弟目前定時發送指令到機器, 機器收到指令後, 會回傳一段資料回來.

    由於小弟要將資料解碼, 故回來的資料的第一個byte會與指令相同

    ex.

    Send: A0

    Recevied: A0 E2 03 42 81 91 78 1A B3

    小弟利用 SerialPort 的 DataReceived 事件去進行接收

     

            void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
            {
                //Decode(sp.ReadExisting());
                int bytes = sp.BytesToRead;
                // 創建字節數組 
                byte[] buffer = new byte[bytes];
                // 讀取緩衝區的數據到數組 
                sp.Read(buffer, 0, bytes);
                string str = System.Text.Encoding.Default.GetString(buffer);
                JoinDataTest(str);
            }


     

    但小弟沒有辦法一次收到完整資料, 會分成三到五筆資料在接收

    小弟有用 AccessPort 來測試過, 確實可以達到小弟想要的效果,

    只是小弟不知道還有那部份沒有注意到

    請問各位先進前輩, 小弟應該怎麼作, 才能一次就完整的收到資料

    ps. 回傳資料的長度並不固定

     

     



    • 已編輯 震洛水 2011年11月7日 下午 10:34
    2011年11月7日 下午 10:17

解答

  • (1) 如果你是發送 --> 回應模型, 其實最好不要用 DataReceived Event, 而是在 Write後 等待一段時間 (Thread.Sleep) 再Read . 而且最好使用另一個執行緒來執行此作業 (Thread, Backgroundworker)

    流程就是 Write to Serial port --> Thread Sleep ---> Read from Serial Port

    (2) 關於更多相關的討論 [論壇關於 Serial Port 的討論]


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    • 已標示為解答 震洛水 2011年11月10日 上午 08:29
    2011年11月8日 上午 12:49
    版主
  • 我的話不是這樣解釋的。

     

    1 個 SerialPort 只能有一隻程式存取。所以發送與接收絕對得依照前後順序來。

    DataReceived 是由另一個事件監測緩衝區記憶體,當緩衝區有新增資料時,就會丟事件出來。

    主執行緒收到事件時,開始處理,假設一次讀完,過了一個 TimeTicks 後,發現緩衝區歸 0 ,再下一次 TimeTicks 後,若有新增資料,會再次引發 DataReceived 事件。

    一問一答的硬體比較不會出問題,因為答完後,就不會有資料會餵進來,因此即使主執行緒忙碌中,DataReceived 事件也不會無窮觸發,造成堆疊鎖死。但是碰上廣播式的,或是線路中有雜訊,就可能造成重複觸發。

    我早期會使用 DataReceived 事件,現在都改成新開一個執行緒負責檢查緩衝區,沒資料就睡覺,定時自己檢查,有資料自己讀下來,也不回丟給別人讀,等資料處理完了以後,再把要得資料用事件扔回去,這樣的好處也可用在多通訊埠同時存取,避免主執行緒負載過高。而主執行緒只要直接存取共用記憶體即可,無須耗費長時間等待或迴圈。

    應盡量減少主執行緒負載,否則當主執行緒負載過高,畫面、事件、BackgroundWorker 都不用玩了,這些都是靠主執行緒控制的。


    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    提問時,錯誤情境描述與錯誤訊息很重要,情境描述包含你做了什麼,預期的結果與實際發生的結果。一個最爛的問法範例:「我的電腦電腦怎麼不能開機?」誰知道你家是不是沒電還是你根本找不到電源鈕。
    • 已標示為解答 震洛水 2011年11月10日 上午 08:28
    2011年11月9日 上午 03:24

所有回覆

  • 您可以設一個全域變數來接每次收進來的資料,等到每段分隔符號到了後,再去處理!
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/
    2011年11月7日 下午 10:54
  • 您可以設一個全域變數來接每次收進來的資料,等到每段分隔符號到了後,再去處理!
    亂馬客blog: http://www.dotblogs.com.tw/rainmaker/

    可是怎麼知道那個是分隔符號?

     

    2011年11月8日 上午 12:29
  • (1) 如果你是發送 --> 回應模型, 其實最好不要用 DataReceived Event, 而是在 Write後 等待一段時間 (Thread.Sleep) 再Read . 而且最好使用另一個執行緒來執行此作業 (Thread, Backgroundworker)

    流程就是 Write to Serial port --> Thread Sleep ---> Read from Serial Port

    (2) 關於更多相關的討論 [論壇關於 Serial Port 的討論]


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    • 已標示為解答 震洛水 2011年11月10日 上午 08:29
    2011年11月8日 上午 12:49
    版主
  • 請問一下

    1. 為什麼最好不要用 DataReceived Event , 是因為這麼容易無法取得完整資料嗎?

    2. DataReceived Event 本身與 SerialPort 是同一個 thread 嗎?

    3. 如果不知道資料長度, 那是如何判斷資料是否完整接收? 小弟有找了一些資料

        a. 利用sleep 去判斷是否還有收到資料, 有收到資料, 就持續sleep. 反之, 沒收到資料就表示資料完整接收.

        b. 小弟要做的程式, 可能一秒之內, 送出四、五個指令, 機器那邊也會依序傳回四、五筆資料, 像這樣的話, 是否以 thread 的方式實作為佳?


    • 已編輯 震洛水 2011年11月8日 上午 02:53
    2011年11月8日 上午 02:39
  • 感覺您似乎不確定機器回傳的訊息格式,不然應該能判斷訊息的結尾才是,最好是能看一下是否有相關的說明。(理論上應該在訊息格式會有如何判斷結尾)

    DataReceived Event 每次接收到的byte數是不固定的,所以不能期待機器發送後,在一次DataReceived Event 就收到完整的資料。

                int bytes = sp.BytesToRead;
                // 創建字節數組 
                byte[] buffer = new byte[bytes];
                // 讀取緩衝區的數據到數組 
                sp.Read(buffer, 0, bytes);
    

    上面的程式會每次讀取目前rx buffer中的所有內容,但仍不保証能完整收完機器的訊息,事件可能被觸發多次才收完。

    用公用變數暫存收到的byte可能是比較好的做法,如List<byte>

    真的不確定資料長度的話,只好用Thread.Sleep來等,但是如果機器發送的時間不定,也可能一次收到多筆資料,如果您不須要即時對每一筆資料回覆,那這也是可行的辦法。(在PC上的Rx buffer夠大,是能夠先sleep再一次讀沒關係)

    DataReceived Event和UI是不同的thread,和Serial port比較不知道是什麼意思………

     

     

     

     


    zeus
    2011年11月8日 上午 04:00
  • 主要是因為小弟用AccessPort去測試機器, 而 AccessPort 並不知道資料的長度

    可是它每次顯示接收的資料, 都剛好是機器回傳的完整資料

    so.小弟才有這個疑惑

    2011年11月8日 上午 06:04
  • AccessPort 只是把每次收到的資料新增到畫面上,大概因為速度很快,所以才讓您覺得他是一次收完的

    所以其實也不知道AccessPort到底收了幾次才收完…,因為他只負責顯示,頂多是把收進來的每個byte轉成可視的16進位而已


    zeus
    2011年11月8日 上午 07:54
  • DataReceive Event 適用在純接收的狀態, 既然你是一送一收, 本身活動流程就有個順序性, 用一送一收最能確保收到就是你送的命令的回應.

    假設一個有點離譜的狀況好了, 如果你送 A, 設備會回應 A0, 你送B, 設備要回應B0

    結果你用 DataRecive Event (這個Event觸發後的委派函式會產生在另一個執行緒) , 送A 後, 結果設備沒回應, 緊接著你又送 B, 結果回應了B0, 這你就很難判斷這個B0倒底是因為A送回來的, 還是因為B送回來的.

    另外, 你一秒要送四五個指令, 也得設備能在一秒有能力回應四五個指令才行. 如果你的設備用 8051, 然後收到命令後要處理超過一秒才會回應, 那怎麼辦 ? 而且你不能同時使用四個Thread對一個Com port 做發送, 這鐵死的.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2011年11月8日 上午 08:41
    版主
  • FAQ

    http://social.msdn.microsoft.com/Search/zh-TW/?Refinement=112&query=SerialPort%20Sleep%20MemoryStream

     

    要一次收回不會太難,AccessPort 分次收跟一次收很明顯,軟體用不著騙你。一行而已的話就是一次收。

     

    DataReceived 事件在 VB6 單緒沒啥問題,在 VBNET 有可能發生同時觸發,但你的主執行緒沒結束,就會造成觸發執行緒一直增加不減少。等到執行緒衝到 50x ~ 512 時,程式就會鎖死。事實上當 DataReceived 觸發事件超過 40 個執行緒後,就會互相咬死,所以往後也只會一直增加到爆掉。


    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    提問時,錯誤情境描述與錯誤訊息很重要,情境描述包含你做了什麼,預期的結果與實際發生的結果。一個最爛的問法範例:「我的電腦電腦怎麼不能開機?」誰知道你家是不是沒電還是你根本找不到電源鈕。
    2011年11月8日 下午 03:02
  • 嗯. 這樣小弟明白要實作的功能, 為何不適用於DataReceive Event.

     

    由於發送到指令, 並取得回資料 有前後順序的問題, 

    但另一方面, 又礙於多組指令的定時發送,

    所以不適合在發送完指令後, 直接 Sleep 與 Recevied Data.

    因此, 小弟需要將 Recevied 的動作, 以 Thread 或 BackgroudWork 的方式

    進行資料的接收與解析.


    小弟這樣的想法, 不知是否正確.


    2011年11月8日 下午 10:23
  • 不對, 因為你的想法基本上是基於Socket的作法才能這樣做.

    Serialport 不像Socket, 可以同時在一個IP的狀態下開啟多個TCP Port通訊, 底層內容也不像TCP Port會帶有來源與回應(ex: source IP: TCP Port , distination IP:TCP port)的標示. 所以你的Thread裡面就必須從 Write--->Read 整個程序做完, 才能發動下一個Write--->Read . 可分離拋接的動作應該是在完整read到buffer data後拋到另一個Thread做資料轉換 (例如 Byte 陣列轉成String 等等)

    受限於硬體的問題, 你必須要先知道 從傳送 --> 設備處理 --> 回應 -->接收這整個程序要花多少時間. 假設你要在一秒完成四個傳收動作, 但設備根本做不到, 那基本上你的要求根本就達不到, 因為這時真正要改善的是設備處理時間.

     


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2011年11月9日 上午 02:53
    版主
  • 我的話不是這樣解釋的。

     

    1 個 SerialPort 只能有一隻程式存取。所以發送與接收絕對得依照前後順序來。

    DataReceived 是由另一個事件監測緩衝區記憶體,當緩衝區有新增資料時,就會丟事件出來。

    主執行緒收到事件時,開始處理,假設一次讀完,過了一個 TimeTicks 後,發現緩衝區歸 0 ,再下一次 TimeTicks 後,若有新增資料,會再次引發 DataReceived 事件。

    一問一答的硬體比較不會出問題,因為答完後,就不會有資料會餵進來,因此即使主執行緒忙碌中,DataReceived 事件也不會無窮觸發,造成堆疊鎖死。但是碰上廣播式的,或是線路中有雜訊,就可能造成重複觸發。

    我早期會使用 DataReceived 事件,現在都改成新開一個執行緒負責檢查緩衝區,沒資料就睡覺,定時自己檢查,有資料自己讀下來,也不回丟給別人讀,等資料處理完了以後,再把要得資料用事件扔回去,這樣的好處也可用在多通訊埠同時存取,避免主執行緒負載過高。而主執行緒只要直接存取共用記憶體即可,無須耗費長時間等待或迴圈。

    應盡量減少主執行緒負載,否則當主執行緒負載過高,畫面、事件、BackgroundWorker 都不用玩了,這些都是靠主執行緒控制的。


    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    提問時,錯誤情境描述與錯誤訊息很重要,情境描述包含你做了什麼,預期的結果與實際發生的結果。一個最爛的問法範例:「我的電腦電腦怎麼不能開機?」誰知道你家是不是沒電還是你根本找不到電源鈕。
    • 已標示為解答 震洛水 2011年11月10日 上午 08:28
    2011年11月9日 上午 03:24
  • 謝謝 Bill Chung 與 心冷熱情熄 兩位先進前輩的解釋

    小弟將想實作出的功能說明一下, 再說明自己整合對先進前輩們給的意見後的想法, 還請先進前輩們指教一下小弟的想法是否正確

    小弟想持續監控機器內部的參數資料, 而資料取得的方式, PC to Device 一個Byte的指令碼, 其後, Device to PC 一串以傳入Device的指令碼開頭的資料(資料長度已知).

    在接收資料後, 會進行解析, 並將解析完成的數值, 以 MsChart 方式顯示.

    由於是持續監控, 而且所需取的資料可能位於不同指令碼之下, 所以才會想說同時發送多組指令碼, 以便監控時, 取得資料的時間相同, 但在各位先進的指正下, 發現這樣似乎不可以, 因此, 會改為 固定時間輪替發送指令碼

    最後目標, 是用軟體同時監控多台device.

    小弟的想法是...由於回傳的資料, 可以由開頭的byte內容, 取得對應的長度, 所以開一個 thread 來負責接收, 另外再開一個 thread 負責 decode data.

    可是這樣如果多台同時監控, 勢必會產生大量的thread, 是否使用 Threadpool 為佳?

    ps.

    1. 因為小弟研究資料, 看到的資料是將接收動作獨立出來, 所以才會以為在接收的時候, 可以獨立thread去負責接收與解析.http://www.4ucode.com/Study/Topic/1343558

    2. 目前有看到一篇與小弟功能類似的討論 http://social.msdn.microsoft.com/Forums/zh-TW/232/thread/caa3cb83-7ea0-4319-828c-9385a9e4bf66






    • 已編輯 震洛水 2011年11月9日 上午 07:40
    2011年11月9日 上午 05:34
  • 謝謝先進前輩們給的寶貴建議與解釋.

    小弟現在打算以多執行緒的方式來進行資料的接收解析

    現在在研究緒行緒間的資料交換機制


    在程式大海中游蕩的小人物
    2011年11月10日 上午 08:32