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 }