welche Compilermodule verwenden F_CPU?

OP #1684441
Lesenswert?

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
OP #1684470
Lesenswert?

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.
#1684497
Lesenswert?

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.
Gast #1684516
Lesenswert?

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
OP #1684518
Lesenswert?

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 :-)
OP #1684577
Lesenswert?

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
#1685049
Lesenswert?

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
Gast #1685141
Lesenswert?

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.
OP #1685192
Lesenswert?

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)
OP #1685198
Lesenswert?

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!
Moderator Persönliche Seite #1685286
Lesenswert?

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.
#1685333
Lesenswert?

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
OP #1685352
Lesenswert?

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.
#1685371
Lesenswert?

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
OP #1685412
Lesenswert?

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).
OP #1685424
Lesenswert?

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...
#1685435
Lesenswert?

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_t halfspeed = 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.
Moderator Persönliche Seite #1685444
Lesenswert?

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?
Gast #1685476
Lesenswert?

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 :)
#1685541
Lesenswert?

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.
Gast #1685586
Lesenswert?

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.
OP #1685587
Lesenswert?

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 ;-).
OP #1685588
Lesenswert?

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 :-)
#1685644
Lesenswert?

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:
1
void delay_ms( uint16_t ms )
2
{
3
  do{
4
    _delay_ms( 0.5 );
5
    if( !(CLKPR & 1<<CLKPS0) )
6
      _delay_ms( 0.5 );
7
  }while( --ms );
8
}
Moderator Persönliche Seite #1685704
Lesenswert?

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.
#1685711
Lesenswert?

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.
1
void delay_ms( uint16_t ms )
2
{
3
    if( !(CLKPR & 1<<CLKPS0)
4
        ms <<= 1;
5
    while (ms--)
6
        _delay_us(500);
7
}
OP #1685808
Lesenswert?

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
> void delay_ms( uint16_t ms )
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.
OP #1685818
Lesenswert?

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...
#1685838
Lesenswert?

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.
Moderator Persönliche Seite #1685859
Lesenswert?

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.
#1685984
Lesenswert?

Noch weniger Extra-Overhead, und ohne großen Auflösungsverlust:
1
static inline void
2
my_delay_ms(double ms)
3
{
4
        _delay_ms(ms);
5
        if (!(CLKPR & 1<<CLKPS0))
6
                _delay_ms(ms);
7
}

Das FP-Berechnung wird hier vom Compiler komplett wegoptimiert, so wie 
es auch bei _delay_ms() alleine der Fall ist.

Andreas

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren