Seven Segment Display - Πολυπλεγμένη λειτουργία

 

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

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

electronics@porlidas.gr

Facebook

Linkedin


 

 

Τα Seven Segment Display με LED είναι ίσως η πιο συνηθισμένη μέθοδος απεικόνισης αριθμών. Η διακριτική τους ικανότητα, η υψηλή φωτεινότητα σε συνάρτηση με το μεγάλο μέγεθος, την ποικιλία των χρωματικών αποχρώσεων και την μικρή κατανάλωση ρεύματος τα κάνει ελκυστικά σε διάφορες εφαρμογές. Το κάθε display μπορεί να απεικονίσει τους αριθμούς και ορισμένα γράμματα[1] με συνδυασμό επτά LED σε κατάλληλη διάταξη και ένα όγδοο για την τελεία (ως υποδιαστολή ή ένα διπλό για άνω – κάτω τελεία). Το κάθε LED χαρακτηρίζεται από ένα γράμμα και το ένα άκρο του είναι ελεύθερο σε ακροδέκτη ενώ το άλλο συνδέεται εσωτερικά με τα υπόλοιπα σε έναν κοινό ακροδέκτη. Ο κοινός ακροδέκτης μπορεί να είναι οι άνοδοι ή οι κάθοδοι των LED και με αυτόν τον τρόπο χαρακτηρίζεται ο τύπος του display, κοινής ανόδου ή καθόδου αντίστοιχα. Οι αριθμοί σχηματίζονται αν ανάψουν συγκεκριμένα LED δίνοντας τάση με κατάλληλη πόλωση (μια αντίσταση περιορισμού του ρεύματος για κάθε είναι απαραίτητη). Για να σχηματιστεί το 2 για παράδειγμα πρέπει να ανάψουν τα a, b, g, e, d.

Υπάρχουν πολλά ηλεκτρονικά κυκλώματα που υποστηρίζουν Seven Segment Display καθώς και ολοκληρωμένα όπως τα CD4511 και MC14495, τα οποία είναι αποκωδικοποιητές BCD και Hexadecimal σε Seven Segment Display (with latch) αντίστοιχα, ή το MAX6954, το οποίο είναι προγραμματιζόμενος αποκωδικοποιητής με κύκλωμα πολύπλεξης. Επίσης μπορεί να γίνει χρήση μικροελεγκτή για αποκωδικοποίηση και απεικόνιση. Οι απαιτήσεις σε λογισμικό είναι ελάχιστες καθώς δε χρειάζονται ειδικές βιβλιοθήκες και ο κώδικας περιορίζεται σε λίγες μόνο εντολές, με αποτέλεσμα να μπορεί να χρησιμοποιηθεί ο ίδιος ο μικροελεγκτής που εκτελεί την εφαρμογή η οποία χρειάζεται συσκευή απεικόνισης, αρκεί να έχει ελεύθερες εξόδους.

Αν χρησιμοποιήσουμε περισσότερα από ένα Seven Segment Display τα σήματα προς αυτά είναι πολυπλεγμένα. Συνδέονται μεταξύ τους παράλληλα, εκτός από την κοινή κάθοδο (ή άνοδο), η οποία ελέγχεται μέσω ενός τρανζίστορ – διακόπτη ξεχωριστά για το κάθε display. Ένα μόνο display ενεργοποιείται κάθε φορά μέσω του τρανζίστορ εμφανίζοντας το νούμερο που αντιστοιχεί στην κατάλληλη θέση. Η εναλλαγή γίνεται με υψηλή ταχύτητα ώστε το ανθρώπινο μάτι να αντιλαμβάνεται όλα τα ψηφία αναμμένα. Η τεχνική αυτή απλουστεύει το σχεδιασμό αφού χρειάζονται λιγότερες έξοδοι (8 για τα 7 segment και την τελεία και από μια για κάθε ένα display). Αν χρησιμοποιήσουμε εξωτερικούς αποκωδικοποιητές χρειάζονται ακόμα λιγότεροι έξοδοι, για παράδειγμα με έναν αποκωδικοποιητή BCD to Seven Segment Display (74LS347) και έναν αποκωδικοποιητή 4-Line to 16-Line Decoder/Demultiplexer (74LS154) αρκούν 8 έξοδοι (ένα port) για 16 display. Ο έλεγχος γίνεται σε όλες τις περιπτώσεις από τον μικροελεγκτή.  Ένα τυπικό κύκλωμα οθόνης με 4 Seven Segment Display τύπου LED παρουσιάζεται στο παρακάτω σχήμα.

Για την υποστήριξη του παραπάνω κυκλώματος χρειάζεται να χρησιμοποιηθεί ένας Timer για την εναλλαγή των display σε σταθερά χρονικά διαστήματα, ώστε είναι σταθερή η φωτεινότητα και να μη γίνεται αισθητή διακοπή ή τρεμοπαίξιμο. Με αυτόν τον τρόπο η εναλλαγή είναι ανεξάρτητη από την εκτέλεση του προγράμματος. Οι απαραίτητες εντολές για την εναλλαγή βρίσκονται στο διάνυσμα της διακοπής που προκαλείται από τον Timer με αποτέλεσμα στον κυρίως κώδικα να χρειάζεται μόνο να τροποποιείται η μεταβλητή που χρησιμοποιείται ως buffer για τα display. Στη συνέχεια παρουσιάζεται ένα παράδειγμα γραμμένο σε γλώσσα προγραμματισμού C:

