VC控制元件MSComm編寫串列埠通訊程式

Mr_John_Liang發表於2015-05-11

在眾多網友的支援下,串列埠除錯助手從2001年5月21日釋出至今,短短一個月,在全國各地累計下載量近5000人次,在近200多個電子郵件中,20多人提供了使用測試意見,更有50多位朋友提出要串列埠除錯助手的原始碼,為了答謝謝朋友們的支援,公開推出我最初用VC控制元件MSComm編寫串列埠通訊程式的原始碼,並寫出詳細的程式設計過程,姑且叫串列埠除錯助手源程式V1.0VC串列埠通訊源程式吧,我相信,如果你用VC程式設計,那麼有了這個程式碼,就可以輕而易舉地完成串列埠程式設計任務了。(也許本文過於詳細,高手就不用看)

開始吧:

1.建立專案:開啟VC++6.0,建立一個基於對話方塊的MFC應用程式SCommTest(與我原始碼一致,等會你會方便一點);

2.在專案中插入MSComm控制元件   選擇Project選單下Add To Project子選單中的 Components and Controls…選項,在彈出的對話方塊中雙擊Registered ActiveX Controls項(稍等一會,這個過程較慢),則所有註冊過的ActiveX控制元件出現在列表框中。選擇Microsoft Communications Control, version 6.0,,單擊Insert按鈕將它插入到我們的Project中來,接受預設的選項。(如果你在控制元件列表中看不到Microsoft Communications Control, version 6.0,那可能是你在安裝VC6時沒有把ActiveX一項選上,重新安裝VC6,選上ActiveX就可以了),

這時在ClassView視窗中就可以看到CMSComm類了,(注意:此類在ClassWizard中看不到,重構clw檔案也一樣),並且在控制元件工具欄Controls中出現了電話圖示(如圖1所示),現在要做的是用滑鼠將此圖示拖到對話方塊中,程式執行後,這個圖示是看不到的。



 

 

 

3.利用ClassWizard定義CMSComm類控制物件  開啟ClassWizard->Member Viariables選項卡,選擇CSCommTestDlg類,為IDC_MSCOMM1新增控制變數:m_ctrlComm,這時你可以看一看,在對話方塊標頭檔案中自動加入了//{{AFX_INCLUDES()  #include "mscomm.h"  //}}AFX_INCLUDES (這時執行程式,如果有錯,那就再從頭開始)。

4.在對話方塊中新增控制元件  向主對話方塊中新增兩個編輯框,一個用於接收顯示資料ID為IDC_EDIT_RXDATA,另一個用於輸入傳送資料,ID為IDC_EDIT_TXDATA,再新增一個按鈕,功能是按一次就把傳送編輯框中的內容傳送一次,將其ID設為IDC_BUTTON_MANUALSEND。別忘記了將接收編輯框的Properties->Styles中把Miltiline和Vertical Scroll屬性選上,傳送編輯框若你想輸入多行文字,也可選上Miltiline。

再開啟ClassWizard->Member Viariables選項卡,選擇CSCommTestDlg類,為IDC_EDIT_RXDATA新增CString變數m_strRXData, 為IDC_EDIT_TXDATA新增CString變數m_strTXData。說明: m_strRXData和m_strTXData分別用來放入接收和傳送的字元資料。

       休息一會吧?我們天天與電腦打交道,要注意保重,我現在在單槓上做引體向上可以來40次,可我都32了,佩服嗎? 。。。。。。好了,再接著來,下面是關鍵了:

5.新增串列埠事件訊息處理函式OnComm() 開啟ClassWizard->Message Maps,選擇類CSCommTestDlg,選擇IDC_MSCOMM1,雙擊訊息OnComm,將彈出的對話方塊中將函式名改為OnComm,(好記而已)OK。

 這個函式是用來處理串列埠訊息事件的,如每當串列埠接收到資料,就會產生一個串列埠接收資料緩衝區中有字元的訊息事件,我們剛才新增的函式就會執行,我們在OnComm()函式加入相應的處理程式碼就能實現自已想要的功能了。請你在函式中加入如下程式碼:

