Swordfish Code Snippet - TMR0

TMR0 is an 8 byte timer/counter that can be pre-scaled to change vary its incrementing cycle. It is quite different in regards to setting up compared to the 16F's, but here's an example.

Pre-scaling is important as we may want the Timer Register to increment faster/slower in different applications. The speed in which it does cycle is dependent on both the external crystal speed and the pre-scaling options, or the Prescaler and external clock if an external source is used.

Now to make a timer that can be used to count mS very accurately. An easy way to find out how much time each clock cycle will take is to use a pic timer calculator. Configure the oscillator speed (20Mhz for this example), along with the pre-scaler (1:32 for this example). The TMR0 period should now be 1.638mS (when TMR0 reaches 255 and rolls over to 0, 1.638mS has elapsed). To make things easier, I will work with a scale of 1000 for my mS counter. Now when mS = 10000, 10mS has elapsed, so there is no need to use Floats.

*** note, the linked calculator appears to have a bug when calculating TMR0 (discussed in the article comments below).

The sub routine "TMR0_Initialize" goes through the required steps to set up TMR0, follow the code at your own pace;

TMR0 Example
Device = 18F452
Clock = 20
 
Dim
 mS As Word,
 TMR0ON As T0CON.7,
 T08BIT As T0CON.6,
 T0CS As T0CON.5,
 T0SE As T0CON.4,
 PSA As T0CON.3,
 TMR0IF As INTCON.2,
 TMR0 As TMR0L,
 TMR0IE As INTCON.5,
 TMR0_Event As Boolean
 
Interrupt TMR0_Interrupt()
 Save(0) // Backup system variables
 If TMR0IF = 1 Then // Check if a TMR0 Interrupt occurred
 TMR0IF = 0 // Clear the interrupt
 Inc(mS, 1638) // Increment the mS counter (scale of 1000)
 If mS >= 10000 Then // Working with a scale of 1000, so this
 mS = mS - 10000 // checks if 10mS has elapsed
 TMR0_Event = True
 EndIf
 EndIf 
 Restore // Backup system variables
End Interrupt
 
Sub TMR0_Initialize()
TMR0ON = 0 // Disable TMR0
T08BIT = 1 // Ensure TMR0 is working in 8 Bit mode
T0CS = 0 // Ensure TMR increments from internal clock
T0SE = 0 // Only used if external source is selected
PSA = 0 // Ensure the Clock source uses the Prescaler
T0CON.0 = 0 // Set the Prescaler bits
T0CON.1 = 0 //
T0CON.2 = 1 //
TMR0 = 0 // Clear the TMR0 register
TMR0IE = 1 // Enable TMR0 Interrupts
Enable(TMR0_Interrupt) // Enable the TMR0 Interrupt Handler
TMR0ON = 1 // Enable TMR0 to increment
End Sub
 
// Start Of Main Program...
mS = 0 // Reset the mS counter
TMR0_Event = False // Clear the TMR0 Event Flag
TMR0_Initialize // Setup and enable TMR0
Low(PORTB.0) // Make PORTB.0 and output and set it low
 
While True
 While TMR0_Event = False // Wait for 10mS to elapse
 Wend //
 TMR0_Event = False // Reset the Event Flag
 Toggle(PORTB.0) // Toggle PORTB.0
Wend 

 

The result, a very accurate interrupt;

TMR0

Note the PIC's power supply/oscillator are not shown


Posted: 8 years 7 months ago by odessa #6873
odessa's Avatar
Hi Guys,

I have a question about the Swordfish Timer0 Example.

Its says
Make sure that the Internal Check Box is selected, now be sure that 20Mhz is the clock speed, and the pre-scaler is set to 1]
I have downloaded the timer calc. linked in the article and it doesn't have a tick box for 'Internal'. Also when set as above it doesn't show 1.638 unless the 'Timer Offset' slide is moved from 0 to 1. Then it shows 1638.4 which is correct... What is the Timer Offset for ?

Many Thanks

Jay
Posted: 8 years 7 months ago by be80be #6874
be80be's Avatar
Your preloading timer0 with a 1

Timer Offset is set at 1 and counts up
Posted: 8 years 7 months ago by bitfogav #6875
bitfogav's Avatar
Make sure that the Internal Check Box is selected

Graham also points out this in the tutorial, but like me and odessa we can't find what this Check box is?

