[016] Wyświetlacz siedmiosegmentowy

[016] Wyświetlacz siedmiosegmentowy

Na płytce edukacyjnej TME, do ekspandera, o którym pisałem w poprzednim artykule, podłączono wyświetlacz siedmiosegmentowy. Cóż to jest? To siedem, a właściwie osiem diod świecących, ułożonych w kształcie ósemki z towarzyszącą kropką. Segmenty mają ustaloną kolejność, którą opisuje się literami alfabetu. Każdy zna doskonale świecące cyferki o charakterystycznym kroju. Powstają na skutek włączania wybranych segmentów, czyli wystawiania tam stanów wysokich (albo niskich – w zależności od budowy konkretnego wyświetlacza).


Rzućmy okiem na schemat. Ekspander posiada osiem wyjść, a więc akurat tyle, ile potrzeba dla jednego takiego wyświetlacza. Połączono tutaj te wyjścia z poszczególnymi segmentami przez rezystory ograniczające prąd do bezpiecznej zarówno dla diod świecących, jak i dla układu wartości. Przygotowałem program, który będzie wyświetlał cyferki od zera do dziewięciu. Wykorzystam przy tym dwa przyciski: jeden będzie zwiększał wartość wyświetlaną, drugi – zmniejszał. A więc do dzieła.

#include <Adafruit_MCP23008.h>  // Dołącz bibliotekę sterującą układem MCP23008
Adafruit_MCP23008 ekspander;    // Nadaj układowi nazwę "ekspander"

const byte pstryczekMinus = 4;  // Adres pstryczka zmniejszającego stan licznika.
const byte pstryczekPlus = 7;   // Adres pstryczka zwiększającego stan licznika.

byte licznik;      // Licznik do wyświetlenia na wyświetlaczu siedmiosegmentowym.
byte wyswietlacz;  // Bity - segmenty wyświetlacza, reprezentujące kolejne cyfry.
byte x;            // Zmienna ogólnego przeznaczenia

void setup() {
  pinMode(pstryczekMinus, INPUT_PULLUP);  // Deklaruj linie pstryczków jako wejścia podciągnięte wewnętrznie do wysokiego stanu.
  pinMode(pstryczekPlus, INPUT_PULLUP);
  ekspander.begin(36);       // Inicjuj bibliotekę sterującą układem MCP23008 pod adresem 0x4
  for (x = 0; x < 8; x++) {  // Zadeklaruj wszystkie porty jako wyjścia.
    ekspander.pinMode(x, OUTPUT);
  }
}

void loop() {
  if (digitalRead(pstryczekPlus) == HIGH && licznik < 9) {  // Jeśli wciśnięto pstyczek plus, a licznik jest mniejszy od 9...
    licznik++;                                              // Zwiększ wartość licznika.
    wyswietl();                                             // Wyświetl stan licznika na wyświetlaczu siedmiosegmentowym.
    while (digitalRead(pstryczekPlus) == HIGH) {}           // Czekaj dopóki przycisk nie zostanie zwolniony.
  }

  if (digitalRead(pstryczekMinus) == HIGH && licznik > 0) {  // Jeśli wciśnięto pstyczek plus, a licznik jest mniejszy od 9...
    licznik--;                                               // Zmniejsz wartość licznika.
    wyswietl();                                              // Wyświetl stan licznika na wyświetlaczu siedmiosegmentowym.
    while (digitalRead(pstryczekMinus) == HIGH) {}           // Czekaj dopóki przycisk nie zostanie zwolniony.
  }
}

void wyswietl() {     // Procedura wyświetlająca wartość licznika na wyświetlaczu siedmiosegmentowym.
  switch (licznik) {  // Zestaw bity - segmenty wyświetlacza, reprezentujące kolejne cyfry ze stanem licznika.
    case 0:
      wyswietlacz = B00111111;  // Połączenia: P-G-F-E-D-C-B-A
      break;
    case 1:
      wyswietlacz = B00000110;
      break;
    case 2:
      wyswietlacz = B01011011;
      break;
    case 3:
      wyswietlacz = B01001111;
      break;
    case 4:
      wyswietlacz = B01100110;
      break;
    case 5:
      wyswietlacz = B01101101;
      break;
    case 6:
      wyswietlacz = B01111101;
      break;
    case 7:
      wyswietlacz = B00000111;
      break;
    case 8:
      wyswietlacz = B01111111;
      break;
    case 9:
      wyswietlacz = B01101111;
      break;
  }
  for (x = 0; x < 8; x++) {  // Ustaw każdy bit - segment wyświetlacza
    ekspander.digitalWrite(x, bitRead(wyswietlacz, x));
  }
}

Początek już znamy z poprzedniego artykułu. pstryczekMinus i pstryczekPlus to przyciski, o których pisałem przed chwilą. licznik będzie liczbą, którą będę wyświetlał, wyswietlacz – graficznym reprezentantem tej liczby na wyświetlaczu, x natomiast to dodatkowa zmienna, która mi się przyda w kilku miejscach.

