Hallo, es gibt doch sicher die Möglichkeit im Avr Studio zu sagen, dass bestimmte Zeiöen nicht wegoptimiert werden sollen. Z.b. möchte ich das folgende Zeilen tatsächlch so ausgeführt wird, wie ich es geschrieben habe: x = y; z = x; mfg
Warum sollte der C-Compiler da was wegoptimieren? x und z erhalten beide den Wert von y.
Es gibt Wege und Tricks, wie man bestimmte Optimierungen gezielt verhindet. Beispielsweise indem die Variablen volatile sind. Andere Tipps setzen einen grösseren Kontext voraus. Ausserdem wäre noch offen, ob du grad die richtige Frage stellst, oder schon ein Weilchen auf dem Holzweg unterwegs bist und an der falschen Stelle fragst.
Floh schrieb: > Warum sollte der C-Compiler da was wegoptimieren? > x und z erhalten beide den Wert von y. Wahrscheinlöich, weil ich x nur an dieser Stelle verwende und sonst nirgends im Code.
A. K. schrieb: > Ausserdem wäre noch offen, ob du grad die richtige Frage stellst, oder > schon ein Weilchen auf dem Holzweg unterwegs bist und an der falschen > Stelle fragst. Es handelt sich hier um eine Zeitkritische verarbeitung. Die wirklichen Zeilen in meinem Code lauten:
1 | value = TCNT1; |
2 | capture._int[x*2] = value; //Dieser Code verbraucht ca. 30 byte mehr als der obere, deutlich länger als der obere Code. |
Ich möchte erst den zeitkritischen Transfer in value machen, dann den Zeitaufwendigerendarunter.
So wird das nix. Aber vielleicht meist du sowas: Beitrag "GCC Optimierer zerlegt Block (compound-statement)"
Schreib mal die Variablendefinitionen dazu, daß man das übersetzen und angucken kann. Peter
1 | [...]
|
2 | unsigned int value; |
3 | |
4 | union{ |
5 | unsigned char _char[NULL_N*4]; |
6 | unsigned int _int[NULL_N*2]; |
7 | unsigned long _long[NULL_N]; |
8 | }capture; |
9 | [...]
|
10 | capture._int[x*2] = TCNT1; |
11 | caapture._int[x*2+1] = (int)overflow; |
12 | [...]
|
capture._int[x*2] = TCNT1; Diese Zeile dauert viel zu lange. 30 Bytes (warum soviel?).
1 | void test( uint8_t x ) |
2 | {
|
3 | capture._int[x*2] = TCNT1; |
4 | 82: 2c b5 in r18, 0x2c ; 44 |
5 | 84: 3d b5 in r19, 0x2d ; 45 |
6 | 86: e8 2f mov r30, r24 |
7 | 88: f0 e0 ldi r31, 0x00 ; 0 |
8 | 8a: ee 0f add r30, r30 |
9 | 8c: ff 1f adc r31, r31 |
10 | 8e: ee 0f add r30, r30 |
11 | 90: ff 1f adc r31, r31 |
12 | 92: e0 5a subi r30, 0xA0 ; 160 |
13 | 94: ff 4f sbci r31, 0xFF ; 255 |
14 | 96: 31 83 std Z+1, r19 ; 0x01 |
15 | 98: 20 83 st Z, r18 |
16 | }
|
17 | 9a: 08 95 ret |
TCNT1 wird doch zuerst eingelesen. Und der Rest ist eben den Index aufdröseln. Das geht nicht besser. 30 Byte = 15 Befehle ist fürn nen RISC garnichts. Besser wirds nur mit nem CISC, der entsprechende Adressierungsbefehle hat. Peter
Es kann passieren, dass eine vorherige Berechnung von x erst an dieser Stelle durchgeführt wird. Das entspricht dann exakt dem Problem im oben angeführten Thread. Abhilfe wäre dann wie ebendort: #define barrier(var) asm volatile ("" : "+r"(var)) ... barrier(x); capture._int[x*2] = TCNT1; Effektiver wäre aber wohl sowas wie unsigned *p = &capture._int[x*2]; barrier(p); p[0] = TCNT1; p[1] = (int)overflow; weil dann die Adressrechnung ebenfalls vorgezogen wird.
ALso es geht um Frequenzmessung. Wenn ich 55 Hz erzeuge messe ich ca. 55,01.. Hz und bei 4000HZ messe ich 4100. Der Timerwert spiegelt die Frequenz wieder. Es scheint ein konstanter Verarbeitungswert des Timerwertes um 30-40 Taktzyklen vorzuliegen, welcher den Timerwert verfälscht. Bei tiefen Frequenzen sind diese +30 kaum erwähnenswert, da dem gegenüber ein Timerwert von 100.000+ steht. Bei sehr hohen Frequenzen liegt allerdings ein Timerwert von 4000- vor, da sind dann diese 30 Taktzyklen verarbeitungszeit katastrophal. Also vom feststellen, dass ein Nulldurchgang passiert ist, bis zu dem Zeitpunkt, wo der Timerwert abgeseichert ist vergeht zuviel Zeit. Die Schleife, ob ein Nulldurchgang passiert braucht ca. 14 Byte. Das kann natürlich auch die Fehlerquelle sein. Ich hab mal einen schnelleren Quarz verwendet und schon ist der Fehler deutlich geringer. Bin aber schon bei 16Mhz.
Ich verwende jetzt mal den ICP, kriege aber identische Werte heraus. Der Entsprechende Code sieht ungefähr so aus:
1 | for(x = 0; x < NULL_N ; x++){ //N Nulldurchgänge messen |
2 | if(TIFR & (1<<ICF1)) TIFR |= (1<<ICF1); |
3 | TCNT1 = 0; |
4 | overflow = 0; |
5 | if(TIFR & (1<<TOV1)) TIFR |= (1<<TOV1); |
6 | |
7 | do{ |
8 | if(TIFR & (1<<TOV1)){ |
9 | overflow++; |
10 | TIFR |= (1<<TOV1); |
11 | if(overflow > 4) break; |
12 | }
|
13 | }while(!(TIFR & (1<<ICF1))); |
14 | |
15 | capture._int[x*2] = ICR1; |
16 | capture._int[x*2+1] = (int)overflow; |
17 | |
18 | if(overflow > 4) break; |
19 | }
|
Kann vielleicht die Software die ich verwende doch ungenaue Frequenzen erzeugen? Oder habe ich bei der Konfiguration des ICP etwas verkehr tgemacht? Ich benutze den Analog Komparator als Trigger.
Pardon. Ich messe bei 4000Hz nun 4030Hz, vorher waren es 4100Hz. Noch mehr Tipps`?
Nils schrieb: > if(TIFR & (1<<ICF1)) TIFR |= (1<<ICF1); > TCNT1 = 0; Damit holst Du doch wieder nen Jitter rein. Du mußt den Timer durchlaufen lassen und die Differenz von einem zum nächsten ICP nehmen. Peter
Peter Dannegger schrieb: > Damit holst Du doch wieder nen Jitter rein. > Du mußt den Timer durchlaufen lassen und die Differenz von einem zum > nächsten ICP nehmen. Natürlich kostet das Zeit, aber diese Befehle werden bei einer maximal zu messenden Frequenz von ca. 4400Hz doch locker innherlab einer halben Periode durchgeführt. Also erst die neue Messung initialisieren, dann warten, bis das Flag vom ICP gesetzt wird und darauf reagieren. Bis das Warten auf das Flag beginnt ist doch bei weitem noch keine halbe Periode vergangen. Nach dem Warten wird ebenfalls wieder keine halbe Periode vergehen, bis wann auf den nächsten Nulldurchgang wartet. Bzw. da der ICP auf steigende oder fallende Flanke reagiert und nicht auf einen Flankenwechsel, kann man nur ganze Perioden messen oder? So hab ich es eben gemacht.
Nils schrieb: > Natürlich kostet das Zeit, aber diese Befehle werden bei einer maximal > zu messenden Frequenz von ca. 4400Hz doch locker innherlab einer halben > Periode durchgeführt. Es bringt nichts, den Timer am Ende der Messzeit per Capture taktgenau einzufangen, aber dessen Anfangswert per Software mit Jitter zu setzen. Das kann sogar weniger genau sein als zweimal per Software. Da du den Timer nicht per Capture-Event auf 0 setzen kannst, musst du statt dessen die Differenz zwischen zwei Captures verwenden. Nur so wird das taktgenau. Der Anfang der Messung ist genauso wichtig wie das Ende.
Nils schrieb: > Ich verwende jetzt mal den ICP, kriege aber identische Werte heraus. > Der Entsprechende Code sieht ungefähr so aus: > > [c]for(x = 0; x < NULL_N ; x++){ //N Nulldurchgänge messen > if(TIFR & (1<<ICF1)) TIFR |= (1<<ICF1); > TCNT1 = 0; > overflow = 0; > if(TIFR & (1<<TOV1)) TIFR |= (1<<TOV1); Hä? Gemeint war: verwende den Input Capture Interrupt in Form einer ISR! Hier hast du doch schon wieder Latenzen. es gibt doch wirklich genügend Frequenzzähler-Beiträge hier im Forum und in der Codesammlung. Mal ansehen wie andere das machen?
A. K. schrieb: > Da du den Timer nicht per Capture-Event auf 0 setzen kannst, musst du > statt dessen die Differenz zwischen zwei Captures verwenden. Nur so wird > das taktgenau. Der Anfang der Messung ist genauso wichtig wie das Ende. Danke, das habe ich verstanden.
Nils schrieb: > Danke, das habe ich verstanden. Jetzt musst du nur noch verstehen, dass man zwei uint16_t-Werte (aus dem ICRx) unabhängig von ihrer aktuellen Größe einfach und bedenkenlos voneinander subtrahieren kann, solange man sich sicher ist, dass zwischen beiden Werte höchstens ein timer overflow dazwischen liegt.
Jörg Wunsch schrieb: > bedenkenlos voneinander subtrahieren kann, solange man sich > sicher ist, dass zwischen beiden Werte höchstens ein timer overflow > dazwischen liegt. Und genau da liegt bei ihm der Hase im Pfeffer, denn der Code zählt auch die Overflows mit, weil er sonst offenbar mit dem Frequenzbereich nicht klar kommt. Das geht zwar, aber weil da hässliche Gleichzeitigkeiten auftreten können muss man deutlich Hirnschmalz in eine Korrektur investieren, um nicht ab und zu Fehlmessungen zu bekommen. Weniger anstrengend für die grauen Zellen: den Prescaler adaptiv einstellen. Wenn mehr als ein Overflow reinrutscht, dann war der Prescaler zu klein, also vergrössern und nochmal messen. Wenn die gemessene Differenz zu klein ist, dann war der Prescaler zu gross.
Beitrag "AVR Timer mit 32 Bit" Das gleiche Prinzip geht natürlich auch für 16Bit-Timer und ICP. Peter
A. K. schrieb: > Und genau da liegt bei ihm der Hase im Pfeffer, denn der Code zählt auch > die Overflows mit, weil er sonst offenbar mit dem Frequenzbereich nicht > klar kommt. Das geht zwar, aber weil da hässliche Gleichzeitigkeiten > auftreten können muss man deutlich Hirnschmalz in eine Korrektur > investieren, um nicht ab und zu Fehlmessungen zu bekommen. > > Weniger anstrengend für die grauen Zellen: den Prescaler adaptiv > einstellen. Wenn mehr als ein Overflow reinrutscht, dann war der > Prescaler zu klein, also vergrössern und nochmal messen. Wenn die > gemessene Differenz zu klein ist, dann war der Prescaler zu gross. So ist es. Das mit dem Prescaler wechseln, auf die IDee bin ich bis jetzt noch nicht gekommen. Ich habe den Code jetzt soweit verbessert, ohne ICP, dass ich bei 4000Hz ein Pendeln zwischen 4011-4018 angezeigt bekomme.
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.