Genauer ungenau Counter

Gast #260482
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
#260486
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.
Gast #260487
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
#260488
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.
#260490
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.

...
Gast #260492
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
Angehängte Dateien:
Gast #260495
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
Gast #260496
Lesenswert?
• ▲
▼
Mal meine Version mit Kommentaren
1
#define STATE_RISING   0
2
#define STATE_FALLING  1
3
#define STATE_EVALUATE 2
4

5
static unsigned char icp_state = STATE_RISING;
6
static unsigned int dutycycle_value;
7
static volatile unsigned int periode_value;
8
static volatile unsigned char periode_value_changed = 0;
9

10
/*
11
 * Liefert neue Periodendauer oder 0 falls nicht Neues
12
 */
13
unsigned int getNewPeriodValue(void)
14
{
15
    unsigned int ret_val = 0;
16
    cli();  // atomare opertation
17
    if(periode_value_changed)
18
    {
19
        periode_value_changed = 0;
20
        ret_val = periode_value;
21
    }
22
    sei();
23
    return ret_val;
24
}
25

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

48
          // als 16Bit Wert auslesen kann.         
49
      TCCR1B|= (1<<ICES1);  // Rising edge will trigger capture.
50
      icp_state++;
51
        break;
52
    case STATE_EVALUATE:  
53
        periode_value = ICR1 - dutycycle_value;
54
        periode_value_changed = 1;
55
      TCNT1= 0x00; // Warum???
56
      TCCR1B&= ~(1<<ICES1);  // Falling edge will trigger capture.
57
      icp_state = STATE_FALLING;
58
        break;
59
    default:  
60
        errorcount++;
61
            periode_value_changed = 0;
62
      icp_state = STATE_RISING;
63
        break;
64
  }
65
  sei();  // Wird von "SIGNAL" sowiso wieder gesetzt, als weg
66
}
Gast #260497
Lesenswert?
• ▲
▼
Das hat mir keine Ruhe gelassen (meine Fehler im letzten Posting)
Jetzt können eigentlich nur noch Tabbföhler drin sein ;)
1
#define STATE_RISING   0 /* Initialisierungsphase */
2
#define STATE_FALLING  1 /* Warten auf fallende Flanke */
3
#define STATE_EVALUATE 2 /* Warten auf steigende Flanke */
4

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

12

13
static unsigned char icp_state = STATE_RISING;
14
static unsigned int dutycycle_value;
15
static volatile unsigned int periode_value;
16
static volatile unsigned char periode_value_changed = NEW_VALUE_NONE;
17

18
/*
19
 * Liefert neue Periodendauer oder 0 falls nicht Neues.
20
 *
21
 * Die Dauer der oberen "Halbwelle" wird als positiver,
22
 * die Dauer einer unteren "Halbwelle" als negativer
23
 * Wert geliefert.
24
 */
25
signed long getNewPeriodValue(void)
26
{
27
    signed long ret_val = 0L;
28

29
    cli();  // atomare opertation
30
    if(periode_value_changed == NEW_VALUE_UPPER)
31
    {
32
        ret_val = periode_value;
33
        periode_value_changed = NEW_VALUE_NONE;
34
    }
35
    elseif(periode_value_changed == NEW_VALUE_LOWER)
36
    {
37
        ret_val = periode_value;
38
        ret_val = -ret_val;
39
        periode_value_changed = NEW_VALUE_NONE;
40
    }
41
    sei();
42
    return ret_val;
43
}
44

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

57
  switch(icp_state)
58
  {
59
    case STATE_RISING:
60
        temp = ICR1;
61
        // Falling edge will trigger capture.
62
        TCCR1B &= ~(1<<ICES1);
63
      icp_state++;
64
        break;
65
    case STATE_FALLING:
66
        temp = ICR1;
67
        periode_value = temp - dutycycle_value;
68
        periode_value_changed = NEW_VALUE_UPPER;
69
        // Rising edge will trigger capture.
70
      TCCR1B |= (1<<ICES1);
71
      icp_state++;
72
        break;
73
    case STATE_EVALUATE:
74
        temp = ICR1;
75
        periode_value = temp - dutycycle_value;
76
        periode_value_changed = NEW_VALUE_UPPER;
77
        // Falling edge will trigger capture.
78
      TCCR1B &= ~(1<<ICES1);
79
      icp_state = STATE_FALLING;
80
        break;
81
    default:
82
        errorcount++;
83
      icp_state = STATE_RISING;
84
            // Wait for rising edge to start over
85
      TCCR1B |= (1<<ICES1);
86
        break;
87
  }
88
    dutycycle_value = temp;
89
}
Gast #260498
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

Eine eigene Frage? Dieser Thread ist über 20 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.