BOLT Microcontroller LITE with PIC18F2550

Microchip PIC PIC18F2550 Using TMR0 Interrupts

by Lewis Loflin


In this demo we will use TMR0 to generate X number of interrupts and increment an integer variable called count. After a number of counts we will toggle a LED connected to Rb0.

The PIC18F2550 has three timers labeled TMR0 (8-bit or 16-bit), TMR1 (16-bit) and 8-bit TMR2.

In this case I'm using the BOLT micro-controller board with a 20 mHz external crystal through internal multipliers is operating at 48 mHz.

The 48 mHz is divided by four then one merely selects a prescaler value (3 bits) from 2 to 256. In this case I selected divide by two giving me 6 mHz at TMR0.

The period of 6 mHz is 1.67 e-7. TMR0 operates in either 8-bit or 16-bit mode. Using 16-bit I can have a maximum value of 2^16 or 65536. Multiply 65536 * 1.67 e-7 = 0.011 ; 1/.011 = 91.5 Let's say 92 interrupts will equal 1 second.

This demo also shows how to setup MPLAB fir using interrupts.

Using the timers.h Library

The C18 compiler timers.h library can be useful for those not inclined to flipping individual register bits or tearing their hair out with frustrating and poorly written spec sheets. We have four functions to learn:

CloseTimerx Disable timer x.
OpenTimerx Configure and enable timer x.
ReadTimerx Read the value of timer x.
WriteTimerx Write a value into timer x.

Setting up the timer is the most critical step and one still has to setup interrupt bits. For example:

OpenTimer0( TIMER_INT_ON & T0_16BIT & T0_SOURCE_INT & T0_EDGE_RISE & T0_PS_1_2 )

This did the very same thing I did with T0CON = 0x80 and INTCONbits.TMR0IE = 1. I find this rather useless as an electronics person, but for those that are only programmers it could be a big help. See page 61 in MPLAB C18 Libraries.

What we did here was turn on TMR0 in 16-bit mode, enabled interrupts, use rising edge on the clock pulse, set the prescaler to divide by 2.

Using TMR0 in the 18F2550 Programming Example

See page 127 in spec sheet. 8 or 16 bits.
T0CON 0x0FD5, TMR0L 0x0FD6, TMR0H 0x0FD7


bit 7 TMR0ON: Timer0 On/Off Control bit
1 = Enables Timer0
0 = Stops Timer0

bit 6 T08BIT: Timer0 8-bit/16-bit Control bit
1 = Timer0 is configured as an 8-bit timer/counter
0 = Timer0 is configured as a 16-bit timer/counter

bit 5 T0CS: Timer0 Clock Source Select bit
1 = Transition on T0CKI pin
0 = Internal instruction cycle clock (CLKO)

bit 4 T0SE: Timer0 Source Edge Select bit
1 = Increment on high-to-low transition on T0CKI pin
0 = Increment on low-to-high transition on T0CKI pin

bit 3 PSA: Timer0 Prescaler Assignment bit
1 = Timer0 prescaler is NOT assigned. 
Timer0 clock input bypasses prescaler.
0 = Timer0 prescaler is assigned. 
Timer0 clock input comes from prescaler output.

bit 2-0 T0PS2:T0PS0: Timer0 Prescaler Select bits
111 = 1:256 Prescale value
110 = 1:128 Prescale value
101 = 1:64 Prescale value
100 = 1:32 Prescale value
011 = 1:16 Prescale value
010 = 1:8 Prescale value
001 = 1:4 Prescale value
000 = 1:2 Prescale value

Setting up INTCON

Setting up interrupt with the INTCON register is very different in advanced processors such as the 18F2550 as opposed to 16F628A I've used with assembly. While both have interrupt capability, the 18F2550 has so many interrupt sources it has priority bits. They don't seem to change anything in this simple demo, but be cautious. I set RCONbits.IPEN to gain control of the other interrupts which I disabled for this. The following we must be concerned with:

INTCONbits.GIE must be set to enable interrupts;

INTCONbits.TMR0IE TMR0 Overflow Interrupt Enable bit must be set;

INTCONbits.TMR0IF TMR0 Overflow Interrupt Flag bit must be cleared by software before another interrupt can occur.

#include <p18cxxx.h>
#include <delays.h>

#define HIGH 1
#define LOW 0
#define Rb0  PORTBbits.RB0
#define delay_us	Delay10TCYx

// prototype declarations 
void timer0_isr(void);  
// interrupt service routine
void setup(void); 
void init_18F2550(void);
void delay_ms(int);

// See c018i.c in your C18 compiler dir 
extern void _startup( void ); 
#pragma code _RESET_INTERRUPT_VECTOR = 0x000800

void _reset( void ) 
  _asm goto _startup _endasm 

#pragma code _HIGH_INTERRUPT_VECTOR = 0x000808

void _high_ISR (void)
  _asm goto timer0_isr _endasm

#pragma interrupt timer0_isr
#pragma code

unsigned int count = 0;    // Define variable cnt

void main()   {
  setup(); // setup PIC18F2550
  for (;;)   {   
  } // idle here do nothing// end main

void setup()   { 
  PORTB = 0x00;
  // make sure IPEN is set
  RCONbits.IPEN = 1; //enable priority levels
  T0CON = 0x80; // TMR0 on, prescaler 1/2 16-bit mode
  // 48 MHZ / 4 = 12 MHZ Prescale / 2 = 6 MHZ
  // 1 / 6 MHZ = 1.67 e-7 * 2^16 = .011 Sec.
  INTCONbits.TMR0IE = HIGH; // enable TMR0 int
  // must be cleared after every interrupt
  INTCONbits.TMR0IF = LOW;  // Clear TMR0 IRQ flag
  INTCONbits.GIE = HIGH; 	//enable global interrupts
  TMR0L = 0;
  TMR0H = 0;	

// interrupt service routine
// after 91 interrupts or 1 second toggle Rb0. 
void timer0_isr() {
  // Interrupt causes count to be incremented by 1
  if (count == 91)   { 
    // 91 * .0109 Sec. = approx. 1 Sec
    Rb0 = !Rb0; // toggle Rb0
    count = 0;
  INTCONbits.TMR0IF = 0; 
  // clear TMR0 interrupt flag 

//Ports initialized A, B, C
void init_18F2550(void)
  ADCON1=0x0F; 	// disables converters A/D
  TRISB=0;	//PORTB are outputs
  PORTB=0;	// off LEDs
  //Ra4,Ra5 are inputs (MICRO SWITCHES). 
  // RA0,RA1,RA2,RA3 outputs
  //Rc0,Rc1 are inputs (MICRO SWITCHES)
  //select pull-up resistors on port B (Rb4...Rb7).

//also available: delay_us(); delay in microseconds.
// 48MHZ / 4 = 12 MHZ; 1/12 MHZ = 8.33e-8
// 8.33e-8 * 12,000 = 1 mS.
// see delay.h in C18 compiler libraries
void delay_ms(int i)
  long int j;

See How I got into Electronics


Videos, Links, Downloads for the PIC18F2550 BOLT

Related Videos, Links