Design a task using a microcontroller with the following requirements
Note: We can generate pulses using any microcontroller.
Also, consider the following condition
Your void loop() should be as given below.
void loop() {
while (true){
//In this loop Microcontroller is busy in monitoring and performing important tasks constantly.
}
}
For example:
Code for pulse generation using Arduino UNO.
void setup() {
Serial.begin(115200); // Initialize serial communication at 115200 baud rate
pinMode(7, OUTPUT); // Set pin 7 as an output pin
digitalWrite(7, LOW); // Set pin 7 to LOW (initial state)
}
void loop() {
PORTD = 0x80; // Set the 7th bit of PORTD to HIGH (binary 10000000), turning on pin 7
PORTD = 0x00; // Set all bits of PORTD to LOW (binary 00000000), turning off pin 7
Serial.println("Pulse Generated"); // Print "Pulse Generated" to the Serial Monitor
delay(5000); // Wait for 5000 milliseconds (5 seconds) before repeating the process
}
Generated output pulse:
The pulse below is generated by the Pulse Generator after every 5 seconds.
The width of the pulse is around 64 nanoseconds.

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
}