[017] Wyświetlacze i tablice

[017] Wyświetlacze i tablice

W ostatnim artykule przedstawiłem sposób na wyłuskiwanie kształtów cyferek za pomocą rozbudowanej instrukcji switch – case. Pisałem wtedy także o tym, że sposób ten upraszcza pierwotną metodę, w której mamy szereg warunków if. To poniekąd racja i jeśli zadaniem miało być wyświetlenie jednej z dziesięciu cyferek, wspomniane „wyłuskanie” kształtu wymagałoby dziesięciu instrukcji if. Ale switch – case wcale znowu tak wszystkiego nie ułatwia, wszak elementów case nadal mamy dziesięć. Zatem uproszczenie nastąpiło, ale tak jakby połowicznie.


Tak naprawdę tamta instrukcja przydaje się wtedy, gdy skutki zaistnienia konkretnych wartości zmiennych warunku są różne. Prześledźmy sobie przykład praktyczny. Robimy makietę skrzyżowania z samochodzikiem. Samochodzik zbliża się do sygnalizatora świetlnego, który może wyświetlić szereg sygnałów. Więc po kolei:

  1. Sygnał czerwony – samochodzik powinien zwolnić i zatrzymać się.

  2. Sygnał żółty, ale wcześniej był czerwony – samochodzik może przygotować się do odjazdu, wrzucając bieg i zwiększając obroty przy wciśniętym jeszcze sprzęgle.

  3. Sygnał zielony – samochodzik powinien przejechać skrzyżowanie, czyli nic nie powinno się zmienić.

  4. Sygnał żółty, gdy wcześniej był zielony – samochodzik powinien albo przyspieszyć, albo zwolnić i zatrzymać się, w zależności od tego jak długo ów sygnał był podany.

Widzimy zatem, że skutki są tutaj bardzo różne i gdybyśmy to chcieli przełożyć na instrukcje Arduino, po każdym case mielibyśmy kilka różnych instrukcji. I do tego właśnie służy switch – case. Zdarzają się jednak innego rodzaju zadania do rozwiązania i takie mieliśmy w ostatnim przypadku. Przypomnę jak to wyglądało.

  1. Jeśli licznik równy jest 0, niech wyświetlacz przyjmie wartość B00111111

  2. Jeśli licznik równy jest 1, niech wyświetlacz przyjmie wartość B00000110

  3. Jeśli licznik równy jest 2, niech wyświetlacz przyjmie wartość B01011011

  4. Jeśli licznik równy jest 3, niech wyświetlacz przyjmie wartość B01001111

I tak dalej. Po każdym case mamy dokładnie tę samą konstrukcję: jedną instrukcję, przyporządkowującą zmiennej wyświetlacz jakąś tam wartość, która przekłada się na kształt wyświetlanej cyferki. W przypadku takich konstrukcji serii warunków istnieją lepsze metody programistyczne i taką teraz przedstawię. Zaczniemy od całego szkicu. Przypomnę, że ma on za zadanie wyświetlać kolejne cyfry od zera do dziewięciu na wyświetlaczu siedmiosegmentowym, zwiększając lub zmniejszając wartość po każdorazowym wciśnięciu przycisków na pokładowej klawiaturce płytki edukacyjnej TME. Po szczegóły odsyłam do poprzedniego artykułu.

#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.

const byte tablica[10] = {
  // Tablica zawierająca wzorce znaków wyświetlacza siedmiosegmentowego.
  B00111111,  // 0, połączenia: P-G-F-E-D-C-B-A
  B00000110,  // 1
  B01011011,  // 2
  B01001111,  // 3
  B01100110,  // 4
  B01101101,  // 5
  B01111101,  // 6
  B00000111,  // 7
  B01111111,  // 8
  B01101111,  // 9
};

byte licznik;  // Licznik do wyświetlenia na wyświetlaczu siedmiosegmentowym.
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.
  for (x = 0; x < 8; x++)  // Ustawiaj każdy bit - segment wyświetlacza, według wzorców z tablicy.
    ekspander.digitalWrite(x, bitRead(tablica[licznik], x));
}

