方韬,黄建国,戴志坚
(电子科技大学自动化工程学院 成都 611731)
示波表是在数字示波器技术的基础上发展起来的一种新型便携设备,覆盖了台式示波器的基本功能,可独立完成对信号的捕获、显示、计算相关参数等功能,作用场合主要是现场检测和维修使用。WinCE操作系统可扩展性好,有现成的USB和网口的驱动程序, 可以充分利用微软开发环境EVC和基础类库MFC已有的模块开发出友好的人机界面。基于WinCE平台的数字存储示波表,其强大的功能、良好的便携性保证了其具有广阔的市场前景。示波表选用了WinCE系统作为开发平台,EVC4.0作为开发工具。
WinCE是一个由微软公司自行开发的32位、多线程、多任务的嵌入式操作系统。WinCE继承了Windows95、98的图形界面。对于熟悉Windows操作的广大用户来说,WinCE易于学习和使用。WinCE具有结构化和模块化的特点,它提供的应用程序编程接口API(Application Programming Interface)函数与Windows XP下的API接口一致,是Windows XP下API的一个子集。在WinCE下开发的应用程序可以方便地移植到WindowsXP操作系统。
EVC是Windows CE下的软件开发工具,其编程支持WinCE下的API函数,支持微软基础类库MFC。这些类库提供的函数和PC平台下的函数具有相同的接口。示波表工程中采用EVC4.0作为开发工具。
Windows编程本质是对各种消息进行响应,对图形进行绘制也是如此。在程序代码中,一般将绘制图形的代码放在OnPaint或OnDraw函数中,当系统产生WM_PAINT消息时,上述函数会自动进行消息响应,从而实现图形的绘制。在图形很少改变或者程序窗口很少刷新时,这样的处理方法没有任何问题,但是如果程序窗体的内容经常刷新,图形就会出现闪烁。很多人认为出现这样的现象是图形刷新速度过快而造成的,实际上,在程序中,比如最小化最大化、移动窗体、覆盖等等都会引起图形的重绘。然而通过实验,我们可以发现,刷新速度并不是造成图形闪烁的最根本的原因。通过编写一个刷新速度很慢的应用程序可以发现,即使程序窗口的刷新速度很慢,但是在每次刷新的时候仍然存在闪烁的问题,只是闪烁没有快速刷新时那么明显。在本质上,造成图形闪烁的原因实际上是相邻两帧图像之间存在的巨大差异。而造成这一差异的原因又在于EVC本身的处理机制。在EVC中,窗体每次刷新时,将自动调用OnEraseBkgnd函数,该函数的作用是利用系统背景色填充窗体绘图区,在填充完了之后,系统才会重新调用绘图代码对窗口进行重绘。在默认情况下,系统背景色一般为白色,因此每次重绘时,相当于在相邻两帧图像间插入了一帧全白的图像。而白色一般与绘图颜色差别很大,因此,这样一擦一写造成了图象颜色的巨大反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。于是就看到了闪烁现象。要解决这一问题,首先直接能想到的方法就是禁用OnEraseBkgnd函数,避免系统对窗口进行白色填充。但是这样的处理方法又会带来新的问题,因为每次绘制图像的时候都没有将原来的图像清除,造成了图像的残留,于是窗体重绘时,如果没有重绘所有的区域,画面往往会变得混乱。所以单纯的禁止背景重绘是不够的,还必须重新对窗体的所有区域进行重绘。由于要对所有区域进行重绘,传统的方法可能就不够迅速。
双缓冲图形刷新技术顾名思义是采用双缓存实现的。传统的绘图方式实际上是一种单缓冲。在Windows中每一种设备都在内存中有一个设备描述表与其对应,这个设备描述表实际上就是一个内存缓冲区。传统的绘图中我们是将图形绘制在设备描述表缓冲区中,然后由GDI自动地将设备描述表中的图像拷贝到显存中进行显示。这样一个自动的拷贝过程屏蔽了传统的绘图方式是单缓冲的实质,使我们感觉到是在直接操纵显存一样。双缓冲图形刷新技术在内存中有两块缓存,除了设备描述表以外还有一块需要手动建立的与设备描述表缓冲区相兼容的后备缓冲区。绘图过程中,首先将图形绘制在后备缓冲区中,然后再手动地将后备缓冲区中的图像拷贝到前端缓冲区中,再由GDI自动将前端缓冲区中的图像拷贝到显存完成图形的显示过程。在实际中,我们使用BitBlt函数。它可以支持图形块的复制,速度很快。我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪烁。以上也就是双缓冲绘图的基本的思路。在EVC中,可以按照以下步骤来使用双缓冲技术进行图形的绘制:
(a)创建与窗口设备描述表(前端缓冲区)兼容的内存设备描述表(后端缓冲区)。
(b)创建与内存设备描述表相兼容的位图并将该位图选入内存设备描述表中。
(c)将图形绘制在内存设备描述表中。
(d)将内存设备描述表中的内容拷贝到窗口设备描述表。
(e)释放设备描述表句柄、位图等资源。
示波表作为一种便携式的示波器,需要实现示波器的全部功能,对波形的刷新率要求较高。基于WinCE的示波表软件,其画面的显示原理同Windows下的绘图。
示波表中,考虑到提高画图的效率,创建了两个内存兼容DC,将固定的背景(显示波形的网格以及显示测量结果的分隔线)专门放在一个命名m_bitmapBack的位图中,当需要重新绘制波形时,只需将该位图贴到画波形的位图m_bitmapWave上即可,这样大大节约了调用MoveTo、LineTo等API函数所消耗的时间。
在本工程中,波形刷新显示时的画图专门在画图线程中实现,双缓冲技术在工程中的应用如下:
(1) 在使能画图线程前先进行内存DC的初始化工作:
①创建兼容的内存DC。工程中创建了两个内存DC,m_dcBack用来保存固定的背景,m_dcWave用来绘制波形数据。
CClientDC clientDC(this);
m_dcBack.CreateCompatibleDC(&clientDC);
m_dcWave.CreateCompatibleDC(&clientDC);
② 创建与内存DC兼容的位图,并选入内存DC中。
GetClientRect(&rect);
m_rectBack.SetRect(rect.left,rect.top, rect.right, rect.bottom);
m_bitmapBack.CreateCompatibleBitmap(&clientDC,m_rectBack.Width(),m_rectBack.Height());
m_bitmapWave.CreateCompatibleBitmap(&clientDC,m_rectBack.Width(), m_rectBack.Height());
m_dcBack.SelectObject(&m_ bitmapBack);
m_dcWave.SelectObject(&m_ bitmapWave);
(2) 在画图线程中实现波形的刷新显示,流程图如图1所示。
图1 画图线程流程图
画图线程中,先等待线程事件,若事件为真(待采集到波形数据并转换为屏幕上的像素点坐标时,将画图线程事件置真),则进行波形的绘制。波形的绘制分为两个步骤:
① 将已经绘制好背景的背景DC贴到波形DC上,该部分实现每次刷新波形时都将上次的波形清除:
m_dcWave->BitBlt(22,32,500,400,
&m_ dcBack, 22, 32, SRCCOPY);
② 在画图DC上,根据得到的波形数据绘制波形。上层应用程序调用底层驱动获得波形数据,将数据转换成屏幕坐标后,将坐标画到画图DC上。
DrawWave(&m_ bitmapWave);
在DrawWave函数中使用PolyLine函数,将波形数据绘制到画图DC上。
③将绘制好波形的画图DC贴到设备DC上,实现波形的刷新。
在②中完成了画图DC的绘制,之后调用Invalidate()函数,给窗口发送WM_PAINT消息,在消息处理函数OnPaint中,将画图DC贴到设备DC上面。实现如下:
void COscCtrl::OnPaint()
{
EnterCriticalSection(&m_csBitmapWave);
CPaintDC dc(this);
dc.BitBlt(0,0,m_rectBack.Width(), m_rectBack.Height(), &m_dcWave, 0, 0, SRCCOPY);
LeaveCriticalSection(&m_csBitmapWave);
}
在软件中加入双缓冲技术绘图后,波形刷新时示波表界面不会出现混乱,也不会出现闪屏的现象,同时也提高了波形刷新的速率,很好地实现了示波表功能所要求的显示效果。示波表界面显示如图2所示。
图2 示波表界面显示
在WinCE系统中使用EVC进行界面编程可以方便地运用Windows编程技术,在实际应用中,为了实现对界面快速刷新的要求时,使用双缓冲技术可以很好地解决这一问题。在基于WinCE平台的示波表软件中,由于对波形的刷新率要求较高,使用双缓冲技术来实现波形的刷新,很好地达到了功能的要求。
[1]JeffreyRichter,Christophe Nasarre.Windows核心编程[M].葛子昂,译.北京:清华大学出版社,2008.
[2]侯捷.深入浅出MFC[M].武汉:华中科技大学出版社,2001.
[3]谭锋.在基于MFC的VC程序中缩放显示位图[J].内江科技,2007:32-36.
[4]周民扬.Visual C++ 界面编程技术[M].北京:北京希望电子出版社,2003.
[5]杨乐.测试仪器中的动态波形绘制[J].仪器仪表学报,2006:26-27.
[6]孙鑫,余安萍.VC++深入详解[M].北京:电子工业出版社,2006.
[7]夏萍,潘宏侠,王芳.基于VC的BMP位图伸缩显示[J].电子测试,2009(12):27-29.
[8]高伟卫,杨胜强,张满栋.基于VC++6.0基础类库的图像显示[J].现代电子技术,2002(1):36-40.