by Jon Wilder
The on-chip timer on PICs is confusing to some. But, not to worry…it’s probably one of the simplest on-chip peripherals to use on a PIC. In this article, we’ll use the PIC16F628A as our example of why that’s true.
Timer 0 (TMR0) is one of three timers available on the 16F628A that is running constantly — there’s no way to turn it off. It’s basically an 8-bit wide register in the SFRs and a clock source controls its value. The clock source for TMR0 can be either an external strobe signal fed in on pin RA4/T0CKI (T0CKI = Timer 0 Clock Input), or it can be the internal instruction cycle clock. When operating with an external strobe, it can be the counter that “counts” the strobe pulses of an external strobe source. When operating with the instruction cycle clock, it works as a timer. For this reason, you will sometimes hear engineers refer to TMR0 as the “timer/counter”.
The value residing in the TMR0 register starts at 0x00, and then rises by a factor of one upon every low-to-high transition of the clock source. You can also configure it to increment on the high-low or the low-high transition by using an external clock source. Basically, it’s just a register that counts pulses from one of two possible clock pulse sources as high as 0xFF (decimal 255). For this example, we will use the instruction clock operating as a timer.
The instruction clock runs at 1/4th the Fosc frequency. This means that if Fosc = 4MHz, our instruction clock runs at 1MHz, or one instruction per microsecond. In this scenario — assuming we don’t assign the prescaler TM — TMR0’s value is incremented on every pulse of the instruction clock. At a 1MHz instruction clock frequency, this increments the value in register TMR0 once every microsecond (1/μ).
Because the TMR0 register is only 8 bits wide, the maximum value that TMR0 can be incremented to is 0xFF, or decimal 255. At that point, it will roll over back to zero upon the next clock pulse. Once this rollover occurs, the Timer 0 interrupt flag is set (T0IF) in the INTCON register.This flag must be cleared in software.
Since TMR0 is currently running at one increment per μ, this means that the rollover occurs and the T0IF flag gets set once every 256μ (0x00 – 0xFF or 0-255 for 256 increments). If we wanted to know when a period of 256μ has passed, we can clear TMR0, clear the T0IF flag, then poll interrupt flag T0IF until T0IF goes high (rollover occurred), and continue on with our program.
Of course, a timer wouldn’t be useful without some way to slow it down to different increment rates. This is the job of the “prescaler”. The prescaler allows us to prescale the timer so that instead of it being incremented on every clock pulse, we can have it increment every two clock pulses, every four, every eight, etc…all the way up to every 256 clock pulses. If we were to set the prescaler to 1:256 with a 1MHz instruction clock, the timer would be incremented once every 256μ. Since our TMR0 register can count up to 256, it would count how many times a 256μ period passes up to 256 times. This means T0IF would get set every 65.5mS (milliseconds…256μ x 256 = 65.5mS).
The four registers of TMR0
There are four registers associated with TMR0: TMR0, OPTION_REG, INTCON, and TRISA. Let’s look at these in detail.
Register TMR0 holds the value that is being incremented by the clock source. Any writes to this register (i.e. clrf TMR0, movwf TMR0, bsf TMR0, X) will also clear the prescaler back to a 1:2 prescale. This means you will have to reset the prescaler back to where you had it every time you write to register TMR0 if you had it set anywhere other than 1:2.
| RBPU |INTEDG| T0CS | T0SE | PSA | PS2 | PS1 | PS0 |
The OPTION register holds the control bits for the following:
- Timer 0 Clock Source (T0CS)
- Timer 0 Source Edge Select Bit (TOSE)
- Prescaler Assign Bit (PSA)
- Prescaler Rate Select Bits (PS2-PS0)
T0CS – Selects the clock source for TMR0. Default is 1/T0CKI. Clearing this bit will select the instruction clock as the TMR0 clock source.
T0SE – Selects whether TMR0 is incremented on the rising or falling edge of T0CKI. Default is 1/Increment on falling edge. Only applies when T0CKI is the clock source.
PSA – This sets whether the prescaler is assigned to TMR0 or the Watchdog Timer. Default is 1/Prescaler assigned to WDT. When assigned to the WDT, TMR0 runs on a 1:1 prescale (increments on every pulse of the clock source).
PS2-PS0 – Sets the prescaler rate between 1:2 – 1:256 in bit-divisible values. Default is 111/1:256 (1:128 if assigned to the WDT).
The interrupt control register INTCON contains both the TMR0 interrupt control bit (T0IE) as well as the TMR0 interrupt flag bit (T0IF). If TMR0 interrupt is enabled (T0IE = 1…disabled by default), the program counter will jump to the interrupt handler code upon TMR0 overflow when T0IF gets set (T0IF must be cleared in software prior to exiting the interrupt). However, since T0IF continues to run normally whether the TMR0 interrupt is enabled or disabled, it’s always available for polling if you do not want TMR0 to trigger an interrupt.
If you use multiple interrupts, you will have to poll all of your interrupt flags to determine which interrupt triggered the interrupt condition, then jump it to the applicable code.
The TRISA register contains the port direction bit for RA4/T0CKI. To configure RA4 as the TMR0 clock source input, TRISA bit 4 must be set to configure it as input.
If we omit the bottom two lines of code so that we’re not working with a TMR0 interrupt, we can still poll the TMR0 interrupt flag to know when the overflow happens:
Using TMR0 as a counter
You can also configure a TMR0 as a pulse counter. To do this, you configure T0CKI as the clock source by setting bit T0CS in OPTION_REG. Bit T0SE in OPTION_REG will control whether the TMR0 register gets incremented on the falling edge of the pulse source (1) or on the rising edge of the pulse source (0).
You can implement one of the other two hardware timers to set up a sample interval, or you could do it in software with delay loops. Set up the code where it calls the delay loop after which the TMR0 is sampled to see how many pulses were counted during the set delay interval.
And that’s all there is to know about TMR0. Simple right?
About the Author
Jon Wilder is a freelance electronics engineer and electronics enthusiast for over 20 years. He spent four years in the US Navy as an aviation electronics technician. Jon has also been playing the guitar since age 13 and started integrating electronics and music from age 15. Jon built his first vacuum tube amp at age 17. “Musical electronics”, says Jon, is his love and passion.
Jon is also a frequent contributor and passionate member of Electro-Tech-Online, an electrical engineering membership community. On Electro-Tech-Online, you can ask questions and get answers from your fellow engineers on everything from microcontrollers, renewable energy, and automotive electronics, to circuit simulation and design. Additionally, there are MCU-specific forums for 8051/8951, AVR, ARM, Arduino, Oshonsoft Project, as well as a Code Repository where members share snippets of code.
Follow Jon on Twitter at @PICmcuguy.