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

VC++ 串口程式設計進階:第三方類別 cnComm 的應用與實踐

     編輯:關於VC++

前言:為什麼需要第三方串口類別?

在前幾篇文章中,我們已見識過 WIN32 API 的底層複雜性與 MSComm ActiveX 控制項的便利性。雖然 MSComm 以事件驅動方式屏蔽了多執行緒細節,實現了非同步存取,但其資料傳輸仍需在 VARIANT 型別與字串間轉換,使用上仍有不便。為追求更簡潔高效的開發體驗,許多優秀開發者自行封裝了串口類別。本文將介紹由程式設計師 llbird 開發的 cnComm 類別——一個極具代表性的輕量級串口封裝庫。

cnComm 類別架構解析

llbird 的代碼風格簡潔緊湊,類別宣告與實現全部定義在單一標頭檔 cnComm.h 中,使用者只需引入此檔案即可。以下是核心架構亮點:

1. 靈活的建構子與資源管理

cnComm(bool fAutoBeginThread = true, DWORD dwIOMode = FILE_FLAG_OVERLAPPED)
    : _dwIOMode(dwIOMode), _fAutoBeginThread(fAutoBeginThread) { Init(); }

virtual ~cnComm() { Close(); UnInit(); }

支援同步/非同步 (Overlapped I/O) 兩種模式,預設啟用監控執行緒。

2. 豐富的屬性存取介面

  • 狀態檢查:IsOpen()、operator bool()
  • 句柄獲取:GetHandle()、operator HANDLE()
  • DCB 參數操作:多重載 GetState()/SetState(),支援字串格式 "9600, 8, n, 1"
  • 逾時與緩衝區設定:GetTimeouts()/SetTimeouts()、SetBufferSize()
  • 視窗通知綁定:SetWnd(HWND)、SetNotifyNum(DWORD)
  • 執行緒控制:BeginThread()/SuspendThread()/ResumeThread()/EndThread()

3. 核心通訊操作:簡潔至極致

// 開啟串口 (預設 9600,8,n,1)
bool Open(DWORD dwPort);
bool Open(DWORD dwPort, DWORD dwBaudRate);
bool Open(DWORD dwPort, char *szSetStr); // 如 "9600, 8, n, 1"

// 讀取資料
DWORD Read(LPVOID Buffer, DWORD dwBufferLength, DWORD dwWaitTime = 10);
char *ReadString(char *szBuffer, DWORD dwBufferLength, DWORD dwWaitTime = 20);

// 寫入資料
DWORD Write(LPVOID Buffer, DWORD dwBufferLength);
DWORD Write(const char *szBuffer);                    // ANSI 字串
DWORD Write(char *szBuffer, DWORD dwBufferLength, char *szFormat, ...); // 格式化
DWORD Write(char *szBuffer, char *szFormat, ...);     // 格式化 (不檢查長度)

// 同步版本 (適用於特定場景)
DWORD ReadSync(LPVOID Buffer, DWORD dwBufferLength);
DWORD WriteSync(LPVOID Buffer, DWORD dwBufferLength);

4. 信號線控制與完成端口式事件通知

提供 SetDTR()、SetRTS()、SetBreak() 等模擬信號控制。核心創新在於內建監控執行緒,透過 WaitCommEvent 等待事件,並將 EV_RXCHAR、EV_TXEMPTY、EV_CTS 等事件轉發為自訂視窗訊息 (ON_COM_RECEIVE 等),實現了類似 MSComm 的事件驅動模型,但介面更原生、效能更高。

5. 監控執行緒核心邏輯 ThreadFunc()

virtual DWORD ThreadFunc() {
    if (!::SetCommMask(_hCommHandle, _dwMaskEvent)) { /* 錯誤處理 */ return 1; }
    COMSTAT Stat; DWORD dwError;
    for (DWORD dwLength, dwMask = 0; _fRunFlag && IsOpen(); dwMask = 0) {
        if (!::WaitCommEvent(_hCommHandle, &dwMask, &_WaitOverlapped)) {
            if (::GetLastError() == ERROR_IO_PENDING)
                ::GetOverlappedResult(_hCommHandle, &_WaitOverlapped, &dwLength, TRUE);
            else continue;
        }
        if (dwMask == 0) continue;
        switch (dwMask) {
            case EV_RXCHAR:
                ::ClearCommError(_hCommHandle, &dwError, &Stat);
                if (Stat.cbInQue >= _dwNotifyNum) OnReceive();
                break;
            case EV_TXEMPTY: OnTXEmpty(); break;
            case EV_CTS: OnCTS(); break;
            case EV_DSR: OnDSR(); break;
            case EV_RING: OnRing(); break;
            case EV_RLSD: OnRLSD(); break;
            case EV_BREAK: OnBreak(); break;
            case EV_ERR: OnError(); break;
        }
    }
    return 0;
}

實戰範例:基於 cnComm 的串口收發程式

功能與界面同前文 (WIN32 API/MSComm 版本) 一致,僅替換串口實作層。界面截圖:

cnComm 串口類別測試程式界面

關鍵實作步驟

  1. 定義接收訊息常數:#define ON_COM_RECEIVE WM_USER + 618 (對應 cnComm 內部定義)
  2. 對話框類別成員:cnComm com;、afx_msg void OnCommRecv(WPARAM, LPARAM);
  3. 訊息映射:ON_MESSAGE(ON_COM_RECEIVE, OnCommRecv)
  4. 初始化開啟串口:
    BOOL CSerialPortClassDlg::OnInitDialog() {
        // ... 標準初始化 ...
        com.Open(1);                    // 開啟 COM1,預設參數
        com.SetWnd(AfxGetMainWnd()->m_hWnd); // 設定通知視窗
        return TRUE;
    }
  5. 發送資料:com.Write(m_send); (一行搞定)
  6. 接收資料:
    void CSerialPortClassDlg::OnCommRecv(WPARAM wParam, LPARAM lParam) {
        UpdateData(TRUE);
        char str[100];
        com.ReadString(str, 100);      // 讀取字串
        m_recv += str;                 // 追加到顯示區
        UpdateData(FALSE);
    }

總結與感悟

使用 cnComm 類別後,串口初始化、讀寫操作幾乎都只需 1~2 行代碼。這正是我們專門用一期連載介紹第三方類別的原因——優秀的封裝能讓開發者專注於業務邏輯,而非纏繞於底層細節。筆者在網路程式設計中同樣傾向使用第三方網路庫而非 MFC 的 CSocket,因為它們提供了更簡潔明快的介面,這也是值得主流框架借鑑的地方。

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