转至繁体中文版     | 网站首页 | 图文教程 | 资源下载 | 站长博客 | 图片素材 | 武汉seo | 武汉网站优化 | 
最新公告:     敏韬网|教学资源学习资料永久免费分享站!  [mintao  2008年9月2日]        
您现在的位置: 学习笔记 >> 图文教程 >> 软件开发 >> Delphi程序 >> 正文
利用Delphi建立精确计时器         ★★★★

利用Delphi建立精确计时器

作者:闵涛 文章来源:闵涛的学习笔记 点击数:1211 更新时间:2009/4/23 18:26:00

 

  www.applevb.com

利用Delphi建立精确计数器

在Windows中的很多场合下编程(例如工业控制、游戏)中需要比较精确的记时器,本文讨论的是在Delphi下实现记时器的若干方法以及它们的精度控制问题。

在Delphi中最常用的是Timer控件,它的设置和使用都非常方便,理论上它的记时精度可以达到1ms(毫秒)。但是众所周知的,实际上Timer在记时间隔小于50ms之下是精度是十分差的。它只适用于对于精度要求不太高的场合。

       这里作者要介绍的是两种利用Windows API函数实现精确记时的方法。第一中方法是利用高性能频率记数(作者本人的称呼)法。利用这种方法要使用两个API函数QueryPerformanceFrequency和QueryPerformanceCounter。QueryPerformanceFrequency函数获得高性能频率记数器的震荡频率。

调用该函数后,函数会将系统频率记数器的震荡频率(每毫秒)保存到一个LargeInteger中。不过利用该函数在几台机器上做过试验,结果都是1193180。读者朋友可以在自己的机器上试一下

QueryPerformanceCounter函数获得系统频率记数器的震荡次数,结果也保存到一个Largenteger中。

很显然,如果在计时中首先使用QueryPerformanceFrequency获得高性能频率记数器每毫秒的震荡次数,然后在计时开始时使用QueryPerformanceCounter函数获得当前系统频率记数器的震荡次数。在计时结束时再调用QueryPerformanceCounter函数获得系统频率记数器的震荡次数。将两者相减,再将结果除以频率记数器每毫秒的震荡次数,就可以获得某一事件经过的准确时间。(次数除以频率等于时间)

另外的一种精确记时器的功能是利用多媒体记时器函数(这也是作者的定义,因为这个系列的函数是在Winmm.dll中定义并且是为媒体播放服务的)。

实现多媒体记时器首先要使用timeSetEvent函数建立计时事件。该函数在Delphi中的mmsystem.pas中有定义,定义如下:

function timeSetEvent(uDelay, uResolution: UINT;

  lpFunction: TFNTimeCallBack; dwUser: DWORD; uFlags: UINT): MMRESULT; stdcall

函数定义中参数uDelay定义延迟时间,以毫秒为单位,该参数相当于Timer控件的Interval属性。参数uResolution定义记时精度,如果要求尽可能高的精度,要将该参数设置为0;参数lpFunction定义了timeSetEvent函数的回调函数。该函数相当于一个定时中断处理函数,每当经过一个uDelay长度的时间间隔,该函数就会被调用,编程者可以在该函数中加入相应的处理语句。参数dwUser定义用户自定义的回调值,该值将传递给回调函数。参数uFlags定义定时类型,如果要不间断的记时,该值应设置为1。

如果函数调用成功,在系统中建立了一个多媒体记时器对象,每当经过一个uDelay时间后lpFunction指定的函数都会被调用。同时函数返回一个对象标识,如果不再需要记时器则必须要使用timeKillEvent函数删除记时器对象。

 

由于Windows是一个多任务的操作系统,因此基于API调用的记时器的精度都会受到其它很多因素的干扰。到底这两中记时器的精度如何,我们来使用以下的程序进行验证:

设置三种记时器(Timer控件、高性能频率记数、多媒体记时器)。将它们的定时间隔设置为10毫秒,让它们不停工作直到达到一个比较长的时间(比如60秒),这样记时器的误差会被累计下来,然后同实际经过的时间相比较,就可以得到它们的精度。

下面是具体的检测程序。

unit Unit1;

 

interface

 

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

  StdCtrls, ExtCtrls,mmSystem;

 

