刘刚
(苏州市职业大学计算机工程学院,苏州215104)
随着工业物联网的发展,各种嵌入式产品也得到了广泛地应用。这些嵌入式产品将感知、监控、控制、通信等技术融入到了工业生产的各个环节,在提高制造效率,改善产品质量,降低生产成本等方面发挥了巨大的作用[1]。串口通信技术作为嵌入式产品开发中一项重要的通信技术,其通信性能的高低决定了嵌入式产品的性能好坏。特别是对实时性要求较高的应用尤为重要。传统的双串口通信机制在数据收发和处理时存在程序的负荷过重,响应不及时的缺点。为了解决这个问题,本文设计了一个新的高性能双串口通信模型,该模型的每个串口对应一个应用进程,负责和单个串口进行通信和数据处理。当两个串口之间需要数据交互时,两个应用程序可通过进程间通信实现。这样减轻了程序的处理负荷,降低了程序之间的耦合度,加快了程序的响应速度,提升了用户体验。
串行接口(简称串口)是计算机上通用的设备通信协议。它将接收来自CPU的并行数据转换为串行数据流发送出去,同时可将接收到的串行数据流转换为并行的数据字符供给CPU,来实现数据处理设备和外围设备之间的信息传输[2]。其通信示意图如图1所示。
图1串口通信示意图
由于串口通信是一种通用的,且使用非常广泛标准协议,因此在进行应用程序开发时有很多标准的串口通信组件或接口可使用。CnComm[3]是llbird开发的Windows/WinCE多线程串口通讯开源库,使用C++(ANSI/Unicode)开发,支持的平台包括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并发访问的支持,内存管理采用内存池技术。CnComm多线程串口类的类结构如图2所示。
图2 CnComm多线程串口类结构
CnComm是定义的多线程串口类,CnComm::Block⁃Buffer类是根据通讯特点开发的缓冲区类,单向链表内存块,提供一些扩展以支持和API挂接,CnComm::In⁃nerLock是自动锁类,用于函数内部,利用对象的生命周期完成锁定及解锁,CnComm::MfcException是一个异常处理类,用于MFC的异常处理,CnComm::BlockBuf⁃fer::Block是定义的缓冲区内存块,CnComm::BlockBuf⁃fer::InnerLock是定义的自动锁类,CnComm::BlockBuf⁃fer::Iterator是定义的缓冲区迭代器。
英达EM9000是一款面向工业自动化领域的高端嵌入式工控主板[4]。其内核CPU为200MHz的ARM920T,预装正版Window CE5.0实时多任务操作系统。EM9000带有一个10M/100M自适应快速以太网接口、4个异步串口以及2个HOST模式的USB接口、一个高速全双工SPI以及一个CAN总线接口。EM9000产品正视图如图3所示。
图3 EM9000工控板正视图
EM9000的4个输入输出插座,是按照一定的功能划来配置的。CN1主要系统的通讯接口(如以太网、异步串口、USB等)和矩阵键盘;CN2主要包括精简ISA扩展总线、GPIO以及+5V电源供电,数字音频和SPI与部分GPIO管脚复用;CN4主要是TFT LCD接口信号和触摸屏;而CN5则只包括标准IDE接口信号。EM9000的CN1、CN2和CN4所在位置示意图如图4所示。
图4 EM9000的CN1、CN2、CN4和CN5所在位置示意图
本文使用Visual Studio 2005创建应用程序,在创建应用程序之前,假设读者已搭建好了开发环境(包括WinCE SDK、EM9000SDK和Visual Studio 2005的安装与配置等),也已经下载了相应的CnComm类库文件。
首先,打开Visual Studio 2005,选择创建新项目。在项目创建向导中,选择智能设备->MFC智能设备应用程序,并为程序输入一个合适的项目名称。接下来在Platform SDK中,选择之前已经安装好的EM9000 SDK,应用程序类型选择基于对话框的类型。
首先,在新建的项目中,导入CnComm.h头文件。接着,在应用程序类中添加两个CnComm类对象m_com1和m_com2。之后,在应用程序的初始化实例中,就可以打开串口,代码如下:
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类的初始化函数中设置串口数据接收的窗口句柄,代码如下:
theApp.m_com1.SetWnd(this->m_hWnd);
theApp.m_com1.SetWnd(this->m_hWnd);
其次,在SerialTestDlg.h文件中,添加消息处理函数,代码如下:
afx_msg LRESULTOnComRecv(WPARAM,LPARAM)
接着,在SerialTestDlg.cpp文件中,添加消息映射,代码如下:
BEGIN_MESSAGE_MAP
ON_MESSAGE(ON_COM_RECEIVE,OnComRecv)
END_MESSAGE_MAP
最后,实现OnComRecv消息处理函数,代码如下:
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类进行串口通信应用开发非常方便,只需要定义两个串口对象,之后就可以调用CnComm类中封装好的函数,完成诸如串口的打开、数据的发送和接收等。上述的双串口通信中,数据的接收处理全部是在OnComRecv函数中完成,其通信模型如图5所示。
图5传统双串口通信模型
传统通信模型的优点是:若串口1和串口2之间存在信息交互,可直接调用对方串口对象的Write函数实现信息的发送。但问题是串口1和串口2的数据处理全部糅合在一个应用程序中完成,增加了应用程序数据处理的负荷,特别是对于一些复杂的、耗时的处理和实时性要求较高的应用,就会造成程序卡顿或假死现象。
为了解决上述问题,本文设计了一个新的高性能双串口通信模型,如图6所示。该模型每个串口对应一个应用程序,负责和单个串口进行通信和数据处理。当两个串口之间需要数据交互时,两个应用程序可通过进程间通信实现。
图6高性能双串口通信模型
单串口通信应用程序仅需要创建一个串口类对象,其余过程实现和第2节所述过程完全相同,这不再赘述。若串口1和串口2之间有信息交换,可通过两个单串口应用程序的进程间通信来完成,即通过向对方窗口发送WM_COPYDATA消息来实现。以串口1给串口2发送数据,串口2接收串口1的发送来的数据为例。
(1)串口1给串口2发送数据,通过FindWindow函数找到串口2应用程序的进程,将所要发送的数据封装到COPYDATASTRUCT的结构体中,之后向串口2的应用程序窗口发送WM_COPYDATA消息即可。具体代码如下:
CString sendContent=“要发送的数据”;
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发送来的数据
给应用程序2的对话框类添加WM_COPYDATA消息,此消息处理函数可以接收到应用程序1进程发送过来的数据,并在其中对接收来的数据进行处理。具体代码如下:
BOOL CApp2Dlg::OnCopyData(CWnd*pWnd,COPYDATA⁃STRUCT*pCopyDataStruct)
{
CString strReceivedText=(LPWSTR)(pCopyDataStruct->lpData);
strReceivedText=strReceivedText.Left(pCopyDataStruct->cbData);
}
本文所提出的通信模型,将每个串口数据的收发和处理独立开来,分别剥离到单一的应用程序中。这样就减轻了程序的处理负荷,降低了程序之间的耦合度,加快了程序的响应速度。避免应用程序出现卡顿或假死的现象,提升了用户体验。图7和图8是相同功能采用两种通信模型实现的应用程序在CPU处理负荷和响应速度上的表现。由图7和图8可知,本文所提模型在CPU负荷上相比传统模型平均降低了41%,在响应速度上平均加快近了4.5倍。
图7 CPU处理负荷对比
图8响应速度对比
串口通信是嵌入式应用开发中进行设备间数据交换的重要途径之一。在应用程序开始时,面对传统双串口通信模型的缺点,本文提出了一种高性能双串口通信模型,该模型相比传统通信模型平均降低CPU负荷41%,提升程序的响应速度约4.5倍。