07 - AVR Timer + Counter Programming

Updated 4 Oct 2026

#Summarized

How to config microcontroller when we want to use timer/counter of that microcontroller.

AVR Timer/Counter Structure

Introduction: On/Off an LED Every 2 Seconds

  • A common method for creating a time delay in microcontrollers is using nested loops with register decrements. However, a more efficient way is using hardware timers.

Example: LED Blinking with Delay

The following assembly code toggles an LED every 2 seconds using a software delay subroutine (DELAY).

	.ORG 0  ; Origin of the program in memory
	
	; Stack Initialization (used for function calls)
	LDI R16, HIGH(RAMEND) ; Load the high byte of the RAM end address into R16
	OUT SPH, R16          ; Set the Stack Pointer High (SPH)
	LDI R16, LOW(RAMEND)  ; Load the low byte of the RAM end address into R16
	OUT SPL, R16          ; Set the Stack Pointer Low (SPL)
	
	; Pin Configuration
	SBI DDRD, 4           ; pinMode(4, OUTPUT);
	CBI PORTD, 4          ; digitalWrite(4, LOW);
	SBI DDRD, 5           ; pinMode(5, OUTPUT);
	CBI PORTD, 5          ; digitalWrite(5, LOW);
 
AGAIN: 
	SBI PORTD, 4      ; digitalWrite(4, HIGH);
	CBI PORTD, 5      ; digitalWrite(5, LOW);
	CALL DELAY        ; Call the delay subroutine
	CBI PORTD, 4      ; digitalWrite(4, LOW);
	SBI PORTD, 5      ; digitalWrite(5, HIGH);
	CALL DELAY        ; Call the delay subroutine
	RJMP AGAIN        ; Repeat loop
 
DELAY:  ; Delay subroutine
	LDI R20, 128
L1:	LDI R21, 200
L2:	LDI R22, 250
L3:	NOP
	NOP     
	DEC R22
	BRNE L3
	DEC R21
	BRNE L2
	DEC R20
	BRNE L1
	RET

This is the traditional method. Let's explore using timers instead!

Basic Idea of Timers/Counters

  • A microcontroller has a hardware timer/counter module that counts clock pulses. It serves two purposes:
    1. Timer Mode – Creates a time delay based on an internal clock.
    2. Counter Mode – Counts external events from an external clock signal.

Basic Components of Timers/Counters

  1. Inputs to Timer/Counter
    • Internal Clock Source (e.g., 16MHz quartz crystal oscillator)
      • Used for both instruction execution and timer operations.
    • External Clock Source (e.g., square wave input to a timer pin like T0).
  2. Timer/Counter Circuit
    • Detects rising or falling edges of clock pulses.
  3. Counter Register
    • Stores the number of counted clock pulses.
    • Example: An 8-bit register can count from 0x00 to 0xFF (0–255).
  4. Flag Bit
    • A 1-bit flag is set when the counter reaches a predefined value.

Timer vs. Counter

ModeInput SourceUsage
TimerInternal clock (e.g., 16MHz)Time delay
CounterExternal clock (e.g., function generator)Event counting
For now, we will focus on timers only.

ATmega328 Timers

The ATmega328 (used in Arduino Uno) has 3 timers:

  • Timer0 → 8-bit counter ✅ (Covered here)
  • Timer1 → 16-bit counter
  • Timer2 → 8-bit counter

Next, we’ll explore how to configure Timer0 for precise time delays. 🚀

Timer0: Timer Structure

A diagram of the Timer0 hardware structure is shown below:

  1. Input Source
    • Clk → Internal clock (16MHz crystal oscillator).
    • T0 → External clock input via PD4 pin.
  2. Edge Detector
    • Detects rising or falling edge of the external clock.
  3. MUX (Multiplexer)
    • Selects the clock input based on flags CS02, CS01, CS00.
  4. Control Unit
    • Updates the clock pulse count.
    • Uses WGM02, WGM01, WGM00 to set the timer mode.
    • TOV0 (Timer Overflow Flag) is set when TCNT0 overflows (0xFF → 0x00).
  5. TCNT0 Register
    • Stores the current clock pulse count.
    • 8-bit register (0x00 – 0xFF).
  6. OCR0A & OCR0B Registers
    • Store the target pulse count for comparison. (Store number of clock pulse we want!)
  7. OCF0A & OCF0B Flags
    • Set to 1 when TCNT0 == OCR0A or TCNT0 == OCR0B.

Timer0: Registers and Flags

TCNT0 register

  • Stands for Timer/Counter 0.
  • 8-bit register (memory address 0x46).
  • Stores the current pulse count and increments with each clock tick!
  • We can set it to start at any number between 0x00 – 0xFF.
  • When TCNT0 overflows (0xFF → 0x00), TOV0 flag is set.

Example Calculation

