Difference between revisions of "AVR Embedded Tutorial - Timer, Counter/de"

From Free Pascal wiki
(Beispiel-Code)
m (Fixed template loop; modded categories to German; added language category)
 
(27 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 +
{{LanguageBar}}
 +
 +
=Timer / Counter=
 +
 
==Titel==
 
==Titel==
 +
 +
Hier geht es nicht darum, alle Register zu verstehen. Dies sollte nur ein praktisches Beispiel sein, wie man es in FPC/Lazarus umsetzt.
 +
 +
Genauere Details gibt es hier:
 +
 +
* https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR
 +
 +
==Beispiel, LEDs über Timer steuern==
 +
 
Dieses Beispiel zeigt, wie man die beiden Timer eines ATtiny2313 verwenden kann.
 
Dieses Beispiel zeigt, wie man die beiden Timer eines ATtiny2313 verwenden kann.
Jeder Timer steuert eine LED an, welche an Bit 0 und 1 des PortD sind.
+
Jeder Timer steuert eine LED an, welche an Bit 0 und 1 des PortD sind. Dabei sollte die LED an PD0 etwas schneller blinken.
Hier geht es nicht, das man alle Register versteht, dies sollte nur ein praktisches Beispiel sein, wie man es in FPC umsetzt.
 
Genauere Details gibt es hier: https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR
 
  
==Beispiel-Code==
+
===Beispiel Code===
<syntaxhighlight lang="pascal">program Project1;
 
  
  procedure sei; assembler; inline; // Interrupt aus
+
<syntaxhighlight lang="pascal">
  asm
+
program Project1;
          Sei
+
</syntaxhighlight>
  end;
 
  
  procedure cli; assembler; inline; // Interrupt ein
+
====Timer 0 Interrupt====
  asm
 
          Cli
 
  end;
 
  
  procedure Timer0_Interrupt; public Name 'TIMER0_COMPA_ISR'; interrupt;
+
<syntaxhighlight lang="pascal">
  const
+
procedure Timer0_Interrupt; public Name 'TIMER0_COMPA_ISR'; interrupt;
    t = 10;        // LED sollte nur bei jedem 10. Durchlauf umschalten.
 
 
   const
 
   const
     p: integer = 0;
+
     t          = 10; // LED sollte nur bei jedem 10. Durchlauf umschalten.
 +
    z: integer = 0; // Zähler für Leerdurchläufe.
 
   begin
 
   begin
     TCNT0 := 128;                      // Speed halbieren  0 = langsm (default)
+
     TCNT0 := 128;                      // Speed halbieren  0 = langsam (default)
     Inc(p);
+
     Inc(z);
     if (p = t) then begin
+
     if (z = t) then begin
 
       PORTD := PORTD or (1 shl 0);      // LED Pin0 ein
 
       PORTD := PORTD or (1 shl 0);      // LED Pin0 ein
 
     end;
 
     end;
     if (p = t * 2) then begin
+
     if (z = t shl 1) then begin
       PORTD := PORTD and not (1 shl 0); // LED Pin1 aus
+
       PORTD := PORTD and not (1 shl 0); // LED Pin0 aus
       p := 0;
+
       z := 0;
 
     end;
 
     end;
 
   end;
 
   end;
 +
</syntaxhighlight>
  
  procedure Timer1_Interrupt; public Name 'TIMER1_COMPA_ISR'; interrupt;
+
====Timer 1 Interrupt====
  const
+
 
    t = 500;        // LED sollte nur bei jedem 500. Durchlauf umschalten.
+
<syntaxhighlight lang="pascal">
 +
procedure Timer1_Interrupt; public Name 'TIMER1_COMPA_ISR'; interrupt;
 
   const
 
   const
     p: integer = 0;
+
     t          = 500; // LED sollte nur bei jedem 500. Durchlauf umschalten.
 +
    z: integer =   0; // Zähler für Leerdurchläufe.
 
   begin
 
   begin
     Inc(p);
+
     Inc(z);
     if (p = t) then begin
+
     if (z = t) then begin
 
       PORTD := PORTD or (1 shl 1);      // LED Pin1 ein
 
       PORTD := PORTD or (1 shl 1);      // LED Pin1 ein
 
     end;
 
     end;
     if (p = t * 2) then begin
+
     if (z = t shl 1) then begin
 
       PORTD := PORTD and not (1 shl 1); // LED Pin1 aus
 
       PORTD := PORTD and not (1 shl 1); // LED Pin1 aus
       p := 0;
+
       z := 0;
 
     end;
 
     end;
 
   end;
 
   end;
  
 +
</syntaxhighlight>
 +
 +
====Timer initialisieren====
  
 +
<syntaxhighlight lang="pascal">
 
begin
 
begin
   // Interupt unterbinden.
+
   // -- Interrupt unterbinden.
   cli();
+
   asm cli end;
  
   // -- Bit 0 und 1 von PortD auf Ausgabe stellen.
+
   // -- PD0 und PD1 auf Ausgabe stellen.
   DDRD := DDRD or (1 shl 0) or (1 shl 1);
+
   DDRD   := %00000011;
  
 
   // -- Timer0 initialisieren.
 
   // -- Timer0 initialisieren.
 
   TCCR0A := 0;
 
   TCCR0A := 0;
   TCCR0B := %101; // CPU-Takt / 1024
+
   TCCR0B := %101;                   // CPU-Takt / 1024
   TIMSK := TIMSK or (1 shl OCIE0A);
+
   TIMSK := TIMSK or (1 shl OCIE0A); // Timer0 soll Interrupt auslösen.
  
 
   // -- Timer1 initialisieren.
 
   // -- Timer1 initialisieren.
   TCCR1A := 1;
+
   TCCR1A := 1;                       // CTC-Modus
   TCCR1B := %010; // CPU-Takt / 8
+
   TCCR1B := %010;                   // CPU-Takt / 8
   TIMSK := TIMSK or (1 shl OCIE1A);
+
   TIMSK := TIMSK or (1 shl OCIE1A); // Timer1 soll Interrupt auslösen.
  
 
   // -- Interrupt aktivieren.
 
   // -- Interrupt aktivieren.
   sei();
+
   asm sei end;
 +
 
 +
  // -- Hauptschleife
 
   repeat
 
   repeat
 
     // Mache Irgendwas.
 
     // Mache Irgendwas.
 
   until 1 = 2;
 
   until 1 = 2;
end.</syntaxhighlight>
+
end.
 +
</syntaxhighlight>
 +
 
 +
==Timer extern getaktet==
 +
 
 +
Einen Timer kann man auch extern getaktet werden. Folgendes Beispiel demonstriert dies mit einem Atmega328.
 +
Zum extern takten gibt es folgende Pins am AVR:
 +
 
 +
* '''T0''' taktet Timer 0
 +
* '''T1''' taktet Timer 1
 +
 
 +
Der Timer 2 kann '''nicht''' extern getaktet werden.
 +
 
 +
===Beispiel einer Uhr===
 +
 
 +
Für eine exakte Uhr ist ein Quarz/Oszilator mit 4.194304MHz geeignet. Dieser lässt sich gut in einer 2er Potenz teilen, so das man auf einen Sekunden-Takt kommt.
 +
Hier wird der Timer 0 extern getaktet, dazu muss man den Oszilator an '''T0''' (PD4) anschliessen.
 +
 
 +
====Timer Interrupt====
 +
 
 +
Jeder 16384 Aufruf des Interupts ist eine Sekunde.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
procedure Timer0_Interrupt; public Name 'TIMER0_OVF_ISR'; interrupt;
 +
  const
 +
    cl = 16384 shr 1;    // 4194304 / 256 / 2;
 +
    zaehler: UInt16 = 0; // Zählt bis cl erreicht ist.
 +
 
 +
  begin
 +
    Inc(zaehler);
 +
 
 +
    // Nach einer halben Sekunde LED ausschalten.
 +
    if zaehler = cl then begin
 +
      PORTB := PORTB and not (1 shl 5);
 +
    end;
 +
 
 +
    // Sekunde ist erreicht.
 +
    if zaehler >= cl shl 1 then begin
 +
      PORTB  := PORTB or (1 shl 5);
 +
      zaehler := 0; // Zähler zurücksetzen.
 +
    end;
 +
  end;
 +
</syntaxhighlight>
 +
 
 +
====Timer inizialisieren====
 +
 
 +
Der springende Punkt ist der Wert bei '''TCCR0B''', dieser sagt, das der Timer extern getaktet wird.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
begin
 +
  DDRB  := 1 shl 5;      // Pin PD5 auf Ausgabe
 +
 
 +
  TCCR0A := %00;          // Normaler Timer
 +
  TCCR0B := %111;          // Clock / Externer Pin TO, steigende Flanke.
 +
  TIMSK0 := (1 shl TOIE0); // Timer 0 Interrupt aktivieren.
 +
 
 +
  asm sei end;            // Interrupts einschalten.
 +
 
 +
  repeat
 +
    // Hier kann die Zeit auf ein Display ausgegeben werden.
 +
  until 1 = 2;
 +
end.
 +
</syntaxhighlight>
 +
 
 +
==Stolperfalle==
 +
 
 +
Die Namen der Proceduren können abweichen, ein Beispiel:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
// ATtiny2313
 +
procedure Timer0_Interrupt; public Name 'TIMER0_COMPA_ISR'; interrupt;
 +
// ATtiny44
 +
procedure Timer0_Interrupt; public Name 'TIM0_COMPA_ISR'; interrupt;
 +
</syntaxhighlight>
 +
 
 +
= Siehe auch =
 +
 
 +
* Übersichtseite [[AVR Embedded Tutorial/de|AVR Embedded Tutorial]]
 +
 
 +
Autor: [[User:Mathias|Mathias]]
 +
 
 +
[[Category:AVR/de]]
 +
[[Category:Arduino/de]]
 +
[[Category:Embedded/de]]
 +
[[Category:Tutorials/de]]
 +
{{AutoCategory}}

Latest revision as of 00:35, 24 January 2020

Deutsch (de) English (en)

Timer / Counter

Titel

Hier geht es nicht darum, alle Register zu verstehen. Dies sollte nur ein praktisches Beispiel sein, wie man es in FPC/Lazarus umsetzt.

Genauere Details gibt es hier:

Beispiel, LEDs über Timer steuern

Dieses Beispiel zeigt, wie man die beiden Timer eines ATtiny2313 verwenden kann. Jeder Timer steuert eine LED an, welche an Bit 0 und 1 des PortD sind. Dabei sollte die LED an PD0 etwas schneller blinken.

Beispiel Code

program Project1;

Timer 0 Interrupt

procedure Timer0_Interrupt; public Name 'TIMER0_COMPA_ISR'; interrupt;
  const
    t          = 10; // LED sollte nur bei jedem 10. Durchlauf umschalten.
    z: integer =  0; // Zähler für Leerdurchläufe.
  begin
    TCNT0 := 128;                       // Speed halbieren  0 = langsam (default)
    Inc(z);
    if (z = t) then begin
      PORTD := PORTD or (1 shl 0);      // LED Pin0 ein
    end;
    if (z = t shl 1) then begin
      PORTD := PORTD and not (1 shl 0); // LED Pin0 aus
      z := 0;
    end;
  end;

Timer 1 Interrupt

procedure Timer1_Interrupt; public Name 'TIMER1_COMPA_ISR'; interrupt;
  const
    t          = 500; // LED sollte nur bei jedem 500. Durchlauf umschalten.
    z: integer =   0; // Zähler für Leerdurchläufe.
  begin
    Inc(z);
    if (z = t) then begin
      PORTD := PORTD or (1 shl 1);      // LED Pin1 ein
    end;
    if (z = t shl 1) then begin
      PORTD := PORTD and not (1 shl 1); // LED Pin1 aus
      z := 0;
    end;
  end;

Timer initialisieren

begin
  // -- Interrupt unterbinden.
  asm cli end;

  // -- PD0 und PD1 auf Ausgabe stellen.
  DDRD   := %00000011;

  // -- Timer0 initialisieren.
  TCCR0A := 0;
  TCCR0B := %101;                    // CPU-Takt / 1024
  TIMSK  := TIMSK or (1 shl OCIE0A); // Timer0 soll Interrupt auslösen.

  // -- Timer1 initialisieren.
  TCCR1A := 1;                       // CTC-Modus
  TCCR1B := %010;                    // CPU-Takt / 8
  TIMSK  := TIMSK or (1 shl OCIE1A); // Timer1 soll Interrupt auslösen.

  // -- Interrupt aktivieren.
  asm sei end;

  // -- Hauptschleife
  repeat
    // Mache Irgendwas.
  until 1 = 2;
end.

Timer extern getaktet

Einen Timer kann man auch extern getaktet werden. Folgendes Beispiel demonstriert dies mit einem Atmega328. Zum extern takten gibt es folgende Pins am AVR:

  • T0 taktet Timer 0
  • T1 taktet Timer 1

Der Timer 2 kann nicht extern getaktet werden.

Beispiel einer Uhr

Für eine exakte Uhr ist ein Quarz/Oszilator mit 4.194304MHz geeignet. Dieser lässt sich gut in einer 2er Potenz teilen, so das man auf einen Sekunden-Takt kommt. Hier wird der Timer 0 extern getaktet, dazu muss man den Oszilator an T0 (PD4) anschliessen.

Timer Interrupt

Jeder 16384 Aufruf des Interupts ist eine Sekunde.

procedure Timer0_Interrupt; public Name 'TIMER0_OVF_ISR'; interrupt;
  const
    cl = 16384 shr 1;    // 4194304 / 256 / 2;
    zaehler: UInt16 = 0; // Zählt bis cl erreicht ist.

  begin
    Inc(zaehler);

    // Nach einer halben Sekunde LED ausschalten.
    if zaehler = cl then begin
      PORTB := PORTB and not (1 shl 5);
    end;

    // Sekunde ist erreicht. 
    if zaehler >= cl shl 1 then begin
      PORTB   := PORTB or (1 shl 5);
      zaehler := 0; // Zähler zurücksetzen.
    end;
  end;

Timer inizialisieren

Der springende Punkt ist der Wert bei TCCR0B, dieser sagt, das der Timer extern getaktet wird.

begin
  DDRB   := 1 shl 5;       // Pin PD5 auf Ausgabe

  TCCR0A := %00;           // Normaler Timer
  TCCR0B := %111;          // Clock / Externer Pin TO, steigende Flanke.
  TIMSK0 := (1 shl TOIE0); // Timer 0 Interrupt aktivieren.

  asm sei end;             // Interrupts einschalten.

  repeat
    // Hier kann die Zeit auf ein Display ausgegeben werden.
  until 1 = 2;
end.

Stolperfalle

Die Namen der Proceduren können abweichen, ein Beispiel:

// ATtiny2313
procedure Timer0_Interrupt; public Name 'TIMER0_COMPA_ISR'; interrupt;
// ATtiny44
procedure Timer0_Interrupt; public Name 'TIM0_COMPA_ISR'; interrupt;

Siehe auch

Autor: Mathias