分享

Windows精准计时&精准Timer

 云将东游 2017-06-26

以前做上层程序的时候,需要计时的时候就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的精确时间:

  1. #include <stdio.h>  
  2. #include <windows.h>  
  3. void main()  
  4. {  
  5.      LARGE_INTEGER nFreq;  
  6.      LARGE_INTEGER nBeginTime;  
  7.      LARGE_INTEGER nEndTime;  
  8.      double time;  
  9.    
  10.      QueryPerformanceFrequency(&nFreq);  
  11.      QueryPerformanceCounter(&nBeginTime);   
  12.    
  13.      Sleep(1000);  
  14.    
  15.      QueryPerformanceCounter(&nEndTime);  
  16.      time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;  
  17.    
  18.      printf("%f\n",time);  
  19.      Sleep(1000);  
  20. system("Pause");  
  21. }  


结果为

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毫秒周期性地产生事件。


  1. // TestTimer.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <Windows.h>  
  6. #include <MMSystem.h>  
  7. #pragma comment(lib, "Winmm.lib")  
  8. MMRESULT timer_id;  
  9. LARGE_INTEGER nFreq;  
  10. LARGE_INTEGER nBeginTime;  
  11. LARGE_INTEGER nEndTime;  
  12. double time;  
  13.    
  14. void WINAPI onTimeFunc(UINT wTimerID, UINT msg,DWORD dwUser,DWORD dwl,DWORD dw2)  
  15. {  
  16.      QueryPerformanceCounter(&nEndTime);  
  17.      time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;  
  18.    
  19.      printf("%f\n",time);  
  20.     timeKillEvent(timer_id);  
  21.     return;  
  22. }  
  23. int _tmain(int argc, _TCHAR* argv[])  
  24. {  
  25.   
  26.      QueryPerformanceFrequency(&nFreq);  
  27.        
  28.     int n = 0;  
  29.       
  30.      QueryPerformanceCounter(&nBeginTime);   
  31.      timer_id = timeSetEvent(16, 1, (LPTIMECALLBACK)onTimeFunc, DWORD(1), TIME_PERIODIC);  
  32.   
  33.      Sleep(1000);  
  34. system("Pause");  
  35.     return 0;  
  36. }  
  1. 通过运行结果可以看到,这个Timer误差在1ms以内,够用了。  



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多