小生使用delphi编写工控界面程序,为了实现1ms内读和写(硬件满足要求),将timer的interval属性设置为1。但是发现Timer这个过程执行时间不是1ms执行一次,而且误差很大。请问各位大神们在做工业控制时有没有遇到这种情况,该如何解决?
另外一个问题是假如Timer实际可以达到1ms,那么Timer中的程序如果执行到中间的时候,1ms到了,是继续执行程序还是重新开始执行Timer?
感激不尽!
另外一个问题是假如Timer实际可以达到1ms,那么Timer中的程序如果执行到中间的时候,1ms到了,是继续执行程序还是重新开始执行Timer?
感激不尽!
window上实现1MS控制,神了
begin
inc(dwUser^);
end;procedure TForm1.Button1Click(Sender: TObject);
var
nID : DWORD;
i : integer;
begin
i := 0;
nID := timeSetEvent(1 , 0 , @__TimeProc2 , integer(@i) , TIME_PERIODIC or TIME_CALLBACK_FUNCTION);
Sleep(1000);
timeKillEvent(nID);
CloseHandle(nID);
Memo1.Lines.Add('1秒内执行次数:' + IntToStr(i));
end;基本上是1MS执行一次,引用MMSystem单元,注意TimeProc执行体非主线程.
小的下去自学一下timeSetEvent和timeKillEvent的用法。
另外微弱的问一句,4楼代码中@i是取i的地址,integer(@i)是啥意思?将i的地址取整吗?
小菜鸟十分菜鸟,大神见谅~
再次感谢!!!
我的实验设备中:
1.模拟输出卡的建立输出电压时间为130us;
2.伺服放大器和伺服电机的响应时间为5ms;
3.编码器的输出频率不大于150k(响应时间不小于6.7us);
4.运动控制卡的编码器输入频率为3.2MHz(最小采样时间为0.3us);
5.工控机与板卡的PCI总线频率为33MHz(延时0.3us)。
一、其中伺服放大器和伺服电机与机械部分作为控制对象,可以将5ms看作其最小周期,根据香农采样定理可知系统采样时间不能大于2.5ms。
二、由1345控制系统的延时可得采样时间不能小于0.14毫秒。
综上我选取1ms或2ms的采样时间,个人觉得比较合理,就是不知道能不能实现。老师说这套工控设备已经是很快的了。另外4楼前辈kiboisme的代码中,函数timeSetEvent调用时integer(@i)是啥意思?是不是指指向i的整形指针?
多媒体定时器的精度是ms级的,对于要1ms精确定时肯定不够,你只能用CPU的“CLICK”来定时,但是,对WINDOWS这种非实时多任务操作系统来说,时间响应要求1ms太高了。从I/O口得到数据,你的程序检测到数据,处理,再从I/O口输出数据(控制命令),这里不知道经过了多少层,即使现在CPU的频率再高,1ms也来不及响应的。何况你还得刷新界面,如果还得保存数据,这就更杯具了。对于这种高实时实响应系统,只能用真实时操作系统,也就是实时嵌入式操作系统,比如ucos,FreeRtos,等等,wondow系统只能做为后台历史数据处理(或者时间响应远大于1ms,比如100MS以上的控制的操作)。
我们原来就客户要求做个时间精度1ms的波形分析,只是纯粹的板卡采集数据,没有控制命令,只是实时波形界面显示,还是不理想。定时器用QueryPerformanceFrequency()来读取CPU的“CLICK”,自己做底层驱动,尽量绕过API,等等
Process Stopped. Use Step or Run to continue.代码如下:unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, MMSystem, StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure my_TimeProc(uTimerID, uMessage: UINT; dwUser: PInteger; dw1, dw2: DWORD) stdcall;
begin
inc(dwUser^);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
nID : DWORD;
i : integer;
begin
i := 0;
nID := timeSetEvent(1 , 0 , @my_TimeProc, integer(@i) , TIME_PERIODIC or TIME_CALLBACK_FUNCTION);
Sleep(1000);
timeKillEvent(nID);
CloseHandle(nID);
Memo1.Lines.Add('1秒内执行次数:' + IntToStr(i));
end;
end.再次感谢大家的回复!感激!
private
FActiveOnly: Boolean;
FEnabled: Boolean;
FFrameRate: Integer;
FInitialized: Boolean;
FInterval: Cardinal;
FInterval2: Cardinal;
FNowFrameRate: Integer;
FOldTime: DWORD;
FOldTime2: DWORD;
FOnActivate: TNotifyEvent;
FOnDeactivate: TNotifyEvent;
FOnTimer: TDXTimerEvent;
procedure AppIdle(Sender: TObject; var Done: Boolean);
function AppProc(var Message: TMessage): Boolean;
procedure Finalize;
procedure Initialize;
procedure Resume;
procedure SetActiveOnly(Value: Boolean);
procedure SetEnabled(Value: Boolean);
procedure SetInterval(Value: Cardinal);
procedure Suspend;
protected
procedure DoActivate; virtual;
procedure DoDeactivate; virtual;
procedure DoTimer(LagCount: Integer); virtual;
procedure Loaded; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property ActiveOnly: Boolean read FActiveOnly write SetActiveOnly;
property Enabled: Boolean read FEnabled write SetEnabled;
property FrameRate: Integer read FFrameRate;
property Interval: Cardinal read FInterval write SetInterval;
property OnActivate: TNotifyEvent read FOnActivate write FOnActivate;
property OnDeactivate: TNotifyEvent read FOnDeactivate write FOnDeactivate;
property OnTimer: TDXTimerEvent read FOnTimer write FOnTimer;
end; { TDXTimer } TDXTimer = class(TCustomDXTimer)
published
property ActiveOnly;
property Enabled;
property Interval;
property OnActivate;
property OnDeactivate;
property OnTimer;
end;
{ TCustomDXTimer }constructor TCustomDXTimer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FActiveOnly := True;
FEnabled := True;
Interval := 1000;
Application.HookMainWindow(AppProc);
end;destructor TCustomDXTimer.Destroy;
begin
Finalize;
Application.UnHookMainWindow(AppProc);
inherited Destroy;
end;procedure TCustomDXTimer.AppIdle(Sender: TObject; var Done: Boolean);
var
t, t2: DWORD;
LagCount, i: Integer;
begin
Done := False; t := TimeGetTime;
t2 := t-FOldTime;
if t2>=FInterval then
begin
FOldTime := t; LagCount := t2 div FInterval2;
if LagCount<1 then LagCount := 1; Inc(FNowFrameRate); i := Max(t-FOldTime2, 1);
if i>=1000 then
begin
FFrameRate := Round(FNowFrameRate*1000/i);
FNowFrameRate := 0;
FOldTime2 := t;
end; DoTimer(LagCount);
end;
end;function TCustomDXTimer.AppProc(var Message: TMessage): Boolean;
begin
Result := False;
case Message.Msg of
CM_ACTIVATE:
begin
DoActivate;
if FInitialized and FActiveOnly then Resume;
end;
CM_DEACTIVATE:
begin
DoDeactivate;
if FInitialized and FActiveOnly then Suspend;
end;
end;
end;procedure TCustomDXTimer.DoActivate;
begin
if Assigned(FOnActivate) then FOnActivate(Self);
end;procedure TCustomDXTimer.DoDeactivate;
begin
if Assigned(FOnDeactivate) then FOnDeactivate(Self);
end;procedure TCustomDXTimer.DoTimer(LagCount: Integer);
begin
if Assigned(FOnTimer) then FOnTimer(Self, LagCount);
end;procedure TCustomDXTimer.Finalize;
begin
if FInitialized then
begin
Suspend;
FInitialized := False;
end;
end;procedure TCustomDXTimer.Initialize;
begin
Finalize; if ActiveOnly then
begin
if Application.Active then
Resume;
end else
Resume;
FInitialized := True;
end;procedure TCustomDXTimer.Loaded;
begin
inherited Loaded;
if (not (csDesigning in ComponentState)) and FEnabled then
Initialize;
end;procedure TCustomDXTimer.Resume;
begin
FOldTime := TimeGetTime;
FOldTime2 := TimeGetTime;
Application.OnIdle := AppIdle;
end;procedure TCustomDXTimer.SetActiveOnly(Value: Boolean);
begin
if FActiveOnly<>Value then
begin
FActiveOnly := Value; if Application.Active and FActiveOnly then
if FInitialized and FActiveOnly then Suspend;
end;
end;procedure TCustomDXTimer.SetEnabled(Value: Boolean);
begin
if FEnabled<>Value then
begin
FEnabled := Value;
if ComponentState*[csReading, csLoading]=[] then
if FEnabled then Initialize else Finalize;
end;
end;procedure TCustomDXTimer.SetInterval(Value: Cardinal);
begin
if FInterval<>Value then
begin
FInterval := Max(Value, 0);
FInterval2 := Max(Value, 1);
end;
end;procedure TCustomDXTimer.Suspend;
begin
Application.OnIdle := nil;
end;