Video summary
Level Up Your Arduino Code: Timer Interrupts
Main summary
Key takeaways
Main ideas / concepts
- Timer interrupts let an ATmega328P microcontroller run tasks at precise, regular times by using its hardware timers instead of software timing loops.
- Timers are like kitchen timers: they keep counting in the background while the CPU does other work, then interrupt the CPU when they reach certain conditions.
- The ATmega328P (used in Arduino Uno) provides three timers:
- Timer0 →
TCNT0(8-bit) - Timer1 →
TCNT1(16-bit, uses 2 bytes) - Timer2 →
TCNT2(8-bit)
- Timer0 →
- Timers count based on the system clock (typically 16 MHz on Arduino Uno/ATmega328P).
- A prescaler can divide the clock frequency to slow timer counting.
How timers count (system clock + prescaler)
- Each timer can be driven by the system clock or a divided version.
- Without prescaler:
- The timer register increments once per system clock pulse.
- For 16 MHz: clock period =
1 / 16,000,000 = 62.5 ns
- With prescaler:
- The timer increments less frequently (e.g., every 8, 64, 256, or 1024 clock cycles depending on configuration).
- Changing the prescaler changes the rate at which timer count increases.
Timer size and rollover (important for timing)
- Timer0 and Timer2 are 8-bit:
- Count 0 → 255, then roll over to 0
- Timer1 is 16-bit:
- Count 0 → 65,535, then roll over to 0
How interrupts are generated
Each timer can generate interrupts in multiple ways:
- Compare match interrupt
- Set an output compare value in a register (e.g.,
OCR1A). - When the timer count equals that value, the corresponding compare match interrupt fires.
- Set an output compare value in a register (e.g.,
- Overflow interrupt
- Fires when the timer rolls over from its maximum back to 0.
- Input capture interrupt (Timer1 only)
- When a signal level changes on a dedicated input pin (e.g., ICP1), Timer1 captures the counter value into
ICR1and triggers an input capture interrupt. - Useful for measuring time between pulses or frequency.
- When a signal level changes on a dedicated input pin (e.g., ICP1), Timer1 captures the counter value into
Arduino compatibility warning (what breaks if you change timers)
Several Arduino functions rely on timers:
- Using Timer0 can break
delay(),millis(), andmicros(). - Using Timer1 or Timer2 can interfere with
Servoandtone(). analogWrite()uses all three timers on the Uno; which timer it uses depends on the pin.
Example guidance:
- If manually using Timer1, avoid
analogWrite()on pins 9 and 10 (commonly tied to Timer1 on Uno).
Method / instruction set: Timer1 “Blinky” using compare match interrupts
Goal
- Blink the onboard LED (Arduino pin 13, which corresponds to ATmega328P port B bit 5 / PB5) at 1 Hz (500 ms on, 500 ms off) using Timer1 compare match interrupts.
Step-by-step setup
- Hardware target
- Arduino board with ATmega328P (e.g., Uno or RedBoard).
- LED mapping
- Arduino pin 13 = ATmega328P Port B, Pin 5 → use PB5.
1) Replace pinMode() / digitalWrite() with direct register I/O
- Set LED pin as output (LED is on Port B):
DDRB |= (1 << LED_PIN);
2) Demonstrate baseline blinking (non-interrupt version)
- In
loop():- Toggle the LED by flipping the bit in
PORTB:PORTB ^= (1 << LED_PIN);
- Then delay:
delay(500);
- Toggle the LED by flipping the bit in
This confirms basic behavior before moving to interrupts.
3) Configure Timer1 registers for interrupts
- Use Timer1 (chosen because it can count high and the video notes it’s not used by
delay()/millis()/micros()). -
Datasheet-based actions:
- Reset timer control register A:
TCCR1A = 0;
- Set prescaler (desired timing):
- Choose prescaler 256
- Set
CS12 CS11 CS10= 100
- Load timer counter:
TCNT1 = t1Load;(wheret1Load = 0)
- Set compare match value:
OCR1A = t1Comp;(wheret1Comp = 31250)
- Enable the Timer1 Compare Match A interrupt:
TIMSK1 = (1 << OCIE1A);
- Enable global interrupts:
sei();
- Reset timer control register A:
4) Implement the ISR (interrupt service routine)
- Enable/define ISR for compare match A:
ISR(TIMER1_COMPA_vect) { ... }
-
Inside the ISR:
- Toggle LED:
PORTB ^= (1 << LED_PIN);
- Reset timer counter (in the non-CTC version):
TCNT1 = t1Load;
- Toggle LED:
5) Determine the compare value for 500 ms timing
- Clock period with no prescaler:
1 / 16 MHz = 62.5 ns
- Convert 0.5 seconds to counts:
- No prescaler would require too many counts for 16-bit.
-
Prescaler experiments:
- Prescaler 8 → 1,000,000 (too big)
- Prescaler 64 → 125,000 (too big)
- Prescaler 256 → 31,250 (fits)
- Prescaler 1024 → 7,812.5 (not a whole number → slight timing error)
-
Final choice:
- Prescaler = 256
OCR1A = 31250
6) Main loop behavior when using interrupts
- Remove the LED toggle from
loop(). - Keep something harmless so the CPU appears active, e.g.:
delay(500);insideloop()(LED timing is handled by interrupts).
Expected result
- LED continues to blink at ~1 Hz.
- The video notes an oscilloscope check showing ~1 second between rising edges.
Optimization method: Use CTC mode to reduce ISR work
What changes
- Switch Timer1 to CTC (Clear Timer on Compare) mode:
- Hardware automatically resets the timer to 0 at each compare match.
- Result:
- The ISR no longer needs to manually reset
TCNT1.
- The ISR no longer needs to manually reset
Step-by-step changes for CTC mode
- Set CTC mode bits in
TCCR1B:- Configure mode 4
- Do:
- Clear
WGM13 - Set
WGM12
- Clear
- Modify ISR:
- Remove the line resetting
TCNT1 = t1Load;
- Remove the line resetting
- Expected behavior:
- Still blinks at ~1 Hz, but with a slightly shorter ISR.
Why it matters
- CTC saves one instruction.
- Shorter ISRs reduce latency and timing jitter.
Speakers / sources featured
- Video creator / presenter: The narrator introduces and explains timer interrupts and provides the code example (no specific name given in the subtitles).
- ATmega328P documentation (datasheet): Referenced as the source for register details and timer mode selection.
- Background music: Mentioned as
[background music](no specific track/artist named).