Given:

  • Initial TCNT0 = 0x00
  • Current TCNT0 = 0x6A
  • Clock frequency = 16MHz

How many clock pulses have passed?
How much time has elapsed?

TCCR0A and TCCR0B Registers

  • TCCR0A (Timer/Counter Control Register A)
    • 8-bit register, memory address: 0x44.
  • TCCR0B (Timer/Counter Control Register B)
    • 8-bit register, memory address: 0x45.
  • These registers are used to configure Timer0:
    • Operation mode (Normal, CTC, etc.)
    • Input source (Internal/external clock)
  • The structure of the TCCR0A register is shown below.
  • The structure of the TCCR0B register is shown below.

Timer Operation Modes (Selected via WGM02, WGM01, WGM00)

Timer operation mode.  There are 8 timer operation modes.  We use the flags WGM02, WGM01, WGM00 to set the Timer0 operation mode. Two modes will be studied here.

ModeWGM02WGM01WGM00Description
Normal000Counts to 0xFF, then resets to 0x00 (TOV0 flag set).
CTC (Clear Timer on Compare Match)010Counts to OCR0A value, then resets to 0x00 (OCF0A flag set). ไปจนถึงตัวเลขที่เรากำหนดไว้
Other modes---Not covered here.
  • The other modes are shown in the next table. (แต่เราจะสนใจแค่ Normal, CTC)

Example: Configure Timer0 for Normal Mode
; Set Normal mode
LDI R16, 0x00
OUT TCCR0A, R16

Timer Input Source (Selected via CS02, CS01, CS00)

CS02:00Input Clock Source
000No clock (Timer stopped)
001 - 101Internal clock (with/without prescaling)
110 - 111External clock

Example: Configure Timer0 with Internal Clock (No Prescaler)

Write an Assembly program to set Timer0 to work as follows.

; No prescale (use original clock frequency)
LDI R16, 0x01
OUT TCCR0B, R16

OCR0A and OCR0B Registers

  • OCR0A (Output Compare Register A)
    • Stores an 8-bit value that is compared with TCNT0.
    • When TCNT0 == OCR0A, OCF0A flag is set (=1).
    • Memory address: 0x47.
  • OCR0B (Output Compare Register B)
    • Similar to OCR0A, but with its own compare flag (OCF0B).
    • Memory address: 0x48.
  • We will use the OCR0A register in the CTC operation mode.

Example: Store 0xAA in OCR0A

LDI R16, 0xAA
OUT OCR0A, R16

TIFR0 Register (Timer/Counter Interrupt Flag Register 0)

  • Memory address: 0x35.
  • Contains flags that indicate timer events:
    • TOV0 → Set (=1) when TCNT0 overflows (0xFF → 0x00).
    • OCF0A → Set (=1) when TCNT0 == OCR0A.
    • OCF0B → Set (=1) when TCNT0 == OCR0B.

ก็คือถ้าใช้ Normal Mode จะเป็น SBIS TIFR0, 0
แต่ถ้าเป็น CTC Mode จะเป็น SBIS TIFR0, 1

  • Its structure is shown below.

Using Timer0 as a Timer

In this section, we will set up and use Timer0 as a timer (i.e., internal clock input). The following three modes are studied:

  1. Normal Mode (No Pre-Scaling Frequency)
  2. Normal Mode (With Pre-Scaling Frequency)
  3. CTC (Compare Match) Mode

Normal Mode (No Pre-Scaling Frequency)

In this mode, Timer0 operates with the following specifications:

  • Clock source: Internal 16 MHz crystal clock (ATmega328P)
  • Counting range: 0x00 to 0xFF (255)
  • Overflow behavior: When TCNT0 rolls over from 0xFF → 0x00, TOV0 flag is set (=1).

The operation of Normal Mode can be visualized below:

Steps to Generate a Time Delay Using Timer0

  1. Load TCNT0 register with an initial value.
  2. Set TCCR0A and TCCR0B registers as follows:
    • CS02:CS00 = 001 → No pre-scaling
    • WGM02:WGM00 = 000 → Normal mode
    • Other flags = 0
    • This results in:
      • TCCR0A = 0b00000000 = 0x00
      • TCCR0B = 0b00000001 = 0x01 (starts the timer)
  3. Monitor TOV0 flag and exit the loop when TOV0 = 1.
  4. Stop Timer0 by setting TCCR0B = 0x00.
    • มันก็ต้อง LDI Value เข้ามาก่อน แล้วค่อย Set ให้มันนะ!
  5. Clear TOV0 flag for the next round.
    • To clear an AVR flag, write 1 to it:
      LDI R20, 0x01
      OUT TIFR0, R20  ; Clear TOV0
  6. Repeat Step 1 for continuous operation.

Example: DELAY Subroutine Using Timer0

