随着自动控制技术的发展,以Windows为平台的实时控制系统得到广泛应用。在实时控制系统的控制过程中,采样时间的xx度是得以正常运行的关键。 对于以DOS为平台的控制系统,可以通过对硬件直接操作而得到xx的采样时间。而对于Windows平台,由于Windows9X是强占式多任务操作系统,系统接管全部硬件资源,用户无法直接同硬件打交道,时间控制离不开操作系统的支持。因此,在Windows9X平台下如何xx地控制采样频率是非常值得研究的。下面结合在造波机上控制系统研制中的具体方法加以探讨。 一、几种定时方法的比较 (1)普通定时器 普通定时器是简单的时间控制方法。首先由SetTimer()函数创建一个内存对象,设定间隔时间。此间隔时间被转换为IRQ 0中断请求的次数。当到达要求的次数时,计时器对象就发送一个WM_TIMER消息,由相应函数处理。由于系统定时器(8253定时器T/C0通道)的输入频率为1.1953180MHz,初始化为工作方式3,计数器初值为0({zd0}计数为65536),则其输出频率=1195318 / 65536≈18.2Hz,且直接连到IRQ 0。这样,CPU以18.2Hz的频率响应中断,周期为54.827ms。也就是说计数器的最短时间间隔约为55ms,转换成中断次数时还会产生舍入误差。另外,虽然中断是实时发生的,但由于WM_TIMER消息优先级不高,且易出现消息合并情况,即当消息队列中有一条WM_TIMER消息,而系统又向其中放入另一条WM_TIMER消息时,2条消息合并为1条消息。因此,普通定时器对消息处理在时间上和数目上存在不确定性,无法满足毫秒级控制要求。在工业实时控制系统中往往需要小于10ms的定时时钟,也就是说普通定时器在有较高要求的工业实时控制系统中不能满足要求。 (2)多媒体时钟 虽然多媒体时钟也是利用系统定时器工作的,但它的工作机理和普通定时器不同:①它可以通过函数TimeBeginPeriod()设置最小定时精度,即按精度要求设置8253的T/C0通道的计数初值,使计数器不在存在55ms的限制;②该顶时器并不倚赖消息机制,而是由函数TimeSetEvent()产生一个独立的线程,在一定的中断次数到达后,直接调用预先设置好的回调函数进行处理,而不必等应用程序的消息队列为空,从而保障了定时器得到实时响应。因此在基于Window9X平台的实时控制系统中,多媒体时钟是一种很理想的高精度定时器,它可以实现精度为1ms的高精度定时。 二、多媒体定时器的定制和使用 在造波机系统的实际研制中采用了多媒体定时器,其步骤如下: (1)利用函数TimeGetCaps()确定定时器服务所能提供的最小和{zd0}事件周期。这一周期对不同的计算机、不同的硬件配置以及Windows的不同运行方式是不一样的的。 (2)用TimeBeginPeriod()函数建立想要使用的最小计时精度,由于精度较高的定时器消耗的系统资源就越多,因此在具体应用中应根据需要综合考虑。 (3)用TimeSetEvent()函数初始化和启动定时器事件,此时应给出定时器事件的周期、使用精度及调用函数的入口地址。其函数原型如下: MMRESULT timeSetEvent(UINT uDelay,UINT uResolution,LPTIMECALLBACK lpTimeProc,DWORD dwUser,UINT fuEvent); 其中,uDelay表示时间间隔;uResolution表示定时精度,其值越小,精度越高,为0时表示能达到的{zg}精度;lpTimeProc为回调用函数的地址;dwUser为用户自定义的参数值;fuEvent为定时器类型,可取TIME_ONESHOT、TIME_PERIODIC、TIME_CALLBACK_FUNCTION、TIME_CALLBACK_EVENT_SET、TIME_CALLBACK_EVENT_PULSE。其中TIME_PERIODIC表示事件每隔uDelay毫秒发生1次,TIME_CALLBACK_FUNCTION表示事件发生时调用回调函数(而非设置事件),本系统采用这两个值。 (4)回调函数是一个中断服务程序,它必须驻留在动态链接库中,或由开发者自己编写。回调函数可以定义成相应类的成员函数,也可以为全局函数。但要注意:因为C++编译器为类成员函数准备了一个隐藏参数this,使得当回调函数定义成类的普通成员函数时,函数类型与Windows callback函数的预设类型不符。此时必须定义成静态类型,以xx隐含的this指针。但静态成员函数只能访问静态成员,所以实际操作起来并不方便。因此多数情况下,回调函数定义成全局函数比较合适,并将相关类的指针作为参数(timeSetEvent()函数中的dwUser参数)传递给回调函数。用该指针实现相应的操作,对于数据的处理和显示更为方便。 (5)使用TimeKillEvent(UINT uTimerID)删除定时器以释放系统资源。其中uTimerID为由timeSetEvent()函数返回的定时器标号,然后调用函数TimeEndPeriod()删除应用程序建立的最小定时器精度。 三、具体实现和动态显示 根据以上步骤,用Visual C++6.0作为开发工具,完成了具体的工作,其程序框图如图所示。 需要说明的是,若采用分割视图的界面,尽管可以在全局回调函数中得到相关视图和文档的指针,但当调用文档类的UpdateAllViews()函数时经常出错,即使编译通过,在实际运行中也会出现错误。因此采取了一种曲线的方法,就是向相关视图发自定义消息,在自定义消息中更新视图,使其实时地动态地显示采集回的数据,并画出实际曲线和理论曲线,二者比较给操作人员更直观的印象。 另外,如每采集一个数据后就更新相应的视图,屏幕会出现难以忍受的闪烁现象。因此先在内存中建立一个与屏幕设备环境相对应的兼容性设备环境,所有的绘图及清屏操作读在兼容设备环境中完成;{zh1}再将画好的曲线图从兼容设备环境传送到屏幕设备环境上显示,从而避免了闪烁。 主要程序实现代码如下: //启动多媒体定时器 void CControlView::OnStart( ) {
} //终止多媒体定时器 void CControlView::OnStop( ) {
} //采集数据,处理数据,向视图发自定义消息 void CALLBACK TimeProc (UINT uID,UINT uMsg,DWORD dwUser,DWORD dw1, DWORD dw2) {
} //更新视图 LRESULT CControlView::OnMyMessage(WPARAM wParam,LPARAM lpParam) {
} //在CCurveView中动态绘制曲线 void CCurveView::OnUpdate(CView* pSender,LPARAM lHint,Cobject* pHint) {
} 通过测试和实际应用,本文所探讨的高精度多媒体定时器和实时显示方法工作良好,能满足一般定时控制系统的要求,值得推广。 附:向工程中添加多媒体定时器支持的方法 通过Components and Controls插入Windows Multimedia Library,这样就把多媒体计时器所需的头文件mmsystem.h和库winmm.lib自动插入工程的stdafx.h中。 #include <MMSystem.h> // CG: The following line was added by the Windows Multimedia component. #pragma comment(lib, "winmm.lib") |