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

Windows CE 串口通訊 MFC 類別實作指南

     編輯:VC++

背景與挑戰

Windows CE 採用 Unicode 核心,且不支援桌面版 Windows 常用的重疊 I/O(OVERLAPPED)機制,這使得串口通訊類別的撰寫與桌面版有顯著差異。SDK 範例多為非 MFC 架構,移植難度較高。本文提供一個基於 MFC、採用「主動發送請求、被動接收回應」模型的 CSerial 類別,已在 eMbedded Visual C++ 4.0 與 ARM9 (S3C2410) + WinCE.NET 4.1 環境驗證通過。

類別架構

Serial.h - 介面定義

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;                        // 接收執行緒控制代碼
};

核心實作細節 (Serial.cpp)

1. 開啟串口與初始化

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;
}

2. DCB 參數設定 (InitDCB)

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);
}

3. 接收執行緒 (ReadPortThread)

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;
}

4. 關閉串口與資源釋放

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;
}

關鍵差異提醒

  • 無 OVERLAPPED 支援:不能使用非同步讀寫,需以專用執行緒輪詢 WaitCommEvent。
  • Unicode 環境:字串操作需使用 TCHAR、_T() 巨集。
  • 硬體流控:範例啟用 CTS/RTS,依硬體規格調整。

改進方向

目前為單接收執行緒模型,高負載場景建議擴充為獨立發送/接收執行緒,並加入環狀緩衝區避免資料遺失。歡迎同好交流指正。

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