c#微秒级定时器的实现,
在工业生产控制系统中,有许多操作需要定时完成,如定时显示当前时间,定时刷新屏幕上的进度条,定时从上位机向下位机发送命令和数据等。特别是在控制性能要求很高的控制系统和数据采集系统中,精确的定时操作更是必不可少。众所周知,Windows是一个基于消息机制的系统,任何事件的执行都是通过发送和接收消息来完成的。这就带来了一些问题,比如一旦计算机的CPU被某个进程占用,或者系统资源紧张,发送到消息队列的消息就被暂时挂起,无法实时处理。所以不能简单的通过Windows消息引发一个有严格计时要求的事件。另外,由于对计算机底层硬件的访问已经封装在Windows中,直接使用访问硬件很难完成精确计时。在实际应用中,应根据具体的计时精度要求,采用合适的计时方法。
这个例子实现了一微秒级的精确定时。程序的界面提供了两个“编辑”编辑框,其中一个输入用户的理想计时长度,另一个返回实际时间长度。经过大量的实验测试,误差一般小于5微秒。程序运行界面如图1所示:
图一。微秒级精密定时器的实现
一、实施方法
Visual C提供了许多关于时间操作的函数,通过这些函数,控制程序可以准确地完成计时和定时操作。Visaul C中的WM_TIMER消息映射可以进行简单的时间控制。首先调用函数SetTimer()设置计时间隔(退出程序时别忘了调用与SetTimer()配对的KillTimer()函数)。例如,settimer (0,200,NULL)是200ms的时间间隔。然后在应用程序中加入定时响应函数OnTimer(),在这个函数中加入响应处理语句,完成到达定时时间的操作。这种计时方法很简单,但是它的计时功能和Sleep()函数的delay函数一样,精度很低,只能用于实现位图的动态显示等对计时精度要求不高的场合。
微软在其多媒体窗口中为精确计时器提供底层API支持。多媒体定时器可以准确地读取系统的当前时间,并且可以在非常准确的时间间隔内完成一个事件、函数或过程的调用。利用多媒体定时器的基本功能,有两种方法可以实现精确定时。1)使用timeGetTime()函数,该函数具有毫秒级的计时精度,并返回从窗口开始所经过的时间。因为此函数用于通过查询来控制定时,所以应该建立一个定时周期来控制定时事件。2)使用timeSetEvent()函数,其原型如下:mmresult timesetevent (uintdelay,UINT uResolution,LPTIMECALLBACK lpTimeProc,DWORD dwUser,UINT fuevent);
该函数的参数描述如下:参数uDelay表示延迟时间;参数uResolution表示时间精度,Windows中默认值为1ms;LpTimeProc代表回调函数,是自定义函数,定期调用;参数dwUser表示用户提供的回调数据;参数fuEvent是定时器的事件类型,TIME_ONESHOT表示执行一次;TIME_PERIODIC:定期执行。在应用中,可以调用timeSetEvent()函数来定义lpTimeProc回调函数中需要定期执行的任务(如定时采样、控制等。),从而完成需要处理的事件。请注意,任务处理时间不能大于周期间隔时间。另外,定时器用完之后,要及时调用timeKillEvent()来释放。下面这段代码的主要作用是设置两个时钟定时器,一个间隔1ms,一个间隔2s。每次执行时,将当前系统时钟值输入到“cure.out”文件中,以比较计时器的准确性。
# define ONE_MILLI_SECOND 1 //以ms为单位定义1ms和2s时钟间隔;
#定义两秒2000
# define TIMER_ACCURACY 1 //定义时钟分辨率,以女士为单位
UINT wTimerRes_1ms,wTimerRes _ 2s//定义时间间隔
UINT wAccuracy//定义分辨率
UINT TimerID_1ms,TimerID _ 2s//定义定时器句柄
///////////////////////////////
CCU re prep:CCU re prep():fout( cure。out ,ios:out) //打开输出文件治愈。出去;
{
//给时间间隔变量赋值
wTimerRes _ 1 ms=ONE _ MILLI _ SECOND;
wTimerRes _ 2s=两秒钟
时间上限TC;
//利用函数timeGetDevCaps取出系统分辨率的取值范围,如果无错则继续;
if(timeGetDevCaps(&tc,sizeof(time caps))==TIMERR _ no error)
{
wAccuracy=最小值(最大值(TC。wperiodmin,//分辨率的值不能超出系统的取值范围
TIMER_ACCURACY)、TC。wperiodmax);
//调用时间开始周期函数设置定时器的分辨率
timeBeginPeriod(wAccuracy);
//设置定时器
初始化timer();
}
}
ccureprep:~ ccureprep()
{
富特结束时钟endl//结束时钟
timeKillEvent(time rid _ 1 ms);//删除两个定时器
time kill事件(time rid _ 2s);//删除设置的分辨率
timeEndPeriod(wAccuracy);
}
void ccureprep:初始化计时器()
{
startonemillisendtimer();
starttwossecondtimer();
}
//1毫秒定时器的回调函数,类似于中断处理程序,一定要声明为全局电脑语言函数,
//否则编译会有问题
void PASCAL onemilliseconproc(UINT wTimerID,UINT msg,DWORD dwUser,
双字dw1、双字dw2)
{
//定义计数器
静态int ms=0;
CCU re prep * app=(CCU re prep *)dw用户;
//取得系统时间,以女士为单位
DWORD osBinaryTime=GetTickCount();
//输出计数器值和当前系统时间
app-fout ++ ms :1毫秒:
}
//加装一毫秒定时器
void ccureprep:startonemillisendtimer()
{
if((time rid _ 1 ms=time setevent(wTimerRes _ 1 ms,wAccuracy,
(LPTIMECALBACK)OneMil liSecondProc,//回调函数;
(DWORD)这个,//用户传送到回调函数的数据;
TIME_PERIODIC))==0)//周期调用定时处理函数;
{
AfxMessageBox(不能进行定时!,MB _ OK MB _ icon星号);
}
其他
16毫秒计时: endl//不等于0表明加装成功,返回此定时器的句柄;
}
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。