天天看點

timeSetEvent()函數

原文連結位址:http://www.cnblogs.com/kangwang1988/archive/2010/09/16/1827872.html

微軟公司在其多媒體Windows中提供了精确定時器的底層API支援。利用多媒體定時器可以很精确地讀出系統的目前時間,并且能在非常精确的時間間隔内完成一個事件、函數或過程的調用。利用多媒體定時器的基本功能,可以通過兩種方法實作精确定時。

1)使用timeGetTime()函數,該函數定時精度為ms級,傳回從Windows啟動開始所經過的時間。由于使用該函數是通過查詢的方式進行定時控制的,是以,應該建立定時循環來進行定時事件的控制。

2)使用timeSetEvent()函數,該函數原型如下:

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

uDelay:以毫秒指定事件的周期。

Uresolution:以毫秒指定延時的精度,數值越小定時器事件分辨率越高。預設值為1ms。

LpTimeProc:指向一個回調函數。

DwUser:存放使用者提供的回調資料。

FuEvent:指定定時器事件類型:

TIME_ONESHOT:uDelay毫秒後隻産生一次事件

TIME_PERIODIC :每隔uDelay毫秒周期性地産生事件。

該函數的參數說明如下:參數uDelay表示延遲時間;參數uResolution表示時間精度,在Windows中預設值為1ms;lpTimeProc表示回調函數,為使用者自定義函數,定時調用; 參數dwUser表示使用者提供的回調資料;參數fuEvent為定時器的事件類型,TIME_ONESHOT表示執行一次;TIME_PERIODIC:周期性執行。具體應用時,可以通過調用timeSetEvent()函數,将需要周期性執行的任務定義在lpTimeProc回調函數中(如:定時采樣、控制等),進而完成所需處理的事件。需要注意的是:任務處理的時間不能大于周期間隔時間。另外,在定時器使用完畢後,應及時調用timeKillEvent()将之釋放。下面這段代碼的主要功能是設定兩個時鐘定時器,一個間隔是1ms,一個間隔是2s。每執行一次,把目前系統時鐘值輸入檔案"cure.out"中,以比較該定時器的精确度。

# define ONE_MILLI_SECOND 1 //定義1ms和2s時鐘間隔,以ms為機關 ;
# define TWO_SECOND 2000 
# define TIMER_ACCURACY 1 //定義時鐘分辨率,以ms為機關 
UINT wTimerRes_1ms,wTimerRes_2s; //定義時間間隔 
UINT wAccuracy; //定義分辨率 
UINT TimerID_1ms,TimerID_2s; //定義定時器句柄
///////////////////////////////
CCureApp::CCureApp():fout("cure.out", ios::out) //打開輸出檔案"cure.out";
{ 
     // 給時間間隔變量指派 
     wTimerRes_1ms = ONE_MILLI_SECOND; 
      wTimerRes_2s = TWO_SECOND; 
       TIMECAPS tc; 
        //利用函數timeGetDeVCaps取出系統分辨率的取值範圍,如果無錯則繼續; 
        if(timeGetDevCaps(&tc,sizeof(TIMECAPS))==TIMERR_NOERROR) 
         { 
                wAccuracy=min(max(tc.wPeriodMin, //分辨率的值不能超出系統的取值範圍
                        TIMER_ACCURACY),tc.wPeriodMax); 
                  //調用timeBeginPeriod函數設定定時器的分辨率 
                  timeBeginPeriod(wAccuracy); 
                    //設定定時器 
                    InitializeTimer(); 
         } 
} 
CCureApp:: ~CCureApp() 
{ 
     fout <<"結束時鐘"<< endl; //結束時鐘 
      timeKillEvent(TimerID_1ms); // 删除兩個定時器 
       timeKillEvent(TimerID_2s); // 删除設定的分辨率 
        timeEndPeriod(wAccuracy); 
} 
void CCureApp::InitializeTimer() 
{ 
     StartOneMilliSecondTimer(); 
      StartTwoSecondTimer(); 
} 
//1ms定時器的回調函數,類似于中斷處理程式,一定要聲明為全局PASCAL函數,
//否則編譯會有問題 
void PASCAL OneMilliSecondProc(UINT wTimerID, UINT msg,DWORD dwUser,
                               DWORD dwl,DWORD dw2) 
{ 
     // 定義計數器 
     static int ms = 0; 
      CCureApp *app = (CCureApp *)dwUser; 
       // 取得系統時間,以ms為機關 
       DWORD osBinaryTime = GetTickCount(); 
        //輸出計數器值和目前系統時間 
        app->fout<<++ms<<":1ms:" 
} 
// 加裝1ms定時器 
void CCureApp::StartOneMilliSecondTimer() 
{ 
     if((TimerID_1ms = timeSetEvent(wTimerRes_1ms, wAccuracy, 
           (LPTIMECALBACK) OneMil liSecondProc, // 回調函數; 
             (DWORD)this, // 使用者傳送到回調函數的資料;
               TIME_PERIODIC)) == 0)//周期調用定時處理函數;
      { 
            AfxMessageBox("不能進行定時!", MB_OK | MB_ICONASTERISK); 
      } 
      else 
            fout << "16ms 計 時:" << endl; //不等于0表明加裝成功,傳回此定時器的句柄; 
}      

