www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Frequenzzähler 8Mhz ohne Vorteiler - Atmega8


Autor: Swen S. (swen1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

auch wenn ich denke das schon viele Counter Codes hier so rumgekommen 
sind, will ich euch meinen nicht vorenthalten, da ich den einen oder 
anderen Tip von hier erhalten habe. Er ist nach einem Softwareabgleich 
der Schwingungsfrequenz des Quarzes sehr genau, so genau wie das 
Quarztempverhalten hergibt. Vielleicht kann der eine oder andere damit 
was anfangen.

mfg

Swen
/*********************************************************************

Frequenzzähler (Berich: 1Hz bis etwa theor. 8MHz). Mit Vorteiler 
einfach erweiterbar für höhere Messfrequenzen

Dieses Programm stellt die am T0 Eingang (PD4) eines  Atmega8 (16Mhz) 
anliegende Frequenz (TTL Pegel Rechteck) auf einem LCD Hz-genau dar. 
Weiterhin gibt es die Möglichkeit eines Abgleiches der Frequenz 
mittels Korrekturfaktor bei Bedarf wegen der unvermeidlichen 
"Quarz-Schwingfrequenzabweichung"). Die Abweichungen sind 
ohne Abgleich innerhalb 0,03% Bereich (linear) bei annähernd konstanter
Raumtemperatur. Für normale Zwecke völlig ausreichend. 

Zum Abgleich wird die Variable "korrekturfaktor" anpasst. d.h. es wird
eine bekannte, stabile Frequenz (Rechteck, TTL - sollte mindestens 
10Khz, besser 1Mhz sein) angelegt und der angezeite Wert ermittelt.
Anschließend wird der wahre Wert durch den angezeigten Wert geteilt 
und sinnvoll auf 5 oder 6 Stellen nach dem Komma gerundent. z.b.

100000Hz/100026Hz=0.99974

Dieser Wert wird dann der Variable "korrekturfaktor" zugeteilt.
Genauigkeit nach Abgleich 1Hz genau. Zumindestens bei meinen 
Referenzmessungen. Viel Spass damit! 

Swen

*********************************************************************/

#include <avr/io.h>
#include "lcd-routines.h"
#include "avr/interrupt.h"

int count = 0, a = 0;
float korrekturfaktor = 1; //hier anpassen für Abgleich (z.b 0.99974)
int ztmp = 0;
unsigned long freq = 0;
char anz[20];
volatile int s=0; 
volatile unsigned short z=0;

/*****************************************************************
Funktion für Unwandlung Unsigned Long to Char (Für LCD Ausgabe)
******************************************************************/

void uLongtoChar(unsigned long u,char* Buffer) 
{
  int i = 0;
  int j;
  char tmp;
   // die einzelnen Stellen der Zahl berechnen
    do {
      Buffer[i++] = '0' + u % 10; // = String "0" (=48) + Rest von u/10
      u = u/10;
    } while( u > 0 );
   // den String in sich spiegeln (d.h. Reihenfolge umkehren)
    for( j = 0; j < i / 2; ++j ) {
      tmp = Buffer[j];
      Buffer[j] = Buffer[i-j-1];
      Buffer[i-j-1] = tmp;
    }
    Buffer[i] = '\0';
}

/*********************************
Timer 0 und 2 Interruptroutinen
**********************************/

ISR(TIMER2_COMP_vect) // Löst aus alle (16Mhz/1024/125) 8ms
{
s++;
}

ISR(TIMER0_OVF_vect) // Eingangsignal Overflow Zähler Timer 0
{
z++;
TIFR = (1<<TOV0); //ov flag timer 0 zurücksetzen
}

/*************************
Hauptfunktion
**************************/

int main(){
lcd_init(); // LCD init
TCCR2 |= (1<<WGM21); // CTC Mode Aktivierung Timer2
TCCR2 |= (1<<COM20) | (1<<COM21); // set OCR Flag bei "Compare Match"
TCCR0 |= (1<<CS02) | (1<<CS01) | (1<<CS00); //  Ext Flanke Interrupt (T0)
TCCR2 |= (1<<CS22) | (1<<CS21) | (1<<CS20); //  Prescaler Timer2 auf 1024 setzen
OCR2  |= 124; // Output Compare register Timer2 auf 124 (für Loop 0-124 => 125)
TIMSK |= (1<<OCIE2); // Enable comp match flag interrupt Timer 2 
TIMSK |= (1<<TOIE0); // Enable overflow flag interrupt Timer 0

sei();

while(1)
{
  if (s==125){ // "Zähltor": Abarbeitung jede Sekunde (16MHz/1024/125/125)
  count = TCNT0;
  s=0;
  ztmp = z;
  z= 0;
  TCNT0 = 0;
  lcd_clear();
  freq = 0.5+((256UL*ztmp + count)*korrekturfaktor); //siehe Bemerkung unten
/*********************************************************************************
- freq: Ermittlung der Frequenz: Anzahl Timer 0 Overflows + aktuellen Timer 0 Count
- die + 0,5 sind zum korrektem "Runden" ->Fließkommazahl zu Ganzzahl
- "korrekturfaktor" ist die SW-Korrektur zur Kompensation der lin.Quarzungenauigkeit 
**********************************************************************************/

  uLongtoChar(freq,anz);   // Unwandlung Long zu String für Displayausgabe
  set_cursor (0,1);     // Cursor Pos setzen
  lcd_string( anz );     // Anzeige der ermittelten Frequenz
  set_cursor (8,1);     // Cursor Pos setzen
  lcd_string( "Hz" );   // Anzeige Hz
  }
}
return 0;
}

Autor: Swen S. (swen1)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
der Code als Datei

Autor: Swen S. (swen1)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
foto :) eh ich wieder alles einstampfe