void CSCommTestDlg::OnComm() 
{
    // TODO: Add your control notification handler code here
    VARIANT variant_inp;
    COleSafeArray safearray_inp;
    LONG len,k;
    BYTE rxdata[2048]; //設定BYTE陣列 An 8-bit integerthat is not signed.
    CString strtemp;
    if(m_ctrlComm.GetCommEvent()==2) //事件值為2表示接收緩衝區內有字元
    {             ////////以下你可以根據自己的通訊協議加入處理程式碼
        variant_inp=m_ctrlComm.GetInput(); //讀緩衝區
        safearray_inp=variant_inp; //VARIANT型變數轉換為ColeSafeArray型變數
        len=safearray_inp.GetOneDimSize(); //得到有效資料長度
        for(k=0;k<len;k++)
            safearray_inp.GetElement(&k,rxdata+k);//轉換為BYTE型陣列
        for(k=0;k<len;k++) //將陣列轉換為Cstring型變數
        {
            BYTE bt=*(char*)(rxdata+k); //字元型
            strtemp.Format("%c",bt); //將字元送入臨時變數strtemp存放
            m_strRXData+=strtemp; //加入接收編輯框對應字串 
        }
    }
    UpdateData(FALSE); //更新編輯框內容
}

到目前為止還不能在接收編輯框中看到資料,因為我們還沒有開啟串列埠,但執行程式不應該有任何錯誤,不然,你肯定哪兒沒看仔細,因為我是開啟VC6對照著做一步寫一行的,執行試試。沒錯吧?那麼做下一步:

6.開啟串列埠和設定串列埠引數  你可以在你需要的時候開啟串列埠,例如在程式中做一個開始按鈕,在該按鈕的處理函式中開啟串列埠。現在我們在主對話方塊的CSCommTestDlg::OnInitDialog()開啟串列埠,加入如下程式碼:

// TODO: Add extra initialization here
if(m_ctrlComm.GetPortOpen())
m_ctrlComm.SetPortOpen(FALSE);

m_ctrlComm.SetCommPort(1); //選擇com1
if( !m_ctrlComm.GetPortOpen())
m_ctrlComm.SetPortOpen(TRUE);//開啟串列埠
else
AfxMessageBox("cannot open serial port");

m_ctrlComm.SetSettings("9600,n,8,1"); //波特率9600,無校驗,8個資料位,1個停止位

m_ctrlComm.SetInputModel(1); //1:表示以二進位制方式檢取資料
m_ctrlComm.SetRThreshold(1); 
//引數1表示每當串列埠接收緩衝區中有多於或等於1個字元時將引發一個接收資料的OnComm事件
m_ctrlComm.SetInputLen(0); //設定當前接收區資料長度為0
m_ctrlComm.GetInput();//先預讀緩衝區以清除殘留資料

現在你可以試試程式了,將串列埠線接好後(不會接?去看看我寫的串列埠接線基本方法),開啟串列埠除錯助手,並將串列埠設在com2,選上自動傳送,也可以等會手動傳送。再執行你編寫的程式,接收框裡應該有資料顯示了。

7.傳送資料  先為傳送按鈕新增一個單擊訊息即BN_CLICKED處理函式,開啟ClassWizard->Message Maps,選擇類CSCommTestDlg,選擇IDC_BUTTON_MANUALSEND,雙擊BN_CLICKED新增OnButtonManualsend()函式,並在函式中新增如下程式碼:

void CSCommTestDlg::OnButtonManualsend() 
{
// TODO: Add your control notification handler code here
UpdateData(TRUE); //讀取編輯框內容
m_ctrlComm.SetOutput(COleVariant(m_strTXData));//傳送資料
}

執行程式,在傳送編輯框中隨意輸入點什麼,單擊傳送按鈕,啊!看看,在另一端的串列埠除錯助手(或別的除錯工具)接收框裡出現了什麼。

如果你真是初次涉獵串列埠程式設計,又一次成功,那該說聲謝謝我了,因為我第一次做串列埠程式時可費勁了,那時網上的資料也不好找。開開玩笑,謝謝你的支援,有什麼好東西別忘了給我寄一份。

最後說明一下,由於用到VC控制元件,在沒有安裝VC的計算機上執行時要從VC中把mscomm32.ocx、msvcrt.dll、mfc42.dll拷到Windows目錄下的System子目錄中(win2000為System32)並再進行註冊設定,請參考

如何手工註冊MSComm控制元件。

龔建偉 2001.6.20