在精度要求較高的情況下,如要求定時誤差不大于1ms時,還可以利用GetTickCount()函數傳回自計算機啟動後的時間,該函數的傳回值是DWORD型,表示以ms為機關的計算機啟動後經曆的時間間隔。通過兩次調用GetTickCount()函數,然後控制它們的內插補點來取得定時效果.下列的代碼可以實作50ms的精确定時,其誤差是毫秒級的。

// 起始值和中止值
DWORD dwStart, dwStop ; 
dwStop = GetTickCount(); 
while(TRUE) { 
 // 上一次的中止值變成新的起始值 
 dwStart = dwStop ; // 此處添加相應控制語句 
 do 
 {
  dwStop = GetTickCount() ; 
 }while(dwStop - 50 < dwStart) ; 
}      

用上述兩種方式取得的定時效果雖然在許多場合已經滿足實際的要求,但由于它們的精度隻有毫秒級的,而且在要求定時時間間隔小時,實際定時誤差大。對于精确度要求更高的定時操作,則應該使用QueryPerformanceFrequency()和QueryPerformanceCounter()函數。這兩個函數是Visual C++提供并且僅供Windows 95及其後續版本使用,其精度與CPU的時鐘頻率有關,它們要求計算機從硬體上支援精确定時器。QueryPerformanceFrequency()函數和QueryPerformanceCounter()函數的原型如下:

BOOL QueryPerformanceFrequency (LARGE_INTEGER *lpFrequency);

BOOL QueryPerformanceCounter (LARGE_INTEGER *lpCount);

上述兩個函數的參數的資料類型LARGE_INTEGER既可以是一個8位元組長的整型數,也可以是兩個4位元組長的整型數的聯合結構,其具體用法根據編譯器是否支援64位而定。該類型的定義如下:

typedef union _LARGE_INTEGER

{

struct{

DWORD LowPart ; // 4位元組整型數

LONG HighPart ; // 4位元組整型數

};

LONG QuadPart ; // 8位元組整型數

} LARGE_INTEGER ;

使用QueryPerformanceFrequency()和QueryPerformanceCounter()函數進行精确定時的步驟如下:

1、首先調用QueryPerformanceFrequency()函數取得高精度運作計數器的頻率f,機關是每秒多少次(n/s),此數一般很大;

2、在需要定時的代碼的兩端分别調用QueryPerformanceCounter()以取得高精度運作計數器的數值n1、n2,兩次數值的內插補點通過f換算成時間間隔,t=(n2-n1)/f,當t大于或等于定時時間長度時,啟動定時器;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

自己的一個try

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#pragma comment(lib,"Winmm.lib")


# define ONE_MILLI_SECOND 1 //定義1ms和2s時鐘間隔,以ms為機關 ;
# define TWO_SECOND 2000 
# define TIMER_ACCURACY 1 //定義時鐘分辨率,以ms為機關 
void PASCAL OneMilliSecondProc(UINT wTimerID, UINT msg,DWORD dwUser,DWORD dwl,DWORD dw2) 
{ 
    printf("11111111111\n");
    exit(0);
} 

void main()

{
    HANDLE hHandle;

    UINT wTimerRes_1ms,wTimerRes_2s;//定義時間間隔 
    UINT wAccuracy; //定義分辨率 
    UINT TimerID_1ms,TimerID_2s; //定義定時器句柄
    wTimerRes_1ms = 5000;
    if((TimerID_1ms = timeSetEvent(wTimerRes_1ms, wAccuracy,(LPTIMECALLBACK)OneMilliSecondProc, // 回調函數
        (DWORD)(1), // 使用者傳送到回調函數的資料;
        TIME_PERIODIC)) == 0)//周期調用定時處理函數
    {
        printf("start!!!!!!!!!!!\n");
    } 
    else
    {
        printf("end!!!!!!!!!!!\n");
    }

    while (1)
    {
        printf("hello!\n");
        Sleep(1000);
    }
}      
timeSetEvent()函數