實驗室/工業場景需後台即時接收串口資料、避免輪詢阻塞、支援命令下發控制外設。採用 多執行緒 + 串口中斷(事件驅動) + 定時器刷新 UI 架構。
typedef struct {
int nPort; // COM1~COM4
int nBaud; // CBR_1200 ~ CBR_57600
int nData; // 7/8
int nStop; // 1/1.5/2
int nParity; // NOPARITY/ODDPARITY/EVENPARITY
} COM_CONFIG;
// 開啟與設定
HANDLE hCom = CreateFile(portName, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
DCB dcb = {0}; dcb.DCBlength = sizeof(dcb);
GetCommState(hCom, &dcb);
dcb.BaudRate = cfg.nBaud; dcb.ByteSize = cfg.nData; dcb.StopBits = cfg.nStop; dcb.Parity = cfg.nParity;
SetCommState(hCom, &dcb);
// 設定逾時:立即返回,配合事件驅動
COMMTIMEOUTS ct = {0,0,0,0,0}; SetCommTimeouts(hCom, &ct);
UINT __cdecl RecvThread(LPVOID pParam) {
CMyClass* pThis = (CMyClass*)pParam;
OVERLAPPED ov = {0}; ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
SetCommMask(hCom, EV_RXCHAR); // 只關心接收字元事件
DWORD dwEvtMask;
while (pThis->m_bRunning) {
if (WaitCommEvent(hCom, &dwEvtMask, &ov)) {
if (dwEvtMask & EV_RXCHAR) {
char buf[1024]; DWORD dwRead;
ReadFile(hCom, buf, sizeof(buf), &dwRead, &ov); // 實際讀取
if (dwRead) {
// 進入臨界區推入環形緩衝區
pThis->PushData(buf, dwRead);
}
}
} else {
DWORD err = GetLastError();
if (err == ERROR_IO_PENDING) {
WaitForSingleObject(ov.hEvent, 100); // 短暫等待
}
}
}
CloseHandle(ov.hEvent);
return 0;
}
主執行緒設定 SetTimer(1, 100, NULL),OnTimer 中從緩衝區取出資料更新 ListView,避免接收執行緒直接操作 UI 造成死鎖。
CCriticalSection cs;
void PushData(const char* p, int len) {
CSingleLock lock(&cs); lock.Lock();
m_ringBuffer.Write(p, len);
lock.Unlock();
}
void PopData(char* p, int& len) { /* 類似加鎖 */ }
CListCtrl (Report 模式) 記錄時間戳、方向、十六進位/ASCII 內容。