ATmega32 Frequency Period Measurement with Input Capture

The ATmega32 has three timers called timer 0, timer 1 and timer 2 but the input capture is only available with timer 1. Simple example of using Atmega32A input capture was illustrated in the previous tutorials ATmega32A Input Capture Example and ATmega32 Input Capture Interrupt Example. Here it is illustrated how ATmega32 microcontroller input capture can be used for measuring external signal frequency and period. 

Circuit Diagram for Frequency and Period Measurement

The following shows the hardware circuit diagram of measuring frequency and period of external signal and displaying them on LCD.

frequency ATmega32A input capture
The above circuit diagram shows a function generator generating 12KHz signal which is connected  to the PD6, the input capture pin. The LCD shows the measured frequency and period by the ATmega32 microcontroller.

Program code for Frequency and Period Measurement

The following is the program code for frequency and period measurement.


#include <avr/io.h>
#include <stdlib.h>
#include "lcd.h"

#define icPin (1<<PD6)


int main(void)
{
	DDRD |= icPin;	//make PD7 pin an output for LED
	PORTD |= icPin;

        float t1,t2;
        char period[16], freq[16];
        float p,F,T;
	float Fcpu = 16000000;

	lcdinit();
	lcdprint("Period Calc..");

	//Timer 1 config: no noise canceller, rising edge, normal mode, no prescalar
        TCCR1A = 0b00000000;	//COM1A1 COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10
	TCCR1B = 0b01000001;	//ICNC1 ICES1 – WGM13 WGM12 CS12 CS11 CS10

    while(1){

		TCNT1 = 0;	//clear TCNT1 register
		TIFR = (1<<ICF1);  //clear the ICF1
    
		while((TIFR &(1<<ICF1))==0);
			t1 = ICR1;
			TIFR = (1<<ICF1);
		while((TIFR &(1<<ICF1))==0);
			t2 = ICR1; 
            
		p = t2 - t1;		
                F = Fcpu/p;
		T = 1/F;       
        
		lcdclear();
		
        
		if(F >= 1000){
			F = F/1000;
                        dtostrf(F, 3, 2, freq);
			lcdprint("F:");
                        lcdprint(freq);
			lcdprint(" KHz");

			T = 1000*T;
                        dtostrf(T, 3, 2, period);	
			lcdsetcursor(1,2);
                        lcdprint("T:");
			lcdprint(period);
                        lcdprint(" ms");
                        _delay_ms(100);	
		}
		else if(F >= 1000000){
			F = F/1000000;
                        dtostrf(F, 3, 2, freq);
                        lcdsetcursor(1,2);
			lcdprint("F:");
                        lcdprint(freq);
			lcdprint(" MHz");

                        T = 1000000*T;
                        dtostrf(T, 3, 2, period);	
			lcdsetcursor(1,2);
                        lcdprint("T:");
			lcdprint(period);
                        lcdprint(" us");
                        _delay_ms(10);
		}
		else{
			dtostrf(F, 3, 2, freq);
			lcdprint("F:");
			lcdprint(freq);
                        lcdprint(" Hz");
			T = T*1000;
                        dtostrf(T, 4, 2, period);	
			lcdsetcursor(1,2);
                        lcdprint("T:");
			lcdprint(period);
                        lcdprint(" ms");
                        _delay_ms(400);
		}
	}
return(0);
}

In the above we have used lcd library for the 16x2 LCD which are as follows.

lcd.h


#include <avr/io.h>
#include <util/delay.h>

#define RS (1<<PD2)
#define E (1<<PD3)
#define D4 (1<<PB4)
#define D5 (1<<PB5)
#define D6 (1<<PB6)
#define D7 (1<<PB7)
#define DATAPORT PORTB
#define CTRLPORT PORTD

void lcdinit();
void lcdcmd(char);
void lcdchar(char);
void lcdprint(char *);
void latch(void);
void lcdsetcursor(unsigned char, unsigned char);
void lcdclear();

lcd.c


#include "lcd.h"


void lcdinit(){
   
   //initialize PORTs for LCD
   DDRD |= RS | E;
   DDRB |= D4 | D5 | D6| D7;  //make output for E,RW,RS and 4 bit Data

   _delay_ms(20);
   CTRLPORT &= ~E;		//send low
   _delay_ms(20);  //delay for stable power
   lcdcmd(0x33);
   //_delay_us(100);
  lcdcmd(0x32);
  //_delay_us(100);
   lcdcmd(0x28);	// 2 lines 5x7 matrix dot
  // _delay_us(100);
    lcdcmd(0x0C);  // display ON, Cursor OFF
 // _delay_us(100);
   lcdcmd(0x01);	//clear LCD
 //  _delay_us(2000);
   lcdcmd(0x06);	//shift cursor to right
   _delay_us(1000);
   }
   
   void lcdcmd(char cmd){
      DATAPORT = (DATAPORT & 0x0F) | (cmd & 0xF0);  // send high nibble
    //  PORTD &= ~RW;	//send 0 for write operation
      CTRLPORT &= ~RS;	//send 0 to select command register
      CTRLPORT |= E;		//send high
      _delay_ms(5);		//wait
      CTRLPORT &= ~E;		//send low
   _delay_ms(5);		//wait
      
      DATAPORT = (DATAPORT & 0x0F) | (cmd<<4);	//send low nibble 
       CTRLPORT |= E;		//send high
      _delay_ms(5);		//wait
      CTRLPORT &= ~E;		//send low
		_delay_ms(5);		//wait
      }
      
  void lcdchar(char data){
      
      DATAPORT = (DATAPORT & 0x0F) | (data & 0xF0);  // send high nibble
     // PORTD &= ~RW;	//send 0 for write operation
      CTRLPORT |= RS;	//send 1 to select data register
      CTRLPORT |= E;		//send high
      _delay_ms(5);		//wait
      CTRLPORT &= ~E;		//send low
	_delay_ms(5);		//wait
     
      DATAPORT = (DATAPORT & 0x0F) | (data<<4);  // send low nibble
      CTRLPORT |= E;		//send high
      _delay_ms(5);		//wait
      CTRLPORT &= ~E;		//send low
	  _delay_ms(5);		//wait
      
      }
      
 void lcdprint(char *str){
    unsigned char k=0;
    while(str[k] != 0){
	 lcdchar(str[k]);
       k++;
       }
    }
 
 void lcdsetcursor(unsigned char x, unsigned char y){
      unsigned char firstcharadr[] = {0x80, 0xC0, 0x94, 0xD4};
      lcdcmd(firstcharadr[y-1] + x-1);
      _delay_ms(10);
    }

void lcdclear(){
	lcdcmd(0x01);
	_delay_ms(10);
}


 In the above program code, first we have included the necessary libraries. One of them is the stdlib.h library which is needed for using dtostr() function later in the program.


#include <avr/io.h>
#include <stdlib.h>
#include <util/delay.h>
#include "lcd.h"

Alias name for the input capture pin ICP1 or PD6 is created using the following statement.

#define icPin (1<<PD6)

In the main() function we setup the direction and internal pull resistor for the input capture pin.

DDRD |= icPin;	//make PD7 pin an output for LED
PORTD |= icPin;

Variables required in the program are created.

        float t1,t2;
        char period[16], freq[16];
        float p,F,T;
	float Fcpu = 16000000;

And the LCD library is initialized which sets up the pins and LCD functions. Then a message is displayed on the LCD.

	lcdinit();
	lcdprint("Period Calc..");

Then we setup the Timer 1 in normal mode, no-prescalar, no noise canceller and rising edge by configuring the TCCR1A and TCCR1B registers.

//Timer 1 config: no noise canceller, rising edge, normal mode, no prescalar
        TCCR1A = 0b00000000;	//COM1A1 COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10
	TCCR1B = 0b01000001;	//ICNC1 ICES1 – WGM13 WGM12 CS12 CS11 CS10

 In the while() loop we clear the TCNT1 register and then clear the ICF1 flag in the TIFR register.

		TCNT1 = 0;	//clear TCNT1 register
		TIFR = (1<<ICF1);  //clear the ICF1

Then we take input capture time stamp and store them in variables. This is done by waiting and first detecting the first rising edge, store the time variable in t1, clearing the ICF1 flag and then waiting and detecting the second consecutive rising edge and store the time variable in t2.

                while((TIFR &(1<<ICF1))==0);
			t1 = ICR1;
			TIFR = (1<<ICF1);
		while((TIFR &(1<<ICF1))==0);
			t2 = ICR1;

Once we have the time stamps in variables t1 and t2 we can then measure the period, p, and calculate the frequency F and the period T of the external signal.

                p = t2 - t1;		
                F = Fcpu/p;
		T = 1/F; 

Next before displaying the results we clear the LCD.

lcdclear();

The next if else statements are used to arrange the frequency measured and put them in different frequency ranges for display.

if(F >= 1000){
			F = F/1000;
                        dtostrf(F, 3, 2, freq);
			lcdprint("F:");
                        lcdprint(freq);
			lcdprint(" KHz");

			T = 1000*T;
                        dtostrf(T, 3, 2, period);	
			lcdsetcursor(1,2);
                        lcdprint("T:");
			lcdprint(period);
                        lcdprint(" ms");
                        _delay_ms(100);	
		}
		else if(F >= 1000000){
			F = F/1000000;
                        dtostrf(F, 3, 2, freq);
                        lcdsetcursor(1,2);
			lcdprint("F:");
                        lcdprint(freq);
			lcdprint(" MHz");

                        T = 1000000*T;
                        dtostrf(T, 3, 2, period);	
			lcdsetcursor(1,2);
                        lcdprint("T:");
			lcdprint(period);
                        lcdprint(" us");
                        _delay_ms(10);
		}
		else{
			dtostrf(F, 3, 2, freq);
			lcdprint("F:");
			lcdprint(freq);
                        lcdprint(" Hz");
			T = T*1000;
                        dtostrf(T, 4, 2, period);	
			lcdsetcursor(1,2);
                        lcdprint("T:");
			lcdprint(period);
                        lcdprint(" ms");
                        _delay_ms(400);
		}

In this way we can measure the frequency and period of external signal using the input capture feature of ATmega32 and ATmega32A.

See other example of input capture for measuring frequency and period.

- High Frequency Counter with Arduino

Post a Comment

Previous Post Next Post