在程序中如果要用到多個串口,而且還要做很多復雜的處理,那么最好不用MSComm通訊控件,如果這時你還不願意自己編寫底層,就用這個類:CserialPort類。
作者是 Remon Spekreijse ,可在 http://www.codeguru.com/cpp/i-n/network/serialcommunications/article.php/c2483/A-communication-class-for-serial-port.htm找到作者的基於對話框的可以同時檢測4個串口的通信例子.
本文介紹基於文檔的程序中的用法:(實例為計算機上兩個串口之間的發送與接收)
編程步驟:
◆1. 建立程序:
建立一個基於單文檔的MFC應用程序SCPortTest,所有步驟保持缺省狀態。
作者是 Remon Spekreijse ,可在 http://www.codeguru.com/cpp/i-n/network/serialcommunications/article.php/c2483/A-communication-class-for-serial-port.htm找到作者的基於對話框的可以同時檢測4個串口的通信例子.
本文介紹基於文檔的程序中的用法:(實例為計算機上兩個串口之間的發送與接收)
編程步驟:
◆1. 建立程序:
建立一個基於單文檔的MFC應用程序SCPortTest,所有步驟保持缺省狀態。
◆2. 添加類文件:
將SerialPort.h SerialPort.cpp兩個類文件復制到工程文件夾中,並加入工程。並在視圖類的頭文件中包含:#include "SerialPort.h"。
將SerialPort.h SerialPort.cpp兩個類文件復制到工程文件夾中,並加入工程。並在視圖類的頭文件中包含:#include "SerialPort.h"。
◆3. 人工增加串口消息響應函數:OnCommunication(WPARAM ch, LPARAM port)
首先在視圖類頭文件中添加串口字符接收消息WM_COMM_RXCHAR(串口接收緩沖區內有一個字符)的響應函數聲明:
//{{AFX_MSG(CSCPortTestView)
afx_msg LONG OnCommunication(WPARAM ch, LPARAM port);
//}}AFX_MSG
首先在視圖類頭文件中添加串口字符接收消息WM_COMM_RXCHAR(串口接收緩沖區內有一個字符)的響應函數聲明:
//{{AFX_MSG(CSCPortTestView)
afx_msg LONG OnCommunication(WPARAM ch, LPARAM port);
//}}AFX_MSG
然后在視圖類的cpp文件中進行WM_COMM_RXCHAR消息映射:
BEGIN_MESSAGE_MAP(CSCPortTestView, CView)
//{{AFX_MSG_MAP(CSCPortTestView)
ON_MESSAGE(WM_COMM_RXCHAR, OnCommunication)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CSCPortTestView, CView)
//{{AFX_MSG_MAP(CSCPortTestView)
ON_MESSAGE(WM_COMM_RXCHAR, OnCommunication)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
並且在本文件中加入函數的實現:
LONG CPortTestView::OnCommunication(WPARAM ch, LPARAM port)
{ ….. }
LONG CPortTestView::OnCommunication(WPARAM ch, LPARAM port)
{ ….. }
注意:由於這個串口類加入工程后,沒有自動的消息映射機制,因此上述步驟均需要手工添加。
◆4 初始化串口
在視創建時初始化串口,首先利用ClassWizardr生成OnInitialUpdate()函數。
接着在SerialPort.h文件中說明我們在程序中要用到的全局變量:
保存兩個串口接收數據:
char m_chChecksum; //用於COM1的校驗和計算
CString m_strRXhhCOM1; //用於存放COM1接收的半BYTE校驗字節hh
CString m_strRXDataCOM1; //COM1接收數據
CString m_strRXDataCOM2; //COM2接收數據
UINT m_nRXErrorCOM1; //COM1接收數據錯誤幀數
UINT m_nRXErrorCOM2; //COM2接收數據錯誤幀數
UINT m_nRXCounterCOM1; //COM1接收數據錯誤幀數
UINT m_nRXCounterCOM2; //COM2接收數據錯誤幀數CString
在視創建時初始化串口,首先利用ClassWizardr生成OnInitialUpdate()函數。
接着在SerialPort.h文件中說明我們在程序中要用到的全局變量:
保存兩個串口接收數據:
char m_chChecksum; //用於COM1的校驗和計算
CString m_strRXhhCOM1; //用於存放COM1接收的半BYTE校驗字節hh
CString m_strRXDataCOM1; //COM1接收數據
CString m_strRXDataCOM2; //COM2接收數據
UINT m_nRXErrorCOM1; //COM1接收數據錯誤幀數
UINT m_nRXErrorCOM2; //COM2接收數據錯誤幀數
UINT m_nRXCounterCOM1; //COM1接收數據錯誤幀數
UINT m_nRXCounterCOM2; //COM2接收數據錯誤幀數CString
再在SerialPort.h文件中說明串口類對象:CSerailPort m_ComPort[2]; (public)。
因為要初始化2個串口,所以這里用了數組。
下面是初始化串口1和串口2:
因為要初始化2個串口,所以這里用了數組。
下面是初始化串口1和串口2:
void CSCPortTestView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// TODO: Add your specialized code here and/or call the base class
m_chChecksum=0; //校驗和置0
m_nRXErrorCOM1=0; //COM1接收數據錯誤幀數置0
m_nRXErrorCOM2=0; //COM2接收數據錯誤幀數置0
m_nRXCounterCOM1=0; //COM1接收數據錯誤幀數置0
m_nRXCounterCOM2=0; //COM2接收數據錯誤幀數置0
m_strRXhhCOM1.Empty(); //清空半BYTE校驗hh存儲變量
for(int i=0;i<2;i++)
{
if (m_ComPort.InitPort(this,i+1,9600,'N',8,1,EV_RXFLAG | EV_RXCHAR,512))
//portnr=1(2),baud=960,parity='N',databits=8,stopsbits=1,
//dwCommEvents=EV_RXCHAR|EV_RXFLAG,nBufferSize=512
{
m_ComPort.StartMonitoring(); //啟動串口監視線程
if(i==1) SetTimer(1,1000,NULL); //設置定時器,1秒后發送數據
}
else
{
CString str;
str.Format("COM%d 沒有發現,或被其它設備占用",i+1);
AfxMessageBox(str);
}
}
}
◆5 利用ClassWizard生成CPortTestView的時間消息WM_TIMER響應函數:
void CSCPortTestView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
int randdata=rand()�00; //產生9000以內的隨機數
CString strSendData;
strSendData.Format("d",randdata);
SendString(strSendData, 2); //串口2發送數據;
CView::OnTimer(nIDEvent);
}
{
// TODO: Add your message handler code here and/or call default
int randdata=rand()�00; //產生9000以內的隨機數
CString strSendData;
strSendData.Format("d",randdata);
SendString(strSendData, 2); //串口2發送數據;
CView::OnTimer(nIDEvent);
}
上面用到的SendString()需按如下方式生成:
在ClassView中單擊鼠標右鍵,在環境菜單中選擇Add Member Function:
在ClassView中單擊鼠標右鍵,在環境菜單中選擇Add Member Function:
void CSCPortTestView::SendString(CString &str, int Port)
{
char checksum=0,cr=CR,lf=LF;
char c1,c2;
for(int i=0;i<str.GetLength();i++)
checksum = checksum^str;
c2=checksum & 0x0f; c1=((checksum >> 4) & 0x0f);
if (c1 < 10)
c1+= '0';
else c1 += 'A' - 10;
if (c2 < 10)
c2+= '0';
else c2 += 'A' - 10;
CString str1;
str1='$'+str+"*"+c1+c2+cr+lf;
m_ComPort[Port-1].WriteToPort((LPCTSTR)str1);
}
{
char checksum=0,cr=CR,lf=LF;
char c1,c2;
for(int i=0;i<str.GetLength();i++)
checksum = checksum^str;
c2=checksum & 0x0f; c1=((checksum >> 4) & 0x0f);
if (c1 < 10)
c1+= '0';
else c1 += 'A' - 10;
if (c2 < 10)
c2+= '0';
else c2 += 'A' - 10;
CString str1;
str1='$'+str+"*"+c1+c2+cr+lf;
m_ComPort[Port-1].WriteToPort((LPCTSTR)str1);
}
請注意上面函數中是如何生成校驗碼的,要切記的是發送的校驗碼生成方式和對方接收的校驗檢測方式要一致。
◆6 在OnCommunication(WPARAM ch, LPARAM port)函數中進行數據處理
說明:WPARAM、 LPARAM 類型是多態數據類型(polymorphic data type),在WIN32中為32位,支持多種數據類型,根據需要自動適應,這樣程序有很強的適應性。在此我們可以分別理解為char和 integer 類型數據。
每當串口接收緩沖區內有一個字符時,就會產生一個WM_COMM_RXCHAR消息,觸發OnCommunication函數,這時我們就可以在函數中進行數據處理,所以這個消息就是整個程序的"發動機"。
下面是根據本文最初提出的問題寫出的處理函數:
說明:WPARAM、 LPARAM 類型是多態數據類型(polymorphic data type),在WIN32中為32位,支持多種數據類型,根據需要自動適應,這樣程序有很強的適應性。在此我們可以分別理解為char和 integer 類型數據。
每當串口接收緩沖區內有一個字符時,就會產生一個WM_COMM_RXCHAR消息,觸發OnCommunication函數,這時我們就可以在函數中進行數據處理,所以這個消息就是整個程序的"發動機"。
下面是根據本文最初提出的問題寫出的處理函數:
LONG CSCPortTestView::OnCommunication(WPARAM ch, LPARAM port)
{
static int count1=0,count2=0,count3=0;
static char c1,c2;
static int flag;
CString strCheck="";
{
static int count1=0,count2=0,count3=0;
static char c1,c2;
static int flag;
CString strCheck="";
if(port==2) //COM2接收到數據
{
CString strtemp=(char)ch;
if(strtemp=="Y")
{
m_nRXCounterCOM2++;
CString strtemp;
strtemp.Format("COM2: NO.d", m_nRXCounterCOM2);
CDC* pDC=GetDC(); //准備數據顯示
pDC->TextOut(10,50,strtemp);//顯示接收到的數據
ReleaseDC(pDC);
}
}
{
CString strtemp=(char)ch;
if(strtemp=="Y")
{
m_nRXCounterCOM2++;
CString strtemp;
strtemp.Format("COM2: NO.d", m_nRXCounterCOM2);
CDC* pDC=GetDC(); //准備數據顯示
pDC->TextOut(10,50,strtemp);//顯示接收到的數據
ReleaseDC(pDC);
}
}
if(port==1) //COM1接收到數據
{
m_strRXDataCOM1 += (char)ch;
switch(ch)
{
case '$':
m_chChecksum=0; //開始計算CheckSum
flag=0;
break;
case '*':
flag=2;
c2=m_chChecksum & 0x0f; c1=((m_chChecksum >> 4) & 0x0f);
if (c1 < 10) c1+= '0'; else c1 += 'A' - 10;
if (c2 < 10) c2+= '0'; else c2 += 'A' - 10;
break;
case CR:
break;
case LF:
m_strRXDataCOM1.Empty();
break;
default:
if(flag>0)
{
m_strRXhhCOM1 += ch; //得到收到的校驗值hh
if(flag==1)
{
strCheck = strCheck+c1+c2; //計算得到的校驗值hh
if(strCheck!=m_strRXhhCOM1) //如果校驗有錯
{
m_strRXDataCOM1.Empty();
m_nRXErrorCOM1++; //串口1錯誤幀數加1
}
else
{
m_nRXCounterCOM1++;
if(m_strRXDataCOM1.Left(1)=="$") //接收數據的第一個字符是$嗎?
{
char tbuf[6];
char *temp=(char*)((LPCTSTR)m_strRXDataCOM1);
tbuf[0]=temp[1]; tbuf[1]=temp[2];
tbuf[2]=temp[3]; tbuf[3]=temp[4];
tbuf[4]=0; //0表示字符串的結束,必要
int data=atoi(tbuf);
CString strDisplay1,strDisplay2;
strDisplay1.Format("NO. d: The reseived data is d",
m_nRXCounterCOM1,data);
strDisplay2.Format("Error Counter=d.",m_nRXErrorCOM1);
CDC* pDC=GetDC(); //准備數據顯示
//int nColor=pDC->SetTextColor(RGB(255,255,0));
pDC->TextOut(10,10,strDisplay1);//顯示接收到的數據
pDC->TextOut(30,30,strDisplay2);//顯示錯誤幀數
//pDC->SetTextColor(nColor);
ReleaseDC(pDC);
}
CString str1="Y";
m_ComPort[0].WriteToPort((LPCTSTR)str1);//發送應答信號Y
}
m_strRXhhCOM1.Empty();
}
flag--;
}
else
m_chChecksum ^= ch;
break;
}
}
return 0;
}
{
m_strRXDataCOM1 += (char)ch;
switch(ch)
{
case '$':
m_chChecksum=0; //開始計算CheckSum
flag=0;
break;
case '*':
flag=2;
c2=m_chChecksum & 0x0f; c1=((m_chChecksum >> 4) & 0x0f);
if (c1 < 10) c1+= '0'; else c1 += 'A' - 10;
if (c2 < 10) c2+= '0'; else c2 += 'A' - 10;
break;
case CR:
break;
case LF:
m_strRXDataCOM1.Empty();
break;
default:
if(flag>0)
{
m_strRXhhCOM1 += ch; //得到收到的校驗值hh
if(flag==1)
{
strCheck = strCheck+c1+c2; //計算得到的校驗值hh
if(strCheck!=m_strRXhhCOM1) //如果校驗有錯
{
m_strRXDataCOM1.Empty();
m_nRXErrorCOM1++; //串口1錯誤幀數加1
}
else
{
m_nRXCounterCOM1++;
if(m_strRXDataCOM1.Left(1)=="$") //接收數據的第一個字符是$嗎?
{
char tbuf[6];
char *temp=(char*)((LPCTSTR)m_strRXDataCOM1);
tbuf[0]=temp[1]; tbuf[1]=temp[2];
tbuf[2]=temp[3]; tbuf[3]=temp[4];
tbuf[4]=0; //0表示字符串的結束,必要
int data=atoi(tbuf);
CString strDisplay1,strDisplay2;
strDisplay1.Format("NO. d: The reseived data is d",
m_nRXCounterCOM1,data);
strDisplay2.Format("Error Counter=d.",m_nRXErrorCOM1);
CDC* pDC=GetDC(); //准備數據顯示
//int nColor=pDC->SetTextColor(RGB(255,255,0));
pDC->TextOut(10,10,strDisplay1);//顯示接收到的數據
pDC->TextOut(30,30,strDisplay2);//顯示錯誤幀數
//pDC->SetTextColor(nColor);
ReleaseDC(pDC);
}
CString str1="Y";
m_ComPort[0].WriteToPort((LPCTSTR)str1);//發送應答信號Y
}
m_strRXhhCOM1.Empty();
}
flag--;
}
else
m_chChecksum ^= ch;
break;
}
}
return 0;
}