Hallo,
ich habe ein kleines Problem mit F_CPU.
Beim ATMega2560 z.B.kann ich zwar einen 14,7456 MHz Quarz anschließen,
der endgültige CPU-Takt hängt aber vom Taktteiler ab.
Da z.B. der ATMega2560V nur bis 8MHz spezifiziert ist, muss der
Quarz-Takt halbiert werden. Deswegen mache ich in meiner 'cpu.h' eine
Abfrage auf den Controllertyp und setze F_CPU entsprechend. Zur Laufzeit
wird dann in 'cpu.c' der Takteiler entsprechend gesetzt. Deswegen gibt
es bei mir noch ein F_XTAL um den Quarztakt zu spezifizieren.
Das GCC-Modul 'delay.h' verwendet aber F_CPU um die Delayzeiten zu
berechnen. Umschiffen kann ich das, indem man 'cpu.h' vor 'delay.h'
einbindet (und der Compiler meckert dann, dass F_CPU 'redefined' ist,
aber egal).
Welche Module verwenden denn noch F_CPU ?
Daniel
Da F_CPU der CPU-Takt und nicht der Quarztakt ist, warum setzt du das
nicht auf halbe Quarzfrequenz und fertig? Wie diese CPU-Frequenz
zustande kommt, ob halbiert oder nicht, ist ja egal.
Dann habe ich mich nicht klar ausgedrückt:
Wenn ich zur Laufzeit (bzw. beim 'booten') den Takteiler ändere, ändert
sich auch meine CPU-Frequenz. Dann stimmen die Ergebnisse aus z.B.
'delay.h' nicht mehr, weil diese zur Compilezeit errechnet wurden.
Deswegen prüfe ich zur Compilezeit den CPU-Typ und definiere den
Taktteiler und F_CPU neu.
Leider wird aber F_CPU ja auch schon von der Entwicklungsumgebung
(Eclipse+WinAVR) definiert, und diese Definition ist unter Umständen
falsch, wenn ich F_CPU nicht vorher umdefiniere.
Deswegen meine Frage von oben.
A. K. schrieb:> Da F_CPU der CPU-Takt und nicht der Quarztakt ist, warum setzt du das> nicht auf halbe Quarzfrequenz und fertig? Wie diese CPU-Frequenz> zustande kommt, ob halbiert oder nicht, ist ja egal.
Weil ich auf ein und dem gleichen Board einmal ATMEGA2560 und einmal
ATMEGA2560V bestücken kann. Der Quarz bleibt der gleiche.
Das wird so nicht funktionieren. Delay.h ist (im wesentlichen) nichts
anderes als ein Sammlung von Makros welche ein konstantes F_CPU erwarten
welche vom Compiler/Preprozessor ausgewertet werden. Wenn du F_CPU zu
einer Variablen machst stimmen die Delays generell nicht mehr. Abgesehen
davon wird immer die Floating Point Bibliothek benötigt.
Daniel V. schrieb:> Leider wird aber F_CPU ja auch schon von der Entwicklungsumgebung> (Eclipse+WinAVR) definiert, und diese Definition ist unter Umständen> falsch, wenn ich F_CPU nicht vorher umdefiniere.
Eclipse definiert gar nichts, wenn du das nicht möchtest. Es gibt für
alles in den settings eine Einstellung, die man anpassen kann.
Daniel V. schrieb:> Umschiffen kann ich das, indem man 'cpu.h' vor 'delay.h'> einbindet (und der Compiler meckert dann, dass F_CPU 'redefined' ist,> aber egal).
Deine Lösung ist völlig in Ordnung, und wenn du Eclispe richtig
einstellst, meckert auch der Compiler nicht mehr.
F_CPU wird überhaupt nicht vom Compiler versendet, nur von der avrlibc.
Dort nutzt es IMHO ausser delay.h nur noch setbaud.h.
Oliver
Das ist ja auch mein Problem.
Um das ganze variabel zu machen, wäre es ja vorstellbar, dass zur
Laufzeit (beim Aufrufen von delay_ms() z.B.) geschaut wird, welche
Frequenz der Quarz hat (das gibt man z.B. über F_XTAL an) und wie der
Takteiler eingestellt ist. Daraus errechnet sich dann die Wartezeit. Das
wäre doch mal ein kleines Projekt....ausserdem könnte man die CPU
runtertakten und die Delay-Zeiten stimmen immer noch :-)
Oliver schrieb:> Eclipse definiert gar nichts, wenn du das nicht möchtest. Es gibt für> alles in den settings eine Einstellung, die man anpassen kann.> Oliver
Tja, leider kann ich Eclipse nicht dazu bewegen F_CPU nicht zu
definieren.
Daniel V. schrieb:> Tja, leider kann ich Eclipse nicht dazu bewegen F_CPU nicht zu> definieren.
project properties->C/C++-Build->settings
dort unter AVR Compiler/Symbols (welche Überraschung ;-) gibt es eine
Checkbox "Omit F_CPU".
Anklicken, weg ist das F_CPU.
Olver
Oliver schrieb:> Daniel V. schrieb:>> Tja, leider kann ich Eclipse nicht dazu bewegen F_CPU nicht zu>> definieren.>> project properties->C/C++-Build->settings>> dort unter AVR Compiler/Symbols (welche Überraschung ;-) gibt es eine> Checkbox "Omit F_CPU".> Anklicken, weg ist das F_CPU.>> Olver
Das muss man aber auch erstmal finden, ich bin unter
project properties->C/C++-Build->environment
rumgeeiert
Nimm einen Timer für die Delay-Generierung, und bau die Funktionen
so, dass sie sich an die CPU-Frequenz anpassen können. Je nach
Timer kannst du die unterschiedlichen CPU-Frequenzen sogar durch
den Vorteiler ausgleichen.
Oder betreibe einfach gleich beide Setups mit dem halben Takt...
Daniel V. schrieb:> Um das ganze variabel zu machen, wäre es ja vorstellbar, dass zur> Laufzeit (beim Aufrufen von delay_ms() z.B.) geschaut wird, welche> Frequenz der Quarz hat (das gibt man z.B. über F_XTAL an) und wie der> Takteiler eingestellt ist.
Das geht nicht.
Ein Macro muß zur Compilezeit definiert sein und kann auch nur zur
Compilezeit ausgewertet werden.
Zur Laufzeit geht das garnicht.
Du mußt für den ATMega2560 und den ATMega2560V verschiedene
Programmversionen compilieren. Einmal mit F_CPU = 14,7456MHz und einmal
mit 7,3728MHz. Fertig ist die Laube.
Peter
Mir leuchtet noch nicht ein, warum man ein und dasselbe Programm einmal
mit 14,7456MHz und einmal mit 7,3728MHz laufen lassen muß. Entweder
reicht der niedrige Takt, dann aber auch auf beiden CPUs, da es ja das
selbe Programm ist, oder es reicht nicht, dann ist der V sowieso nicht
geeignet.
Jörg Wunsch schrieb:> Nimm einen Timer für die Delay-Generierung, und bau die Funktionen> so, dass sie sich an die CPU-Frequenz anpassen können. Je nach> Timer kannst du die unterschiedlichen CPU-Frequenzen sogar durch> den Vorteiler ausgleichen.>> Oder betreibe einfach gleich beide Setups mit dem halben Takt...
ja, aber dann hätte ich immer noch das Problem, dass der GCC eventuell
das falsche F_CPU verwendet (auch wennes jetzt nur in delay.h und
setup.h vorkommt, aber das könnte sich ja in Zukunft ändern)
Peter Dannegger schrieb:> Das geht nicht.> Ein Macro muß zur Compilezeit definiert sein und kann auch nur zur> Compilezeit ausgewertet werden.> Zur Laufzeit geht das garnicht.>> Peter
Hab ja nicht gesagt, dass ein Macro das machen muss...
Rolf Magnus schrieb:> Mir leuchtet noch nicht ein, warum man ein und dasselbe Programm einmal> mit 14,7456MHz und einmal mit 7,3728MHz laufen lassen muß. Entweder> reicht der niedrige Takt, dann aber auch auf beiden CPUs, da es ja das> selbe Programm ist, oder es reicht nicht, dann ist der V sowieso nicht> geeignet.
Das ist ein berechtigter Einwand. Vielleicht liegt es einfach an mir,
dass ich mich zunächst noch nicht festlegen möchte. Aber ich sollte wohl
mal eine Grundsatzentscheidung treffen, da hast du Recht!
Daniel V. schrieb:> ja, aber dann hätte ich immer noch das Problem, dass der GCC eventuell> das falsche F_CPU verwendet
Wenn du weder <util/delay.h> noch <util/setbaud.h> einbindest, musst
du auch kein F_CPU definieren. Du hast ja schon festgestellt, dass
das sonst niemand braucht. Der Rest der Bibliothek (und der Compiler
erst recht) interessiert sich einen feuchten Kehrricht dafür, mit
welcher Frequenz deine CPU läuft. Am ehesten würdest du diese
Konstante noch in deiner eigenen Applikation benötigen, um das ganze
Timer-Setup zu berechnen, aber dafür verbietet dir ja 1.) niemand,
einen anderen Namen als F_CPU zu benutzen und zweitens kannst du dort
prima mit einem #ifdef arbeiten.
Daniel V. schrieb:> ja, aber dann hätte ich immer noch das Problem, dass der GCC eventuell> das falsche F_CPU verwendet
Dann bist Du aber selbst dran schuld.
Das F_CPU darf man nur an einer einzigen Stelle definieren für alle
Module gleich.
Also entweder im Make auf der GCC-Kommandozeile.
Oder z.B. in einem "hardware.h", welches alle includieren.
Sicherheitshalber sollte man auch vor der Definition testen, ob schon
ein F_CPU existiert und dann knallhart mit einem #error abbrechen.
Peter
Peter Dannegger schrieb:> Daniel V. schrieb:>> ja, aber dann hätte ich immer noch das Problem, dass der GCC eventuell>> das falsche F_CPU verwendet>> Dann bist Du aber selbst dran schuld.
Deswegen wollte ich ja wissen, welche Module F_CPU verwenden. Aber woher
weiß ich denn, dass zukünftige Module das nicht auch verwenden, z.B.
wenn der Ordner utils weitere Headers bekommt. Jaaa, vielleicht denk
ich da zu weit vorraus. Selber schuld.
> Das F_CPU darf man nur an einer einzigen Stelle definieren für alle> Module gleich.
Das ist mir völlig klar.
Ich wollte in diesem Thread eigentlich nur darauf hinweisen, dass die
Definition von F_CPU völlig ausblendet, dass der AVR das Feature eines
Taktteilers hat. Und der kann nunmal zur Laufzeit geändert werden.
Also darf ich z.B. delay_ms() nicht verwenden, wenn ich am Taktteiler
rumspiele.
Daniel V. schrieb:> Ich wollte in diesem Thread eigentlich nur darauf hinweisen, dass die> Definition von F_CPU völlig ausblendet, dass der AVR das Feature eines> Taktteilers hat. Und der kann nunmal zur Laufzeit geändert werden.
Die AVR-GCC Entwickler gehen wohl davon aus, daß man nur einmal in der
Initialisierung den Teiler einstellt und dann kann man den Teiler gleich
in F_CPU hineinrechnen.
Inwieweit es sinnvoll ist, den Teiler mitten im Programm umzustellen,
muß jeder für sich entscheiden.
Daß dann neben dem Delay auch Timer, UART, CAN, ADC, I2C, SPI-Takte nach
dem Mond gehen, dürfte klar sein.
Peter
Peter Dannegger schrieb:> Die AVR-GCC Entwickler gehen wohl davon aus, daß man nur einmal in der> Initialisierung den Teiler einstellt
So wie ich das bisher verstanden hab, macht Daniel V. genau das. Oder?
Daniel V. schrieb:> Wenn ich zur Laufzeit (bzw. beim 'booten') den Takteiler ändere, ändert> sich auch meine CPU-Frequenz.
Peter Dannegger schrieb:> Inwieweit es sinnvoll ist, den Teiler mitten im Programm umzustellen,> muß jeder für sich entscheiden.> Daß dann neben dem Delay auch Timer, UART, CAN, ADC, I2C, SPI-Takte nach> dem Mond gehen, dürfte klar sein.>>> Peter
Ja, das zieht dann einen ganzen Rattenschwanz hinter sich her....und die
Taktberechnungen müssten in den Init-Routinen über eine Variable
erfolgen (nicht über F_CPU).
Daniel V. schrieb:> und die> Taktberechnungen müssten in den Init-Routinen über eine Variable> erfolgen (nicht über F_CPU).
...was zusätzlichen Laufzeitaufwand bedeutet, obwohl du eigentlich
in der Applikation am Ende mit genau einer CPU-Frequenz arbeitest.
Dieter schrieb:> Peter Dannegger schrieb:>> Die AVR-GCC Entwickler gehen wohl davon aus, daß man nur einmal in der>> Initialisierung den Teiler einstellt>> So wie ich das bisher verstanden hab, macht Daniel V. genau das. Oder?>> Daniel V. schrieb:>> Wenn ich zur Laufzeit (bzw. beim 'booten') den Takteiler ändere, ändert>> sich auch meine CPU-Frequenz.
Jepp, beim booten stelle ich alles ein. Mitten im Programm ändere ich
nichts. Aber das wäre ja auch mal interessant...
Dieter schrieb:> Peter Dannegger schrieb:>> Die AVR-GCC Entwickler gehen wohl davon aus, daß man nur einmal in der>> Initialisierung den Teiler einstellt>> So wie ich das bisher verstanden hab, macht Daniel V. genau das. Oder?
Ja, aber in Abhängigkeit von der CPU-Variante, auf der der Code gerade
läuft, d.h. die endgültige Teilereinstellung ist zur Compilezeit nicht
bekannt.
Eine Lösung könnte noch sein, die Delay-Funktionen in ein Makro zu
packen, das den übergebenen Wert bei Bedarf halbiert, um bei halber
Frequenz wieder auf die gleiche Zeit zu kommen:
1
#define F_CPU 14745600
2
#include<util/delay.h>
3
4
uint8_thalfspeed=0;// Wird bei ATmega2560V von init auf 1 gesetzt
5
6
#define my_delay_us(a) if (halfspeed) _delay_us((a)/2); else _delay_us((a))
7
#define my_delay_ms(a) if (halfspeed) _delay_ms((a)/2); else _delay_ms((a))
Das verdoppelt halt den Platzbedarf für jede Verwendung von Delay.
Mir dünkt, du diskutierst hier Stunden über ein Problem, das gar keins
ist: entweder läuft deine Applikation mit 7 MHz, dann kannst du sie
auch auf den nicht-V-AVRs damit laufen lassen, oder sie läuft damit
nicht, dann kannst du die Vs einfach in die Ecke legen.
Hat denn jemand schon vor der Fertigstellung des Prototyps schachtel-
weise beide CPU-Varianten gekauft?
Jörg Wunsch schrieb:> Mir dünkt, du diskutierst hier Stunden über ein Problem, das gar keins> ist: [...]
Klar mag es in diesem konkreten Fall bessere Lösungen geben, aber warum
die Frage nicht zum Anlaß nehmen, sich mal Gedanken zu machen, wie
Delays mit einer laufzeitabhängigen Takfrequenz unter einen Hut gebracht
werden können?
R. Max schrieb:> Klar mag es in diesem konkreten Fall bessere Lösungen geben, aber warum> die Frage nicht zum Anlaß nehmen, sich mal Gedanken zu machen, wie> Delays mit einer laufzeitabhängigen Takfrequenz unter einen Hut gebracht> werden können?
Mit Timern! (!!!!) Denn dazu sind die schließlich da :)
Dieter schrieb:> Mit Timern! (!!!!) Denn dazu sind die schließlich da :)
Kein Grund hier rumzuschreien, zumal Deine Antwort am Problem vorbei
geht. Auch Timer sind von der Taktfrequenz abhängig (wenn man extern
getaktete mal außen vor läßt). Man muß sich also auch bei Timern
zusätzliche Gedanken machen, wenn die Taktfrequenz erst zur Laufzeit
endgültig feststeht.
Erstens habe ich nicht geschriehen, zweitens ist es bei Timern ja wohl
überhaupt kein Problem, zur Laufzeit die den richtigen Wert zur
Verzögerung auszurechnen. Denn ob man das vom Compiler oder zur Laufzeit
machen lässt, ist quasi kein Unterschied. Nen problem hat man nur, wenn
mans zuvor per Hand und Taschenrechner ausgerechnet hat.
Und mein Beitrag war insofern durchaus ernst gemeint, weil es
Hauptproblem beim verwenden der delay-Macros bei Timern nicht auftritt.
Die delay-Macros kann man nämlich nicht mit variablen (=zur Laufzeit
berechneten Werten) verwenden, ohne irgendwelche Krücken drumherum zu
stricken.
Jörg Wunsch schrieb:> Mir dünkt, du diskutierst hier Stunden über ein Problem, das gar keins> ist: entweder läuft deine Applikation mit 7 MHz, dann kannst du sie> auch auf den nicht-V-AVRs damit laufen lassen, oder sie läuft damit> nicht, dann kannst du die Vs einfach in die Ecke legen.>> Hat denn jemand schon vor der Fertigstellung des Prototyps schachtel-> weise beide CPU-Varianten gekauft?
Du musst dich ja nicht an der Diskussion beteiligen :-)
Ich habe mit den nicht V-AVRs angefangen und festgestellt, dass es bei
einem Spannungsverlust am Board (also wenns ausgeschaltet wird)
vorteilhaft ist, wenn man einen V-AVR drin hat - der arbeitet auch noch
bei 2,7V ;-).
R. Max schrieb:> Jörg Wunsch schrieb:>> Mir dünkt, du diskutierst hier Stunden über ein Problem, das gar keins>> ist: [...]>> Klar mag es in diesem konkreten Fall bessere Lösungen geben, aber warum> die Frage nicht zum Anlaß nehmen, sich mal Gedanken zu machen, wie> Delays mit einer laufzeitabhängigen Takfrequenz unter einen Hut gebracht> werden können?
Du sprichst mir aus der Seele :-)
Dieter schrieb:> Denn ob man das vom Compiler oder zur Laufzeit> machen lässt, ist quasi kein Unterschied.
Das ist schon ein gewaltiger Unterschied, ob man ne Konstante zuweist
oder ob man erstmal die Math-Lib ackern läßt (in float, damits richtig
reinhaut).
> Nen problem hat man nur, wenn> mans zuvor per Hand und Taschenrechner ausgerechnet hat.
Wer macht den sowas?
Die Compiler rechnen Konstanten selber aus. Sie sind zu faul, unnötigen
Code zu generieren.
Ich schreib immer nur die Formel hin, Taschenrechner wäre mir zu
umständlich und fehlerträchtig.
Delay abhängig vom Vorteiler 1 oder 2 ist auch nicht aufwendig:
Daniel V. schrieb:> Das ist mit Abstand die Beste Idee, die ich hier gelesen habe!
Geht aber auch nur im Millisekundenbereich, und da sind die delay-
Makros eigentlich schon nur noch mal für'n schnellen Hack da, in
einer fertigen Applikation sollte man es seiner CPU ersparen,
sinnlose Kreise für solch eine Ewigkeit zu drehen.
Für den Mikrosekundenbereich schlägt der Overhead bei dieser Lösung
schon zu stark auf's Ergebnis durch.
Daniel V. schrieb:> Ich habe mit den nicht V-AVRs angefangen und festgestellt, dass es bei> einem Spannungsverlust am Board (also wenns ausgeschaltet wird)> vorteilhaft ist, wenn man einen V-AVR drin hat
Dann musst du doch aber ohnehin in der Lage sein, mit den 7 MHz
auszukommen. Wenn du das andererseits nicht bist, dann war das
mit den V-AVRs auch nicht sinnvoll, dann kannst du entweder normale
Typen mit 14 MHz takten und hoffen, dass sie weit genug runter noch
arbeiten, oder du könntest genauso gut die V-Type mit 14 MHz takten.
Beides wird ziemlich gleichermaßen gut oder schlecht gehen, ist aber
halt vom Hersteller nicht mehr garantiert.
Daniel V. schrieb:> Das ist mit Abstand die Beste Idee, die ich hier gelesen habe!
Ja, recht kompakt, hat aber den Nachteil, daß die Auflösung mit 1ms
deutlich schlechter ist als bei _delay_ms().
Hier noch eine Variante, mit dem gleichen Nachteil, die aber etwas
kleiner sein dürfte, weil sie den Delay-Code nur einmal einbindet und
etwas genauer, weil sie das Clock-Register nur einmal testet, statt in
jedem Schleifendurchlauf. Allerdings halbiert sich die maximal mögliche
Wartezeit.
R. Max schrieb:> Hier noch eine Variante, mit dem gleichen Nachteil, die aber etwas> kleiner sein dürfte, weil sie den Delay-Code nur einmal einbindet und> etwas genauer, weil sie das Clock-Register nur einmal testet, statt in> jedem Schleifendurchlauf. Allerdings halbiert sich die maximal mögliche> Wartezeit.>>
1
>voiddelay_ms(uint16_tms)
2
>{
3
>if(!(CLKPR&1<<CLKPS0)
4
>ms<<=1;
5
>while(ms--)
6
>_delay_us(500);
7
>}
8
>
Ja mein ich doch, die Idee ist interessant und kann ausgebaut werden.
Jörg Wunsch schrieb:> Daniel V. schrieb:>> Das ist mit Abstand die Beste Idee, die ich hier gelesen habe!>> Geht aber auch nur im Millisekundenbereich, und da sind die delay-> Makros eigentlich schon nur noch mal für'n schnellen Hack da, in> einer fertigen Applikation sollte man es seiner CPU ersparen,> sinnlose Kreise für solch eine Ewigkeit zu drehen.>> Für den Mikrosekundenbereich schlägt der Overhead bei dieser Lösung> schon zu stark auf's Ergebnis durch.
Bei den manchen Display-Routinen braucht man halt die Wartezeiten, weil
die Displays so schnarchig sind.
>> Daniel V. schrieb:>> Ich habe mit den nicht V-AVRs angefangen und festgestellt, dass es bei>> einem Spannungsverlust am Board (also wenns ausgeschaltet wird)>> vorteilhaft ist, wenn man einen V-AVR drin hat>> Dann musst du doch aber ohnehin in der Lage sein, mit den 7 MHz> auszukommen. Wenn du das andererseits nicht bist, dann war das> mit den V-AVRs auch nicht sinnvoll, dann kannst du entweder normale> Typen mit 14 MHz takten und hoffen, dass sie weit genug runter noch> arbeiten, oder du könntest genauso gut die V-Type mit 14 MHz takten.> Beides wird ziemlich gleichermaßen gut oder schlecht gehen, ist aber> halt vom Hersteller nicht mehr garantiert.
Ja, da sollte ich mal ne Grundsatzentscheidung treffen und einfach nur
einen Typ verbauen. Stimmt.
btw: Wie zitiere ich denn mehrere verschiedene Beiträge in einem Post?
Einfach kopieren scheint nicht zu funktionieren...
Daniel V. schrieb:> btw: Wie zitiere ich denn mehrere verschiedene Beiträge in einem Post?> Einfach kopieren scheint nicht zu funktionieren...
Du kannst mehrmals nacheinander einen Beitrag oder einen Ausschnitt
daraus markieren und dann auf "Markierten Text zitieren" klicken. Es
wird dann im Eingabefeld jeweils ein neuer Zitatblock angehängt.
Daniel V. schrieb:> Bei den manchen Display-Routinen braucht man halt die Wartezeiten, weil> die Displays so schnarchig sind.
Bei Millisekunden lohnt es sich aber eigentlich immer, einen
Timer anzuwerfen und die CPU schlafen zu legen (*). Ich schrieb ja
schon weiter oben, wenn du das mit dem Timer geschickt machst,
brauchst du nur je nach CPU-Frequenz zwei verschiedene Vorteiler
zu wählen (die sich um 2:1 unterscheiden), während die eigentliche
Berechnung dann komplett unabhängig von der tatsächlichen CPU-Frequenz
klappt. Beim ATmega2560 geht das auf diese Weise für den Timer 2,
dessen Vorteiler komplett 2:1 gestaffelt sind.
(*) Oder aber du legst die komplette Ausgabe in den Hintergrund,
wenn das Display so schnarchlangsam ist, ähnlich wie man das für
eine UART-Ausgabe ja auch machen würde.