Lab3 Timer Interrupt

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.

lab3-sch

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
}

Leave a Reply

Your email address will not be published. Required fields are marked *

Tools, techniques, design approaches and fun!