1. 程式人生 > >[MFC]使用強大的第三方串列埠類 CSerialPort

[MFC]使用強大的第三方串列埠類 CSerialPort

原創文章,歡迎轉載。轉載請註明:轉載自 祥的部落格

最近要寫一個串列埠程式,用MFC自帶COM元件的效果很差(一次接收一個緩衝區的資料,沒辦法像寫嵌入式程式那樣單位元組就能觸發中斷接收那樣),所以進行了一些研究,發現了一個 第三方串列埠類 很強大,效果非常好。

資源下載

步驟詳解

程式設計環境:VS2008

Step1 構建介面

建立一個基於對話方塊的MFC應用程式,我在這裡是 SerialPortTest ,畫的介面如下:

k1)

Step2 新增第三方類

SerialPort.hSerialPort.cpp 兩個檔案複製到工程所在的資料夾中,而且新增到工程裡,並在MFC生成的對話方塊類的標頭檔案中包含 #include "SerialPort.h"

k2

Step3 新增串列埠響應函式

在我的對話方塊類中( class CSerialPortTestDlg : public CDialogSerialPortTest.h 標頭檔案中) 新增串列埠字元接收訊息 WM_COMM_RXCHAR(串列埠接收緩衝區內有一個字元)的響應函式宣告:

    public:
    //******************************************
    afx_msg LONG OnCommunication(WPARAM ch, LPARAM port);//串列埠接收處理函式
    //******************************************

然後再在 SerialPortTest.cpp 中進行 WM_COMM_RXCHAR 訊息對映:

    BEGIN_MESSAGE_MAP(CSerialPortTestDlg, CDialog)
        ·········
        //******************************************
        ON_MESSAGE(WM_COMM_RXCHAR, OnCommunication)//串列埠接收處理函式
        //******************************************
        ·········
    END_MESSAGE_MAP()

最後在 SerialPortTest.cpp 對串列埠接收響應函式進行實現:

    LONG CSerialPortTestDlg::OnCommunication(WPARAM ch, LPARAM port)
    {//串列埠接收  處理函式

        ······

        return 0;
    }

Step4 初始化串列埠

其實就是開啟串列埠和關閉串列埠
首先 class CSerialPortTestDlg : public CDialog 類加入一個 Public 的成員變數 m_Com :

CSerialPort m_Com;//串列埠類

在對對話方塊的 “開啟串列埠”CButton 控制元件新增 單擊訊息處理函式 在裡面實現開啟和關閉串列埠的功能

    void CSerialPortTestDlg::OnBnClickedBtnOpen()
    {//開啟&關閉 串列埠

        m_PortName = m_Combox.GetCurSel()+1;                       //獲取串列埠號
        m_Baud     = m_Baud_Group[ m_Combox_Baud.GetCurSel() ];    //獲取波特率
        m_Parity   = m_Parity_Group[ m_Combox_Parity.GetCurSel() ];//獲取校驗位
        m_DataBit  = m_DataBit_Group[ m_Combox_Data.GetCurSel() ]; //獲取資料位
        m_StopBit  = m_StopBit_Group[ m_Combox_Stop.GetCurSel() ]; //獲取停止位
        if (m_IsOpenCom)//串列埠已經開啟
        {
            //關閉串列埠
            m_Com.ClosePort();
            m_IsOpenCom = FALSE;
            m_Btn_Open.SetWindowText("開啟串列埠");//說明已經關閉了串列埠

            //修改狀態
            GetDlgItem(IDC_COMBO_PORT)->EnableWindow(TRUE);  //允許改
            GetDlgItem(IDC_COMBO_BAUD)->EnableWindow(TRUE);  //允許改
            GetDlgItem(IDC_COMBO_PARITY)->EnableWindow(TRUE);//允許改
            GetDlgItem(IDC_COMBO_DATA)->EnableWindow(TRUE);  //允許改
            GetDlgItem(IDC_COMBO_STOP)->EnableWindow(TRUE);  //允許改

        } 
        else//串列埠已經關閉
        {
            //if (m_Com.InitPort(this, m_PortName, 9600 ,'N',8, 0))
            if (m_Com.InitPort(this, m_PortName, m_Baud ,m_Parity,m_DataBit, m_StopBit))
            {                           //串列埠號,波特率,校驗位,資料位,停止位為1(在此輸入0,代表停止位為1)
                //開啟串列埠成功
                m_Com.StartMonitoring();
                m_IsOpenCom = TRUE;
                m_Btn_Open.SetWindowText("關閉串列埠");//說明已經打開了串列埠


                //修改狀態          
                GetDlgItem(IDC_COMBO_PORT)->EnableWindow(FALSE);  //不許改
                GetDlgItem(IDC_COMBO_BAUD)->EnableWindow(FALSE);  //不許改
                GetDlgItem(IDC_COMBO_PARITY)->EnableWindow(FALSE);//不許改
                GetDlgItem(IDC_COMBO_DATA)->EnableWindow(FALSE);  //不許改
                GetDlgItem(IDC_COMBO_STOP)->EnableWindow(FALSE);  //不許改

            }
            else
            {//串列埠開啟失敗           
                MessageBox("沒有發現此串列埠或被佔用","串列埠開啟失敗",MB_ICONWARNING);
            }   
        }   
    }

