_delay_ms() läuft 4 Mal schneller als erwartet

Gast #1927671
Lesenswert?

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:
1
#include <avr/io.h>
2
#include <util/delay.h>
3

4
int main( void )
5
{
6
    DDRB = ( 1 << PB5 );
7
 
8
    while( 1 ) {
9
        PORTB ^= ( 1 << PB5);
10
        _delay_ms(4000.0);
11
    }
12
    return 0;
13
}

F_CPU ist auf 8000000UL gesetzt:
1
avr-gcc  -mmcu=atmega328p -Wall -gdwarf-2 -std=gnu99 -DF_CPU=8000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT LED.o -MF dep/LED.o.d  -c  ../LED.c

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(int i=0; i<4000; i++) {
2
        _delay_ms(1.0);
3
    }
ersetze ändert sich nichts am zeitlichen Verhalten.

Was mache ich falsch?
Gast #1927902
Lesenswert?

Hi

If you are sure the AVR is running woth the correct clockfrequency.

There is a bug in the delay.h library in avr-libc-1.7.0

Are you using WinAVR2010 (it uses avr-libc-1.7.0)?
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=726144#726144



Maybe try this library , for a test.
http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_id=665&item_type=project

mfg
Bingo
Gast #1928072
Lesenswert?

Hello Bingo,

i used the actual AVR toolchain from the atmel website. This toolchain 
seems to contain the _delay_ms() bug.
1
#define  __HAS_DELAY_CYCLES 0
and also
1
#include "delay_x.h"
resovled the problem.

Thanks for the clue.

Thomas
Moderator Persönliche Seite #1943039
Lesenswert?

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.
Angehängte Dateien:
Gast #2189810
Lesenswert?

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

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

Peter Dannegger schrieb:
> 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?

Bin zwar nicht Jürgen, hab aber den Link :)

http://www.atmel.com/dyn/resources/prod_documents/avr-toolchain-installer-3.0.0.240-win32.win32.x86.exe

Auch über die Webseiten von Atmel zu finden auf der Übersichtsseite zum 
AS4 (dort aber mit Registgrierung):
1
AVR Toolchain Installer
2
(87 MB, updated 9/10)
3
For use with AVR Studio 4.18 SP3
http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725&category_id=163&family_id=607&subfamily_id=760
Persönliche Seite #2190915
Lesenswert?

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

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

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-Functions
http://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
   unsigned long i = 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.
Moderator Persönliche Seite #2191176
Lesenswert?

Johann L. schrieb:

> Den Patch hab ich nicht 1:1 übernommen.
>
> 
http://gcc.gnu.org/onlinedocs/gcc/AVR-Built_002din-Functions.html#AVR-Built_002din-Functions
> http://gcc.gnu.org/viewcvs?view=revision&revision=172416

OK.  Vielleicht baue ich das nochmal um, wenn deine Version dann
eine gewisse Verbreitung erreicht hat.  Oder die Heuristik via
./configure, aber ein Überschreiben, falls _BUILTIN... dann zur
Compilezeit der Applikation existiert.

> Ich erinnere mich an Probleme bei Code wie
>
1
{
2
>    unsigned long i = 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.

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. :-/
Gast #2191177
Lesenswert?

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)
1
#if __HAS_DELAY_CYCLES && defined(__OPTIMIZE__)
2
  extern void __builtin_avr_delay_cycles(unsigned long);
3
  __builtin_avr_delay_cycles(__tmp);
4
#else
Gast #2195944
Lesenswert?

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
int main( void )
13
{
14
    DDRB = ( 1 << PB0 );        // PB0 an PORTB als Ausgang setzen
15
 
16
    while( 1 ) {                // Endlosschleife
17
        PORTB ^= ( 1 << PB0 );  // Toggle PB0 z.&nbsp;B. angeschlossene LED
18
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...
19
                                // funktioniert nicht mit Bibliotheken vor 1.6
20
 
21
    }
22
    return 0;
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
Gast #2196022
Lesenswert?

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

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

Hallo,

wenn ich das richtig interpetiere ist das Version 2.0
Damit sollte alles okay sein. Danke.
1
#ifndef _AVR_VERSION_H_
2
#define _AVR_VERSION_H_
3

4
/** \ingroup avr_version
5
    String literal representation of the current library version. */
6
#define __AVR_LIBC_VERSION_STRING__ "2.0.0"
7

8
/** \ingroup avr_version
9
    Numerical representation of the current library version.
10
 */
11
#define __AVR_LIBC_VERSION__        20000UL
12

13
/** \ingroup avr_version
14
    String literal representation of the release date. */
15
#define __AVR_LIBC_DATE_STRING__    "20150208"
16

17
/** \ingroup avr_version
18
    Numerical representation of the release date. */
19
#define __AVR_LIBC_DATE_            20150208UL
20

21
/** \ingroup avr_version
22
    Library major version number. */
23
#define __AVR_LIBC_MAJOR__          2
24

25
/** \ingroup avr_version
26
    Library minor version number. */
27
#define __AVR_LIBC_MINOR__          0
28

29
/** \ingroup avr_version
30
    Library revision number. */
31
#define __AVR_LIBC_REVISION__       0
32

33
#endif /* _AVR_VERSION_H_ */
#6161432
Lesenswert?

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

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

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

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

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

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

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

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.

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