Ο Rotary encoder είναι ένας μηχανικός
κωδικοποιητής περιστροφής incremental encoder[1].
Αποτελείται από ένα άξονα, ο οποίος περιστρέφεται και δύο διακόπτες οι οποίοι
κλείνουν – ανοίγουν με μια συγκεκριμένη σειρά που εξαρτάται από τη φορά
περιστροφής του άξονα. Ο άξονας έχει ένα μηχανισμό με θέσεις συγκράτησης (detents) κατά την περιστροφή. Οι δύο διακόπτες (A, B) έχουν το ένα άκρο τους κοινό και
το άλλο ελεύθερο. Το κοινό άκρο συνδέεται συνήθως στη γείωση και τα ελεύθερα
άκρα Α και Β σε εισόδους του μικροελεγκτή με ενεργοποιημένες τις εσωτερικές pull up αντιστάσεις ή με συνδεδεμένες εξωτερικές. Συνήθως υπάρχει
και ένας τρίτος ανεξάρτητος διακόπτης που κλείνει με πάτημα του άξονα και έχει
δύο ελεύθερους ακροδέκτες, οι οποίοι συνδέονται με τον ίδιο τρόπο, ο ένας στη
γείωση και ο άλλος στον μικροελεγκτή. Αν αρχίσουμε να περιστρέφουμε τον άξονα
συνεχώς, οι δύο διακόπτες θα παράγουν τετραγωνικούς παλμούς με διαφορά φάσης.
Όταν ο αριθμός των θέσεων συγκράτησης είναι ίσος με τον αριθμό των παλμών, τότε
έχουμε τον έναν τύπο ROT (detents
= pulses) όπου στις θέσεις συγκράτησης οι διακόπτες
είναι πάντα ανοιχτοί. Όταν ο αριθμός των θέσεων συγκράτησης είναι διπλάσιος από
τον αριθμό των παλμών, έχουμε το δεύτερο τύπο ROT (detents = 2*pulses) όπου στις θέσεις
συγκράτησης εναλλάσσονται είτε και οι δύο ανοιχτοί είτε και οι δύο κλειστοί.
Στο σχήμα παρουσιάζεται ο πρώτος τύπος ROT όπου δείχνονται οι θέσεις συγκράτησης. Για τον δεύτερο τύπο
υπάρχουν και ενδιάμεσες θέσεις.
Στις περισσότερες εφαρμογές η κίνηση του ROT αυξάνει ή ελαττώνει ένα μετρητή, ο οποίος χρησιμοποιείται
στη συνέχεια σε κάποια λειτουργία. Το λογισμικό αναγνωρίζει την κίνηση του
άξονα αν είναι κατά τη φορά δεικτών του ρολογιού (CW) ή
αντίθετη (CCW). Η αναγνώριση γίνεται ελέγχοντας την
κατάσταση των Α, Β. Ο πιο ασφαλής τρόπος είναι τα Α, Β να προκαλούν το καθένα
εξωτερική διακοπή στον μικροελεγκτή και η ρουτίνα της διακοπής να διαχειρίζεται
τη μεταβολή του μετρητή. Στη συνέχεια παρουσιάζεται
ένα παράδειγμα χρήσης ROT γραμμένο σε
γλώσσα προγραμματισμού C:
#include <avr/interrupt.h>
volatile int i1;
// Has to be "volatile" if
other routine than ISR(INTx_vect) affects "reg1bin"
void InitROT (void) // Start up, initialization routine
{
MCUCR |= (1<<ISC01); // Interrupt by the
falling edge INT0
MCUCR |= (1<<ISC11); // Interrupt by the
falling edge INT1
GICR |= (3<<6); // Enable INT0, INT1 interrupt
sei();
// Enable interrupts
DDRD &= ~((1<<2)|(1<<3)); // Port D1, D2 inputs
PORTD |= (3<<PORTD2); // Port D1, D2 Enable
pull up
}
ISR(INT0_vect) // INT0, PD2 Rot A, CW Increase, start moving (A->0, B=1)
{
//sei(); //
Enable interrupts (if necessary)
//cli(); //
Disable interrupts (if necessary)
_delay_ms(1);
while (!(bit_is_set(PIND,2))); //
A=0
_delay_ms(1);
if ((bit_is_set(PIND,2)) && (!(bit_is_set(PIND,3)))) // A=1, B=0
i1++;
// Increase
while (!(bit_is_set(PIND,3))); //
B=0
_delay_ms(1)
}
ISR(INT1_vect) // INT1, PD3 Rot B, CCW Decrease, start moving (B->0, A=1)
{
//sei(); //
Enable interrupts (if necessary)
//cli(); //
Disable interrupts (if necessary)
_delay_ms(1);
while (!(bit_is_set(PIND,3))); //
B=0
_delay_ms(1);
if ((bit_is_set(PIND,3)) && (!(bit_is_set(PIND,2)))) // B=1, A=0
i1--;
// Decrease
while (!(bit_is_set(PIND,2))); //
A=0
_delay_ms(1);
}
Οι έξοδοι Α, Β του ROT είναι
συνδεδεμένοι στις εισόδους INT0, INT1
(PD2,
PD3) του μικροελεγκτή. Η βιβλιοθήκη avr/interrupt είναι απαραίτητη για τη λειτουργία των διακοπών και η
μεταβλητή i1 πρέπει να είναι volatile αν θέλουμε να υπάρχει δυνατότητα να αλλάξει η τιμή της και
από άλλες ρουτίνες. Η ρουτίνα InitROT(void) ενεργοποιεί τις διακοπές στο κατερχόμενο μέτωπο
και προγραμματίζει τα PD2, PD3
εισόδους με ενεργοποιημένες τις εσωτερικές pull up αντιστάσεις. Οι δύο επόμενες
ρουτίνες, ISR(INT0_vect) και ISR(INT1_vect), εκτελούνται με την αντίστοιχη διακοπή.
Αν περιστρέψουμε τον άξονα κάποιο από τα Α, Β θα περάσει πρώτο σε λογικό 0 και
το κατερχόμενο μέτωπο θα προκαλέσει τη διακοπή INT0 ή INT1
ανάλογα με τη φορά περιστροφής.
Αν το Α προκαλέσει τη διακοπή αρχίζουν να εκτελούνται οι εντολές της ρουτίνας ISR(INT0_vect).
Σε περίπτωση που θέλουμε να υπάρχει δυνατότητα κάποια άλλη διακοπή να εμπλακεί
μπορούμε να ενεργοποιήσουμε τις διακοπές (στο παράδειγμα η εντολή είναι σε
σχόλιο). Εκτελείται αρχικά μια καθυστέρηση για τους παρασιτικούς παλμούς του διακόπτη
και στη συνέχεια το πρόγραμμα παραμένει στη while για όσο το Α
βρίσκεται σε λογικό 0. Καθώς συνεχίζει να περιστρέφεται ο άξονας
το Β θα περάσει σε λογικό 0 και αυτό πριν το Α περάσει σε λογικό 1
ξανά. Όταν το Α περάσει σε λογικό 1 το πρόγραμμα βγαίνει από τη while και
εκτελείται μια καθυστέρηση για τους παρασιτικούς παλμούς. Αν το Α είναι σε λογικό
1
και το Β σε λογικό 0 επιβεβαιώνεται τότε η φορά περιστροφής CW
και αυξάνει ο μετρητής i1. Το πρόγραμμα περιμένει να περάσει και το Β σε λογικό
1,
εκτελείται μια καθυστέρηση για τους παρασιτικούς παλμούς, ενεργοποιούνται ξανά
οι διακοπές αν είχαν απενεργοποιηθεί και βγαίνει από τη ρουτίνα διακοπής
δίνοντας τον έλεγχο στο κυρίως πρόγραμμα. Αν η περιστροφή είναι αντίθετη (CCW), το Β θα προκαλέσει τη διακοπή INT1
και θα αρχίσει να εκτελείται η ρουτίνα ISR(INT1_vect), η οποία εκτελεί ίδιες εντολές που όμως το Α
βρίσκεται στη θέση του Β και το αντίθετο και ελαττώνει τον μετρητή i1.
©2016 Πορλιδάς Δημήτριος
|