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

VC++ 學習:Windows CE 串口通訊類 (MFC 版) 完整實作

     編輯:VC 教學

背景與挑戰

Windows CE 為 Unicode 專屬系統,且不支援重疊 I/O (OVERLAPPED),導致桌面版串口類無法直接移植。本文提供從 SDK 範例移植改寫的 MFC 版 CSerial 類,採「主動發送請求、被動接收回應」模型,單一接收執行緒設計。

類別宣告 (Serial.h)


#if !defined(AFX_SERIAL_H__INCLUDED_)
#define AFX_SERIAL_H__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif

DWORD WINAPI ReadPortThread(LPVOID lpVoid); // 接收執行緒

class CSerial {
public:
    CSerial();
    virtual ~CSerial();

    BOOL OpenPort(LPCTSTR lpszPortName);    // 開啟串口 (如 "COM1:")
    BOOL ClosePort(HANDLE hCommPort);       // 關閉串口
    DWORD WritePort(TCHAR* buf, DWORD dwBytesToWrite); // 發送資料
    BOOL InitDCB();                         // 設定 DCB (鮑率、資料位、停止位、奇偶校驗)
    BOOL InitCommTimeouts();                // 設定逾時參數

    BOOL m_bConnected;
    HANDLE hReadThread;
};

#endif

核心實作 (Serial.cpp) 關鍵節錄

開啟串口與初始化


BOOL CSerial::OpenPort(LPCTSTR lpszPortName) {
    if (hPort) return FALSE; // 已開啟

    // 1. CreateFile 開啟裝置 (CE 不支援 FILE_FLAG_OVERLAPPED)
    hPort = CreateFile(lpszPortName, GENERIC_READ | GENERIC_WRITE,
        0, NULL, OPEN_EXISTING, 0, NULL);
    if (hPort == INVALID_HANDLE_VALUE) {
        CString err; err.Format(_T("無法開啟 %s, 錯誤碼=%d"), lpszPortName, GetLastError());
        MessageBox(NULL, err, _T("Error"), MB_OK);
        return FALSE;
    }

    // 2. 設定事件遮罩:僅關注接收字元
    SetCommMask(hPort, EV_RXCHAR);

    // 3. 配置緩衝區
    SetupComm(hPort, 512, 512);
    PurgeComm(hPort, PURGE_TXCLEAR | PURGE_RXCLEAR);

    // 4. DCB 與逾時初始化
    if (!InitDCB() || !InitCommTimeouts()) return FALSE;

    // 5. 啟動 DTR/RTS 訊號
    EscapeCommFunction(hPort, SETDTR);
    EscapeCommFunction(hPort, SETRTS);

    // 6. 建立接收執行緒
    DWORD dwThreadId;
    hReadThread = CreateThread(NULL, 0, ReadPortThread, this, 0, &dwThreadId);
    m_bConnected = (hReadThread != NULL);
    return m_bConnected;
}

接收執行緒


DWORD WINAPI ReadPortThread(LPVOID lpVoid) {
    CSerial* pThis = (CSerial*)lpVoid;
    BYTE buf[256];
    DWORD dwRead, dwError;
    COMSTAT comStat;

    while (pThis->m_bConnected) {
        if (WaitCommEvent(pThis->hPort, &dwEvent, NULL)) {
            if (dwEvent & EV_RXCHAR) {
                ClearCommError(pThis->hPort, &dwError, &comStat);
                if (comStat.cbInQue > 0) {
                    ReadFile(pThis->hPort, buf, min(sizeof(buf), comStat.cbInQue), &dwRead, NULL);
                    // 將 buf 處理或發送通知給主執行緒 (PostMessage/PostThreadMessage)
                }
            }
        }
        Sleep(10); // 降低 CPU 佔用
    }
    return 0;
}

發送資料


DWORD CSerial::WritePort(TCHAR* buf, DWORD dwBytesToWrite) {
    DWORD dwWritten;
    WriteFile(hPort, buf, dwBytesToWrite, &dwWritten, NULL);
    return dwWritten;
}

關鍵差異提醒

  • Unicode 字串:所有字串參數使用 TCHAR/LPCTSTR,專案設定為 Unicode。
  • 無 OVERLAPPED:CreateFile 旗標不含 FILE_FLAG_OVERLAPPED,ReadFile/WriteFile 同步阻塞。
  • 事件驅動:WaitCommEvent 替代重疊 I/O 完成通知。
  • 緩衝區管理:SetupComm、PurgeComm、ClearCommError 配合 COMSTAT 監控隊列。

已知限制與改進方向

  • 單一接收執行緒,高負載下可能丟包,建議改雙執行緒 (獨立發送/接收) 配合環狀緩衝區。
  • 錯誤處理與重連機制未完善。
  • 未實作流控 (XON/XOFF, RTS/CTS) 完整支援。

歡迎 CE 開發者交流指正,共同完善。E-mail: zhenxizhou@elong.com

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