Następnie deklaruję sobie porty przycisków jako wejścia i inicjuję ekspander – o tym też już kiedyś pisałem. Teraz muszę ustawić wszystkie porty ekspandera jako wyjścia. Ale zamiast powtarzać tę instrukcję osiem razy, dla każdego portu po kolei, zrobię to w pętli, wykorzystując wspomnianą zmienną x:

for (x = 0; x < 8; x++) {  // Zadeklaruj wszystkie porty jako wyjścia.
  ekspander.pinMode(x, OUTPUT);
}

Z taką pętlą jeszcze nie spotkaliśmy się, więc zatrzymam się tutaj na chwilę. Pętla for była niegdyś bardzo często wykorzystywana, jeszcze w czasach komputerów ośmiobitowych z lat osiemdziesiątych. Jaką ma budowę? Zaczyna się oczywiście od słowa for, a następnie, w nawiasie okrągłym mamy trzy argumenty: startu, zakończenia i przyrostu. W tym wypadku określamy sobie zmienną x, która w momencie wejścia w pętle przybiera wartość startową równą zero. Za wymienionymi argumentami znajduje się nawias klamrowy i tam już można umieszczać dowolne instrukcje, które będą się powtarzać w pętli for.

I właśnie drugi argument w nawiasie okrągłym mówi o tym ile razy będzie się powtarzać: dopóki będzie mniejsze od ośmiu. Trzeci argument, czyli przyrost określi o ile x wzrośnie za każdym przebiegiem pętli. Zwykle przyjmuje się, że będzie się zwiększać o jeden, bo to jest po prostu naturalne. Ale wcale nie musi tak być, może zwiększać się o dwa, o jakiś ułamek, a nawet zmniejszać się.

Dla zwiększeń o jeden, co w pełnej formie brzmi: x = x + 1 powstał skrót: x++ Analogiczny skrót powstał dla przyrostu ujemnego o jeden, oczywiście ze znakami minus. Informatycy lubią skróty i trzeba na nie uważać, bo jak się ich nie zna, to nie wiadomo o co chodzi.

W nawiasie klamrowym będziemy wysyłać do ekspandera rozkazy ustawienia kolejnych pinów jako wyjścia. I tutaj x przyda się znowu, bowiem w pętli przyjmuje kolejne wartości od zera do siedmiu, a to akurat ni mniej, ni więcej, tylko adresy pinów. Zatem x pełni rolę podwójną: licznika pętli for oraz adresów pinów ekspandera i taka konstrukcja tej pętli zdarza się bardzo często.

Skoro już wiemy jak działa for, przejdźmy dalej. W głównej pętli będą badane dwa warunki. Jeśli naciśnięto pstryczekPlus, należy zwiększyć licznik, jeśli pstryczekMinus – zmniejszyć. Ale tylko pod drugim jeszcze warunkiem: licznik zwiększać można tylko wtedy, gdy jest mniejszy od 9, a zmniejszać – gdy jest większy od 0. A to dlatego, że chcemy wyświetlać wyłącznie wartości z przedziału od 0 do 9. Gdybyśmy przekroczyli ten zakres, mogłyby się nam wyświetlić głupoty albo stan wyświetlacza nie zmieniłby się, a my nie wiedzielibyśmy dlaczego.

if (digitalRead(pstryczekPlus) == HIGH && licznik < 9) {  // Jeśli wciśnięto pstyczek plus, a licznik jest mniejszy od 9...
  licznik++;                                              // Zwiększ wartość licznika.
  wyswietl();                                             // Wyświetl stan licznika na wyświetlaczu siedmiosegmentowym.
  while (digitalRead(pstryczekPlus) == HIGH) {}           // Czekaj dopóki przycisk nie zostanie zwolniony.
}

Przypomnę jeszcze, o czym kiedyś już pisałem: do łączenia warunków zależnych używamy dwóch znaków & Dwa plusy – to już poznaliśmy – oznaczają tyle, co zwiększenie zmiennej o jeden. Następnie wyskoczymy do podprogramu wyświetlającego cyferki, a na końcu będziemy czekać na puszczenie przycisku. Teoretycznie powinienem był tu dodać jeszcze pętle opóźniające, zatrzymujące analizy na czas ustabilizowania się styków przycisków, ale przypomnę: na płytce edukacyjnej TME znajdują się przerzutniki Schmitta. O ich pozytywnym działaniu na stabilność pracy klawiatur pisałem w artykule o klawiaturach, więc ciekawych odsyłam tam, a tutaj tylko dodam, że całość pracuje pewnie bez jakichkolwiek pętli opóźniających. Zajrzyjmy do procedury wyświetlania, bo to jest najciekawsze.

