// Frequenzmesser // // Holger Brenner // // Das zu messende Signal wird an den Input Capture Pin des Prozessors // angelegt. Zur Freqenzbestimmung wird mittels Timer1 die Perioden- // dauer des Signals von einer steigenden Flanke bis zur naechsten // steigenden Flanke ermittelt. Daraus laesst sich dann die Frequenz // errechnen. // // Das Beispiel benutzt die LCD Funktionen von .... // Für andere Ausgabemedien muss das entsprechend angepasst werden // // 8. Feb. 2007 ///////////////////////////////////////////////////////////////////// // // Prozessor Taktfrequenz einstellen sofern es nicht eine Vorgabe // beim Aufruf des C Compilers gab. // #ifndef F_CPU #define F_CPU 4000000 #endif #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <stdlib.h> #include "lcd.h" #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif volatile unsigned char NrOverflows = 0; // Anzahl der Timer Overflows die während // der Messung passiert sind volatile unsigned int StartTime = 0; // ICR-Wert bei 1.High-Flanke speichern volatile unsigned int EndTime = 0; // ICR-Wert bei 2.High-Flanke speichern volatile unsigned char UpdateDisplay; // Job Flag volatile uint32_t Erg = 0; // 32-bit reicht sicher aus für das Ergebnis // LCD Position an der das Ergebnis der Frequenzmessung ausgegeben wird #define LCD_LINE1 0x00 ISR( TIMER1_CAPT_vect ) { static unsigned char ErsteFlanke = TRUE; if( UpdateDisplay ) // Das Display wurde mit den Ergebnissen der vorhergehenden return; // Messung noch nicht upgedated. Die naechste Messung // verzögern, bis die Start und EndTime Variablen wieder // gefahrlos beschrieben werden koennen // // Bei der ersten Flanke beginnt die Messung, es wird der momentane // Timer beim Input Capture als Startwert gesichert // if( ErsteFlanke ) { StartTime = ICR1; NrOverflows = 0; ErsteFlanke = FALSE; // Die naechste Flanke ist das Ende der Messung } // // das ist die zweite Flanke im Messzyklus. Die Messung wird gestoppt // else { EndTime = ICR1; UpdateDisplay = TRUE; // Eine vollständige Messung. Sie kann ausgewertet werden ErsteFlanke = TRUE; // Bei der naechsten Flanke beginnt der naechste Messzyklus if ( (TIFR & (1 << TOV1)) && (!(EndTime & 0x8000)) ){ // Überlaufstattgefunden, der zur Messung gehört? NrOverflows++; } if ( EndTime < StartTime ){ // Overflow stattgefunden, der aber nicht zur Messung gehört? NrOverflows--; } Erg = (NrOverflows * 65536) + EndTime - StartTime; } } ISR( TIMER1_OVF_vect ) { NrOverflows++; } int main() { char lcdCounterString[8]; initLcd(); TCCR1B = (1<<ICES1) | (1<<CS10); // Input Capture Edge, kein PreScale TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts akivieren, Capture + Overflow sei(); while(1) { // // liegt eine vollständige Messung vor? // if( UpdateDisplay ) { // // Die Zeitdauer zwischen den Flanken bestimmen // Da EndTime und StartTime unsigned sind, braucht nicht // darauf Ruecksicht genommen werden, dass EndTime auch // kleiner als StartTime sein kann. Es kommt trotzdem // das richtige Ergebnis raus. // Allerdings muss die Anzahl der Overflows in der Messperiode // beruecksichtigt werden // // Die Zeitdauer wird als Anzahl der Taktzyklen zwischen den // beiden gemessenen Flanken berechnet ... Erg = (NrOverflows * 65536) + EndTime - StartTime; // ... mit der bekannten Taktfrequenz ergibt sich dann die Signalfrequenz Erg = F_CPU / Erg; // f = 1 / t // // Das Ergebnis fuer die Anzeige aufbereiten ... // dtostrf( Erg, 5, 3, lcdCounterString ); // 3 Nachkommastellen // // ... und ausgeben // clearLcd(); setLcdCursorPos( LCD_LINE1 ); writeStringToLcd(lcdCounterString); // // Das wars: Display ist wieder up to date // die naechste Messung kann starten // UpdateDisplay = FALSE; } } }