10. Προγραμματίζοντας τους timers σε C

Όπως έχουμε δει, οι γενικού σκοπού καταχωρητές του AVR είναι στον έλεγχο του C compiler και δεν έχουμε άμεση πρόσβασή σε αυτούς με τις εντολές της C. Μπορούμε όμως, να έχουμε πρόσβαση σε όλους τους SFRs (Special Function Registers) άμεσα χρησιμοποιώντας τις εντολές της C.

Στη γλώσσα C μπορούμε να έχουμε πρόσβαση στους καταχωρητές του timer όπως στους TCNT0, OCR0 και TCCR0 άμεσα χρησιμοποιώντας τα ονόματα τους. Δες το ακόλουθο παράδειγμα:

Παράδειγμα: Γράψε ένα πρόγραμμα στη C που να εναλλάσσει όλα τα bits της PORTB συνεχόμενα με κάποια χρονική καθυστέρηση. Χρησιμοποίησε την λειτουργία Normal, no prescaler

Απάντηση

#include "avr/io.h"
void T0Delay();
int main()
{
     DDRB = 0xFF;   //PORTB output port
     while(1)
     {
         PORTB = 0x55;  
         T0Delay();
         PORTB = 0xAA;
         T0Delay();
     }
}
void ToDelay()
{
      TCNT0 = 0x20;      // load TCNT0
      TCCR0A = 0;
      TCCR0B = 0x01;     // Timer0, Normal mode, no prescaler
      while((TIFR0 & (1<<TOV0))==0); //wait for Timer0 to roll over
      TCCR0B = 0;
      TIFR0 = 1<<TOV0;   // clear TOV0
}

Υπολογίζοντας το μέγεθος της καθυστέρησης χρησιμοποιώντας timers

Όπως είδαμε στις τελευταίες δυο ενότητες, το μέγεθος της καθυστέρησης εξαρτάται από δυο παράγοντες (α) την συχνότητα του κρυσταλλικού ταλαντωτή και (β) την επιλογή του prescaler. Ένας τρίτος παράγοντας είναι στον C compiler, διότι διαφορετικοί C compiler παράγουν διαφορετικά hex αρχεία κώδικα και οι καθυστερήσεις των συνοδευτικών εντολών εξαρτώνται από τον compiler.

Παράδειγμα: Γράψε ένα πρόγραμμα σε C που να εναλλάσσει μόνο το PORTB.4 bit συνεχόμενα κάθε 35μs Χρησιμοποίησε τον Timer0, Normal mode και 1:8 prescaler για την παραγωγή της καθυστέρησης. Θεωρήστε XTAL=16MHz

Απάντηση: Έχουμε XTAL = 16MHz δηλαδή Τmachine cycle= 1/16MHz
Είναι Prescaler = 1:8 δηλαδή Tclock = 8x 1/16MHz = 0,5μs
Επομένως 35μs / 0,5μs = 70 clocks και έχουμε 1+0xFF-70 = 0x100 – 0x46 = 0xBA = 186

#include "avr/io.h"
void T0Delay();
int main()
{
       DDRB = 0xFF;              //PORTB output port
       while(1)
       {
           T0Delay();            // Timer0, Normal mode
           PORTB = PORTB ^ 0x10  // toggle PORTB.4
       }
}
void T0Delay()
{
       TCNT0 = 186;
       TCCR0A = 0;
       TCCR0B = 0x02              // Timer0, Normal mode, Prescaler=1:8
       while((TIFR0 & (1<<TOV0))==0); // wait for TOV0 to roll over
       TCCR0B = 0;                // turn off Timer0
       TIFR0 = (1<<TOV0);
}

Παράδειγμα: Γράψε ένα πρόγραμμα σε C που να εναλλάσσει μόνο το PORTB.4 bit συνεχόμενα κάθε 1ms. Χρησιμοποίησε τον Timer1, Normal mode και no prescaler για την παραγωγή της καθυστέρησης. Θεωρήστε XTAL=16MHz

Απάντηση: Έχουμε XTAL = 16MHz επομένως Tmachine cycle = 1/16MHz = 0,0625μs
Όμως prescaler = 1:1 άρα Tclock = 0,0625μs
Είναι 1ms/0,0625μs = 16.000 clocks = 0x3E80 clocks
1+0xFFFF – 0x3E80 = 0xC180

