AVR Embedded Tutorial - Delays/de

From Lazarus wiki
Jump to navigationJump to search

Deutsch (de) English (en)

Zur Übersichtseite AVR Embedded Tutorial/de.

Delays für ATmega328 / Arduino

... und natürlich andere AVR Controller.

Genaues Timing auf den AVR Controllern ist eigentlich ganz einfach - wenn man es in Assembler macht. Einfache Schleifen in Pascal dagegen lassen sich nicht genau timen, denn man weiss nie wie der Compiler die Schleife beim nächsten Mal baut.

So wird zum Beispiel bei Compileroption -o1 eine for-Schleife mit einer 16bit-Zählvariable angelegt, die gleiche Schleife bei -o2 und -o3 mit 8bit. Dadurch wird die Schleife fast doppelt so schnell.

Kurze Delays im Mikrosekundenbereich

{$goto on}  // muss angegeben sein

procedure delay_short();
label
  loop;
begin
  asm
    ldi r16, 60
    loop:
      nop
      dec r16
      brne loop
  end['r16'];
end;

Die Größe der Zählvariable muss man ausprobieren. Die Loop dauert 4 Taktzyklen, bei 8MHz wären also 2 Loops eine Mikrosekunde. Dazu kommen aber noch Einsprung in die Routine und der Compiler bastelt noch etwas Stackpointer- und Registersicherung drumrum.

Längere Delays im Millisekundenbereich

{$goto on}  // muss angegeben sein

// Pause msec, 1 bis 255 msec

procedure delay_ms(time : uint8);
const
  fmul = 1 * fcpu div 1000000;
label
  loop1, loop2, loop3;
begin
  asm
    ldd r20, time
    loop1:
      ldi r21, fmul
      loop2:  // 1000 * fmul = 1000 * 1 * 8 = 8000 cycles / 8MHz
        ldi r22, 250
        loop3:  // 4 * 250 = 1000 cycles
          nop
          dec r22
          brne loop3
        dec r21
        brne loop2
      dec r20
      brne loop1
  end['r20','r21','r22'];
end;

Mit dieser Schleife bekommt man ein ziemlich genaues Timing im Bereich 1 bis 255msec. Die Schleife darf durch Interrupts unterbrochen werden, dann dauert sie natürlich länger.

Die innere Schleife dauert 4 Taktzyklen pro Durchlauf, ergo 1000 Zyklen. Die mittlere Schleife macht daraus je nach Taktfrequenz 1msec (bei 1Mhz 1 Durchlauf, bei 8MHz 8 Durchläufe, bei 16MHz 16 Durchläufe). Die äußere Schleife entspricht dann der Anzahl Millisekunden.

Achtung! fmul, fcpu müssen Konstanten sein. Dann berechnet der Compiler den Wert beim Kompilieren und setzt ihn fest ein. Als Variablen würde der Compiler eine Ganzzahldivision einbauen und das wollt ihr nicht wirklich - außerdem geht die Zuweisung ldi auf eine Variable schief.

noch längere Delays

... im Sekundenbereich? Also echt mal, das muss doch nicht sein. Schon 50msec nimmt man vielleicht beim Startup, wenn der Controller noch auf die Peripherie warten muss. Aber im Programmablauf sind lange Delays schlechter Stil und es gibt eigentlich immer bessere Möglichkeiten.

Siehe auch