Timer & Threads
Re: Timer & Threads
If SizeOf( IOCtr.PortBit ) <= SizeOf( Integer ), 4 or 8 depending on used compiler (32-bit or 64-bit), there is no risk to not use mutex, because there is normally no possibility of transitory state when the machine instruction modifies the IOCtrl compared to its previous state.
Re: Timer & Threads
70 and 66.
You guys rock!
Thank you fxm for all the help and coding wisdom you give to others.
You guys rock!
Thank you fxm for all the help and coding wisdom you give to others.
Re: Timer & Threads
Hi All
tinram, when I was 20 I thought my father was ancient at 45. Now at 70 I think 85 is old.
fxm, I am using Linux 64 bit compiler, and I am told (on this forum) that it all gets converted to Long anyway.
So the IOCtrl udt is as follows.
Today I ran the system for 4 hours on the actual production machine without using MutexLock, and no obvious problems.
I will let the system run like this and see what happens.
I do really appreciate your help on this, especially since this is my first foray into Threading.
Regards
Edit: The actual code that matters. Please point out any improvements.
Note that the whole purpose of this code is to have an Ice Cream Nozzle open for a fixed time.
The very stable output pressure from the Freezer guarantees accuracies of a couple of grams.
At the same time the other 23 output bits are also set/reset plus 24 bits of input are read, from here.
tinram, when I was 20 I thought my father was ancient at 45. Now at 70 I think 85 is old.
fxm, I am using Linux 64 bit compiler, and I am told (on this forum) that it all gets converted to Long anyway.
So the IOCtrl udt is as follows.
Code: Select all
Type IOCtrls
Manuel As Long
IOValue As Long
PortBit(0 To 47) As Long
NozzleBit As Long
HeartBeat As Long
OldBeat As Long
InputDone As Long
End Type
I will let the system run like this and see what happens.
I do really appreciate your help on this, especially since this is my first foray into Threading.
Regards
Edit: The actual code that matters. Please point out any improvements.
Note that the whole purpose of this code is to have an Ice Cream Nozzle open for a fixed time.
The very stable output pressure from the Freezer guarantees accuracies of a couple of grams.
At the same time the other 23 output bits are also set/reset plus 24 bits of input are read, from here.
Code: Select all
Sub USBController
'----------------------------------------------------------------------
'This routine does all comms to IA-3124, if Unit #0
'All Inputs and Outputs are set here as well.
'First Set Output Bits , then Read I/O bits
'This Comm's method holds loop time stable within 2 uSec
'To do a complete Tx / Rx takes 24 mSec.
'-----------------------------------------------------------------------
USBTimer.mSec = (Timer - USBTimer.CalTime) * 1000
With USBControl
.Handle = USBInit.Handle
Select Case .StepNbr
Case -1
Sleep 1
If USBInit.InitOK > 0 Then .StepNbr = 1
Case 0
.StepNbr = -1
Case 1
.OPValue = 0
If IOCtrl.PortBit(IOCtrl.NozzleBit) > 0 Then 'If Main is calling for Nozzle output
If Nozzle.StepNbr = 2 Then 'for the first time this cycle
If .NozzleTime < 1 Then 'and we haven't started a time yet
.NozzleTime = USBTimer.mSec 'then set the timer.
EndIf
EndIf
If .NozzleTime > 0 Then 'If Timer is set
If (USBTimer.mSec - .NozzleTime) >= (Settings.CreamOpenTime - 50) Then
.Crit_Time = 1 'we are close to critical time slot
EndIf 'so halt all usb commands.
If (USBTimer.mSec - .NozzleTime) > Settings.CreamOpenTime Then
If .Crit_Time = 1 Then 'Time's up , so enable usb commands again
IOCtrl.PortBit(IOCtrl.NozzleBit) = 0 'and turn the Nozzle bit off.
.NozzleTime = 0 'cancel Nozzle timer
.Crit_Time = -1 'allow all usb commands again.
EndIf
EndIf
EndIf
EndIf
Dim As Long BitCount
For BitCount = 0 To 23 'for the 24 O/P bits
If IOCtrl.PortBit(BitCount) > 0 Then 'set the output value
.OPValue = BitSet(.OPValue,BitCount)
Else
.OPValue = BitReset(.OPValue,BitCount)
EndIf
Next
'-----------------WRITE OUTPUT BITS--------------------------------------
Dim as String OPStr
OPStr = Str(Hex(.OPValue)) 'convert it to a string
.LenStr = Len(OPStr)
While Len(OPStr) < 6
OPStr = "0" + OPStr 'make sure the length is correct
Wend
If .OPValue = 0 then OPStr = "000000"
If LOC(.Handle) > 0 Then 'Tidy up any left over chr's
.Temp = Input(Loc(.Handle),#.Handle)
EndIf
If .OldOPValue = .OPValue Then
.StepNbr = 3 'Don't re-Tx the same data,
If USBInit.InitOK = 1 Then
USBInit.InitOK = 2 'go for inputs instead
.StepNbr = 2
EndIf
Else
.StepNbr = 2
EndIf
.OPStr = OPStr
If .Crit_Time > 0 Then .StepNbr = -1 'loop back if we are in Critical Time Zone
Case 2
.TxStr = "!002" + .OPStr
Print #.Handle,.TxStr + Chr(&H0D); 'send "Set Data bits" command
.OldOPValue = .OPValue 'now that Sent = Requested, sync them
.RxStr = ""
.LastTx = USBTimer.mSec
.StepNbr = 3
Case 3 'Ask for Inputs
Sleep 1
If USBTimer.mSec - .LastTx >= 9 Then 'Typically takes 10 mSec
.TxStr = "?002" 'before we can Tx again
Print #.Handle,.TxStr + CHR(&H0D);
.RxStr = ""
.StartTime = USBTimer.mSec
.StepNbr = 4
.LastTx = USBTimer.mSec
EndIf
Case 4 'Wait for Input String takes 12 mSec, so may as well sleep for 5.
Sleep 5
If LOC(.Handle) > 0 Then
Do
.Buffer = Input(1,#.Handle)
.RxStr = .RxStr + .Buffer
If .Buffer = Chr(&H0d) Then '&h0d means end of Tx
If InStr(.RxStr,"_") > 0 Then 'and response has valid Chr in it
.StartTime = USBTimer.mSec
.StepNbr = 5
.OldBeat = .HeartBeat 'let screen led know
If .OldBeat = .HeartBeat Then
IOCtrl.OldBeat = IOCtrl.HeartBeat
EndIf
Exit Do
Else
.StepNbr = -1
.ErrCount3 += 1
Exit Do
EndIf
EndIf
If USBTimer.mSec - .StartTime > 150 Then 'escape route
.ErrCount4 += 1
.StepNbr = -1
Exit Do
EndIf
USBTimer.mSec = (Timer - USBTimer.CalTime) * 1000
Loop
EndIf
Case 5 'Now sort out the input data and assign to IOCtrl.PortBit(n)
Dim As Long CPos
CPos = InStr(.RxStr,"_")
If CPos > 0 Then
USBInit.InitOK = 2
.IPStr = "&H" + Mid(.RxStr,CPos + 1,12)
.IOValue = Val(.IPStr)
.IOBits = .IOValue
Dim As Long BitCount
For BitCount = 24 To 47 'Only do Input bits
If Bit(.IOValue,BitCount) Then
IOCtrl.PortBit(BitCount) = 1
Else
IOCtrl.PortBit(BitCount) = 0
EndIf
Next
EndIf
.StepNbr = 1
End Select
End With
End Sub
Sub USBthread( ByVal userdata As Any Ptr )
USBTimer.CalTime = Timer * 1000
Sleep 100
USBInitialize
Sleep 100
Do
USBController '1 successful Tx/Rx loop takes 24 mSec
If USBFlag > 0 Then Exit Do 'Dim Shared Quit Flag from Main.
Loop
End Sub
Re: Timer & Threads
I'm quite positive, that this is a sort of misunderstanding ... more likely: a Integer.Dinosaur wrote:I am using Linux 64 bit compiler, and I am told (on this forum) that it all gets converted to Long anyway.
For starters I'd improve on the type definition. At the moment you're really wasting memory (all those Long's).
Flag's (or Switches, e.g. on/off) are best coded as Boolean's = 1 Byte vs. Long = 4 Byte. This *weights in* mainly
on the used array ...
Code: Select all
Type IOCtrls
IOValue As Long ' maybe a Byte/UByte does the trick, can't say ...(don't know required range)
PortBit(0 To 47) As Boolean
Manuel As Boolean
NozzleBit As Boolean
HeartBeat As Boolean
OldBeat As Boolean
InputDone As Boolean
End Type
Code: Select all
Case 5 'Now sort out the input data and assign to IOCtrl.PortBit(n)
Dim As Long CPos
CPos = InStr(.RxStr,"_")
If CPos > 0 Then
USBInit.InitOK = 2
.IPStr = "&H" + Mid(.RxStr,CPos + 1,12)
.IOValue = Val(.IPStr)
.IOBits = .IOValue
'Dim As Long BitCount ' not needed + using fastest loop-counter: UInteger
For BitCount As UInteger = 24 To 47 'Only do Input bits
If Bit(.IOValue,BitCount) Then
IOCtrl.PortBit(BitCount) = 1 ' could be made TRUE (for Boolean)
Else
IOCtrl.PortBit(BitCount) = 0 ' could be made FALSE (for Boolean)
EndIf
Next
EndIf
.StepNbr = 1
E.g. the type definition of USBControl ...
We don't have the *big picture*, as of now!
Re: Timer & Threads
Hi All
MrSwiss , I have tested changing the udt elements to the minimum size suited to the use in my program.
It did reduce the size of the udt significantly, but I could not detect any performance improvements.
I didn't use Boolean as I like to see zeroes & Ones, but the Byte is the same size as Boolean.
Anyway, I think I have gone as far as I can go on this, but posted below is the final product.
So, basically I start another thread after initializing my main program.
The Main program reads/sets IOCtrl.PortBits(n) according to the machine needs.
The usbThread runs until the Main program quits, which sets the usbFlag that causes the thread to end.
The Main Quit routine has a ThreadWait(Handle) to ensure the thread has terminated.
There are only two references to Main udt's in the usb thread. (Nozzle.StepNbr & IOCtrl.PortBits)
I tried making a copy of IOCtrl udt and using that in the usb Thread, and then copying it back, but could not get that to work,
and anyway copying the udt back & to probably takes more resources then what I am doing now.
MrSwiss , I have tested changing the udt elements to the minimum size suited to the use in my program.
It did reduce the size of the udt significantly, but I could not detect any performance improvements.
I didn't use Boolean as I like to see zeroes & Ones, but the Byte is the same size as Boolean.
Anyway, I think I have gone as far as I can go on this, but posted below is the final product.
So, basically I start another thread after initializing my main program.
The Main program reads/sets IOCtrl.PortBits(n) according to the machine needs.
The usbThread runs until the Main program quits, which sets the usbFlag that causes the thread to end.
The Main Quit routine has a ThreadWait(Handle) to ensure the thread has terminated.
There are only two references to Main udt's in the usb thread. (Nozzle.StepNbr & IOCtrl.PortBits)
I tried making a copy of IOCtrl udt and using that in the usb Thread, and then copying it back, but could not get that to work,
and anyway copying the udt back & to probably takes more resources then what I am doing now.
Code: Select all
Main udt
Type IOCtrls
Manuel As Byte 'indicates manual button used to set O/P
IOValue As ULong '24 bits
PortBit(0 To 47) As Byte 'bits Read 0 or 1
NozzleBit As Byte '0 or 1
HeartBeat As Byte 'Led on screen = 0 or 1
OldBeat As Byte '0 or 1
InputDone As Byte '0 to 10
End Type
Dim Shared IOCtrl As IOCtrls
usb Thread udt's
Type USBInits
Handle As Byte '1 to ?
InitOK As Byte '1 to 2
StartTime As ULong 'Timer
Buffer As String * 1
TxStr As String * 15
RxStr As ZString * 15
End Type
Dim Shared USBInit as USBInits
Type USBControls
StepNbr As Byte '-1 to 100
Crit_Time As Byte '-1 or +1
InputDone As Byte '1 to 10
Handle As Byte '1 to ?
CreamOpenTime As Integer '300 to 750
NozzleTime As ULong 'Timer value
LastTx As ULong 'Timer
StartTime As ULong 'Timer
IOValue As ULong '24 bit value
OPValue As ULong '24 bit value
OldOPValue As ULong '24 bit value
IOBits As ULong '24 bit value
Buffer As String * 1
TxStr As String * 15
RxStr As String * 15
Temp As String * 15
IPStr As String * 15
OPStr As String * 15
End Type
Dim Shared USBControl as USBControls
Type USBTimers
mSec As ULong
StartTime AS ULong
CalTime As ULong
End Type
Dim Shared USBTimer As USBTimers
declare sub usbinitialize()
declare sub usbcontroller()
declare sub USBThread (Byval Userdata as Any Ptr)
usb Thread Routines.
Sub USBInitialize
'-----------------------------------------------------------
'Open Com Port and Test controller,if Err Exit Sub
'Send test Tx to IA-3124
'Check the Receive String.
'-----------------------------------------------------------
Dim As Byte CP
USBTimer.mSec = (Timer - USBTimer.CalTime) * 1000
With USBInit
'First see if SYMLINK File was created (Slow PC causes delay)
.StartTime = USBTimer.mSec
Do
If FileExists ("/dev/IA3125") Then 'Ok it is there
Print "Waited;";USBTimer.mSec - .StartTime
Exit Do
EndIf
USBTimer.mSec = (Timer - USBTimer.CalTime) * 1000
If (USBTimer.mSec - .StartTime) > 60000 then 'if not there after 1 Min
.InitOK = 0
Close #CP
Exit Sub 'and leave this Sub.
EndIf
Loop
Shell "echo ******** | sudo -S udevadm trigger" 'USB devices recognised, so now re-read .rules
Sleep 500
shell "stty -F /dev/IA3125 speed 19200 -clocal -hupcl" 'set Baud rate
Sleep 500
CP = FreeFile
.Handle = CP 'Open the port by it's SYMLINK Name
Open Com "/dev/IA3125:19200,n,8,1,cs0,ds0,cd0,rs" For Binary As #CP
Sleep 500
If ERR > 0 THEN
.InitOK = 0
Close #CP
Exit Sub
Else
If PUT (#CP, , "!00542" & CHR(&H0D)) Then 'set up board for "No Replies" to O/P commands
.InitOK = 0
Close #CP
Exit Sub
EndIf
END If
USBTimer.mSec = (Timer - USBTimer.CalTime) * 1000
.StartTime = USBTimer.mSec
Do
USBTimer.mSec = (Timer - USBTimer.CalTime) * 1000
If LOC(.Handle) > 0 Then
.Buffer = Input(1,#.Handle)
.RxStr = .RxStr + .Buffer
If .Buffer = Chr(&H0d) Then 'no more coming
If InStr(.RxStr,"|42") > 0 Then 'if response is valid
.InitOK = 1
Print "Init Done" 'we are good to go
Print #CP, "!002000000" + Chr(&h0d) 'let's turn all O/P off.
Exit Do
Endif
Endif
EndIf
If (USBTimer.mSec - .StartTime) > 3000 Then 'If it takes to long
Print "RxStr + Len(RxStr);";.RxStr, Len(.RxStr)
Print "Err mSec;";USBTimer.mSec
Print "Elapsed;";USBTimer.mSec - .StartTime
Print USBTimer.mSec-.StartTime
.InitOK = 0
Exit Sub
Endif
Loop
End With
End Sub
Sub USBController
'----------------------------------------------------------------------
'This routine does all comms to IA-3124, if Unit #0
'All Inputs and Outputs are set here as well.
'First Set Output Bits , then Read I/O bits
'This Comm's method holds loop time stable within 2 uSec
'To do a complete Tx / Rx takes 24 mSec.
'-----------------------------------------------------------------------
USBTimer.mSec = (Timer - USBTimer.CalTime) * 1000
With USBControl
.Handle = USBInit.Handle
Select Case .StepNbr
Case -1
Sleep 1
If USBInit.InitOK > 0 Then .StepNbr = 1
Case 0
.StepNbr = -1
Case 1
.OPValue = 0
If IOCtrl.PortBit(IOCtrl.NozzleBit) > 0 Then 'If Main is calling for Nozzle output
If Nozzle.StepNbr = 2 Then 'for the first time this cycle
If .NozzleTime < 1 Then 'and we haven't started a time yet
.NozzleTime = USBTimer.mSec 'then set the timer.
EndIf
EndIf
If .NozzleTime > 0 Then 'If Timer is set
If (USBTimer.mSec - .NozzleTime) >= (.CreamOpenTime - 50) Then
.Crit_Time = 1 'we are close to critical time slot
EndIf 'so halt all usb commands.
If (USBTimer.mSec - .NozzleTime) > .CreamOpenTime Then
If .Crit_Time = 1 Then 'Time's up
IOCtrl.PortBit(IOCtrl.NozzleBit) = 0 'turn the Nozzle bit off.
.NozzleTime = 0 'cancel Nozzle timer
.Crit_Time = -1 'allow all usb commands again.
EndIf
EndIf
EndIf
EndIf
Dim As Byte BitCount
For BitCount = 0 To 23 'for the 24 O/P bits
If IOCtrl.PortBit(BitCount) > 0 Then 'set the output value
.OPValue = BitSet(.OPValue,BitCount)
Else
.OPValue = BitReset(.OPValue,BitCount)
EndIf
Next
'-----------------WRITE OUTPUT BITS--------------------------------------
Dim as String OPStr
OPStr = Str(Hex(.OPValue)) 'convert it to a string
While Len(OPStr) < 6
OPStr = "0" + OPStr 'make sure the length is correct
Wend
If .OPValue = 0 then OPStr = "000000"
If LOC(.Handle) > 0 Then 'Tidy up any left over chr's
.Temp = Input(Loc(.Handle),#.Handle)
EndIf
If .OldOPValue = .OPValue Then
.StepNbr = 3 'Don't re-Tx the same data,
If USBInit.InitOK = 1 Then
USBInit.InitOK = 2 'go for inputs instead
.StepNbr = 2
EndIf
Else
.StepNbr = 2
EndIf
.OPStr = OPStr
If .Crit_Time > 0 Then .StepNbr = -1 'loop back if no data change
Case 2
.TxStr = "!002" + .OPStr
Print #.Handle,.TxStr + Chr(&H0D); 'send "Set Data bits" command
.OldOPValue = .OPValue
.RxStr = ""
.LastTx = USBTimer.mSec
.StepNbr = 3
Case 3 'Ask for Inputs
Sleep 1
If USBTimer.mSec - .LastTx >= 9 Then 'Typically takes 10 mSec
.TxStr = "?002" 'before we can Tx again
Print #.Handle,.TxStr + CHR(&H0D);
.RxStr = ""
.StartTime = USBTimer.mSec
.StepNbr = 4
.LastTx = USBTimer.mSec
EndIf
Case 4 'Wait for Input String takes 12 mSec, so may as well sleep for 5.
Sleep 5
If LOC(.Handle) > 0 Then
Do
.Buffer = Input(1,#.Handle)
.RxStr = .RxStr + .Buffer
If .Buffer = Chr(&H0d) Then
If InStr(.RxStr,"_") > 0 Then
.StartTime = USBTimer.mSec
.StepNbr = 5 'coming here means a good Rx
IOCtrl.OldBeat = IOCtrl.HeartBeat
Exit Do
Else
.StepNbr = -1
Exit Do
EndIf
EndIf
If (USBTimer.mSec - .StartTime) > 150 Then 'escape route
.StepNbr = -1
Exit Do
EndIf
USBTimer.mSec = (Timer - USBTimer.CalTime) * 1000
Loop
EndIf
Case 5 'Now sort out the input data and assign to IOCtrl.PortBit(n)
Dim As Byte CPos
CPos = InStr(.RxStr,"_")
If CPos > 0 Then
USBInit.InitOK = 2
.IPStr = "&H" + Mid(.RxStr,CPos + 1,12)
.IOValue = Val(.IPStr)
.IOBits = .IOValue
Dim As Byte BitCount
For BitCount = 24 To 47 'Only do Input bits
If Bit(.IOValue,BitCount) Then
IOCtrl.PortBit(BitCount) = 1
Else
IOCtrl.PortBit(BitCount) = 0
EndIf
Next
EndIf
.StepNbr = 1
End Select
End With
End Sub
Sub USBthread( ByVal userdata As Any Ptr )
USBTimer.CalTime = Timer * 1000
Sleep 100
USBInitialize
Sleep 100
Do
USBController '1 successful Tx/Rx loop takes 24 mSec
If USBFlag > 0 Then Exit Do 'Dim Shared Quit Flag from Main.
Loop
End Sub
Re: Timer & Threads
@Dinosaur,
made a couple of changes in:
This should improve the speed (however, I can't say whether it'll be of any significance).
Some *Do ... Loop* changed to *While ... Wend*, used UInteger in *For* loops (for speed!).
Also added some Sleep in *USBThread*.
the modified code:
made a couple of changes in:
- USBController()
- USBThread( ByVal userdata As Any Ptr )
This should improve the speed (however, I can't say whether it'll be of any significance).
Some *Do ... Loop* changed to *While ... Wend*, used UInteger in *For* loops (for speed!).
Also added some Sleep in *USBThread*.
the modified code:
Code: Select all
Sub USBController
'----------------------------------------------------------------------
'This routine does all comms to IA-3124, if Unit #0
'All Inputs and Outputs are set here as well.
'First Set Output Bits , then Read I/O bits
'This Comm's method holds loop time stable within 2 uSec
'To do a complete Tx / Rx takes 24 mSec.
'-----------------------------------------------------------------------
USBTimer.mSec = (Timer - USBTimer.CalTime) * 1000
With USBControl
.Handle = USBInit.Handle
Select Case .StepNbr
Case -1
Sleep 1
If USBInit.InitOK > 0 Then .StepNbr = 1
Case 0
.StepNbr = -1
Case 1
.OPValue = 0
If IOCtrl.PortBit(IOCtrl.NozzleBit) > 0 Then 'If Main is calling for Nozzle output
If Nozzle.StepNbr = 2 Then 'for the first time this cycle
If .NozzleTime < 1 Then 'and we haven't started a time yet
.NozzleTime = USBTimer.mSec 'then set the timer.
EndIf
EndIf
If .NozzleTime > 0 Then 'If Timer is set
If (USBTimer.mSec - .NozzleTime) >= (.CreamOpenTime - 50) Then
.Crit_Time = 1 'we are close to critical time slot
EndIf 'so halt all usb commands.
If (USBTimer.mSec - .NozzleTime) > .CreamOpenTime Then
If .Crit_Time = 1 Then 'Time's up
IOCtrl.PortBit(IOCtrl.NozzleBit) = 0 'turn the Nozzle bit off.
.NozzleTime = 0 'cancel Nozzle timer
.Crit_Time = -1 'allow all usb commands again.
EndIf
EndIf
EndIf
EndIf
'Dim As Byte BitCount <-- no way, slow
For BitCount As UInteger = 0 To 23 'for the 24 O/P bits
If IOCtrl.PortBit(BitCount) > 0 Then 'set the output value
.OPValue = BitSet(.OPValue,BitCount)
Else
.OPValue = BitReset(.OPValue,BitCount)
EndIf
Next
'-----------------WRITE OUTPUT BITS--------------------------------------
Dim as String OPStr = Str(Hex(.OPValue)) 'convert it to a string
'OPStr
While Len(OPStr) < 6
OPStr = "0" + OPStr 'make sure the length is correct
Wend
If .OPValue = 0 then OPStr = "000000"
If LOC(.Handle) > 0 Then 'Tidy up any left over chr's
.Temp = Input(Loc(.Handle),#.Handle)
EndIf
If .OldOPValue = .OPValue Then
.StepNbr = 3 'Don't re-Tx the same data,
If USBInit.InitOK = 1 Then
USBInit.InitOK = 2 'go for inputs instead
.StepNbr = 2
EndIf
Else
.StepNbr = 2
EndIf
.OPStr = OPStr
If .Crit_Time > 0 Then .StepNbr = -1 'loop back if no data change
Case 2
.TxStr = "!002" + .OPStr
Print #.Handle,.TxStr + Chr(&H0D); 'send "Set Data bits" command
.OldOPValue = .OPValue
.RxStr = ""
.LastTx = USBTimer.mSec
.StepNbr = 3
Case 3 'Ask for Inputs
Sleep 1
If USBTimer.mSec - .LastTx >= 9 Then 'Typically takes 10 mSec
.TxStr = "?002" 'before we can Tx again
Print #.Handle,.TxStr + CHR(&H0D);
.RxStr = ""
.StartTime = USBTimer.mSec
.StepNbr = 4
.LastTx = USBTimer.mSec
EndIf
Case 4 'Wait for Input String takes 12 mSec, so may as well sleep for 5.
Sleep 5 ' why not 10 ???
'If LOC(.Handle) > 0 Then
'Do
While LOC(.Handle) > 0 ' the "> 0" isn't really needed: loop ends at = 0
.Buffer = Input(1,#.Handle)
.RxStr = .RxStr + .Buffer
If .Buffer = Chr(&H0d) Then
If InStr(.RxStr,"_") > 0 Then
.StartTime = USBTimer.mSec
.StepNbr = 5 'coming here means a good Rx
IOCtrl.OldBeat = IOCtrl.HeartBeat
Exit Do
Else
.StepNbr = -1
Exit Do
EndIf
EndIf
If (USBTimer.mSec - .StartTime) > 150 Then 'escape route
.StepNbr = -1
Exit Do
EndIf
USBTimer.mSec = (Timer - USBTimer.CalTime) * 1000
'Loop
Wend
'EndIf
Case 5 'Now sort out the input data and assign to IOCtrl.PortBit(n)
Dim As Byte CPos = InStr(.RxStr,"_")
'CPos
If CPos > 0 Then
USBInit.InitOK = 2
.IPStr = "&H" + Mid(.RxStr,CPos + 1,12)
.IOValue = Val(.IPStr)
.IOBits = .IOValue
'Dim As Byte BitCount <-- no way, slow
For BitCount As UInteger = 24 To 47 'Only do Input bits
If Bit(.IOValue,BitCount) Then
IOCtrl.PortBit(BitCount) = 1
Else
IOCtrl.PortBit(BitCount) = 0
EndIf
Next
EndIf
.StepNbr = 1
End Select
End With
End Sub
Sub USBthread( ByVal userdata As Any Ptr )
USBTimer.CalTime = Timer * 1000
Sleep 100
USBInitialize
Sleep 100
While USBFlag 'Do ... as long as USBFlag <> 0
USBController '1 successful Tx/Rx loop takes 24 mSec
Sleep 20 ' might as well sleep a bit ...
'If USBFlag > 0 Then Exit Do 'Dim Shared Quit Flag from Main.
Wend 'Loop
End Sub
Re: Timer & Threads
Hi All
MrSwiss, thanks for your input on this.
The only disagreement is the Sleep in the USBThread.
I can't afford to ignore the usbController for not even 1 mSec.
That is why I placed Sleeps strategically in the usbController, because I know it takes a certain amount
of time for the IOBoard to respond.
I note your comments on the length of the sleep, guess just being ultra conservative.
But, will experiment with longer sleep times and see the result.
I made one mistake in making file & port Handles Bytes, and it caused some problems initializing the port.
Once I changed it to Integer, all was good.
Once again Thanks to all.
Regards
MrSwiss, thanks for your input on this.
The only disagreement is the Sleep in the USBThread.
I can't afford to ignore the usbController for not even 1 mSec.
That is why I placed Sleeps strategically in the usbController, because I know it takes a certain amount
of time for the IOBoard to respond.
I note your comments on the length of the sleep, guess just being ultra conservative.
But, will experiment with longer sleep times and see the result.
I made one mistake in making file & port Handles Bytes, and it caused some problems initializing the port.
Once I changed it to Integer, all was good.
Once again Thanks to all.
Regards
-
- Posts: 4292
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: Timer & Threads
In the above post we have 'Sleep 1' used twice, 'Sleep 5' used once, 'Sleep 20' used once and 'Sleep 100' used twice.
In Windows Sleep has a resolution of 15.625ms (64Hz) so only 'Sleep 100' will sleep for approximately what we ask for - the others will not even qualify as near misses.
For example:
with one run giving:-
Notice how 'Sleep 5' and 'Sleep 10' are almost the same - they are both 'under the radar'.
Add these two macros and repeat the above after inserting 'StartHiResSleep' before the code.
with one run giving:-
So, we now have Sleep with a resolution of 1ms and when we fine tune we know we are fine tuning.
If we forget to employ StopHiResSleep, Windows will do it for us at the end of the application session.
In Windows Sleep has a resolution of 15.625ms (64Hz) so only 'Sleep 100' will sleep for approximately what we ask for - the others will not even qualify as near misses.
For example:
Code: Select all
Dim as Double t
Dim as Long i
For i = 1 to 20
t = Timer
Sleep i
t = Timer - t
Print i;": "; t*1000
Next
Code: Select all
1: 1.856171664145867
2: 14.88841458890167
3: 13.5762366444272
4: 14.17268116494452
5: 13.75572873068798
6: 13.79728429166316
7: 13.83807159852957
8: 13.88263033427895
9: 13.79099857665622
10: 13.79057952878138
11: 13.7724207963783
12: 13.82522080321325
13: 13.67045252950927
14: 29.34353388472433
15: 28.7752353998485
16: 29.34234658328916
17: 29.53133708312228
18: 29.45590850243285
19: 29.55955295991753
20: 29.56122915064196
...
...
100: 114.3238875332984
Add these two macros and repeat the above after inserting 'StartHiResSleep' before the code.
Code: Select all
#Macro StartHiResSleep
Scope
Dim As TIMECAPS tc
TimeGetDevCaps( @tc, SizeOf(tc) )
TimeBeginPeriod(tc.wPeriodMin)
End Scope
Sleep (16,1) ' Tests have shown that the new resolution will not 'bite' until next 'old' tick
#EndMacro
#Macro StopHiResSleep
Scope
Dim As TIMECAPS tc
TimeGetDevCaps( @tc, SizeOf(tc) )
TimeEndPeriod(tc.wPeriodMin)
End Scope
Sleep (2,1) ' Tests have shown that the new resolution will not 'bite' until next 'old' tick
#EndMacro
Code: Select all
1: 2.00102247612044
2: 2.567854294337701
3: 3.30007026026391
4: 4.145359256598313
5: 5.945308691676088
6: 6.137092842784409
7: 7.982858156589279
8: 8.526782035001235
9: 9.769258383595769
10: 10.24697272998032
11: 11.10085220331891
12: 12.10237613996412
13: 13.78799540164488
14: 14.15605894053718
15: 15.90341789244487
16: 16.92407833956344
17: 17.95360862912898
18: 18.8075579439555
19: 19.83981204294727
20: 20.93799630960125
...
...
100: 100.1176825546068
If we forget to employ StopHiResSleep, Windows will do it for us at the end of the application session.
Re: Timer & Threads
I agree that a Byte can't handle that:Dinosaur wrote:I made one mistake in making file & port Handles Bytes, and it caused some problems initializing the port.
Once I changed it to Integer, all was good.
however, Integer is IMO, to large (FBC 64 = LongInt = 64bit), a UShort should do ...
Port = 0 to 65535 (exactly UShort range)
FreeFile return is Long, but it never returns a value larger, than a UShort (see manual).
@deltarho[1859], Dinosaur is on Linux, not Windows ...
-
- Posts: 4292
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: Timer & Threads
Oops <smile>Dinosaur is on Linux, not Windows ...
So, why isn't 'usleep' being used? I am on thin ice here as I am not a Linux user.
Re: Timer & Threads
To use these, I should rather insert:deltarho[1859] wrote:Add these two macros and repeat the above after inserting 'StartHiResSleep' before the code.
Code: Select all
#include "windows.bi"
#include "win/mmsystem.bi"
-
- Posts: 4292
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: Timer & Threads
As I did in the code I took them from. I didn't have my afternoon nap today. <smile>. I got used to only using Include "Win32API.inc" at the top of my code without further ado. After 13 years some habits will be difficult to break.To use these, I should rather insert:
Re: Timer & Threads
Regarding sleep 1
The duration depends on your hardware.
As I remember it can be either of two values.
Sleep 1 (at least) is essential in any freebasic graphics loop.
Either value will suffice for this.
But sleep 1 is not needed in an api message loop,( it is slow anyway).
sleep 100 just stabs at slowing down framerate and because of the above, it is just an inaccurate stall.
Timer is unique, so sleep can be adjusted via the timer to be of use.
I have used this regulation for a while to specify a framerate for fb graphics.
Use the mouse on the slider circle:
The duration depends on your hardware.
As I remember it can be either of two values.
Sleep 1 (at least) is essential in any freebasic graphics loop.
Either value will suffice for this.
But sleep 1 is not needed in an api message loop,( it is slow anyway).
sleep 100 just stabs at slowing down framerate and because of the above, it is just an inaccurate stall.
Timer is unique, so sleep can be adjusted via the timer to be of use.
I have used this regulation for a while to specify a framerate for fb graphics.
Use the mouse on the slider circle:
Code: Select all
#define map(a,b,x,c,d) ((d)-(c))*((x)-(a))/((b)-(a))+(c)
#define inbox (mx>125) and (mx<475)' and (my>300) and (my<350)
#define incircle(cx,cy,radius,x,y) (cx-x)*(cx-x) +(cy-y)*(cy-y)<= radius*radius
#macro display
fr=map(125,475,cx,10,60)
screenlock
cls
draw string(20,20),"Actual Framerate = " &fps
draw string(20,40),"Requested Framerate = " &fr
draw string(20,60),"Sleeping time = " &sleeptime
draw string(100,280),"10"
draw string(480,280),"60"
draw string(250,280),"<--slider-->"
angle+=.1
drawline(600,200,.2*sin(angle)-pi/2,300,4)
line(100,300)-(500,350),2,bf
circle(cx,cy),25,5,,,,f
screenunlock
sleep sleeptime,1
#endmacro
#macro mouse
Dim As Long x=mx,y=my,dx,dy
While mb = 1
Display
Getmouse mx,my,,mb
If inbox Then
If mx<>x Or my<>y Then
dx = mx - x
dy = my - y
x = mx
y = my
cx=x+dx
if cx<125 then cx=125
if cx>475 then cx=475
End If
End If
Wend
#endmacro
sub drawline(x as long,y as long,angle as single,length as long,col as ulong)
var x2=x+length*cos(angle)
var y2=y-length*sin(angle)
line(x,y)-(x2,y2)
circle(x2,y2),10,6,,,,f
end sub
Function Regulate(Byval MyFps As long,Byref fps As long) As long
Static As Double timervalue,lastsleeptime,t3,frames
frames+=1
If (Timer-t3)>=1 Then t3=Timer:fps=frames:frames=0
Var sleeptime=lastsleeptime+((1/myfps)-Timer+timervalue)*1000
If sleeptime<1 Then sleeptime=1
lastsleeptime=sleeptime
timervalue=Timer
Return sleeptime
End Function
screen 19
dim as integer mx,my,mb
dim as long cx=125,cy=325,fps,fr,sleeptime
dim as single angle,pi=4*atn(1)
do
getmouse mx,my,,mb
display
if incircle(cx,cy,25,mx,my) and mb=1 then
mouse
end if
sleeptime= regulate(fr,fps)
loop until len(inkey)
Re: Timer & Threads
Hi All
Concluded that Sleep in my usbController section of code is a complete waste of Time (no pun intended)
I tested as per deltaro and found on my cpu the same inaccuracies.
On top of that, by removing the Sleeps in that part of the code, the stability of output pulse
increased, so that it now sits at about 0.5 mSec.
I should have learned this lesson long ago, both marcov & michaelw said as much in previous posts.(relating to my GUI kind of code)
Sleep is good if you want delays > 1/18.5 sec , for me that is already to long.
Also there is no penalty for me not to use sleep's.
Ok, if the quad cpu unit get a bit warmer, it won't worry either, as they operate in a constant 2 degree C (36 F) room.
Once again many thanks to ALL the contributors.
Regards
Concluded that Sleep in my usbController section of code is a complete waste of Time (no pun intended)
I tested as per deltaro and found on my cpu the same inaccuracies.
On top of that, by removing the Sleeps in that part of the code, the stability of output pulse
increased, so that it now sits at about 0.5 mSec.
I should have learned this lesson long ago, both marcov & michaelw said as much in previous posts.(relating to my GUI kind of code)
Sleep is good if you want delays > 1/18.5 sec , for me that is already to long.
Also there is no penalty for me not to use sleep's.
Ok, if the quad cpu unit get a bit warmer, it won't worry either, as they operate in a constant 2 degree C (36 F) room.
Once again many thanks to ALL the contributors.
Regards