Question.5
After uploading and executing the code below on an Arduino UNO, apply the given digital signal to PIN 2 (External Interrupt INT0). What will be the final updated value of the counter variable after the signal is applied?
Code
volatile unsigned long counter = 0;
void setup() {
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), incrementCounter, LOW);
}
void loop() {
}
void incrementCounter() {
counter++;
}Digital Signal:

Signal that pauses the main program to handle a specific event, then resumes execution from where it stopped.

Why do we need it?
Interrupts allow a microcontroller to respond quickly to important or time-sensitive events without being stuck in checking for them, making programs faster and more efficient.
Common Interrupt Sources in Microcontrollers:
An Interrupt Service Routine (ISR) is a special function that runs automatically when an interrupt occurs, handling the event and then returning control to the main program.
Best practices for writing ISRs in microcontrollers:
printf() – These are slow and not safe in ISRs.volatile to prevent compiler optimizations.ISR Syntax for different Architectures:
void _ _interrupt() ISR(void)void _ _interrupt() ISR(void) { /* code */ }ISR(VECTOR_NAME)ISR(TIMER1_COMPA_vect) { /* code */ }void Handler_Name(void) void TIM2_IRQHandler(void) { /* code */ }The Interrupt Vector Table (IVT) is a special memory table in microcontrollers that contains the addresses of Interrupt Service Routines (ISRs). When an interrupt occurs, the microcontroller looks up this table to determine the correct ISR to execute.
For example, the IVT of ATmega328P:

The Controller checks for an interrupt after each machine cycle. If an interrupt occurs, it is handled as follows

Interrupt nesting is when a microcontroller allows a higher-priority interrupt to interrupt a currently running ISR. This ensures critical tasks are handled quickly, even during another ISR is execution.
Understanding Nested Interrupts on Arduino UNO
In the Arduino UNO, global interrupts are disabled automatically at the start of any ISR to avoid nested execution.
However, if your application needs to handle more urgent interrupts during an ongoing ISR, you can call sei() within the ISR. This manually re-enables global interrupts and allows other interrupts to be nested inside.
Example Code (Enabling Nested Interrupts)
ISR(TIMER0_OVF_vect) {
// By default, interrupts are disabled inside ISR
sei(); // Re-enable global interrupts (allows nested interrupts)
// ISR logic here
}
With this, any other interrupt will get served inside the ISR, irrespective of its priority
In microcontrollers, an interrupt race condition occurs when both the main program and an ISR access or modify a shared resource (such as a global variable) without proper coordination.
This can cause unexpected results due to overlapping or incomplete operations.
Without using techniques like disabling interrupts temporarily or declaring the variable as volatile, the program may behave unpredictably.
Consider the following Arduino UNO example where a shared variable counter is modified by both the main code and a timer overflow ISR:
volatile uint8_t counter = 0;
void ISR_TIMER0_OVF_vect(void) {
counter++; // Interrupt modifies counter
}
int main() {
cli(); // Disable global interrupts
if (counter > 0) {
counter--; // Main code modifies the same counter
}
sei(); // Enable global interrupts
}
Explanation:
volatile ensures correct access to the counter variable.cli()/sei() create a critical section, preventing the ISR from interrupting the main code during access.What is Atomicity?
Some code must run without interruption to avoid errors or data corruption. To protect it, interrupts are turned off before the critical part and turned back on after.
Interrupt latency is the time delay between when an interrupt occurs and when the ISR starts executing.
It depends on factors like current instruction execution, interrupt priority, and system overhead. Lower latency allows faster response to real-time events.
In ATmega328P, the typical latency is around 4–5 clock cycles after an interrupt is enabled and a signal is received. (~250–312.5 ns at 16 MHz).
In AVR microcontrollers, each peripheral (like Timer, ADC, UART) uses key interrupt registers:
TIMSK enables timer interrupts; UCSRB enables UART interrupts.TIFR holds timer interrupt flags (like overflow). Flags clear automatically when the ISR runs or by writing ‘1’.EICRA configures edge triggering for external interrupts INT0 and INT1.ADCSRA for ADC).Global interrupts must be enabled via the GIE bit in SREG (using the SEI instruction).
AVR interrupt priorities are fixed by the vector table order.
For other microcontrollers (e.g., ARM Cortex-M):
Interrupt enabling, flag status, and configuration are similar but often managed by separate peripheral registers plus a central interrupt controller (like NVIC), which handles priorities and masking more flexibly.
Generic Interrupt Setup Flow (Microcontroller)
TIMx_DIER, UARTx_CR1, or EXTI_IMR) to enable the interrupt generation for that peripheral.Set Interrupt Priority (Optional but Recommended)
If supported by the MCU (e.g., ARM Cortex-M), assign a priority using NVIC (Nested Vectored Interrupt Controller) functions/registers.
Enable Interrupt in NVIC (if applicable)
Turn on the interrupt at the global controller level (e.g., using NVIC_EnableIRQ() for ARM Cortex-M devices).
__enable_irq() in ARM).1.Arduino UNO example for LED toggling on switch press using an interrupt.

Code
volatile bool ledState = false; // Shared between ISR and main
void setup() {
pinMode(10, OUTPUT); // LED pin
pinMode(2, INPUT_PULLUP); // INT0 pin with internal pull-up
attachInterrupt(digitalPinToInterrupt(2), toggleLED, FALLING); // Interrupt on falling edge
}
void loop() {
// Nothing here; LED toggling handled by ISR
}
void toggleLED() {
ledState = !ledState; // Toggle state
digitalWrite(10, ledState); // Reflect the new state on the LED
}
2. Arduino UNO example for LED blinking using timer interrupt

Code
// LED connected to pin 13 (built-in onboard LED)
const int ledPin = 13;
volatile bool ledState = false;
void setup() {
pinMode(ledPin, OUTPUT);
// Step 1: Disable interrupts during timer setup
cli();
// Step 2: Set Timer1 to CTC mode (Clear Timer on Compare Match)
TCCR1A = 0; // Normal port operation
TCCR1B = 0; // Clear previous settings
TCCR1B |= (1 << WGM12); // Set CTC mode
// Step 3: Set compare value for desired time interval
// Formula: OCR1A = (F_CPU / (2 * Prescaler * Frequency)) - 1
// For 500ms: 1Hz = toggle every 1s → interrupt every 0.5s = 2Hz
// Using Prescaler = 1024: OCR1A = 16,000,000 / (2 * 1024 * 2) - 1 ≈ 3905
OCR1A = 3905;
// Step 4: Set prescaler to 1024 and start the timer
TCCR1B |= (1 << CS12) | (1 << CS10); // Prescaler = 1024
// Step 5: Enable Timer1 Compare Match A interrupt
TIMSK1 |= (1 << OCIE1A);
// Step 6: Enable global interrupts
sei();
}
ISR(TIMER1_COMPA_vect) {
ledState = !ledState;
digitalWrite(ledPin, ledState);
}
void loop() {
// Main loop does nothing – LED blinks via interrupt
}