Programming ATmega328p in CTC mode(Timer 0)

Clear Timer on Compare (CTC) mode is a fundamental operation mode for microcontroller timers. It allows developers to generate precise square wave frequencies by continually comparing the internal timer counter value against a predetermined reference, known as the compare value.

This tutorial explores how to utilize the ATmega328P microcontroller in CTC mode through practical code examples while also contrasting CTC operation with standard timer modes. For broader applications, you can explore more ATmega328p Timer Programming Examples.

Timers and CTC pins

The ATmega328P features three distinct timer/counter modules: Timer 0, Timer 1, and Timer 2. Timer 0 and Timer 2 are 8-bit counters, whereas Timer 1 is a 16-bit counter. Each of these timers includes built-in Clear Timer on Compare Match (CTC) functionality. Equipped with dual compare units, every timer can independently output CTC waveforms on two separate pins. Specifically, Timer 0 controls pins PD6 (OC0A) and PD5 (OC0B), Timer 1 manages pins PB1 (OC1A) and PB2 (OC1B), and Timer 2 drives pins PB3 (OC2A) and PD3 (OC2B). The pin allocations are illustrated below.

ATmega328P CTC pins

Timer 0

 For the practical implementation in this guide, we will focus on Timer 0.

Using timer CTC mode offers two primary advantages. First, it gives users fine-grained and highly flexible control over the output frequency of square waves generated at the OC0A or OC0B pins. Second, it greatly streamlines the process of counting and responding to external events.

When configuring Timer 0 in CTC mode, a target count (compare) value is written into either the OCR0A or OCR0B register. Once initiated, the timer increments from zero until it equals the value stored in the respective OCR0x register. Upon hitting this match condition, the corresponding OCF0A or OCF0B flag inside the TIFR0 register is triggered. Developers can choose to either poll this flag directly within a loop to execute tasks like toggling LEDs, reading switches, or sampling data, or configure an automatic interrupt service routine via TIMER0_COMPA_vect or TIMER0_COMPB_vect.

The mathematical formula used to determine the count value required for the OCR0A or OCR0B register is shown below.

\[C=\frac{F_{clk}}{2 N F_{w}}-1\]

 where Fclk represents the CPU clock frequency of the microcontroller, N denotes the chosen prescaler value, and Fw stands for the desired output wave frequency.

Alternatively, the equation can be expressed using the square wave time period (Tw) instead of frequency (Fw).

\[C=\frac{F_{clk}  T_{w}}{2 N}-1\]

 For instance, to generate a square wave with a period Tw = 100 microseconds (equal to a 10 kHz frequency) using an 8 MHz CPU clock and an 8 prescaler, the register count is computed as:

\[C=\frac{8*10^6*100*10^{-6}}{2*8}-1\]

resulting in:

C = 49

Important Registers for CTC mode

Proper configuration of the ATmega328P microcontroller for CTC operation relies on several core registers.

a. TCCR0A – Timer/Counter 0 Control Register A

 The behavior of the Compare Output Mode bits COM0A1 and COM0A0 during Non-PWM operation is outlined below.
 
The functionality of COM0A1 and COM0A0 in Fast PWM mode is detailed below (similar configurations are also explored in tutorials on ATmega328P Fast PWM mode Programming Examples).
 
 For Phase Correct PWM mode (further detailed in guides on Phase Correct PWM with ATmega328P), the corresponding configurations for COM0A1 and COM0A0 apply as follows:
 
 
 Similarly, bits COM0B1 and COM0B0 in Non-PWM mode operate under these parameters:
 
 
Fast PWM behavior for the COM0B1 and COM0B0 pins is defined as follows.
 

  Phase Correct PWM settings for COM0B1 and COM0B0 are structured as follows:
 

b. TCCR0B – Timer/Counter 0 Control Register B

 
 Waveform generation types are established using the WGM bits (WGM02, WGM01, and WGM00) split across TCCR0A and TCCR0B. The matrix below outlines how these bits govern different modes.


Clock prescaling values are selected by adjusting the CS bits as shown below.

c. TIMSK0 – Timer/Counter 0 Interrupt Mask Register



d. TIFR0 – Timer/Counter 0 Interrupt Flag Register

e. OCR0A – Output Compare Register0 A

f. OCR0B – Output Compare Register0 B

Programming Examples of ATmega328p in CTC mode

CTC mode can be implemented using either a polling mechanism or an interrupt-driven approach. Below, we first examine the polling method followed by the interrupt configuration using the OCR0A register.

CTC mode with Polling

The C code snippet below sets up Timer 0 in CTC toggle mode with an 8 prescaler. This configures Port D Pin 6 (OC0A) to output a square wave frequency calculated via the formulas above. Here, the goal is a 10 kHz signal corresponding to a 100 microsecond period. The target pin must be defined as an output using the DDRD register.

# C program code

#ifndef F_CPU
#define F_CPU 8000000UL
#endif

#include <avr/io.h>

void ctcDelay();


int main()
 { 
   DDRD |= (1<<PD6);    //make PORT D pin 6(OC0A pin) an output pin    
	
   while (1){
		ctcDelay();
	}

   return 0;
 }


ctcDelay(){
	OCR0A = 49;	//load 49 into OCR0A for delay of 100us
	//configure timer0 for CTC mode with prescalar of 8
	TCCR0A |= (1<<COM0A0) | (1<<WGM01);
	TCCR0B |= (1<<CS01); 

	while((TIFR0 &  (1<<OCF0A)) == 0);	//check OCF0A flag
	TIFR0 = (1<<OCF0A);	//clear OCF0A flag
}
Alternatively, the same logic can be adapted for output options beyond the primary OC0A pin. A typical hardware schematic layout for this configuration is shown below.

square wave CTC mode

CTC mode with Interrupt

Alternatively, timer compare matches can trigger interrupts. This requires enabling the Output Compare Match A Interrupt Enable bit (OCIE0A) inside the TIMSK0 register.

#C program code for CTC mode with Interrupt

#ifndef F_CPU
#define F_CPU 8000000UL
#endif

#include <avr/io.h>
#include <avr/interrupt.h>

int main()
 { 
   DDRD |= (1<<PD6);
   
   OCR0A = 49;	//load 49 into OCR0A for delay of 100us
	//configure timer0 for CTC mode with prescalar of 8
	TCCR0A |= (1<<COM0A0) | (1<<WGM01);
	TCCR0B |= (1<<CS01); 
	TIMSK0 |= (1<<OCIE0A);	//enable  timer0 output compare match A interrupt
	sei(); 	//set global interrupt
	
   while (1);

   return 0;
 }

ISR(TIMER0_COMPA_vect){
	
}

In this interrupt-driven setup, the microcontroller creates a 10 kHz wave (100 microsecond period) using an 8 MHz CPU clock and an 8 prescaler by loading a count of 49 into OCR0A. Standard timer calculation utilities or manual formulas yield this identical value. Implementation requires including , setting pin OC0A as an output, initializing the control registers for toggle CTC mode, activating the OCIE0A bit in TIMSK0, and finally enabling global interrupts via the sei() function.

Conclusion 

Mastering CTC mode on the ATmega328P provides a robust technique for generating accurate timing and stable square wave frequencies. By understanding the register layouts and configuration steps detailed here, developers can easily implement precise timing into their hardware projects using either polling or interrupt-based designs.

Post a Comment

Previous Post Next Post