Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2016-07

Microsecond Delay Suggestions?

15 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

James Richters

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?

Dmitry Boyarintsev
to James Richters

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

Dmitry Boyarintsev
to Dmitry Boyarintsev

On Tue, Jul 26, 2016 at 11:19 AM, Dmitry Boyarintsev <

Show quoted text 11 lines
wrote: 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?

Maybe you want to look into QueryPerformanceCounter. https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx

thanks,
Dmitry

Dmitry Boyarintsev
to Dmitry Boyarintsev

On Tue, Jul 26, 2016 at 11:21 AM, Dmitry Boyarintsev <

Show quoted text 7 lines
wrote: Maybe you want to look into QueryPerformanceCounter. https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx Here's an example:
{$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.
James Richters
to Dmitry Boyarintsev: 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
From: On Behalf Of Dmitry Boyarintsev Sent: Tuesday, July 26, 2016 11:19 AM To: FPC-Pascal users discussions Subject: Re: [fpc-pascal] Microsecond Delay Suggestions? 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
James Richters
to Dmitry Boyarintsev: 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
From: On Behalf Of Dmitry Boyarintsev Sent: Tuesday, July 26, 2016 11:45 AM To: FPC-Pascal users discussions Subject: Re: [fpc-pascal] Microsecond Delay Suggestions? On Tue, Jul 26, 2016 at 11:21 AM, Dmitry Boyarintsev < > wrote: Maybe you want to look into QueryPerformanceCounter. https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx <https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904%28v=vs.85%29.aspx> Here's an example: {$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.
Dmitry Boyarintsev
to James Richters: 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
wrote: 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.

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

James Richters
to Dmitry Boyarintsev

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
From: On Behalf Of Dmitry Boyarintsev Sent: Tuesday, July 26, 2016 1:05 PM To: FPC-Pascal users discussions Subject: Re: [fpc-pascal] Microsecond Delay Suggestions? On Tue, Jul 26, 2016 at 12:38 PM, James Richters < > wrote: 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. 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
Sven Barth
to Dmitry Boyarintsev: On Tue, Jul 26, 2016 at 12:38 PM, James Richters < Here's an example of Sleep with…
Dmitry Boyarintsev wrote 36 lines
On Tue, Jul 26, 2016 at 12:38 PM, James Richters < > wrote: 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. 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.

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

Dmitry Boyarintsev
to James Richters: 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
wrote: I think there may be a solution with this performance counter for my purposes… here’s what I’m trying to accomplish:

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

James Richters
to Sven Barth: 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.

Sven Barth
to James Richters
James Richters wrote 24 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). 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.

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

James Richters
to Sven Barth

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
Luca Olivetti
to James Richters: I think there may be a solution with this performance counter for my purposes… …
James Richters wrote 6 lines
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..

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

Luca