Windows中的精度定时问题

来源:互联网 发布:图片二维码识别软件 编辑:程序博客网 时间:2024/04/19 22:03

Windows中的精度定时问题

  在Windows中,可以使用不同精度的定时器来满足不同的要求:

1、使用Timer组件

  它其实是先调用SetTimer()函数建立一个定时器,然后每隔一定的时间向Windows发送一个WM_TIMER的消息,操作系统捕获此消息后处理相应的事件。但是由于Windows的定时器是建立在DOS1CH的中断基础上的,而此中断的响应频率是每秒18.2次,所以Timer组件对小于55ms的计时器就无能为力了。此外,WM_TIMER消息没胡被处理时,定时器又触发了新的WM_TIMER消息就会被舍弃。因此组件只能适用于那些对时间要求不是很严格的场合。

2、使用多媒体定时器

  使用多媒体定时器不容易遗失消息,且触发的最小时间间隔在10ms左右,可以称之为高精度定时器,它主要是使用TimeSetEvent()函数建立定时器事件,通过回调函数来触发,多媒体定时器会在程序中建立另外一个线程,不妨称为A线程。当定时器触发时,此线程就会暂时停下来,进行环境切换,切换到设定计时器的线程,执行回调函数,而不管线程的工作,这就是多媒体定时器定时精度的原因。此外A线程的优先级是15级,比大多数线程都高,所以可以很方便地执行此线程中的代码。

3、高精度定时器

  可以使用Win32的取得计数器频率的函数QueryPerformanceFrequence()和取得计数值的函数QueryPerformanceCounter()来获得更高的计时精度。其中QueryPerformanceCounter()IntelX86上准确度为0.8ms

                                         《工业控制计算机》2006.32730

   

 

  国防科技大学机电工程与仪器系(张云洲梁科山)

    在工业控制软件中,数据采集和实时控制是经常性的工作。目前,工业控制软件已经从DOSWindows3.x平台,转到了Windows95上。然而,Windows95并不能直接支持中断,这就意味着在DOSWindows3.x中的定时器中断不能为我们所用。因此,我们必须寻求新的定时方式。

  在C++ BuilderDelphi等典型的编程语言中,都提供了定时器控件,可以方便地实现定时和事件响应。此外,Windows95还提供了SetTimerKillTimer函数来设置和删除一个定时器,在事件WM_TIMER响应函数中实现处理。然而,遗憾的是,通过这些方式获得的Win95定时器最小只能精确到55毫秒,对于55毫秒以下的时间精度便无能为力。这对于Win95下测控软件的开发是十分不利的。幸运的是,多媒体定时器可以解决这一难题。

  以下是一个完整的多媒体定时器设计示例程序,该定时器的精度为1毫秒。该程序的内容十分简单:按下按钮1,启动定时器,在Edit框中显示定时值(计数值),按下按钮2则将关闭定时器。此程序在Windows95环境下,采用C++Builder3.0编程并编译通过,但设计思想同样适用于其它高级语言。按照本程序中的实现方法,读者将可以轻松实现自己的高精度定时器。

程序清单如下:

#include < vcl.h>
#pragma hdrstop

#include"mmsystem.h"   //包含多媒体定时器函数的头文件
#define MilliSecond  1  //
定时间隔1毫秒
#define Accuracy     1  //
系统允许的分辨率最小值

#defineMin(x,y)  ((x < y) ? x : y)
#define Max(x,y)  ((x > y) ? x : y)

#include"HighTimerU.h"
//------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
UINT TimerID;    //
定义定时器句柄
int count;       //
定义一个变量以进行计数
int TimerAccuracy;
TForm1 *Form1;
//------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}

void PASCALTimerCallProc(UINT 
TimerID, UINT msg,DWORD dwUser,
           DWORD dwa,DWORDdwb) 
//
定义定时器事件的调用函数
{
    count++;
    Form1- >Edit1->Text=count;  
//
在一个编辑框内显示计数值,即定时值
}

//---------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    TIMECAPS timecaps;
    int TimerResolution;

