PMIC_CTRL=PMIC_LOLVLEN_bm;/* LOW LEVEL Interrupt on */
65
TCC0.CTRLA=TC_CLKSEL_DIV8_gc;
66
TCC0.INTCTRLA=TC_OVFINTLVL_LO_gc;
67
TCC0.PER=periode_value;
68
TCC0.INTFLAGS=TC0_OVFIF_bm;
69
70
sei();
71
uint16_tstart=TCC0_CNT;
72
_delay_ms(200);
73
uint16_tend=TCC0_CNT;
74
75
doubleellapsed_time=get_elapsed_time(start,end);
76
77
while(1)
78
{
79
//TODO:: Please write your application code
80
}
81
}
Um meine Funktion zu testen, habe ich ein Delay von 200 ms eingebaut.
Leider kommt beim Debuggen immer 0,09213223 sek raus.
Mache ich irgendetwas falsch?!
Danke im Voraus!
L. G.
Steffo
kommt mir jetzt komisch vor.
1/SystemClock ist klar.
Aber warum nochmal durch den Division Factor dividieren?
Wenn der Faktor höher wird, zählt der Timer langsamer. Zählt der Timer
langsamer, dann wird der Zeitbedarf für einen Zähltick des Timers
größer.
Bei dir wird er aber durch die Division kleiner.
Schau dir halt mal die Einzelwerte an, schnapp dir Papier und Bleistift
und kontrollier ob deine Werte plausibel sind. Was nichts bringt: Wenn
du dieselbe Berechnung auf dem Papier noch mal machst. Du musst dir eine
alternative Möglichkeit überlegen, wie du dir selbst anschaulich die
Dinge erklären kannst.
wenn der Timer mit 2Mhz getaktet wird, dann macht er in 1 Sekunde
2 Mio Zählvorgänge.
Hast du einen Vorteiler von 8, dann macht er nicht 2 Mio
Zählvorgänge sondern nur 2Mio / 8 = 250000
Bei 250000 Zählvorgängen, dauert dann einer davon 1/250000
oder eben ausgerechnet 0.000004 Sekunden
Und genau dasselbe muss auch dein Programm für time_per_clock_cycle
rausbringen. Und das kann man kontrollieren!
Was auch hilfreich sein kann:
Die Umkehrung rechnen. Wenn du den Timer startest, wie weit würde der in
200ms kommen? Dann siehst du dir den start und den end Wert an und
vergleichst mal damit.
Was überhaupt nichts bringt: Eine Berechnung über ein paar Stufen
machen, sich die Ausgangswerte nicht ansehen, sich die Zwischenwerte
nicht ansehen und nur rufen: Mein Endergebnis stimmt nicht, Hilfe!
Wenn dein Endergebnis nicht stimmt, dann gibt es 3(!) mögliche
Fehlerquellen:
* das grundsätzliche Verständnis was da physikalisch passiert
ist schon mal falsch. Eng damit gekoppelt ist der nächste
Punkt:
* die daraus hergeleiteten Formeln sind falsch
(wobei es da auch noch die Möglichkeit gibt, dass die Lösungsidee
grundsätzlich schon richtig ist, und man beim Zusammenstellen
der Formeln einen mathematischen Fehler gemacht hat.)
* die Ausgangswerte stimmen schon nicht.
Insbesondere den letzten Punkt sollte man nicht unterschätzen. Kommt
öfter vor als man annehmen sollte.
Und grundsätzlich gilt in der Fehlersuche: Nimm nichts als gegeben an!
Kontrolliere alles! Und sei es noch so banal. Jede einzelne Berechnung
gilt erst mal als fehlerhaft, solange bis nahcgewiesen wurde, dass sie
stimmt. Und zwar sowohl konzeptionell als auch von den Zahlenwerten her
(soll ja auch Rechenfehler durch Overflows geben)
Übrigens.
Ein Overflow Counter von 1 muss noch nicht heißen, dass man das Ergebnis
mit 16 Bit nicht berechnen kann.
Wenn du den Timer bei 65533 startest und bei 2 stoppst, dann sind da 5
Timertakte vergangen. Selbst wenn ein Overflow passiert ist. Die
unsigned Rechnerei end - start macht das dann schon richtig.
Erst dann, wenn der Endstand größer als der Startstand ist UND noch dazu
ein Overflow passiert ist, erst dann bist du über die 65535 drüber, die
der Timer einmal zählt um 'rundum' zu kommen.
Trotzdem bekam ich einen völlig unsinnigen Wert heraus.
Ich habe anschließend den Code weiter aufgebröselt und bekam bei
system_clock einen Wert von 33920 heraus. Klar: 2 Mio. passen auch nicht
in 16 Bit... Die Lösung war also uint32 zu verwenden.
Das Ergebnis ist nun besser, aber immernoch nicht perfekt, denn ich
bekomme immer eine Abweichung von -100 ms und so kommt in meinem Fall
0,1000044 sek bei einem delay von 200 ms raus.
Mein geänderter Code:
PMIC_CTRL=PMIC_LOLVLEN_bm;/* LOW LEVEL Interrupt on */
67
TCC0.CTRLA=TC_CLKSEL_DIV8_gc;
68
TCC0.INTCTRLA=TC_OVFINTLVL_LO_gc;
69
TCC0.PER=periode_value;
70
TCC0.INTFLAGS=TC0_OVFIF_bm;
71
72
sei();
73
uint16_tstart=TCC0_CNT;
74
_delay_ms(300);
75
uint16_tend=TCC0_CNT;
76
77
doubleellapsed_time=get_elapsed_time(start,end);
78
printf("Test, %f",ellapsed_time);
79
while(1)
80
{
81
//TODO:: Please write your application code
82
}
83
}
Ist da noch ein Rechenfehler drin?
L. G.
Steffo
PS: Dein Absatz mit dem Overflow verstehe ich ehrlich gesagt nicht ganz,
aber erst mal hat das korrekte Messen Priorität. :)
Steffo schrieb:> Das Ergebnis ist nun besser, aber immernoch nicht perfekt, denn ich> bekomme immer eine Abweichung von -100 ms und so kommt in meinem Fall> 0,1000044 sek bei einem delay von 200 ms raus.
Das könnte man auch so auffassen, dass du nicht eine Abweichung von
-100ms hast, sondern die Hälfte des erwarteten Messergebnisses. Also ein
Faktor 2.
Was dann die 2Mhz in Bezug zu den per Auslieferungszustand eingestellten
1Mhz des Mega interessant macht. Auch dort gibt es einen Faktor 2.
Karl Heinz Buchegger schrieb:> Was dann die 2Mhz in Bezug zu den per Auslieferungszustand eingestellten> 1Mhz des Mega interessant macht. Auch dort gibt es einen Faktor 2.
Ach, das ist ja ein XMega.
Von dem weiß ich nicht, wie sich der Factory Default verhält.
Aber es wär es mir mal wert, die tatsächlich Zeitdauer und Korrektheit
eines _delay_ms festzustellen.
Du hast Recht! Bei einem delay von 300 ms, erhalte ich 150 ms und bei
400 ms, 200 ms.
Das Problem war, dass ich F_CPU unterhalb vom include der delay.h
definiert habe und delay.h prüft, ob F_CPU definiert wurde und falls das
nicht der Fall ist, wird standardmäßig 1 MHz eingestellt. :-)
Ich habe das Makro jetzt einfach ganz oben definiert und gut ist.
Danke, du hast mir wirklich weitergeholfen!!! :-)
Wie war das eigentlich mit dem overflow gemeint?
>Wenn du den Timer bei 65533 startest und bei 2 stoppst, dann sind da 5
Timertakte vergangen.
Wie kommst du auf die 5 Timertakte?
L. G.
Steffo
Stefano schrieb:> Das Problem war, dass ich F_CPU unterhalb vom include der delay.h> definiert habe
Ach Mist. Das hätte ich eigentlich sehen sollen :-)
> Wie war das eigentlich mit dem overflow gemeint?>>>Wenn du den Timer bei 65533 startest und bei 2 stoppst, dann sind da 5> Timertakte vergangen.>> Wie kommst du auf die 5 Timertakte?
65533, 65534, 65535, 0, 1 , 2
| | | | | | | | | |
+---+ +---+ +--+ +-+ +---+
1 2 3 4 5
2 - 65533 ergibt 5 (16 Bit unsigned gerechnet)
Das heißt aber auch:
Solange du sicher weißt, dass dein Timer nie öfter als 65535 mal tickt,
brauchst du dir um Overflows keine Gedanken machen. Durch die unsigned
Rechnerei kommt immer das richtige raus.
Stefano schrieb:> Du hast Recht! Bei einem delay von 300 ms, erhalte ich 150 ms und bei> 400 ms, 200 ms.> Das Problem war, dass ich F_CPU unterhalb vom include der delay.h> definiert habe und delay.h prüft, ob F_CPU definiert wurde und falls das> nicht der Fall ist, wird standardmäßig 1 MHz eingestellt. :-)> Ich habe das Makro jetzt einfach ganz oben definiert und gut ist.
F_CPU muss in den Projekteinstellungen definiert werden, NICHT im
Quelltext!
Das muss so, damit jede kompilierte Datei dieses Makro zur Verfügung
hat.
Hallo Karl,
> Solange du sicher weißt, dass dein Timer nie öfter als 65535 mal tickt,> brauchst du dir um Overflows keine Gedanken machen. Durch die unsigned> Rechnerei kommt immer das richtige raus.
Ah, jetzt ist mir das klar, aber sicherstellen, kann ich das leider
nicht! :-)
Hier übrigens der komplette funktionierende Code, falls den mal jemand
braucht.
1
/**
2
* CAUTION:
3
* Modify this macro, if the CPU Clock is different than defined here!!!
PMIC_CTRL=PMIC_LOLVLEN_bm;/* LOW LEVEL Interrupt on */
71
TCC0.CTRLA=TC_CLKSEL_DIV64_gc;
72
TCC0.INTCTRLA=TC_OVFINTLVL_LO_gc;
73
TCC0.PER=periode_value;
74
TCC0.INTFLAGS=TC0_OVFIF_bm;
75
76
sei();
77
uint16_tstart=TCC0_CNT;
78
_delay_ms(500);
79
uint16_tend=TCC0_CNT;
80
81
doubleellapsed_time=get_elapsed_time(start,end);
82
printf("Test, %f",ellapsed_time);
83
while(1)
84
{
85
//TODO:: Please write your application code
86
}
87
}
Simon K. schrieb:
> F_CPU muss in den Projekteinstellungen definiert werden, NICHT im> Quelltext!> Das muss so, damit jede kompilierte Datei dieses Makro zur Verfügung> hat.
Aber, wenn ich das Makro ganz oben definiere, tuts doch auch, oder
nicht?
Wenn ich das Projektspezifisch deklariere, sehe ich die Gefahr, dass,
falls der Code woanders verwendet wird, die Projekteinstellungen nicht
übernommen werden. Gleiches Problem hat man, wenn der CPU-Takt geändert
wird. Im Quelltext sieht man sofort, dass das Makro geändert werden
muss, in den Projekteinstellungen nicht.
L. G.
Steffo
Steffo schrieb:> Aber, wenn ich das Makro ganz oben definiere, tuts doch auch, oder> nicht?
Nein, tuts nicht. Dann ist F_CPU in der Datei definiert, wo du es
definiert hast. In allen anderen .c Dateien (Compilation Units) ist es
nicht definiert.
> Wenn ich das Projektspezifisch deklariere, sehe ich die Gefahr, dass,> falls der Code woanders verwendet wird, die Projekteinstellungen nicht> übernommen werden.
Das ist doch auch gut so! Sobald du ein neues Projekt anfängst, solltest
du ganz zu Anfang die ganzen Einstellungen für die Plattform (damit auch
die Taktfrequenz) angeben.
> Gleiches Problem hat man, wenn der CPU-Takt geändert> wird. Im Quelltext sieht man sofort, dass das Makro geändert werden> muss, in den Projekteinstellungen nicht.
Wenn, dann musst du es aber in jeder Compilation Unit definieren. Und
dann dreht sich deine Aussage sofort um (Ergo: Man sieht es eben nicht
sofort).
Was du in deinen Code einbauen kannst ist:
#ifndef F_CPU
#error F_CPU nicht definiert
Du hast einen Zähler vom Typ: uint8_t installiert. Der flippt nach
kurzer Zeit schon aus (255).
Würdest Du dich nicht besser stehen, wenn Du einen "großen" Zähler
installierst (z.B. long) und dann deine Messungen nach folgendem Schema
durchführst:
StartZeit = akt_Zähler();
<<Hau rein Onkel Otto;>>
Dauer = akt_Zähler() - StartZeit;
Ein Zähler, im Format long, bereitet erst nach mehreren Tagen Dauerlauf
Probleme. Nur den atomaren Zugriff auf den Zähler musst Du
sicherstellen.
Besser als +/- 2 Takte geht das ganze sowieso nicht. Wird aber immer der
gleiche Zugriff verwandt, so ist die Ungenauigkeit minimiert.
Steffo schrieb:>> F_CPU muss in den Projekteinstellungen definiert werden, NICHT im>> Quelltext!>> Das muss so, damit jede kompilierte Datei dieses Makro zur Verfügung>> hat.>> Aber, wenn ich das Makro ganz oben definiere, tuts doch auch, oder> nicht?
Jain.
main.c
1
#define F_CPU 2000000UL
2
3
....
lcd.c
1
#define F_CPU 1000000UL
2
....
Jetzt hast du in deinem Projekt 2 C-Dateien, deren F_CPU Einstellung
immer der Realität entsprechen müssen. Je mehr C-Dateien es werden,
desto unangenehmer ist es sicherzustellen, dass die
a) alle den gleichen Wert haben
b) dieser Wert auch stimmt
Hast du den Wert in den Projekteinstellungen, so übernimmt ihn der
Compiler in jede C-Datei die er kompiliert. Es ist als ob JEDE C-Datei
am Anfang genau dieses #define hätte - d.h. du kannst per Definition
nicht vergessen eine F_CPU Angabe zu machen. Nur dass es an einer Stelle
zusammengezogen ist und somit in allen C-Dateien zumindest gleich ist.
Ob der Wert stimmt ist damit noch nicht gesagt. Aber du kannst dich
zumindest darauf verlassen, das wen du den Wert in den
Projekteinstellungen an einer Stelle änderst und alle C-Dateien
kompilierst, das dann alle C-Dateien die gleicher Vorstellung davon
haben, wie hoch F_CPU ist.
> Wenn ich das Projektspezifisch deklariere, sehe ich die Gefahr, dass,> falls der Code woanders verwendet wird, die Projekteinstellungen nicht> übernommen werden. Gleiches Problem hat man, wenn der CPU-Takt geändert> wird. Im Quelltext sieht man sofort, dass das Makro geändert werden> muss, in den Projekteinstellungen nicht.
Der springende Punkt ist, dass man mit ein wenig Erfahrung als ERSTES in
den Projekteinstellungen nachsieht.
Dort sollte eigentlich der allererste Anlaufpunkt sein. Und erst dann,
wenn man dort nichts findet, sieht man in den Einzelfiles nach.
@Karl + Simon: Danke, jetzt ist das klarer! :-)
@amateur: Ich benutze uint8 nur um zu prüfen, ob ein overflow
stattgefunden hat. Genau genommen reicht mir dafür auch ein einziges
Bit.
L. G.
Steffo
Nochmal eine Frage:
Ich habe diesen Code in einem Beispielcode von Atmel eingebaut und
sowohl über den Code als auch über IO-View während des Debuggens, kann
ich TCC0.CTRLA (Clock Selection) nicht ändern. D. h., der Timer läuft
erst gar nicht los und das delay() braucht entsprechend unendlich lange!
Weiß jemand, weshalb ich TCC0.CTRLA nicht manipulieren kann? Wurde das
irgendwie gelockt?!
Danke im Voraus!
L. G.
Steffo