8.傳送十六進位制字元

    在主對話方塊中加入一個複選接鈕,ID為IDC_CHECK_HEXSEND Caption: 十六進位制傳送,再利用ClassWizard為其新增控制變數:m_ctrlHexSend;

    在ClassView中為SCommTestDlg類新增以下兩個PUBLIC成員函式,並輸入相應程式碼;

 

//由於這個轉換函式的格式限制,在傳送框中的十六制字元應該每兩個字元之間插入一個空隔
//如:A1 23 45 0B 00 29
//CByteArray是一個動態位元組陣列,可參看MSDN幫助
int CSCommTestDlg::String2Hex(CString str, CByteArray &senddata)
{
int hexdata,lowhexdata;
int hexdatalen=0;
int len=str.GetLength();
senddata.SetSize(len/2);
for(int i=0;i<len;)
{
char lstr,hstr=str[i];
if(hstr==' ')
{
i++;
continue;
}
i++;
if(i>=len)
break;
lstr=str[i];
hexdata=ConvertHexChar(hstr);
lowhexdata=ConvertHexChar(lstr);
if((hexdata==16)||(lowhexdata==16))
break;
else 
hexdata=hexdata*16+lowhexdata;
i++;
senddata[hexdatalen]=(char)hexdata;
hexdatalen++;
}
senddata.SetSize(hexdatalen);
return hexdatalen;
}

//這是一個將字元轉換為相應的十六進位制值的函式
//好多C語言書上都可以找到
//功能:若是在0-F之間的字元,則轉換為相應的十六進位制字元,否則返回-1
char CSCommTestDlg::ConvertHexChar(char ch) 
{
if((ch>='0')&&(ch<='9'))
return ch-0x30;
else if((ch>='A')&&(ch<='F'))
return ch-'A'+10;
else if((ch>='a')&&(ch<='f'))
return ch-'a'+10;
else return (-1);
}

 

  再將CSCommTestDlg::OnButtonManualsend()修改成以下形式:

void CSCommTestDlg::OnButtonManualsend() 
{
// TODO: Add your control notification handler code here
UpdateData(TRUE); //讀取編輯框內容
if(m_ctrlHexSend.GetCheck())
{
CByteArray hexdata;
int len=String2Hex(m_strTXData,hexdata); //此處返回的len可以用於計算髮送了多少個十六進位制數
m_ctrlComm.SetOutput(COleVariant(hexdata)); //傳送十六進位制資料
}
else 
m_ctrlComm.SetOutput(COleVariant(m_strTXData));//傳送ASCII字元資料

}

現在,你先將串列埠線接好並開啟串列埠除錯助手V2.1,選上以十六制顯示,設定好相應串列埠,然後執行我們這個程式,在傳送框中輸入00 01 02 03 A1 CC等十六進位制字元,並選上以十六進位制傳送,單擊手動傳送,在串列埠除錯助手的接收框中應該可以看到00 01 02 03 A1 CC了。

 

9.在接收框中以十六進位制顯示

    這就容易多了:  在主對話方塊中加入一個複選接鈕,IDC_CHECK_HEXDISPLAY Caption: 十六進位制顯示,再利用ClassWizard為其新增控制變數:m_ctrlHexDiaplay。 然後修改CSCommTestDlg::OnComm()函式:

void CSCommTestDlg::OnComm() 
{
// TODO: Add your control notification handler code here
VARIANT variant_inp;
COleSafeArray safearray_inp;
LONG len,k;
BYTE rxdata[2048]; //
設定BYTE陣列 An 8-bit integerthat is not signed.
CString strtemp;
if(m_ctrlComm.GetCommEvent()==2) //事件值為2表示接收緩衝區內有字元
{
variant_inp=m_ctrlComm.GetInput(); //讀緩衝區
safearray_inp=variant_inp; //VARIANT型變數轉換為ColeSafeArray型變數
len=safearray_inp.GetOneDimSize(); //得到有效資料長度
for(k=0;k<len;k++)
safearray_inp.GetElement(&k,rxdata+k);//轉換為BYTE型陣列
for(k=0;k<len;k++) //將陣列轉換為Cstring型變數
{
BYTE bt=*(char*)(rxdata+k); //字元型
if(m_ctrlHexDisplay.GetCheck())
strtemp.Format("%02X ",bt); //將字元以十六進位制方式送入臨時變數strtemp存放,注意這裡加入一個空隔
else 
strtemp.Format("%c",bt); //將字元送入臨時變數strtemp存放

m_strRXData+=strtemp; //加入接收編輯框對應字串 
}
}
UpdateData(FALSE); //更新編輯框內容
}

