Forum: Mikrocontroller und Digitale Elektronik Genauer ungenau Counter


von Leo L. (Gast)


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

von Jan (Gast)


Lesenswert?

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

von Werner B. (Gast)


Lesenswert?

Spielt der "Noise Canceler" eine Rolle?

Könnte Da eine Hysterese ein Rolle spielen?

Messe doch mal zwischen den jeweils entgegengesetzen Signalflanken?

von Mark S. (struberg)


Lesenswert?

Mißt du ein Sinussignal oder ein Rechtecksignal?

von Rahul D. (rahul)


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.

von Leo L. (Gast)


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

von Rahul D. (rahul)


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.

von Werner J. (werner_j)


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

von Hannes L. (hannes)


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.

...

von Hannes L. (hannes)


Lesenswert?

Wo ist das "o" in "Response" geblieben???

...

von Leo L. (Gast)


Angehängte Dateien:

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

von ,,,, (Gast)


Lesenswert?

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

von Μαtthias W. (matthias) Benutzerseite


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

von Leo L. (Gast)


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

von Werner B. (Gast)


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
}

von Werner B. (Gast)


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
}

von Leo L. (Gast)


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

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.