Forum: Mikrocontroller und Digitale Elektronik Messung niedriger Frequenzen schlägt fehl


von Sash u. (sashm)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

nach kräftiger Unterstützung dieses Forums klappt meine Frequenzmessung 
mit einem ATmega32 und einem Frequenzgenerator(als Testsignal) ab ca. 20 
Hz einwandfrei. Die Werte werden per UART übertragen. Doch gehe ich 
niedriger, so erhalte ich willkürliche Werte. Ich weiß nicht, woran es 
liegen kann. Die Frequenzen werden nochmal zusätzlich mit einem 
Oszilloskop überprüft.

Könnt ihr mir vielleicht weiter helfen?

Ich benutze eine interne Frequenz von 8MHz, Timer1 mit 125kHz, Input 
Capture Unit reagiert auf steigende Flanke vom Analog Komparator. Dieser 
hat eine Referenzspannung von einigen mV. (Ich habe mehrere 
Oszillator-Frequenzen und Taktefrequenzen des Timer1 ausprobiert und 
immer bekomme ich das selbe Fehlerbild)

Im Anhang befindet sich der komplette Code.

Hier ist der Ausschnitt aus dem Quellcode: (Nachträglich werde ich noch 
eine Mittelung des Wertes einbauen)

while(1)
  {

    TCNT1 = 0;            //Setze Zählerstand T1 auf 0
    ICR1=0;              //Setze Input Capture Register auf 0
    TIFR |= (1<<ICF1);         // Input Capture Flag löschen

    while (!(TIFR & 1<<ICF1))      //Warte bis nächste positive Flanke
    {
      if (TIFR & 1<<TOV1)      //Ergibt sich innerhalb von 1 
Timerüberlauf von T1 keine positive Flanke,
      {              // so ist v=0 km/h
          UART_SendByte('v');
          UART_SendByte(0);
          TIFR |= (1<<TOV1);  //Timerüberlauf = 0 setzen

      }
    }

    TCNT1 = 0;          //Setze Zählerstand T1 auf 0
    ICR1=0;            //Setze Input Capture Register auf 0
    TIFR |= (1<<ICF1);        // Input Capture Flag löschen

    while (!(TIFR & 1<<ICF1))      //Warte bis nächste positive Flanke

    {
      if (TIFR & 1<<TOV1)      //Ergibt sich innerhalb von 1 
Timerüberlauf von T1 keine positive Flanke,
      {          //so ist v=0 km/h

          UART_SendByte('v');
          UART_SendByte(0);
          TIFR |= (1<<TOV1);
      }
    }

    Periode = ICR1;          // Lese Wert der nächsten positiven Flanke


    //Bestimmung der Geschwindigkeit

    Frequenz = (125000.0 / (double)Periode);

von André A. (nummer5) Benutzerseite


Lesenswert?

Die Auswertung der ICP in der main-Schleife verstehe nicht nicht so 
ganz, ich würde das über einen Interrupt lösen.
Du überprüfst zwar auf einen Überlauf des Timers, berechnest am Ende 
trotzdem immer die Frequenz, die im Falle des Überlaufes nicht stimmt.

Hast du mal ausgerechnet was die niedrigste Frequenz ist die du noch 
messen kannst, ohne das der Timer überläuft?

von Andreas K. (derandi)


Lesenswert?

Warum wertest du den Timerüberlauf nicht einfach mit dem passenden 
Interrupt aus?

von Ulrich (Gast)


Lesenswert?

Wenn man schon input capture nutzt, dann sollte man die Differenz von 2 
ICP Events nutzen, nicht bei der ersten Flanke nur dem Timer auf 0 
setzen und dann messen.

Der Fehler könnte durch die Qualität des Einagnsgsignals verursacht 
sein.  Gibt es das Problem denn auch bei einem Digitalen Eingangssignal 
?

von Sash u. (sashm)


Lesenswert?

Erstmal Danke für die schnellen Antworten!!!


André Althaus schrieb:
> Die Auswertung der ICP in der main-Schleife verstehe nicht nicht so
> ganz, ich würde das über einen Interrupt lösen.
> Du überprüfst zwar auf einen Überlauf des Timers, berechnest am Ende
> trotzdem immer die Frequenz, die im Falle des Überlaufes nicht stimmt.

Der Überlauf des Timer findet nur statt, wenn kein Signal oder eine zu 
kleine Frequenz anliegt. Wenn kein Signal anliegt, klappt die Auswertung 
ohne Probleme. Wird die Frequenz zu klein, bekomme ich auch diese 
willkürlichen Werte.



André Althaus schrieb:
> Hast du mal ausgerechnet was die niedrigste Frequenz ist die du noch
> messen kannst, ohne das der Timer überläuft?

Die niedrigste Frequenz, welche ohne Fehler angezeigt wird liegt ca. bei 
20Hz. An der Stelle ist der Timer gerade mal 6250 also weit vom Überlauf 
entfernt. Ich denke, dass es nichts mit dem Überlauf zutun hat, denn der 
Fehler tritt weitaus früher auf.



Andreas K. schrieb:
> Warum wertest du den Timerüberlauf nicht einfach mit dem passenden
> Interrupt aus?

Weil der Interrupt nicht funktioniert hatte. Hab dann einfach wieder die 
klassische Variante gewählt, um erst einmal den 1. Fehler zu beheben. 
Danach kann ich dann den Quellcode etwas übersichtlicher gestalten.



Ulrich schrieb:
> Wenn man schon input capture nutzt, dann sollte man die Differenz von 2
> ICP Events nutzen, nicht bei der ersten Flanke nur dem Timer auf 0
> setzen und dann messen.

Das könnte ich versuchen, aber meinst du, der Fehler sei dadurch 
behoben? Ob ich jetz die Frequenz aus einem Wert berechne oder aus der 
Differenz zweier Werte, macht doch keinen Unterschied.



Ulrich schrieb:
> Der Fehler könnte durch die Qualität des Einagnsgsignals verursacht
> sein.  Gibt es das Problem denn auch bei einem Digitalen Eingangssignal
> ?

Wenn ich als Eingangssignal ein Rechtecksignal auswähle, dann tritt der 
Fehler genauso auf. Auf meinem Oszilloskop ist es ein reiner Sinus bzw. 
Rechteck.

von UR Schmitt (Gast)


Lesenswert?

Sascha M. schrieb:
> An der Stelle ist der Timer gerade mal 6250 also weit vom Überlauf
> entfernt.

Bist du dir sicher? Wenn man die 6250 mal 10 nimmt kommt das verdächtig 
nahe an 65536 heran also einen 16 Bit Überlauf.
Was misst du denn? Ständig wechselnde Frequenzen, oder systematisch 
falsche Werte also immer das gleiche falsche Ergebnis bei einer 
konstanten Frequenz?

von sashm (Gast)


Lesenswert?

UR Schmitt schrieb:
> Bist du dir sicher? Wenn man die 6250 mal 10 nimmt kommt das verdächtig
> nahe an 65536 heran also einen 16 Bit Überlauf.

Bei einer Timerfrequenz von 125kHz (Pro Takt 8us) und einer zu messenden 
Periodendauer von 20Hz = 50ms ergibt es einen Counterwert von 6250.


UR Schmitt schrieb:
> Was misst du denn? Ständig wechselnde Frequenzen, oder systematisch
> falsche Werte also immer das gleiche falsche Ergebnis bei einer
> konstanten Frequenz?

Ständig wechselnde Werte. Der Wert springt ständig zwischen 
Verschiedenen Werten hin und her...

von UR Schmitt (Gast)


Lesenswert?

sashm schrieb:
> Ständig wechselnde Werte. Der Wert springt ständig zwischen
> Verschiedenen Werten hin und her...

Dann könnte es ein Hardwareproblem sein. Vieleicht fängt sich deine 
Schaltung Netzbrumm oder Netzstörungen ein. Bei höheren Frequenzen 
merkst du das nicht weil die Messung schnell genug erfolgt.

von UR Schmitt (Gast)


Lesenswert?

Blöde Idee:
Du hast aber schon ausgeshlossen daß dein Testgenerator bei den 
Frequenzen stabil ist? Nicht daß der da ein Problem hat.

von Sash u. (sashm)


Lesenswert?

UR Schmitt schrieb:
> Dann könnte es ein Hardwareproblem sein. Vieleicht fängt sich deine
> Schaltung Netzbrumm oder Netzstörungen ein. Bei höheren Frequenzen
> merkst du das nicht weil die Messung schnell genug erfolgt.


Könnte möglich sein. Ich habe am Eingangspin des Atmegas mein 
Oszilloskop angeschlossen und sehe keine Störungen oder sonstige 
Verzerrung des Sinussignals.
Könnte ich das mit einem Trenntrafo lösen?


UR Schmitt schrieb:
> Blöde Idee:
> Du hast aber schon ausgeshlossen daß dein Testgenerator bei den
> Frequenzen stabil ist? Nicht daß der da ein Problem hat.


Ideen sind doch nie blöd :)
Das Bild auf dem Oszilloskop sieht sauber aus. Kann dort keine 
Verzerrungen feststellen... das wundert mich deshalb wirklich...