Autor: Matze (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
guten morgen swen,

möchte gerne deinen frequenzzähler nachbauen, leider bin ich totaler 
Laie   so das ich fragen wollte ob ich einen schaltplan für die LED - 
Variante und das hex-file bekommen könnte.

Vielen Dank!
Matze

Autor: Wolfgang (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Matze schrieb:
> einen schaltplan für die LED-Variante ...

Die LED-Variante auf dem Photo ist die Referenz und hat nichts mit dem 
vorgestellten Projekt zu tun ;-)

Autor: m.n. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Matze schrieb:
> möchte gerne deinen frequenzzähler nachbauen,

Vielleicht ließt Du hier weiter und stellst fest, dass ein Vorteiler 
nicht Böses ist. Beitrag "Vorteiler Frequenzzähler"

Autor: W.S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Swen S. schrieb:
> Vielleicht kann der eine oder andere damit
> was anfangen.

Und wie reagiert er auf asymmetrisches Tastverhältnis?
Wie reagiert er bei Input-Frequenzen über 8 MHz?
Wie hast du das Eingangsteil gestaltet? Oder ist sowas außerhalb deines 
Interessenbereiches?

W.S.

Autor: Kilo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Versuche seit Stunden das auf einen Atmega644 zu portieren aber ich 
bekomme das mit den Timern einfach nicht hin :((((((((
Hiiiilfe

Autor: Markus Weber (Firma: guloshop.de) (m-w)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kilo schrieb:
> Versuche seit Stunden das auf einen Atmega644 zu portieren aber ich
> bekomme das mit den Timern einfach nicht hin :((((((((
> Hiiiilfe

Hallo! Ok, vielleicht ist meine Frage doof, aber warum nimmst du nicht 
einfach einen ATmega8?

Falls es wirklich so ein Brummer wie der 644 sein muss, hast du die 
verwendeten Timer-Register per Datenblatt miteinander verglichen? Was 
genau "geht nicht"? Vielleicht ist der Fehler in Zeile 42? ;-)

Autor: Peter R. (pnu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Skeptische Frage:

Die Eingänge des atmega8 werden ja vom Kontroller im sampling-Verfahren 
eingelesen.

Sample-Takt ist dabei die Frequenz des Taktquarzes.(16MHz)

Gibt es da nicht um die 8 MHz herum nicht üble Fehlanzeigen, bedingt 
durch Abtastfehler, so nahe an der Nyquist-Grenze? Wird also z.B. eine 
Frequenz von 8,2000000 MHz nicht krottenfalsch angezeigt?

Autor: Kilo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Markus

Puh, gut das jemand schreibt, ich dachte schon der Thread ist zu alt :))

Aaaalso: Ich bastel gerade an einem Tricorder. Siehe:
Atmega Tricorder Projekt Star Trek

Deshalb habe ich schon einen Atmega644 verbaut und kann nicht mehr 
wechseln.
Ich bin ein totaler Laie was Timer betrifft. Bzw. kapiere ich einfach 
die Register nicht. Warum gibt es beim Atmega644 zum Beispiel TCCR0A und 
TCCR0B und beim Atmega 8 NUR TCCR0 ???
Von daher ist es für mich schwierig das umzuschreiben und zu vergleichen 
laut Datenblätter...

Als Test habe ich mir die Variable 's', welche im Interrupt hochgezählt 
wird im LCD anzeigen lassen. s wird zwar hochgezählt aber im 
Sekundentakt und nicht in 8ms. Lasse ich die Variable 'freq' anzeigen, 
so passiert gar nichts. Nichts wird angezeigt. Als ob die Variable s die 
125 gar nicht erreicht. Ganz komisch.
An T0 also PinB0 liegt eine Frequenz an. Takt des µC ist 16MHz. Ich 
denke mal das ich die Timer falsch initalisiert habe. Deswegen: 
HIIIIILFE :)

Autor: Kilo (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
PS: wenn ich alle Register mit 42 fülle, müsste es doch klappen oder? 
lach

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kilo schrieb:

> Ich bin ein totaler Laie was Timer betrifft. Bzw. kapiere ich einfach
> die Register nicht. Warum gibt es beim Atmega644 zum Beispiel TCCR0A und
> TCCR0B und beim Atmega 8 NUR TCCR0 ???

Weil der Timer 0 des M644 nun mal eine ganze Dinge mehr kann als der 
Timer 0 des M8.

> Von daher ist es für mich schwierig das umzuschreiben und zu vergleichen
> laut Datenblätter...

Wo liegt das Problem?
Du hast Code für einen M8. Aus dem suchst du dir raus, welche Bits im 
Konfigurationsregister gesetzt werden.
Dann gehst du ins Datenblatt des M8 und siehst dir dort in der Register 
Summary beim Timer 0 an, was diese Bits bedeuten.
Nachdem du dann weißt, was da eingestellt wird, gehts du ins Datenblatt 
des M644, wieder in die Register Summary beim Timer 0 und siehst in den 
dort vorhandenen Beschreibungen erst mal nach, wo du die gleiche 
FUnktionalität wieder findest. Aus dieser Beschreibung ergibt sich dann, 
wie die zu setzenden Bits beim M644 heissen und in welchem der beiden 
Konfigurationsregister das jeweilige Bit beheimatet ist.

Sehr wahrscheinlich werden die Bits beim M644 sogar gleich heissen, wie 
die beim M8. Aber verlassen würde ich mich nicht darauf. LIeber anhand 
der Funktionalität den Port vom M8 zum M644 machen.

Ist doch nicht weiter wild.

Und nein. Einen Kommentar im Programm würde ich bestenfalls als Hinweis 
nehmen und keinesfalls als bare Münze. Kommentare sind schon auch mal 
falsch. Im Zweifel gilt immer das Datenblatt.

> wird im LCD anzeigen lassen. s wird zwar hochgezählt aber im
> Sekundentakt und nicht in 8ms.

Da kann zb der Takt des Prozessors ein anderer sein, oder eben dieselben 
Vorteiler Bits stellen beim M8 einen anderen Vorteiler als beim M644 
ein.

> Takt des µC ist 16MHz.
Das haben schon viele gesagt. Hast du es kontrolliert?

: Bearbeitet durch Moderator
Autor: Kilo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Karl Heinz,

ok, du hast Recht.. das sollte eigentlich einfach sein! Ich werde es 
versuchen!!
Aber versuch du mal einen journalistischen Text vom arabischen ins 
deutsche zu übersetzen mit nur einem Wörterbuch! So in etwa geht es mir 
dabei! :)

Autor: Zwölf Mal Acht (hacky)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann sollte man den Tricorder vielleicht auf eine Plasticbuechse mit ein 
paar LED reduzieren ...

:-) :-) :-)

: Bearbeitet durch User
Autor: Kilo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, meiner Meinung nach habe ich alles so eingestellt wie es eigentlich 
sein sollte:
ISR(TIMER2_COMPA_vect) // Löst aus alle (16Mhz/1024/125) 8ms
{
s++;
}

ISR(TIMER0_OVF_vect) // Eingangsignal Overflow Zähler Timer 0
{
z++;
TIFR0 = (1<<TOV0); //ov flag timer 0 zurücksetzen
}


int main (void)
{
TCCR2B |= (1<<WGM21); // CTC Mode Aktivierung Timer2
TCCR2A |= (1<<COM2A0) | (1<<COM2A1); // set OCR Flag bei "Compare Match"
TCCR0B |= (1<<CS02) | (1<<CS01) | (1<<CS00); //  Ext Flanke Interrupt (T0)
TCCR2B |= (1<<CS22) | (1<<CS21) | (1<<CS20); //  Prescaler Timer2 auf 1024 setzen

OCR2A  |= 124; // Output Compare register Timer2 auf 124 (für Loop 0-124 => 125)
TIMSK2 |= (1<<OCIE2A); // Enable comp match flag interrupt Timer 2 
TIMSK0 |= (1<<TOIE2); // Enable overflow flag interrupt Timer 0

sei();

...

Aber es tut sich nix bei der Anzeige für Frequenz!

Autor: Lutz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kilo schrieb:
> TIMSK0 |= (1<<TOIE2); // Enable overflow flag interrupt Timer 0

Zumindest zum Schluß hast du wohl beim Portieren mit der Konzentration 
nachgelassen.

Autor: Lutz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kilo schrieb:
> TCCR2B |= (1<<WGM21); // CTC Mode Aktivierung Timer2

dito

Autor: Sdjelano w GDR (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Hacky ätzte:
>Dann sollte man den Tricorder vielleicht auf eine Plasticbuechse mit ein
>paar LED reduzieren ...

>:-) :-) :-)

Du bist einer der größten Komiker, die ich hier erlebt habe.
:-( :-( :-(

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>> TIMSK0 |= (1<<TOIE2); // Enable overflow flag interrupt Timer 0

>Zumindest zum Schluß hast du wohl beim Portieren mit der Konzentration
>nachgelassen.

Da passiert nichts. TOIE2 ist genau wie TOIE0 Bit0.

MfG Spess

Autor: Lutz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann schon sein; ich klappere nun nicht jedes Register ab.
Und stimmen sollten die defines ja prinzipiell, sonst kann man ja 
einfach magische Zahlen reinkodieren ...

Viel wichtiger ist, daß er, wie von Karl Heinz beschrieben, die 
Register durchgeht und versteht. Das ist die erforderliche Investition 
in die Zukunft.

Autor: Kilo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich hab jetzt nochmal ein paar Änderungen gemacht und versucht 
anzupassen.. mh..

Wenn ich mir die variable s anzeigen lasse dann zählt die irgendwo im 
Hunderterbereich hoch und hört gar nicht mehr auf.. also nix von 
0-125... ach keine ahnung.. ich blicks nicht :(

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>hört gar nicht mehr auf.. also nix von
>0-125... ach keine ahnung.. ich blicks nicht :(

Dann ersetze doch mal

if (s==125)

durch

if (s>=125)

MfG Spess

Autor: Kilo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hatte ich auch schon mal versucht!

So, ich bekomme nun endlich eine Anzeige der Frequenz.. Juhu!!!
Jetzt muss ich erstmal checken ob der wert auch stimmt!

Danke erstmal an Euch!!!!

Autor: Kilo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also es funktioniert jetzt! JUHU
Bekomme eine Frequenz angezeigt mit angeschlossenem 
Luftfeuchtigkeitssensor. Diese lag heute zwar bei ca. 107kHz was laut 
Referenz Wetterstation 49% Luftfeuchtigkeit bedeuten müsste.

Hab mir den Sensor aus einer Wetterstation ausgebaut. Keine 
Beschriftung, von daher keine Ahnung welche Marke... Muss ich also mal 
ein bißchen rumtüfteln an meiner Formel!

Autor: Kilo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also der wert stimmt nicht...
Der µC läuft mit 16MHz, nachgemessen und auch ausgegeben mit CKOUT.
Frequenzmesser zeigt mir ca. 80KHz an... der Atmega gibt aber nur ca. 
20KHz an...grummel...

Autor: Ulrich (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Im Programm sind noch so einige Schwächen drin. Einmal bei der 
Festlegung der Torzeit, und dann noch einmal beim Auslesen der 
Zählerwerte. Mit etwas größeren Fehler (bis etwa 256 Hz + ca. 0.01%) ist 
dabei also zu rechnen. Wegen der Abtastung geht der Zähler auch nur 
gerade so bis zum halben µC Takt, realistischer wäre eher 1/4-1/3 des 
Taktes je nach Tastverhältnis.

Das Problem beim auslesen des Timers ist, da noch einen ggf. anstehenden 
Overflow Interrrupt abzufangen. Das geht besser indem man den Timer0 
kurz stopt, dann erst ausliest. Der Interrupt kommt dann schon wenn 
nötig.

Das mit der Torzeit ist etwas trickreicher. So geht es jedenfalls nur 
sehr schlecht, weil die Abfrage von s in einer Schleife unterschiedlich 
lange braucht - die Interrupts vom Timer 0 sorgen dann dafür das die 
genaue Zeit von der Frequenz abhängt. Besser wäre es die Abfrage auf das 
Ende der Torzeit schon im Interrupt zu machen, und da den Timer 0 
bereits zu stoppen.

Auch da hat man noch ein kleines Problem: durch einen Interrupt von 
Timer0 kann der Interrupt für das Ende der Torzeit noch etwas verzögert 
werden, wenn auch nicht lange. Eine Abhilfe hierfür ist nicht so einfach 
aber möglich:

Der Overflowinterrupt für timer0 wird kurz vor Ablauf der Torzeit 
abgeschaltet - man muss nur sicher gehen, das in der kurzen Zeit am Ende 
nur maximal 1 Overflow vorkommen kann - beim 8 bit timer0 hat man also 
512 Zyklen zeit am Ende. Sinnvollerweise läuft dafür der timer 1 nicht 
mehr mit so großem Vorteiler und dafür aber deutlich weiter als 125, 
sondern nur noch maximal 64, so dass man den Compare match Interrupt für 
die kurze Zeit von etwa 200 Zyklen nutzen kann. Die Tor-Zeit muss ggf. 
auch nicht genau eine Sekunde sein, wenn man den Abgleich über eine 
Multiplikation am Ende macht - besser wäre da sowieso eine 
Berücksichtigung des µC Taktes über den Zählerstand von Timer1 an Anfang 
der Messung - dann idealerweise ohne Vorteiler oder nur 8. So kann man 
dann auch Rundungsfehler vermeiden und man könnte wirklich die +-1 Hz 
Grenze eines klassischen Zählers erreichen.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.