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); }