AVR Bare-Metal Blinking

AVR Bare-Metal Blinking

No Arduino libraries. No HAL. Just registers.

An ATmega328P has DDRB, PORTB, and a 16-bit timer. We configure Timer1 in CTC mode with a 1 Hz compare match, toggle PB5 (the onboard LED pin) in the ISR, and end up with a binary that fits in 176 bytes. The Makefile uses avr-gcc and avrdude directly — no IDE required.

This exercise looks trivial, but it trains the exact muscle many developers skip: understanding cause and effect between register writes and hardware behavior. You do not “ask an API” to blink. You define direction bits, timer prescalers, compare values, and interrupt masks yourself.

Minimal mental model

  • DDRB configures PB5 as output.
  • TCCR1A/TCCR1B define timer mode and prescaler.
  • OCR1A sets compare threshold.
  • TIMSK1 enables compare interrupt.
  • ISR toggles PORTB bit for the LED.

When this chain is explicit, debugging gets faster. If timing is wrong, you inspect clock and prescaler. If the LED is dark, verify direction and pin. Each symptom maps to a small set of causes.

Why still do this in 2026

Bare-metal AVR is still a great teaching platform because feedback is fast and tooling is mature. You can compile, flash, and verify behavior in a few seconds, then iterate. Even if your production target is different, this discipline transfers directly to RISC-V, ARM, and RTOS-based projects.

Related reading:

2025-08-20