nerdegutta.no
PIC16F690 - Day 5: Combined Interrupts – Timer0 + Button Input
10.08.25
Embedded
Goal:Learn to combine multiple interrupts:
Use Timer0 to blink an LED (every 500 ms)
Use RB4 input to toggle whether the blinking is enabled
Hardware:LED on RB0
Button on RB4 (with pull-down resistor)
Concepts:Enabling multiple interrupts (Timer0 + PORTB Change)
Distinguishing interrupt sources inside the ISR
Debounce logic
Using a global flag to control behavior
Code 1: Timer0 LED Blink + Button Control via RB4 Interrupt
#define _XTAL_FREQ 4000000
#include < xc.h > // Remove extra spaces
#include < stdint.h >
// CONFIG
#pragma config FOSC = INTRCIO
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config MCLRE = OFF
#pragma config BOREN = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
volatile uint8_t overflow_count = 0;
volatile uint8_t blinking_enabled = 1;
void __interrupt() isr(void) {
// Timer0 interrupt (every ~65.5ms with 1:256 prescaler)
if (T0IF) {
T0IF = 0;
if (blinking_enabled) {
overflow_count++;
if (overflow_count >= 8) { // 8 * 65ms = ~520ms
RB0 = !RB0; // Toggle LED
overflow_count = 0;
}
}
}
// PORTB Change interrupt (RB4-RB7)
if (RBIF) {
__delay_ms(20); // Simple debounce
if (RB4 == 1) {
blinking_enabled ^= 1; // Toggle blink enable flag
}
RBIF = 0; // Clear interrupt-on-change flag
}
}
void main(void) {
TRISB0 = 0; // RB0 output (LED)
TRISB4 = 1; // RB4 input (button)
ANSEL = 0;
ANSELH = 0;
PORTB = 0;
// Setup Timer0
OPTION_REGbits.T0CS = 0; // Internal clock
OPTION_REGbits.PSA = 0; // Prescaler to Timer0
OPTION_REGbits.PS = 0b111; // 1:256 prescaler
// Enable Timer0 interrupt
TMR0 = 0;
T0IE = 1;
// Enable RB change interrupt for RB4
IOCB4 = 1;
RBIF = 0;
RBIE = 1;
// Global Interrupt Enable
PEIE = 1;
GIE = 1;
while (1) {
// Nothing to do in main; logic handled in ISR
}
}
Code 2: Same functionality without using ISR (polling + timer flag)
#define _XTAL_FREQ 4000000
#include < xc.h > // Remove extra spaces
#include < stdint.h >
// CONFIG
#pragma config FOSC = INTRCIO
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config MCLRE = OFF
#pragma config BOREN = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
uint8_t blinking_enabled = 1;
void main(void) {
TRISB0 = 0; // LED output
TRISB4 = 1; // Button input
ANSEL = 0;
ANSELH = 0;
PORTB = 0;
// Timer0 setup
OPTION_REGbits.T0CS = 0;
OPTION_REGbits.PSA = 0;
OPTION_REGbits.PS = 0b111; // 1:256
TMR0 = 0;
uint8_t prev_btn = 0;
uint8_t overflow_count = 0;
uint8_t last_timer = 0;
while (1) {
// Polling button
if (RB4 && !prev_btn) {
__delay_ms(20); // debounce
if (RB4) {
blinking_enabled ^= 1; // Toggle flag
}
}
prev_btn = RB4;
// Poll Timer0
if (TMR0 < last_timer) { // Overflow occurred
overflow_count++;
if (blinking_enabled && overflow_count >= 8) {
RB0 = !RB0;
overflow_count = 0;
}
}
last_timer = TMR0;
}
}