Consider the DELAY subroutine using Timer0, which starts from 0xF2.

Solution

  • Timer0 runs in the AGAIN loop.
  • The SBIS TIFR0, 0 instruction checks TOV0:
    • If TOV0 = 1, exit the loop.
    • If TOV0 = 0, stay in the loop.
  • Loop execution analysis:
    • SBIS takes 1 or 2 cycles (depending on condition).
    • RJMP takes 2 cycles.
    • Each loop iteration requires 3 cycles.
  • TCNT0 Increments:
    0xF2 → 0xF5 → 0xF8 → 0xFB → 0xFE → 0x01 (overflow, TOV0 is set)
    
    • Repeats 5 times before TOV0 is set.
    • Exiting loop takes 2 cycles.
  • Total cycles in loop: (3×5)+2=17 instruction cycles(3 \times 5) + 2 = 17 \text{ instruction cycles}
  • Total instruction cycles for DELAY subroutine: 17+14=31 cycles17 + 14 = 31 \text{ cycles}
  • Time delay calculation: 31×0.0625=1.9375 μs31 \times 0.0625 = 1.9375\ \mu s where 0.0625 µs is the period of a 16 MHz clock.

General Formula for Time Delay Calculation

From the previous example, the time delay formula for a DELAY subroutine using Timer0 is:

Time delay(μs)=[(⌈255−A+13⌉×3)+2+14]×0.0625\Huge\boxed{\text{Time delay}(\mu s) = \left[\left(\left\lceil\frac{255-A+1}{3}\right\rceil \times 3\right) + 2 + 14 \right] \times 0.0625}

where:

  • AA = Initial value of TCNT0
  • 255255 = 0xFF (maximum count)
  • ⌈\lceil ⌉\rceil = Ceiling operator

Design Problem:

Normal Mode with a Pre-Scaling Frequency

If we need a larger time delay, we can set Timer0 to work with a pre-scaled clock to slow down the timer increments. The available pre-scaling options are:

Pre-Scaling FactorEffect
clk/8Every 8 clock cycles → TCNT0 increments by 1
clk/64Every 64 clock cycles → TCNT0 increments by 1
clk/256Every 256 clock cycles → TCNT0 increments by 1
clk/1024Every 1024 clock cycles → TCNT0 increments by 1
![[Screenshot 2025-03-19 at 1.07.27 PM.pngcenter

Register Configuration

  • Mode Selection:
    • Set WGM02:WGM00 = 000 for Normal Mode. (อย่าลืมว่าตอนนี้เราก็ยังอยู่ใน Normal Mode นะ)
  • Pre-Scaling Selection:
    • Configure CS02:CS00 bits based on desired pre-scaling factor.

Example: Configuring Timer0 for clk/8

Problem Statement

Set Timer0 to Normal Mode with a clk/8 pre-scaling frequency.

Solution

Configure TCCR0A and TCCR0B as follows:

LDI R20, 0x00  
OUT TCCR0A, R20   ; Set Normal Mode  
 
LDI R20, 0x02  
OUT TCCR0B, R20   ; Set clk/8 and Start Timer  
What if we want clk/1024?

Simply modify TCCR0B as follows:

LDI R20, 0x05  
OUT TCCR0B, R20   ; Set clk/1024 and Start Timer  

Example: Time Delay with clk/8

Problem Statement:

Find the time delay for the following DELAY subroutine using clk/8 with a 16 MHz crystal.

DELAY:
    LDI R20, 0xF2  
    OUT TCNT0, R20  
    LDI R20, 0x00  
    OUT TCCR0A, R20  ; Set Normal Mode  
    LDI R20, 0x02  
    OUT TCCR0B, R20  ; Set clk/8 and Start Timer  
 
AGAIN:
    SBIS TIFR0, 0  
    RJMP AGAIN  
 
    LDI R20, 0x00  
    OUT TCCR0B, R20  ; Stop Timer  
    LDI R20, 0x01  
    OUT TIFR0, R20   ; Clear Overflow Flag  
 
    RET  
Solution:

Since clk/8 slows down the timer, each tick occurs every 8 clock cycles.

  1. Calculate Timer Overflow Condition:

    • TCNT0 starts at 0xF2.
    • TOV0 flag sets when TCNT0 = 0x00 (overflows).
    • Number of ticks required: 0xFF−0xF2+1=14 ticks0xFF - 0xF2 + 1 = 14 \text{ ticks}
    • Each tick takes 8 cycles, so total clock cycles: 14×8=112 cycles14 \times 8 = 112 \text{ cycles}
  2. AGAIN Loop Execution:

    • SBIS + RJMP = 3 instruction cycles per iteration.
    • Loop executes: ⌈112/3⌉=38 iterations\lceil 112 / 3 \rceil = 38 \text{ iterations}
    • Total cycles in loop: (38×3)+2=116 cycles(38 \times 3) + 2 = 116 \text{ cycles}
  3. Other Instructions:

    • Additional 14 instruction cycles.
    • Total instruction cycles: 116+14=130 cycles116 + 14 = 130 \text{ cycles}
  4. Final Time Delay Calculation:

    • Each cycle = 0.0625 µs (for 16 MHz clock).
    • Total time delay: 130×0.0625=8.125 μs130 \times 0.0625 = 8.125\ \mu s

General Formula for Time Delay Calculation

To compute the time delay for a DELAY subroutine with pre-scaling, use:

Time delay(μs)=[(⌈(255−A+1)×P3⌉×3)+2+14]×0.0625\Huge\boxed{\text{Time delay}(\mu s) = \left[\left(\left\lceil\frac{(255 - A + 1) \times P}{3}\right\rceil \times 3\right) + 2 + 14 \right] \times 0.0625}

where:

  • AA = Initial TCNT0 value
  • 255255 = 0xFF (max counter value)
  • PP = Pre-scaling factor (8, 64, 256, or 1024)
  • ⌈\lceil ⌉\rceil = Ceiling function

Lab 6 - 01 Timer (Normal + Prescale).pdf

Clear Timer0 on Compare Match (CTC Mode)

In CTC mode, instead of counting up to 0xFF, we set a custom max value for TCNT0 using the OCR0A register.

  • Key Behavior:
    • TCNT0 resets to 0x00 when it matches the value in OCR0A.
    • The OCF0A flag is set (=1) at the match point, the TCNT will be restored to 0x00.
    • ==Overflow Flag (TOV0) is disabled (=0) in this mode.==
  • The relation among TCNT0, OCR0A, and OCF0A is shown in the diagram below.

Register Configuration

  • Mode Selection:
    • Set WGM02:WGM00 = 010 for CTC Mode.
  • Pre-Scaling Selection:
    • Use CS02:CS00 to control clock speed (frequency).

Steps for a Time Delay using CTC Mode

  1. Set the initial value in TCNT0.
  2. Set the compare match value in OCR0A (max value).
  3. ==Configure CTC mode & pre-scaling (modify TCCR0A and TCCR0B).==
  4. Implement a polling loop:
    • Stay in the loop while OCF0A = 0.
    • Exit when OCF0A = 1.
  5. Stop Timer0.
  6. Clear OCF0A flag.

Example: CTC Mode with No Pre-Scaling (clk/1)

Problem Statement:

Compute the time delay when:

  • Initial Value: TCNT0 = 0x25
  • Max Compare Value: OCR0A = 0x5A
  • No pre-scaling (clk/1)
Solution:
  1. Compute Loop Iterations (N)

    N=⌈OCR0A−TCNT0−13⌉N = \Big\lceil\frac{\text{OCR0A} - \text{TCNT0} - 1}{3}\Big\rceil N=⌈0x5A−0x25+13⌉=18N = \Big\lceil\frac{0x5A - 0x25 + 1}{3}\Big\rceil = 18
  2. Total Cycles in Loop (AGAIN)

    • Since each iteration takes 3 cycles, total cycles spent: 18×3=54 cycles18 \times 3 = 54 \text{ cycles}
    • Add 2 cycles for exiting the loop → 56 cycles.
  3. Other Instructions:

    • Additional 16 instruction cycles.
  4. Total Instruction Cycles:
    56+16=72 cycles56 + 16 = 72 \text{ cycles}

  5. Time Delay Calculation:

    • Each cycle duration = 0.0625 µs (for 16 MHz clock).
    72×0.0625=4.5 μs72 \times 0.0625 = 4.5\ \mu s

General Formula for Time Delay in CTC Mode

To calculate the time delay in CTC Mode, use:

Time delay(μs)=[(⌈(B−A+1)×P3⌉×3)+2+16]×0.0625\Huge\boxed{\text{Time delay} (\mu s) = \left[\left(\left\lceil\frac{(B - A + 1) \times P}{3}\right\rceil \times 3\right) + 2 + 16\right] \times 0.0625}

where:

  • AA = Initial TCNT0 value
  • BB = Max OCR0A value
  • PP = Pre-scaling factor (1, 8, 64, 256, or 1024)
  • ⌈\lceil ⌉\rceil = Ceiling function

บางครั้งก็ถาม A (ให้ B มา) ต้องให้อย่างใดอย่างนึงนะ!

Design Problem: Finding Timer Parameters

Given a required time delay, determine:

  • Initial TCNT0 value (A)
  • Max OCR0A value (B)
  • Pre-scaling factor (P)

Key Constraint:

  • OCF0A flag is set (=1) when TCNT0 rolls over OCR0A and resets to 0x00.

📄 Lab 6 - 02 Timer (CTC).pdf