VC基于MSCOMM控件串口通讯
在mfc中進(jìn)行串口通訊最簡單的方法莫過于在對(duì)話框中使用MSCOMM控件了,MSComm通信控件提供了一系列標(biāo)準(zhǔn)通信命令的接口,它允許建立串口連接,可以連接到其他通信設(shè)備(如Modem).
還可以發(fā)送命令、進(jìn)行數(shù)據(jù)交換以及監(jiān)視和響應(yīng)在通信過程中可能發(fā)生的各種錯(cuò)誤和事件,從而可以用它創(chuàng)建全雙工 、事件驅(qū)動(dòng)的、高效實(shí)用的通信程序。
一、用MSComm控件通信
1.串口通信基礎(chǔ)知識(shí)
???? 一般悅來,計(jì)算機(jī)都有一個(gè)或多個(gè)串行端口,它們依次為com1、Com2、…,這些串口還提供了外部設(shè)備與pC進(jìn)行數(shù)據(jù)傳輸和
皿信的通道。這些串口在CPU和外設(shè)之間充當(dāng)解釋器的角色。當(dāng)字符數(shù)據(jù)從CPU發(fā)送給外設(shè)時(shí),這些字符數(shù)據(jù)將被轉(zhuǎn)換成串行比特
流數(shù)據(jù);當(dāng)接收數(shù)據(jù)時(shí),比特流數(shù)據(jù)被轉(zhuǎn)換為字符數(shù)據(jù)傳遞給CPU,再進(jìn)一步說,在操作系統(tǒng)方面,Windows用通信驅(qū)動(dòng)程序
(COMM.DRV)調(diào)用API函數(shù)發(fā)送和接收數(shù)據(jù),當(dāng)用通信控件或聲明調(diào)用API函數(shù)時(shí),它門由COMM. DRV解釋并傳遞給設(shè)備驅(qū)動(dòng)程序,
作為一個(gè)vB程序員,要編寫通信程序.只需知道通信控件提供給Windows通信AP1函數(shù)的接口即可.換句話說,只需設(shè)定和監(jiān)視通
信控件的屬性和事件即可。
2.使用Mscomm控件
在開始使用MSComm控件之前。需要先了解其屬性、事件或錯(cuò)誤
屬性???????????? 描述
CommPort???? 設(shè)置或返回通信端口號(hào)
Settings???? 以字符串的形式設(shè)置或返回波特率、奇偶校驗(yàn)、數(shù)據(jù)位和停止位
PortOpen???? 設(shè)置或返回通信端口的狀態(tài)。也可以打開和關(guān)閉端口
Input??????? 返回和刪除接收緩沖區(qū)中的字符
Output?????? 將字符串寫入發(fā)送緩沖區(qū)
CommEvent屬性為通信事件或錯(cuò)誤返回下列值之一。在該控件的對(duì)象庫中也可以找到這些常量。
常量??????????? 值???????? 描述
ComEventBreak??? 1001???? 收到了斷開信號(hào)
ComEventCTSTO??? 1002???? Clear To Send Timeout。在發(fā)送字符時(shí),在系統(tǒng)指定的事1件內(nèi),CTS(Clear To Send)線是低電平
ComEventDSRTO??? 1003???? Data Set Ready Timeout。在發(fā)送字符時(shí),在系統(tǒng)指定的事件內(nèi),DSR(Data Set Ready)線是低電平
ComEventFrame??? 1004???? 數(shù)據(jù)幀錯(cuò)誤。硬件檢測到一個(gè)數(shù)據(jù)幀錯(cuò)誤
ComEventOverrun 1006???? 端口溢出。硬件中的字符尚未讀,下一個(gè)字符又到達(dá),并且丟失
ComEventCDTO???? 1007???? Carrier Detect Time。在發(fā)送字符時(shí),在系統(tǒng)指定的事件內(nèi),CD(Carrier Detect)線是低電平。CD
???????????????????????? 也稱為RLSD(Receive Line Singal Detect,接收線信號(hào)檢測)
ComEventRxOver?? 1008???? 接收緩沖區(qū)溢出。在接收緩沖區(qū)中沒有空間
ComEventRxParity 1009??? 奇偶校驗(yàn)錯(cuò)。硬件檢測到奇偶校驗(yàn)錯(cuò)誤7
ComEventTxFull?? 1010???? 發(fā)送緩沖區(qū)滿。在對(duì)發(fā)送字符排隊(duì)時(shí),發(fā)送緩沖區(qū)滿
ComEventDCB????? 1011???? 檢取端口DCB(Device Control Blick)時(shí)發(fā)生了沒有預(yù)料到的錯(cuò)誤
通信事件包含了下面的設(shè)置:
常量????????? 值???????? 描述
ComEvSend?????? 1???? 發(fā)送緩沖區(qū)中的字符數(shù)比Sthreshold值低
ComEvReceive??? 2???? 接收到了Rthreshold個(gè)字符。持續(xù)產(chǎn)生該事件,直到使用了Input屬性刪除了接收緩沖區(qū)中的數(shù)據(jù)
ComEvCTS??????? 3???? CTS(Clear To Send)線改變
ComEvDSR??????? 4???? DSR(Data Set Ready)線改變。當(dāng)DSR從1到0改變時(shí),該事件發(fā)生
ComEvCD???????? 5???? CD(Carrier Detect)線改變ComEvRing6檢測到響鈴信號(hào)。一些URAT(Universal AsynchronousReciver-
???????????????????? -Transmitters,通用異步收發(fā)器)不支持該事件
ComEvEOF??????? 7???? 收到了EOF字符(ASCII字符26)
Error消息(MSComm控件)下表列出了MSComm控件可捕獲的錯(cuò)誤消息:
常量??????????????????????? 值?????? 描述
ComInvalidPropertyValue???? 380??? 無效的屬性值
ComSetNotSupported????????? 383??? 屬性只讀
ComGetNotSupported????????? 394??? 屬性只讀
ComPortOpen??????????????? 8000??? 端口打開時(shí)該存在無效
?????????????????????????? 8001??? 超時(shí)設(shè)置必須比0值大
ComPortInvalid???????????? 8002??? 無效的端口號(hào)
?????????????????????????? 8003??? 屬性只在運(yùn)行時(shí)有效
?????????????????????????? 8004??? 屬性在運(yùn)行時(shí)是只讀的
ComPortAleadyOpen????????? 8005??? 端口已經(jīng)打開
?????????????????????????? 8006??? 設(shè)備標(biāo)識(shí)符無效或不支持
?????????????????????????? 8007??? 不支持設(shè)備的波特率
?????????????????????????? 8008??? 指定的字節(jié)大小無效
?????????????????????????? 8009??? 缺省參數(shù)錯(cuò)誤
?????????????????????????? 8010??? 硬件不可用(被其他設(shè)備鎖住)
?????????????????????????? 8011??? 函數(shù)不能分配隊(duì)列
ComNoOpen????????????????? 8012??? 設(shè)備沒有打開
?????????????????????????? 8013??? 設(shè)備已經(jīng)打開
?????????????????????????? 8014??? 不能使用通信通知
ComSetCommStateFailed????? 8015??? 不能設(shè)置通信狀態(tài)
?????????????????????????? 8016??? 不能設(shè)置通信事件屏蔽
ComPortNotOpen???????????? 8018??? 該存在只在端口打開是有效
?????????????????????????? 8019??? 設(shè)備忙
ComReadError?????????????? 8020??? 通信設(shè)備讀錯(cuò)誤
ComDCBError??????????????? 8021??? 檢取端口設(shè)備控制塊時(shí)出現(xiàn)內(nèi)部錯(cuò)誤
注意在使用的時(shí)候一定要保證兩個(gè)通訊串口的設(shè)置是相同的,否則受到的信息將會(huì)產(chǎn)生錯(cuò)誤!
由于取值位數(shù)的不同,有可能發(fā)送的信息要讀很多次才能組合成需要的信息!
1。建立mfc工程,都會(huì)撒。??
??? 將控件加進(jìn)來:打開“Project->Add To Project->Components and Controls->Registered Activex Controls”,然后選擇控件:Microsoft Communication Control,version 6.0插入到當(dāng)前的工程中。這樣就將類 CMSComm 的相關(guān)文件 mscomm.cpp 和 mscomm.h 一并加入到了工程中。編程時(shí)只需將控件對(duì)話中的 MSComm 控件拖至你的應(yīng)用對(duì)話框中就OK了
2。定義串口對(duì)象:
???? CMSComm?????? m_MSComm;
3。串口初始化:
???
DWORD style=WS_VISIBLE;
m_MSComm.Create(NULL,style,CRect(0,0,0,0),this,IDC_MSCOMM);
if(m_MSComm.GetPortOpen()) //如果串口是打開的,則行關(guān)閉串口
{
m_MSComm.SetPortOpen(FALSE);
}
m_MSComm.SetCommPort(1); //選擇COM1
m_MSComm.SetInBufferSize(1024); //接收緩沖區(qū)
m_MSComm.SetOutBufferSize(1024);//發(fā)送緩沖區(qū)
m_MSComm.SetInputLen(0);//設(shè)置當(dāng)前接收區(qū)數(shù)據(jù)長度為0,表示全部讀取
m_MSComm.SetInputMode(1);//以二進(jìn)制方式讀寫數(shù)據(jù)
m_MSComm.SetRThreshold(1);//接收緩沖區(qū)有1個(gè)及1個(gè)以上字符時(shí),將引發(fā)接收數(shù)據(jù)的OnComm事件
m_MSComm.SetSettings("9600,n,8,1");//波特率9600無檢驗(yàn)位,8個(gè)數(shù)據(jù)位,1個(gè)停止位
if(!m_MSComm.GetPortOpen())//如果串口沒有打開則打開
m_MSComm.SetPortOpen(TRUE);//打開串口
else
{
m_MSComm.SetOutBufferCount(0);
AfxMessageBox("Open The Serial Port 1 Failurre!");
}
4。串口數(shù)據(jù)讀寫:
MSComm 類的讀寫函數(shù)比較簡單:GetInput()和SetOutput()。函數(shù)原形分別為VARIANT GetInput()和void SetOutput(const VARIANT newValue),均使用VARIANT類型。但PC機(jī)發(fā)送和接收數(shù)據(jù)時(shí)習(xí)慣用字符串形式。MSDN中查閱VARIANT類型,可以用BSTR表示字符串,但所有的BSTR都包含寬字符,而只有Windows NT支持寬字符,Windows 9X并不支持。所以要完成一個(gè)適應(yīng)各平臺(tái)的串口應(yīng)用程序必須解決這個(gè)問題。這里使用CbyteArray即可解決之。
發(fā)數(shù)據(jù):在對(duì)話框?qū)尤?按鈕 控件并給你添加消息
??? void CTest_mscommDlg::OnSend()
{
// TODO: Add your control notification handler code here
int i,Count;
CString m_SendData;
m_SendData="Hello!";
Count=m_SendData.GetLength();
CByteArray m_Array;
m_Array.RemoveAll();
m_Array.SetSize(Count);
for(i=0;i?? m_Array.SetAt(i,m_SendData[i]);
m_MSComm.SetOutput(COleVariant(m_Array));
}
收數(shù)據(jù):給串口控件添加消息
void CTest_mscommDlg::OnOnCommMscomm()
{
VARIANT m_input;
char *str,*str1;
int k,nEvent,i;
CString str2,m_RcvData;
nEvent=m_MSComm.GetCommEvent();
switch(nEvent)
{
case 2:
?? k=m_MSComm.GetInBufferCount();????? //接收緩沖區(qū)的字符數(shù)目
?? if(k>0)
?? {
??? m_input=m_MSComm.GetInput();
??? str=(char*)(unsigned char*)m_input.parray->pvData;
?? }
?? i=0;
?? str1=str;
?? while(i?? {
??? i++;
??? str1++;
?? }
?? *str1='{post.content}';??????????????????????????????
?? str2=(const char*)str;????????????? //清除字符串中的不必要字符
?? m_RcvData=(const char *)str;
}
//數(shù)據(jù)顯示處理
m_disp+=m_RcvData;
UpdateData(false);
}
=====================================
摘要:本文介紹了在Microsoft Visual C++ 6.0環(huán)境下通過對(duì)Active X控件的編程來實(shí)現(xiàn)串口的通信的一般方法。
一、 引言
當(dāng)我們?cè)赪indows操作系統(tǒng)下開發(fā)串行通信程序時(shí)通常不得不面對(duì)許多復(fù)雜的API函數(shù),因?yàn)樵赪indows操作系統(tǒng)下不能直接對(duì)設(shè)備端口進(jìn)行操作,也不能在系統(tǒng)級(jí)(Ring 3級(jí)別)使用任何DOS或BIOS中斷,如要對(duì)端口進(jìn)行編程則只能以文件的形式來對(duì)端口進(jìn)行操作,這就使開發(fā)人員不得不面對(duì)非常煩瑣的API函數(shù)編程。本文對(duì)此提出了另外一種封裝性很好的使用Microsoft Visual C++ 6.0自帶的"Microsoft Communications Control,version 6.0"Active X控件的編程方法,通過對(duì)該控件的正確使用,我們可以比較輕松地編寫出所需的串行通信程序。
下面,我們將結(jié)合一個(gè)實(shí)際的程序示例來對(duì)此方法進(jìn)行說明。本程序的編程環(huán)境是Windows 98和Microsoft Visual C++ 6.0。在本程序示例中對(duì)為避免阻塞而對(duì)線程的使用以及在使用中遇到的一些問題也做了詳細(xì)的介紹。
二、 程序的設(shè)計(jì)實(shí)現(xiàn)
在開始進(jìn)行代碼編程前,首先以在工程中插入組件或控件的方式將Active X控件"Microsoft Communications Control,version 6.0"加入到工程中來,此時(shí)將會(huì)在工程中添加一個(gè)關(guān)于此控件的新類。使用該控件的一些方法和屬性時(shí)不能象使用類一樣簡單的聲明一個(gè)實(shí)例對(duì)象,而要通ClassWizard為該控件和一個(gè)成員變量建立起綁定關(guān)系,在此我們將該控件同變量m_Comm相綁定后就可以通過該控件提供的方法來對(duì)串口的各種通訊參數(shù)進(jìn)行設(shè)置了。為了編程方便起見,也可以在資源視圖中直接對(duì)該控件的屬性進(jìn)行設(shè)置,如無特別要求,對(duì)下表所列屬性進(jìn)行設(shè)置就基本可以滿足編程要求了。現(xiàn)將常用的屬性列表如下:
屬性 設(shè)定值 屬性說明
CommPort 1 串口號(hào),一般從1到4
InBufferSize 30720 接收緩沖區(qū)大小,為保持程序的穩(wěn)定,建議設(shè)得值足夠大
InputMode 0-Text 接收數(shù)據(jù)的類型,0表示文本類型,1表示二進(jìn)制類型
InputLen 0 從接收緩沖區(qū)讀取的字節(jié)數(shù),0表示全部讀取
OutBufferSize 512 發(fā)送緩沖區(qū)大小
Settings 4800,n,8,1 串口的參數(shù)設(shè)置,依次為波特率、奇偶校驗(yàn)(n-無校驗(yàn),e-偶校驗(yàn),o-奇校驗(yàn))、數(shù)據(jù)位數(shù)、停止位數(shù)
RThreshold 1 設(shè)定當(dāng)接收幾個(gè)字符時(shí)觸發(fā)OnComm事件,0表示不產(chǎn)生事件,
1表示每接收一個(gè)字符就產(chǎn)生一個(gè)事件
SThreshold 0 設(shè)定在觸發(fā)OnComm事件前,發(fā)送緩沖區(qū)內(nèi)所允許的最少的字符數(shù),
0表示發(fā)送數(shù)據(jù)時(shí)不產(chǎn)生事件,1表示當(dāng)發(fā)送緩沖區(qū)空時(shí)產(chǎn)生OnComm事件
我們要求能在程序啟動(dòng)的同時(shí)就打開串口以便即時(shí)對(duì)從串口到達(dá)的數(shù)據(jù)進(jìn)行接收、處理。一般來說可以將下面的打開端口的代碼寫在OnCreate()、OnInitialUpdate()、InitInstance ()等程序入口函數(shù)中:
……
if(!m_Comm.GetPortOpen()) //檢測是否已經(jīng)打開過端口
m_Comm.SetPortOpen(TRUE); //如沒有打開則將端口打開
……
接下來的工作就是對(duì)數(shù)據(jù)的發(fā)送與接收了,這也是本文所要介紹的重點(diǎn)所在。發(fā)送數(shù)據(jù)的代碼原則上是可以寫到一個(gè)成員函數(shù)中被直接調(diào)用的,但這并不是一個(gè)良好的編程習(xí)慣:我們應(yīng)當(dāng)把比較耗時(shí)的操作,如文件拷貝、打印、端口傳輸?shù)裙ぷ鞣诺揭粋€(gè)單獨(dú)的線程當(dāng)中,以避免其在工作時(shí)會(huì)引起整個(gè)進(jìn)程的阻塞,以提高整個(gè)系統(tǒng)對(duì)CPU的利用率。例如我們可以在視類中菜單或按鈕的響應(yīng)函數(shù)中用AfxBeginThread(WriteProc,this)函數(shù)來開啟一個(gè)名為"WriteProc"的線程,由于在線程中還需要使用視類的函數(shù)和變量,為了不產(chǎn)生新的視類的實(shí)例對(duì)象,我們通過該函數(shù)的第二個(gè)參數(shù)將指向當(dāng)前的視類的指針this作為參數(shù)傳遞給線程。在線程中可以用如下兩種方法之中的一種調(diào)用視類的成員函數(shù):
((COLECommView*) pParam)->DoSendProc();
或是:
COLECommView* view=(COLECommView*) pParam;
View->DoSendProc();
其中從pParam傳來的變量就是指向視類的指針。在線程中通過調(diào)用視類中的DoSendProc函數(shù)來完成對(duì)數(shù)據(jù)的發(fā)送,正是由于該函數(shù)是被全局的線程所調(diào)用的,我們就不可以使用取編輯框上的數(shù)據(jù)時(shí)通常所用的UpdateData()函數(shù)了,取而帶之的是API 函數(shù)GetDlgItemText(),取到輸入的數(shù)據(jù)后通過控件的SetOutput() 方法就把數(shù)據(jù)從串口發(fā)出去了,其中發(fā)送數(shù)據(jù)必須經(jīng)ColeVariant類將其轉(zhuǎn)換為通用的VARIANT型變量。實(shí)現(xiàn)
主要代碼如下:
……
char a[255];
HWND hwnd=GetSafeHwnd();
::GetDlgItemText(hwnd,IDC_ED99v1,a,255);
int i=0;
CString str;
while(a[i]!='{post.content}')
{
str.Format("%c",a[i]);
m_SendData+=str;
i++;
}
str.Format("%c",10);
m_SendData+=str;
m_Comm.SetOutput(COleVariant(m_SendData));
……
至于數(shù)據(jù)的接收,我們可以通過讓MS Comm控件響應(yīng)其OnComm事件來完成,通過ClassWizard加入其對(duì)事件的響應(yīng)后,通過下面的事件映射,當(dāng)有字符到達(dá)時(shí)便會(huì)通知 OnComm()函數(shù)去處理,從而實(shí)現(xiàn)數(shù)據(jù)的異步接收:
……
BEGIN_EVENTSINK_MAP(COLECommView, CFormView)
//{{AFX_EVENTSINK_MAP(COLECommView)
ON_EVENT(COLECommView, IDC_MSCOMM1, 1 /* OnComm */, OnComm, VTS_NONE)
//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()
……
void COLECommView::OnComm()
{
VARIANT Input;
if(m_Comm.GetCommEvent()==2)//接收緩沖區(qū)內(nèi)有字符
{
Input=m_Comm.GetInput();//讀取緩沖區(qū)內(nèi)的數(shù)據(jù)
CString msg=Input.bstrVal;
CString str;
str.Format("%c",10);
if(msg.Right(1)==str)
{
m_RecvData+=msg;
m_History.AddString(m_RecvData);
m_RecvData="";
}
else
m_RecvData+=msg;
}
}
當(dāng)數(shù)據(jù)被接收到接收緩沖區(qū)后,對(duì)于字符可以從VARIANT型結(jié)構(gòu)變量的bstrVal成員變量中獲取,VARIANT數(shù)據(jù)結(jié)構(gòu)相當(dāng)復(fù)雜,并牽扯到COM(Component Object Model,組件對(duì)象模型)中的一些概念,具體詳情請(qǐng)參閱Microsoft Corpration發(fā)布的Msdn
總結(jié)
以上是生活随笔為你收集整理的VC基于MSCOMM控件串口通讯的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windows进程间通信方式总结
- 下一篇: 用VC开发串口通信dll控件