Editorial Solution

We will use external interrupts to detect button presses instantly and a timer interrupt for debouncing and switching LED patterns.

  • Interrupts on Arduino Uno
    • External Interrupts
      • INT0 (Pin 2, higher priority).
      • INT1 (Pin 3).
    •  Timer Interrupts
      • Timer0, Timer1, and Timer2 are available.

Hardware connection 

  • Push button  → Pin 2 (internal pull-up)
  • 4 LEDs  → Arduino UNO (GPIO pins 3,4,5,6)
  • Use a 330 Ohm resistor for each LED to limit current to 10mA.

Circuit connection

Firmware

We will implement the following approach for the task:

  • External Interrupt (INT0): Used to detect button presses for switching LED patterns.
  • Timer 1: Used to handle the debouncing delay of the button.
  • Timer 2: Used to generate a 1-second delay for transitioning between LED patterns.

Code

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

 

Code Explanation

Interrupts

1. Button Press ISR (isButtonPress())

  • This ISR is triggered on the falling edge of the button press.
  • It starts Timer1 to handle debouncing by
    • Setting the prescaler for Timer1 to 1024.
    • Enabling the Timer1 Compare Match interrupt (TIMSK1) for 20ms.

2. Timer1 Compare Match ISR (TIMER1_COMPA_vect())

  • Triggered after 20 ms when button press for debounce handling:
    • If the button is still pressed (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.
    • Timer1 is then disabled.

3. Timer2 Compare Match ISR (TIMER2_COMPA_vect())

  • Called every 10ms:
    • Periodic LED Updates:
      • Every 100 interrupts (1 second) or if buttonPressed == true then LED pattern is updated based on patternIndex and pattern.
      • patternIndex alternates between 0 and 1.

 Timer1 Configuration

  • Timer1 is configured in CTC (Clear Timer on Compare Match) mode.
  • The prescaler is set to 1024, and OCR1A is set to 312 for a 20ms debounce interval:
    • Interrupt Time =(OCR1A + 1 * Prescaler ) / 16000000

                       = 20 msec.

Timer2 Configuration

  • Timer2 is also set to CTC mode with a prescaler of 1024.
  • OCR2A is set to 156 for a 10ms interrupt interval:
    • Interrupt Time =(OCR2A + 1 * Prescaler ) / 16000000

                       = 10 msec

Output

Hardware Setup

 

Video

Submit Your Solution