Όπως έχουμε δει, οι γενικού σκοπού καταχωρητές του 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
}
}