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
10 Schritte von wie vielen? Interessant ist der prozentuale Fehler, nicht der absolute. Benutzt du einen internen oder externen Takt?
Spielt der "Noise Canceler" eine Rolle? Könnte Da eine Hysterese ein Rolle spielen? Messe doch mal zwischen den jeweils entgegengesetzen Signalflanken?
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.
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
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.
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
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. ...
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
Kleiner Tip am Rande: stell die Tabsize auf 50, sieht dann übersichtlicher aus.
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
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
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 | }
|
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 | }
|
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.