mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Genauer ungenau Counter


Autor: Leo L. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,
wollt in Erfahrung bringen, ob der Fehler meines Counters im normalen
Toleranzbereich liegt oder ob der Fehler mein Verschulden ist.

Anwendung: Frequenz/Periodendauer messen.
Messmethode: Flanken dedektieren und Zeit dazwischen messen.
uC: ATmega16 (Unter Verwendung der "Input Capture Unit" im uC)

(Fehler-)Beschreibung: Die Software die ich dazu geschrieben hab, gibt
mir immer einen um 10 Zählerschritte zu niedrigen Wert aus (Geprüft am
Oszilloskopen). Getesteter Frequenzbereich 100-1000Hz.

Ausgeschlossenes: An der Laufzeit meines Programs liegt es denke ich
nicht. Dafür ist der Fehler zu konstant. Und bei Laufzeitfehlern müsste
der Zählerwert höher sein und nicht niedriger.

Vermutung: Quarz zu langsam oder interner Oszilator zu langsam.

<<Und hier nochmal meine Frage: Normaler Toleranzbereich?>>

PS: Denke es kommen jetzt erst einmal mehr Fragen als Antworten. :D

Autor: Jan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
10 Schritte von wie vielen? Interessant ist der prozentuale Fehler,
nicht der absolute.
Benutzt du einen internen oder externen Takt?

Autor: Werner B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Spielt der "Noise Canceler" eine Rolle?

Könnte Da eine Hysterese ein Rolle spielen?

Messe doch mal zwischen den jeweils entgegengesetzen Signalflanken?

