簡體   English   中英

具有最小延遲的串行通信

[英]Serial communication with minimal delay

我有一台通過串行通信(即物理或仿真串行端口的 RS-232/RS-422)與外部設備連接的計算機。 它們通過頻繁的數據交換(30Hz)相互通信,但只有小數據包(每個數據包小於 16 字節)。

通信最關鍵的要求是傳輸和接收之間的低延遲或延遲。

數據交換模式類似於握手。 一台主機設備發起通信並不斷在客戶端設備上發送通知。 客戶端設備需要盡快回復來自主機設備的每個通知(這正是需要實現低延遲的地方)。 通知和回復的數據包定義明確; 即數據長度是已知的。 並且基本上不允許數據丟失。

我使用以下常見的 Win API 函數以同步方式進行 I/O 讀/寫:CreateFile、ReadFile、WriteFile

客戶端設備使用 ReadFile 從主機設備讀取數據。 客戶端讀取完長度已知的完整數據包后,使用 WriteFile 將相應的數據包回復給主機設備。 讀和寫總是順序的,沒有並發。

不知何故,通信速度不夠快。 即數據發送和接收之間的時間持續時間太長。 我想這可能是串行端口緩沖或中斷的問題。

這里我總結了一些可能的措施來改善延遲。 請給我一些建議和更正:)

  1. 使用 FILE_FLAG_NO_BUFFERING 標志調用 CreateFile? 我不確定這個標志在這種情況下是否相關。
  2. 在每個 WriteFile 之后調用 FlushFileBuffers? 或任何可以通知/中斷串口立即傳輸數據的動作?
  3. 為處理串行通信的線程和進程設置更高的優先級
  4. 為模擬設備(及其驅動程序)設置延遲計時器或傳輸大小。 但是物理串口呢?
  5. Windows 上有什么類似的東西,比如 Linux 下的 setserial/low_latency?
  6. 禁用先進先出?

提前致謝!

我通過將通信超時設置為{MAXDWORD,0,0,0,0}解決了這個問題。

經過多年的努力,在這一天,我終於能夠使用 Microsoft 的 CDC 類 USB UART 驅動程序(USBSER.SYS,現在內置在 Windows 10 中使其真正可用)使我的串行通信終端變得足夠快。

顯然,前面提到的一組值是一個特殊的值,它設置了最小的超時和最小的延遲(至少對於 Microsoft 驅動程序,或者在我看來是這樣)並且如果接收中沒有新字符,也會導致 ReadFile 立即返回緩沖。

這是我的代碼(Visual C++ 2008,項目字符集從“Unicode”更改為“未設置”以避免端口名的 LPCWSTR 類型轉換問題)打開端口:

static HANDLE port=0;
static COMMTIMEOUTS originalTimeouts;

static bool OpenComPort(char* p,int targetSpeed) { // e.g. OpenComPort ("COM7",115200); 
    char portname[16];
    sprintf(portname,"\\\\.\\%s",p);
    port=CreateFile(portname,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);
    if(!port) {
        printf("COM port is not valid: %s\n",portname);
        return false;
    }
    if(!GetCommTimeouts(port,&originalTimeouts)) {
        printf("Cannot get comm timeouts\n");
        return false;
    }
    COMMTIMEOUTS newTimeouts={MAXDWORD,0,0,0,0};
    SetCommTimeouts(port,&newTimeouts);
    if(!ComSetParams(port,targetSpeed)) {
        SetCommTimeouts(port,&originalTimeouts);
        CloseHandle(port);
        printf("Failed to set COM parameters\n");
        return false;
    }
    printf("Successfully set COM parameters\n");
    return true;
}

static bool ComSetParams(HANDLE port,int baud) {
    DCB dcb;
    memset(&dcb,0,sizeof(dcb));
    dcb.DCBlength=sizeof(dcb);
    dcb.BaudRate=baud;
    dcb.fBinary=1;
    dcb.Parity=NOPARITY;
    dcb.StopBits=ONESTOPBIT;
    dcb.ByteSize=8;
    return SetCommState(port,&dcb)!=0;
}

這是它工作的 USB 跟蹤。 請注意 OUT 事務(輸出字節),然后是 IN 事務(輸入字節),然后是更多 OUT 事務(輸出字節),所有這些都在 3 毫秒內:

具有最少超時的 USB UART 數據包跟蹤

最后,如果您正在閱讀本文,您可能有興趣查看我通過 UART 發送和接收字符的函數:

    unsigned char outbuf[16384];
    unsigned char inbuf[16384];
    unsigned char *inLast = inbuf;
    unsigned char *inP = inbuf;
    unsigned long bytesWritten;
    unsigned long bytesReceived;

    // Read character from UART and while doing that, send keypresses to UART.
    unsigned char vgetc() { 
        while (inP >= inLast) { //My input buffer is empty, try to read from UART
            while (_kbhit()) { //If keyboard input available, send it to UART
                outbuf[0] = _getch(); //Get keyboard character
                WriteFile(port,outbuf,1,&bytesWritten,NULL); //send keychar to UART
            }
            ReadFile(port,inbuf,1024,&bytesReceived,NULL); 
            inP = inbuf;
            inLast = &inbuf[bytesReceived]; 
        }
        return *inP++;
    }

大型傳輸在代碼中的其他地方處理。

最后一點,顯然這是自 1998 年放棄 DOS 以來我設法編寫的第一個快速 UART 代碼。哦,當你玩得開心時,時間會飛逝嗎?

這是我找到相關信息的地方: http : //www.egmont.com.pl/addi-data/instrukcje/standard_driver.pdf

我遇到過類似的串口問題。 就我而言,我解決了降低串行端口延遲的問題。 您可以使用控制面板更改每個端口的延遲(默認設置為 16 毫秒)。 您可以在此處找到該方法: http : //www.chipkin.com/reducing-latency-on-com-ports/

祝你好運!!!

暫無
暫無

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

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