sobota, 16 sierpnia 2014

Mulipleksacja

Witam.
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).

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.

Program

Oto program, który używałem (link). Program wyświetla liczby od 0 do 9999.

//Pin connected to RCK of 74HC595
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