#include "avr/io.h"

void T1Delay();

int main()
{
      DDRB = 0xFF;
      while(1)
      {
          PORTB = PORTB ^ (1<<PB4); // toggle PB4
          T1Delay();
      }
}

void T1Delay()
{
      TCNT1H = 0xC1;     // TEMP = 0xC1
      TCNT1L = 0x80;
      TCCR1A = 0x00;     // Normal mode
      TCCR1B = 0x01;     // Normal mode, no prescaler
      while((TIFR1 & (0x1<<TOV1))==0); //wait for TOV1 to roll over
      TCCR1B = 0;
      TIFR1 = 0x1<<TOV1; // clear TOV1
}

Παράδειγμα: Γράψε ένα πρόγραμμα σε C που να εναλλάσσει το bit PORTB.4 συνεχώς κάθε δευτερόλεπτο. Χρησιμοποίησε τον Timer1, Normal mode και 1:1024 prescaler για την παραγωγή της καθηστέρησης. Θεώρησε XTAL = 16MHz

Απάντηση: Έχουμε XTAL = 16MHz επομένως Tmachine cycle = 1/16MHz = 0,0625μs = Tclock
Είναι Plescaler = 1:1024 άρα Tclock = 1024 x 0,0625μs = 64μs
Όμως 1s/64μs = 15.625 clocks = 0x3D09 clocks επομένως 1+0xFFFF-0x3D09 = 0xC2F7

#include "avr/io.h"

void T1Delay();

int main()
{
       DDRB = 0xFF;     // PORTB output port
       while(1)
       {
            PORTB = PORTB ^ (1<<PB4);  // toggle PB4
            T1Delay();                  
       }
}

void T1Delay()
{
     TCNT1H = 0xC2;     
     TCNT1L = 0xF7;
     TCCR1A = 0x00;     // Normal mode
     TCCR1B = 0x05;     // Normal mode, 1:256 prescaler
     while((TIFR1 & (0x1<<TOV1))==0);  // wait for Timer to roll over
     TCCR1B = 0;
     TIFR1 = 0x1<<TOV1;               // clear TOV1
}
Σημείωση: Στο παραπάνω πρόγραμμα μπορούμε επίσης να φορτώσουμε τον TCNT1 γράφοντας TCNT1 = 0xC2F7

Προγραμματισμός σε C των Timer 0 και 1 σαν counters

Δείξαμε σε προηγούμενη ενότητα πώς να χρησιμοποιήσουμε τους Timers 0 και 1 σαν απαριθμητές (counters). Οι Timers μπορούν να χρησιμοποιηθούν σαν counters, εάν τους παρέχουμε παλμούς από εξωτερική πηγή αντί για κύκλους ρολογιού του εσωτερικού ταλαντωτή. Εφαρμόζοντας παλμούς στα pins Τ0 (PD4) και T1 (PD5) μπορούμε να χρησιμοποιήσουμε τους Timer0 και Timer1 σαν Counter0 και Counter1, αντίστοιχα.

Παράδειγμα: Υπέθεσε ότι παλμοί συχνότητας 1Hz τροφοδοτούνται στο pin T0. Χρησιμοποίησε τη σημαία TOV0 για να επεκτείνεις τον Timer0 σε ένα 16bit counter και εμφάνισε το περιεχόμενο του counter στις θύρες PORTC και PORTD.

Απάντηση: Σε αυτό το παράδειγμα χρησιμοποιούμε τον μικροελεγκτή ATmega32. Στις θύρες PORTC και PORTD συνδέονται 16 LED και το Τ0 (ΡΒ0) συνδέεται σε εξωτερικό σήμα ρολογιού συχνότητας 1 Ηz.

#include "avr/io.h"

int main()
{
      PORTB = 0x01;    //activate pull-up of PB0
      DDRC = 0xFF;     //PORTC as output
      DDRD = 0xFF;     //PORTD as output
      TCCR0 = 0x06;     //output clock source
      TCNT0 = 0x00;
      while(1)
      {
             do
             {
                  PORTC = TCNT0;
             }while((TIFR0 & (0x1<<TOV0))==0); //wait for TOV0 to roll over
             TIFR0 = 0x1<<TOV0;   //clear TOV0
             PORTD++;             //increment PORTD
      }
}