We will use external interrupts to detect button presses instantly and a timer interrupt for debouncing and switching LED patterns.
We will implement the following approach for the task:
#define BUTTON_PIN 2 // Push button connected to pin 2 (interrupt pin)
// Pattern Array (2 sets of 2 patterns)
const int patterns[2][2][4] = {
{ { 1, 1, 1, 1 }, // pattern 1
{ 0, 0, 0, 0 } },
{ { 1, 0, 1, 0 }, // pattern 2
{ 0, 1, 0, 1 } }
};
// LED Pin Array
const uint8_t ledPins[] = { 3, 4, 5, 6 };
volatile bool pattern = 0;
volatile bool patternIndex = 0;
volatile bool buttonPressed = false;
volatile bool timerFlag = true;
volatile uint8_t isr_call_count = 0;
void setup() {
// Set all LED pins as OUTPUT
for (uint8_t i = 0; i < 4; i++) {
pinMode(ledPins[i], OUTPUT);
}
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Attach interrupt to button pin, trigger on FALLING edge (button press)
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), isButtonPress, FALLING);
/* Configuring Timer 1 for 20 msec delay */
TCCR1A = 0; // Normal operation
TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10); // CTC mode, prescaler = 1024
OCR1A = 312; // Set compare match value (for 20ms interrupt with 1024 prescaler)
/* Configuring Timer 2 for 10 msec delay */
TCCR2A = 0; // Normal operation
TCCR2B = (1 << WGM22) | (1 << CS22) | (1 << CS21); // CTC mode, prescaler = 1024
OCR2A = 156; // Set compare match value (for 10ms interrupt with 1024 prescaler)
TIMSK2 |= (1 << OCIE2A); // enable Timer2 Compare A interrupt
// enable global interrupt
sei();
}
void loop() {
while (true) {
// microcontroller is busy in doing critcal task countinously.
}
}
// Interrupt Service Routine (ISR) to handle button press
void isButtonPress() {
if (timerFlag == true) {
// set prescaler to 1024
TCCR1B |= (1 << CS12);
TCCR1B |= (1 << CS10);
// Enable Timer1 Compare interrupt
TIMSK1 = (1 << OCIE1A);
timerFlag = false;
}
TCNT1 = 0; // Reset timer count to 0.
}
// Timer1 interrupt service routine for 20 msec debounce delay check
ISR(TIMER1_COMPA_vect) {
timerFlag = true;
if (digitalRead(BUTTON_PIN) == LOW) {
pattern = !pattern;
buttonPressed = true;
}
// Disable Timer1 interrupt after 20ms
TIMSK1 &= ~(1 << OCIE1A); // Disable Timer1 Compare A interrupt
// Stop Timer1 by clearing prescaler bits
TCCR1B &= ~(1 << CS12);
TCCR1B &= ~(1 << CS10);
TCCR1B &= ~(1 << CS20);
}
//Timer2 to ISR is called after every 10ms delay
ISR(TIMER2_COMPA_vect) {
isr_call_count++;
// update count after every 1 sec or if button press ocurre.
if (isr_call_count == 100 || buttonPressed) {
digitalWrite(ledPins[0], patterns[pattern][patternIndex][0]);
digitalWrite(ledPins[1], patterns[pattern][patternIndex][1]);
digitalWrite(ledPins[2], patterns[pattern][patternIndex][2]);
digitalWrite(ledPins[3], patterns[pattern][patternIndex][3]);
patternIndex = !patternIndex;
isr_call_count = 0;
buttonPressed = false;
}
}
Interrupts
1. Button Press ISR (isButtonPress()
)
2. Timer1 Compare Match ISR (TIMER1_COMPA_vect()
)
digitalRead(BUTTON_PIN) == LOW
), it marks the button press as valid (buttonPressed = true
) and changes the pattern by doing pattern = !pattern;
, considering the button is pressed for a straight 20 ms.3. Timer2 Compare Match ISR (TIMER2_COMPA_vect()
)
buttonPressed == true
then LED pattern is updated based on patternIndex
and pattern
.patternIndex
alternates between 0 and 1.Timer1 Configuration
= 20 msec.
Timer2 Configuration
= 10 msec