Overview
These routines illustrate various uses of the TMR0 and External interrupts.
Program TMR0_1.C uses TMR0 to provide 1 ms timing so as to generate a 500 Hz tone and also flash an LED on and off with a period of 8 seconds.
Program TMR0_2.C expands this to also toggle another LED in response to an interrupt on RB.0.
Program TMR0_3.C counts the number of events appearing on RB.0 over a one second period and displays the result on a serial LCD.
// Program TMR0_1.C (CCS PCM, PIC16F84)
//
// Configuration. LED on RB.3. Speaker through capacitor on RB.2
//
// Illustrates use of TMR0 to time for 1 ms. Generates 500 Hz tone on
// speaker on RB.2 and continually flashes LED on RB.3 on for 4 secs
// and off for 4 seconds.
//
// Note that TMR0 is configured for CLOCK, assigned to OSC, prescale
// by 4. Thus, 1.00 MHz / 4 = 250 KHz. Period = 4 usecs. Thus, TMRO
// is loaded with the twos comp of 250 to achieve interrupt timing of 1 ms.
//
// Copyright, Peter H. Anderson, Georgetown, Co, SC, Mar, '99
#case
#include <16f84.h>
#include <defs_f84.h>
#define LED_FL_PIN rb3 // RB3
#define SPKR_PIN rb2
#define LED_FL_DIR trisb3
#define SPKR_DIR trisb2
#define T_TICK (~250) + 1 // see program description
long time_count = 4000;
void main(void)
{
// turn on RBPU resistors, make SPKR_PIN and LED_FL_PIN outputs
not_rbpu = 0;
SPKR_DIR = 0; // make SPKR and LED_FL outputs
LED_FL_DIR = 0;
LED_FL_PIN = 0;
SPKR_PIN = 0;
// configure TMR0, CLK as source, prescale assigned to TMR0, prescale = 4
t0cs = 0;
psa = 0;
ps2=0; ps1=0; ps0=1;
// configure interrupts, T0IE=1, GIE=1
t0if=0;
t0ie=1;
gie=1;
while(1)
{
#asm
CLRWDT
#endasm
}
}
#int_rtcc rtcc_handler()
{
TMR0 = T_TICK; // load TMR0
SPKR_PIN = !SPKR_PIN;
if (--time_count==0)
{
LED_FL_PIN = !LED_FL_PIN;
// reverse LED every 4000 ms
time_count=4000;
}
// note that compiler takes care of clearing interrupt flag
}
#int_default default_handler(void)
{
}
// Program TMR0_2.C (CCS PCM, PIC16F84)
//
// Configuration. LEDs on RB.3 and RB.1. Speaker through capacitor on
// RB.2. Switch on RB.0 to force external interrupt.
//
// Illustrates use of TMR0 to time for 1 ms. Generates 500 Hz tone on
// speaker on RB.2 and continually flashes LED on RB.3 on for 4 secs
// and off for 4 seconds.
//
// Also reverses state of LED on RB1 when external interrupt on RB.0.
//
// Note that TMR0 is configured for CLOCK, assigned to OSC, prescale
// by 4. Thus, 1.00 MHz / 4 = 250 KHz. Period = 4 usecs. Thus, TMRO
// is loaded with the twos comp of 250 to achieve interrupt timing of 1 ms
//
// Copyright, Peter H. Anderson, Georgetown Co, SC, Mar, '99
#case
#include <16f84.h>
#include <defs_f84.h>
#define LED_FL_PIN rb3
#define SPKR_PIN rb2
#define LED_EV_PIN rb1
#define EXT_INT_PIN rb0
#define LED_FL_DIR trisb3
#define SPKR_DIR trisb2
#define LED_EV_DIR trisb1
#define EXT_INT_DIR trisb0
#define IN 1
#define OUT 0
#define T_TICK (~250) + 1 // see program description
long time_count = 4000;
void main(void)
{
// turn on RBPU resistors, make SPKR, LED_FL and LED_EV outputs.
// make EXT_INT an input
not_rbpu = 0;
SPKR_DIR = OUT;
LED_FL_DIR = OUT;
LED_EV_DIR = OUT;
EXT_INT_DIR = IN;
SPKR_PIN =0;
LED_FL_PIN=0;
LED_EV_PIN =0;
// configure TMR0, CLK as source, prescale assigned to TMR0, prescale = 4
t0cs = 0;
psa = 0;
ps2=0; ps1=0; ps0=1;
t0if = 0;
t0ie = 1; // configure interrupts, T0IE=1
// configure for external interrupt
intedg = 0;
intf = 0;
inte = 1;
// enable interrupts and loop
gie=1;
while(1)
{
#asm
CLRWDT
#endasm
}
}
#int_rtcc rtcc_handler(void)
{
TMR0 = T_TICK; // load TMR0
SPKR_PIN = !SPKR_PIN;
if (--time_count==0)
{
LED_FL_PIN = !LED_FL_PIN;
// reverse LED every 4000 ms
time_count=4000;
}
// note that compiler takes care of clearing interrupt flag
}
#int_ext ext_int_handler(void)
{ // toggle LED_EV
LED_EV_PIN = !LED_EV_PIN;
}
#int_default default_handler(void)
{
}
// Program TMR0_3.C
//
// Pulse source (such as the Morgan Logic Probe) on RB.0. Ser LCD
// on RA.0.
//
// Counts the number of events on RB.0 over one second.
//
// TMR0 is used to time for 1000 ms. External interrupt is used for
// counting.
//
// Displays count in hexadecimal on serial LCD on RA.0.
//
// Note that TMR0 is configured for CLOCK, assigned to OSC, prescale
// by 4. Thus, 1.00 MHz / 4 = 250 KHz. Period = 4 usecs. Thus, TMRO
// is loaded with the twos comp of 250 to achieve interrupt timing of 1 ms
//
// Copyright, Peter H. Anderson, Georgetown Co, SC, Mar, '99
#case
#include <16f84.h>
#include <defs_f84.h>
#define EXT_INT_DIR trisb0
#define EXT_INT_PIN rb0
#define OUT 0
#define IN 1
#define T_TICK (~250) + 1 // see program description
#define TxData 0 // RA.0 - interface to serial LCD
// lcd routines
void delay_ms(long t);
void delay_10us(int t);
void lcd_init(void);
void out_RAM_str(int *s);
void lcd_hex_byte(int val);
void lcd_dec_byte(int val, int digits);
int num_to_char(int val);
void lcd_char(int ch);
void lcd_new_line(void);
void count(void);
long ct, timer_count; // globals
void main(void)
{
not_rbpu = 0; // enable PORTB pullups
while(1)
{
timer_count = 1000; // count for 1000 ms
count();
lcd_init();
lcd_hex_byte((int)(ct >> 8) & 0xff); // display a long int
lcd_hex_byte((int) (ct & 0xff));
delay_ms(4000);
}
}
void count(void)
{
// note that timer_count and ct are globals
// timer_count is the time to count
// ct is the count over this time
ct = 0;
// configure TMR0
t0cs=0; // use CLK as source
psa=0; // use OSC clock
ps2=0; ps1=0; ps0=1; // prescale by 4
t0if = 0; // clear any existing interrupt
t0ie = 1;
// configure external interrupt
EXT_INT_DIR = IN;
intedg = 0;
intf = 0;
inte = 1;
//
gie = 1;
while(timer_count) // wait until timer_count is decremented to zero
{
#asm
CLRWDT
#endasm
}
inte=0;
t0ie=0;
while(gie) // turn off GIE and be sure they are off
{
gie=0;
}
}
#int_rtcc rtcc_handler(void)
{
TMR0 = T_TICK; // load TMR0
--timer_count; // decrement global timer_count
t0if=0; // clear flag - not sure if necessary
}
#int_ext ext_int_handler(void)
{
++ct;
intf = 0; // not usure if necessary
}
#int_default default_handler(void)
{
}
// delay routines
void delay_10us(int t)
{
#asm
BCF STATUS, RP0
DELAY_10US_1:
CLRWDT
NOP
NOP
NOP
NOP
NOP
NOP
DECFSZ t, F
GOTO DELAY_10US_1
#endasm
}
void delay_ms(long t) // delays t millisecs
{
do
{
delay_10us(100);
} while(--t);
}
// LCD Routines
int num_to_char(int val) // converts val to hex character
{
int ch;
if (val < 10)
{
ch=val+'0';
}
else
{
val=val-10;
ch=val + 'A';
}
return(ch);
}
void lcd_char(int ch) // serial output to PIC-n-LCD, 9600 baud
{
int n, dly;
// start bit + 8 data bits
#asm
BCF STATUS, RP0
MOVLW 9
MOVWF n
BCF STATUS, C
LCD_CHAR_1:
BTFSS STATUS, C
BSF PORTA, TxData
BTFSC STATUS, C
BCF PORTA, TxData
MOVLW 32
MOVWF dly
LCD_CHAR_2:
DECFSZ dly, F
GOTO LCD_CHAR_2
RRF ch, F
DECFSZ n, F
GOTO LCD_CHAR_1
BCF PORTA, TxData
CLRWDT
MOVLW 96
MOVWF dly
LCD_CHAR_3:
DECFSZ dly, F
GOTO LCD_CHAR_3
CLRWDT
#endasm
}
void lcd_init(void) // sets TxData in idle state and resets PIC-n-LCD
{
#asm
BCF STATUS, RP0
BCF PORTA, TxData
BSF STATUS, RP0
BCF TRISA, TxData
BCF STATUS, RP0
#endasm
lcd_char(0x0c);
delay_ms(250);
}
void lcd_new_line(void) // outputs 0x0d, 0x0a
{
lcd_char(0x0d);
delay_ms(10); // give the PIC-n-LCD time to perform the
lcd_char(0x0a); // new line function
delay_ms(10);
}
void out_RAM_str(int *s)
{
while(*s)
{
lcd_char(*s);
++s;
}
}
void lcd_hex_byte(int val) // displays val in hex format
{
int ch;
ch = num_to_char((val>>4) & 0x0f);
lcd_char(ch);
ch = num_to_char(val&0x0f);
lcd_char(ch);
}
void lcd_dec_byte(int val, int digits)
// displays byte in decimal as either 1, 2 or 3 digits
{
int d;
int ch;
if (digits == 3)
{
d=val/100;
ch=num_to_char(d);
lcd_char(ch);
}
if (digits >1) // take the two lowest digits
{
val=val%100;
d=val/10;
ch=num_to_char(d);
lcd_char(ch);
}
if (digits == 1) // take the least significant digit
{
val = val%100;
}
d=val % 10;
ch=num_to_char(d);
lcd_char(ch);
}