Windows精准计时&精准Timer

以前做上层程序的时候,需要计时的时候就GetTickCount()获取当前ms来做个减法,如果有Timer需求,那肯定直接SetTimer,OnTimer,KillTimer三件套。最近遇到了一个要求发送频率50Hz,直接SetTimer将间隔设置为20,结果发现数据间隔差的离谱。查了一下,GetTickCount和Timer都是基于Windows消息循环,最小单元为55ms左右。如果想实现更高的精确度:

精确获取时间

QueryPerformanceFrequency() - 基本介绍

类型:Win32API

原型:BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);

作用:返回硬件支持的高精度计数器的频率。

返回值:非零,硬件支持高精度计数器;零,硬件不支持,读取失败。

QueryPerformanceFrequency() - 技术特点

供WIN9X使用的高精度定时器:QueryPerformanceFrequency()和QueryPerformanceCounter(),要求计算机从硬件上支持高精度定时器。需包含windows.h头文件。

函数的原形是:

BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);

BOOL QueryPerformanceCounter (LARGE_INTEGER *lpCount);

数据类型LARGEINTEGER既可以是一个作为8字节长的整数,也可以是作为两个4字节长的整数的联合结构,其具体用法根据编译器是否支持64位而定。该类型的定义如下:

typeef union _ LARGE_INTEGER

{

struct

{

DWORD LowPart;

LONG HighPart;

};

LONGLONG QuadPart;

} LARGE_INTEGER;

在定时前应该先调用QueryPerformanceFrequency()函数获得机器内部计时器的时钟频率。接着在需要严格计时的事件发生前和发生之后分别调用QueryPerformanceCounter(),利用两次获得的计数之差和时钟频率,就可以计算出事件经历的精确时间。

测试Sleep的精确时间:

#include <stdio.h>
#include <windows.h>
void main()
{
     LARGE_INTEGER nFreq;
     LARGE_INTEGER nBeginTime;
     LARGE_INTEGER nEndTime;
     double time;
 
     QueryPerformanceFrequency(&nFreq);
     QueryPerformanceCounter(&nBeginTime); 
 
     Sleep(1000);
 
     QueryPerformanceCounter(&nEndTime);
     time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;
 
     printf("%f\n",time);
     Sleep(1000);
system("Pause");
}


结果为

0.999982

1.000088

1.000200

单位为妙,乘1000000即为微秒

------------------------------------------------------------------------


对于精准Timer,微软提供的多媒体库里面有如下的函数:


MMRESULT timeSetEvent( UINT uDelay, 
                                 UINT uResolution, 
                                 LPTIMECALLBACK lpTimeProc, 
                                 WORD dwUser, 
                                 UINT fuEvent )

        uDelay:以毫秒指定事件的周期。
         Uresolution:以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms。
         LpTimeProc:指向一个回调函数。
         DwUser:存放用户提供的回调数据。
         FuEvent:指定定时器事件类型:
         TIME_ONESHOT:uDelay毫秒后只产生一次事件
         TIME_PERIODIC :每隔uDelay毫秒周期性地产生事件。


// TestTimer.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Windows.h>
#include <MMSystem.h>
#pragma comment(lib, "Winmm.lib")
MMRESULT timer_id;
LARGE_INTEGER nFreq;
LARGE_INTEGER nBeginTime;
LARGE_INTEGER nEndTime;
double time;
 
void WINAPI onTimeFunc(UINT wTimerID, UINT msg,DWORD dwUser,DWORD dwl,DWORD dw2)
{
     QueryPerformanceCounter(&nEndTime);
     time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;
 
     printf("%f\n",time);
	timeKillEvent(timer_id);
    return;
}
int _tmain(int argc, _TCHAR* argv[])
{

     QueryPerformanceFrequency(&nFreq);
	 
    int n = 0;
    
     QueryPerformanceCounter(&nBeginTime); 
	 timer_id = timeSetEvent(16, 1, (LPTIMECALLBACK)onTimeFunc, DWORD(1), TIME_PERIODIC);

     Sleep(1000);
system("Pause");
	return 0;
}

通过运行结果可以看到,这个Timer误差在1ms以内,够用了。



你可能感兴趣的:(windows,timer,精准计时器)