3

我正在使用來自「大牌」之一的第三方串行端口組件(是的,我已經要求他們提供支持,但是存在時區差異,我需要要很快解決這個問題)。該組件已存在多年,我沒有理由相信問題在於它(與硬件相同)。用戶界面活動如何破壞我的串口輸入

h/w規範說如果我向seial端口寫入一個特定的字符串,以回車符結束,然後讀取,它將以特殊格式化的8個字符的字符串進行回覆,再次以回車結束。

代碼可以正常運行幾個小時,並根據它讀取的內容更新GUI。

但是,當在GUI上有任何用戶活動時,我從串口讀取垃圾。

我第一次注意到它時,點擊一個按鈕,導致模態窗體打開,然後關閉窗體。

但是,當我簡單地拖動TStringGrid的scollbar時,我也會看到它。

這是代碼。有什麼建議?


更新:組件是線程化的,供應商同意這裏的海報 - 串行端口是異步設備。我更改了代碼以編寫數據請求並處理組件的OnCharReceived()事件處理程序中的每個接受字符。塔恩斯克所有的幫助。


function TForm1.ReadChannelValueFromSerialPort(
         device_number : String; channel_number : String) : Real; 
    const SLEEP_TIME = 50;   // ms 
     NUM_READ_ATTEMPTS = 10; 

    var serialPortInput : String; 
     read_attempt_counter : Integer; 
     messageString : String; 
begin 
    WriteToSerialPort('#' + device_number + channel_number + #13); 

    serialPortInput := ''; 
    read_attempt_counter := 0; 

    while Length(serialPortInput) = 0 do 
    begin 
     try 
     Application.ProcessMessages(); 
     serialPortInput := serialPortInput + SerialPort.ReadText(); 

     except 
     on E: Exception do 
     begin 
      messageString := 'Can''t read from serial port' ; 
      MessageDlg(messageString, mtError, [mbOK], 0); 
      Halt(0); 
     end; 
     end; 

     Inc(read_attempt_counter); 

     if (read_attempt_counter = NUM_READ_ATTEMPTS) 
     and (Length(serialPortInput) = 0) then 
     begin 
     messageString := 'Can''t read from serial port after trying ' + 
        IntToStr(NUM_READ_ATTEMPTS) + ' times in ' + 
      FloatToStr((SLEEP_TIME * NUM_READ_ATTEMPTS)/1000) + ' seconds'; 
     MessageDlg(messageString, mtError, [mbOK], 0); 
     Halt(0); 
     end; 

     if (Length(serialPortInput) = 0) then 
     Sleep(SLEEP_TIME); 
    end; 

    if Copy(serialPortInput, 1, 1) <> '>' then 
    begin 
     DebugBreak(); 
     MessageDlg('Invalid value read from serial port "' + 
        serialPortInput + '"', mtError, [mbOK], 0); 
     Halt(0); 
    end; 

    // drop the3 leading > 
    serialPortInput := Copy(serialPortInput, 2, Length(serialPortInput) - 1); 
    serialPortInput := TrimRight(serialPortInput); // just in case 
    Result := StrToFloat(serialPortInput); 
end; // ReadChannelValueFromSerialPort(); 
+1

當serialPortInput <>''做'時,有沒有什麼理由使用效率較低的'while Length(serialPortInput)= 0 do'而不是''? –

+0

不,沒有: - /(+1) – Mawg

回答

10

沒有你實際使用的是哪個組件,或者它在內部做什麼瞭解,就很難說了。但是用戶活動會經過主消息隊列,並且您的代碼會手動抽取隊列中的新消息,因此可能與您的問題有關。您允許在您的寫入和讀取之間處理用戶活動。您是否嘗試將串行端口邏輯移入單獨的工作線程?

+5

IOW,擺脫'Application.ProcessMessages'。 –

+0

Remy(+1),我寧願不公開地說 - 我正在使用哪個第三方組件,因爲它已經過時,經過測試並且來自「其中一個大個子」,所以我確信這個問題是我的而不是他們的。命名他們可能會被視爲批評,我不希望這樣。我相信這個問題是我的。 – Mawg

+0

「您的代碼正在手動抽取新郵件隊列,因此可能與您的問題有關」。我同意我的代碼可能存在錯誤 - 但不知道錯誤是什麼。當我寫入請求數據的串口時,我懷疑我無法立即讀取,所以我試圖通過睡眠和處理消息來「禮貌」。也許如果我刪除一個/這兩個問題可能會消失?畢竟,它應該只需要幾分之一秒的時間以緊密的循環讀取串行端口,直到出現一些數據,而滾動條等的WM_纔會排隊......您怎麼看? – Mawg

2

串行通信通常涉及發送/接收緩衝區以及流量控制。由於您的實施,正常流量顯然中斷。

我建議你將訪問端口的代碼遷移到後臺線程,並在適當的時候對客戶端GUI進行某種同步回調。 (一個選項是簡單的Synchronize()調用)

+0

+1感謝您的反饋。串口組件是帶螺紋的。我想我應該寫一個請求,然後處理它的OnDataReceieve()事件。 – Mawg

0

它記得我幾年前有同樣的問題:單擊鼠標中斷與PLC的串行通信(嚴重!)。

並不確切地知道我們是如何固定的,但我認爲它有一些東西需要用USB鼠標,接地不當和/或串行電纜的電/磁屏蔽,過長串行電纜,雙絞線USB的組合/串行電纜或類似的東西(是的,在R & D工作區有點雜亂...)

所以也許你可以嘗試改變和/或檢查一些硬件?