[RESOLVED] Help with timing procedures
Hi everyone,
I’m having trouble yet again, this time to do with timing. I have created a bunch of Public Sub procedures that draw different graphics, but I want them to be executed in a specific order and for different amounts of time. More precisely, after a certain key is pressed N times, I need the following sequence to be executed:
Public Sub A () for 1000ms
Public Sub B () for 1500ms
Public Sub C () for 50ms
Public Sub D () for 50ms
Public Sub E () for 100ms
Public Sub F () for 900ms
Public Sub G () until a keyboard input
And this sequence needs to cycle multiple times. Something along those lines. Is this even possible?
Is it possible to use some sort of Select Case code that would dictate the interval of the timer? Or do I use a different timer for each sub? Actually, do I call the procedures within a timer's Sub? As you can tell I'm quite clueless. I generally find working with timers very frustrating and I’m not quite certain I even understand how they work.
Not sure if coding the event sequence by using different sub procedures was a good idea now :confused: Maybe I need to find an alternative way…
Any suggestion will be much appreciated. Thank you!
Re: Help with timing procedures
Ok, so I did something which in my head should be working just fine, but in reality it behaves quite oddly...
I set the timer's interval to 1 millisecond and then created a variable to count each time it fires. After that I used select case in order to make it execute specific subs at certain time... It does do it, but not withing the timing I expect. Here's the code for the timer:
Private Counter as Double
Counter = 0
Private Sub Tmr1_Timer()
Counter = Counter + 1
Select Case Counter
'Display Modal for 2 seconds
Case 1 To 2000
Call Modal
'Change colour for 100ms
Case 2001 To 2101
Call ColourChange(vbRed, vbBlue, vbYellow, vbCyan, vbMagenta, vbGreen)
'Display Cue for 50ms
Case 2102 To 2152
Call Cue(x3, y3)
'Hide Cue for 50ms
Case 2153 To 2203
Call CueOff(x3, y3)
'Display Probe after
Case Else
Call Probe(vbBlue, vbCyan, vbGreen, vbRed, vbMagenta, vbYellow, x4, y4)
Tmr1.Enabled = False
Counter = 0
End Select
End Sub
I though setting a case from 1 to 2000 would suggest the procedure would be executed for 2 seconds, instead it lasts about 30 seconds! After that everything proceeds in order, but for way longer than it should. For example the procedure supposed to last 50ms lasts for about a second and a half :ehh:
Any idea what I'm doing wrong?
Thank you!
Re: Help with timing procedures
You are assuming two things that are not necessarily true.
The first assumption is that a Timer with an interval of 1 will fire 1000 times a second, and it usually wont. Timers are a very low priority thing, so the interval only counts as the minimum... while you will usually be relatively close to it, sometimes it can be dramatically longer (even a few seconds longer, depending on what the computer is doing at the time).
The other assumption is that the Timer will fire at the frequency related to the interval, but it usually wont: because it will run the code inside the event, then start the next interval (so if your code takes 50ms, the second time it runs will be 50+1ms after the first).
If you need to know the time since the start, then you should be calculating/storing that independently of the timer ticks. An easy way to do that (which has problems if you run the code during midnight) is to read the value of the Timer function, which specifies the number of milliseconds since midnight, eg:
Code:
Private TimerStart as Single
...
Tmr1.Enabled = True
TimerStart = Timer
...
Private Sub Tmr1_Timer()
Select Case (Timer - TimerStart)
Re: Help with timing procedures
Wow, that would have never occured to me... Thank you!
I did try it, but couldn't get it to work properly either. Anyway, I've found a way around it! Very clumsy coding (making the timer read a string where each letter corresponds to executing a specific sub for a specified interval), but it works and that's all I care about :bigyello:.
Thanks again for the info, I've certainly learnt something new! :wave:
Re: [RESOLVED] Help with timing procedures
Erm, hello again :wave:.
I have another problem, but didn't want to start a new thread as it's still related to timing.
I coded a couple of loops within a timer, but although everything seems to be working fine in terms of order of progression (in step mode), the intervals of the timer seem to be ignored... so what I get at run time is the very end result. What I expect though is for it to cycle through the code with the specified timing, in reality none of that is visible. Step mode suggests that it is indeed registering and changing the interval of the timer as I want it to, but it doesn't seem to be actually executing it. And it all worked fine before I introduced the loops. Here are bits of the code:
This one is supposed to change the values of the variables for each cycle:
Private Sub Tmr1_Timer()
Do While Block1 < 4
Select Case Block1
Case 1
strTrial = "0156789 !"
'Set variable values
Call Timer
Case 2
strTrial = "0256789 !"
'Change variable values
Call Timer
Case 3
'Change variable values
Call Timer
End Select
Block1 = Block1 + 1
Loop
End Sub
This is the Timer procedure which is called. It executes certain commands for specified periods of time, using the variable values determined from the previous loop:
Public Sub Timer()
Static bytCounter As Byte
If bytCounter = 0 Then
bytCounter = 1
End If
Do
strStage = Mid(strTrial, bytCounter, 1)
Select Case strStage
Case "0"
Do stuff...
.........
Tmr1.Interval = 1000
Case "1"
..........
Tmr1.Interval = 1500
Case "2"
..........
Tmr1.Interval = 1500
Case "3"
.........
Tmr1.Interval = 1500
Case "4"
......
Tmr1.Interval = 1500
Case "5"
........
Tmr1.Interval = 50
Case "6"
........
Tmr1.Interval = 50
Case "7"
.......
Tmr1.Interval = 100
Case "8"
........
Tmr1.Interval = 900
Case "9"
.......
Tmr1.Interval = 1
Case " "
.......
Tmr1.Interval = 2000
Case "!"
....
Tmr1.Interval = 1
End Select
If bytCounter >= Len(strTrial) Then
bytCounter = 0
Tmr1.Enabled = False
End If
bytCounter = bytCounter + 1
Loop Until bytCounter >= Len(strTrial)
End Sub
Any ideas why the timer is not working (properly)?? :confused:
Re: [RESOLVED] Help with timing procedures
When you post code please put it inside code tags so it is displayed in a more readable way - either using the Code/VBCode buttons in the post editor screen (or at the top of the Quick Reply box), or by putting them in manually, like this: [code] code here [/code]
Given the code you have shown, there seems to be no way you could tell what it is doing, as there is no kind of output whatsoever.
Re: [RESOLVED] Help with timing procedures
Sorry about that, I did want to wrap it in code tags, but I wasn't sure how to do it.
As for the code itself, I didn't post all of it on purpose, I just though it's irrelevant and makes it quite cluttered. Where you see "........." it's all code that produces output, but I'm not sure if it'll make any sense to people because it's mostly prodecures I've created. Here is the whole thing anyway:
The timer (and I was hoping the loop as well) is activates after a key press:
Code:
Private Sub Form_KeyPress(KeyAscii As Integer)
If KeyAscii = vbKeyEscape Then
Dim bytEscape As Byte
bytEscape = MsgBox("Exit?", 1, "Esc Command")
If bytEscape = 1 Then
Unload Me
End If
ElseIf KeyAscii = 44 Then
Tmr1.Enabled = True
Tmr1.Interval = 1
End If
End Sub
There is a loop coded within the Timer event to make it cycle and change the values of the specific variables (A, B, C...) for each cycle:
Code:
Private Sub Tmr1_Timer()
Do While Block1 < 4
Select Case Block1
Case 1
strTrial = "0156789 !"
A = vbRed
B = vbGreen
C = vbYellow
D = vbWhite
E = Purple
F = vbCyan
G = vbBlue
P = x5
O = y5
Q = x
R = y
Call Timer
Case 2
strTrial = "0256789 !"
A = Purple
B = vbRed
C = vbYellow
D = vbBlue
E = vbCyan
F = vbWhite
G = vbGreen
P = x
O = y
Q = x2
R = y2
Call Timer
Case 3
strTrial = "0356789 !"
A = vbRed
B = Purple
C = vbYellow
D = vbBlue
E = vbCyan
F = vbWhite
G = Purple
P = x
O = y
Q = x2
R = y2
Call Timer
End Select
Block1 = Block1 + 1
Loop
End Sub
This is the code for the Timer procedure, which is called after setting the variable values. It contains another loop to make it read through each letter of the string and do the associated events before moving to the next set of variables (from the first loop):
Code:
Public Sub Timer()
Static bytCounter As Byte
If bytCounter = 0 Then
bytCounter = 1
End If
Do
strStage = Mid(strTrial, bytCounter, 1)
Select Case strStage
Case "0"
Gap.Visible = True
Tmr1.Interval = 1000
Case "1"
Gap.Visible = False
Segmented.Visible = False
Amodal.Visible = False
Modal.Visible = False
Whole.Visible = True
Tmr1.Interval = 1500
Case "2"
Gap.Visible = False
Whole.Visible = False
Modal.Visible = False
Amodal.Visible = False
Segmented.Visible = True
Tmr1.Interval = 1500
Case "3"
Gap.Visible = False
Whole.Visible = False
Amodal.Visible = False
Segmented.Visible = False
Modal.Visible = True
Tmr1.Interval = 1500
Case "4"
Gap.Visible = False
Whole.Visible = False
Modal.Visible = False
Segmented.Visible = False
Amodal.Visible = True
Tmr1.Interval = 1500
Case "5"
Call Cue
Tmr1.Interval = 50
Case "6"
Call CueOff
Tmr1.Interval = 50
Case "7"
Call ColourChange
Tmr1.Interval = 100
Case "8"
Fixation.Visible = True
Tmr1.Interval = 900
Case "9"
Static Press As Integer
Fixation.Visible = False
Tmr1.Interval = 1
Case " "
Call ColourChange
Call Probe
Tmr1.Interval = 2000
Case "!"
Gap.Visible = True
Tmr1.Interval = 1
End Select
If bytCounter >= Len(strTrial) Then
bytCounter = 0
Tmr1.Enabled = False
End If
bytCounter = bytCounter + 1
Loop Until bytCounter >= Len(strTrial)
End Sub
That's it. It loops through everything as it's supposed to, but as you said, there is no output. When I run it in step mode it seems like the problem is in the timer intervals, it goes through them but doesn't seem to execute them. If there is no loop it works fine, which made me think it's something to do with the loop itself :ehh:
In worst case scenario I'll do it without a loop and variables will change based on keyboard input, but ideally I wanted it to loop by itself without waiting for the user to respond.
Re: [RESOLVED] Help with timing procedures
I presume Gap/SegmentedAmodal/etc are controls, and therefore changing the visibility is a kind of output you can check.
Assuming they are controls, the problem is that there isn't a chance to update screen (which happens automatically when your code is no longer running). To force it to update quicker, tell the form (or each control individually) to refresh, eg: Gap.Refresh
Note that using a Timer and a loop is a rather strange thing to do (it is usually one or the other, as they serve the same kind of purpose), and a quick check makes me think what you've got will cause odd behaviour.
Re: [RESOLVED] Help with timing procedures
I see... Yes, if I remove the timer and just use the loop plus refreshing the controls I do get a rather 'subliminal' glimpse of the output. I have heard that a loop can function as a timer (or something like that), but I've never really uderstood how you can slow it down and make it do different parts of the code for different amounts of time. That's why I went for the timer/ loop combination (as you can tell I don't really know what I'm doing, it's only the second time I've ever had to program something), but apparently it doesn't work.
Thanks for the info, at least now I can stop faffing about with it and move on to an input-driven progression (plan B).
Edition: Eureka! I've just discovered the Sleep function! Life makes sense again :bigyello:
Thanks again! :wave:
Re: [RESOLVED] Help with timing procedures
A timer is meant for doing things at regular intervals, and a loop is meant to do things repeatedly.
For this kind of situation I would probably use a timer along with the code I showed in post #2 (and no loop). A loop with Sleep (and no timer) can be used for the same effect, but it generally takes more code to deal with the timing.
The important thing however is that you get the desired result, and understand the code.