void wyswietl() {     // Procedura wyświetlająca wartość licznika na wyświetlaczu siedmiosegmentowym.
  switch (licznik) {  // Zestaw bity - segmenty wyświetlacza, reprezentujące kolejne cyfry ze stanem licznika.
    case 0:
      wyswietlacz = B00111111;  // Połączenia: P-G-F-E-D-C-B-A
      break;
    case 1:
      wyswietlacz = B00000110;
      break;
    case 2:
      wyswietlacz = B01011011;
      break;
    case 3:
      wyswietlacz = B01001111;
      break;
    case 4:
      wyswietlacz = B01100110;
      break;
    case 5:
      wyswietlacz = B01101101;
      break;
    case 6:
      wyswietlacz = B01111101;
      break;
    case 7:
      wyswietlacz = B00000111;
      break;
    case 8:
      wyswietlacz = B01111111;
      break;
    case 9:
      wyswietlacz = B01101111;
      break;
  }
  for (x = 0; x < 8; x++) {  // Ustaw każdy bit - segment wyświetlacza
    ekspander.digitalWrite(x, bitRead(wyswietlacz, x));
  }
}

Będziemy mieć tutaj funkcję warunkową, która zadeklaruje wartość zmiennej wyswietlacz według stanu zmiennej licznik. W każdym wypadku owa zmienna przyjmie taką wartość, by zaświecić te segmenty, które reprezentują daną liczbę. Dlatego jej format jest binarny. Format nie ma żadnego znaczenia dla programu, ale ma duże dla naszej czytelności. Przyjrzyjmy się jedynce: w tej cyfrze muszą być zapalone jedynie segmenty B i C.

Segmenty w zmiennej wyswietlacz liczymy od tyłu: zatem A jest zgaszony, B i C – zapalone, a potem już do końca wszystko jest pogaszone.

case 1:
  wyswietlacz = B00000110;

Dla ósemki wszystkie segmenty świecą się. Z wyjątkiem ostatniego, bo to jest kropka dziesiętna.

case 8:
  wyswietlacz = B01111111;

I tak dalej. Projektowanie własnych znaków nie jest trudne, jeśli zrozumie się powiązanie segmentów z bitami i zaraz do tego wrócę. Dodam jeszcze, że wartości binarne określa się w ten sposób, że na początku należy postawić literę B.

Zestaw naszych dziesięciu warunków, czyli przyporządkowań licznikowi zmiennej określającej wygląd reprezentanta owego licznika można złożyć z dziesięciu instrukcji if. Istnieje inna instrukcja, która do tego służy, którą stosuje się w wypadku dużej ilości warunków wykorzystujących tę samą zmienną i takiej tu użyłem. Zaczyna się ona od słowa switch, a następnie w nawiasie okrągłym występuje zmienna – w naszym przypadku licznik. Za nią otwiera się nawias klamrowy i w nim siedzi cały szereg instrukcji case wraz z konkretną już wartością zmiennej – tutaj licznik. Po dwukropku należy umieścić instrukcje, które mają się wykonać tylko dla tej konkretnej wartości zmiennej (czyli licznika). Ostatnią instrukcją przed kolejnym case winna być instrukcja break, która nakazuje porzucenie dalszych analiz case i wyjście z pętli switch. Można jej nie wpisywać, ale niepotrzebnie wydłuża to pracę programu.

Na końcu można także dołożyć instrukcję default: która obejmie wszelkie inne warunki, które nie pojawiły się w żadnym case. U nas tak nie będzie, ale czasem nie jest się w stanie przewidzieć każdej analizowanej wartości zmiennej takich pętli, więc warto zabezpieczyć się na wypadki nieokreślone z góry.

Do tych instrukcji nieraz jeszcze będę wracał, więc jak coś jest niejasne, to będzie okazja o tym jeszcze porozmawiać. Gdy już zmienna wyswietlacz, dzięki któremuś z case, przyjmie konkretną wartość, kolejne jej bity zostaną przesłane do kolejnych portów na samym końcu programu:

for (x = 0; x < 8; x++) {  // Ustaw każdy bit - segment wyświetlacza
  ekspander.digitalWrite(x, bitRead(wyswietlacz, x));
}

Także tutaj zamiast ośmiu takich samych funkcji, wykorzystałem pętlę ze zmienną x, która powtórzy się osiem razy. Za każdym razem będę wybierał kolejny bit ze zmiennej wyswietlacz instrukcją bitRead i wysyłał go pod jeden z ośmiu adresów ekspandera. Zauważmy, że x pełni tutaj trzy zadania: licznika przebiegów pętli, adresów kolejnych pinów ekspandera i adresu stanu kolejnego segmentu wyświetlacza. Programowanie bowiem zwykle polega na takim właśnie złożonym wykorzystywaniu zmiennych, które same będąc manipulowane jakimś algorytmem, równocześnie manipulują jakimś zupełnie niezależnym zbiorem danych.

Dzisiejszy artykuł może być nieco zagmatwany, ale do tego tematu będę jeszcze wielokrotnie powracał. Poza tym tak naprawdę używanie funkcji switch do zadań takich jak w tym szkicu jest mało eleganckie. Ale o tym napiszę kolejną historię.

Płytka edukacyjna TME-EDU-ARD-2Płytka edukacyjna TME-EDU-ARD-2Sprawdź tutaj

Przeczytaj również

Nasi partnerzy

TMETech Master EventTME EducationPoweredby
Copyright © 2024 arduino.pl