type

  TForm1 = class(TForm)

    Edit1: TEdit;

    Edit2: TEdit;

    Edit3: TEdit;

    Button1: TButton;

    Button2: TButton;

    Timer1: TTimer;

    procedure FormCreate(Sender: TObject);

    procedure Button1Click(Sender: TObject);

    procedure Timer1Timer(Sender: TObject);

    procedure Button2Click(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;

 

var

  Form1: TForm1;

  actTime1,actTime2:Cardinal;

  smmCount,sTimerCount,sPCount:Single;

  hTimeID:Integer;

  iTen:Integer;

  proTimeCallBack:TFNTimeCallBack;

 

procedure TimeProc(uTimerID, uMessage: UINT;

    dwUser, dw1, dw2: DWORD) stdcall;

procedure proEndCount;

implementation

 

{$R *.DFM}

//timeSetEvent的回调函数

procedure proEndCount;

begin

  actTime2:=GetTickCount-actTime1;

  Form1.Button2.Enabled :=False;

  Form1.Button1.Enabled :=TRue;

  Form1.Timer1.Enabled :=False;

  smmCount:=60;

  sTimerCount:=60;

  spCount:=-1;

 

  timeKillEvent(hTimeID);

end;

 

procedure TimeProc(uTimerID, uMessage: UINT;

    dwUser, dw1, dw2: DWORD) stdcall;

begin

  Form1.Edit2.Text:=FloatToStr(smmCount);

  smmCount:=smmCount-0.01;

end;

 

procedure TForm1.FormCreate(Sender: TObject);

begin

  Button1.Caption :=''''开始倒计时'''';

  Button2.Caption :=''''结束倒计时'''';

  Button2.Enabled :=False;

  Button1.Enabled :=True;

  Timer1.Enabled :=False;

  smmCount:=60;

  sTimerCount:=60;

  sPCount:=60;

end;

 

procedure TForm1.Button1Click(Sender: TObject);

var

  lgTick1,lgTick2,lgPer:TLargeInteger;

  fTemp:Single;

begin

  Button2.Enabled :=True;

  Button1.Enabled :=False;

  Timer1.Enabled :=True;

  Timer1.Interval :=10;

  proTimeCallback:=TimeProc;

  hTimeID:=timeSetEvent(10,0,proTimeCallback,1,1);

  actTime1:=GetTickCount;

 

  //获得系统的高性能频率计数器在一毫秒内的震动次数

  QueryPerformanceFrequency(lgPer);

  fTemp:=lgPer/1000;

  iTen:=Trunc(fTemp*10);

  QueryPerformanceCounter(lgTick1);

  lgTick2:=lgTick1;

  sPCount:=60;

  while sPCount>0 do begin

    QueryPerformanceCounter(lgTick2);

    //如果时钟震动次数超过10毫秒的次数则刷新Edit3的显示

    If lgTick2 - lgTick1 > iTen Then begin

            lgTick1 := lgTick2;

            sPCount := sPCount - 0.01;

            Edit3.Text := FloatToStr(sPCount);

            Application.ProcessMessages;

    end;

  end;

end;

 

procedure TForm1.Timer1Timer(Sender: TObject);

begin

  Edit1.Text := FloatToStr(sTimerCount);

  sTimerCount:=sTimerCount-0.01;

end;

 

procedure TForm1.Button2Click(Sender: TObject);

begin

  proEndCount;

  //显示从开始记数到记数实际经过的时间

  ShowMessage(''''实际经过时间''''+IntToStr(actTime2)+''''毫秒'''');

end;

 

end.

       运行程序,点击“开始倒记时”按钮,程序开始60秒倒记时,由于上面的程序只涉及了记时器程序的原理而没有将错误处理加入其中,所以不要等60秒倒记时结束。点击“结束倒记时”按钮可以结束倒记时。这时在弹出对话框中会显示实际经过的时间(单位为毫秒),将三个文本框内的时间乘以1000再加上实际经过的时间,越接近60000,则记时精度越高。

下面是在我的机器上的执行结果。

[1] [2]  下一页


没有相关教程
教程录入:mintao    责任编辑:mintao 
  • 上一篇教程:

  • 下一篇教程:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网]
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)

    同类栏目
    · C语言系列  · VB.NET程序
    · JAVA开发  · Delphi程序
    · 脚本语言
    更多内容
    热门推荐 更多内容
  • 没有教程
  • 赞助链接
    更多内容
    闵涛博文 更多关于武汉SEO的内容
    500 - 内部服务器错误。

    500 - 内部服务器错误。

    您查找的资源存在问题,因而无法显示。

    | 设为首页 |加入收藏 | 联系站长 | 友情链接 | 版权申明 | 广告服务
    MinTao学以致用网

    Copyright @ 2007-2012 敏韬网(敏而好学,文韬武略--MinTao.Net)(学习笔记) Inc All Rights Reserved.
    闵涛 投放广告、内容合作请Q我! E_mail:admin@mintao.net(欢迎提供学习资源)

    站长:MinTao ICP备案号:鄂ICP备11006601号-18

    闵涛站盟:医药大全-武穴网A打造BCD……
    咸宁网络警察报警平台