inglewoodpete
Senior Member
I had developed a program that uses the internal timer of an X2 PICAXE to read an array of Touch pads. The timer was set to cause an interrupt every 100mS, allowing the Touch pads to be read about 10 times a second.
Some of the foreground tasks either shared some variables and flags with the interrupt routine or were time critical. This made it necessary to disable interrupts in the foreground for critical but short periods; usually in response to a Touch event.
The problem
Periodically, I found that the regular timer interrupts would stop but then, about 11 minutes later, would mysteriously start again.
The code that I was using was some that I had copied from the forum some years ago:
My first lightbulb moment came when I realised that 11 minutes is (about) 65,536 x 100mS! Hmm. The Timer value was somehow ticking over from 65,535 ($FFFF) to 0 without causing an interrupt.
How the timer works in the X2
The whole timer consists of 2 x 16-bit registers. The first 16-bit counter is preloaded with the value specified in the SetTimer command. Every time it rolls over from 65,535 to 0 it increments the second 16-bit register. The first register is then reloaded with the SetTimer value and continues to count up again. The second 16-bit register is actually the Timer variable and can be set with 'Timer = value'. When the Timer variable ticks over from 65,535 to 0, it sets the TOFlag bit to 1, which can be configured to cause an interrupt. When using interrupts, more often than not the Timer variable is set to 65,535 so that it triggers an interrupt every time the first timer rolls over to 0. For a periodical timer like I wanted, the Timer value must be reset (preset) to 65,535 on every interrupt, in preparation for the next timer interrupt.
What was going wrong
Then I realised that the timer runs in the background, regardless of (almost) everything else that happens in the PICAXE code, be it in the interrupt routine or elsewhere. If, in the interrupt routine, the Timer variable is set to 65,535 just before the first counter rolls over to 0, there may not be enough time to clear the TOFlag before the rollover event occurred. This would mean that the Timer variable would have to increment another 65,535 times before the TOFlag is set and another interrupt occurred. Hence the 11 minute break between timer interrupts.
So the TOFlag should be cleared before the Timer value is set; especially when the Timer is being set to 65,535 ($FFFF).
The revised code
The change in code is critical but quite subtle. I have included a more complete piece of code to show how the timer interrupt is configered for a regular 100mS timer.
Some of the foreground tasks either shared some variables and flags with the interrupt routine or were time critical. This made it necessary to disable interrupts in the foreground for critical but short periods; usually in response to a Touch event.
The problem
Periodically, I found that the regular timer interrupts would stop but then, about 11 minutes later, would mysteriously start again.
The code that I was using was some that I had copied from the forum some years ago:
Code:
Interrupt: 'This code can be problematic, so don't use it!
<application specific code goes here>
Timer = $FFFF 'Reset timer0 to interrupt on 1st major tick
TOFlag = False 'Clear the flag
SetIntFlags %10000000, %10000000 'Set timer 0 to cause interrupts
Return
How the timer works in the X2
The whole timer consists of 2 x 16-bit registers. The first 16-bit counter is preloaded with the value specified in the SetTimer command. Every time it rolls over from 65,535 to 0 it increments the second 16-bit register. The first register is then reloaded with the SetTimer value and continues to count up again. The second 16-bit register is actually the Timer variable and can be set with 'Timer = value'. When the Timer variable ticks over from 65,535 to 0, it sets the TOFlag bit to 1, which can be configured to cause an interrupt. When using interrupts, more often than not the Timer variable is set to 65,535 so that it triggers an interrupt every time the first timer rolls over to 0. For a periodical timer like I wanted, the Timer value must be reset (preset) to 65,535 on every interrupt, in preparation for the next timer interrupt.
What was going wrong
Then I realised that the timer runs in the background, regardless of (almost) everything else that happens in the PICAXE code, be it in the interrupt routine or elsewhere. If, in the interrupt routine, the Timer variable is set to 65,535 just before the first counter rolls over to 0, there may not be enough time to clear the TOFlag before the rollover event occurred. This would mean that the Timer variable would have to increment another 65,535 times before the TOFlag is set and another interrupt occurred. Hence the 11 minute break between timer interrupts.
So the TOFlag should be cleared before the Timer value is set; especially when the Timer is being set to 65,535 ($FFFF).
The revised code
The change in code is critical but quite subtle. I have included a more complete piece of code to show how the timer interrupt is configered for a regular 100mS timer.
Code:
#PICAXE 28X2 ' or 20X2, 28X1, 40X1, 40X2
'
' **** Constants ****
'
Symbol False = 0
Symbol True = 1
Symbol mskBGTimer = %10000000 'Timer 0 is bit 7
Symbol flgBGTimer = %10000000 'Timer 0 is bit 7
Symbol tmrIntOn1stTick = 65535 'Interrupt to be caused by roll over on first major tick
Symbol tmr100mS_8 = 62411 '(for 8MHz) = 65536 - (Treq * 1,000,000 / Clk / 256)
'
Init: 'PICAXE running at the default speed (8MHz)
'Start the background timer (runs continuously)
TOFlag = False
Timer = tmrIntOn1stTick '
SetTimer tmr100mS_8 'Expires after (every) 100 milliseconds
SetIntFlags flgBGTimer, mskBGTimer 'Set timer 0 to interrupt
'
MainLoop: Do
'<your main loop code
' goes here>
Loop
'
Interrupt:'<your interrupt code
' goes here>
TOFlag = False 'Reset (clear) the flag first
Timer = tmrIntOn1stTick 'Then reset the timer
'
SetIntFlags flgBGTimer, mskBGTimer 'Configure timer 0 to interrupt
Return 'Return enables interrupts again