基於 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);
特點: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);
}
特點: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 旗標 | 0 | FILE_FLAG_OVERLAPPED |
| ReadFile/WriteFile 最後參數 | NULL | OVERLAPPED* |
| 調用返回 | 操作完成 | 立即返回(可能 ERROR_IO_PENDING) |
| 等待完成方式 | 隱式阻塞 | WaitForSingleObject(hEvent, timeout) |
| UI 響應性 | 操作期間凍結 | 可保持響應(配合工作線程更佳) |
| 錯誤處理複雜度 | 低 | 高(需檢查 GetLastError) |
GetOverlappedResult 或 IOCP(完成端口)實現高性能。ReadIntervalTimeout=MAXDWORD 實現「讀到數據即返回」,適配不定長幀。OnClose 必須 CloseHandle(hCom),異步模式下還要確保所有 OVERLAPPED.hEvent 釋放。