Postanowiłem napisać artykuł na temat mulipleksacji. Stosujemy ją, gdy chcemy "zaoszczędzić" na pinach w mikrokontrolerze (arduino). Mechanizm ten wykorzystałem tworząc zegarek elektroniczny (patrz strona z projektami) i będę też wykorzystywał w innych projektach.
Budowa wyświetlacza
W swoich projektach wykorzystuję wyświetlacz AF-05643FS-B (nota katalogowa tutaj). Jest to wyświetlacz 7 segmentowy 4-cyfrowy o wspólnej anodzie (model zegarkowy z dwukropkiem rozdzielającym cyfry).
Jak widać, aby w pełni wysterować wyświetlacz, potrzebujemy 5 anod i 9 katod, co zajmuje 14 pinów. W projekcie z zegarkiem nie korzystałem ze znaku kropki (DP), co daje nam 5 anod i 8 katod do wysterowania (13 pinów). Mulipleksacja polega na zapaleniu jednej cyfry na określony, krótki czas (np. 3 ms), następnie przełączenie się na kolejną cyfrę i zapalenie jej. Ponieważ dzieje się to bardzo szybko, to oko ludzkie nie jest w stanie tego rozróżnić i widzi cyfry, tak jakby były zapalone wszystkie na raz.
Zgodnie z notą katalogową maksymalny prąd ciągły płynący przez segment to 20mA, ale rekomenduje się 12mA. Spadek napięcia na segmencie to 2V. Zgodnie z prawem Ohma rezystancja ograniczająca przepływający przez segment prąd powinna wynosić R=(5-2)V/0,012A=250 Ohm. Ja użyłem 330 Ohm, co daje prąd 9mA (wystarczający, aby było widać cyfry w padającym słońcu).
Podłączając anody do +5V decydujemy, którą cyfrę będziemy wysterowywać. Ponieważ zapalenie wszystkich 7 segmentów powoduje przepływ prądu 7x9=63mA, co jest o wiele za duże dla mikrokontrolera (max. 40 mA), czy układu 74HC595 (max. 35 mA), który użyłem (ale o tym później), to konieczne jest użycie tranzystora o odpowiednio dużym prądzie kolektora, który będzie sterował przepływem prądu do anody. Ja korzystałem z tranzystorów BC338 o maksymalnym prądzie kolektora 800mA.
int latchPin = 8;
//Pin connected to SCK of 74HC595
int clockPin = 12;
//Pin connected to SER of 74HC595
int dataPin = 11;
//holders for information you're going to pass to shifting function
byte dataCyfry;
byte dataPozycja;
byte dataArrayCyfry[12];
byte dataArrayPozycja[5];
unsigned long sekunda1, sekunda2;
unsigned long czas;
int i,j;
void setup() {
pinMode(latchPin, OUTPUT);
sekunda1=sekunda2=0;
i=0;
//definicja cyfr - konfiguracja segmentów na wyświetlaczu
dataArrayCyfry[0] = B11000000; //0
dataArrayCyfry[1] = B11111001; //1
dataArrayCyfry[2] = B10100100; //2
dataArrayCyfry[3] = B10110000; //3
dataArrayCyfry[4] = B10011001; //4
dataArrayCyfry[5] = B10010010; //5
dataArrayCyfry[6] = B10000010; //6
dataArrayCyfry[7] = B11111000; //7
dataArrayCyfry[8] = B10000000; //8
dataArrayCyfry[9] = B10010000; //9
dataArrayCyfry[10]= B01111111; //dwukropek on
dataArrayCyfry[11]= B11111111; //wszystkie segmenty off
dataArrayPozycja[0] = 0x01; //pozycja 1
dataArrayPozycja[1] = 0x02; //pozycja 2
dataArrayPozycja[2] = 0x08; //pozycja 3
dataArrayPozycja[3] = 0x10; //pozycja 4
dataArrayPozycja[4] = 0x04; //pozycja dwukropka
}
void loop() {
j=i/1000; // wyswietl wielokrotności tysiąca
dataCyfry = dataArrayCyfry[j];
dataPozycja = dataArrayPozycja[0];
Wyswietl();
j=i/100-10*(i/1000); // wyswietl ilość setek i odejmij od tego tysiące
dataCyfry = dataArrayCyfry[j];
dataPozycja = dataArrayPozycja[1];
Wyswietl();
j=i/10-10*(i/100); // jak wyżej, ale w odniesieniu do dziesiątek
dataCyfry = dataArrayCyfry[j];
dataPozycja = dataArrayPozycja[2];
Wyswietl();
j=i%10; // wyświetl jedności
dataCyfry = dataArrayCyfry[j];
dataPozycja = dataArrayPozycja[3];
Wyswietl();
czas=millis(); // zliczaj czas od uruchomienia układu
sekunda1=czas / 100; //odliczanie co 100 ms (0,1 sekundy)
if (sekunda2<sekunda1)
{
sekunda2=sekunda1;
i++;
if (i>9999) i=0;
}
// to jest zabezpieczenie na wypadek jakby się przepełniła zmienna 'czas' i znowu przyjęła wartość 0
if (sekunda1==0 and sekunda2!=0) sekunda2=0;
}
// the heart of the program
void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
// This shifts 8 bits out MSB first,
//on the rising edge of the clock,
//clock idles low
//internal function setup
int i=0;
int pinState;
pinMode(myClockPin, OUTPUT);
pinMode(myDataPin, OUTPUT);
//clear everything out just in case to
//prepare shift register for bit shifting
digitalWrite(myDataPin, 0);
digitalWrite(myClockPin, 0);
//for each bit in the byte myDataOut
//NOTICE THAT WE ARE COUNTING DOWN in our for loop
//This means that 000001 or "1" will go through such
//that it will be pin Q0 that lights.
for (i=7; i>=0; i--) {
digitalWrite(myClockPin, 0);
//if the value passed to myDataOut and a bitmask result
// true then... so if we are at i=6 and our value is
// %11010100 it would the code compares it to %01000000
// and proceeds to set pinState to 1.
if ( myDataOut & (1<<i) ) {
pinState= 1;
}
else {
pinState= 0;
}
//Sets the pin to HIGH or LOW depending on pinState
digitalWrite(myDataPin, pinState);
//register shifts bits on upstroke of clock pin
digitalWrite(myClockPin, 1);
//zero the data pin after shift to prevent bleed through
digitalWrite(myDataPin, 0);
}
//stop shifting
digitalWrite(myClockPin, 0);
}
void Wyswietl()
{
digitalWrite(latchPin, 0); // odblokuj zatrzask i zacznij ustawiać dane na wyjściach równoległych
shiftOut(dataPin, clockPin, dataCyfry); // zapisz 1 bajt danych do 74HC595
// zapisz 2 bajt danych do 74HC595, a pierwszy bajt danych idzie do drugiego układu 74HC595
shiftOut(dataPin, clockPin, dataPozycja);
// zatrzaśnij zatrzask - dane już są poprawnie zapisane na wyjściach równoległych Q
digitalWrite(latchPin, 1);
delay(3);
}
Rys.1 - Budowa wyświetlacza
Rys. 2 - Rozkład segmentów
Rys. 3 - Podłączenie anod i katod w wyświetlaczu
Jak widać, aby w pełni wysterować wyświetlacz, potrzebujemy 5 anod i 9 katod, co zajmuje 14 pinów. W projekcie z zegarkiem nie korzystałem ze znaku kropki (DP), co daje nam 5 anod i 8 katod do wysterowania (13 pinów). Mulipleksacja polega na zapaleniu jednej cyfry na określony, krótki czas (np. 3 ms), następnie przełączenie się na kolejną cyfrę i zapalenie jej. Ponieważ dzieje się to bardzo szybko, to oko ludzkie nie jest w stanie tego rozróżnić i widzi cyfry, tak jakby były zapalone wszystkie na raz.
Schemat i obliczenia
Poniżej przedstawiam schemat podłączeń jaki użyłem.
Zgodnie z notą katalogową maksymalny prąd ciągły płynący przez segment to 20mA, ale rekomenduje się 12mA. Spadek napięcia na segmencie to 2V. Zgodnie z prawem Ohma rezystancja ograniczająca przepływający przez segment prąd powinna wynosić R=(5-2)V/0,012A=250 Ohm. Ja użyłem 330 Ohm, co daje prąd 9mA (wystarczający, aby było widać cyfry w padającym słońcu).
Podłączając anody do +5V decydujemy, którą cyfrę będziemy wysterowywać. Ponieważ zapalenie wszystkich 7 segmentów powoduje przepływ prądu 7x9=63mA, co jest o wiele za duże dla mikrokontrolera (max. 40 mA), czy układu 74HC595 (max. 35 mA), który użyłem (ale o tym później), to konieczne jest użycie tranzystora o odpowiednio dużym prądzie kolektora, który będzie sterował przepływem prądu do anody. Ja korzystałem z tranzystorów BC338 o maksymalnym prądzie kolektora 800mA.
Sterowanie
Do sterowania tranzystorami oraz ustawiania napięć na katodach użyłem rejestrów przesuwnych 74HC595. Umożliwiają one wysterowanie 8 wyjść za pomocą tylko trzech wejść: wejscie oznaczone SER (inaczej 'A')(pin 14) to szeregowe wejście danych - dane na tym pinie podawane są na 8-bitowy rejestr przesuwny; SCK (Shift Clock) (pin 11)- wejście zegarowe sterujące przesuwaniem danych z wejścia SER do 8-bitowego rejestru przesuwnego; oraz RCK (Latch Clock) (pin 12)- zmiana ze stanu niskiego na wysoki na tym wejściu powoduje "zatrzaśnięcie" danych rejestru przesuwnego i pojawiają się one na wyjściach równoległych Q(A)-Q(H). Jeden układ 74HC595 posiada tylko 8 wyjść, ale łącząc układy szeregowo ze sobą możemy obsłużyć dowolną liczbę wyjść. W tym celu wyjście szeregowe Q(H*) (inaczej SQ(H)) (pin 9) pierwszego układu łączymy z wejściem SER (A) (pin 14) kolejnego. Na wejście RESET (SCL) (pin 10) podajemy 5V, a wejście OUTPUT ENABLE (G)(pin 13) zwieramy do masy GND.
//Pin connected to RCK of 74HC595Program
int latchPin = 8;
//Pin connected to SCK of 74HC595
int clockPin = 12;
//Pin connected to SER of 74HC595
int dataPin = 11;
//holders for information you're going to pass to shifting function
byte dataCyfry;
byte dataPozycja;
byte dataArrayCyfry[12];
byte dataArrayPozycja[5];
unsigned long sekunda1, sekunda2;
unsigned long czas;
int i,j;
void setup() {
pinMode(latchPin, OUTPUT);
sekunda1=sekunda2=0;
i=0;
//definicja cyfr - konfiguracja segmentów na wyświetlaczu
dataArrayCyfry[0] = B11000000; //0
dataArrayCyfry[1] = B11111001; //1
dataArrayCyfry[2] = B10100100; //2
dataArrayCyfry[3] = B10110000; //3
dataArrayCyfry[4] = B10011001; //4
dataArrayCyfry[5] = B10010010; //5
dataArrayCyfry[6] = B10000010; //6
dataArrayCyfry[7] = B11111000; //7
dataArrayCyfry[8] = B10000000; //8
dataArrayCyfry[9] = B10010000; //9
dataArrayCyfry[10]= B01111111; //dwukropek on
dataArrayCyfry[11]= B11111111; //wszystkie segmenty off
dataArrayPozycja[0] = 0x01; //pozycja 1
dataArrayPozycja[1] = 0x02; //pozycja 2
dataArrayPozycja[2] = 0x08; //pozycja 3
dataArrayPozycja[3] = 0x10; //pozycja 4
dataArrayPozycja[4] = 0x04; //pozycja dwukropka
}
void loop() {
j=i/1000; // wyswietl wielokrotności tysiąca
dataCyfry = dataArrayCyfry[j];
dataPozycja = dataArrayPozycja[0];
Wyswietl();
j=i/100-10*(i/1000); // wyswietl ilość setek i odejmij od tego tysiące
dataCyfry = dataArrayCyfry[j];
dataPozycja = dataArrayPozycja[1];
Wyswietl();
j=i/10-10*(i/100); // jak wyżej, ale w odniesieniu do dziesiątek
dataCyfry = dataArrayCyfry[j];
dataPozycja = dataArrayPozycja[2];
Wyswietl();
j=i%10; // wyświetl jedności
dataCyfry = dataArrayCyfry[j];
dataPozycja = dataArrayPozycja[3];
Wyswietl();
czas=millis(); // zliczaj czas od uruchomienia układu
sekunda1=czas / 100; //odliczanie co 100 ms (0,1 sekundy)
if (sekunda2<sekunda1)
{
sekunda2=sekunda1;
i++;
if (i>9999) i=0;
}
// to jest zabezpieczenie na wypadek jakby się przepełniła zmienna 'czas' i znowu przyjęła wartość 0
if (sekunda1==0 and sekunda2!=0) sekunda2=0;
}
// the heart of the program
void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
// This shifts 8 bits out MSB first,
//on the rising edge of the clock,
//clock idles low
//internal function setup
int i=0;
int pinState;
pinMode(myClockPin, OUTPUT);
pinMode(myDataPin, OUTPUT);
//clear everything out just in case to
//prepare shift register for bit shifting
digitalWrite(myDataPin, 0);
digitalWrite(myClockPin, 0);
//for each bit in the byte myDataOut
//NOTICE THAT WE ARE COUNTING DOWN in our for loop
//This means that 000001 or "1" will go through such
//that it will be pin Q0 that lights.
for (i=7; i>=0; i--) {
digitalWrite(myClockPin, 0);
//if the value passed to myDataOut and a bitmask result
// true then... so if we are at i=6 and our value is
// %11010100 it would the code compares it to %01000000
// and proceeds to set pinState to 1.
if ( myDataOut & (1<<i) ) {
pinState= 1;
}
else {
pinState= 0;
}
//Sets the pin to HIGH or LOW depending on pinState
digitalWrite(myDataPin, pinState);
//register shifts bits on upstroke of clock pin
digitalWrite(myClockPin, 1);
//zero the data pin after shift to prevent bleed through
digitalWrite(myDataPin, 0);
}
//stop shifting
digitalWrite(myClockPin, 0);
}
void Wyswietl()
{
digitalWrite(latchPin, 0); // odblokuj zatrzask i zacznij ustawiać dane na wyjściach równoległych
shiftOut(dataPin, clockPin, dataCyfry); // zapisz 1 bajt danych do 74HC595
// zapisz 2 bajt danych do 74HC595, a pierwszy bajt danych idzie do drugiego układu 74HC595
shiftOut(dataPin, clockPin, dataPozycja);
// zatrzaśnij zatrzask - dane już są poprawnie zapisane na wyjściach równoległych Q
digitalWrite(latchPin, 1);
delay(3);
}




Brak komentarzy:
Prześlij komentarz