此文章以visual C++數(shù)據(jù)采集與串口通信測控應用實戰(zhàn)為參考教程
此文章適合VC++串口通信入門 一、頁面布局及添加控件 1, 安裝好vs2010如圖
2, 新建一個基于VC++的MFC項目comm
注意:點擊ok,然后next,這時候要將application type改成dialog base,接著next到最后一個對話框是將generated dasses改成CcommDlg,,然后finish
4, 將新生成的項目的對話框默認dialog edit刪去,如圖
5,在對話框中添加兩個static text,,兩個edit text,,兩個按鈕, 成品圖如下
6,添加comm控件 1)在解決方案窗口右擊新建的解決方案,,點擊add->class 2)選擇MFC class from activex control如圖
點擊add,available activex controls選擇microsoft communication controls versions 6.0,然后點確定就行 這時候?qū)υ捒驎霈F(xiàn)一個電話圖標,,可能有一半白邊去不了,這時候右擊電話圖標點擊edit control就可以去掉了,。 7,同時定義各個控件的類型,、ID及相關(guān)屬性
注:此項目只添加了發(fā)送和退出程序按鈕 這時候得到了完整的串口通信對話框:
8, 添加成員變量,右擊對話框,,點擊class wizard,點擊member variables標簽,,選中需要添加的id,雙擊即可添加 依次為下表中的ID添加變量
期間,IDC_MSCOMM1控件在標簽中沒有,,則在生成的對話框中右擊comm控件點擊add variables即可 9, 為mscomm,,兩個button添加響應事件,切換到class wizard的virtual function雙擊控件ID,添加響應事件,,默認即可,,也可改為自己想要的標題 為comm控件添加響應事件可能雙擊不了(我就是遇到這種問題),這時候只要右擊comm控件圖標,,點擊add event handler即可,。如圖
這時候基本界面已經(jīng)布置好了,開始添加代碼了,。 二,、代碼添加 1、找到解決方案(solution explorer)的sources files點開,雙擊其中的mscommDlg.cpp我們的所有代碼將添加到這個源文件中 2,、進行串口初始化及其他串口設(shè)置 將以下代碼添加到oninitialdialog函數(shù) m_ctrlcomm.put_CommPort(3);//選擇com3口 m_ctrlcomm.put_InputMode(1);//輸入方式為二進制方式 m_ctrlcomm.put_InBufferSize(1024);//輸入緩沖區(qū)大小為1024byte m_ctrlcomm.put_OutBufferSize(512);//輸出緩沖區(qū)大小為512byte m_ctrlcomm.put_Settings(_T("9600,n,8,1"));//設(shè)置串口參數(shù):9600波特率,,無奇偶校驗,8個數(shù)據(jù)位,,1個停止位 if(!m_ctrlcomm.get_PortOpen()) m_ctrlcomm.put_PortOpen(1);//打開串口 m_ctrlcomm.put_RThreshold(1);//每當串口接收緩沖區(qū)有多余或等于1個字符時將引發(fā)一個接收數(shù)據(jù)的oncomm事件 m_ctrlcomm.put_InputLen(0);//設(shè)置當前接收區(qū)數(shù)據(jù)長度為0 m_ctrlcomm.get_Input();//預讀緩沖區(qū)以清空殘留數(shù)據(jù)
m_ctrlcomm.put_CommPort(3);//選擇com3口 m_ctrlcomm.put_InputMode(1);//輸入方式為二進制方式 m_ctrlcomm.put_InBufferSize(1024);//輸入緩沖區(qū)大小為1024byte m_ctrlcomm.put_OutBufferSize(512);//輸出緩沖區(qū)大小為512byte m_ctrlcomm.put_Settings(_T("9600,n,8,1"));//設(shè)置串口參數(shù):9600波特率,,無奇偶校驗,8個數(shù)據(jù)位,,1個停止位 if(!m_ctrlcomm.get_PortOpen()) m_ctrlcomm.put_PortOpen(1);//打開串口 m_ctrlcomm.put_RThreshold(1);//每當串口接收緩沖區(qū)有多余或等于1個字符時將引發(fā)一個接收數(shù)據(jù)的oncomm事件 m_ctrlcomm.put_InputLen(0);//設(shè)置當前接收區(qū)數(shù)據(jù)長度為0 m_ctrlcomm.get_Input();//預讀緩沖區(qū)以清空殘留數(shù)據(jù) 2,、實現(xiàn)發(fā)送按鈕,退出按鈕相應的響應函數(shù) void CmscommDlg::OnBnClickedOk() { // TODO: Add your control notification handler code here CDialogEx::OnOK(); UpdateData(1);//讀取編輯框內(nèi)容 m_ctrlcomm.put_Output(COleVariant(m_strsend));//發(fā)送數(shù)據(jù) }
void CmscommDlg::OnBnClickedExit() { // TODO: Add your control notification handler code here m_ctrlcomm.put_PortOpen(0);//關(guān)閉串口 CDialog::OnCancel();//退出程序 } 3,、實現(xiàn)MSComm控件相應的響應函數(shù)OnOnCommMsComm1() VARIANT variant_inp; COleSafeArray safearray_inp; LONG len,k; BYTE rxdata[2048];//設(shè)置byte數(shù)組 CString strtemp; if(m_ctrlcomm.get_CommEvent()==2)//事件2表示接受緩沖區(qū)有字符 { variant_inp=m_ctrlcomm.get_Input();//讀緩沖區(qū) safearray_inp=variant_inp;//variant數(shù)據(jù)轉(zhuǎn)換成colesafearray型變量 len=safearray_inp.GetOneDimSize();//得到有效數(shù)據(jù)長度 for(k=0;k<len;k++) safearray_inp.GetElement(&k,rxdata+k);//轉(zhuǎn)換為byte型數(shù)組 for(k=0;k<len;k++){//將數(shù)組轉(zhuǎn)換成CString型變量 BYTE bt=*(char *)(rxdata+k);//字符型 strtemp.Format((char) bt);//將字符送入臨時變量strtemp存放 m_strreceive+=strtemp;//加入接收編輯框相應字符串 } } UpdateData(0);//更新編輯框內(nèi)容 4,、編譯運行程序
在調(diào)試運行時,必須兩臺機子同時運行此程序,,并且都要開啟同一個串口號 VS2010下MFC的串口編程串口通信簡介 一般來說,,計算機都有一個或多個串行端口,這些串口提供了外部設(shè)備與PC進行數(shù)據(jù)傳輸和通信的通道,,在CPU和外設(shè)之間充當解釋器的角色,。當字符數(shù)據(jù)從CPU發(fā)送給外設(shè)時,這些字符數(shù)據(jù)將被轉(zhuǎn)換成串行比特流數(shù)據(jù),;當接收數(shù)據(jù)時,,比特流數(shù)據(jù)被轉(zhuǎn)換為字符數(shù)據(jù)傳遞給CPU,再進一步說,,在操作系統(tǒng)方面,Windows用通信驅(qū)動程序(COMM.DRV)調(diào)用API函數(shù)發(fā)送和接收數(shù)據(jù),;當用通信控件或聲明調(diào)用API函數(shù)時,,它們由COMM.DRV解釋并傳遞給設(shè)備驅(qū)動程序。作為一個程序員,,要編寫通信程序,,只需知道通信控件提供的Windows API通信函數(shù)的接口即可,換句話說,,只需設(shè)定和監(jiān)視通信控件的屬性和事件即可,。 串口通信方法一般有以下幾種: - 利用Windows API通信函數(shù);
- 利用Visual C++的標準通信函數(shù)_inp,、_inpw,、_inpd、_outp,、_outpw,、_outpd等直接對串口進行操作;
- 通過微軟的串口通信控件MSComm,它是一種ActiveX控件,;
- 利用第3方編寫的通信類,,比如MuMega Technologies公司提供的CSerail類;
我在項目開發(fā)過程中用的是第三種方法——通過MSComm控件操作串口,,下面是我使用此控件的筆記,。 MSComm控件簡介 MSComm 是 Microsoft 公司為簡化Windows下串行端口編程而提供的ActiveX控件,它提供了一系列標準通訊命令的使用接口,。MSComm 控件通過串行端口(serial port)傳送和接收數(shù)據(jù),,為應用程序提供了串行通訊功能。在可視化編程盛行的今天,,我們可以很方便的在Visual Basic(VB),、Visual C++(VC)、Delphi等語言及開發(fā)平臺中應用,。處理數(shù)據(jù)的方式有事件驅(qū)動(Event-driver),、查詢法(Inquire)兩種。 事件驅(qū)動法:在使用事件驅(qū)動法設(shè)計程序時,,每當有新字符到達,、端口狀態(tài)變化或發(fā)生錯誤時,MSComm控件將觸發(fā)OnComm事件,,而應用程序在捕獲該事件后,,通過檢查MSComm控件的CommEvent屬性可以獲知所發(fā)生的事件或錯誤,從而采取相應的操作,。這種方法的優(yōu)點是程序響應及時,,可靠性高。 查詢法:這種方法適合于較小的應用程序,。在這種情況下,,每當應用程序執(zhí)行完某一串行口操作后,將不斷檢查MSComm控件的CommEvent屬性以檢查執(zhí)行結(jié)果或者檢查某一事件是否發(fā)生,。例如,,當程序向串行設(shè)備發(fā)送了某個命令后,可能只是在等待收到一個特定的響應字符串,,而不是對收到的每一個字符都立刻響應并處理,。 使用的每個MSComm控件都與一個串口對應。如果在應用程序中需要訪問多個串口,,必須使用多個MSComm控件,,可以在Windows 控制面板中修改串口地址的中斷地址。 MSComm控件的常用屬性 - CommPort屬性:設(shè)置或返回通訊端口號,,可以設(shè)置為1到16之間的任何值,;
- Settings屬性:以字符串形式設(shè)置或返回波特率,、奇偶校驗、數(shù)據(jù)位和停止位;
- PortOpen屬性:設(shè)置或返回通訊口的狀態(tài)以及打開和關(guān)閉端口,,可通過把該屬性設(shè)置為true或者false來打開或者關(guān)閉端口,;
- InBufferSize和OutBufferSize屬性:分別設(shè)置接收和發(fā)送緩沖區(qū)分配的內(nèi)存數(shù)量,單位為字節(jié),,缺省值分別為1024byte和512byte,;
- InputLen屬性:確定希望從接收緩沖區(qū)移出的字符數(shù)量,當InputLen=0時,,一次把接收緩沖區(qū)的字符全部移出,;
- Input屬性:從接收緩沖區(qū)中讀出數(shù)據(jù),然后將該數(shù)據(jù)從緩沖區(qū)移走,。
- OutPut屬性:向發(fā)送緩沖區(qū)傳遞待發(fā)送的數(shù)據(jù),。
- InBufferCount和OutBufferCount屬性:分別確定當前駐留在接收緩沖區(qū)等待被取出和發(fā)送緩沖區(qū)準備發(fā)送的字符數(shù)量,這兩個屬性設(shè)置為0,,接收和發(fā)送緩沖區(qū)的內(nèi)容將被清除,;
- InputMode屬性:設(shè)置接收傳入數(shù)據(jù)的格式,設(shè)置為0采用文本形式,,設(shè)置為1采用二進制格式,;
- SThreshold屬性:保存一個產(chǎn)生發(fā)送OnComm事件的界限值,本系統(tǒng)設(shè)置該屬性為0,,發(fā)送數(shù)據(jù)時不產(chǎn)生OnComm事件,;
- RThreshold屬性:設(shè)定當接收幾個字符時觸發(fā)OnComm事件,本系統(tǒng)設(shè)置該屬性為1,,每接收一個字符就產(chǎn)生一個OnComm事件,;
MSComm控件的事件 MSCOMM控件只使用一個事件OnComm,用屬性CommEvent的17個值來區(qū)分不同的觸發(fā)時機,,主要有以下幾個: - CommEvent=1時:傳輸緩沖區(qū)中的字符個數(shù)已少于Sthreshold(可設(shè)置的屬性值)個,;
- CommEvent=2時:接收緩沖區(qū)中收到Rthreshold(可設(shè)置的屬性值)個字符,利用此事件可編寫接收數(shù)據(jù)的過程,;
- CommEvent=3時:CTS線發(fā)生變化,;
- CommEvent=4時:DSR線發(fā)生變化,;
- CommEvent=5時:CD線發(fā)生變化,;
- CommEvent=6時:檢測到振鈴信號;
另外十種情況是通信錯誤時產(chǎn)生,,即錯誤代碼,。 基于VS2010下MFC的MSComm串口程序的實現(xiàn) 1、注冊MSComm控件 我在網(wǎng)上下載了MSComm控件之后,,將其放于項目目錄下,,并在當前目錄建了個.bat批處理文件,,其內(nèi)容如下: copy .\\MSCOMM\\MSCOMM.SRG %windir%\system32
copy .\\MSCOMM\\MSCOMM32.DEP %windir%\system32
copy .\\MSCOMM\\MSCOMM32.oca %windir%\system32
copy .\\MSCOMM\\mscomm32.ocx %windir%\system32
regsvr32 mscomm32.ocx 雙擊此文件,即可注冊MSComm控件,。 2,、添加MSComm控件 首先將MSComm控件添加進VS2010工具箱,再給項目添加該ActiveX控件對應的“基于MFC的ATL類”,,最后將工具箱中的MSComm控件(電話圖標)拖至對話框即可,。在對話框中添加MSComm控件后,其側(cè)面會有白色,,右擊此控件,,選擇“編輯控件”,即可去除白色,。 3,、添加控件變量 在主對話框中添加與MSComm控件相關(guān)聯(lián)的控件變量(成員對象),通過此成員變量可操作串口,。 4,、串口信息配置及打開串口 在對話框模板上右擊MSComm控件,選擇Property菜單項,,即可設(shè)置MSComm控件各項屬性,。在調(diào)制解調(diào)器通訊的程序中,設(shè)置“Control”屬性頁中Handshaking項為“2-comRTS”,,否則國內(nèi)部分廠家modem不能正常通訊,,其它接受缺省設(shè)置。另外亦可通過修改對話框類的OnInitDialog()函數(shù)來設(shè)置控件的屬性,。具體參考MSDN中的關(guān)于Comm Control的詳細說明,。 我程序的串口設(shè)置代碼大致如下: //*********************** 串口設(shè)置 **************************//
m_ctrlComm.put_CommPort(port);//選擇com口
m_ctrlComm.put_InputMode(1);//輸入方式為二進制方式
m_ctrlComm.put_InBufferSize(1024);//輸入緩沖區(qū)大小為1024byte
m_ctrlComm.put_OutBufferSize(512);//輸出緩沖區(qū)大小為512byte
CString strBaudrate;
strBaudrate.Format(_T("%ld"),baudrate);
m_ctrlComm.put_Settings(strBaudrate+_T(",n,8,1"));//設(shè)置串口參數(shù):9600波特率,無奇偶校驗,,8個數(shù)據(jù)位,,1個停止位
if(!m_ctrlComm.get_PortOpen())
{
/*
HANDLE m_hCom;
CString strCom;
strCom.Format(_T("\\\\.\\COM%d"),(int)(m_ctrlComm.get__CommPort()));
// 這里的CreateFile函數(shù)起了很大的作用,可以用來創(chuàng)建系統(tǒng)設(shè)備文件,,
//如果該設(shè)備不存在或者被占用,,則會返回一個錯誤,即下面的 INVALID_HANDLE_VALUE ,,
//據(jù)此可以判斷可使用性,。詳細參見MSDN中的介紹。
m_hCom = CreateFile(strCom, 0, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(m_hCom == INVALID_HANDLE_VALUE)//如果沒有該設(shè)備,,或者被其他應用程序在用
{
int errornum=GetLastError();
if(errornum==2)
strCom.Format(_T("端口%d 不存在"),(int)(m_ctrlComm.get__CommPort()));
else if(errornum==5)
strCom.Format(_T("端口%d被占用"),(int)(m_ctrlComm.get__CommPort()));
AfxMessageBox(strCom);
CloseHandle(m_hCom); // 關(guān)閉文件句柄,,后面我們采用控件,不用API
return ;//這是因為串口初始化封裝在另一個函數(shù)里面在OnInitDialog調(diào)用,。
}
CloseHandle(m_hCom); // 關(guān)閉文件句柄,,后面我們采用控件,,不用API
*/
try
{
m_ctrlComm.put_PortOpen(true);//打開串口
}
catch(COleDispatchException *e)
{
CString strError;
strError.Format(_T("打開串口失敗,!\n\nError Number: %d \nError Message: %s"),
e->m_wCode,e->m_strDescription);
MessageBoxW(strError,_T("錯誤提示"),MB_ICONERROR);
return;
}
}
else
{
//MessageBox(_T("Cannot open serial port!"));
}
m_ctrlComm.put_RThreshold(1);//每當串口接收緩沖區(qū)有多余或等于1個字符時將引發(fā)一個接收數(shù)據(jù)的oncomm事件
m_ctrlComm.put_InputLen(0);//設(shè)置當前接收區(qū)數(shù)據(jù)長度為0
m_ctrlComm.get_Input();//預讀緩沖區(qū)以清空殘留數(shù)據(jù) 5,、串口數(shù)據(jù)的讀寫 MSComm 類的讀寫函數(shù)比較簡單:get_Input()和put_Output()。函數(shù)原形分別為VARIANT get_Input()和void put_Output(const VARIANT newValue),均使用VARIANT類型,。但PC機發(fā)送和接收數(shù)據(jù)時習慣用字符串形式,。MSDN中查閱VARIANT類型,可以用BSTR表示字符串,,但所有的BSTR都包含寬字符,,而只有Windows NT支持寬字符,Windows 9X并不支持,。所以要完成一個適應各平臺的串口應用程序必須解決這個問題,,這里使用CByteArray解決之。 添加接收數(shù)據(jù)函數(shù),,在對話框中雙擊Comm Control,,接受默認函數(shù),則對話框類的成員函數(shù)為OnCommMscomm(),,其大致代碼如下: CDataTypeConverter DTC;
//電話圖標可能有一半白邊去不了,,右擊電話圖標點擊edit control就可以去掉
if(m_ctrlComm.get_CommEvent()==2)//事件值為2表示接收事件
{
BYTE rxdata[255]={0};//設(shè)置BYTE數(shù)組
VARIANT variant_inp=m_ctrlComm.get_Input();//讀緩沖區(qū)
COleSafeArray safearray_inp = variant_inp;//VARIANT型變量轉(zhuǎn)換為COleSafeArray變量
long len=safearray_inp.GetOneDimSize();//得到有效數(shù)據(jù)長度
for(long k=0;k<len;k++)
safearray_inp.GetElement(&k,rxdata+k);//轉(zhuǎn)換為BYTE數(shù)組
m_ctrlComm.put_OutBufferCount(0);//清空發(fā)送緩沖區(qū)
m_ctrlComm.put_InBufferCount(0);//滑空接收緩沖區(qū)
safearray_inp.Clear();
for(long k=0;k<len;k++)
{
BYTE bt = *(char*)(rxdata+k);//字符型
short int intDec=(int)bt;
CString strtemp=DTC.Dec2Hex(intDec);
m_strDataRXTemp+=strtemp;//加入接收編輯框?qū)址?/span>
}
m_strDataRX=m_strDataRXTemp;
m_strDataRXTemp=""; } 其中,Dec2Hex()函數(shù)的代碼如下: CString CDataTypeConverter::Dec2Hex(unsigned int intDec)
{
CString strHex;
char charHex[255];
sprintf(charHex,"%x",intDec);
strHex=charHex;
if(strHex.GetLength()==1)
strHex=_T("0")+strHex;
return strHex;
} 發(fā)送數(shù)據(jù)的代碼大致如下: //UpdateData(true);//讀取編輯框內(nèi)容m_strDataTX
//發(fā)送的字符串上表面為十六進制格式
CString m_strCtrlLightBL;
m_strCtrlLightBL="55AA0AAA6B4310100000";//"55aa0aaa6b4310100000"
CDataTypeConverter DTC;
COleVariant m_OleVariant=DTC.HexM2OleVariant(m_strCtrlLightBL);
m_ctrlComm.put_Output(m_OleVariant);//發(fā)送數(shù)據(jù) 其中,,HexM2OleVariant()函數(shù)定義如下: COleVariant CDataTypeConverter::HexM2OleVariant(CString strHexM)
{
BYTE bt[255];
short int len=strHexM.GetLength();
short int length=0;
short int intDec;
for(int n=0,i=0;n<len-1;n+=2,i++)
{
intDec=Hex2Dec(strHexM.Mid(n,2));
bt[i]=char(intDec);
length=i+1;
}
CByteArray m_Array;
m_Array.RemoveAll();
m_Array.SetSize(length);
for(int i=0;i<length;i++)
m_Array.SetAt(i,bt[i]);
return COleVariant(m_Array);
} 注意:接收數(shù)據(jù)時,,RThreshold屬性很重要,因為它影響著OnComm事件的觸發(fā)條件,,在程序中可以通過put_RThreshold()函數(shù)來設(shè)定RThreshold屬性,。
|