We can use the tone()
function for toggling LEDs. However, it has some limitations
tone()
function generates frequency on only 1 pin at a time.tone()
function can generate a lowest frequency of 31 Hz.So we will use Timer1 and Timer2 for toggling of pins.
void loop()
.1 << WGM12
).62.5 kHz
(16MHz/256
).ISR (TIMER1_COMPA_vect)
toggles LED1 every 100ms.1 << WGM21
).#include <avr/io.h>
#include <avr/interrupt.h>
// Pin Definitions
#define LED1_PIN 9 // LED1 toggles every 100ms
#define LED2_PIN 10 // LED2 toggles every 360ms
// LED States
volatile bool led1State = LOW;
volatile bool led2State = LOW;
// Software counter for LED2
volatile uint8_t led2Counter = 0;
void setup() {
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
// Timer1 Configuration (CTC Mode for LED1 - 100ms interval)
TCCR1A = 0;
TCCR1B = (1 << WGM12) | (1 << CS12); // CTC Mode, Prescaler 256
OCR1A = 6249; // Corrected OCR1A for 100ms
TIMSK1 = (1 << OCIE1A); // Enable Timer1 Compare Match Interrupt
// Timer2 Configuration (CTC Mode for LED2 - ~10ms interval)
TCCR2A = (1 << WGM21); // CTC Mode
TCCR2B = (1 << CS22) | (1 << CS21) | (1 << CS20); // Prescaler 1024
OCR2A = 155; // Generates an interrupt every ~10ms
TIMSK2 = (1 << OCIE2A); // Enable Timer2 Compare Match Interrupt
sei(); // Enable Global Interrupts
}
void loop() {
while (true) {
// Microcontroller remains busy in this loop continuously monitoring a critical task.
}
}
// Timer1 ISR - Toggles LED1 every 100ms
ISR(TIMER1_COMPA_vect) {
led1State = !led1State;
digitalWrite(LED1_PIN, led1State);
}
// Timer2 ISR - Toggles LED2 every 360ms using a software counter
ISR(TIMER2_COMPA_vect) {
led2Counter++;
if (led2Counter >= 36) { // 36 x 10ms = 360ms
led2Counter = 0;
led2State = !led2State;
digitalWrite(LED2_PIN, led2State);
}
}
Timer1 Configuration (for LED1 - 100ms interval)
TCCR1A = 0;
TCCR1B = (1 << WGM12) | (1 << CS12); // CTC Mode, Prescaler 256
OCR1A = 6249; // Corrected OCR1A for 100ms
TIMSK1 = (1 << OCIE1A); // Enable Timer1 Compare Match Interrupt
TCCR1A = 0: Clears Timer1 Control Register A.
TCCR1B
: Configures Timer1 in Clear Timer on Compare Match (CTC) mode with a prescaler of 256.
OCR1A
= 6249: Sets the compare match value for 100 ms intervals.
TIMSK1
: Enables the Timer1 Compare Match A interrupt.
Timer2 Configuration (for LED2 - ~10ms interval)
TCCR2A = (1 << WGM21); // CTC Mode
TCCR2B = (1 << CS22) | (1 << CS21) | (1 << CS20); // Prescaler 1024
OCR2A = 155; // Generates an interrupt every ~10ms
TIMSK2 = (1 << OCIE2A); // Enable Timer2 Compare Match Interrupt
TCCR2A: Configures Timer2 in CTC mode.
TCCR2B
: Sets the prescaler to 1024.
OCR2A
= 155: Sets the compare match value for ~10ms intervals.
TIMSK2
: Enables the Timer2 Compare Match A interrupt.
ISR is an Interrupt Service Routine, it is serviced when an associated interrupt is triggered.
Timer1 ISR (for LED1)
ISR(TIMER1_COMPA_vect) {
led1State = !led1State;
digitalWrite(LED1_PIN, led1State);
}
This ISR is triggered every 100ms (as configured by Timer1).
It toggles the state of LED1_PIN by flipping led1State and updating the pin output.
Timer2 ISR (for LED2)
ISR(TIMER2_COMPA_vect) {
led2Counter++;
if (led2Counter >= 36) { // 36 x 10ms = 360ms
led2Counter = 0;
led2State = !led2State;
digitalWrite(LED2_PIN, led2State);
}
}
This ISR is triggered every ~10ms (as configured by Timer2).
It increments the led2Counter
variable.
When led2Counter reaches 36 (36 x 10 ms = 360ms), it resets the counter, toggles the state of LED2_PIN, and updates the pin output.
Summary
LED1: Toggles every 100ms using Timer1.
LED2: Toggles every 360ms using Timer2 and a software counter.
The microcontroller uses hardware timers and interrupts to achieve precise timing without blocking the main program execution.
The loop()
function is intentionally left empty, allowing the microcontroller to focus on handling interrupts and other tasks.