PIC16F690 Blinks an LED Using TMR0 Interrupts
An independent hardware timer can deliver a regular pulse. In this lab TMR0 is set up to provide an interrupt every 200 μsec. Inside the interrupt service routine a counter is used to multiply the 200 μsec period by 50, yielding a coarser period of 10 msec. This tick is a message to the main line run loop, which further extends it by a factor of 50 to produce the required blink interval.
Below is the schematic. We will add to it for labs 4 &5. The components labeled *-sbb go on the solderless breadboard.
Show text file suitable for cut-and-paste.
/*
* File: lab3.c
* Author: Brian
*
* DESCRIPTION: blink LED more precisely using the timers available on the
* PIC16F690.
*/
#include <xc.h>
#include <stdint.h>
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
// CONFIG
#pragma config FOSC = INTRCIO // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD)
#pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = OFF // Brown-out Reset Selection bits (BOR disabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode is disabled)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)
// function prototypes
void Initialize(void);
void interrupt ISR(void);
#define TMR0_PRELOAD 58 // see "Timer 0 Configuration"
#define DEBOUNCE_FACTOR 50 /* number of sysClocks per debounce
* 0.2 msec * 50 = 10 msec
*/
#define BLINK_FACTOR 50 // 10 msec * 50 = 500 msec
/* ISR modifies these */
volatile uint32_t sysClock; // running time in units of 200 microseconds
volatile bit eventDebounceTick = 0; // signal from TMR0 ISR to task loop
void main(void) {
static uint8_t blinkCounter = 0;
Initialize(); // Initialize relevant registers
RC5 = 1; // RED LED
RC4 = 1; // GREEN LED
while (1) {
/******************
* DEBOUNCE TASK
******************/
if (eventDebounceTick) {
/*
* timer initiated by ISR
* main line for a particular event tells me what work to do
*/
eventDebounceTick = 0; // clear event
blinkCounter += 1;
if (blinkCounter == BLINK_FACTOR) {
blinkCounter = 0;
// blink period, derived from debounce rate
// sysclock period = 0.2 msec
// debounce period = 50 * sysclock = 10 msec
// blink period = 50 * 10 = 500 msec
RC4 = ~RC4; // toggle GREEN LED
}
}
}
}
void Initialize(void) {
/*
* I/O Configuration
*
* OUTPUT PINS
* 6 RC4 = GREEN LED
* 5 RC5 = RED LED
*/
// PORTC
TRISC4 = 0; // output
TRISC5 = 0; // output
/*
* Timer 0 Configuration
*
* TMR0 will interrupt (on overflow) 5000 times per second (T0i = 200 usec).
* system clock = Fosc/4 = 1 MHz
* period T = 1 microsecond
* want T0i = 0.0002 sec
* prescale must be power of 2. The smaller the prescale, the closer we
* can hit our target. The other constraint is the size of TMR0, 8-bits
* We can preload TMR0 with 0 through 255.
* So, with a prescale of 1, we need TMR0 to count
* N = 0.0002 / 0.000001 = 200
* Per data sheet 5.1.1, we must compensate for a 2 cycle delay
* TMR0_PRELOAD = 256 - (200 - 2) = 58
*
* Resulting TMR0 interrupt interval:
* T0i = 200 * 0.000001 = 0.0002 second
*/
OPTION_REGbits.PS = 0b111; // XXX Don't need prescaler
OPTION_REGbits.PSA = 1; // Assign prescaler to WDT
OPTION_REGbits.T0CS = 0; // Select FOSC/4 as Timer0 clock source
OPTION_REGbits.T0SE = 0; // X don't care
/* lines above may be achieved by a single statement:
* OPTION_REG = 0b00001111;
*/
T0IF = 0; // Clear the TMR0 interrupt flag
TMR0 = TMR0_PRELOAD; // use this value consistently
T0IE = 1; // Enable TMR0 interrupts
GIE = 1; // Enable interrupt system
}
void interrupt ISR(void) {
static uint8_t isrCoarseCounter = 0;
/**********************
* TIMER 0
**********************/
RC5 = 1; // RED LED
if (T0IE && T0IF) // TMR0 overflow
{
T0IF = 0; // Clear the TMR0 interrupt flag
TMR0 = TMR0_PRELOAD; // use this value consistently
sysClock += 1; // bump sysClock
isrCoarseCounter += 1;
if (isrCoarseCounter == DEBOUNCE_FACTOR) {
isrCoarseCounter = 0;
eventDebounceTick = 1; // fire event
}
// _delay(150); // what happens when the ISR is too long?
}
RC5 = 0; // RED LED
}