von Digi Tal (Gast)


Lesenswert?

du sprichst von Sinussignal....schalte um auf Rechteck; was passiert ?

von Sash u. (sashm)


Lesenswert?

Digi Tal schrieb:
> du sprichst von Sinussignal....schalte um auf Rechteck; was passiert ?

Der gleiche Fehler tritt auf.  Ich weiß wirklich nicht, woran es liegen 
könnte!

von Karl H. (kbuchegg)


Lesenswert?

Dann lass dir doch mal alle Werte ausgeben.
Fang bei ICR1 an.

(Seltsame Ausgabe hast du da. Da werden Buchstaben gesendet und hinten 
nach die Zahlen in Binärform. Hast du am anderen Ende der UART ein 
Spezialprogramm hängen? Wenn ja, dann würd ich erst mal eine vernünftige 
ASCII Ausgabe machen und da ganz einfach ein Terminal drannhängen. Dann 
kann man auch beliebige Ausgaben machen ohne am PC Programm rumdoktoren 
zu müssen)

von Sash u. (sashm)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Dann lass dir doch mal alle Werte ausgeben.
> Fang bei ICR1 an.
>
> (Seltsame Ausgabe hast du da. Da werden Buchstaben gesendet und hinten
> nach die Zahlen in Binärform. Hast du am anderen Ende der UART ein
> Spezialprogramm hängen? Wenn ja, dann würd ich erst mal eine vernünftige
> ASCII Ausgabe machen und da ganz einfach ein Terminal drannhängen. Dann
> kann man auch beliebige Ausgaben machen ohne am PC Programm rumdoktoren
> zu müssen)

