请问怎样实现微秒级别的延时?(50分)

  • 主题发起人 主题发起人 luyueming
  • 开始时间 开始时间
L

luyueming

Unregistered / Unconfirmed
GUEST, unregistred user!
毫秒级的可以用sleep()函数,
但是延时太长了,会导致其他代码来不及执行就超过定时器设定了。
另外,请问大家平时延时的方法。
 
用指令延时,不过windows的实时性比较差的,不会很精确
 
Windows操作系统不太可能精确的实现毫秒以上级的延时。除非进程数相当少,CPU的利用率很低,
才可以用指令来近似。
>平时延时的方法
——Sleep,用于桌面的程序延时精度达到0.01秒就绝对够用了。
到MMSystem.pas里找一下'micro',有几个,不过我还不会用,自己查MSDN吧。
如果你有特殊需要,可以考虑使用8253时钟芯片的控制寄存器,达到修改时钟中断间隔的目的。
(参考“兄弟变速器”的源码)
 
windows的定时器精度好像是18毫秒,楼上那位同志的方法不错
 
windows不是实时操作系统不可能单纯用软件实现任何高精度定时
必须软硬结合用io卡或其他卡上的8253 8254等进行软件触发 硬件定时
~~~~~
这里 HTTP://GO.TO/MASDP 有一个微妙极的高精度定时器控件HPCONTER可以试用一下
效果吗 呵呵
 
毫秒级可行,微秒级吗,在windows是不可能的。
 
哪有此事,Windows可以得到时钟周期级的精度!
用QueryPerformanceCounter和QueryPerformanceFrequency
 
在《Delphi深度历险》一书中对此有一章专门的论述,你可以参考一下。
 
精度可以得到,但进程间是要切换的,用户程序不可能独占CPU
 
var MyEvent :Thandle;
...
MyEvent := CreateEvent(nil,Ture,False,nil) ;
waitforSingleObject(MyEvent,500);
这本来是Windows 32位编程多线程同步时用到的技术,
但用在这里绝妙!MyEvent事件永远不发生,WaiForSingleObject 永
远返回WM_TIMEOUT.它的另一好处是等待的500ms中,几乎不占用CPU时间,
别的线程可干其他事情.定时精度比我所知的其他方法都高
 
好像有一个控件叫 HpTime ,在源代码的基础上修改一下是可以达到微秒极的延时的。
因为它是利用 CPU 的频率来做的,具体的方法不太记得了,好像是这样,你自己找找
这个控件的源代码来看看吧。如果找不到,请在我的 QQ 上留言或发个EMAIL给我,我
发给你。不过我保存的是我自己修改过的控件。
 
用QueryPerformanceCounter在Intel X86上拥有约0.8微秒的分辨率
Function QueryPerformanceCounter(
var lpPerformanceCount : TLargeInter
) : BOOL;
stdcall;
 
网上有个SuperTime控件,可以精确到 1/1193180 秒(误差在 0.00000587--0.00000838 之间)。
利用的原理是计算机中的可编程时间间隔定时器芯片每秒固定产生1193180次脉冲。
 
sleep()函数
 
简单方法,用delphiX里的那个timer控件
 
用第三方控件:
RDTSC v1.00
作者:Alexander Zloba. 这款控件是奔腾RDTSC指令的封装,
使你能够得到十亿分之一秒的时间间隔,而且使用非常简单。
URL:http://www.delphibyte.com/download/softdown.php?softid=421&url=http://61.132.118.165/soft/delphi/Delphi3/zlrdtsc.zip
 
利用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>0do
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,则记时精度越高。
下面是在我的机器上的执行结果。

从上面的结果看,由Delphi的Timer控件建立的记时器的精度十分差,无法在实际中使用,而利用高性能频率记数法和多媒体计数器法的误差都在1%以下。考虑到程序中在文本框中显示时间对程序所造成的影响,这个误差在应用中是完全可以忽略的。
另外在运行程序时作者还发现一个问题,如果在倒记时时拖动窗口,文本框中的显示都会停止,而当停止窗口拖放后,多媒体记时器显示会跳过这段时间记时,而其它两种记时器显示倒记时却还是从原来的时间倒数。这说明多媒体记时器是在独立的线程中运行的,不会受到程序的影响。
综合上面的介绍和范例,我们可以看到,如果要建立高精度的记时器,使用多媒体记时器是比较好的选择。而高性能频率记数法比较适合计算某个耗时十分短的过程所消耗的时间(例如分析程序中某个被多次调用的程序段执行时间以优化程序),因为毕竟高性能频率记数的理论可以达到微秒级别。Timer控件虽然精度比上面两者差很多,但是它使用方便,在要求不高的场合它还是最佳选择。
(最后要说的是,以上的结果都是在Windows 9X下获得的,作者在Windows 2000下运行该程序时发现,Timer控件的精度比在Windows 9X下要高出很多,一般误差在5%以下,这说明Windows 2000是一个真正的多任务操作系统。再加上Windows NT/2000的稳定性和易用性,在工业控制或实时检测等领域是一个比较完美的平台)

 
把分给我吧!问题解决了! CPU 耗用100%的问题没有了![:D] 程序也不会假死。(D6 + XP 下调试通过)
procedure Delay(msec:integer);
//延时函数,msec 为微秒(千分之1秒)
var
FirstTickCount : real;
begin
FirstTickCount := GetTickCount();
FirstTickCount := FirstTickCount + msec;
While FirstTickCount > GetTickCount()do
Application.HandleMessage;
//关键是这里
// application.processmessages;
end;

delay (5000);
// 延时 5 秒
 
后退
顶部