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


von Tom (Gast)


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

von willivonbienemaya (Gast)


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.

von Falk (Gast)


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

von Andy (Gast)


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

von johnny.m (Gast)


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.

von johnny.m (Gast)


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.

von Tom (Gast)


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?

von Falk (Gast)


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

von A.K. (Gast)


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.

von Peter D. (peda)


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

von blubblub (Gast)


Lesenswert?

_delay_ms()??

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

Gruß

von Ulrich (Gast)


Lesenswert?

lol natürlich eine 5.

von blubblub (Gast)


Lesenswert?

Hallo Ulrich..

In der Lib. steht aber:

max. 262.14ms/F_CPU[MHz]

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

Gruß

von Daniel (Gast)


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
1
void WAIT_ms(int ms)
2
{
3
      int i;
4
      TCCR0B=4;
5
      for(i=0;i<=ms;i++)
6
      {
7
          TCN01=0;
8
          while(TCNT0<125);
9
      }
10
      TCCR0B=0;
11
}

von Rolf Magnus (Gast)


Lesenswert?

> max. 262.14ms/F_CPU[MHz]

Genau.

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

Weiß nicht. Wie verstehst du es denn?

von blubblub (Gast)


Lesenswert?

überhaupt nicht!!??

von Rolf Magnus (Gast)


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.

von Sonic (Gast)


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!

von blub-blub (Gast)


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.

von johnny.m (Gast)


Lesenswert?

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

von Sonic (Gast)


Lesenswert?

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

von Peter D. (peda)


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

von Sonic (Gast)


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.

von Rolf Magnus (Gast)


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?

von Sonic (Gast)


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.

von Werbungverschmäher (Gast)


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

von Peter D. (peda)


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.

von Rolf Magnus (Gast)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.