Rotary Encoder (ROT)

Υλοποίηση με εξωτερική διακοπή (IRQ) στον ATmega16

 

Συγγραφέας: Πορλιδάς Δημήτριος

Βιογραφικό Σημείωμα

electronics@porlidas.gr

Facebook

Linkedin


 

 

Ο 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 Πορλιδάς Δημήτριος

 


[1] Η άλλη κατηγορία μηχανικών κωδικοποιητών είναι absolute encoder.

 

Σας ευχαριστώ για την υποστήριξή σας ώστε να γίνει η ιστοσελίδα μου καλύτερη.

© 2017 Πορλιδάς Δημήτριος