Autor: Mark Struberg (struberg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mißt du ein Sinussignal oder ein Rechtecksignal?

Autor: Rahul Der trollige (rahul)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
poste doch einfach mal den Code!
Wenn es bei jeder Frequenz 10 Schritte (also Taktzyklen) sind, dann
liegt es vermutlich am Programm.
Wenn es Unterschiede bei den Frequenzen gibt, oder Abweichungen bei
einer Frquenz, dann müsste es mit der Takterzeugung zutun haben.

Autor: Leo L. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erst mal danke für das rege Interesse und eure Hilfsbereitschaft.

Also...

Jan: Es sind immer 10 Schritte. Egal ob ich 100Hz(Zählerstand= 9990),
110Hz(9080) ... 990Hz (1000) oder 1000Hz(990) messen will. Prozentual
gesehen, steigt mein Fehler mit der Frequenz. Beäugt man es schlicht
und einfach, zählt er mir immer 10 zu wenig. uC-Clock= 8MHz (Quarz) und
Prescaler=8 macht pro Zählschritt 1us.

Werner: Ich denke nicht, dass der Noise Canceler eine Rolle spielt. Zum
einen hab ich den nicht aktiviert und zum anderen würde mein Zählerstand
denke ich grösser ausfallen und nicht kleiner.
An eine Hysterese hab ich auch gedacht. War´s aber leider nicht. Hab
die Anstiegszeiten mit dem Oszi. gemessen. liegt im ns-Bereich. Ist
denke ich zu vernachlässigen, da ich im us-Bereich zähle.
Das mit den entgegengesetzten Signalflanken probier ich mal.

Mark: Es ist eine Rechteckspannung (TTL-Pegel).

Rahul: Das mit dem Code posten kann ich leider erst morgen machen.

Nach euren Aussagen, heisst das wohl, dass das nicht normal ist.
Dann heisst das für mich wohl auch, weiter mit der Fehlersuche.

Gruß,
Leo

Autor: Rahul Der trollige (rahul)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wenn die Abweichung konstant (absolut) ist, dann ist der Fehler doch
erst bei Frequenzen relevant, für die die 10 Schritte ein Fehler im
Prozentbereich (>1000Hz) bedeuten.
Und ich vermute den Fehler eher im Programm.
Würde es am Quarz oder so liegen, dann würde die Abweichung schwanken.

Autor: Werner Just (werner_j)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Vermutung: 10 Takte zwischen Interrupt-Aufruf und löschen des Timers?

Aber ohne Deinen Code zu kennen ist es unmöglich zu erkennen wo die 10
Zählschritte (80 Takte?) versickern.

Ciao,
Werner

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das vermute ich auch. Die 10 Takte können vergehen durch
Intterrupt-Respnse-Time und Sprung zur ISR.

Man sollte bei ICP den Timer keinesfalls löschen, sondern die Differenz
zum vorherigen "Zeitstempel" berechnen (den man sich natürlich merken
muss). Das wird dann sehr genau. Und zusätzlich kann man noch die
Output-Compare-Interrupts nutzen, ohne ICP zu beeinrtächtigen.

...

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wo ist das "o" in "Response" geblieben???

...

Autor: Leo L. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Tach zusammen....

Rahul: Ganz genau erkannt. Will auf 1Hz genau messen. Bei 100Hz ist es
noch kein Problem (Fehler= 0,1Hz). Bei 1kHz ist es dann unaktzeptabel
(Fehler= 10Hz).

Rahul&Werner: Ob dass nun am Programm liegt, wollt ich eigentlich
ausschliessen. Hab mir meinen Code im AVRStudio angeschaut
(Disassembler) und die Befehle gezählt. Passt!
Hab die die dazugehörige Interrupt Service Routine mal angehängt.

Gruß,
Leo

Autor: ,,,, (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kleiner Tip am Rande: stell die Tabsize auf 50, sieht dann
übersichtlicher aus.

Autor: Μαtthias W. (matthias) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

wie Hannes geschrieben hat: Du solltest niemals das Timer-Zählregister
löschen. Input Capture funktioniert auch ohne dieses Löschen wenn du
die Differenz zwischen zwei Capture-Events berechnest.

Matthias

Autor: Leo L. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich denke, dat is it.

>>> Intterrupt-Resp"o"nse-Time und Sprung zur ISR. <<<

Hab zum Testen ´nen Portpin in der ISR gesetzt. Hab dann darauf
getriggert und siehe da: Zwischen Flanke des Eingangssignal und dem
Peak vom Portpin ein schönes konstantes Delay. Werde jetzt (erstmal)
ein Korrekturwert zu meinem Zähler dazu addieren und weiter coden.
Hoffe das es so gut geht.

Dank&Gruß an alle, lesen von einander,
Leo

Autor: Werner B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mal meine Version mit Kommentaren
#define STATE_RISING   0
#define STATE_FALLING  1
#define STATE_EVALUATE 2

static unsigned char icp_state = STATE_RISING;
static unsigned int dutycycle_value;
static volatile unsigned int periode_value;
static volatile unsigned char periode_value_changed = 0;

/*
 * Liefert neue Periodendauer oder 0 falls nicht Neues
 */
unsigned int getNewPeriodValue(void)
{
    unsigned int ret_val = 0;
    cli();  // atomare opertation
    if(periode_value_changed)
    {
        periode_value_changed = 0;
        ret_val = periode_value;
    }
    sei();
    return ret_val;
}

/*
 * Zu beachten ist, dass die Periodendauer natürlich kleiner als die 
 * Zeit eines vollen Zählerdurchlaufes ist.
 * Der Zähler muss nicht auf 0 gesetzt werden.
 * Die Diskussion ob a-b == -(-a - (-b)) ist, 
 * wurde bereits in der Schule geführt ;) 
 */
SIGNAL (SIG_INPUT_CAPTURE1)
{
  //cli() ist absolut überflüssig, sind im "SIGNAL" disabled.
  //TCCR1B|= (1<<CS11); Input Capture stoppt doch den Zähler nicht;
Oder? Also weg damit!
  switch(icp_state)
  {
    case STATE_RISING:   
        TCCR1B &= ~(1<<ICES1);  // Falling edge will trigger capture.
      icp_state++;
        break;
    case STATE_FALLING:  
        dutycycle_value = ICR1;
          // Alle mir bekannten AVRs haben ICRH+ICRL so dass man sie als

          // als 16Bit Wert auslesen kann.         
      TCCR1B|= (1<<ICES1);  // Rising edge will trigger capture.
      icp_state++;
        break;
    case STATE_EVALUATE:  
        periode_value = ICR1 - dutycycle_value;
        periode_value_changed = 1;
      TCNT1= 0x00; // Warum???
      TCCR1B&= ~(1<<ICES1);  // Falling edge will trigger capture.
      icp_state = STATE_FALLING;
        break;
    default:  
        errorcount++;
            periode_value_changed = 0;
      icp_state = STATE_RISING;
        break;
  }
  sei();  // Wird von "SIGNAL" sowiso wieder gesetzt, als weg
}

Autor: Werner B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das hat mir keine Ruhe gelassen (meine Fehler im letzten Posting)
Jetzt können eigentlich nur noch Tabbföhler drin sein ;)
#define STATE_RISING   0 /* Initialisierungsphase */
#define STATE_FALLING  1 /* Warten auf fallende Flanke */
#define STATE_EVALUATE 2 /* Warten auf steigende Flanke */

/* Alle Werte gelesen und nichts Neues zu melden ;) */
#define NEW_VALUE_NONE  0
/* Dauer obere/untere <Anführungszeichen>Halbwelle</Anführungszeichen>
*/
#define NEW_VALUE_UPPER 1
#define NEW_VALUE_LOWER 2


static unsigned char icp_state = STATE_RISING;
static unsigned int dutycycle_value;
static volatile unsigned int periode_value;
static volatile unsigned char periode_value_changed = NEW_VALUE_NONE;

/*
 * Liefert neue Periodendauer oder 0 falls nicht Neues.
 *
 * Die Dauer der oberen "Halbwelle" wird als positiver,
 * die Dauer einer unteren "Halbwelle" als negativer
 * Wert geliefert.
 */
signed long getNewPeriodValue(void)
{
    signed long ret_val = 0L;

    cli();  // atomare opertation
    if(periode_value_changed == NEW_VALUE_UPPER)
    {
        ret_val = periode_value;
        periode_value_changed = NEW_VALUE_NONE;
    }
    elseif(periode_value_changed == NEW_VALUE_LOWER)
    {
        ret_val = periode_value;
        ret_val = -ret_val;
        periode_value_changed = NEW_VALUE_NONE;
    }
    sei();
    return ret_val;
}

/*
 * Zu beachten ist, dass die Periodendauer
 * natürlich kleiner als die
 * Zeit eines vollen Zählerdurchlaufes ist.
 * Der Zähler muss nicht auf 0 gesetzt werden.
 * Die Diskussion ob a-b == -(-a - (-b)) ist,
 * wurde bereits in der Schule geführt ;)
 */
SIGNAL (SIG_INPUT_CAPTURE1)
{
    unsigned int temp;

  switch(icp_state)
  {
    case STATE_RISING:
        temp = ICR1;
        // Falling edge will trigger capture.
        TCCR1B &= ~(1<<ICES1);
      icp_state++;
        break;
    case STATE_FALLING:
        temp = ICR1;
        periode_value = temp - dutycycle_value;
        periode_value_changed = NEW_VALUE_UPPER;
        // Rising edge will trigger capture.
      TCCR1B |= (1<<ICES1);
      icp_state++;
        break;
    case STATE_EVALUATE:
        temp = ICR1;
        periode_value = temp - dutycycle_value;
        periode_value_changed = NEW_VALUE_UPPER;
        // Falling edge will trigger capture.
      TCCR1B &= ~(1<<ICES1);
      icp_state = STATE_FALLING;
        break;
    default:
        errorcount++;
      icp_state = STATE_RISING;
            // Wait for rising edge to start over
      TCCR1B |= (1<<ICES1);
        break;
  }
    dutycycle_value = temp;
}

Autor: Leo L. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Werner,
hab mir deine Version mal angeschaut. Gefällt mir ganz gut.

Aber was passiert wenn der Zähler überläuft und wieder von 0 an gezählt
wird (temp < dutycycle_value)?

Deine Erklärung als Kommentarzeilen hab ich leider nicht verstanden.

Gruß,
Leo

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.