測試:在串列埠除錯助手傳送框中輸入00 01 02 03 A1 CC等十六進位制字元,並選上以十六進位制傳送,單擊手動傳送,在本程式執行後選上以十六進位制顯示,在串列埠除錯助手中單擊手動傳送或自動傳送,則在本程式的接收框中應該可以看到00 01 02 03 A1 CC了。

 

10.如何設定自動傳送

     最簡單的設定自動傳送週期是用SetTimer()函式,這在資料採集中很有用,在控制中指令的傳送也可能用到定時傳送。

    方法是:在ClassWizard中選上MessageMap卡,然後在Objects IDs選中CSCommTestDlg類,再在Messages框中選上WM_TIMER訊息,單擊ADD_FUNCTION加入void CSCommTestDlg::OnTimer(UINT nIDEvent) 函式,這個函式是放入“時間到”後要處理的程式碼:

void CSCommTestDlg::OnTimer(UINT nIDEvent) 
{
// TODO: Add your message handler code here and/or call default
OnButtonManualsend();
CDialog::OnTimer(nIDEvent);
}

再在在主對話方塊中加入一個複選接鈕,ID為IDC_CHECK_AUTOSEND Caption: 自動傳送(週期1秒),再利用ClassWizard為其新增BN_CLICK訊息處理函式void CSCommTestDlg::OnCheckAutosend():

void CSCommTestDlg::OnCheckAutosend() 
{
// TODO: Add your control notification handler code here
m_bAutoSend=!m_bAutoSend;
if(m_bAutoSend)
{
SetTimer(1,1000,NULL);//時間為1000毫秒
}
else
{
KillTimer(1);  //取消定時
}
}

其中:m_bAutoSend為BOOL型變數,在CLASSVIEW中為CSCommTestDlg類加入,並在建構函式中初始化:

      m_bAutoSen=FALSE;
現在可以執行程式測試了。

 

11.什麼是VARIANT資料型別?如何使用VARIANT資料型別?

     不知如何使用VARIANT資料型別, 有不少朋友對VARIANT這個新的資料型別大感頭疼。SetOutput()函式中 需要的VARIANT引數還可以使用COleVariant類的建構函式簡單生成,現在GetInput()函式的返回值也成了VARIANT型別,那麼如何從返回的值中提取有用的內容。 VARIANT及由之而派生出的COleVariant類主要用於在OLE自動化中傳遞資料。實際上VARIANT也只不過是一個新定義的結構罷了,它的主要成員包括一個聯合體及一個變數。該聯合體由各種型別的資料成員構成,而該變數則用來指明聯合體中目前起作用的資料型別。我們所關心的接收到的資料就儲存在該聯合體的某個資料成員中。 該聯合體中包含的資料型別很多,從一些簡單的變數到非常複雜的陣列和指標。由於通過串列埠接收到的內容常常是一個位元組串,我們將使用其中的某個陣列或指標來訪問接收到的資料。這裡推薦給大家的是指向一個SAFEARRAY(COleSafeArray)型別變數。新的資料型別SAFEARRAY正如其名字一樣,是一個“安全陣列”,它能根據系統環境自動調整其16位或32 位的定義,並且不會被OLE改變(某些型別如BSTR在16位或32位應用程式間傳遞時會被OLE翻譯從而破壞其中的二進位制資料)。大家無須瞭解SAFEARRAY的具體定義,只要知道它是另外一個結構,其中包含一個 (void *)型別的指標pvData,其指向的記憶體就是存放有用資料的地方。簡而言之,從GetInput()函式返回的VARIANT型別變數中,找出parray 指標,再從該指標指向的SAFEARRAY變數中找出pvData指標,就可以向訪問陣列一樣取得所接收到的資料了。具體應用請參見void CSCommTestDlg::OnComm()函式。

    大概我現在也說不清這個問題,我自己從第一次接觸這個東西,到現在還是給別人講不清。

 

另:二進位制收發設定請參考MSComm控制元件說明

相關文章