Timer Input Capture Mode in AVR ATmega16/ATmega32

Introduction to Input Capture Mode

The input capture function is used in many applications such as:

  • Pulse width measurement
  • Period measurement
  • Capturing the time of an event

In AVR ATmega32, Timer1 can be used as an input capture to detect and measure events happening outside the microcontroller.

Upon detection of a defined event i.e. rising edge or falling edge on ICP pin (PORTD.6), the TCNT1(Timer / Counter register) value is loaded into the ICR1 (input capture) register and the ICF1 flag will get set.

Programming ICP Registers

To program, first, let us see TCCR1B (Timer Counter Control Register B)

TCCR1B: Timer Counter Control Register B

ATmega TCCR1B Register


Bit 7 - ICNC1: Input Capture Noise canceller

          Setting this bit activates the noise canceller. It causes a delay of 4 clock cycles as it considers a change only if it persists for at least 4 successive system clocks.

Bit 6 - ICES1: Input Capture Edge select

Select edge detection for input capture function.

        0 = Capture on the falling edge

        1 = Capture on rising edge

Bit 4: 3 - WGM13 : WGM12: Timer1 Mode select

           These bits are used for mode selection like Normal mode, PWM mode, CTC mode, etc. here we will select normal mode, so set these bits to zero.

Bit 2: 0 - CS12: CS10:Timer1 Clock Select

000No clock source (Timer / Counter stopped)
001clk (no pre-scaling)
010clk / 8
011clk / 64
100clk / 256
101clk / 1024
110External clock source on T0 pin. Clock on falling edge
111External clock source on T0 pin. Clock on rising edge.


Steps to Program

  1. Initialize the TCCR1A and TCCR1B for proper timer mode (any mode other than 8, 10, 12, 14), to select the edge (Positive or Negative).
  2. Monitor the ICF1 flag in the TIFR register to see if the edge has arrived. Upon the arrival of the edge, the TCNT1 value is loaded into the ICR1 register automatically by the controller.

Note: Input capture pin, which is PORTD.6, has one more function i.e. output of the analog comparator. We can use ACIC bit from the ACSR register, to make this pin function as the ‘analog comparator output’ by setting it to logic HIGH. Otherwise, this pin remains as an ICP pin by default after power on or reset. So we don’t need to define this Register here.


Input Capture Mode Program

Assuming that the clock pulses are fed into the pin ICP1, the following program will read the TCNT1 value at every rising edge and place the result on PORTA and PORTB.

 ATmega16 input capture mode 

#include "avr/io.h"
int main ( )
	unsigned int t;
	DDRA = 0xFF;	
	DDRB = 0xFF;	
	PORTD = 0xFF;	
	TCCR1A = 0;
	TIFR = (1<<ICF1);		/* clear input capture flag */
	TCCR1B = 0x41;			/* capture on rising edge */ 

	while ((TIFR&(1<<ICF1)) == 0);  /* monitor for capture*/
	t = ICR1;			
	TIFR = (1<<ICF1);		/* clear capture flag */

	while ((TIFR&(1<<ICF1)) == 0);  /* monitor for next rising
					edge capture */

	t = ICR1 - t;  			/* period= recent capture-
					previous capture */
	PORTA = t;  			/* put period count on PORTA & PORTB */	
	PORTB = t>>8;	

	while (1);
	return 0;


Measuring Frequency and Duty Cycle

Program to measure the frequency and duty cycle and displaying it on the LCD16x2

  Measuring ATmega16 frequency and duty cycle using input capture

#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include "LCD_16x2_H_file.h"

int main ( )
	unsigned int a,b,c,high,period;
	char frequency[14],duty_cy[7];
	PORTD = 0xFF;			/* Turn ON pull-up resistor */
		TCCR1A = 0;
		TIFR = (1<<ICF1);  	/* Clear ICF (Input Capture flag) flag */

		TCCR1B = 0x41;  	/* Rising edge, no prescaler */
		while ((TIFR&(1<<ICF1)) == 0);
		a = ICR1;  		/* Take value of capture register */
		TIFR = (1<<ICF1);  	/* Clear ICF flag */
		TCCR1B = 0x01;  	/* Falling edge, no prescaler */
		while ((TIFR&(1<<ICF1)) == 0);
		b = ICR1;  		/* Take value of capture register */
		TIFR = (1<<ICF1);  	/* Clear ICF flag */
		TCCR1B = 0x41;  	/* Rising edge, no prescaler */
		while ((TIFR&(1<<ICF1)) == 0);
		c = ICR1;  		/* Take value of capture register */
		TIFR = (1<<ICF1);  	/* Clear ICF flag */

		TCCR1B = 0;  		/* Stop the timer */
		if(a<b && b<c)  	/* Check for valid condition, 
					to avoid timer overflow reading */
			long freq= F_CPU/period;/* Calculate frequency */

						/* Calculate duty cycle */
            		float duty_cycle =((float) high /(float)period)*100;			
			LCD_String("Freq: ");
			LCD_String(" Hz    ");
			LCD_String("Duty: ");
			LCD_String(" %      ");
			LCD_String("OUT OF RANGE!!");




Components Used

ATmega 16
ATmega 16
LCD16x2 Display
LCD16x2 Display


Proteus Simulation File Download
Atmega16_Freq_DutyCycle_Project_File Download