How is the signal frequency measured in ATmega? In particular, how is the signal frequency (sine) measured in ATmega8?
-
1It may be better to write Sine wave or Sinusoid instead of Sinus – RedGrittyBrick Dec 14 '13 at 12:40
-
Does ATmega8 have an ICP (input capture pin)? That is ideal for timing signal period. – jippie Dec 15 '13 at 07:44
2 Answers
Assuming that you are only interested to measure the frequency of the sinusoidal I would say convert it to square wave pulses (for example with an optocoupler) and then use the timers to measure the square wave.
There are several ways to measure the frequency but the more accurate is by using the input capture unit, refer to this post.
Another alternative that will give pretty accurate results but not as accurate as the above method is to use timer 1 as a gate for timer 0 to measure pulses in a specified window (for example 100ms) and then use math to get the signal frequency. In order to do that you set timer 0 to use the external signal pulses as a clock source, here is what I mean:
// sample for mega88
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/power.h>
#define ENABLE_TIMER1 TCCR1B=(1<<CS11) | (1<<CS10);
#define DISABLE_TIMER1 TCCR1B=0x00;
#define ENABLE_TIMER0 TCCR0B=(1<<CS02) | (1<<CS01) | (1<<CS00);
#define DISABLE_TIMER0 TCCR0B=0x00;
#define RESET_TIMERS TCNT1 = 53036; TCNT0=0x00; timer0_ovf=0; timer1_ovf=0;
volatile unsigned char timer1_ovf;
volatile unsigned int timer0_ovf;
uint32_t frequency;
// Timer 0 overflow interrupt service routine
ISR(TIMER0_OVF_vect)
{
timer0_ovf++; // overfloaw counter , will be used in calculations later
}
// Timer1 overflow interrupt service routine
ISR(TIMER1_OVF_vect)
{
DISABLE_TIMER1
DISABLE_TIMER0
timer1_ovf = 1; // flag that measure has finshed
}
uint32_t measure_frequency(void)
{
RESET_TIMERS
_delay_ms(1);
ENABLE_TIMER1
ENABLE_TIMER0
while(timer1_ovf != 1); // wait for flag
// measure window is 0.1sec, to convert to sec multiply with 10
return (((TCNT0 + (256UL * timer0_ovf)) * 10));
}
void main(void)
{
// Crystal Oscillator division factor: 1
clock_prescale_set(clock_div_1);
// Timer/Counter 0 initialization
// Clock source: T0 pin Rising Edge
// Mode: Normal top=0xFF
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A = (0 << COM0A1) | (0 << COM0A0) | (0 << COM0B1) | (0 << COM0B0) | (0 << WGM01) | (0 << WGM00);
TCCR0B = (0 << WGM02) | (1 << CS02) | (0 << CS01) | (0 << CS00);
TCNT0 = 0x00;
OCR0A = 0x00;
OCR0B = 0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 125,000 kHz
// Mode: Normal top=0xFFFF
// OC1A output: Disconnected
// OC1B output: Disconnected
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer Period: 0,52429 s
// Timer1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (0 << COM1B1) | (0 << COM1B0) | (0 << WGM11) | (0 << WGM10);
TCCR1B = (0 << ICNC1) | (0 << ICES1) | (0 << WGM13) | (0 << WGM12) | (0 << CS12) | (0 << CS11) | (0 << CS10);
TCNT1 = 53036; // 65536-12500 = 53036 , overflow at 100ms
ICR1H = 0x00;
ICR1L = 0x00;
OCR1AH = 0x00;
OCR1AL = 0x00;
OCR1BH = 0x00;
OCR1BL = 0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0 = (0 << OCIE0B) | (0 << OCIE0A) | (1 << TOIE0);
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1 = (0 << ICIE1) | (0 << OCIE1B) | (0 << OCIE1A) | (1 << TOIE1);
// Global enable interrupts
sei();
frequency = measure_frequency(); // measure frequency and store to variable
while(1)
{
// Place your code here
}
}
- 11,130
- 1
- 29
- 62
Sample values using an ADC with a minimum frequency which needs to be twice (more is better ;-) the frequency you expect your sinus to be. Then perform a FFT (see here as well). Depending on how fast/often you need this to be done this should be possible on the atmega family.
That one here actually looks pretty impressive to be honest
- 8,059
- 1
- 22
- 36