Frequency Meter

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using an ATMEGA88 running at 8.192 Mhz and I'm having difficulties with making a basic frequency counter.

I'm using a 555 timer to create fairly low frequency signals (0 to 1000 Hz). Unfortunately, I'm unable to get this to work.

Here is my code:

#include 
#include 
#include 
#include  

#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
#define FLIPBIT(ADDRESS,BIT) (ADDRESS ^= (1<<BIT))
#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT))

//8.192 MHz xtal
#define F_CPU 8192000

//Define functions
//======================
static int uart_putchar(char c, FILE *stream);
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
//======================

#define BAUD 9600
#define MYUBRR (F_CPU/16/BAUD-1)

volatile unsigned int pulses = 0;
volatile unsigned long long int overflow1 = 0;
volatile unsigned int overflow0 = 0;

void ioinit (void)
{
    //USART Baud rate: 9600
	// This simply sets the baud rate to 9600.
	// UBRR0H / L is an 8-bit register, so we need to treat it like a 16-bit
    UBRR0H = MYUBRR >> 8;
    UBRR0L = MYUBRR;
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);		// Enable Receiving / Transmission
    
    stdout = &mystdout; //Required for printf init
}

static int uart_putchar(char c, FILE *stream)
{
    if (c == '\n') uart_putchar('\r', stream);
  
    loop_until_bit_is_set(UCSR0A, UDRE0);
    UDR0 = c;
    
    return 0;
}

int uart_putnum(uint16_t num)
{
	// Accepts upto 8 bytes
	unsigned char c[8];
	
	for(int i = 0; i<8; ++i)
	{
		itoa(num, (char*)c, 10);
		if(c[i] == '\0' || c[i] == ' ')
			break;
		loop_until_bit_is_set(UCSR0A, UDRE0);
		UDR0 = c[i];
	}

	return 0;
}

ISR(TIMER1_CAPT_vect)
{
pulses++;
}

ISR(TIMER1_OVF_vect)
{
pulses = 0;
overflow1++;
}

ISR(TIMER0_OVF_vect)
{
overflow0++;	
}

uint8_t uart_getchar(void)
{
    while( !(UCSR0A & (1<<RXC0)) );
    return(UDR0);
}

int main(void)
{
 	ioinit();
	printf("Test\n");

	DDRB = 0b11111110; // PORTB output
	DDRD = 0x00; // PIND input
	PORTB = 0xFF; // Turn off all LEDs
  
	// Timer1: Counter
	// Timer0: Timer
	TCCR0B = (1<< CS02); //F_CPU/256 Prescaler
	TIMSK0 = (1 << TOIE0); // Enable overflow interrupt
	TCNT0 = 0;

   TIMSK1 = (1 << TOIE1) | (1 << ICIE1); // Enable overflow interrupt
   TCNT1 = 0;
	//Noise canceller, without prescaler, rising edge
	TCCR1B = 1 << CS10 | 1 << ICNC1 | 1 << ICES1;

   sei();

   CLEARBIT(PORTB, 5);
   for (;;)
   {
if(overflow0 >= 125)
{
 cli();
 uart_putnum(pulses + overflow1*65536);
 printf("\n");

 overflow0 = 0;
 overflow1 = 0;
 TCNT0 = TCNT1 = 0;
 ICR1 = 0;
 pulses = 0;
	
 sei();
}
 }


	return 0;
}

Timer 0 is an 8-bit timer running with a prescaler of 256.
I am using the 16-bit Timer1 (no prescaler) with the input capture unit.

My thoughts were that if timer0 overflows 125 times that would be 1 second. The number of pulses counted by the input capture unit would then give me the frequency.

Unfortunately I have tried this for a few different frequencies and it doesn't seem to work.

For 65 Hz frequency I get 0
For 2.8 Hz frequency I get 0
For ~650 Hz I get ~5 Hz

Unfortunately I don't have any instruments to verify that the 555 timer is given anything close to the ideal output frequency, but I am using an LED to see the lower frequencies (2.8, 10 Hz etc)

Does anyone see what I am doing incorrectly with my code?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lordnir wrote:
I am using an ATMEGA88 running at 8.192 Mhz and I'm having difficulties with making a basic frequency counter.

I'm using a 555 timer to create fairly low frequency signals (0 to 1000 Hz). Unfortunately, I'm unable to get this to work.

Here is my code:
... abridged ...

Timer 0 is an 8-bit timer running with a prescaler of 256.
I am using the 16-bit Timer1 (no prescaler) with the input capture unit.

My thoughts were that if timer0 overflows 125 times that would be 1 second. The number of pulses counted by the input capture unit would then give me the frequency.


Although you will get an interrupt per pulse on your "input capture" pin, the main beauty of the input capture is that the AVR will take a snapshot of the 16-bit value in Timer1 at (or darn close to) the instant that the input capture event fires. You'd typically use this to get two "Timer1 values" at two successive cycles of your input. For slow signals, the difference between the times will be a high-resolution measure of the period, from which you can compute the frequency. In case the period is *very* long, one typically extends Time1 in software by using the Timer1 overflow event to increment a counter.

Your comments suggest that you're going at the frequency counter biz the other way: count up the number of cycles in a second. If that's what you're after, I'd suggest that you operate Counter/timer #1 in "counter" mode, by programming its clock source to be the "T1" pin. Then use another timer in "timer" mode to establish some suitable period of time (like maybe your one-second interval) to allow the counter to count only during that interval. When the interval's over, the number of counts will be in Timer1.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you measure just two edges of a signal, it might have jitter, and you would get error from that.

An alternative is to run the signal you're trying to measure into the external clock input of the 16bit timer. Then you will "gate" the 16bit timer by enabling and disabling it for a specific time window by using another timer running off a crystal.

For example, if you enabled this 16bit counter/timer for exactly one second, you could measure 0 to 65535Hz with one Hz resolution. You could then sacrifice resolution for measuring speed buy enabling the timer for less time, and/or dividing the measured signal.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

650Hz -> 5Hz...
it seems to be your count interval is little shorter than 1s :) it could be about 0.01s (between 0.01-0.0078 ... and results what you get is in resolution 100Hz... ie. 5*100Hz

Computers don't make errors - What they do they do on purpose.