mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik zeitfunktion "_delay_ms()"


Autor: Tom (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
es gibt ja in der <util/delay.h> die Zeitfunktion "_delay_ms(int)", weiß 
jemand wie genau man sich auf die verlassen kann? Bzw ist es wenn man es 
genau braucht sinnvoller das selbst über einen Timer oder so zu 
programmieren?
und  gibt es zu fällig auch eine Funktion für µ-Sekunden? Also irgendwas 
in der form "_delay_us(int)" oder so?

vielen Dank schon einmal im Vorraus
MFG
Tom

Autor: willivonbienemaya (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"
Bzw ist es wenn man es
genau braucht sinnvoller das selbst über einen Timer oder so zu
programmieren?
"

Es ist immer sinnvoller selbst zu programmieren, dann weiss man was das 
Programm macht. Dann kann man zB auch vorhersagen wie lange das Delay 
dauern wird.

Du könntest mal den Simulator + Stopuhr befragen, wie lange das delay 
dauert.

Autor: Falk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@willivonbienemaya

"
Bzw ist es wenn man es
genau braucht sinnvoller das selbst über einen Timer oder so zu
programmieren?
"

>Es ist immer sinnvoller selbst zu programmieren, dann weiss man was das
>Programm macht. Dann kann man zB auch vorhersagen wie lange das Delay

Quark. Lesen bildet. In der Doku der libc vom AVR ist alles drin.

MfG
Falk

Autor: Andy (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du kannst doch einfach selbst eine Fkt definieren und die gewünschte 
Zeit selbst programmieren :

void Delay(int del) {
   int d;
   for (d=0; d<=del; d++) { }
}

Die ist von deiner Taktfrequenz abhängig

Viel Glück

Mfg Andy

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn Du in die Dokumentation der AVR-libc geschaut hättest, dann 
wüsstest Du, dass es eine _delay_us()-Funktion gibt. Diese Funktionen 
sind aber nur dann sinnvoll einsetzbar, wenn konstante Werte (die zur 
Compile-Zeit bekannt sind) übergeben werden, da der Compiler (weitere 
Voraussetzung: Optimierung aktiviert!) die eigentlichen Zählschleifen 
direkt berechnen kann. Und in dem Falle sind diese Funktionen sehr 
genau, allerdings auch nur dann, wenn man im Makefile (oder im 
AVRStudio) die korrekte CPU-Taktfrequenz angegeben hat. Die 
_delay_xx()-Funktionen sind allerdings begrenzt, d.h. in Abhängigkeit 
von der CPU-Frequenz sind nur eingeschränkte Verzögerungszeiten möglich 
(bei _delay_ms() max. 262.14ms/F_CPU[MHz] und bei _delay_us() max. 
768µs/F_CPU[MHz]). Die Funktionen sind nur sinnvoll einsetzbar, wenn es 
um kurze Verzögerungen geht. Bei längeren und flexiblen Zeiten ist ein 
Timer das einzig wahre.

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Andy:
Wenn meine lib Funktionen zur Verfügung stellt, die reproduzierbar und 
ohne viel Rumprobiererei die erforderlichen Zeiten präzise erzeugen 
können, dann werd ich sicher nicht mit irgendwelchen C-Zählschleifen 
verwenden, die man erst mal durch experimentelles Rumprobieren mit 
unterschiedlichen Zahlenwerten "einstellen" muss. Und das wäre bei 
Deinem Vorschlag der Fall.

> Viel Glück
Das würde er dabei brauchen... Und auch viel Zeit.

Autor: Tom (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tach
ich bräuchte ein mal 2000 ms ( wo es ja mit der _delay_ms() funktion 
eher düster aussieht) und einmal bräuchte ich 400 µs des müsste doch 
eigentlich recht problem los über "_delay_ms(0.4);" gehen oder?

Autor: Falk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Tom

>ich bräuchte ein mal 2000 ms ( wo es ja mit der _delay_ms() funktion
>eher düster aussieht) und einmal bräuchte ich 400 µs des müsste doch
>eigentlich recht problem los über "_delay_ms(0.4);" gehen oder?

Ist es sooooschwer mal die Doku der libc zu lesen?

MfG
Falk

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> void Delay(int del) {
>   int d;
>   for (d=0; d<=del; d++) { }
> }
>
> Die ist von deiner Taktfrequenz abhängig

Eher von der Intelligenz des Compilers, diesen Code komplett über Bord 
zu werfen, weil aus seiner Sicht völlig nutzlos.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Delay-Funktionen sind grundsätzlich ungenau.

Sie dienen nur dazu, worst-case Zeiten einzuhalten, z.B. eine notwendige 
minimale Wartezeit.

Jeder Interrupt verlängert natürlich das Delay um seine Ausführungszeit.

Und der Code vor und nach dem Delay braucht ja auch Zeit für seine 
Ausführung.


Nur mit einem der HW-Timer kann man genaue Zeiten erzielen.


Peter

Autor: blubblub (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
_delay_ms()??

was muß jetzt in der Klammer stehen um z.b. 5ms Verzögerung zu 
erhalten???

Gruß

Autor: Ulrich (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
lol natürlich eine 5.

Autor: blubblub (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Ulrich..

In der Lib. steht aber:

max. 262.14ms/F_CPU[MHz]

verstehe ich da etwas falsch ???????

Gruß

Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du einen freien Timer hast, dann programmier die Funtkion selbst. 
Vorallem weist du auch wie genau deine Funktion ist.

z.B Takt 8MHz->Teiler Timer0=64
void WAIT_ms(int ms)
{
      int i;
      TCCR0B=4;
      for(i=0;i<=ms;i++)
      {
          TCN01=0;
          while(TCNT0<125);
      }
      TCCR0B=0;
}

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> max. 262.14ms/F_CPU[MHz]

Genau.

> verstehe ich da etwas falsch ???????

Weiß nicht. Wie verstehst du es denn?

Autor: blubblub (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
überhaupt nicht!!??

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Na die maximale Wartezeit ist 262,14ms geteilt durch F_CPU in Megahertz.
Bei einem Takt von 1Mhz kannst du mit der Funktion also maximal 262,14ms 
warten, bei 8Mhz nur noch 32,768ms.

Autor: Sonic (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie Peter D. oben schon schrieb sind die _delay-Funktionen ungenau wenn 
Interrupts aktiviert sind. Wenn du's genau haben willst musst du den 
globalen Interrupt (SREG &= ~_BV(I);) deaktivieren. Dann sind sie so 
genau wie deine Taktfrequenz. Allerdings wartet dann ALLES.
Also längere Zeiten, wie oben erwähnt, über Timer-Interrupts 
realisieren!

Autor: blub-blub (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Tag...

Ah, jetzt ja....

Bei 8MHz bis max. 32ms benutzen.
Dann stimmt die Zahl in der Klammer mit der Verzögerungszeit überein.

Habe das im Simulator von Studio4 mal laufenlassen.

Alles darüber hinaus stimmt dann nicht.

VIIIIIIIIIIIEEEEEEEEEEELEN Dank für die Hilfe.

Klasse Forum

Ein schönes Restwochenende gewünscht.

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> SREG &= ~_BV(I);
Bitte nicht... Dafür gibts "cli();"

Autor: Sonic (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK, viele Wege führen nach Rom ...
cli(); macht auch nix anderes.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sonic wrote:
> OK, viele Wege führen nach Rom ...
> cli(); macht auch nix anderes.

Aber woll !

cli() braucht 4 Bytes weniger und garantiert, daß Interrupts sofort 
gesperrt sind.

Bei IN+ANDI+OUT kann aber ein Interupt noch dahinter kommen, bevor das 
Löschen des Bits wirksam ist und schon isses wieder gesetzt.

D.h. die Anweisung kann fehlschlagen.

Außerdem ergibt I einen Compilerfehler, es muß SREG_I heißen.


Peter

Autor: Sonic (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jo, stimmt natürlich mit dem SREG_I.
Genaugenommen sind's 2 Zyklen, die der cli(); weniger braucht, nämlich 
einen.
SREG &= ~_BV(SREG_I); braucht drei.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum wird eigentlich für's SREG nicht derselbe Code wie bei den anderen 
I/O-Registern (sowas wie cbi SREG, SREG_I) erzeugt?

Autor: Sonic (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
+0000005F:   94F8        CLI                      Global Interrupt 
Disable
Das erzeugt er bei cli();

+00000060:   B78F        IN      R24,0x3F         In from I/O location
+00000061:   778F        ANDI    R24,0x7F         Logical AND with 
immediate
+00000062:   BF8F        OUT     0x3F,R24         Out to I/O location
und das bei SREG &= ~_BV(SREG_I);

Ich schätze mal damit dem Programmierer etwas mehr Flexibilität bleibt, 
vielleicht will man das ja mal per SREG &= ~_BV(SREG_I); machen?

Für cli(); muss auch die <avr/interrupt.h> eingebunden werden.

Autor: Werbungverschmäher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> sowas wie cbi SREG, SREG_I ...

SREG ist im oberen Teil des I/O-Adressraums und kann daher nicht 
bitorientiert angesprochen werden.

Damit das SREg trotzdem schnell manipuliert werden kann, enthält der 
ASM-Befehlssatz die Befehle SEx und CLx, wobei x für das Bit im SREG 
steht, beim Interrupt-Bit eben SEI und CLI, beim Carry SEC und CLC.

Auch unter C könnte es nützlich sein, den ASM-Befehlssatz zu kennen, 
denn der AVR kann kein C, der kann nur ASM bzw. Maschinencode. Jedes 
C-Konstrukt muss also vom Compiler in ASM übersetzt werden.

MfG

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Fakt ist doch, egal wie man SREG_I löscht, nur mit CLI wird es 
100%-ig gelöscht, sonst nicht !!!

Deshalb muß man beim AVR CLI nehmen !

Es mag zwar selten sein, daß ein Interrupt währende des OUT zuschlägt, 
aber dadurch sind solche Fehler nur umso gefährlicher, da schwerer 
festzustellen.

Es ist also müßig über andere Befehle zu palavern, sie wuppen es einfach 
nicht !


Peter

P.S.:
Beim 8051 hat man das anders gelöst:
Jeder Zugriff zu den Interruptprioritätsbits oder den 
Interruptenablebits, verzögert die Interruptbehandlung um einen Befehl. 
Das dient genau dazu, alle Änderungen zu speichern, ehe der nächste 
Interrupt zuschlagen kann.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> SREG ist im oberen Teil des I/O-Adressraums und kann daher nicht
> bitorientiert angesprochen werden.

Ah richtig. cbi und sbi gehen nur bis 32, nicht bis 64, wie in und out.

> Damit das SREg trotzdem schnell manipuliert werden kann, enthält der
> ASM-Befehlssatz die Befehle SEx und CLx, wobei x für das Bit im SREG
> steht, beim Interrupt-Bit eben SEI und CLI, beim Carry SEC und CLC.

Oder allgemein auch als BSET/BCLR verfügbar.

> Auch unter C könnte es nützlich sein, den ASM-Befehlssatz zu kennen,
> denn der AVR kann kein C, der kann nur ASM bzw. Maschinencode. Jedes
> C-Konstrukt muss also vom Compiler in ASM übersetzt werden.

Das ist bei jedem Prozessor so.

> Der Fakt ist doch, egal wie man SREG_I löscht, nur mit CLI wird es
> 100%-ig gelöscht, sonst nicht !!!

sbi/cbi wären auch atomar gewesen.

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.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

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