//从系统获得关于定时器服务能力的信息,
//
分辨率不能超出系统许可值(116毫秒)
  if (timeGetDevCaps(&timecaps,sizeof(
TIMECAPS))==TIMERR_NOERROR)
      TimerAccuracy=Min(Max(timecaps.wPeriodMin,
Accuracy),timecaps.wPeriodMax);

   timeBeginPeriod(TimerAccuracy); 
//
设置定时器分辨率
 
    TimerResolution=1;    //
设置定时间隔为1毫秒

   //产生间隔1毫秒,周期执行的定时器事件;启动定时器
    TimerID = timeSetEvent(TimerResolution,TimerAccuracy,
                       &TimerCallProc,1,TIME_PERIODIC);
}
//-------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    timeKillEvent(TimerID);       //
删除定时器事件
    timeEndPeriod(TimerAccuracy);  //
清除定时器分辨率
}
  多媒体定时器对实现高精度定时是很理想的工具,而且其精度是十分可*的。但是,多媒体定时器也并不是完美的。因为它可*的精度是建立在对系统资源的消耗之上的。因此,在利用多媒体定时器完成工作的同时,必须注意以下几点:
1多媒体定时器的设置分辨率不能超出系统许可范围。

2在使用完定时器以后,一定要及时删除定时器及其分辨率,否则系统会越来越慢。

3多媒体定时器在启动时,将自动开辟一个独立的线程。在定时器线程结束之前,注意一定不能再次启动该定时器,不然将迅速造成死机。

 

 

  以下可以精确到毫秒。  
  BOOL   CDeviceThread::InitInstance()  
  {  
  //   TODO:     perform   and   per-thread  initialization   here  
  Sleep(3000);//
完成初始化工作    
  //
获得本机的最小定时器分辨率,所有应用应该大于等于该分辨率  
  if(timeGetDevCaps(&m_tc,sizeof(TIMECAPS))==TIMERR_NOERROR)  
  {  
  m_wAccuracy=min(max(m_tc.wPeriodMin,1),m_tc.wPeriodMax);//
取得分辨率  
  timeBeginPeriod(m_wAccuracy);  
  }  
  //
timeSetEvent设定事件的触发方式,参数1为定时间隔,2为设定程序所需的最小分辨率  
  //
参数3为调用回调函数,4为用户提供的回调数据,5为每隔一定时间触发一次,  
  //
如为TIME_ONESHOT事件仅触发一次  
  //
虽然可最小精确到1ms,但这里为1s  
 m_Timer_ID=timeSetEvent(1000,m_wAccuracy,(LPTIMECALLBACK)CatchMMTimer,0,TIME_PERIODIC); 
  return   TRUE;  
  }  
   
  int   CDeviceThread::ExitInstance()  
  {  
  //   TODO:     perform   any   per-thread  cleanup   here  
  timeKillEvent(m_Timer_ID);   //
删除定时器句柄  
  timeEndPeriod(m_wAccuracy);   //
删除定时器分辨率  
  return   CWinThread::ExitInstance();  
  }  
   
  BEGIN_MESSAGE_MAP(CDeviceThread,   CWinThread)  
  //{{AFX_MSG_MAP(CDeviceThread)  
  ON_MESSAGE(WM_READFROMDEVICE,   OnReadFromDevice)//
自定义消息映射  
  //}}AFX_MSG_MAP  
  END_MESSAGE_MAP()  
  //
用户定义的函数用于接收多媒体定时器的事件通知  
  void   CALLBACK   CatchMMTimer(UINT   wTimerID,UINT  nMsg,DWORD   dwUser,DWORD   dw1,DWORD   dw2)  
  {  
  //
发送消息到自定义消息处理函数    
 PostThreadMessage(((CMainFrame*)(theApp.m_pMainWnd))->m_DeviceThread.m_nThreadID,WM_READFROMDEVICE,0,0); 
  PostThreadMessage(::m_DataPool.m_AlertClassThread.m_nThreadID,WM_READALERTDATA,0,0); 
   
  }

 内容从别处转载而来、、、、、、、、、、、、、、、、、

 

0 0
原创粉丝点击