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

Windows 序列埠通訊 API 完整指南:從 CreateFile 到非同步 I/O 實戰

     編輯:VC 教學

序列埠通訊在 Windows 下的標準化實現路徑

DOS 時代透過中斷直接控制 UART,Windows 硬體抽象層 (HAL) 禁止應用程式直接操作中斷/埠口。所有序列埠操作必須透過 Win32 API,核心思想:序列埠即檔案,統一使用 CreateFile/ReadFile/WriteFile 檔案 I/O 模型,搭配 DCB (Device Control Block) 設定參數,實現跨硬體平台的可移植通訊。

核心 API 分類速查表

階段API用途
開啟/關閉CreateFile開啟 COM 埠,取得通用句柄
CloseHandle關閉埠口,釋放資源
參數設定GetCommState/SetCommState讀取/寫入 DCB 結構 (鮑率、資料位、停止位、奇偶校驗)
BuildCommDCB字串解析填充 DCB (如 "9600,n,8,1")
GetCommTimeouts/SetCommTimeouts讀寫逾時參數 (關鍵:避免 ReadFile 永久阻塞)
事件驅動SetCommMask設定監視事件 (RXCHAR, TXEMPTY, CTS, DSR, RLSD, BREAK, ERR, RING)
WaitCommEvent阻塞等待事件發生
WaitForMultipleObjects同時等待通訊事件與其他物件 (如關閉事件)
資料傳輸ReadFile/WriteFile同步/非同步讀寫 (配合 OVERLAPPED 結構)
GetOverlappedResult取得非同步操作結果
PurgeComm清空輸入/輸出緩衝區、中止掛起操作
狀態/錯誤ClearCommError更新 COMSTAT 狀態、清除硬體錯誤旗標

關鍵步驟:開啟與設定序列埠

// 1. 開啟 COM1,支援重疊 (非同步) I/O
HANDLE hComm = CreateFile(
    "COM1",                    // 埠口名稱
    GENERIC_READ | GENERIC_WRITE,
    0,                         // 不共享
    NULL,                      // 安全屬性
    OPEN_EXISTING,             // 必須存在
    FILE_FLAG_OVERLAPPED,      // 關鍵:啟用非同步
    NULL                       // 模板檔案
);

// 2. 設定 DCB 參數
DCB dcb = {0};
dcb.DCBlength = sizeof(DCB);
GetCommState(hComm, &dcb);     // 先取得預設值
dcb.BaudRate = CBR_115200;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
SetCommState(hComm, &dcb);

// 3. 設定逾時 (關鍵!避免讀取卡死)
COMMTIMEOUTS timeouts = {0};
timeouts.ReadIntervalTimeout = 50;        // 字元間最大間隔
timeouts.ReadTotalTimeoutConstant = 500;  // 總計常數
timeouts.ReadTotalTimeoutMultiplier = 10; // 乘數
timeouts.WriteTotalTimeoutConstant = 500;
timeouts.WriteTotalTimeoutMultiplier = 10;
SetCommTimeouts(hComm, &timeouts);

非同步讀寫模式 (生產環境標準)

使用 OVERLAPPED 結構 + 事件物件:

// 讀取範例
OVERLAPPED osReader = {0};
osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
DWORD dwRead;
BOOL fWaitingOnRead = FALSE;

if (!ReadFile(hComm, buffer, BUFSIZE, &dwRead, &osReader)) {
    if (GetLastError() == ERROR_IO_PENDING) {
        fWaitingOnRead = TRUE;
        // 可在此處做其他事,或 WaitForSingleObject(osReader.hEvent, INFINITE)
    }
}
// 後續 GetOverlappedResult 取得實際讀取位元組數

事件驅動架構範例

// 設定監視字元接收事件
SetCommMask(hComm, EV_RXCHAR);

// 工作執行緒迴圈
DWORD dwEvtMask;
while (running) {
    if (WaitCommEvent(hComm, &dwEvtMask, NULL)) {
        if (dwEvtMask & EV_RXCHAR) {
            // 有資料到達,呼叫 ReadFile 讀取
        }
    }
}

常見坑與最佳實踐

  1. 逾時設定:預設逾時常為 0 (無限等待),必須呼叫 SetCommTimeouts 設定合理值
  2. 緩衝區清理:開啟後、關閉前呼叫 PurgeComm(hComm, PURGE_TXCLEAR | PURGE_RXCLEAR)
  3. 錯誤恢復:ClearCommError 可清除 CE_FRAME、CE_OVERRUN、CE_RXPARITY 等硬體錯誤
  4. 流控制:硬體 (RTS/CTS) 或軟體 (XON/XOFF) 需在 DCB 正確設定 fOutxCtsFlow、fInX 等旗標
  5. 埠口名稱:COM10 以上必須用 \\\\.\\COM10 格式
  6. 資源釋放:嚴格配對 CreateFile/CloseHandle、CreateEvent/CloseHandle

現代替代方案

  • Windows 10+ Windows.Devices.SerialCommunication (WinRT API,UWP/WinUI 3 優先)
  • 跨平台庫:libserialport、Boost.Asio (serial_port)、QtSerialPort

掌握 Win32 序列埠 API 是理解 Windows I/O 子系統、裝置驅動互動的絕佳切入點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved