Arduino Timer0 in Assembly Explained Like I’m Teaching My Grandma
by BoboacaC in Teachers > Pre-K
58 Views, 0 Favorites, 0 Comments
Arduino Timer0 in Assembly Explained Like I’m Teaching My Grandma


Many beginners — and even some experienced makers — get confused or stuck when trying to understand or use Timer0 on the ATmega328, the microcontroller inside the classic Arduino Nano and Uno. Abstract definitions in datasheets can be intimidating, so in this guide we’ll break things down into clear, practical steps you can actually use in your own projects.
Supplies

In this tutorial will use Arduino nano (with atmega328p) an usb cable and an pc or phone tat can comunicate over serial and https://costycnc.github.io/avr-compiler-js/ compiler and uploader based on webserial
Timer0: Opening the Drawer

When you pull out the Timer0 drawer, you’ll see several compartments inside. Each one is a register — a small storage space inside the microcontroller — and each has a job to do.
See this Atmega328p datasheet at page 279
Activating Timer0

The first step is opening the right drawer.
For Timer0, that drawer is called 0x25 — the TCCR0B register.
Inside, you’ll find 8 little switches (bits). At the beginning, they’re all in the off position (0).
So at the start, the register (drawer) 0x25 has the value 0b00000000 — all switches are off.
Each switch controls something different:
- Some set the speed (prescaler)
- Others decide how the timer counts
- One can even stop everything completely
To “activate” Timer0, we need to flip the right switches so it knows:
“Okay, Timer0, start counting — and use this speed!”
In the next step, we’ll see which switches to flip to get it running.
See this Atmega328p datasheet at page 279
Step 3 — Waking Up Timer0 and Counting

Step 3 — Waking Up Timer0 and Counting
Now that we know the switches in drawer 0x25 (TCCR0B), let’s see how Timer0 actually starts counting.
- If the last three switches (bits 0–2) are all 0, the timer is off.
- If the value is greater than 0, the timer wakes up and starts incrementing the counter in register 0x26 (TCNT0).
Here’s how the last three bits affect the timer speed:
For example, if we set the last three bits to 101, the clock is divided by 1024:
16,000,000/1024=15,625 ticks per second
Each tick increments TCNT0 (0x26). Since TCNT0 is an 8-bit register, it counts from 0 to 255, then overflows back to 0 and starts again.
- With a prescaler of 1024, this overflow happens roughly 60 times per second, giving us a timer tick frequency that can be used for blinking LEDs, generating PWM, or timing events.
See this Atmega328p datasheet at page 279
Writing the ASM to Activate Timer0

Now that we know which switches to flip, it’s time to write some ASM code to turn them on.
We want to set the last three switches in drawer 0x25 (TCCR0B) to 0b101.
Unfortunately, we cannot write directly to the drawer in one instruction. Instead, we use a temporary register (r16) as a helper:
- Load the value into r16:
- Write r16 into drawer 0x25:
Once this is done, Timer0 is awake.
- Every tick of the timer increments register 0x26 (TCNT0).
- When TCNT0 reaches 255, it overflows back to 0 and starts counting again.
Puoi anche aggiungere un piccolo commento esplicativo sotto:
This simple routine makes Timer0 start counting at the speed defined by the last three bits we set (in this case, 101 → prescaler 1024 → ~60 overflows per second).
Generating a Square Wave on PD6 Using Timer0 Compare Match

The timer has two dedicated pins for Timer0 operations: PD6 and PD5.
We will use PD6.
We will also use register 0x27 (OCR0A) to compare with the counter 0x26 (TCNT0).
For example, if we write the value 128 in register 0x27:
- The counter 0x26 counts up
- Every time the counter equals the value in 0x27, PD6 changes its state (toggles)
- Then the counter continues counting and repeats the process
This way, PD6 automatically toggles each time the counter reaches the compare value.
This is the easy mode to work with Timer0.
Timer0 also offers many other modes, but they are a bit more complex.
Configuring TCCR0A for CTC and OC0A Toggle

This scenario uses the following TCCR0A register (0x24) configuration:
- WGM01 = 1 → CTC mode
- COM0A1:0 = 0b01 → toggle OC0A on compare match
As you can see, we set the bits to create this scenario.
Note: I used the bit values directly to make it easier to understand. If I had used abstract names with >> or <<, it would have been more difficult for beginners.
In ASM:
- Load R16 with this configuration:
- Copy R16 into register 0x24:
Now everything is ready for the timer to operate with OC0A toggling on compare match.
See this Atmega328p datasheet at page 279
Complete ASM Example With Prescaler 1024.
The avr asm code complete:
How it works
- TCCR0A → sets CTC mode and toggle OC0A.
- TCCR0B → sets prescaler 1024 → slows down the timer.
- OCR0A = 128 → OC0A (PD6) toggles every time TCNT0 reaches 128 → produces a square wave ~50% duty.
- Loop does nothing; hardware timer toggles PD6 automatically.
And uncomented version (only code) that will use to compile :
📜 the Tale of a Timer – Told Like a Tech Fairy Story 🕰️
Let’s imagine Arduino as a big cabinet full of little drawers 🗄️, and inside lives a tiny man 👷♂️ who opens them, checks what’s inside, and does whatever the notes inside tell him.
At the start of our program, the little man opens drawer 0x0A (DDRD) and sees that compartment 6 has a marble in it (bit = 1). This means: “Set pin D6 as output!”. He does it and closes the drawer. ✅
See this Atmega328p datasheet at page 279
Then he opens 0x24 (TCCR0A) and finds the number 01000010:
He looks at the last two bits: 10. In his manual, it says:
So 1 0 = CTC mode: when the timer counter reaches the value in OCR0A (drawer 0x27), the timer resets to zero and starts counting again.
Then he checks the first two bits: 01. The manual says:
So 0 1 = Toggle OC0A: every time the timer matches OCR0A, pin D6 flips state (HIGH to LOW or vice versa).
He closes drawer 0x24 and moves on.
He opens 0x25 (TCCR0B) and sees 00000101:
The last 3 bits are 101:
So the timer ticks once every 1024 CPU clock pulses. It’s like the little man keeps a separate counter and, when it reaches 1024, he drops a marble into 0x26 (TCNT0).
Finally, he opens drawer 0x27 (OCR0A) and puts in the value 128:
Now the little man knows exactly what to do:
- Count ticks in TCNT0.
- When TCNT0 = 128, reset it to zero.
- Toggle pin D6.
The result? An automatic square wave, generated without the main program doing anything. 🎯