Step4 串列埠傳送

至於串列埠的傳送可以呼叫 CSerialPort類 中的類成員函式:

    void        WriteToPort(char* string);
    void        WriteToPort(char* string,int n); // add by mrlong 2007-12-25
    void        WriteToPort(LPCTSTR string);     // add by mrlong 2007-12-25
    void        WriteToPort(BYTE* Buffer, int n);// add by mrlong

具體我是這樣用的:

      //m_String_Send_ASCII 是我CEdit控制元件繫結的CString的一個成員變數,用於獲取傳送資料
      char* SendBuf;
      int length = m_String_Send_ASCII.GetLength();
      SendBuf = m_String_Send_ASCII.GetBuffer(length);

      m_Com.WriteToPort( SendBuf ); //傳送資料

      m_String_Send_ASCII.ReleaseBuffer();

出現的錯誤及其解決

使用這個串列埠類會遇到這個問題

    Run-Time Check Failure #3 - The variable 'comstat' is being used without being initialized

下面是我搜集到的解決方法,屢試不爽(基本用 方案1)

方案1: 改變專案配置屬性

一種解決方案是改變基本執行時檢查(changing the runtime checks in project settings):在選單Project->Project properties-> C/C++ -> Code generation-> Basic Runtime checks –> change to ‘Default’,在中文版中是:專案-〉屬性-〉配置屬性-〉C/C++程式碼生成-〉基本執行時檢查-〉設定為預設,當將基本執行時檢查改為預設之後,編譯自然順利通過了,向串列埠除錯助手傳送訊息正常了,再從串列埠除錯助手向SerialPortTest傳送訊息時,也正常了。

方案2:改變comstat變數屬性

另一種解決方案是將CSerialPort.CPP中的COMSTAT comstat;改為static COMSTAT comstat;這樣改了之後,debug順利通過,然後除錯,與串列埠除錯助手相互發訊息都OK了。為什麼這樣就能解決呢?其實當你使用debug解決方案時,它的基本執行時檢查初始設定為:兩者(/RTC1,等同於 /RTCsu),這個兩者是指:堆疊幀(/RTCs),未初始化的變數(/RTCu)。由於它要檢查未初始化的變數,所以將SerialPort.cpp中的COMSTAT comstat;改為static COMSTAT comstat;就可以正常使用CSerialPort類了。
以上是在debug下的解決方案,當在Release編譯狀態時,你會發現並不需要將COMSTAT comstat;改為static COMSTAT comstat;就能編譯成功並且傳送接收訊息正常。其實,在Release下,它的基本執行時檢查初始設定已為預設。這應該也算是第一種解決方案之內。
而當你將它設定為兩者(/RTC1,等同於 /RTCsu)時,不論是否將COMSTAT comstat;改為static COMSTAT comstat;,編譯都不能通過,報錯““cl.exe”返回的結果有誤。”,這個應該是屬於Release的問題了,另當別論了。

方案3:

新增程式碼:memset(&comstat, 0, sizeof(COMSTAT)); //VC6不用這句也可以用,2008就要加

donate