一、简介
在工业生产控制系统中,有许多需要定时完成的操作,如数据采集程序。Win32提供了一个基于消息机制的定时器,使用SetTimer函数创建一个内存对象,设定间隔时间,当到达要求的间隔时,计时器对象发送一个WM_TIMER消息,由相应函数处理。但是由于WM_TIMER优先级低,只有等待消息队列中的其他消息都处理完毕后系统才会响应该消息。而且消息队列中的多个WM_TIMER会被合并,因此Win32定时器的精度低,不能满足工业实时控制系统的要求。
本文将介绍一种精度较高的多媒体定时器,该定时器并不依赖于消息机制,可以实现1ms的定时精度。由于多媒体定时器另外开辟一个独立线程执行定时器回调函数,因此当回调函数耗时较多时并不会导致UI的“假死”,相对而言,Win32定时器隶属于主线程,一旦定时器回调函数耗时较多,就会导致UI的“假死”。
多媒体定时器相关API如下:
MMRESULT timeGetDevCaps(
LPTIMECAPS ptc,
UINT cbtc
);
函数功能:获取定时器设备能力
参数:ptc指向一个TIMECAPS型的结构,TIMECAPS有两个成员,wPeriodMin和WperiodMax,表示定时器设备支持的最小时间周期和最大时间周期;cbtc表示TIMECAPS结构的大小
MMRESULT timeBeginPeriod(
UINT uPeriod
);
函数功能:设置定时器设备的最小时间分辨率
参数:最小时间分辨率,以毫秒为单位
MMRESULT timeEndPeriod(
UINT uPeriod
);
函数功能:清除之前对定时器设备的设置
参数:timeBeginPeriod中指定的最小分辨率
注:timeBeginPeriod和timeEndPeriod必须配对存在,并且指定的参数值也相同。
MMRESULT timeSetEvent(
UINT uDelay,
UINT uResolution,
LPTIMECALLBACK lpTimeProc,
DWORD_PTR dwUser,
UINT fuEvent
);
函数功能:创建并初始化定时器事件,给定定时器回调函数的入口地址
参数:
uDelay:定时器触发的时间间隔,以毫秒为单位
uResolution:定时器设备的时间精度,以毫秒为单位,应大于或等于timeBeginPeriod中设置的值,默认为1ms,精度越高,系统在定时器上的负载就越大,通常选择适合于应用程序的最大值
LpTimeProc:定时器触发的事件的回调函数的地址
dwUser:传递给回调函数的数据
fuEvent:定时类型,TIME_ONESHOT表示uDelay毫秒后只产生一次事件,TIME_PERIODIC表示每隔uDelay毫秒周期性的产生事件
MMRESULT timeKillEvent(
UINT uTimerID
);
函数功能:删除一个指定的定时器事件
参数:指向要删除的定时器事件的ID
void CALLBACK TimeProc(UINT uID,UINT uMsg,DWORD dwUsers,DWORD dw1,DWORD dw2);
函数功能:回调函数
参数:uID,多媒体定时器的ID,ID值由timeSetEvent创建定时器事件时返回
uMsg,保留,当前未使用
dwUser,由timeSetEvent传递的用户数据
dw1,dw2保留未使用
二、实现
本程序将多媒体定时器封装成一个类MMTimer,下面是核心代码:
#pragma once #include <mmsystem.h> #pragma comment(lib,"winmm.lib") typedef void (*TIMERCALLBACK)(DWORD); //函数的指针 class MMTimer { public: MMTimer(); ~MMTimer(); bool Start(UINT Delay,UINT Resolution,TIMERCALLBACK lpTimeProc,DWORD fParam); void Stop(); private: bool m_RunningFlag; UINT m_ID; UINT m_Delay; TIMERCALLBACK m_pfCallback; DWORD m_Fparam; static void CALLBACK TimeProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2); };