Timer and Counter with PIC18F4550-compressed

Timer and Counter with PIC18F4550

Basically, a timer is a clock that controls an event sequence at a fixed amount of time. Timers are used for the precise delay generation and also used to trigger an activity before and after a predetermined time and to measure the time elapsed between two successive events. Here in this chapter how to use a timer and counter with PIC18F4550 is described in detail.

Timers in Microcontroller

The timer inside a microcontroller is a free running binary counter. The counter increments for each pulse applied to it. The counter counts continuously from 0 to (2^n)-1 where n is the number of bits. In PIC18F4550, there are 8-bit and 16-bit timers. The timer takes the internal clock as a reference clock, while the counter counts external clocks or pulses applied through port pins. So basically timer is a counter with an internal clock.

The main advantage of timers and counters is that it works independent of microcontroller CPU and the timer values can be read whenever needs. Basically, a standard microcontroller consists of 1 or more hardware timer modules of different bit lengths.

  • 8-bit timers- capable of counting 0-255
  • 16-bit timer – capable of counting 0-65535

The initial values of the timer register can be set by the user and can be used to generate required counts.

Below is a functional diagram of a timer module.

timer module-functional-diagram

Counters

Timers are also called counters because they are used to count external events. Timers are mainly used for counting or measuring external events.

For example in the case of a visitor counter, the sensor placed for detecting the presence of a person goes high when someone crosses the door. The output of the sensor is connected to the Timer Clock Input Pin of the microcontroller. The timer register inside the microcontroller increments each time when a person crosses the door. The value can be later read by the CPU.

Prescalar

Prescalar is a configurable clock-divider circuit. It can be used to divide the clock frequency input to the timer module. For example, if the instruction clock is 5MHz and we use a prescaler of 2 to divide it which effectively make the clock 2.5MHz. So each counting time will increase from 0.2 µs to 0.4 µs.

In PIC microcontroller, timer module provides 256, 128, 64, 32, 16, 8, 4, 2 and 1. 1 is actually prescaler bypassed.

Overflow

Overflow means the counter reached its maximum output and roll over to zero. The microcontroller has an overflow flag to indicate the overflow of the counter and generates overflow interrupts. This gives an additional option to count the number of times the counter overflowed and it extends the range of the counter. For example, if we need to count up to 512, we can use the 8-bit timer and overflow flag. After two overflows, the count must be 512 (256+256).

How to Use Timer with PIC18F4550

PIC18F4550 consists of four hardware timers namely Timer 0, Timer 1, Timer 2, Timer 3. Timer 2 is an 8-bit timer and all others are 16-bit timers.

Timer 0

The Timer0 module has the following features:

  • Software selectable operation as a timer or counter in both 8-bit or 16-bit modes
  • Readable and writable registers
  • Dedicated 8-bit, software programmable prescaler
  • Selectable clock source (internal or external)
  • Edge select for external clock
  • Interrupt on overflow

Timer 1

The Timer1 timer/counter module incorporates these features:

  • Software selectable operation as a 16-bit timer or counter
  • Readable and writable 8-bit registers (TMR1H and TMR1L)
  • Selectable clock source (internal or external) with device clock or Timer1 oscillator internal options
  • Interrupt on overflow
  • Module reset on CCP special event trigger
  • Device clock status flag (T1RUN)

Timer 2

The timer2 module timer incorporates the following features:

  • 8-bit timer and period registers (TMR2 and PR2, respectively)
  • Readable and writable (both registers)
  • Software programmable prescaler (1:1, 1:4 and 1: 16)
  • Software programmable postscaler (1:1 through 1:16)
  • Interrupt on TMR2 to PR2 match
  • Optional use as the shift clock for the MSSP module

TIMER3

The Timer3 module timer/counter incorporates these features:

  • Software selectable operation as a 16-bit timer or counter
  • Readable and writable 8-bit registers (TMR3H and TMR3L)
  • A Selectable clock source (internal or external) with device clock or Timer1 oscillator internal options
  • Interrupt on overflow
  • Module reset on CCP special event trigger

Implementing Timer0 in PIC18F4550

The Functioning of the timer0 can be understood from this block diagram.

