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

Win32 序列埠程式設計完整指南:同步與非同步 I/O 實戰

     編輯:關於VC++

工控領域的序列埠通訊需求

工業控制中,工控機(Windows 平台)常透過 RS-485 總線與智能儀表進行半雙工通訊。主節點(PC)輪詢子節點(儀表),發送命令並接收應答。Win32 下有兩種實作方式:ActiveX 元件(簡單但欠靈活)與 Windows API(掌握機制、自由靈活)。本文專注 API 實作。

兩種操作模式比較

模式特性適用場景
同步操作API 阻塞直到完成;多執行緒下阻塞監聽執行緒簡單應用、低頻通訊
重疊/非同步操作API 立即返回,I/O 在背景進行;避免執行緒阻塞高效能、即時系統、多埠並發

四大核心步驟

  1. 開啟序列埠 CreateFile
  2. 配置序列埠 DCB、SetupComm、SetCommTimeouts
  3. 讀寫序列埠 ReadFile/WriteFile
  4. 關閉序列埠 CloseHandle

關鍵 API 深度解析

1. 開啟序列埠:CreateFile

HANDLE CreateFile(
    LPCTSTR lpFileName,        // "COM1"
    DWORD dwDesiredAccess,     // GENERIC_READ | GENERIC_WRITE
    DWORD dwShareMode,         // 0 (獨佔)
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    DWORD dwCreationDisposition, // OPEN_EXISTING
    DWORD dwFlagsAndAttributes,  // 0=同步, FILE_FLAG_OVERLAPPED=非同步
    HANDLE hTemplateFile       // NULL
);

2. 序列埠配置:DCB 結構與超時設定

DCB 關鍵參數:

  • BaudRate:波特率(CBR_9600 等常數)
  • ByteSize:資料位(4-8)
  • Parity:奇偶校驗(NOPARITY、ODDPARITY、EVENPARITY)
  • StopBits:停止位(ONESTOPBIT、TWOSTOPBITS、ONE5STOPBITS)

緩衝區與超時:

SetupComm(hCom, 1024, 1024);  // 輸入/輸出緩衝區 1024 bytes

COMMTIMEOUTS TimeOuts = {0};
TimeOuts.ReadIntervalTimeout = 1000;        // 讀間隔超時
TimeOuts.ReadTotalTimeoutMultiplier = 500;  // 讀時間係數
TimeOuts.ReadTotalTimeoutConstant = 5000;   // 讀時間常數
TimeOuts.WriteTotalTimeoutMultiplier = 500; // 寫時間係數
TimeOuts.WriteTotalTimeoutConstant = 2000;  // 寫時間常數
SetCommTimeouts(hCom, &TimeOuts);

// 總超時 = 時間係數 × 字元數 + 時間常數

3. 讀寫操作:同步與非同步實作

同步讀寫(簡單直接)

// 同步讀取
char str[100];
DWORD wCount;
BOOL bReadStat = ReadFile(hCom, str, 100, &wCount, NULL);

// 同步寫入
char lpOutBuffer[100];
DWORD dwBytesWrite = 100;
ClearCommError(hCom, &dwErrorFlags, &ComStat);
BOOL bWriteStat = WriteFile(hCom, lpOutBuffer, dwBytesWrite, &dwBytesWrite, NULL);

非同步讀取:WaitForSingleObject 等待

OVERLAPPED m_osRead = {0};
m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

ClearCommError(hCom, &dwErrorFlags, &ComStat);
dwBytesRead = min(dwBytesRead, (DWORD)ComStat.cbInQue);

BOOL bReadStatus = ReadFile(hCom, lpInBuffer, dwBytesRead, &dwBytesRead, &m_osRead);
if (!bReadStatus && GetLastError() == ERROR_IO_PENDING) {
    WaitForSingleObject(m_osRead.hEvent, 2000);  // 等待 2 秒
    // 操作完成,m_osRead.hEvent 變為有號狀態
}

非同步讀取:GetOverlappedResult 等待

if (!bReadStatus && GetLastError() == ERROR_IO_PENDING) {
    GetOverlappedResult(hCom, &m_osRead, &dwBytesRead, TRUE);  // TRUE=一直等待
}

非同步寫入

OVERLAPPED m_osWrite = {0};
m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

BOOL bWriteStat = WriteFile(hCom, buffer, dwBytesWritten, &dwBytesWritten, &m_osWrite);
if (!bWriteStat && GetLastError() == ERROR_IO_PENDING) {
    WaitForSingleObject(m_osWrite.hEvent, 1000);
}

完整範例:RS-485 通訊雙模式實作

文章提供兩個完整的對話框專案範例:

  • 範例 1:同步操作模式
  • 範例 2:非同步操作模式

兩者皆實現與百特顯示儀表(XMA5000)透過 RS-485 通訊,協議為:幀 11 位元、1 起始位、8 資料位、2 停止位、半雙工。主機發送 DC1 AAA BB ETX 讀取瞬時值,儀表回應對應資料。

關鍵差異對照表

項目同步版非同步版
CreateFile 旗標0FILE_FLAG_OVERLAPPED
ReadFile/WriteFile最後參數 NULL最後參數 OVERLAPPED*
等待完成方式函數阻塞自動等待WaitForSingleObject / GetOverlappedResult
UI 回應性可能凍結不阻塞主執行緒

建議:簡單應用用同步,複雜/高效能需求用非同步。理解兩者差異,才能根據專案需求做最佳選擇。

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