#include <avr/io.h>

#include <util/delay.h>

#include <avr/interrupt.h>

 

uint8_t test[]={'0','1','2','3','4','5','6','7','8','9','A','b','C','d','E','F','-','.',' ',0};

uint8_t buffer[4];

uint8_t display[4];

uint8_t i1=0;

uint8_t digit=0;

 

void ato7sd (unsigned char buffer2)      //ASCII to 7 Seg Disp conversion routine

{

switch (buffer2)

{

case '0':

display[i1] = 0b00111111;        //FEDCBA      ABCDEF

break;

             

case '1':

display[i1] = 0b00000110;        //CB          CB

break;

             

case '2':

display[i1] = 0b01011011;        //GEDBA       ABDEG

break;

 

case '3':

display[i1] = 0b01001111;        //GDCBA       ABCDG

break;

 

case '4':

display[i1] = 0b01100110;        //GFCB        BCFG

break;

 

case '5':

display[i1] = 0b01101101;        //GFDCA       ACDFG

break;

 

case '6':

display[i1] = 0b01111101;        //GFEDCA      ACDEFG

break;

 

case '7':

display[i1] = 0b00000111;        //CBA         ABC

break;

 

case '8':

display[i1] = 0b01111111;        //GFEDCBA     ABCDEFG

break;

 

case '9':

display[i1] = 0b01101111;        //GFDCBA      ABCDFG

break;

 

case '.':

display[i1] = 0b10000000;        //H

break;

 

case '-':

display[i1] = 0b01000000;        //G

break;

 

case 'A':

display[i1] = 0b01110111;        //GFECBA      ABCEFG

break;

 

case 'b':

display[i1] = 0b01111100;        //GFEDC       CDEFG

break;

 

case 'C':

display[i1] = 0b00111001;        //FEDA        ADEF

break;

 

case 'd':

display[i1] = 0b01011110;        //GEDCB       BCDEG

break;

 

case 'E':

display[i1] = 0b01111001;        //GFEDA       ADEFG

break;

 

case 'F':

display[i1] = 0b01110001;        //GFEA        AEFG

break;

 

case 'o':

display[i1] = 0b01011100;        //GEDC        CDEG

break;

 

case 'r':

display[i1] = 0b01010000;        //GE          EG

break;

 

case ' ':

display[i1] = 0;

break;

}

}

 

ISR(TIMER1_COMPA_vect)

{

if (i1 >= 4)                  //For 4 displays

{

i1 = 0;                   //Reset display number after 4th display

digit = 1;                //Reset display number after 4th display

}

ato7sd(buffer[i1]);           //Call ASCII to 7 Seg Disp conversion routine

PORTB = 0;                    //Turn off display

PORTA = display[i1];          //Send 7 Seg Disp format character to port

PORTB = digit;                //Turn on display

i1++;                         //Shift display in next cycle

digit = digit<<1;             //Shift display in next cycle

}

 

int main(void)

{

DDRA = 0b11111111;            //Segment port all outputs

DDRB = 0b00001111;            //Displays port all outputs (PORTx0:3)

 

TCCR1B |= (1 << WGM12);       //Configure timer 1 for CTC mode

TCCR1B |= (3 << CS10);        //Start timer at Fcpu/64

OCR1A = 500;                  //Set CTC compare for 4ms at 8MHz AVR clock

TIMSK |= (1 << OCIE1A);       //Enable CTC interrupt

sei();                        //Enable global interrupts

      

while (1)

{

for (uint8_t i2 = 0; i2 < 17; i2++)

{

buffer[0]=test[i2];

buffer[1]=test[i2+1];

buffer[2]=test[i2+2];

buffer[3]=test[i2+3];

_delay_ms(500);

}

             

}

}

Τα segment των display είναι συνδεδεμένα στο PORTA του μικροελεγκτή ενώ οι κοινές κάθοδοι, μέσω τρανζίστορ – διακοπτών, είναι συνδεδεμένες στο PORTB3:0, όπως δείχνεται στο θεωρητικό κύκλωμα. Η βιβλιοθήκη avr/interrupt.h είναι απαραίτητη για τη λειτουργία των διακοπών. Η μεταβλητή test[] είναι ένας πίνακας που χρησιμοποιείται μόνο για την επίδειξη των χαρακτήρων. Η μεταβλητή buffer[4] χρησιμοποιείται ως πίνακας που τοποθετούνται σε κωδικοποίηση ASCII οι χαρακτήρες που εμφανίζονται στα display. Η μεταβλητή display[4] χρησιμοποιείται ως πίνακας που τοποθετούνται σε κωδικοποίηση Seven Segment Display οι χαρακτήρες που εμφανίζονται στα display, ώστε να οδηγηθούν στη συνέχεια στο PORTA. Η μεταβλητή i χρησιμοποιείται για την απαρίθμηση των display κατά την εναλλαγή τους και η μεταβλητή digit χρησιμοποιείται για την εναλλαγή των display.

