Windows CE 採用 Unicode 核心,且不支援桌面版 Windows 常用的重疊 I/O(OVERLAPPED)機制,這使得串口通訊類別的撰寫與桌面版有顯著差異。SDK 範例多為非 MFC 架構,移植難度較高。本文提供一個基於 MFC、採用「主動發送請求、被動接收回應」模型的 CSerial 類別,已在 eMbedded Visual C++ 4.0 與 ARM9 (S3C2410) + WinCE.NET 4.1 環境驗證通過。
class CSerial
{
public:
CSerial();
virtual ~CSerial();
BOOL OpenPort(LPTSTR lpszPortName); // 開啟串口
BOOL ClosePort(HANDLE hCommPort); // 關閉串口
DWORD WritePort(TCHAR* buf, DWORD dwBytesToWrite); // 寫入資料
BOOL InitDCB(); // 設定 DCB 參數
BOOL InitCommTimeouts(); // 設定逾時參數
BOOL m_bConnected; // 連線狀態
HANDLE hReadThread; // 接收執行緒控制代碼
};
BOOL CSerial::OpenPort(LPTSTR lpszPortName)
{
// 建立檔案控制代碼
hPort = CreateFile(lpszPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hPort == INVALID_HANDLE_VALUE) return FALSE;
// 設定事件遮罩、緩衝區、清除佇列
SetCommMask(hPort, EV_RXCHAR);
SetupComm(hPort, 512, 512);
PurgeComm(hPort, PURGE_TXCLEAR | PURGE_RXCLEAR);
// 設定 DCB 與逾時
if (!InitDCB() || !InitCommTimeouts()) return FALSE;
// 啟用 DTR/RTS 訊號
EscapeCommFunction(hPort, SETDTR);
EscapeCommFunction(hPort, SETRTS);
// 建立接收執行緒
DWORD dwThreadID;
hReadThread = CreateThread(NULL, 0, ReadPortThread, 0, 0, &dwThreadID);
m_bConnected = TRUE;
return TRUE;
}
BOOL CSerial::InitDCB()
{
DCB PortDCB; PortDCB.DCBlength = sizeof(DCB);
GetCommState(hPort, &PortDCB);
PortDCB.BaudRate = 19200;
PortDCB.fBinary = TRUE;
PortDCB.fParity = TRUE;
PortDCB.fOutxCtsFlow = TRUE; // CTS 流控
PortDCB.fDtrControl = DTR_CONTROL_ENABLE;
PortDCB.fRtsControl = RTS_CONTROL_ENABLE;
PortDCB.ByteSize = 8;
PortDCB.Parity = NOPARITY;
PortDCB.StopBits = ONESTOPBIT;
// ... 其他旗標設定 ...
return SetCommState(hPort, &PortDCB);
}
DWORD WINAPI ReadPortThread(LPVOID lpvoid)
{
while (hPort != INVALID_HANDLE_VALUE)
{
DWORD dwCommModemStatus;
WaitCommEvent(hPort, &dwCommModemStatus, NULL); // 等待事件
if (dwCommModemStatus & EV_RXCHAR)
{
COMSTAT ComStat; DWORD dwErrorFlags;
ClearCommError(hPort, &dwErrorFlags, &ComStat);
DWORD dwLength = ComStat.cbInQue;
if (dwLength > 0)
{
TCHAR* buf = new TCHAR[256];
DWORD dwRead;
ReadFile(hPort, buf, dwLength, &dwRead, NULL);
strInChar = buf; // 全域緩衝區
delete[] buf;
}
}
GetCommModemStatus(hPort, &dwCommModemStatus);
}
return 0;
}
BOOL CSerial::ClosePort(HANDLE hCommPort)
{
if (hCommPort != INVALID_HANDLE_VALUE)
{
m_bConnected = FALSE;
SetCommMask(hPort, 0); // 觸發 WaitCommEvent 返回
if (hReadThread)
{
TerminateThread(hReadThread, 0); // 強制結束
CloseHandle(hReadThread);
}
EscapeCommFunction(hPort, CLRDTR);
EscapeCommFunction(hPort, CLRRTS);
PurgeComm(hPort, PURGE_TXCLEAR | PURGE_RXCLEAR);
CloseHandle(hCommPort);
hCommPort = INVALID_HANDLE_VALUE;
}
return TRUE;
}
WaitCommEvent。TCHAR、_T() 巨集。目前為單接收執行緒模型,高負載場景建議擴充為獨立發送/接收執行緒,並加入環狀緩衝區避免資料遺失。歡迎同好交流指正。