Ok, ich werde es mal mit allen Werten ausprobieren! Gibt es da eine 
elegante Lösung zum Auslesen der Register?

Ich benutze ein Terminalprogramm, welches mir die Daten als Binär, Hex 
und Dezimal anzeigt: H-Term. Die Buchstaben hab ich nur zum Testen 
eingefügt, da ich am Ende die Geschwindigkeit erhalten möchte, habe ich 
das Formelzeichen v gewählt.

von Karl H. (kbuchegg)


Lesenswert?

Sash uM schrieb:

> Ok, ich werde es mal mit allen Werten ausprobieren! Gibt es da eine
> elegante Lösung zum Auslesen der Register?

Du brauchst nicht alle Werte, sondern nur die Register die du auch 
verwendest. Sind ja bei dir nicht allzuviele.

> Ich benutze ein Terminalprogramm, welches mir die Daten als Binär, Hex
> und Dezimal anzeigt: H-Term.

Dann mach dir doch als erstes mal vernünftige Ausgabefunktionen!
Nur mit einem UART_SendByte wirst du auf Dauer nicht weit kommen.
1
void UART_SendString( char * str )
2
{
3
  while( *str )
4
    UART_SendByte( *str++ );
5
}

und schon kannst du einen String ausgeben
1
    ....
2
    UART_SendString( "Fehler: Ueberlauf\n" );
3
    ....

dann noch welche für Zahlen
1
void UART_SendUI32( uint32 number )
2
{
3
  char Buffer[10];
4
  ultoa( number, Buffer, 10 );
5
  UART_SendString( Buffer );
6
}
7
8
void UART_SendUI16( uint16_t number )
9
{
10
  char Buffer[10];
11
  utoa( number, Buffer, 10 );
12
  UART_SendString( Buffer );
13
}
14
15
void UART_SendD( double number )
16
{
17
   ...
18
}


und schon bist du bereit, deine Register nach Herzenslust auszugeben
1
   ....
2
   UART_SendString( "ICR1 :" );
3
   UART_SendUI16( ICR1 );
4
   UART_SendString( "\n" );
5
   ...


und zwar ohne dass du ständig Bytes im HTerm entschlüsseln musst.
UNd wenn dir das immer noch zu mühsam ist, dann gibt es eben wieder eine 
neue Hilfsfunktion
1
void UART_Dump( char *Label, uint16_t value )
2
{
3
  UART_SendString( Label );
4
  UART_SendUI16( value );
5
  UART_SendString( "\n" );
6
}

und schon ist es kein Thema mehr die Werte von TCNT1, ICR1 und 
meinetwegen auch noch PIND auszugeben
1
    ....