timer-0-module-in-pic18f4550-block-diagram

The Timer 0 stores the value TMR0 registers, TMR0H is the high byte and TMR0L is the lower byte. The higher byte will be useful when using 16-bit mode. Timer0 is worked as timer and counter. In timer mode, the module increments on every clock by default, unless a prescaler value is written into TMR0 register. Clear the T0CS bit(5th bit) of T0CON register to select the timer mode.

Register Configuration

T0CONbits.T08BIT = 0;           // 16-bit mode selected
T0CONbits.T0CS = 0;             // Internal clock selected (timer mode ON)
T0CONbits.PSA = 1;              // Prescalar NOT assigned
T0CONbits.TMR0ON = 1;           // Turn ON the timer

Using Prescalar in Timer

For using prescaler, clear the PSA bit and assign the corresponding value in T0PS2, T0PS1 and T0PS0 registers.

prescalar-timer

For example, to assign a prescaler of value 2

T0CONbits.T08BIT = 0;           // 16-bit mode selected
T0CONbits.T0CS = 0;             // Internal clock selected (timer mode ON)
T0CONbits.PSA = 0;              // Prescalar assigned
T0CONbits.T0PS0 = 0;            // Prescalar values
T0CONbits.T0PS1 = 0;            // Prescalar values
T0CONbits.T0PS2 = 0;            // Prescalar values
T0CONbits.TMR0ON = 1;           // Turn ON the timer

Implementing Counter Using Timer0 Module in PIC18F4550

Set the T0CS bit in T0CON register to select the Counter mode. Timer0 increments based on the pulse on pin RA4/T0CKI. The T0SE bit in T0CON register determines the rising edge or falling edge. Setting this bit will make it a rising edge and clearing it will select a falling edge. The procedure of delay calculation is described in the chapter Timer Delay Implementation

Register Configuration

T0CONbits.T0SE = 0;             // falling edge selected
T0CONbits.T0CS = 0;             // Internal clock selected (timer mode ON)
T0CONbits.PSA = 1;              // Prescalar NOT assigned
T0CONbits.TMR0ON = 1;           // Turn ON the timer

Using Timer0 Interrupt

Every time an overflow occurs at (FF to 00 in 8-bit mode and FFFF to 0000 in 16-bit mode). The setting of corresponding bits in INTCON registers to generate an interrupt.

Peripheral interrupt enable (GIE), global interrupt enable (PEIE), Timer0 interrupt enable (TMR0IE) bits to activate it. Overflow will set TMR0IF bit.

Firmware using XC8 timers library

In the above timer code, we didn’t use any built-in library for learning. For the actual usage, we don’t need to write register level definitions for each time, instead, we can use a built-in library in XC8 compiler.

In the below firmware, we use 16-bit mode with 256 prescaled with an interrupt. The PORT B will complement in every overflow.

#include <xc.h>
#include <plib/timers.h>       // include header files
#include "config.h"             // define configuration bits in this file

unsigned char timerconfig;

void main(void) 
    {
        TRISB=0x00;                 // make PORTB as output
        LATB0=1;                    // make PORTB high
        /* set timer configuration 
         * Timer interrupt on
         * Set timer 16 bit
         *setting prescalar 256
         */ 
        timerconfig= TIMER_INT_ON & T0_16BIT & T0_SOURCE_INT & T0_PS_1_256 ;
        
        OpenTimer0(timerconfig); // Load timer 0 configuration
        WriteTimer0(0xB3B4);     // Load starting value to the timer 0
        INTCONbits.TMR0IF = 1;   // Enable timer interrupts
    
        ei();                    // enabling interrupt
    
        while(1);                 // Runs forever (wait here)
    }
   

void interrupt TimerOverflow()  // Interrupt service routine
    {
        di();                        // disable interrupt forawhile 
        if(INTCONbits.TMR0IF == 1)   // When timer overflows
        {
            LATB0 = ~LATB0;          // make PORTB LOW
            INTCONbits.TMR0IF = 0;   // clear the timer0 interrupt flag
            WriteTimer0(0xB3B4);     // load the timer value
        }
        ei();                       // Enable interrupt

    }