MicroSecond not Nano
See posts bellow, doesn't work on x64 and probably has issues on multicores and speed stepping processors.
if it has any chance of working it needs to lock the thread with setThreadMaskAffinity to a CPU or Core (though I don't know if that works or not) to try and counter the speed step issues it periodically recalculates the CPU frequency.
Code: Select all
;Attemps to produce a workable Microsecond timer / delay
;I don't have a multicore system or speed stepping cpu so I have no idea if it will be stable
;
;The timer needs to be initalised in the main thread as it will try to lock the thread to a core
;should avoid issues with Time Stamps being out of sync on multi cores
;It also spawns another thread to check the frequency periodically (may need a mutex)
;
;Turn debugger off
;
;set to 0 to test without beginTimePeriod higher accuracy
EnableExplicit
#CompareMilliSeconds = 1
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
#X64 = 0
CompilerElse
#X64 = 1
CompilerEndIf
CompilerIf #X64
Procedure.i CPUTime()
CompilerElse
Procedure.q CPUTime()
CompilerEndIf
!XOr eax, eax
!cpuid
!rdtsc
ProcedureReturn
EndProcedure
Global gthreadIDCheck.i,gtidCheckRun.i=1,bchangeFreq
CompilerIf #X64
Global gStartTime.i,gfrequency.i
Procedure.i CPUFrequency(intDelayTime.l=500)
CompilerElse
Global gStartTime.q,gfrequency.q
Procedure.q CPUFrequency(intDelayTime.l=500)
CompilerEndIf
Protected TimerHi.l,TimerLo.l,PriorityClass.l,Priority.l
PriorityClass = GetPriorityClass_(GetCurrentProcess_());
Priority = GetThreadPriority_(GetCurrentThread_());
SetPriorityClass_(GetCurrentProcess_(), #REALTIME_PRIORITY_CLASS);
SetThreadPriority_(GetCurrentThread_(), #THREAD_PRIORITY_TIME_CRITICAL);
Delay(10);
EnableASM
XOr eax, eax
!cpuid
rdtsc
mov TimerLo, eax
mov TimerHi, edx
Delay(intDelayTime);
XOr eax, eax
!cpuid
rdtsc
sub eax, TimerLo
sbb edx, TimerHi
mov TimerLo, eax
mov TimerHi, edx
DisableASM
SetThreadPriority_(GetCurrentThread_(),Priority);
SetPriorityClass_(GetCurrentProcess_(), PriorityClass);
ProcedureReturn TimerLo / (1000.0 * intDelayTime);
EndProcedure
Procedure CheckFreqency(CheckInterval.i=500)
While gtidCheckRun
gfrequency = CPUFrequency(CheckInterval)
bchangeFreq = 1
Delay(CheckInterval)
Wend
EndProcedure
Procedure KillTimer()
gtidCheckRun = 0
If IsThread(gthreadIDCheck)
WaitThread(gthreadIDCheck)
EndIf
EndProcedure
Procedure InitTimer(CheckInterval.i=500)
Protected mask.i=1
If Not IsThread(gthreadIDCheck)
SetThreadAffinityMask_(GetCurrentThread_(),mask)
gfrequency = CPUFrequency(CheckInterval)
gThreadIDCheck = CreateThread(@CheckFreqency(),CheckInterval)
EndIf
gStartTime = CPUTime()
ProcedureReturn gfrequency
EndProcedure
Procedure GetElapsedMicroSeconds()
CompilerIf #x64
Protected tt.i,dt.i
CompilerElse
Protected tt.q,dt.q
CompilerEndIf
tt=CPUTime()
dt = tt-gStartTime
If dt > 0
ProcedureReturn dt / gFrequency
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure DelayMicro(mtime.i)
CompilerIf #x64
Protected te.i, tn.i
CompilerElse
Protected te.q, tn.q
CompilerEndIf
te = GetElapsedMicroSeconds() + (mtime)
Repeat
Delay(0)
Until GetElapsedMicroSeconds() >= te
EndProcedure
;test----------------------------------
;Set #CompareMilliSeconds to 1 to compare MS, higher accuracy without TimeBeginPeriod
CompilerIf #CompareMilliSeconds
Global tt.TIMECAPS
timeGetDevCaps_(@tt, SizeOf(TIMECAPS))
timeBeginPeriod_(tt\wPeriodMin)
CompilerEndIf
Global sum1.i, sum2.i,ct.i,sta.i,elt.i,mst.i,a,el.i,avg1,avg2
OpenConsole()
Delay(100)
Global mfreq = InitTimer(500)
PrintN(Str(mfreq) + " MHz")
Repeat
sta = GetElapsedMicroSeconds()
CompilerIf #CompareMilliSeconds
mst = timeGetTime_()
CompilerEndIf
a=0
While a < 1000; should be ~1000 Micro Seconds or 1MS to compleate loop
DelayMicro(1)
a+1
Wend
elt = GetElapsedMicroSeconds() - sta
CompilerIf #CompareMilliSeconds
el = timeGetTime_() - mst
PrintN(Str(gfrequency) + " MHz " + Str(elt) + " MicroSeconds " + Str(el) + " MilliSeconds")
CompilerElse
If bchangeFreq
PrintN(Str(gfrequency) + " MHz " + Str(elt) + " MicroSeconds " +"frequency bump")
bchangeFreq = #False
Else
PrintN(Str(gfrequency) + " MHz " + Str(elt) + " MicroSeconds ")
EndIf
CompilerEndIf
ct+1
sum1 + elt
sum2 + el
Until Inkey()
avg1.i = sum1 / ct
CompilerIf #CompareMilliSeconds
avg2.i = sum2 / ct
PrintN("Averages " + Str(avg1) + " MicroSeconds " + Str(avg2) + " Milliseconds")
CompilerElse
PrintN("Averages " + Str(avg1) + " MicroSeconds ")
CompilerEndIf
KillTimer()
Input()