Στην αρχή του κώδικα εμφανίζεται η ρουτίνα ato7sd η οποία αναλαμβάνει τη μετατροπή της κωδικοποίησης των χαρακτήρων από ASCII σε Seven Segment Display. Περιέχει μια εντολή switch όπου στις διάφορες case αποθηκεύεται, στο στοιχείο της μεταβλητής display[i], η τιμή που πρέπει να οδηγηθεί στο PORT όταν θα είναι η σειρά του αντίστοιχου ψηφίου να εμφανιστεί.

Στη συνέχεια, στο μπλοκ κάτω από τον τίτλο του διανύσματος ISR(TIMER1_COMPA_vect), βρίσκεται ο κώδικας που εκτελείται από το διάνυσμα της διακοπής του Timer. Αρχικά γίνεται έλεγχος της μεταβλητής i και μηδενισμός της μετά την τιμή 3 (επιτρεπτές τιμές 0, 1, 2, 3 για χρήση τεσσάρων display αριθμημένα από αριστερά προς δεξιά). Στη συνέχεια γίνεται κλήση της ρουτίνας ato7sd όπου είσοδος της ρουτίνας είναι η μεταβλητή buffer[i] και έξοδος η μεταβλητή display[i]. Με την επόμενη εντολή σβήνουν όλα τα display ώστε να προετοιμαστεί η αλλαγή τους, η οποία γίνεται στη συνέχεια αποδίδοντας στο PORTA την τιμή της display[i] και στο PORTB η τιμή της digit ώστε να ανάψει το νέο display με την αντίστοιχη τιμή του. Ακολουθεί η προετοιμασία για τον επόμενο κύκλο με αύξηση της i και αριστερή ολίσθηση της digit. Το σβήσιμο των display είναι απαραίτητο, γιατί δεν είναι δυνατό να φορτωθούν ταυτόχρονα και στα δύο PORT οι νέες τιμές με αποτέλεσμα να φαίνεται αχνά στο display η τιμή του επόμενου (ή του προηγούμενου, ανάλογα με το ποιο PORT θα ενημερωθεί πρώτο).

Ακολουθεί το κυρίως μέρος του προγράμματος int main(void). Προγραμματίζονται τα δύο PORT ως έξοδοι, ρυθμίζεται ο Timer/Counter1A για λειτουργία CTC με prescaler Fcpu/64 και όριο σύγκρισης 500 και ενεργοποιούνται η διακοπή CTC και οι γενικές διακοπές. Το μπλοκ της while(1) περιέχει κώδικα μόνο για επίδειξη των display και δε χρειάζεται να συμπεριληφθεί στις διάφορες εφαρμογές (ομοίως και η μεταβλητή test[]). Για να εμφανιστεί ένας χαρακτήρας στο κατάλληλο ψηφίο πρέπει να γίνει απόδοση της τιμής του σε κωδικοποίηση ASCII στην αντίστοιχη θέση της μεταβλητής buffer[i] κάπου μέσα στο μπλοκ της while(1), σε οποιοδήποτε σημείο θέλουμε του κώδικά μας.

Η κλήση της ρουτίνας ato7sd δεν είναι υποχρεωτικό να καλείται μέσα από το διάνυσμα της διακοπής, αλλά μπορεί να καλείται μέσα από τον κυρίως κώδικα κάθε φορά που θέλουμε να αλλάξουμε τιμή σε κάποιο ψηφίο. Είναι δυνατό μάλιστα να αφαιρεθεί και η μεταβλητή buffer[i] αν ως είσοδο της ato7sd δίνουμε σε κωδικοποίηση ASCII τον επιθυμητό χαρακτήρα. Με αυτόν τον τρόπο ελαττώνεται το μέγεθος του κώδικα και ελαφραίνει το φορτίο του μικροελεγκτή, αφού δεν εκτελεί τη ρουτίνα σε κάθε διακοπή. Αυξάνει όμως η πολυπλοκότητα του προγράμματος στον κυρίως κώδικα, αφού θα πρέπει πάντα να θυμόμαστε να καλούμε τη ρουτίνα για να αποδώσουμε νέα τιμή. Επίσης γίνεται δυσκολότερη η φορητότητα του κώδικα πολύπλεξης, αφού θα πρέπει να προσαρμόζεται στις νέες απαιτήσεις σε περισσότερα σημεία.

Η επιλογή του prescaler και του ορίου σύγκρισης έγινε ώστε να είναι κάθε display αναμμένο 4ms και η συχνότητα σάρωσης να είναι λίγο πάνω από 60Hz, ώστε να μην είναι ορατή η εναλλαγή και να είναι ικανοποιητική η φωτεινότητα των display.       

 

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

 


[1] Μπορούν να απεικονιστούν όλοι οι αριθμοί και γράμματα όπως τα A, b, C, c, d, E, F, H, i, J, L, n, o, P, r, t, U, u.

 

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

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