Timer Input Capture Mode in AVR ATmega16/ATmega32

AVR Atmega16/ATmega32 has a feature of Input Capture Mode through which it can capture signal rising or falling edge events on its pin.

71.3k views4 min readUpdated May 5, 2023
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

CS02CS01CS00Description
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 
 http://www.electronicwings.com
 */

#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
  http://www.electronicwings.com
 */


#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];
	
	LCD_Init();
	PORTD = 0xFF;			/* Turn ON pull-up resistor */
	
	while(1)
	{
		TCCR1A = 0;
		TCNT1=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 */
		{
			high=b-a;
			period=c-a;
			
			long freq= F_CPU/period;/* Calculate frequency */

						/* Calculate duty cycle */
            		float duty_cycle =((float) high /(float)period)*100;			
			ltoa(freq,frequency,10);
			
			itoa((int)duty_cycle,duty_cy,10);
			
			LCD_Command(0x80);
			LCD_String("Freq: ");
			LCD_String(frequency);
			LCD_String(" Hz    ");
			
			LCD_Command(0xC0);
			LCD_String("Duty: ");
			LCD_String(duty_cy);
			LCD_String(" %      ");
			
		}
		
		else
		{
			LCD_Clear();
			LCD_String("OUT OF RANGE!!");
		}
		_delay_ms(50);
	}

}

 

Video

Components Used

Powered byMouser Electronics

Downloads

Comments25

Join the discussion — share a question or tip.

  • ES
    EslamFayad

    Working with very precise readings , But when disconnect the input captured signal, The whole program stop ,stucked in the while waiting the Flag to be set , How to avoid program stuck waiting for input capture flag

  • LE
    leonardopadovano

    Como se conecta el lcd?

  • VA
    vahidn2

    TNKS

  • MU
    muhammedimdaad16

    we haven't considered the overflow in TCNT1. I think we have to consider that for a generalized input capture

  • AM
    amrmagdi50

    need an explantion for this code :long freq= F_CPU/period; ?? why do we divide th frquency by the calculated period

    • MU
      muhammedimdaad16

      because period = c-a is not actual period. Since TCNT1 register doesn't increment one bit at a second. it increment at the clock rate. so you have to take that into consideration. some logical thinking will sort that out.

  • AM
    amrmagdi50

    what is the maning of this line PORTB = t&gt;&gt;8;?

    • MU
      muhammedimdaad16

      using PORTA we get the LS bits of time and using PORTB we get the MSBits of the time. so in order fill only the MSBs into port B we need to shift the values by 8 bits

  • AM
    amrmagdi50

    why do define PORTD in the first code as high ?

    • MO

      to use the pull up resistor ... to use this pin "portD.6" as I/P ... to detect rising or falling edge of the input signal ..

  • SA
    sayemal25

    sir can you give the proteus file of this code

  • TR
    trungkiendt9

    Can you do this using ICP3?. Because there are some difference between Timer1 and Timer3.

  • PU
    purvikjetpace

    how to use for 16 MHz freq. ?

  • PU
    purvikjetpace

    Can i use this program in ATmega328P Controller ?

    • AU
      authorized

      yes. but you need to make sure that registers and their config bits are same in both controllers

    • PU
      purvikjetpace

      Replying to @authorized

      Its work

    • AB
      abdoibra989

      how it can be done using atmega 328p ? i tried but there's ICRH,ICRL don't know which of them to use for calcuations

  • ME
    mehmetcankarsima

    Thank you!! Perfect guide. Freq and duty measurement code working very well for 50 Hz.

  • NN
    nn10

    Hello. What is the range of frequency/period that this code is capable of measuring? I need to measure period in the micro or milli range, how can i accomplish this?

  • PR
    pran0506

    Sir , when I simulate this in proteus it always shows duty cycle 50% and frequencyto be half of what is set , for eg if PWM is 20% at 2KHz , it shows 1000 Hz and 50%

    • LO
      lokeshc

      hi pran056 please check your crystal frequency selected in proteus. May be it is more than 8 MHZ selected. To check/change the frequency, do double-click on ATmega IC and check CKSEL Fuses. check for 8MHz frequency to get proper output. It will work for you,

    • PR
      pran0506· edited

      Replying to @lokeshc

      Thank you so much Sir, I got the error :)

    • CH
      chandannegich

      Replying to @lokeshc

      Hello sir how we move 0108 Blvd cursor

  • WI
    wicaksonoagung95

    Thank you for explanation, i want ask you why the register TICIE1 is not set??