W zasadzie od szkicu od poprzedniego artykułu różni się tylko dwoma szczegółami i na tym dziś się skupimy. Pierwszym jest powołanie do życia tak zwanej tablicy. Cóż to za twór? Tablica to zestawienie elementów takiego samego rodzaju, którym nadaje się numery kolejne, czyli indeksy. Wielu z nas tworzyło w dzieciństwie takie tablice, robiąc spis książeczek, żołnierzyków albo naklejek. Spójrzmy na tablicę, która znajduje się w naszym programie.

const byte tablica[10] = {
  // Tablica zawierająca wzorce znaków wyświetlacza siedmiosegmentowego.
  B00111111,  // 0, połączenia: P-G-F-E-D-C-B-A
  B00000110,  // 1
  B01011011,  // 2
  B01001111,  // 3
  B01100110,  // 4
  B01101101,  // 5
  B01111101,  // 6
  B00000111,  // 7
  B01111111,  // 8
  B01101111,  // 9
};

Powołanie do życia tablicy nie wymaga jakiegoś magicznego słowa, a sprowadza się wyłącznie do nazwy – dowolnej (użyłem nazwy tablica) i nawiasów kwadratowych. Przed nazwą należy jednak określić rodzaj danych, które będą w tablicy zawarte. W tym wypadku będą to naturalne liczby, nie większe od 255, czyli wystarczy najskromniejszy typ: byte. Do tego, jeśli nie przewidujemy zmieniać zawartości tablicy w całym programie, warto dopisać const, co zaoszczędzi cenny ram, przenosząc całość do pamięci stałej, której jest więcej. No i na koniec: w nawiasie tkwi liczba 10, co oznacza, że przewiduję umieścić dziesięć elementów w tablicy. Ale to nie jest wymagane, elegancko jest określać wielkość tablicy, lecz jeśli tego nie zrobimy, kompilator sam je policzy.

No i jeszcze musimy wypełnić tablicę, a robi się to w nawiasie klamrowym, który umieszcza się – nietypowo – po znaku równości. Elementy, czyli tutaj liczby typu byte, oddzielamy od siebie przecinkami. Zauważmy, że tablicę wypełniłem dokładnie tymi samymi wzorami cyfr, które w poprzednim szkicu tkwiły w serii warunków switch – case.

Czas na finał, czyli korzyści z istnienia tablic. Podprogram wyświetlający cyferki na podstawie zmiennej licznik w poprzednim szkicu miał 37 linii. Tutaj – ma tylko cztery. Spójrzmy na szczegóły.

void wyswietl() {          // Procedura wyświetlająca wartość licznika na wyświetlaczu siedmiosegmentowym.
  for (x = 0; x < 8; x++)  // Ustawiaj każdy bit - segment wyświetlacza, według wzorców z tablicy.
    ekspander.digitalWrite(x, bitRead(tablica[licznik], x));
}

Także tutaj mamy pętlę, która osiem razy zaczerpnie informację o stanie kolejnych segmentów i zaświeci je albo zgasi. Ale tym razem nie będziemy rozpatrywać dziesięciu warunków, dla każdej z możliwych kombinacji, a odwołamy się bezpośrednio do wiedzy za pomocą indeksu, czyli numeru kolejnego w tablicy. Jedyne czego trzeba, to dopilnować, by każdy element tablicy tkwił tam, gdzie powinien, czyli na początku wygląd zera, potem jedynki, dwójki i tak dalej. Wyciąganie i wysyłanie tych danych odbywa się w przedostatniej linii, która jest nieco zawiła, więc ją omówię, element po elemencie.

  • ekspander.digitalWrite – będziemy wysyłać dane na porty ekspandera,

  • x – konkretnie na port o numerze x, który przyjmie kolejne wartości od zera do siedmiu,

  • BitRead – tę instrukcję już znamy, odczytuje ona konkretny bit z bajtu znajdującego się w nawiasie,

  • tablica[licznik] – odwołujemy się do bajtu znajdującego się w tablicy pod indeksem (adresem) licznik,

  • x – używamy go drugi raz i za pomocą tej zmiennej wskazujemy BitRead o który konkretnie bit nam chodzi.

I to wszystko. Tablica może być dowolnie duża – oczywiście pod warunkiem zmieszczenia się w pamięci, więc oprócz cyferek możemy tam dołożyć także kilka liter i innych znaków. Niestety wyświetlacze siedmiosegmentowe nie dadzą rady wyświetlić wszystkich liter alfabetu, to potrafią dopiero wyświetlacze szesnastosegmentowe bądź matrycowe, o których kiedyś napiszę.

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