Reputation: 41
I have an old app written in Delphi BDS 2006. The app communicates with an external device via the serial port which requires a quite accureate timing on sending data. The timing must be 2ms, so I use WaitForSingleObject with timeout 2. This works fine on Windows 7, but in later Windows versions the timing becomes (very) inaccurate.
For testing purposes I wrote a simple testapp and executed on different windows versions to compare the results. Here is the code:
unit Unit24;
interface
uses
Windows, SysUtils, Classes, Forms, DateUtils;
type
TTH= class(TThread)
protected
procedure Execute; override;
end;
TForm24 = class(TForm)
procedure FormCreate(Sender: TObject);
private
S: string;
end;
var
Form24: TForm24;
implementation
{$R *.dfm}
procedure TTH.execute;
var
h: THandle;
t: TDateTime;
begin
h:= CreateEvent(nil, False, False, nil);
while not Terminated do
begin
t:= Now;
WaitForSingleObject(h, 2);
Form24.S:= Form24.S+ IntToStr(Round(MilliSecondSpan(Now, t)))+ ',';
end;
end;
procedure TForm24.FormCreate(Sender: TObject);
var
F : TextFile;
TH : TTH;
begin
TH:= TTH.Create(False);
Sleep(100);
TH.Terminate;
Application.Terminate;
AssignFile(F, 'res.log');
Rewrite(F);
Write(F, S);
CloseFile(F);
end;
end.
And here are the results on different OS:
win7: 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
win10: 11,16,15,16,15,15,16,
win11: 8,13,14,13,14,13,13,14,
win server 2022: 3,2,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,2,2,3,3,
As you can see, the result under win10 and win11 is very bad. Under 2022 there is an additional 1 msec on timing. Can anyone suggest a platform independent accurate timing solution?
p.s.
@whosrdaddy: thanks for the suggestion, but I think the problem I outlined is slightly different. I started working on this project under DOS, where I was completely master of the timing. The first Windows version was made for XP, the timing was really rough there (due to the low timing resolution), but with a little trickery we managed to overcome this. Then came Win7, which provided a stable and reliable timing, so we still use it nowadays, but it can't last forever because win7 is very outdated. Unfortunately, the latest operating systems carelessly handle both the timing (and the problem is not with the resolution in my opinion) and the management of serial ports, so external devices. Apparently, the answer I wrote earlier was a solution to the (timing) problem, but since the data arriving via the serial port sometimes reaches the application late (even though the buffers are turned off) so in this case there is no chance to reply within 5ms even if the timing is accurate. Because of this it did not prove to be reliable either. Finally, I decided to move the sensitive timings to an external device (ESP32), so the project will no longer vulnerable to the unreliability of windows. After that I think I should close this topic. Sorry for the long explanation and my bad english.
Upvotes: 1
Views: 103
Reputation: 1
Try like this:
....
Uses MMSystem;
....
timeBeginPeriod(1);
while not Terminated do
begin
t:= Now;
WaitForSingleObject(h, 2);
Form24.S:= Form24.S+ IntToStr(Round(MilliSecondSpan(Now, t)))+ ',';
end;
timeEndPeriod(1);
....
or
procedure TMyForm.FormCreate(Sender: TObject);
begin
timeBeginPeriod(1);
...
end;
....
procedure TMyForm.FormDestroy(Sender: TObject);
begin
timeEndPeriod(1);
...
end;
....
WaitForSingleObject(h, 2);
....
Upvotes: 0
Reputation: 41
it seems I managed to find a solution that is version independent - this post gave me the idea: multimedia timer
And here is the new code for test:
unit Unit24;
interface
uses
Windows, MMSystem, SysUtils, Classes, Forms, DateUtils;
type
TForm24 = class(TForm)
procedure FormCreate(Sender: TObject);
private
end;
var
Form24: TForm24;
implementation
{$R *.dfm}
var
S: string;
t: TDateTime;
procedure TimeCallBack(TimerID, Msg: Uint; dwUser, dw1, dw2: DWORD); pascal;
begin
S:= S+ IntToStr(Round(MilliSecondSpan(Now, t)))+ ',';
t:= Now;
end;
procedure TForm24.FormCreate(Sender: TObject);
var
F : TextFile;
mmRes: Integer;
begin
S:= '';
t:= Now;
mmRes:= TimeSetEvent(2, 0, @TimeCallBack, 0, TIME_PERIODIC);
Sleep(100);
TimeKillEvent(mmRes);
Application.Terminate;
AssignFile(F, 'res.log');
Rewrite(F);
Write(F, S);
CloseFile(F);
end;
end.
Upvotes: 1