Side-by-side comparison
| Parameter | Polling | Interrupt Driven I/O |
|---|---|---|
| CPU Utilisation During Wait | 100% — CPU loops continuously checking flag | Near 0% — CPU executes main task or sleeps |
| Response Latency | Up to one full polling interval (deterministic if interval known) | Hardware latency only: 2–5 µs on ARM Cortex-M4 (12 cycles) |
| Power Consumption | High — CPU cannot enter low-power mode while polling | Low — CPU sleeps between events; STM32L4 at 4 µA in Stop mode |
| Code Complexity | Simple — single while-loop, easy to debug | Moderate — ISR, NVIC configuration, volatile shared variables |
| Determinism | Fully deterministic if single event source | ISR preemption can disturb main loop timing |
| Multiple Event Sources | Must check each source in sequence — increases latency | NVIC prioritises interrupts; higher-priority ISR preempts lower |
| Critical Section Handling | Not needed — no preemption | Required — disable interrupts around shared data access |
| Typical MCU Example | PIC16F877A checking UART receive flag in main loop | STM32F4 EXTI interrupt on GPIO pin, UART RXNE interrupt |
| RTOS Suitability | Polling inside a task is acceptable in RTOS context | Natural fit — ISR posts to queue; task blocks on queue receive |
| Use Case | Simple, single-task firmware; polling ADC at known rate | Asynchronous events: UART receive, timer overflow, GPIO edge |
Key differences
Polling burns CPU cycles proportional to the polling rate — a 1 kHz polling loop consumes 100% of a 1 MHz processor just checking one flag. Interrupts are asynchronous: the ARM Cortex-M4 NVIC saves context and vectors to the ISR in 12 clock cycles (0.75 µs at 16 MHz), far faster than any polling loop can check. The interrupt approach requires volatile-qualified shared variables and critical sections (\_\_disable\_irq() / \_\_enable\_irq() on ARM) to prevent data corruption. Polling is unbeatable for simplicity in bare-metal, single-task firmware; the moment a second event source or power budget appears, interrupts win.
When to use Polling
Use polling when the firmware is single-task, the event is frequent and predictable (such as reading an ADC every 1 ms), and power draw is not constrained. Example: a PIC16F877A-based LED dimmer polls TMR0 overflow flag at 8 kHz in a tight main loop to update the PWM duty cycle.
When to use Interrupt Driven I/O
Use interrupt-driven I/O when events are infrequent or unpredictable, power consumption must be minimised, or multiple peripherals need servicing. Example: an STM32L4 weather sensor node configures EXTI line 3 to trigger on a GPIO edge from a rain gauge reed switch, waking from Stop mode at 4 µA and logging to SPI flash in the ISR.
Recommendation
For battery-powered or multi-event embedded systems, choose interrupt-driven I/O every time — CPU sleep states and asynchronous response are worth the added complexity. Use polling only when the event rate equals the loop rate (so polling wastes nothing) or when the simplicity of no shared-variable management is worth it. Any system running FreeRTOS should use interrupts posting to queues, never busy-polling in a task.
Exam tip: Examiners ask students to explain interrupt latency on a Cortex-M processor and list the steps the NVIC performs between the interrupt assertion and the first ISR instruction — know: stacking 8 registers, vector fetch, pipeline flush — total 12 cycles.
Interview tip: An embedded systems interviewer at a product company will ask how you share data safely between an ISR and main code — answer: declare the variable volatile, and wrap the main-code access in a critical section using disable/enable interrupt instructions or a mutex if running RTOS.