Forum: Compiler & IDEs _delay_ms() läuft 4 Mal schneller als erwartet


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Thomas Boeck (Gast)


Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>
#include <util/delay.h>

int main( void )
{
    DDRB = ( 1 << PB5 );
 
    while( 1 ) {
        PORTB ^= ( 1 << PB5);
        _delay_ms(4000.0);
    }
    return 0;
}

F_CPU ist auf 8000000UL gesetzt:
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
    _delay_ms(4000.0);
durch
    for(int i=0; i<4000; i++) {
        _delay_ms(1.0);
    }
ersetze ändert sich nichts am zeitlichen Verhalten.

Was mache ich falsch?

von Klaus W. (mfgkw)


Bewertung
0 lesenswert
nicht lesenswert
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...

von Valentin B. (nitnelav) Benutzerseite


Bewertung
-6 lesenswert
nicht lesenswert
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

von Klaus W. (mfgkw)


Bewertung
6 lesenswert
nicht lesenswert
Aber wenn _delay nicht stimmt, stimmt der Timer genauso wenig.

von Bingo (Gast)


Bewertung
0 lesenswert
nicht 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

von Thomas Boeck (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hello Bingo,

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

Thanks for the clue.

Thomas

von Bingo (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Jörg has released a fixed delay.h , you can use it until avr-libc-1.7.1 
is released.

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=766404#766404

/Bingo

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Angehängte Dateien:

Bewertung
1 lesenswert
nicht 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.

von Jürgen Hinze (Gast)


Bewertung
0 lesenswert
nicht 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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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.

von Peter D. (peda)


Bewertung
0 lesenswert
nicht 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

von ... (Gast)


Bewertung
0 lesenswert
nicht 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):
AVR Toolchain Installer
(87 MB, updated 9/10)
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

von Peter D. (peda)


Bewertung
0 lesenswert
nicht lesenswert
... 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

von 900ss D. (900ss)


Bewertung
0 lesenswert
nicht lesenswert
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

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht 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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht 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.

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht 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
{
   unsigned long i = 10;
   __builtin_avr_delay_cycles (i); // oder i+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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht 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
>
{
>    unsigned long i = 10;
>    __builtin_avr_delay_cycles (i); // oder i+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. :-/

von ... (Gast)


Bewertung
0 lesenswert
nicht 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)
#if __HAS_DELAY_CYCLES && defined(__OPTIMIZE__)
  extern void __builtin_avr_delay_cycles(unsigned long);
  __builtin_avr_delay_cycles(__tmp);
#else

von Titan (Gast)


Bewertung
0 lesenswert
nicht 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ß:


#include <avr/io.h>
#ifndef F_CPU
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert 
   (z. B. durch Übergabe als Parameter zum Compiler innerhalb 
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die
   "nachträgliche" Definition hinweist */
#warning "F_CPU war noch nicht definiert, wird nun mit 3686400 definiert"
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */
#endif
#include <util/delay.h>
 
int main( void )
{
    DDRB = ( 1 << PB0 );        // PB0 an PORTB als Ausgang setzen
 
    while( 1 ) {                // Endlosschleife
        PORTB ^= ( 1 << PB0 );  // Toggle PB0 z.&nbsp;B. angeschlossene LED
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...
                                // funktioniert nicht mit Bibliotheken vor 1.6
 
    }
    return 0;
}



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

von Helfer (Gast)


Bewertung
0 lesenswert
nicht 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...

von delay_ms (Gast)


Bewertung
2 lesenswert
nicht 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.

von Mario (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

wenn man Atmel Studio 7 verwendet, ist das darin gefixt?
Danke im voraus.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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.

von Mario (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ne, ich meinte ob der Fix vom Beitrag 19.11.2010 11:47 mittlerweile in 
AS7 Einzug gehalten hat?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Ich gehe stark davon aus, dass Microchip nicht mehr diese hornalte
Bibliotheksversion ausliefert.  Im Zweifelsfalle: sieh dir
<avr/version.h> an.

von Mario (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

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

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

/** \ingroup avr_version
    Numerical representation of the current library version.
 */
#define __AVR_LIBC_VERSION__        20000UL

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

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

/** \ingroup avr_version
    Library major version number. */
#define __AVR_LIBC_MAJOR__          2

/** \ingroup avr_version
    Library minor version number. */
#define __AVR_LIBC_MINOR__          0

/** \ingroup avr_version
    Library revision number. */
#define __AVR_LIBC_REVISION__       0

#endif /* _AVR_VERSION_H_ */

von Gottfried S. (gottfried)


Bewertung
-1 lesenswert
nicht 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.
void _delay_us(double __us)
{
  double __tmp ;
...
  uint8_t __ticks;
  double __tmp2 ;
  __tmp = ((F_CPU) / 3e6) * __us;
  __tmp2 = ((F_CPU) / 4e6) * __us;
  if (__tmp < 1.0)
    __ticks = 1;
  else if (__tmp2 > 65535)
  {
    _delay_ms(__us / 1000.0);
  }
  else if (__tmp > 255)
  {
    uint16_t __ticks=(uint16_t)__tmp2;
    _delay_loop_2(__ticks);
    return;
  }
  else
    __ticks = (uint8_t)__tmp;
  _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:
int usleep(useconds_t usec);
typedef unsigned long   __useconds_t;   /* microseconds (unsigned) */
typedef __useconds_t   useconds_t;

struct timespec {
    time_t tv_sec;        /* seconds */
    long   tv_nsec;       /* nanoseconds */
};
int nanosleep(const struct timespec *req, struct timespec *rem); 
Somit 32 oder 64-bit integer bei UNIX
Windows:
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.

von Johannes S. (jojos)


Bewertung
1 lesenswert
nicht lesenswert
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?

von Andreas B. (bitverdreher)


Bewertung
0 lesenswert
nicht lesenswert
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. ;-)

: Bearbeitet durch User
von Johannes S. (jojos)


Bewertung
0 lesenswert
nicht 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.

: Bearbeitet durch User
von Andreas B. (bitverdreher)


Bewertung
0 lesenswert
nicht lesenswert
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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht 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.

von Rolf M. (rmagnus)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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. ;-)

von Gottfried S. (gottfried)


Bewertung
0 lesenswert
nicht 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.

von Rolf M. (rmagnus)


Bewertung
0 lesenswert
nicht 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.

von Gottfried S. (gottfried)


Bewertung
0 lesenswert
nicht 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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht 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
#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.

: Bearbeitet durch Moderator
von Gottfried S. (gottfried)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
1 lesenswert
nicht 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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.