Below is sample code to set up Timer/Counter0 on ATmega328P.
#include <avr/io.h> // For TIMER0_OVF_vect, PRR, TCCR0A, etc. #include <avr/sfr_defs.h> // For _BV(). #include <avr/interrupt.h> // For sei(), ISR(). #include <avr/power.h> // For power_timer0_enable(). ISR(TIMER0_OVF_vect) { ... } // Set compare output mode TCCR0A &= ~(_BV(COM0A0)); // OC0A disconnected TCCR0A &= ~(_BV(COM0A1)); TCCR0A &= ~(_BV(COM0B0)); // OC0B clear on match TCCR0A |= _BV(COM0B1); // Clear OC0B pin (PD5) TCCR0B |= _BV(FOC0B); // Write 1 to force output compare // Set PD5 pin as output. DDRD |= _BV(DDD5); // Set waveform generation mode // Compare output mode is now clear-on-match, set-at-BOTTOM TCCR0A |= _BV(WGM00); // Fast PWM TCCR0A |= _BV(WGM01); TCCR0B |= _BV(WGM02); // TOP = OCR0A // Initialize counter and output compare values TCNT0 = 0; OCR0A = 100; // Set PWM frequency (TOP) OCR0B = 25; // Set PWM duty cycle // Clear interrupt flags to prevent interrupt // subroutines from being called immediately after // enabling interrupts (Note: write 1 to clear). TIFR0 |= _BV(TOV0); // Overflow TIFR0 |= _BV(OCF0A); // Output compare match A TIFR0 |= _BV(OCF0B); // Output compare match B // Set interrupt enable bits TIMSK0 |= _BV(TOIE0); // Overflow enabled TIMSK0 |= _BV(OCIE0A); // Output compare match A enabled TIMSK0 |= _BV(OCIE0B); // Output compare match B enabled // Set global interrupt enable bit (initially disabled) sei(); // Enable Timer/Counter0 module // PRR &= ~(_BV(PRTIM0)); // Write 0 to enable power_timer0_enable(); // Same as above line // Set clock select (start counting) TCCR0B |= _BV(CS00); // 1/64 prescaler TCCR0B |= _BV(CS01); TCCR0B &= ~(_BV(CS02));
Below are all 7 registers related to Timer/Counter0.
| Registers | Bits | ||
|---|---|---|---|
| Control (Settings) | TCCR0A | COM0A1:0, COM0B1:0 | Compare output mode | 
| WGM01, WGM00 | Waveform generation mode | ||
| TCCR0B | WGM02 | ||
| CS02:0 | Clock select | ||
| FOC0A, FOC0B | Write 1 to force output compare | ||
| Counter | TCNT0 | ||
| Output Compare | OCR0A | ||
| OCR0B | |||
| Interrupt Flag | TIFR0 | TOV0 | Overflow | 
| OCF0A, OCF0B | Output compare match | ||
| Interrupt Mask (Enable Bits) | TIMSK0 | TOIE0 | Overflow | 
| OCIE0A, OCIE0B | Output compare match | ||
In addition to above, there is PRTIM0 bit in
PRR register which controls power reduction settings
of Timer/Counter0 module.
All 8 registers above have initial value 0x00.
Writing to TCNT0 blocks all compare matches
for one timer clock cycle. If TCNT0 equals
OCR0A/OCR0B,
compare match will be missed.
PRTIM0 (Power Reduction Timer/Counter0) is a read/write bit
in PRR (Power Reduction Register).
It must be 0 to use Timer/Counter0. Writing 0 enables Timer/Counter0 module.
| Timer/Counter0 | PRTIM0 | 
|---|---|
| Enabled (operating) (initial value) | 0 | 
| Disabled (shut down) | 1 | 
| Register: TCCR0B | ||||
|---|---|---|---|---|
| CS02 | CS01 | CS00 | ||
| Timer/Counter stopped (initial value) | 0 | 0 | 0 | |
| Internal clock source (internal oscillator or external crystal on pins XTAL1andXTAL2) | No prescaling | 0 | 0 | 1 | 
| 1/8 prescaling | 0 | 1 | 0 | |
| 1/64 prescaling | 0 | 1 | 1 | |
| 1/256 prescaling | 1 | 0 | 0 | |
| 1/1024 prescaling | 1 | 0 | 1 | |
| External clock source on pin T0 | Clock on falling edge | 1 | 1 | 0 | 
| Clock on rising edge | 1 | 1 | 1 | |
For example, Arduino UNO uses 1/64 prescaling with 16MHz crystal so
TCNT0 increments every 4µs (0.25MHz).
| TOP | WGM02 | WGM01 | WGM00 | |
|---|---|---|---|---|
| Normal (initial value) | 0xFF | 0 | 0 | 0 | 
| CTC | OCR0A | 0 | 1 | 0 | 
| Fast PWM OCR0A/OCR0Bupdates at BOTTOM | 0xFF | 0 | 1 | 1 | 
| OCR0A | 1 | 1 | 1 | |
| Phase Correct PWM OCR0A/OCR0Bupdates at TOP | 0xFF | 0 | 0 | 1 | 
| OCR0A | 1 | 0 | 1 | 
WGM02 bit is in a different register.Override GPIO with waveform generator on pins
OC0A/OC0B (PD6/PD5):
Step 1: Set or clear OC0A/OC0B.
These are internal bits (initially 0) that directly translate to
pin outputs only if COM0A1:0/COM0B1:0
bits are set and pins are configured as outputs.
In normal and CTC modes, writing 1 to
OCF0A/OCF0B bits
updates OC0A/OC0B as if a real match had occured.
OC0A/OC0B keep their values even when changing
between waveform generation modes.
Therefore, force match in normal mode to set or clear
OC0A/OC0B and then change waveform mode.
Step 2: Set port to output.
Write 1 to DDD6/DDD5
bits in DDRD to set pins as outputs.
These bits are initially 0 (inputs).
OC0A (PD6)| Register: TCCR0A | ||||
|---|---|---|---|---|
| Normal/CTC | Fast PWM | Phase Correct PWM | COM0A1 | COM0A0 | 
| Disconnected (initial value) | 0 | 0 | ||
| Toggle | Toggle* | 0 | 1 | |
| Clear | Clear (set at BOTTOM) | Up-counting: clear Down-counting: set | 1 | 0 | 
| Set | Set (clear at BOTTOM) | Up-counting: set Down-counting: clear | 1 | 1 | 
*Only valid when WGM02 is 1.
OC0B (PD5)| Register: TCCR0A | ||||
|---|---|---|---|---|
| Normal/CTC | Fast PWM | Phase Correct PWM | COM0B1 | COM0B0 | 
| Disconnected (initial value) | 0 | 0 | ||
| Clear | Clear (set at BOTTOM) | Up-counting: clear Down-counting: set | 1 | 0 | 
| Set | Set (clear at BOTTOM) | Up-counting: set Down-counting: clear | 1 | 1 | 
