AVR Embedded Tutorial - Timer, Counter

From Lazarus wiki
Jump to navigationJump to search

Deutsch (de) English (en)

Timer / counter

This is not about understanding all the AVR registers. This is only a practical example of how to implement timers/counters in FPC. You should understand that timers are simply counters.

More details are available here (in German) or use your favourite search engine.

Example, control LEDs via timer

This example shows how you can use the two timers of an ATtiny2313. Each timer controls an LED, which are at bits 0 and 1 of Port D. The LED on PD0 should flash a little faster.

Example code

program Project1;

Timer 0 interrupt

procedure Timer0_Interrupt; public name 'TIMER0_COMPA_ISR'; interrupt;
  const
    t = 10;           // LED should only switch every 10th pass
    z : integer = 0;  // Counter for idle runs
  begin
    TCNT0 := 128;     // Half speed 0 = slow (default)
    Inc(z);

    if(z = t) then 
      begin
        PORTD := PORTD or (1 shl 0);      // Pin 0 LED on
      end;

    if(z = t shl 1) then 
      begin
        PORTD := PORTD and not (1 shl 0); // Pin 0 LED off
        z := 0;
      end;
  end;

Timer 1 interrupt

procedure Timer1_Interrupt; public name 'TIMER1_COMPA_ISR'; interrupt;
  const
    t = 500;          // LED should only switch every 500th pass
    z : integer = 0;  // Counter for idle runs
  begin
    Inc (z);

    if(z = t) then 
      begin
        PORTD := PORTD or (1 shl 1);      // Pin 1 LED on
      end;
  
    if(z = t shl 1) then 
      begin
        PORTD := PORTD and not (1 shl 1); // Pin 1 LED off
        z : = 0;
      end;
  end;

Initialize the timer

  begin
   // Disable interrupts
   asm 
     cli 
   end;

   // Set PD0 and PD1 to output
   DDRD := %00000011;

   // Initialize timer0
   TCCR0A := 0;                        // Normal mode
   TCCR0B := %101;                     // CPU clock / 1024
   TIMSK  := TIMSK or (1 shl OCIE0A);  // Timer0 should trigger an interrupt

   // Initialize timer1
   TCCR1A := 1;                        // CTC (Clear Timer on Compare match) mode
   TCCR1B := %010;                     // CPU clock / 8
   TIMSK  := TIMSK or (1 shl OCIE1A);  // Timer1 should trigger an interrupt

   // Enable interrupts
   asm 
     sei
   end;

   // Main loop
   repeat
     // Do something
   until 1 = 2;
 end.

Timer clocked externally

A timer can also be clocked externally. The following example demonstrates this with an Atmega328. The following pins are available on the AVR for external clocking:

  • T0 clocks timer 0
  • T1 clocks timer 1

Timer 2 cannot be clocked externally.

Clock example

A quartz crystal/oscillator with 4.194304MHz is suitable for an exact clock. This can easily be divided into a power of two, so that you get a cycle of one second. Here timer 0 is clocked externally, for this you have to connect the oscillator to T0 (PD4).

Timer interrupt

Every 16,384th call of the interrupt is one second.

procedure Timer0_Interrupt; public name 'TIMER0_OVF_ISR'; interrupt;
  const
    cl = 16384 shr 1;     // 4194304/256/2
    counter : UInt16 = 0; // Counts until cl is reached

  begin
    Inc(counter);

    // Turn off the LED after half a second
    if count = cl then 
      begin
        PORTB := PORTB and not (1 shl 5);
      end;

    // 1 second is reached.
    if count > = cl shl 1 then 
      begin
        PORTB := PORTB or (1 shl 5);
        count := 0;       // Reset counter.
      end;
  end;

Initialize the timer

The important point is the value at TCCR0B which determines that the timer is clocked externally.

  begin
    DDRB := 1 shl 5;         // Set pin PD5 as an output

    TCCR0A := %00;           // Normal timer
    TCCR0B := %111;          // Clock / external pin TO, rising edge.
    TIMSK0 := (1 shl TOIE0); // Activate timer 0 interrupt.

    asm 
      sei  // enable interrupts
    end;  

   repeat
     // Here the time can be shown on a display
   until 1 = 2; 
 end.

Pitfalls

The names of the procedures can differ, for example:

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

See also