How to reset the timer (20X2)

In my program I want to reset the timer to prevent it overflowing. I do this whenever all actions that are dependent on the timer are complete but I don't think the methods I am using to reset it are working.

timer = 0 . Does this reset the timer?

settimer 34286 . I assume that this resets the timer hardware but where does it leave the timer variable?

For my code as constructed it would be difficult to make use of the timer overflow bit/interupt.
 

piclt

Member
"For my code as constructed it would be difficult to make use of the timer overflow bit/interupt."

Let us see the problem code and we can all see if we can see the problem....??
 

Aries

New Member
At the risk of stating the obvious - try it. Write a very simple program, say
Code:
for b0 = 1 to 20
  sertxd(13,10,#timer)
  pause 1000
next b0
timer = 0
for b0 = 1 to 20
  sertxd(13,10,#timer)
  pause 1000
next b0
which will show you whether timer is reset.

Equally, instead of timer = 0, try a settimer instruction and see what you get.
 

AllyCat

Senior Member
Hi,
timer = 0 . Does this reset the timer?
I'm not an X2 user, but I believe it will Reset the timer variable, but NOT the timer hardware (i.e. the Prescaler "sub-ticks" will continue unchanged). Therefore, "timer" will become zero, but it might be incremented to "1" within a small fraction of a second. However, it should then continue to increment after whatever period has been set by the SETTIMER command.

So I suspect (but it would be best to test) that you need to use SETTIMER 34286 (assuming that you're using the default SETFREQ M8), but may also need to use a timer = 0 (afterwards).

Cheers, Alan.
 
My code is part of a controller for a remote shutter. It's not practical for me to use a sertxd statement for debugging as there is no serial io access at that location. The picaxe is plugged into a cinch type socket so I can easily remove it and bring it back to my desktop for program loading. When I do that it then becomes difficult to set up all the peripheral connections so I generally use the Simulator to test out the logic. So part of the problem I am seeing might be an artifact of the Simulator itself.

With the reset code as it stands (see below) I see that after trying to reset the clock the value of timer is unchanged and continues incrementing. By 'timer' I mean the value shown in the System Values tab of the Simulator. In my code I also copy this value into the word location 'now' and this behaves in a similar fashion. In this code the symbol timer_tic is set = 34286 '1 sec ticks at 8MHz

Code:
if heartbeat_event < timer then ' timer has expired
    'use this event to prevent timer overflow by resetting (if not in use)
    if timer > dr_move_end_time _
    AND timer > switch_debounce_end then
        settimer  timer_tic 'reset the timer
        now = timer
        heartbeat_event = timer + heartbeat_flash_time
        dr_move_end_time = 0
        switch_debounce_end = 0
    endif   
    toggle heartbeat_led
    heartbeat_event = timer + heartbeat_flash_time
    endif
By putting a breakpoint at the line now=timer I can see that the program gets to that point and I can examine the simulated values.

I also wrote some simpler code so I can see what is going on:
Code:
#rem
Test of the timer functionality
#endrem

#picaxe 20x2
#no_data
#no_table

symbol now = w1
symbol start = w0
symbol elapsed = w3
symbol end_time = w4
symbol loops = 3

symbol frequency = m8
setfreq frequency
symbol timer_tic =34286 '1 sec ticks at 8MHz
settimer  timer_tic

start = timer
now = start
elapsed = now - start
end_time = start + loops

do 'main loop
if now > end_time then
    settimer timer_tic
    start = timer
    now = start
    end_time = start + loops
else
    now = timer
    elapsed = now - start   
endif

loop
 

AllyCat

Senior Member
Hi,
... part of the problem I am seeing might be an artifact of the Simulator itself.
Yes, quite probably. It's primarily a Software Simulator, NOT a Hardware Emulator and you're straying into hardware (SFR) aspects of the base PIC (which are not supported by the Simulator). There is also the issue that some (time-related) parts of the Simulator run in (approximately) "Real Time", whilst most instructions must run at very greatly reduced speed (~1000 times).

Cheers, Alan.
 
So, back to the original intent of the question. :) How to reset the timer, it's not clear (to me) from the documentation how best to ensure this.
 

piclt

Member
FWIW.... I didnt follow your program in detail but in the simulator setting timer=0 in the main do/loop
resets the variable timer (s_w7) on the simulator System tab.
seems to reset after 4 counts, then 8 counts, then 12 counts, 16 etc etc ....due to where I put it in the loop
Code:
do 'main loop
if now > end_time then
    settimer timer_tic
    start = timer
    now = start
    end_time = start + loops
    timer=0  ' resets timer system variable .. ??
else
    now = timer
    elapsed = now - start   
endif

loop
 

AllyCat

Senior Member
Hi,

Also I'm dependent only on the Documentation, but suspect the following should (always) work with a "Real" PICaxe X2 (but not necessarily with the Simulator or an M2) :
Code:
SETTIMER OFF        ;  Probably NOT required in practice
SETTIMER 34286      ;  Required to make the FIRST timed period correct (The default SETFREQ M8 assumed)
timer = 0
If you do use the OFF command, then the timer = 0 could/should be before the (second) SETTIMER command.

Cheers, Alan.
 
Alan thank you. By investigating using a combination of "Real" and Simulated I reached the same conclusion.

I don't know how to look directly at the contents of the internal (minor tick) register but I agree that it seems necessary to use SETTIMER OFF before reissuing the SETTIMER statement.

The original problem with my application is that it was misbehaving after being left idle for a long period, which was difficult to diagnose. In retrospect it is probably the failure to reset the minor tick counter (ie not using SETTIMER OFF) that is the cause. The next few days will tell.

If prestart timing is not important it is possible to get away with just a "timer = 0" because the minor tick register just keeps on incrementing the timer counter even when it overflows. I have not tried this in a Real hardware situation but it works with the Simulator.

As an aside I played around looking at using the timer overflow flag using the declared keyword "toflag".
Code:
IF flags BIT toflag SET THEN etc
This passed the syntax checker but did not work. Instead it needed to be
Code:
IF flags BIT 7 SET THEN etc
In my case I don't check for timer overflows because I am always aiming to reset long before that point is ever reached.

Thank you everyone for your assistance's.
 

inglewoodpete

Senior Member
This is how I implemented the background timer with interrupts. While the example here has interrupts occurring every 125mS, you can add a byte- or word-variable incrementing every 1/8 second. This variable can be read, cleared etc as your needs require.

Rich (BB code):
#PICAXE 20X2
'Must use AXE027 (Not COM1) to log data at 76800 baud (if clock @64MHz)
#COM 8
#Terminal 38400   'PICAXE Runs at 32MHz from Version 0.16
'Interrupt masks
Symbol mskSerialOnly = %00100000
Symbol flgSerialOnly = %00100000
Symbol mskTmrAndSer  = %10100000
Symbol flgTmrAndSer  = %10100000
Symbol mskBGTimer    = %10000000 'For the background timer (only) interrupt
Symbol flgBGTimer    = %10000000 'For the background timer (only) interrupt
'Timer constants
Symbol tmrIntOn1stTick = 65535   'Interrupt to be caused by roll over on first major tick
Symbol t125mS_32     = 49911     'SetTimer value for 1/8 second ticks @ 32MHz
' **** Standard Constants
Symbol False         = 0
Symbol True          = 1

Init: SetFreq m32
      '
      'Start the background timer (runs continuously)
      Timer = tmrIntOn1stTick
      SetTimer t125mS_32         'Expires after 1/8 second @ 32 MHz
      Flags = 0                  'Reset all flags
      SetIntFlags flgBGTimer, mskBGTimer 'Set timer 0 to interrupt
      '
      '<Other initialisation code>
      '
      Do
         'Insert Main code
      Loop
      '
Interrupt:
      If TOFlag = True then                        'All timer ticks
         TOFlag = False                            'Reset (clear) the flag first
         '<Other code triggered by timer interrupt>
      EndIf
      '
      SetIntFlags flgBGTimer, mskBGTimer 'Set timer 0 to interrupt
      Return
 
inglewoodpete thank you for your example. In my case I am running what is effectively a state machine with multiple "timer" intervals running in parallel. Using interrupts would, I feel, add to the complexity of the code and make it difficult to see the underlying logic. This state machine runs 24/7 so to prevent timer overflow I reset the timer whenever it is not being used. What I am finding at the moment is that the machine enters some unexpected state after about 12 hours or so. It is difficult to track the faulty logic down while the circuit is remote, boxed and connected but to eliminate the possibility that the Timer was getting out of sync in some way I wanted to ensure that it was being reset correctly.
 

inglewoodpete

Senior Member
While I don't know much about your project, I would have thought that using timer interrupts to handle the generation of timer events would be ideal.

I, too, have built a state machine (a different project to the one above, using a 40X2) with 4 timers, all managed via background timer interrupts.

In its entirity, the interrupt routine is shown below. User bit variables start with a 't', byte variables with a 'b' and words with a 'w'.
Rich (BB code):
'Any registers used here are preserved in the poke statement (next line+5)
Interrupt:If TOFlag = True then
            'Timer event has occurred
            Inc wGlobalTick                     'Runs continually (100mS counter)
            '
            Poke rIntStack, bTimerStatus        'Preserve variable - whatever happened to be in the reg at the time
            Peek rTimersStatus, bTimerStatus    'Fetch status for all software timers
            '
            Dec bMainTimer                      'Main Timer runs continuously
            If bMainTimer = 0 Then              'If just expired...
               tMainTimer = True
               bMainTimer = cMainTimerVal       'Reinitiate timer
            EndIf
            '
            If bDelayTimer <> cNotRunning Then  'Delay Timer runs on demand (non blocking)
               Dec bDelayTimer                  '  and is running
               If bDelayTimer = 0 Then          'If just expired...
                  tDelayTimer = True
                  'SerTxd("++++")
               EndIf                            'Note: next Timer Event will set bDelayTimer to cNotRunning
            EndIf
            If bEventTimer <> cNotRunning Then  'Event Timer runs on demand (blocks other events)
               Dec bEventTimer                  '  and is running
               If bEventTimer = 0 Then          'If just expired...
                  tEventTimer = True
               EndIf                            'Note: next Timer Event will set bEventTimer to cNotRunning
            EndIf
            If bKeyTimer <> cKeyTimerDisabled Then'Keypad key is currently held
               Inc bKeyTimer
            EndIf
            Poke rTimersStatus, bTimerStatus    'Save status for all software timers
            Peek rIntStack, bTimerStatus        'Preserve variable(s)
            '
            Timer = tmrIntOn1stTick             'Sequence changed v3.03
            TOFlag = False
            SetIntFlags flgBGTimerOnly, mskBGTimerOnly 'Set timer 0 to interrupt (Slot 0)
          EndIf
          'Both Timer and hInt interrupts can occur during the same instruction cycle: must check both
          If hIntFlag = True Then               'was If hInt1Flag = True Or hInt2Flag = True Then
            'Hardware Interrupt has occurred: Keyboard event has occurred
            'Note that bLCount (b18) is a global variable that is clocking data out
            '      the 74LS164 keypad driver via sub ShiftRegOut:
            hIntFlag = False                    'Must clear hIntFlags to
            hInt1Flag = False                   '    ensure that execution does not
            hInt2Flag = False                   '    reenter here on timer interrupts
            Poke rIntStack, bKBDStatus, bKBDValue  'Preserve any previous values in variable(s)
            Peek rKeyStatus, bKBDStatus         'Get  the  keyboard  status (byte)
            Peek rKeyStore, bKBDValue           'Get any existing key value (byte)
            If bKBDStatus = 0 Then              'Fresh new keypress
               tKeyUnreslvd = True              'That's bit 5 of b0
               bKeyTimer = cTimerStart          'Start the long-held-key timer                        
            ElseIf tKeyUnreslvd = True Then     '(Bit 6) Resolve the key. (2nd interrupt for this keypress)
               tKeyUnreslvd = False             'Clear 'Unresolved' bit.  KeyShift may have been set already.
               bKBDStatus = bKBDStatus Or cKeyDown 'KeyDown: key is identified but still held.
               bKBDValue = bLCount              'bLCount (b18) must not be used locally
            ElseIf tKeyTimeOut = True Then      '(bit 5) Key-held timeout has occurred
               bKBDValue = bLCount + 9          'Key-held gives range 10-17 ((1 to 8) + 9) (9&18 handled sep'rtly)
               bKBDStatus = cKeyDown            'KeyDown: key is identified as <shifted> but still held
            EndIf
            Poke rKeyStatus, bKBDStatus         'Store Key status
            Poke rKeyStore, bKBDValue           'Store Key value
            Peek rIntStack, bKBDStatus, bKBDValue  'Restore variables
            'Note: Interrupts are not reenabled here! (Done in Sub ResolveKeys (also Sub ResetKBD))
         EndIf
         Return
 
Last edited:
inglewoodpete. Thank you for the suggestions. We are going off topic here but my code spends its time circling around checking the time and looking for true switch closures, otherwise it does precious little except occasionally closing relays.

Not using interrupts, in this case, keeps my code simple (if timer < timeout ...etc) giving my brain a head start every 5 years or so when I might need to open the box and make a minor configuration change. 🤔

If my code needed to react to (say) input from short pulses or if timing was critical then interrupts it would definitely be. A case in point is some assembler code I have for an ATTiny where a lot has to happen within closely defined time intervals.
 

inglewoodpete

Senior Member
My first reply #11 was fuelled by your attempts to access the Timer overflow flag by accessing the flag bit by separating it out of the Flags system byte. The bit can be addressed more simply: "If TOFlag = 1 Then... "

You mentioned a bug that becomes evident after around 12 hours. This type of bug can very difficult to find, especially when dealing with a system variable in foreground code. I have found that having the timer task located in an interrupt routine, where it can gererate simpler 'state' flags which can then be accessed by my foreground code.
 
Top