2
    UART_Dump( "TCNT1 :", TCNT1 );
3
    UART_Dump( "ICR1  :", ICR1 );
4
    UART_Dump( "PIND  :", PIND );
5
    ...

Lass dir die Dinge als Text hinschreiben (OK, der Text "Fehler: 
Ueberlauf\n" ist ein bischen lang, da dauert dann die Übertragung schon 
seine Zeit, so dass du das Timing deines Programmes eventuell 
durcheinander bringst). Dann steht auf deinem Terminal schwarz auf weiss 
dort
1
** neue Messung
2
    TCNT1 : 8354
3
    ICR1  : 8120
4
    PIND  : 127
5
6
    --> Frequenz: 234,8
7
8
** neue Messung
9
    TCNT1 : 8359
10
    ICR1  : 8122
11
    PIND  : 127
12
13
    --> Frequenz: 234,3
14
15
** neue Messung
16
-->  Overflow
17
18
** neue Messung
19
    TCNT1 : 8359

und das ist dann schon etwas ganz anderes als wenn man Hex-Bytes mühsam 
entschlüsseln muss.

Ich werd nie verstehen, warum es so schwer ist, sich in ein paar Minuten 
ein paar Hilfsfunktionen zu machen. Statt dessen wird da lieber mit 
Krücken rumhantiert.

OK. Zurück zum Problem:
Ziel ist es heruaszufinden, ob die Werte in ICR1 schon springen oder 
nicht und wenn ja um wieviel sie springen.

von MWS (Gast)


Lesenswert?

Sash uM schrieb:
> Dieser hat eine Referenzspannung von einigen mV.

Wenn der Signalgenerator ein positives Signal gegen Masse liefert, dann 
würde ich AIN0/PB2 auf halbe Signalspannung setzen.

Zum Testen des Codes den AC abschalten und über ICP1/PD6 mit einem 
Rechtecksignal reingehen.

von MWS (Gast)


Lesenswert?

Dürfte zwar nicht der eigentliche Fehler sein, ist aber dennoch falsch:
1
TIFR |= (1<<ICF1);
Das veroderte Zurückschreiben in ein Flag-Register löscht nicht nur das 
gewünschte Bit, sondern alle die gesetzt waren.
Richtig ist's so:
1
TIFR = (1<<ICF1);

von Digi Tal (Gast)


Lesenswert?

nicht Frequenz sondern Periode messen

von Sash u. (sashm)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Dann mach dir doch als erstes mal vernünftige Ausgabefunktionen!
> Nur mit einem UART_SendByte wirst du auf Dauer nicht weit kommen.

Wow, großen Dank für die ganzen Hilfefunktionen. Ich werde mich dann 
schnellstmöglich darum kümmern.

MWS schrieb:
> Zum Testen des Codes den AC abschalten und über ICP1/PD6 mit einem
> Rechtecksignal reingehen.

Das ist auch eine gute Idee. Danke vielmals.

MWS schrieb:
> Dürfte zwar nicht der eigentliche Fehler sein, ist aber dennoch falsch:TIFR |= 
(1<<ICF1);Das veroderte Zurückschreiben in ein Flag-Register löscht nicht nur das
> gewünschte Bit, sondern alle die gesetzt waren.
> Richtig ist's so:TIFR = (1<<ICF1);

Oh, das hatte ich nicht gewusst. Ich bin davon ausgegangen, dass nur das 
Bit ICF1 gelöscht wird. Im Datenblatt stand beschrieben, dass zum 
Zurücksetzen des Input Capture Flag das Bit ICF1 gesetzt werden muss.

Aber mit dem Befehl
REGISTER1 |= (1<<BIT1)
wird doch nur das Bit1 im Register1 gesetzt, oder?

von Karl H. (kbuchegg)


Lesenswert?

Sash uM schrieb:


> Aber mit dem Befehl
> REGISTER1 |= (1<<BIT1)
> wird doch nur das Bit1 im Register1 gesetzt, oder?

Da diese Schreibweise identisch ist zu

   REGISTER1 = REGISTER1 | (1<<BIT1)

werden daher dann auch alle anderen Bits erneut als 1 in das Register 
geschrieben, die auch vorher schon 1 waren. Und da in diesem Register 
alle Bits auf Neubeschreiben mit 1 mit einem Zurücksetzen reagieren, 
setzt

  REGISTER1 |= (1<<BIT1)

alle bereits gesetzten Bits zurück.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.