Hallo zusammen,
ich beginne gerade mich mit AVR uz beschäftigen. In meinem ersten
Programm wollte ich einen LED an PB5 im Viersekundentakt blinken lassen,
d.h. 4 Sekunde an und 4 Sekunde aus. Das Programm sieht so aus:
Unerwarteterweise ist die LED jedoch nur ca 1 Sekunde an und aus.
Ich benutze einen ATMega 328p. Als Takt ist der interne Oszillator mit 8
MHz konfiguriert. Folgende Fuses sind programmiert: SUT0, CKSEL3,
CKSEL2, CKSEL0. Avrdude sagt beim Auslesen der Fuses folgendes:
lfuse=0xe2, hfuse=0xd9, efuse=0x04.
Wenn ich
1
_delay_ms(4000.0);
durch
1
for(inti=0;i<4000;i++){
2
_delay_ms(1.0);
3
}
ersetze ändert sich nichts am zeitlichen Verhalten.
Was mache ich falsch?
Thomas Boeck schrieb:> lfuse=0xe2
Wenn ich nichts übersehen habe, sollte er damit mit ~8 MHz
(interner Osz.) laufen.
Sicher, daß der Wert von lfuse stimmt?
Es riecht ja irgendwie nach programmiertem CKDIV8...
So etwas macht man immer mit einem Timer!
Die util/delay.h Funktionen sind als kurze Warteschleifen für
Entprellung, LCds, ... gedacht und kein bisschen genau!!!
Mit freundlichen Grüßen,
Valentin Buck
Bingo schrieb:> Jörg has released a fixed delay.h , you can use it until avr-libc-1.7.1> is released.
Der Einfachheit halber hier nochmal als Anhang (ich glaube, die
Anhänge bei avrfreaks.net bekommt man nur zu sehen, wenn man sich
dort anmeldet).
Sorry für die vergurkte Version von delay.h in avr-libc 1.7.0, die
geht auf meine Kappe.
Atmel lässt die Leute da ja wirklich eiskalt auflaufen. Da gibts nämlich
sogar heute noch die kaputte Version zum Download. Wenn ich jetzt nicht
zufällig auf diese Seite hier getoßen wäre, hätte ich langsam angefangen
an mir zu zweifeln. Gibts da sonst noch irgendwelche Bugs, die Atmel
verschweigt?
Naja, die kaputte Version des avr-libc-Quellcodes bekommst du ja auch
nach wie vor zum Download angeboten. Release ist release, einen Bug
kann man in einem existierenden Release nicht mehr nachträglich
reparieren.
Im Rahmen von AVR Studio 5 solltest du bei Atmel eine aktuelle Version
bekommen können.
Jürgen Hinze schrieb:> Atmel lässt die Leute da ja wirklich eiskalt auflaufen. Da gibts nämlich> sogar heute noch die kaputte Version zum Download.
Sicher?
Kannst Du mal den Link posten?
Ein WINAVR mit avr-libc 1.7.0 existiert meines Wissens nicht.
Der letzte WINAVR - 20100110 benutzt die avr-libc 1.6.7 und die enthält
den Bug noch nicht.
Der Bug ist also nur für Linux-er relevant, die sich ihren AVR-GCC
selber compilieren können.
Peter
... schrieb:> Bin zwar nicht Jürgen, hab aber den Link :)>> http://www.atmel.com/dyn/resources/prod_documents/...
Stimmt, die ist neuer als der letzte WINAVR.
Gibt es ne Möglichkeit, sich die libc-Version anzeigen zu lassen, die
der GCC verwendet?
Peter
Peter Dannegger schrieb:> Gibt es ne Möglichkeit, sich die libc-Version anzeigen zu lassen, die> der GCC verwendet?
Bei WinAVR gibt es ein define:
_AVR_LIBC_VERSION_STRING_
Edit:
in avr/version.h
@Jörg
* Wer defniert __HAS_DELAY_CYCLES ? Ist das built-in? Um zu erkennen,
ob eine Version ein bestimmtes Builtin mitbringt, mach ich ein
entsprechendes uppercase Builtin-Define. Jedenfalls schien mir das
als Name naheliegend; für __builtin_avr_swap also __BUILTIN_AVR_SWAP.
* Funktioniert das __builtin_avr_delay_cycles auch mit -O0 zusammen?
Das Builtin selbst funktioniert zwar, aber es verlangt eine Konstante.
O0 faltet zwar Konstanten, aber wenn ich keine Konstante sehe, gebe
ich in 4.7 einen Fehler aus.
Johann L. schrieb:> @Jörg>> * Wer defniert __HAS_DELAY_CYCLES ?
Der configure-Script der avr-libc. Das hat natürlich zur Folge,
dass eine compilierte avr-libc nicht mehr portabel auf einen anderen
Compiler ist als den, mit dem sie compiliert worden ist, aber die
Einschränkung hielt ich für vertretbar, und was besseres hatte ich
nicht gefunden.
> ob eine Version ein bestimmtes Builtin mitbringt, mach ich ein> entsprechendes uppercase Builtin-Define.
Hatte ich bei delay_cycles nicht gefunden. Gut, der Patch ist
natürlich auch noch nicht drin, Anatolij mag den irgendwie nicht.
> * Funktioniert das __builtin_avr_delay_cycles auch mit -O0 zusammen?
Dachte ich zumindest.
> Das Builtin selbst funktioniert zwar, aber es verlangt eine Konstante.> O0 faltet zwar Konstanten, aber wenn ich keine Konstante sehe, gebe> ich in 4.7 einen Fehler aus.
Ja, einen Fehler für nicht-Konstanten fände ich sowieso gut. Die
ganzen delay-Dinger sind ja per definitionem nur für Konstanten
konzipiert, aber immer wieder kommt mal jemand daher, der das (meist
aus Unwissenheit, wer liest schon Doku?) ignoriert.
Jörg Wunsch schrieb:> Johann L. schrieb:>> @Jörg>>>> * Wer defniert __HAS_DELAY_CYCLES ?>> Der configure-Script der avr-libc. Das hat natürlich zur Folge,> dass eine compilierte avr-libc nicht mehr portabel auf einen anderen> Compiler ist als den, mit dem sie compiliert worden ist, aber die> Einschränkung hielt ich für vertretbar, und was besseres hatte ich> nicht gefunden.>>> ob eine Version ein bestimmtes Builtin mitbringt, mach ich ein>> entsprechendes uppercase Builtin-Define.>> Hatte ich bei delay_cycles nicht gefunden. Gut, der Patch ist> natürlich auch noch nicht drin, Anatolij mag den irgendwie nicht.
Den Patch hab ich nicht 1:1 übernommen.
http://gcc.gnu.org/onlinedocs/gcc/AVR-Built_002din-Functions.html#AVR-Built_002din-Functionshttp://gcc.gnu.org/viewcvs?view=revision&revision=172416
Etwas seltsam finde ich manche Builtins schon; die gingen genauso in
inline asm. Und Arithmetik wie FMUL wäre vor allem für Maschinen ohne
Multiplikation sinnvoll (per libgcc), wo es ein FMUL gibt, ist das
Builtin trivial...
>> * Funktioniert das __builtin_avr_delay_cycles auch mit -O0 zusammen?>> Dachte ich zumindest.>>> Das Builtin selbst funktioniert zwar, aber es verlangt eine Konstante.>> O0 faltet zwar Konstanten, aber wenn ich keine Konstante sehe, gebe>> ich in 4.7 einen Fehler aus.>> Ja, einen Fehler für nicht-Konstanten fände ich sowieso gut. Die> ganzen delay-Dinger sind ja per definitionem nur für Konstanten> konzipiert, aber immer wieder kommt mal jemand daher, der das (meist> aus Unwissenheit, wer liest schon Doku?) ignoriert.
Ich erinnere mich an Probleme bei Code wie
1
{
2
unsignedlongi=10;
3
__builtin_avr_delay_cycles(i);// oder i+4
4
}
oder ähnlichen einfachen Fällen, wo der Wert des Arguments zwar
prinzipiell zur Compilezeit bekannt ist, aber das Builtin aufgrund von
O0 keine Konstante sieht.
Das wäre ziemlich blöd, weil man den Code dann nicht einfach mal mit O0
testen könnte. Andererseits kann ich auch nicht in eine Funktion, die
eine Konstante erwartet, ein Register geben. Einzige Lösung wäre eine
Bibliotheksfunktion mit variablem fuzzy delay nebst einer gcc-Warnung,
daß diese benutzt wird.
>> oder ähnlichen einfachen Fällen, wo der Wert des Arguments zwar> prinzipiell zur Compilezeit bekannt ist, aber das Builtin aufgrund von> O0 keine Konstante sieht.
Ja, der ist aber eben auch nicht wirklich konstant, da beispiels-
weise der Nutzer vor dem Debugger auf die Schnapsidee kommen könnte,
i einen anderen Wert zuzuweisen.
Wie sieht es aus, wenn `i' zusätzlich noch als `const' gekennzeichnet
ist? Unter C++ wäre es ja dann eindeutig eine Konstante, aber unter
C vermutlich nach wie vor nicht, oder? Naja, wer in C Konstanten
braucht, muss halt nach wie vor #define benutzen, auch im 3. Jahr-
tausend. :-/
Jörg Wunsch schrieb:>> * Funktioniert das __builtin_avr_delay_cycles auch mit -O0 zusammen?>> Dachte ich zumindest.
Wird das __builtin_avr_delay_cycles bei -O0 überhaupt benutzt?
Aus delay.h (1.7.0)
hey leute bin schon ne ganze weile auf dieser webside am surfen, danke
für alle beiträge hat mir sehr geholfen!!!
nun habe ich jedoch ein problem wo ich net mehr weiter weiß:
1
#include<avr/io.h>
2
#ifndef F_CPU
3
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert
4
(z. B. durch Übergabe als Parameter zum Compiler innerhalb
5
des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die
6
"nachträgliche" Definition hinweist */
7
#warning "F_CPU war noch nicht definiert, wird nun mit 3686400 definiert"
8
#define F_CPU 3686400UL /* Quarz mit 3.6864 Mhz */
9
#endif
10
#include<util/delay.h>
11
12
intmain(void)
13
{
14
DDRB=(1<<PB0);// PB0 an PORTB als Ausgang setzen
15
16
while(1){// Endlosschleife
17
PORTB^=(1<<PB0);// Toggle PB0 z. B. angeschlossene LED
18
_delay_ms(1000);// Eine Sekunde +/-1/10000 Sekunde warten...
19
// funktioniert nicht mit Bibliotheken vor 1.6
20
21
}
22
return0;
23
}
bei dem beispiel aus dem GCC TUT soll die led im 1-sekunden takt blinken
das ist bei mir etwa 4-mal langsamer statt 4-mal schneller liegt es an
der taktfrequenz oder ist das auch so ein "bug" oder hab ich schon die
aktuelle version von WinAVR wo _delay_ms 4-mal langsamer gemacht worden
ist obwohl ich des net brauch?
wo bekomme ich die alte version her?
ich benutze einen Attiny2313
plz help ;)
MfG Titan
Mit welcher Taktfrequenz läuft dein Attiny2313 - noch Werkseinstellung
oder hast du was geändert, wenn ja was?
Kam beim Kompilieren die Meldung "F_CPU war noch nicht definiert, wird
nun mit 3686400 definiert"? Werkseinstellung 1 MHz und im Programm 3.7
MHz ist fast der Faktor 4 zu schnell...
...ich flipp aus - 7 Jahre später klärt sich nun auch, weshalb die (sehr
selten) von mir verwendete _delay_ms(10000) nie funktioniert hat. Was
ein Scheiß - aber gut, dass man weiß, wo man fast IMMER eine Antwort
findet :)
Klaus.
Mario schrieb:> ist das darin gefixt?
Der „Fix“ dafür sitzt vor dem Computer, indem er die Fuses für den
Takt so einstellt, dass der resultierende Takt mit dem Wert, der bei
F_CPU angegeben wird, übereinstimmt.
Da ist aber ein großer Überlegungsfehler drinnen:
Wenn die LED alle 4 Sekunden EIN und AUS schaltet, dann kann der Takt
nicht 1MHz sein.
Es sind 8MHz eingestellt, somit muss die Funktion 8 Millionen Taktzyklen
abwarten. Wenn die Funktion richtig funktioniert, müsste bei 1MHz dann
die LED bei _delay_ms(4000.0) dann 32 Sekunden EIN und 32 Sekunden AUS
sein.
Und es hat nicht Atmel die Schuld daran, sondern das ist ein avr-gcc
problem, da die Funktion im libc enthalten ist.
Übrigens ist die selbe Header Datei auch im Arduino enthalten, die
verwenden aktuell 7.3.0 und Atmel Studio verwendet 5.4.0 udn Winavr
4.3.3
Außerdem, wer kam von den avr-gcc auf die glorreiche Idee, diese
funktion mit einem double auszustatten?
Jetzt müssen sämtliche Flieskommaoperationen eingebunden werden, um eine
Flieskommaoperation auszuführen um dann in eine 32-bit integer
umzurechnen für die Schleife?
Eine Fließkomma-Multiplikation benötigt schon etliche Rechenzeit bei
unter 20MHz Rechentakt.
1
void _delay_us(double __us)
2
{
3
double __tmp ;
4
...
5
uint8_t __ticks;
6
double __tmp2 ;
7
__tmp = ((F_CPU) / 3e6) * __us;
8
__tmp2 = ((F_CPU) / 4e6) * __us;
9
if (__tmp < 1.0)
10
__ticks = 1;
11
else if (__tmp2 > 65535)
12
{
13
_delay_ms(__us / 1000.0);
14
}
15
else if (__tmp > 255)
16
{
17
uint16_t __ticks=(uint16_t)__tmp2;
18
_delay_loop_2(__ticks);
19
return;
20
}
21
else
22
__ticks = (uint8_t)__tmp;
23
_delay_loop_1(__ticks);
Und wie bitte soll hier ein AVR mit 1 MHz Takt nun 1µs warten können?
Wenn er nur 20 Taktzyklen für eine Multiplikation benötigt, dann sind es
schon 40 Taktzyklen für beide Multiplikationen, dann kommt man noch im
schlimmsten Fall zur Prüfung ob der wert kleiner als 1.0 ist, woraufhin
eine fließkomma-Division stattfindet, welche mindestens noch 80-100
Taktzyklen benötigt.
Somit sind wir schätzungsweise bei 140 Taktzyklen, welche hier alleine
für die Berechnungen benötigt wurden.
Bei 1MHz Takt sind das nun 140µs und bei 8MHz sind das immer noch 17µs.
Warum macht man die lib nicht wie alle anderen?
UNIX:
1
int usleep(useconds_t usec);
2
typedef unsigned long __useconds_t; /* microseconds (unsigned) */
3
typedef __useconds_t useconds_t;
4
5
struct timespec {
6
time_t tv_sec; /* seconds */
7
long tv_nsec; /* nanoseconds */
8
};
9
int nanosleep(const struct timespec *req, struct timespec *rem);
Somit 32 oder 64-bit integer bei UNIX
Windows:
1
void Sleep(DWORD dwMilliseconds);
Somit haben die schnelleren Rechner (welche zusätzlich einen Fließkomma
Koprozessor hätten) eine weit effizientere Funktion als ein kleiner
Microcontroller mit weniger Geschwindigkeit und Speicher.
Gottfried S. schrieb:> Außerdem, wer kam von den avr-gcc auf die glorreiche Idee, diese> funktion mit einem double auszustatten?
10 Jahre Zeit und immer noch nicht die Doku in der Funktion gelesen?
Johannes S. schrieb:> 10 Jahre Zeit und immer noch nicht die Doku in der Funktion gelesen?
Die hat sich in den letzten 10 Jahren aber auch geändert. ;-)
Das war vor 10 Jahren hier aber schon die Frage der Woche warum delay
mit double rechnet (angeblich) und da gabs hier schon mit der Keule.
Und heute riechen die Compiler schon was du willst bevor du es
eingetippt hast.
War das vor 10 Jahren nicht mal so, daß immer Festkomma verwendet wurde
und bei zu hohem delay einfach ein Überlauf auftrat? Oder war das schon
20 Jahre her? ;-)
Im übrigen, wer delays ohne nachzudenken verwendet hat es nicht anders
verdient
Vor 10 Jahren wurde auf __builtin_avr_delay_cycles() umgestellt.
double haben die "convenience functions" allerdings schon von Anfang an
verwendet, weil ich halt auch sowas wie "_delay_ms(0.23)" erlauben
wollte. Ohne Optimierung hat der ganze Salat schließlich eh keinen
Zweck, und mit Optimierung sind die doubles im Compilat immer weg.
Jörg W. schrieb:> und mit Optimierung sind die doubles im Compilat immer weg.
Solange man eine Compilezeit-Konstante übergibt. Ich kann mich noch an
die Postings erinnern, in denen gefragt wurde, warum delay_ms() länger
braucht als es sollte und warum es so viel Flash braucht.
Rolf M. schrieb:>> und mit Optimierung sind die doubles im Compilat immer weg.>> Solange man eine Compilezeit-Konstante übergibt.
Ja, aber das gehört zu den Nutzungsbedingungen dieser Funktionen. ;-)
Jörg W. schrieb:> Vor 10 Jahren wurde auf __builtin_avr_delay_cycles() umgestellt.>> double haben die "convenience functions" allerdings schon von Anfang an> verwendet, weil ich halt auch sowas wie "_delay_ms(0.23)" erlauben> wollte. Ohne Optimierung hat der ganze Salat schließlich eh keinen> Zweck, und mit Optimierung sind die doubles im Compilat immer weg.
Aber es ist ein Designfehler, wenn das auch bei delay_us verwendet wird.
_delay_ms(0.23) kann man ja auch mit _delay_us(230) schreiben, wo hier
die Taktzyklen gezählt werden.
Der 2. Nachteil dieser großen Inline Funktion ist, wenn sie öfters, oder
mit verschiedenen Werten benötigt wird, werden so ca. 12 words (24
Bytes) Programmspeicher benötigt.
Meine Funktion benötigt 8 words (16 Bytes) Programmspeicher für
delay_us(uint16_t us) und nur 3-4 words je Aufruf.
Hab jetzt auch versucht die Funktion mit einer double-Variable
aufzurufen, da lässt sich das ganze gar nicht mehr kompilieren mit dem
Fehler:
error: __builtin_avr_delay_cycles expects a compile time integer
constant
Allerdings ist es auch ein Design-Fehler, wenn man im Programm mehrere
Millisekunden warten muss und dabei keine anderen Dinge macht. Aber das
kann man nur selber ausprogammieren, indem man einen Timer programmiert,
der auslöst, wenn die Wartezeit abgelaufen ist. Aber dafür kann man halt
kein generelles Lib machen, da es mehrere Timer und unterschiedliche
Ansätze dafür gibt.
_delay_ms() kann man so lassen, da dies ohnehin nur für Dubug-Zweke
verwendet wird, oder beim Start eines Projektes hilfreich ist.
Aber für delay_us() sollte meiner Meinung nach eine zusätzliche schlanke
usleep() eingebaut werden, da man dies öfters bei externen Hardware
benötigt, wie Displays oder spezielle Bauteile.
Da bin ich jedenfalls drüber gestolpert und hab mal meine eigene
Funktion gemacht. Wenn die im Lib vorhanden wäre, wäre es bestimmt von
Vorteil, da man die nicht immer wieder neu schreiben möchte.
Gottfried S. schrieb:> Der 2. Nachteil dieser großen Inline Funktion ist, wenn sie öfters, oder> mit verschiedenen Werten benötigt wird, werden so ca. 12 words (24> Bytes) Programmspeicher benötigt.> Meine Funktion benötigt 8 words (16 Bytes) Programmspeicher für> delay_us(uint16_t us) und nur 3-4 words je Aufruf.
Wie genau ist die Zeit dann, die du damit schlafen kannst? delay_us kann
das auf einen Taktzyklus genau, sofern die Wartefunktion nicht durch
einen Interrupt unterbrochen wird.
Rolf M. schrieb:> Gottfried S. schrieb:>> Der 2. Nachteil dieser großen Inline Funktion ist, wenn sie öfters, oder>> mit verschiedenen Werten benötigt wird, werden so ca. 12 words (24>> Bytes) Programmspeicher benötigt.>> Meine Funktion benötigt 8 words (16 Bytes) Programmspeicher für>> delay_us(uint16_t us) und nur 3-4 words je Aufruf.>> Wie genau ist die Zeit dann, die du damit schlafen kannst? delay_us kann> das auf einen Taktzyklus genau, sofern die Wartefunktion nicht durch> einen Interrupt unterbrochen wird.
Meine ist so ca. 50% bis 80% daneben, bei 10µs ist sie so bei 20µs oder
5µs.
So genau brauch ich das nicht, dafür braucht es sehr wenig vom
Programmspeicher.
Bei den Displays sind ohnehin auch Toleranzen, und wenn sie angeben
typisch 5µs, dann nehm ich eben 10µs bis 15µs, dann läuft es auch bei
Displays bei +5°C auch noch korrekt.
Gottfried S. schrieb:> Aber es ist ein Designfehler, wenn das auch bei delay_us verwendet wird.
Nein: es ist eine Designentscheidung gewesen.
> _delay_ms(0.23) kann man ja auch mit _delay_us(230) schreiben, wo hier> die Taktzyklen gezählt werden.
Natürlich.
Du kannst dir genauso gut auch alles mit der Hand in Inline-Assembler
codieren, oder halt mittlerweile __builtin_avr_delay_cycles() mit der
Hand aufrufen.
_delay_us und _delay_ms sind "convenience functions" (oder wrappers),
und da hatte es für mich Primat, dass sie auch "convenient" zu benutzen
sind. Die Inconvenience liegt dann auf Seiten der Bibliotheks- und
Compilerbauer, die sich um die Details kümmern müssen, aber für den
Anwender sollen sie so einfach wie möglich sein. Dazu gehörte in meinem
Verständnis übrigens auch, dass man einen CPU-Takt von 3,6864 MHz eben
als
1
#define F_CPU 3.6864E6
schreiben darf und nicht nachzählen muss, wie viele Nullen man da hinten
dranhängen muss, weil es ganzzahlig in Hertz ausgedrückt werden soll.
(Leider haben die Autoren von <util/setbaud.h> mit diesem Paradgima
gebrochen.)
Diese Funktionen haben eine Vorgeschichte in der Frühzeit der AVRs und
der avr-libc, die gab es noch nicht immer. Die Backend-Routinen für die
Delays gab es schon länger, und es zeigte sich, dass die Leute oft genug
Fehler machten bei deren Benutzung, die einfach zu vermeiden sind, wenn
man ihnen so ein nutzerfreundliches Interface anbietet.
Hat sich ja letztlich auch bewährt: keiner macht sich mehr die Mühe, was
anderes groß zu nutzen (wenn, dann gleich mit einem Timer).
> Der 2. Nachteil dieser großen Inline Funktion ist, wenn sie öfters, oder> mit verschiedenen Werten benötigt wird, werden so ca. 12 words (24> Bytes) Programmspeicher benötigt.
Ja, dafür sind sie inline; sie sollen eben so exakt wie möglich sein.
Bei einem Funktionsaufruf hat man immer zusätzlichen Overhead.
Keiner verbietet dir, echte Delay-Funktionen irgendwo in deinem Programm
unterzubringen, wenn es nicht auf den einzelnen Takt ankommt und die
Einsparung von Programmcode bei häufigen Aufrufen ein Thema ist.
Gut, das leuchtet mir jetzt ein.
Also wenn man eine ziemlich genaue Funktion braucht, wo der Speicher
keine Rolle spielt, dann diese Funktion verwenden.
Oder wenn man erste Schritte mit AVR macht, oder schnell mal ein
Programm erstellt.
Danke für die detailierte Antwort.
Gottfried S. schrieb:> error: __builtin_avr_delay_cycles expects a compile time integer> constant
Ja, bei dieser Funktion ging das noch nie anders. _delay_ms und
_delay_us wrappen ja um diese Funktion herum (sofern sie im Compiler
verfügbar ist).
Bezüglich des Overheads: weiß nicht, meine eigene HD44780-Library hat
insgesamt gerade mal 6 Aufrufe von delay-Funktionen. Die paar Byte
Overhead machen das Kraut im Vergleich zu den restlichen Teilen der
jeweiligen Applikation kaum fett.
Gottfried S. schrieb:> Oder wenn man erste Schritte mit AVR macht, oder schnell mal ein> Programm erstellt.
Yep: genau dafür werden sie wohl am häufigsten verwendet.