劉剛
(蘇州市職業(yè)大學(xué)計(jì)算機(jī)工程學(xué)院,蘇州215104)
隨著工業(yè)物聯(lián)網(wǎng)的發(fā)展,各種嵌入式產(chǎn)品也得到了廣泛地應(yīng)用。這些嵌入式產(chǎn)品將感知、監(jiān)控、控制、通信等技術(shù)融入到了工業(yè)生產(chǎn)的各個(gè)環(huán)節(jié),在提高制造效率,改善產(chǎn)品質(zhì)量,降低生產(chǎn)成本等方面發(fā)揮了巨大的作用[1]。串口通信技術(shù)作為嵌入式產(chǎn)品開發(fā)中一項(xiàng)重要的通信技術(shù),其通信性能的高低決定了嵌入式產(chǎn)品的性能好壞。特別是對實(shí)時(shí)性要求較高的應(yīng)用尤為重要。傳統(tǒng)的雙串口通信機(jī)制在數(shù)據(jù)收發(fā)和處理時(shí)存在程序的負(fù)荷過重,響應(yīng)不及時(shí)的缺點(diǎn)。為了解決這個(gè)問題,本文設(shè)計(jì)了一個(gè)新的高性能雙串口通信模型,該模型的每個(gè)串口對應(yīng)一個(gè)應(yīng)用進(jìn)程,負(fù)責(zé)和單個(gè)串口進(jìn)行通信和數(shù)據(jù)處理。當(dāng)兩個(gè)串口之間需要數(shù)據(jù)交互時(shí),兩個(gè)應(yīng)用程序可通過進(jìn)程間通信實(shí)現(xiàn)。這樣減輕了程序的處理負(fù)荷,降低了程序之間的耦合度,加快了程序的響應(yīng)速度,提升了用戶體驗(yàn)。
串行接口(簡稱串口)是計(jì)算機(jī)上通用的設(shè)備通信協(xié)議。它將接收來自CPU的并行數(shù)據(jù)轉(zhuǎn)換為串行數(shù)據(jù)流發(fā)送出去,同時(shí)可將接收到的串行數(shù)據(jù)流轉(zhuǎn)換為并行的數(shù)據(jù)字符供給CPU,來實(shí)現(xiàn)數(shù)據(jù)處理設(shè)備和外圍設(shè)備之間的信息傳輸[2]。其通信示意圖如圖1所示。
圖1串口通信示意圖
由于串口通信是一種通用的,且使用非常廣泛標(biāo)準(zhǔn)協(xié)議,因此在進(jìn)行應(yīng)用程序開發(fā)時(shí)有很多標(biāo)準(zhǔn)的串口通信組件或接口可使用。CnComm[3]是llbird開發(fā)的Windows/WinCE多線程串口通訊開源庫,使用C++(ANSI/Unicode)開發(fā),支持的平臺包括Windows
(Win98/NT/2000/XP/2003/Vista)、WinCE 5.0/6.0、Pock?et PC 2003,在BC++5(free tool)、C++Builder 4,5,6,X、EVC 4(sp4)、G++3,4、Intel C++7,8,9、VC++6(sp6)、.NET 2003,2005等,提供同步I/O并發(fā)訪問的支持,內(nèi)存管理采用內(nèi)存池技術(shù)。CnComm多線程串口類的類結(jié)構(gòu)如圖2所示。
圖2 CnComm多線程串口類結(jié)構(gòu)
CnComm是定義的多線程串口類,CnComm::Block?Buffer類是根據(jù)通訊特點(diǎn)開發(fā)的緩沖區(qū)類,單向鏈表內(nèi)存塊,提供一些擴(kuò)展以支持和API掛接,CnComm::In?nerLock是自動鎖類,用于函數(shù)內(nèi)部,利用對象的生命周期完成鎖定及解鎖,CnComm::MfcException是一個(gè)異常處理類,用于MFC的異常處理,CnComm::BlockBuf?fer::Block是定義的緩沖區(qū)內(nèi)存塊,CnComm::BlockBuf?fer::InnerLock是定義的自動鎖類,CnComm::BlockBuf?fer::Iterator是定義的緩沖區(qū)迭代器。
英達(dá)EM9000是一款面向工業(yè)自動化領(lǐng)域的高端嵌入式工控主板[4]。其內(nèi)核CPU為200MHz的ARM920T,預(yù)裝正版Window CE5.0實(shí)時(shí)多任務(wù)操作系統(tǒng)。EM9000帶有一個(gè)10M/100M自適應(yīng)快速以太網(wǎng)接口、4個(gè)異步串口以及2個(gè)HOST模式的USB接口、一個(gè)高速全雙工SPI以及一個(gè)CAN總線接口。EM9000產(chǎn)品正視圖如圖3所示。
圖3 EM9000工控板正視圖
EM9000的4個(gè)輸入輸出插座,是按照一定的功能劃來配置的。CN1主要系統(tǒng)的通訊接口(如以太網(wǎng)、異步串口、USB等)和矩陣鍵盤;CN2主要包括精簡ISA擴(kuò)展總線、GPIO以及+5V電源供電,數(shù)字音頻和SPI與部分GPIO管腳復(fù)用;CN4主要是TFT LCD接口信號和觸摸屏;而CN5則只包括標(biāo)準(zhǔn)IDE接口信號。EM9000的CN1、CN2和CN4所在位置示意圖如圖4所示。
圖4 EM9000的CN1、CN2、CN4和CN5所在位置示意圖
本文使用Visual Studio 2005創(chuàng)建應(yīng)用程序,在創(chuàng)建應(yīng)用程序之前,假設(shè)讀者已搭建好了開發(fā)環(huán)境(包括WinCE SDK、EM9000SDK和Visual Studio 2005的安裝與配置等),也已經(jīng)下載了相應(yīng)的CnComm類庫文件。
首先,打開Visual Studio 2005,選擇創(chuàng)建新項(xiàng)目。在項(xiàng)目創(chuàng)建向?qū)е?,選擇智能設(shè)備->MFC智能設(shè)備應(yīng)用程序,并為程序輸入一個(gè)合適的項(xiàng)目名稱。接下來在Platform SDK中,選擇之前已經(jīng)安裝好的EM9000 SDK,應(yīng)用程序類型選擇基于對話框的類型。
首先,在新建的項(xiàng)目中,導(dǎo)入CnComm.h頭文件。接著,在應(yīng)用程序類中添加兩個(gè)CnComm類對象m_com1和m_com2。之后,在應(yīng)用程序的初始化實(shí)例中,就可以打開串口,代碼如下:
BOOL CSerialTest::InitInstance()
{
if(!m_com1.IsOpen())
{
if(!m_com1.Open(1,19200)){
return false
}
}
if(!m_com2.IsOpen())
{
if(!m_com2.Open(2,19200))
{
return false
}
}
}
首先,在CSerialTestDlg類的初始化函數(shù)中設(shè)置串口數(shù)據(jù)接收的窗口句柄,代碼如下:
theApp.m_com1.SetWnd(this->m_hWnd);
theApp.m_com1.SetWnd(this->m_hWnd);
其次,在SerialTestDlg.h文件中,添加消息處理函數(shù),代碼如下:
afx_msg LRESULTOnComRecv(WPARAM,LPARAM)
接著,在SerialTestDlg.cpp文件中,添加消息映射,代碼如下:
BEGIN_MESSAGE_MAP
ON_MESSAGE(ON_COM_RECEIVE,OnComRecv)
END_MESSAGE_MAP
最后,實(shí)現(xiàn)OnComRecv消息處理函數(shù),代碼如下:
LRESULT CSerialTestDlg::OnComRecv(WPARAM wParam,
LPARAMlParam)
{
int portNum=(int)wParam;
if(1==portNum)
{
BYTEbuf[256];
int len=theApp.m_com1.Read(buf,256);
buf[len]=0;
BYTEdata[]={0x01};
theApp.m_com2.Write((LPVOID)data,sizeof(data)/sizeof
(BYTE));
}
elseif(2==portNum)
{
BYTEbuf[256];
int len=theApp.m_com1.Read(buf,256);
buf[len]=0;
BYTEdata[]={0x02};
theApp.m_com1.Write((LPVOID)data,sizeof(data)/sizeof
(BYTE))
}
}
從上述代碼中可以看出,利用CnComm類進(jìn)行串口通信應(yīng)用開發(fā)非常方便,只需要定義兩個(gè)串口對象,之后就可以調(diào)用CnComm類中封裝好的函數(shù),完成諸如串口的打開、數(shù)據(jù)的發(fā)送和接收等。上述的雙串口通信中,數(shù)據(jù)的接收處理全部是在OnComRecv函數(shù)中完成,其通信模型如圖5所示。
圖5傳統(tǒng)雙串口通信模型
傳統(tǒng)通信模型的優(yōu)點(diǎn)是:若串口1和串口2之間存在信息交互,可直接調(diào)用對方串口對象的Write函數(shù)實(shí)現(xiàn)信息的發(fā)送。但問題是串口1和串口2的數(shù)據(jù)處理全部糅合在一個(gè)應(yīng)用程序中完成,增加了應(yīng)用程序數(shù)據(jù)處理的負(fù)荷,特別是對于一些復(fù)雜的、耗時(shí)的處理和實(shí)時(shí)性要求較高的應(yīng)用,就會造成程序卡頓或假死現(xiàn)象。
為了解決上述問題,本文設(shè)計(jì)了一個(gè)新的高性能雙串口通信模型,如圖6所示。該模型每個(gè)串口對應(yīng)一個(gè)應(yīng)用程序,負(fù)責(zé)和單個(gè)串口進(jìn)行通信和數(shù)據(jù)處理。當(dāng)兩個(gè)串口之間需要數(shù)據(jù)交互時(shí),兩個(gè)應(yīng)用程序可通過進(jìn)程間通信實(shí)現(xiàn)。
圖6高性能雙串口通信模型
單串口通信應(yīng)用程序僅需要創(chuàng)建一個(gè)串口類對象,其余過程實(shí)現(xiàn)和第2節(jié)所述過程完全相同,這不再贅述。若串口1和串口2之間有信息交換,可通過兩個(gè)單串口應(yīng)用程序的進(jìn)程間通信來完成,即通過向?qū)Ψ酱翱诎l(fā)送WM_COPYDATA消息來實(shí)現(xiàn)。以串口1給串口2發(fā)送數(shù)據(jù),串口2接收串口1的發(fā)送來的數(shù)據(jù)為例。
(1)串口1給串口2發(fā)送數(shù)據(jù),通過FindWindow函數(shù)找到串口2應(yīng)用程序的進(jìn)程,將所要發(fā)送的數(shù)據(jù)封裝到COPYDATASTRUCT的結(jié)構(gòu)體中,之后向串口2的應(yīng)用程序窗口發(fā)送WM_COPYDATA消息即可。具體代碼如下:
CString sendContent=“要發(fā)送的數(shù)據(jù)”;
LRESULTcopyDataResult;
CWnd*pOtherWnd=CWnd::FindWindow(NULL,“App2”);
if(pOtherWnd)
{
COPYDATASTRUCTcpd;
cpd.dwData=1;
cpd.cbData=2*sendContent.GetLength()+1;
cpd.lpData=(void*)sendContent.GetBuffer(cpd.cbData);
copyDataResult=pOtherWnd->SendMessage(WM_COPY?
DATA,this->m_hWnd,(LPARAM)&cpd);
}
(2)串口2接收串口1發(fā)送來的數(shù)據(jù)
給應(yīng)用程序2的對話框類添加WM_COPYDATA消息,此消息處理函數(shù)可以接收到應(yīng)用程序1進(jìn)程發(fā)送過來的數(shù)據(jù),并在其中對接收來的數(shù)據(jù)進(jìn)行處理。具體代碼如下:
BOOL CApp2Dlg::OnCopyData(CWnd*pWnd,COPYDATA?STRUCT*pCopyDataStruct)
{
CString strReceivedText=(LPWSTR)(pCopyDataStruct->lpData);
strReceivedText=strReceivedText.Left(pCopyDataStruct->cbData);
}
本文所提出的通信模型,將每個(gè)串口數(shù)據(jù)的收發(fā)和處理獨(dú)立開來,分別剝離到單一的應(yīng)用程序中。這樣就減輕了程序的處理負(fù)荷,降低了程序之間的耦合度,加快了程序的響應(yīng)速度。避免應(yīng)用程序出現(xiàn)卡頓或假死的現(xiàn)象,提升了用戶體驗(yàn)。圖7和圖8是相同功能采用兩種通信模型實(shí)現(xiàn)的應(yīng)用程序在CPU處理負(fù)荷和響應(yīng)速度上的表現(xiàn)。由圖7和圖8可知,本文所提模型在CPU負(fù)荷上相比傳統(tǒng)模型平均降低了41%,在響應(yīng)速度上平均加快近了4.5倍。
圖7 CPU處理負(fù)荷對比
圖8響應(yīng)速度對比
串口通信是嵌入式應(yīng)用開發(fā)中進(jìn)行設(shè)備間數(shù)據(jù)交換的重要途徑之一。在應(yīng)用程序開始時(shí),面對傳統(tǒng)雙串口通信模型的缺點(diǎn),本文提出了一種高性能雙串口通信模型,該模型相比傳統(tǒng)通信模型平均降低CPU負(fù)荷41%,提升程序的響應(yīng)速度約4.5倍。