Any Suggestions for a microsecond delay? I had a scheme in my old dos program where I would run a massive loop at the beginning of my program and time it with DOS 55mS ticks, then do the math and figure out how many times I needed to loop for a given delay.. worked great on DOS with nothing else using the processor, but completely invalid for windows. I see delay() and sleep() but they are both only good down to 1mS and I would like to go down to 1uS. Any suggestions on how to do this on windows with a console application?
Mailing lists / fpc-pascal / 2016-07
Microsecond Delay Suggestions?
15 messages · 4 people · –
- JJames Richters 7
- DDmitry Boyarintsev 5
- SSven Barth 2
- LLuca Olivetti 1
Reply by email Subscribe to this list Your subscription
Read-only archive. Use j and k to move between messages.
On Tue, Jul 26, 2016 at 11:16 AM, James Richters <
wrote: Any suggestions on how to do this on windows with a console application?
I don't have an answer, but I'm wondering what kind of task is that? Why do you need this 55mS tick?
thanks,
Dmitry
On Tue, Jul 26, 2016 at 11:19 AM, Dmitry Boyarintsev <
Show quoted text 11 lines
Maybe you want to look into QueryPerformanceCounter. https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx
thanks,
Dmitry
On Tue, Jul 26, 2016 at 11:21 AM, Dmitry Boyarintsev <
Show quoted text 7 lines
{$mode objfpc}{$H+}
uses
Windows;
const
MSInSec = 1000;
MCInSec = MSInSec * 1000;
NSInSec = MCInSec * 1000;
function LargeLoop: Integer;
var
i : integer;
begin
Result:=0;
for i:=0 to 1000 do
inc(Result);
//or you can verify Sleep(milliseconds)
//Sleep(100);
end;
var
fr : TLargeInteger;
ct : TLargeInteger;
af : TLargeInteger;
d : double;
diff : TLargeInteger;
begin
QueryPerformanceFrequency(fr);
writeln('Frequency: ' ,fr);
d:=(1/fr) * NSInSec;
writeln('Freq in Time: ', d:0:0, ' ns (roughly)');
QueryPerformanceCounter(ct);
LargeLoop;
QueryPerformanceCounter(af);
diff := af - ct;
writeln('Loop:');
writeln(' Ticks: ', diff);
writeln(' Time: ', ((diff/fr) * NSInSec):0:0,' ns' );
writeln(' ', ((diff/fr) * MCInSec):0:6,' mcs' );
writeln(' ', ((diff/fr) * MSInSec):0:6,' ms' );
end.On Tue, Jul 26, 2016 at 11:16 AM, James Richters < I don't have an answer, but I'm…
I don’t need a 55mS tick, there was a DOS timer that ticked every 55mS, which I used to calibrate a delayloop.. however I can’t use any of that on windows, I can’t even make the DOS function call, and even if I had a way to calibrate my delay loop, it would be meaningless because I have to share the processor with windows, so creating a delay by just looping a lot doesn’t work at all in windows… depending on what else is running I would get totally different time delays out of the loop.
What I need is a timer that I can specify in microseconds, a millisecond is too long. I am using it for timing to read in a string on a serial connection. My fastest baudrate is 250000, so at that rate I would need to delay only 36 microseconds. So I can’t use sleep () or delay () because they only go down to 1mS and that’s too slow.
Earlier message 18 lines
On Tue, Jul 26, 2016 at 11:21 AM, Dmitry Boyarintsev < {$mode objfpc}{$H+} uses Windows;…
That looks great. I’ll give it a try! Thank you very much for the suggestion and the example
Earlier message 56 lines
I don’t need a 55mS tick, there was a DOS timer that ticked every 55mS, which I used to…
On Tue, Jul 26, 2016 at 12:38 PM, James Richters <
Show quoted text 8 lines
Here's an example of Sleep with microseconds that I came up with
procedure SleepMcs(mcs: Int64);
var
ct : TLargeInteger;
fr : TLargeInteger;
af : TLargeInteger;
trg : TLargeInteger;
const
NanoInMicro = 1000;
begin
QueryPerformanceCounter(ct);
QueryPerformanceFrequency(fr);
trg:=round(mcs * NanoInMicro / (NSInSec/fr));
repeat
QueryPerformanceCounter(af);
until trg<=(af-ct);
end;I'm hoping that there's a better solution out there, because this procedure will end up with 100% CPU load.
I doubt it's possible to unload the CPU, since the windows scheduler is in milliseconds accuracy.
May be a lower (kernel/driver) level allows that.
thanks,
Dmitry
I think there may be a solution with this performance counter for my purposes… here’s what I’m trying to accomplish:
I’m trying to read a string from a serial port until I get a linefeed, but I don’t want it to get stuck in an endless loop which could happen if:
A: nothing is ever received
B: garbage is being received due to bad connection or baudrate mismatch.. etc..
So I keep reading the port in a loop until
A: I try 10 times and still get nothing
B: I get the data I’m looking for ending with a #10
C: I get too much data, then I stop after 254 bytes, the response should never be that long, so I’ll flag that as garbage.
D: I try 1000 times and still do not have a #10 but I also do not have 254+ bytes
For fast baud rates, this works fine, but for slow baudrates, I reach 1000 tries way before the string is finished, so I planned on putting in a delay calculated from the baudrate… however I think I can take a different approach here. I don’t really care the exact amount of time between retries, I just want to try for a long enough length of total time before giving up, so I don’t get stuck in a loop. Perhaps I can read QueryPerformanceCounter(ct); during the loop and if it exceeds the set amount of time, then use that to end the loop and give up.
Here’s my function as it is.. fyi, my entire project is in turbo pascal compatibility mode:
Function ReadSerial(SerPortNum:Byte):String;
Var
Loopcount:Word;
CharCount:Byte;
InputString:String;
InputLetter:Char;
Begin
LoopCount:=0;
CharCount:=0;
InputString:='';
repeat
Status:= SerRead(SerialHandle[SerPortNum], Inputletter, 1);
if (status > 0) {and ((InputLetter#10) AND (InputLetter#13))} then
begin
Inc(CharCount);
case InputLetter of
' ':InputString:=InputString+'.';
#10:InputString:=InputString+'#10';
#13:InputString:=InputString+'#13';
else
InputString:=InputString+(InputLetter);
end;
end;
Inc(Loopcount);
{need delay here for slow baud rates}
Until (InputLetter=#10) OR ((Loopcount>10) AND (InputString='')) OR (CharCount>254) OR (Loopcount>1000);
ReadSerial:=InputString;
end;Earlier message 44 lines
On Tue, Jul 26, 2016 at 12:38 PM, James Richters < Here's an example of Sleep with…
Dmitry Boyarintsev wrote 36 lines
An alternative would be NtDelayExecution from unit jwanative. Its interval argument is in multiples of 100ns and is essentially what Windows' Sleep() uses internally.
Note: The first argument "Alertable" determines whether the function can be interrupted by NtAlertThread (and whatever Windows API functions use that internally).
Regards,
Sven
I think there may be a solution with this performance counter for my purposes… …
On Tue, Jul 26, 2016 at 1:58 PM, James Richters <
Show quoted text 5 lines
Well, Windows provides a different means of waiting (and waiting with time out) for the Serial port communication.
The whole topic is discussed here:
https://msdn.microsoft.com/en-us/library/ff802693.aspx#serial_topic1
I'm thinking that using performance counter is a little of over-engineering
thanks,
Dmitry
An alternative would be NtDelayExecution from unit jwanative. Its interval argument is in…
An alternative would be NtDelayExecution from unit jwanative. Its interval
argument is in multiples of 100ns
and is essentially what Windows' Sleep() uses internally. Note: The first argument "Alertable" determines whether the function can be
interrupted by NtAlertThread
(and whatever Windows API functions use that internally).
This sounds like it would work, but I can't figure out how to use it. Do you have an example?
I've tried just a quick test like this:
uses
windows,jwanative;
Var
DelayInterval : PLarge_Integer;
Variable1:integer;
begin
DelayInterval:=1;
Variable1:=NtDelayExecution(true,DelayInterval);
end.It won't let me set DelayInterval with a constant, I get "Incompatible Types: got "shortint" expected "Plarge_Integer"
I really don't undertand these variable types that it wants.
James Richters wrote 24 lines
You need to declare a LargeInteger and pass the pointer to it to NtDelayExecution(). LargeInteger itself is declared as a case-record consisting of two 32-bit values (LowPart and HighPart) and a 64-bit value (QuadPart). Best is you assign your value to QuadPart, as that sets the record in one go.
Oh and I forgot to mention that you need to use a negative value for relative sleeps (otherwise you'll wait until that time from the system start on has passed, so "1" would be a no-op basically ;) )
For an example see the implementation of Sleep in the SysUtils unit of the NativeNT target: rtl/nativent/sysutils.pp (near the end of the file)
Regards,
Sven
Thank You I found the sleep procedure.... Thank you for explaining that it was looking for a pointer, that was helpful. A little cutting and pasting and now I have 3 more ways to sleep... Sleep_S will probably be sufficient and is the most flexible, because if you have 1,000,000,000 seconds you don't need precision down to 100nS.. but you have it anyway, you could specify 1000000000.0000001 seconds and it would work... it comes out to 10000000000000001 intervals... I'm not going to wait over 31 years to test it though :)
Thanks for the help everyone, now I can adjust my delay from 36uS for baudrates of 250000 all the way to 30mS for a baudrate of 300. I know sleep isn't exact, it's more of a minimum and then you need to also wait for the processor to get back to you, but I think it will be close enough without excessive delays.
I posted it on github if anyone happens across this doing a search they can find it here:
https://github.com/Zaaphod/FPC-Delay
You need to declare a LargeInteger and pass the pointer to it to
NtDelayExecution(). LargeInteger itself is declared as a case-record
consisting of two 32-bit values (LowPart and HighPart) and a 64-bit value
(QuadPart). Best is you assign your value to QuadPart, as
that sets the record in one go. Oh and I forgot to mention that you need to use a negative value for
relative sleeps (otherwise you'll wait until that time from the
system start on has passed, so "1" would be a no-op basically ;) ) For an example see the implementation of Sleep in the SysUtils unit of the
NativeNT target: rtl/nativent/sysutils.pp (near the end of the file)
Regards, Sven
I think there may be a solution with this performance counter for my purposes… …
James Richters wrote 6 lines
Look at synaser, its TBlockSerial class has a RecvTerminated method that does just that.
http://synapse.ararat.cz/doc/help/synaser.TBlockSerial.html#RecvTerminated
https://sourceforge.net/p/synalist/code/HEAD/tree/trunk/
Bye
Look at synaser, its TBlockSerial class has a RecvTerminated method that
does just that.
Show quoted text 5 lines
Thank you for the suggestion and the links, that does look like it would take care of the issue for me.
James