Posted: 8 years 7 months ago by Graham Mitchell #6878
Graham Mitchell's Avatar
Ahh yes, I was using one calculator although I linked another.

There looks like a bug with the linked calculator; it's missing one cycle..
Timer Period = Cycle Time x Counts to Overflow
Timer Period = (1/(20000000/4)) x (256x32)
Timer Period = 200nS x 8192
Timer Period = 1.6384 mS
Where as the linked software appears to calculate like so:
Timer Period = Cycle Time x Counts to Overflow
Timer Period = (1/(20000000/4)) x (255x32)
Timer Period = 200nS x 8160
Timer Period = 1.632 mS
Posted: 8 years 7 months ago by bitfogav #6877
bitfogav's Avatar
Thank you Graham for clearing that up

Another question:
The Swordfish Code Snippet - TMR0, as an Interrupt routine which Inc(mS, 1638)

Now if it takes 7 roll overs of TMR0 to Increase mS, so that mS equals over 10000?
(actual figure of mS = 11466 after 7 roll overs of TMR0)
then why is mS = mS - 10000? because correct me if im wrong mS now equals 1466?

So that means that TMR0 has to only roll over 6 times for mS to equal mS >= 10000 ?.

Then I wouldn't have thought that this would be very accurate over time?.
Interrupt TMR0_Interrupt()
    Save(0)                  // Backup system variables
    If TMR0IF = 1 Then       // Check if a TMR0 Interrupt occurred
        TMR0IF = 0           // Clear the interrupt
        Inc(mS, 1638)        // Increment the mS counter (scale of 1000)
        If mS >= 10000 Then  // Working with a scale of 1000, so this
            mS = mS - 10000  //  checks if 10mS has elapsed
            TMR0_Event = True
        EndIf
    EndIf                  
    Restore                  // Backup system variables
End Interrupt
Posted: 8 years 7 months ago by be80be #6879
be80be's Avatar
Timer0 can only count 255 and then it rolls over if you google you'll see there a lot of people saying you get the wrong value if you figure it at 256
a) On a TMR0 overflow (255 → 0) the T0IF flag is set briefly (INTCON<2>).

But when you get down to the end of Microchips data sheet you read this LOL
Time to increment counter variable = Internal instruction cycle x 28
(we must count the zero) = 500nS x 256 = 128μS
So it is 256 when doing the math funny there a lot of people that believe it's 255

This is where zero counts LOL
Posted: 8 years 6 months ago by Graham Mitchell #6880
Graham Mitchell's Avatar
So that means that TMR0 has to only roll over 6 times for mS to equal mS >= 10000 ?.

Then I wouldn't have thought that this would be very accurate over time?.
At first glance it may appear to be a very inaccurate method.. Keep in mind the difference is carried forward - which ensures no timing data is lost.

The oscillator/hardware could be configured to accommodate more-natural timing.. I think when I wrote that short guide I'd considered just that, although kept it at 20Mhz as most PIC new comers would probably be using that hardware by default.

So yes; after the first second, the mS counter (with a scale of 1000) would equal 11466. One second is deducted from the counter, leaving 1466. The next interrupt would add 1638, resulting with 3104, and so on (difference carried forward).

The overall result; at any one-second interval the program would be accurate to within 1.638mS. At large scale (and in the perfect world); the interrupt that occurred after 100 hours would be accurate to within 1.638mS (6000 perfect seconds, plus a possible 1.638mS lag).

This has been a good discussion; I've merged the topic with the comments for the TMR0 article.
Posted: 8 years 6 months ago by odessa #6884
odessa's Avatar
Thanks to all for explaining this, and thanks for posting my question Gav

I wasn't taking into account the -10000 and carry over but it makes perfect sense now
Posted: 8 years 6 months ago by be80be #6886
be80be's Avatar
Gram the Pic multc-cal give the same numbers
Posted: 8 years 6 months ago by Graham Mitchell #6887
Graham Mitchell's Avatar
Not sure I'm on the same page Burt - It is showing us a different result as well.. The Multi Calc has a setting for "Re-load cycles" which is a minimum of 1 cycle. You've also defined a pre-load setting (1). The overall result is 1.6332mS.

Reloading the timer is a common approach, however, I'm using the timer's overflow flag (and not re-loading the timer).

Forum Activity

  • No posts to display.

Member Access