首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Windows 串口編程實戰:同步與異步(Overlapped I/O)完整對比

     編輯:關於C語言

場景:工控機與儀表 RS485 通訊

基於 VC++6.0 MFC 對話框專案,實現「發送」/「接收」按鈕控制儀表讀取瞬時值。通訊協議:半雙工、11 位幀(1 起始位、8 數據位、2 停止位)、波特率 9600、地址 001、通道 01。

共同初始化代碼

HANDLE hCom; // 全局串口句柄

// OnInitDialog 通用部分
hCom = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL,
                  OPEN_EXISTING, FLAG, NULL); // FLAG 區別同步/異步
if (hCom == INVALID_HANDLE_VALUE) { AfxMessageBox("打開失敗"); return FALSE; }

SetupComm(hCom, 1024, 1024); // 緩衝區

COMMTIMEOUTS to = {0};
to.ReadIntervalTimeout = MAXDWORD; // 非阻塞讀特徵
to.ReadTotalTimeoutMultiplier = 0;
to.ReadTotalTimeoutConstant = 0;
to.WriteTotalTimeoutMultiplier = 100;
to.WriteTotalTimeoutConstant = 500;
SetCommTimeouts(hCom, &to);

DCB dcb; GetCommState(hCom, &dcb);
dcb.BaudRate = 9600; dcb.ByteSize = 8;
dcb.Parity = NOPARITY; dcb.StopBits = TWOSTOPBITS;
SetCommState(hCom, &dcb);
PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);

模式一:同步操作(FLAG = 0)

特點:ReadFile/WriteFile 阻塞直到完成或超時,邏輯線性簡單。

// 發送
void OnSend() {
    char buf[7] = {'\x11','0','0','1','0','1','\x03'}; // DC1 001 01 ETX
    DWORD written; COMSTAT stat; DWORD err;
    ClearCommError(hCom, &err, &stat);
    if (!WriteFile(hCom, buf, 7, &written, NULL))
        AfxMessageBox("寫失敗");
}

// 接收
void OnReceive() {
    char buf[100] = {0}; DWORD read = 100;
    if (!ReadFile(hCom, buf, 100, &read, NULL))
        AfxMessageBox("讀失敗");
    PurgeComm(hCom, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
    m_disp = buf; UpdateData(FALSE);
}

模式二:異步操作(FLAG = FILE_FLAG_OVERLAPPED)

特點:ReadFile/WriteFile 立即返回,需配合 OVERLAPPED 結構與事件等待完成,不阻塞 UI 線程。

// 發送
void OnSend() {
    OVERLAPPED os = {0}; os.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    char buf[7] = {'\x11','0','0','1','0','1','\x03'};
    DWORD written; COMSTAT stat; DWORD err;
    ClearCommError(hCom, &err, &stat);
    if (!WriteFile(hCom, buf, 7, &written, &os)) {
        if (GetLastError() == ERROR_IO_PENDING)
            WaitForSingleObject(os.hEvent, 1000); // 等待 1 秒
    }
    CloseHandle(os.hEvent);
}

// 接收
void OnReceive() {
    OVERLAPPED os = {0}; os.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    COMSTAT stat; DWORD err;
    ClearCommError(hCom, &err, &stat);
    DWORD toRead = min(100, (DWORD)stat.cbInQue);
    char buf[100] = {0};
    if (!ReadFile(hCom, buf, toRead, &toRead, &os)) {
        if (GetLastError() == ERROR_IO_PENDING)
            WaitForSingleObject(os.hEvent, 2000); // 等待 2 秒
    }
    PurgeComm(hCom, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
    m_disp = buf; UpdateData(FALSE);
    CloseHandle(os.hEvent);
}

關鍵差異總結表

維度同步異步
CreateFile 旗標0FILE_FLAG_OVERLAPPED
ReadFile/WriteFile 最後參數NULLOVERLAPPED*
調用返回操作完成立即返回(可能 ERROR_IO_PENDING)
等待完成方式隱式阻塞WaitForSingleObject(hEvent, timeout)
UI 響應性操作期間凍結可保持響應(配合工作線程更佳)
錯誤處理複雜度低高(需檢查 GetLastError)

工程建議

  • 簡單工具/單線程:同步足夠,代碼少、易維護。
  • GUI 主線程/高併發/長傳輸:異步必須,建議封裝為專用通訊線程,配合 GetOverlappedResult 或 IOCP(完成端口)實現高性能。
  • 超時設置:ReadIntervalTimeout=MAXDWORD 實現「讀到數據即返回」,適配不定長幀。
  • 資源清理:OnClose 必須 CloseHandle(hCom),異步模式下還要確保所有 OVERLAPPED.hEvent 釋放。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved