簡體   English   中英

如何正確地從.NET中的SerialPort讀取

[英]How to Properly Read from a SerialPort in .NET

我很尷尬不得不問這樣一個問題,但我很難搞清楚如何通過.NET SerialPort類在串口上可靠地讀取數據。

我的第一個方法:

static void Main(string[] args)
{
    _port = new SerialPort
    {
        PortName = portName,
        BaudRate = 57600,
        DataBits = 8,
        Parity = Parity.None,
        StopBits = StopBits.One,
        RtsEnable = true,
        DtrEnable = false,
        WriteBufferSize = 2048,
        ReadBufferSize = 2048,
        ReceivedBytesThreshold = 1,
        ReadTimeout = 5000,
    };    

    _port.DataReceived += _port_DataReceived;
    _port.Open();

    // whatever
}

private void _port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{           
    var buf = new byte[_port.BytesToRead];
    var bytesRead = _port.Read(buf, 0, buf.Length);

    _port.DiscardInBuffer();
    for (int i = 0; i < bytesRead; ++i)
    {
        // read each byte, look for start/end values, 
        // signal complete packet event if/when end is found
    }
}

所以這有一個明顯的問題; 我正在調用DiscardInBuffer ,因此在事件被觸發后進入的任何數據都被丟棄,即我正在丟棄數據。

現在, SerialPort.Read()的文檔甚至沒有聲明它是否提前了當前位置的流(真的嗎?),但我發現其他來源聲稱它確實(這是有道理的)。 但是,如果我不調用DiscardInBuffer我最終會得到一個RXOver錯誤,即我花了太長時間來處理每個消息並且緩沖區溢出。

所以...我真的不喜歡這個界面。 如果我必須在一個單獨的線程上處理每個緩沖區,我會這樣做,但是它帶有一系列問題,我希望我錯過了一些東西,因為我對這個界面沒有多少經驗。

Jason提出了一些關於減少來自工作線程的UI訪問的好點,但更好的選擇是首先不在工作線程上接收數據。

使用port.BaseStream.ReadAsync在您想要的線程上獲取事件驅動的數據。 我在http://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport上寫了更多關於這種方法的文章

要正確處理串行端口中的數據,您需要執行一些操作。

首先,不要處理接收事件中的數據。 將數據復制到其他位置並在另一個線程上執行任何處理。 (對於大多數事件都是如此 - 在事件處理程序中進行任何耗時的處理是一個壞主意,因為它會延遲調用者並可能引入問題。您還需要小心,因為您的事件是在不同的線程上引發的你的主要申請)

其次,您無法保證在收到數據時只收到一個數據包或完整的數據包 - 它可能會以小碎片的形式發送給您。

因此,結果就是你應該創建自己的緩沖區(大到足以容納幾個數據包),當你收到數據時,將它附加到你的緩沖區。 然后在另一個線程中,您可以處理緩沖區,查看是否可以從中解碼數據包然后使用該數據。 在找到有效數據包的開頭之前,您可能必須跳過部分數據包的末尾。 如果您沒有足夠的數據來構建完整數據包,那么您可能需要等待一段時間才能到達更多數據。

你不應該在端口上調用Discard - 只需讀取數據並使用它。 每次調用時,都會有另一個要處理的數據片段。 它不記得以前調用的數據 - 每次調用事件時,都會給出自上次調用以來已經到達的一小段數據。 只需使用您已獲得的數據並返回。

作為最后一個建議:除非您特別需要使其正常運行,否則請勿更改端口的任何設置。 因此,您必須設置波特率,數據/停止位和奇偶校驗,但避免嘗試更改Rts / Dtr,緩沖區大小和讀取閾值等屬性,除非您有充分的理由認為您比串行端口的作者更了解。 如今大多數串行設備都以行業標准的方式工作,改變這些低級選項很可能會造成麻煩,除非你正在談論一些不尋常的設備並且你非常了解硬件。

特別是將ReceivedBytesThreshold設置為1可能是導致您提到的失敗的原因,因為您要求串行端口一次只調用一個字節,每秒57,600次調用事件處理程序 - 僅為您的事件處理程序提供0.017在您開始獲得可重入調用之前處理每個字節的毫秒數。

DiscardInBuffer通常僅在打開串行端口后立即使用。 標准串行端口通信不需要它,因此您不應該在dataReceived處理程序中使用它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM