Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

How can I count a frequency less than 1Hz ATMEGA128 ?

Status
Not open for further replies.

bianchi77

Advanced Member level 4
Joined
Jun 11, 2009
Messages
1,313
Helped
21
Reputation
44
Reaction score
20
Trophy points
1,318
Location
California
Activity points
9,442
Guys,
How can I count a frequency less than 1Hz on INT4 ATMEGA128 ?
I can only count 1,2,3 ?

How can I count 1.2, 0.7 ?

Thanks
 

You can count 1.2 or 0.7 Hz, if you use 10 sec gate time. You get much higher accuracy and shorter measurement time by measuring the period and calculating the frequency.
 

I don't understand with " if you use 10 sec gate time.", what do you mean by that ?
I used interrupt 4 as input and put timer to count every 60 seconds, do you mind seeing my code ?
thank you
 

I think, the term "gate time" in frequency measurement is almost self explanatory https://en.wikipedia.org/wiki/Frequency_counter

You are apparently using 60 secs gate time, so you get a resolution of 1/60 Hz. What's the particularly problem with it?

Most technical frequency instruments are however utilizing period- or multi-period measurement at low frequencies.
 
  • Like
Reactions: betwixt

    betwixt

    Points: 2
    Helpful Answer Positive Rating
What should I update with this code ? so I can detect 1.7 Hz ?
thanks


Code:
volatile uint16_t count=0;    //Main revolution counter
#define F_CPU 16000000UL  // 16 MHz

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




volatile uint16_t rpm=0;   //Revolution per minute
volatile uint16_t rps=0;   //Revolution per second
volatile uint8_t minute; 
volatile uint16_t current_rps;

void main()

{
	char rpm_lcd[50],rps_lcd[50];
	DDRE &= ~(1 << PIN4);
	PORTE |= (1 << PIN4);
	DDR_D.b0 = 1;
	DDR_D.b1 = 1;
	DDR_D.b2 = 1;
	DDR_D.b3 = 1;
	DDR_D.b4 = 1;
	DDR_D.b5 = 0; //button input
	
	DDRA = 0xFF;
	lcd_init();
    lcd_cmd(0x01);
	lcd_string("Heartbeat Meter");
	lcd_cmd(0xC0);
	lcd_string("by Bianchi");
	_delay_ms(1500);
	



	EICRB = _BV(ISC41); //Falling edge on INT4 triggers interrupt.
	EIMSK = _BV(INT4);
	TCCR1B|=((1<<WGM12)|(1<<CS12)|(1<<CS10));
	OCR1A=15624;  //1 second
	TIMSK|=(1<<OCIE1A);  //Output compare 1A interrupt enable
	sei();
	DDRD|=(1<<PD7);


	lcd_cmd(0x01);

	
	while(1)
	{
	 	 cli();
		 current_rps = rps;
		 sei();
		 if (minute==1)
		  {
				  lcd_cmd(0x01);    
				sprintf(rps_lcd,"u",current_rps);
				sprintf(rpm_lcd,".2f", current_rps * 60.0);

				lcd_xy(0,0);
				lcd_string("RPS = ");
				lcd_string(rps_lcd);
		
				lcd_xy(1,0);
				lcd_string("RPM = ");
				lcd_string(rpm_lcd);
         
				if(PINE & (1<<PE4))
				{
					PORTD|=(1<<PD7);
				}
				else

				{
					PORTD&=(~(1<<PD7));
				}
				_delay_ms(2000);
	     minute = 0;
      }//end of minute counter
	}//end while

}//end main


ISR(INT4_vect)
{
	count++;
	
}

ISR(TIMER1_COMPA_vect) 
{   
	//CPU Jumps here every 1 sec exactly!
	 
	 static uint8_t seconds;

	 seconds += 1;

	 if (seconds == 60)
	 {
		 minute = 1;
		 seconds = 0;
 		

	 }
	rps=count;
	rpm=rps*60;
	count=0;
		
	
	
}
 

Follow FvM's advice. For higher frequencies you open a 'gate' and count how many cycles are present before it closes again. For example a one second gate would give a result in cycles per second. For low frequencies, do it the other way around, use the frequency you are measuring as the gate and count how many clock cycles are present while it is open. For example, if you counted at a 1mS rate (1KHz) and the signal was 1Hz it would count to 1000, 2Hz would count to 500 and your 1.7Hz signal would count to 588. If you increase the clock rate you also increase the precision of the measurment. This actually returns a 'period' rather than a frequency but if necessary to convert to Hz use the formula f=1/period.

Brian.
 

The present code counts pulses for 1 second and displays it once a minute. During 59 seconds, the pulses are ignored.

Lean back and think it over.
 

how about this one ?

Code:
#define F_CPU 16000000UL  // 16 MHz

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include <util/delay.h>

#include "lcd.h"

#define SIMULATING
#ifdef SIMULATING
#define _delay_ms(n) (void)0
#else
#define _delay_ms(n) _delay_ms(n)
#endif

volatile uint16_t count=0;    //Main revolution counter
volatile uint16_t rpm=0;   //Revolution per minute
volatile uint16_t rps=0;   //Revolution per second
volatile uint8_t minute; 
volatile uint16_t current_rps;



void main()

{
	char rpm_lcd[50],rps_lcd[50];
	//int minute;
	
	// Initial PORT Used
	// Change pin 7 on bus E to an input by changing bit 7 to zero
	DDRE &= ~(1 << PIN4);
	// Defining a pull-up resistor to to pin 7 on bus E
	// to prevent input floating
	PORTE |= (1 << PIN4);
	DDR_D.b0 = 1;
	DDR_D.b1 = 1;
	DDR_D.b2 = 1;
	DDR_D.b3 = 1;
	DDR_D.b4 = 1;
	DDR_D.b5 = 0; //button input
	
	DDRA = 0xFF;
	lcd_init();
    lcd_cmd(0x01);
	lcd_string("Heartbeat Meter");
	lcd_cmd(0xC0);
	lcd_string("by Bianchi");
	_delay_ms(1500);
	



	//Init INT4
	
	EICRB = _BV(ISC41); //Falling edge on INT4 triggers interrupt.
	EIMSK = _BV(INT4);

	//Timer1 is used as 1 sec time base
	//Timer Clock = 1/1024 of sys clock
	//Mode = CTC (Clear Timer On Compare)
	TCCR1B|=((1<<WGM12)|(1<<CS12)|(1<<CS10));
		OCR1A=15624;  //1 second
	
	TIMSK|=(1<<OCIE1A);  //Output compare 1A interrupt enable

	//Enable interrupts globaly
	sei();

	//LED Port as output
	DDRD|=(1<<PD7);


	lcd_cmd(0x01);

	
	while(1)
	{
	  
	 	 cli();
		 current_rps = rps;
		 sei();
		 if (minute==1)
		  {
				  lcd_cmd(0x01);    
				sprintf(rps_lcd,"%u",current_rps);
				sprintf(rpm_lcd,"%.2f", current_rps * 60.0);

				lcd_xy(0,0);
				lcd_string("RPS = ");
				lcd_string(rps_lcd);
		
				lcd_xy(1,0);
				lcd_string("RPM = ");
				lcd_string(rpm_lcd);
         
				if(PINE & (1<<PE4))
				{
					PORTD|=(1<<PD7);
				}
				else

				{
					PORTD&=(~(1<<PD7));
				}
				_delay_ms(2000);
		//Wait();
	     minute = 0;
      }//end of minute counter
	}//end while

}//end main

ISR(INT4_vect)
{
	//CPU Jumps here automatically when INT4 pin detect a falling edge
		count++;
	
}

ISR(TIMER1_COMPA_vect) 
{   
	//CPU Jumps here every 1 sec exactly!
	 
	 static uint8_t seconds;

	 seconds += 1;

	 if (seconds == 60)
	 {
		 minute = 1;
		 seconds = 0;
 		
 		rpm=count;
 		
		 rps=rpm/60;
        count=0;
	 }
	
	
	
	
}
 

Gives better rpm resolution. But using an integer variable for rps throws away the available resolution.

And you don't want to perform more arithmetic in an interrupt function than necessary.
 

so better be like this :

Code:
volatile uint16_t count=0;    //Main revolution counter
volatile float rpm=0;   //Revolution per minute

volatile float current_rps;
volatile float rps=0;   //Revolution per second

volatile uint8_t minute; 
volatile float current_rps;
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top