Serwisy partnerskie:
Close icon
Serwisy partnerskie

Wokół Arduino: wyświetlacze graficzne cz.1 - multiplexing, sterowniki, odświeżanie, pamięć i transfer

Omawianie aktualnie dostępnych, nowoczesnych ekranów i wyświetlaczy należy zacząć od przypomnienia historii i od ewidentnego zamknięcia pewnych jej rozdziałów. Trzeba zacząć od czasów, gdy królowały lampy elektronowe i gdy wskaźnikami były kontrolki w postaci żaróweczek i neonówek. Były to wskaźniki najprostsze, można powiedzieć „jednobitowe”. Ale już kilkadziesiąt lat temu ujawniło się zapotrzebowanie na bardziej skomplikowane wyświetlacze, które mogły zobrazować cyfry, liczby i inne znaki-symbole.
Article Image

Przykładem zupełnie nieznanym współczesnym elektronikom może być dekatron: odmiana lampy gazowanej (skomplikowanej neonówki), która mogła pełnić jednocześnie funkcję licznika (dziesiętnego) oraz wyświetlacza (zaświecenie jednego z dziesięciu punktów świetlnych).

Opracowano także same wyświetlacze będące odmianą neonówek. Są one znane do dziś jako lampy Nixie. Najpopularniejsze zawierają dziesięć elektrod w kształcie cyfr 0...9, ale elektrody mogą mieć także inne kształty, dowolnych symboli i liter. Tą metodą można zbudować wyświetlacze co najwyżej z kilkunastoma elektrodami, więc nie można było zrealizować gazowanych wyświetlaczy, pokazujących cały alfabet i cyfry.

Rozwiązaniem są wyświetlacze segmentowe. Absolutnie najpopularniejsze są wyświetlacze siedmiosegmentowe (plus punkt dziesiętny). Do dziś bardzo popularne są siedmiosegmentowe wyświetlacze LED, ale wcale nie były one pierwszymi wyświetlaczami segmentowymi. Dziś mało kto pamięta o tak zwanych numitronach.

Numitron to odmiana… klasycznej żaróweczki, gdzie segmentem jest odcinek rozżarzonego, świecącego włókna. Numitrony nigdy nie zdobyły popularności, ponieważ większym zainteresowaniem cieszyły się inne rodzaje wyświetlaczy, w tym wspomniane gazowane (neonówki), a także wyświetlacze fluorescencyjne VFD (Vacuum Fluorescent Display), które były specyficzną odmianą lampy próżniowej – triody z luminoforem.

Fot.1 Wyświetlacze LED, 7- oraz 16-segmentowy

Od początku lat 70. XX wieku zaczęły się upowszechniać zarówno siedmiosegmentowe wyświetlacze LED, jak też wyświetlacze ciekłokrystaliczne LCD. Zapotrzebowanie na wyświetlanie także innych treści niż liczby spowodowało opracowanie różnych „bogatszych” wyświetlaczy segmentowych. Na fotografii 1 znajdziesz wyświetlacze LED, 7- oraz 16-segmentowy, pozwalające wyświetlić znaki alfanumeryczne.

Dostępne do dziś tego rodzaju wielosegmentowe wyświetlacze, choć dość uniwersalne, były mało popularne, między innymi z uwagi na kłopoty ze sterowaniem – na początku były to wyświetlacze pojedyncze, które były sterowane indywidualnie i każdy z nich musiał mieć własny dekoder.

Bardzo popularne stały się jedynie wyświetlacze 7-segmentowe i do ich sterowania opracowano szereg scalonych dekoderów – sterowników, poczynając od klasyka: układu TTL o oznaczeniu 7447. Indywidualne, niezależne sterowanie poszczególnymi segmentami i cyframi w przypadku „dłuższych” wyświetlaczy 7-segmentowych wymagało wielu układów scalonych i mnóstwa linii – połączeń między dekoderami a wyświetlaczami. Dlatego szybko opracowano sposoby sterowania multipleksowego.

Sterowanie multipleksowe – matryce (Arduino)

Idea pokazana na rysunku 2 jest bardzo prosta: wyświetlacze są połączone niejako równolegle. W systemie jest tylko jeden wspólny dekoder zamieniający liczbę dwójkową na sygnały wyświetlające cyfry i jeden „rozdzielacz”, który w danej chwili zaświeca jeden wyświetlacz. Wyświetlacze są zaświecane kolejno na krótką chwilę, ale przy częstotliwości odświeżania ponad 50Hz nie widać efektu migotania.

Rys.2 Sterowanie multipleksowe–matryce

Rysunek 2 jest mocno uproszczony, bo nie pokazuje multiplekserów, podających sygnały z poszczególnych liczników na wspólny dekoder. Dlatego realizacja sterowania multipleksowego na drodze sprzętowej była sensowna w przypadku wyświetlaczy „dłuższych”, zawierających co najmniej kilka znaków – cyfr. Jednak w związku z postępem w dziedzinie układów scalonych i upowszechnianiem mikroprocesorów pomału głównym ograniczeniem stawała się liczba wymaganych linii połączeniowych, a nie stopień skomplikowania scalonych dekoderów.

Przykładowo bezpośrednie sterowanie dziesięcioma wyświetlaczami 7-segmentowymi wymaga dziesięciu dekoderów oraz zasadniczo 70 linii sterujących, w praktyce 80 linii, bo każdy taki wyświetlacz ma też punkt dziesiętny (przecinek), czyli w sumie osiem segmentów. Natomiast przy sterowaniu multipleksowym ten sam wyświetlacz wymaga tylko 18 linii (10+8).

Fot.3 Przykład wyświetlacza matrycowego LED

I tu wreszcie doszliśmy do wyświetlaczy matrycowych, które nie zawierają segmentów, tylko pojedyncze, jednakowe punkty. Przykład wyświetlacza matrycowego LED masz na fotografii 3. Podstawą budowy zarówno multipleksowanych wyświetlaczy 7-segmentowych, jak i punktowych wyświetlaczy matrycowych jest właśnie matryca – siatka, składająca się z pewnej liczby rzędów i kolumn według rysunku 4.

Matryca jednakowych elementów (punktów świetlnych) według fotografii 3 pozwala wyświetlić dowolne znaki, symbole i kształty! Ale nie jest to takie proste. Mamy „rozdzielacz”, włączający w danej chwili albo jeden rządek, albo jedną kolumnę oraz układ rodzaj dekodera) zaświecający w tym włączonym rzędzie lub kolumnie potrzebne punkty świetlne według rysunku 4.

Rys.4 Wyświetlacz matrycowy - rozdzielacz i dekoder

Matrycowe wyświetlacze punktowe są znane od dawna, ale dawniej, ze względu na skomplikowane sterowanie, nie były wykorzystywane przez hobbystów. Sytuację zmieniło upowszechnienie się mikroprocesorów. Mikroprocesor dobrze radzi sobie ze sterowaniem wyświetlaczem multipleksowanym oraz matrycowym LED, bowiem można je dołączyć wprost do portów procesora według rysunku 5. Potrzebnych jest tyle linii i pinów portów procesora, ile wynosi suma rzędów i kolumn, w tym przypadku 24 (8 + 16). Potrzebny jest też odpowiedni program, co nie jest problemem.

Rys.5 Zastosowanie mikroprocesora i sterowania matrycowego

Wydawałoby się, że zastosowanie mikroprocesora i sterowania matrycowego raz na zawsze rozwiązało problem i otworzyło drogę do upowszechnienia wyświetlaczy punktowych – graficznych.

Po części tak!

Jednak z upływem czasu rosło zapotrzebowanie na wyświetlacze punktowe o coraz większej rozdzielczości i coraz lepszych możliwościach i parametrach.

Zmiana jasności

Sekwencyjne zaświecanie daje jeszcze jedną możliwość: można zróżnicować czasy świecenia poszczególnych punktów – pikseli. Dany punkt – piksel może mieć dwa stany: wyłączony lub włączony (pobudzony). Zróżnicowanie czasu włączenia i wyłączenia pozwala uzyskać pośrednie stany jasności pikseli, a to wprowadza nową jakość: otrzymujemy wyświetlacz graficzny półtonowy, pokazujący najprościej biorąc, różne odcienie szarości (różną jasność). Mając możliwość zmiany jasności, można zrobić kolejny ważny krok.

Wyświetlacze kolorowe

Prostsze wyświetlacze są monochromatyczne – jednobarwne. Wystarczy jednak z diod LED zbudować wyświetlacz matrycowy o kolorach RGB (Red, Green, Blue). Mając możliwość zmiany jasności każdego z elementów składowych, otrzymamy pełnokolorowy wyświetlacz graficzny! Ekran, na którym można wyświetlić dowolny obraz, a nawet film!

Fantastycznie!

Ideę już mamy, jednak są pewne problemy i ograniczenia, które trzeba znać i rozumieć.

Nie tylko LED

Podstawowe zasady najłatwiej wytłumaczyć na przykładzie matryc LED. Jednak matrycowe wyświetlacze LED są budowane w postaci wielkich paneli reklamowych, np na ulicach i stadionach. Natomiast mniejsze wyświetlacze matrycowe/ graficzne najczęściej mają inną budowę i zasadę działania.

Fot.6 Wyświetlacz OLED z wyświetlaniem „dwustanowym”

Najnowsze matrycowe wyświetlacze diodowe są zbudowane z organicznych diod LED, stąd skrót OLED. Organicznych, ale nie opartych na białku, tylko na węglowodorach. Diody OLED wymagają wyższych napięć zasilających niż diody LED, co niewątpliwie komplikuje sterowanie. W chwili pisania tego artykułu na rynku są już duże ekrany OLED w telewizorach, a wśród hobbystów aktualnie najpopularniejsze i przystępne cenowo są malutkie wyświetlacze OLED o rozdzielczości 128 × 64 pikseli z wyświetlaniem „dwustanowym” (świeci/nie świeci). Mają przekątną ekranu 0,96 cala albo 1,3 cala – fotografia 6.

Dostępne są też malutkie pełnokolorowe ekrany OLED – fotografia 7 pokazuje przykład wersji o rozdzielczości 128 x 128 i przekątnej ekranu 1,5 cala.

Fot.7 Pełnokolorowy ekran OLED

Jak na razie, wyświetlacze OLED są dość drogie. Bardzo drogie są też wyświetlacze typu e-papier (nazywane wymiennie e-paper i e-ink). Zaletą tych wyświetlaczy jest energooszczędność: pobierają energię tylko podczas zmiany zawartości ekranu. Na rynku nie są jeszcze dostępne wyświetlacze pełnokolorowe. Fotografia 8 pokazuje wyświetlacz e-papierowy.

Fot.8 Wyświetlacz e-papierowy

Najpopularniejsze i zdecydowanie tańsze są wyświetlacze LCD (Liquid Crystal Display), czyli wykorzystujące właściwości tak zwanych ciekłych kryształów. Zależnie od przyłożonego napięcia (pola elektrycznego) ekran przepuszcza więcej lub mniej światła. Proste wyświetlacze LCD mogą wykorzystywać odbite światło otoczenia, ale z reguły mają też podświetlanie.

Ogólnie biorąc, praktycznie wszystkie graficzne, kolorowe ekrany LCD mają z tyłu podświetlacz w postaci białych diod LED i punkty ekranu (piksele) przepuszczają albo nie przepuszczają światła tego podświetlacza. Najbardziej atrakcyjne, pełnokolorowe wyświetlacze LCD mają piksele podzielone na trzy części z kolorowymi filtrami RGB. Fotografia 9 pokazuje wyświetlacze LCD o przekątnych od 1,44 do 2,8 cala.

Fot.9 Wyświetlacze LCD o przekątnych od 1,44 do 2,8 cala

Oferta rynkowa jest szeroka, a kluczowe pytanie elektronika – hobbysty brzmi: jak sterować tego rodzaju wyświetlaczami? Zanim przejdziemy do praktyki, musimy dobrze zrozumieć podstawowe problemy.

Problem liczby połączeń

Sposób z rysunku 5 pozwala w prosty sposób, jedynie z pomocą mikroprocesora, bezpośrednio wysterować matrycowy wyświetlacz graficzny LED 16 × 8, czyli zawierający 128 punktów. Tak, tylko do tego trzeba wykorzystać 24 linie, czyli trzy ośmiobitowe porty procesora. A rozdzielczość 16 × 8 jest dziś żałosna. Niewiele można na nim wyświetlić.

Gdybyśmy analogicznie chcieli sterować małym wyświetlaczem matrycowym – graficznym zwierającym 128 × 64 pikseli, wtedy potrzebne byłyby 192 linie (128+64) i 192 wyjścia portów mikroprocesora, a to już jest nierealne w praktyce. A dziś dostępne są małe tanie wyświetlacze 320 × 240 pikseli, które wymagałyby 560 linii i wyprowadzeń procesora. Czy wyobrażasz sobie procesor, który ma ponad 560 wyprowadzeń i tyleż linii prowadzących do wyświetlacza?

A przecież powszechnie wykorzystywane są wyświetlacze „o wiele większe”. W naszych smartfonach mamy wyświetlacze o rozdzielczości w obu osiach ponad 1000 pikseli, zawierające kilka milionów punktów (3040×1440 w Samsung Galaxy S10). Potrafisz sobie wyobrazić układ scalony z kilkoma tysiącami wyprowadzeń?

Multi-multiplexing - na czym polega?

Wcześniej uznaliśmy, że sterowanie matrycowe jest znakomitym rozwiązaniem, teraz okazuje się, że wyświetlacze o większej rozdzielczości wymagają zastraszająco wielu połączeń. Czy istnieje oszczędniejszy sposób sterowania wyświetlaczem matrycowym?

I tak, i nie!

Istnieją różne sposoby zmniejszenia liczby linii sterujących. Można też sobie wyobrazić, że zamiast dwuwymiarowej matrycy w osiach X, Y, piksele organizujemy w strukturę trójwymiarową (ale nie wyświetlacz trójwymiarowy) w osiach X, Y, Z, gdzie mając x + y + z wyprowadzeń, można byłoby wysterować x*y*z punktów- pikseli. Za pomocą trzech ośmiobitowych portów procesora moglibyśmy bezpośrednio sterować 512 punktami. Za pomocą trzech 16-bitowych portów, czyli 48 linii, moglibyśmy sterować 4096 punktami. Wszystko mało, nas interesują wyświetlacze z setkami tysięcy i milionami punktów.

Z kilku względów realizacja koncepcji matrycy trój- i więcej wymiarowych nie jest powszechna, choć podejmowano i podejmuje się próby zmniejszenia niezbędnych linii sterujących. Przykładem jest tzw. charlieplexing (cross-plexing), niekiedy wykorzystywany w praktyce. Za pomocą n przewodów/połączeń/pinów portu można sterować n2 – n diodami LED. Dla n=24 daje to 552 punktów świetlnych. Rysunek 10 pokazuje przykład połączeń takiej matrycy.

Dalsze szczegóły można znaleźć w angielskojęzycznej Wikipedii. Ta interesująca metoda wykorzystywana jest bardzo rzadko, choć znalazła zastosowanie w układach scalonych, na przykład AS1119 czy IS31FL3732A. Kłopotliwe byłoby jednak wykorzystanie jej w wyświetlaczach graficznych o zdecydowanie większej liczbie punktów, zarówno (O)LED, a tym bardziej LCD.

Rys.10 Charlieplexing (cross-plexing)

Ogólnie biorąc, współczesne wyświetlacze OLED i LCD są (dwuwymiarowymi) wyświetlaczami matrycowymi. Nie wymyślono bowiem lepszego, prostego rozwiązania. Z uwagi na wymaganą dużą liczbę linii sterujących na pewno nie można ich bezpośrednio obsługiwać za pomocą portów procesora – niezbędne są jakieś specjalizowane sterowniki. Nie tylko z uwagi na ogromną liczbę połączeń.

Sterowniki wyświetlaczy graficznych - jak działają?

Klasyczne diody LED, które mają napięcie przewodzenia rzędu 3V, mogłyby być sterowane wprost przez piny portów mikroprocesora, a gdyby potrzebny był większy prąd, można zastosować proste bufory, w najprostszym przypadku jakieś tranzystory. Zmianę jasności można łatwo zrealizować przez regulację czasu świecenia danego piksela. Podobnie mogłoby być w przypadku prostszych wyświetlaczy PMOLED (Passive Matrix OLED), jednak tam wymagane napięcie zasilania jest wyższe i potrzebna byłaby przetwornica.

Dużo trudniejsze jest sterowanie wyświetlaczami LCD. Zasadniczo do sterowania wystarczyłoby napięcie stałe lub jednokierunkowe impulsy. Jednak składowa stała (niezerowe napięcie średnie) spowodowałaby szybkie nieodwracalne uszkodzenie wyświetlacza. Aby tego uniknąć, trzeba usunąć składową stałą, czyli zagwarantować zerowe napięcie średnie.

Najogólniej biorąc, do sterowania wyświetlaczami LCD potrzebne są impulsy dwukierunkowe o zerowej składowej stałej. Istnieją różne rodzaje wyświetlaczy LCD, a coraz popularniejsze są wyświetlacze pełnokolorowe, gdzie wymagana Rys.10 jest też zmiana jasności pikseli. Mogą to być wyświetlacze LCD będące klasyczną matrycą (TN), ale częściej wykonuje się aktywne matryce z tranzystorami cienkowarstwowymi umieszczonymi na samym ekranie, nazywane AM TFT (Active Matrix Thin Film Transistors) według rysunku 11.

Rys.11 AM TFT (Active Matrix Thin Film Transistors)

W uproszczeniu można powiedzieć, że wyświetlacze LCD są dość powolne, dlatego sterowanie jasnością za pomocą zmiany wypełnienia impulsów jest utrudnione, a często wręcz niemożliwe. Także z innych względów sterowanie jasnością realizowane jest przez zmianę amplitudy dwukierunkowych impulsów. A to oznacza, że do sterowania nowoczesnym, kolorowym wyświetlaczem LCD niezbędnych jest mnóstwo przetworników DAC oraz liczne przetwornice napięcia.

Dodatkową komplikację (części cyfrowej) wymusza problem współczynnika zwanego gamma (γ), który ma dostosować charakterystykę zmian jasności wyświetlacza do właściwości oka ludzkiego.

Wyświetlacze OLED o większej rozdzielczości też są realizowane jako matryce aktywne, stąd nazwa AMOLED (Active Matrix OLED), na przykład według rysunku 12.

Rys.12 AMOLED (Active Matrix OLED)

O ile do bezpośredniego sterowania pikseli wyświetlaczy LED i PMOLED teoretycznie można byłoby wykorzystać mikroprocesor i proste drivery, o tyle jest to niemożliwe w przypadku wyświetlaczy LCD, w których realizowana jest zmiana jasności pikseli. Owszem, dostępne są mikrokontrolery, przeznaczone do bezpośredniego sterowania wyświetlaczami LCD, ale tylko prostymi wyświetlaczami z niewielką liczbą segmentów/punktów. W kilku powodów do sterowania współczesnymi wyświetlaczami z dużą liczbą segmentów/punktów niezbędne są specjalizowane scalone sterowniki, i to ściśle dostosowane do konkretnych rodzajów i typów wyświetlaczy.

Taki scalony sterownik, który jest praktycznie tylko krzemową strukturą bez obudowy, jest montowany „na plecach” wyświetlacza niejako bezpośrednio. Wyświetlacz i sterownik stają się jednolitą, zintegrowaną całością bez możliwości ich rozdzielenia.

Fot.13 Obie strony wyświetlacza OLED 128 × 64, zmontowanego z wykorzystaniem giętkiej płytki drukowanej o 30 wyprowadzeniach

Fotografia 13 pokazuje obie strony wyświetlacza OLED 128 × 64, zmontowanego z wykorzystaniem giętkiej płytki drukowanej o 30 wyprowadzeniach. Wbudowany scalony sterownik zawiera także przetwornice pojemnościowe, które wymagają zewnętrznych kondensatorów. Kondensatory i ewentualnie przetwornicę indukcyjną montuje się na klasycznej płytce drukowanej o schemacie według rysunku 14, do której przylutowany jest też wyświetlacz. Gotowy do użycia moduł pokazany jest na fotografii 15.

Rys.14 Schemat płytki drukowanej modułu wyświetlacza

Wyjaśniliśmy kilka bardzo ważnych szczegółów: współczesne wyświetlacze są dwuwymiarową matrycą punktów i do sterowania nimi służą specjalizowane, dedykowane scalone sterowniki, mające setki, a nawet ponad tysiąc (!) wyprowadzeń, montowane bezpośrednio na tylnej stronie wyświetlacza. Słusznie domyślamy się, że mikroprocesor wysyła do takiego sterownika informacje, co należy wyświetlić, a sterownik w sobie wiadomy sposób steruje poszczególnymi punktami matrycy.

Dzięki obecności ściśle specjalizowanych scalonych sterowników, mikroprocesor nie bierze udziału w sterowaniu poszczególnymi punktami. Nasuwa się optymistyczny wniosek, że mikroprocesor wysyła dane do wyświetlenia w jakiś standardowy sposób, niezależnie od typu i rodzaju wyświetlacza i sterownika.

Fot.15 Moduł wyświetlacza

Poniekąd tak, ale sprawa nie jest taka prosta. W grę wchodzi kilka aspektów.

Odświeżanie - jak działa w różnych wyświetlaczach?

W matrycowym wyświetlaczu LED w danej chwili zaświecany jest tylko jeden rząd albo jedna kolumna wyświetlacza. Potem następna, następna, ... W ciągu sekundy musi być co najmniej 50 takich cykli zaświecania, by oko ludzkie nie dostrzegało migotania. W wyświetlaczach LCD jest podobnie: wszystkie piksele są obsługiwane/odświeżane co najmniej 50 razy na sekundę. Poszczególne piksele-komórki LCD mają znaczną pojemność i dany stan piksela utrzymuje się do następnego cyklu odświeżania.

W każdym razie sterownik wyświetlacza graficznego LED, OLED, LCD musi nieustannie pracować i wykonywać mnóstwo operacji.

Inaczej jest z wyświetlaczami e-paper/e-ink. Można powiedzieć, że warstwa czynna to mnóstwo kuleczek, z jednej strony białych, z drugiej czarnych (rysunek 16). Pole elektryczne o odpowiedniej biegunowości powoduje, że widoczna jest albo czarna, albo biała strona kuleczki. Wystarczy za pomocą odpowiednich napięć jednorazowo ustawić kolor poszczególnych kuleczek i pozostaną one trwale w tym stanie.

Rys.16 Zasada działania e-papieru (e-paper)

Oznacza to, że jeżeli chcemy na wyświetlaczu e-paper wyświetlić nieruchomy obraz, wystarczy zrobić to jednorazowo. A mając obraz wyświetlony, można go modyfikować, zmieniając potem stan poszczególnych pikseli. Sterownik dla wyświetlacza e-paper jest niezbędny, ale po ustaleniu prezentowanej treści można go wyłączyć. W mikroprocesorze nie musimy mieć pamięci obrazu, bo możemy treść poszczególnych pikseli ustawiać i modyfikować indywidualnie. Na przykład możemy na przemian zaświecać i wygaszać jakiś jeden piksel albo kilka pikseli. Wyświetlacz e-paper/e-ink zawiera w sobie pamięć obrazu, więc system mógłby wyglądać jak na rysunku 17a.

Takiej pamięci nie mają wyświetlacze (O)LED i LCD. Nawet gdy obraz na ekranie jest niezmienny, sterownik musi nieustannie odświeżać stan wszystkich pikseli kilkadziesiąt razy na sekundę. A skąd sterownik ma wiedzieć, jaki ma być stan poszczególnych punktów ekranu?

Właśnie! W systemie z wyświetlaczem graficznym OLED lub LCD obowiązkowo musi gdzieś być pamięć obrazu (często nazywana Graphic RAM, czyli GRAM).

Logicznym wyborem wydaje się umieszczenie pamięci GRAM w sterowniku, czyli w module wyświetlacza według rysunku 17b.

Jeżeli mamy wyświetlić obraz statyczny, niezmienny, to jednorazowo wyślemy z procesora informacje, jaka jest treść obrazu. Zostanie to zapamiętane w GRAM i potem sterownik będzie po prostu na bieżąco odzwierciedlał na ekranie zawartość pamięci GRAM, pracowicie odświeżając stan pikseli kilkadziesiąt razy na sekundę. Chcąc dokonać zmian, analogicznie jak w przypadku e-paper, nie musimy zapisywać całej pamięci od nowa – możemy tylko zmienić zawartość części pamięci GRAM. To wydaje się oczywiste.

A co sądzisz o umieszczeniu pamięci GRAM według rysunku 17c w mikroprocesorze, a nie w sterowniku?

Rys.17 Moduł wyświetlacza a pamięć GRAM

Uważasz, że to bez sensu?

Istotnie, wtedy po pierwsze zajmujemy cenną pamięć RAM, po drugie procesor musiałby kilkadziesiąt razy na sekundę wysyłać do sterownika wyświetlacza komplet informacji o treści ekranu. W przypadku wyświetlania obrazów nieruchomych rzeczywiście wygląda to na niezbyt rozsądne rozwiązanie, jednak gdy chodzi o wyświetlanie ruchomego obrazu kolorowego (filmu, szybkiej animacji), takie rozwiązanie jest nie tylko sensowne, ale wręcz konieczne.

A wersja z podwójną pamięcią obrazu: i w procesorze, i w sterowniku wyświetlacza? Albo wersja z podwójną pamięcią GRAM w sterowniku? Może w pierwszej chwili wygląda to na ekstrawagancję i marnotrawstwo, ale taki wniosek jest zbyt pochopny z powodów, które musimy omówić, związanych z ilością danych i wymaganą szybkością ich przekazywania.

Wielkość pamięci... - Arduino i wyświetlacze graficzne

Do zapamiętania jednej cyfry (0...9) wystarczą cztery bity, czyli pół bajta. Dwadzieścia cyfr można zapamiętać w dziesięciu bajtach. Do zapamiętania litery potrzebny jest jeden bajt (wprawdzie kod ASCII jest 7-bitowy, ale są też kody znacznie „dłuższe”). Dwadzieścia liter i innych symboli można więc zapamiętać z dwudziestu bajtach.

Zwróć uwagę, że w takich przypadkach wystarczy niewiele pamięci, ponieważ informacja jest najpierw dekodowana, a potem wyświetlana. Potrzebny jest jednak dekoder, który zadecyduje, które punkty ekranu zostają zaświecone przez dany kod (cyfrę, literę, symbol).

Zaletą jest fakt, że stan ekranu jest opisany przez niewielką ilość danych, a wadą jest to, że wyświetlone mogą być wyłącznie symbole wcześniej zdefiniowane w dekoderze.

Dużo więcej pamięci potrzeba, jeśli mamy wyświetlacz graficzny i chcemy dowolnie zaświecać jego punkty składowe (piksele). W przypadku najprostszego wyświetlacza bitmapowego z pikselami typu załącz/wyłącz, stan każdego piksela może być zapamiętany w jednym bicie pamięci. Mały wyświetlacz OLED 128 × 64 składa się z 8192 pikseli (bitów), więc jego stan można zapisać w pamięci o objętości 1 kilobajta (1024 bajty = 8192 bity).

Dla porównania: jeżeli na tym wyświetlaczu chcielibyśmy wyświetlić tekst w postaci znaków (o wielkości 6 × 8 pikseli każdy), to wyświetlimy osiem linii tekstu i 21 znaków – liter w każdej linii, czyli w sumie 168 liter, do których zapamiętania wystarczyłoby 168 bajtów. Widać tu znaczną, sześciokrotną (1024/168) oszczędność, gdybyśmy mogli wykorzystać dekoder znaków.

Jeżeli jednak chcemy indywidualnie sterować poszczególnymi pikselami i jeżeli dodatkowo wyświetlacz monochromatyczny ma możliwość zmiany jasności pikseli, to do zapisania/zapamiętania stanu każdego z nich potrzeba kilku bitów. Często jest to 8 bitów, co pozwala zobrazować 256 odcieni/poziomów jasności każdego piksela. Do obsługi takiego półtonowego wyświetlacza 128 × 64 niezbędne jest co najmniej 8 kilobajtów pamięci RAM, przechowującej aktualny stan ekranu.

W przypadku wyświetlacza pełnokolorowego, gdzie każdy kolor składowy RGB może mieć 256 stopni jasności, do zapisania stanu każdego piksela potrzeba trzech bajtów, po jednym dla każdego z kolorów RGB. W przypadku pełnokolorowego wyświetlacza 128 × 64 potrzebna jest więc pamięć GRAM co najmniej 24 kilobajty. Dla pełnokolorowego wyświetlacza 1024 × 600, czyli zawierającego 614400 kolorowych punktów, potrzeba co najmniej 1843200 bajtów pamięci GRAM, czyli prawie 2 megabajty. Wyświetlacz HD o rozdzielczości 1920 × 1080 zawiera 2073600 pikseli, więc do zapamiętania stanu ekranu potrzeba około 6 megabajtów pamięci GRAM.

Takie pełnokolorowe wyświetlacze mogą pokazać 16777216 kolorów (odcieni). Istnieje wiele małych wyświetlaczy, pokazujących mniej odcieni, gdzie liczba poziomów jasności kolorów RGB jest określana przez liczbę dwójkową 5- albo 6-bitową. Przykładowo „kolor 18-bitowy”, określony przez trzy liczby 6-bitowe wyznaczające jasność składowych RGB pozwala wyświetlić 262144 odcieni. Wymagana objętość danych takich kolorowych obrazów zostaje zmniejszona, ale w sumie też jest bardzo duża.

Istnieją sposoby na zmniejszenie objętości pamięci procesora, koniecznej do obsługi wyświetlacza graficznego. Przede wszystkim w wielu modułach wyświetlaczy pamięć GRAM jest zawarta w sterowniku, czyli w wyświetlaczu. W mikroprocesorze nie trzeba rezerwować cennej pamięci RAM dla grafiki, ponieważ po pierwsze ewentualne obrazy bitmapowe można „zaszyć” w samym programie, czyli zapamiętać w obszerniejszej pamięci FLASH i stamtąd wysłać do wyświetlacza, nie angażując w to RAM-u.

Po drugie, inne składniki obrazu, jak napisy i liczby, można wyświetlić stopniowo, modyfikując stan niektórych pikseli ekranu za pomocą odpowiednio napisanego programu. Oczywiście taki sposób obsługi wyświetlacza uniemożliwia wyświetlenie nawet krótkich filmików czy szybkich animacji. Pozwala realizować jedynie wyświetlanie statyczne i powolne animacje. W wielu zastosowaniach byłoby to wystarczające, jednak hobbyści chcieliby wykorzystywać kolorowe wyświetlacze o dużej rozdzielczości. A już przy małych pojawia się problem.

Na przykład zapamiętanie kolorowego 18-bitowego obrazu dla malutkiego wyświetlacza 320 × 240 punktów, czyli zawierającego 76800 pikseli, wymaga 1382400 bitów czyli 172 kilobajtów. Nawet prymitywny jednobitowy obraz bitmapowy wymaga 76800 bitów, czyli prawie 10 kilobajtów pamięci.

Tymczasem zawarty w płytkach Arduino mikroprocesor ATmega328P zawiera tylko 32 kilobajty pamięci Flash i tylko 2 kilobajty pamięci RAM, a przecież całej pamięci procesora nie można przeznaczyć wyłącznie do obsługi grafiki…

Z uwagi na niewielką ilość pamięci, Arduino nie nadaje się do współpracy z kolorowymi wyświetlaczami o dużej rozdzielczości. Ale jest i inny problem.

Szybkość transferu - Arduino i wyświetlacze graficzne

Problem braku pamięci można byłoby ominąć, dodając do systemu moduł pamięci (FLASH, EEPROM) albo kartę pamięci FLASH (SD), w której przechowane byłyby obrazy do wyświetlenia. Dodanie do Arduino modułu pamięci nie jest dziś żadnym problemem, ani sprzętowym, ani finansowym. Mogłoby się wydawać, że to sprytne rozwiązanie likwiduje problem. Niestety nie do końca. Owszem, w niektórych zastosowaniach takie rozwiązanie może się sprawdzić, ale trzeba mieć na uwadze inny problem: coraz częściej wykorzystujemy łącza szeregowe, głównie I2C oraz odmiany SPI.

I dobrze! Cieszymy się, że łącze I2C zajmuje tylko dwa piny Arduino (procesora) i że do tego jednego łącza możemy podpiąć dziesiątki układów peryferyjnych. Jak pokazuje fotografia 15, łącze I2C może obsługiwać moduł wyświetlacza graficznego. Do tego samego łącza możemy podpiąć dodatkową pamięć FLASH/ EEPROM albo jeszcze pojemniejszą kartę SD. Prosto, łatwo i oszczędnie! Rewelacja!

Niestety, nie do końca: łącze I2C z natury jest łączem powolnym. Nie wchodząc w szczegóły, w Arduino częstotliwość sygnału zegarowego to około 100kHz, co daje 10 mikrosekund na jeden takt zegara. Nie znaczy to, że przesłanie ośmiu bitów (bajtu) zajmuje 80 mikrosekund. W praktyce znacznie więcej z uwagi na konieczność wstępnego „dogadania się” urządzeń, adresowanie oraz kontrolę poprawności transmisji.

W każdym razie łącze I2C jest powolne. I tym powolnym łączem chcemy przesyłać dość duże ilości danych między zewnętrzną pamięcią, procesorem i modułem wyświetlacza. Owszem, jest to wygodne, ale trwa stosunkowo długo. Radość z wygody i prostoty kończy się, gdy zauważamy, że zmiana zawartości wyświetlacza jest powolna. Pamiętamy, że wyświetlacze (O)LED i LCD są odświeżane kilkadziesiąt razy na sekundę, a odświeżanie wpisuje na ekran aktualną zawartość pamięci grafiki GRAM. Jeżeli transfer danych z procesora jest powolny, to podczas zmiany zawartości ekranu zaobserwujemy błędne, zniekształcone obrazy.

Początkujący najpierw cieszą się ogromnie, że obsługę wyświetlaczy graficznych można zrealizować w zaskakująco prosty sposób. Tak, ale z czasem zauważają problem szybkości transmisji i kłopoty z odświeżaniem. W przypadku Arduino często nie ma sensowych sposobów polepszenia sytuacji. Po pierwsze, trzeba zastosować inny wyświetlacz z szybszym, ale mniej wygodnym łączem.

Owszem, bardzo popularne, ale powolne łącze I2C można zastąpić dużo szybszym szeregowym łączem SPI. Ale i to nie rozwiązuje problemu do końca. Nie do końca pomaga zastosowanie wyświetlacza z jeszcze szybszym łączem równoległym. Łącze przestaje być ograniczeniem, a staje się nim mizerna prędkość Arduino i obsługi pinów portu. Przecież Arduino nie ma możliwości szybkiej i bezpośredniej transmisji danych zawartych w pamięci (DMA).

Dlatego po drugie, Arduino... trzeba zastąpić potężniejszym i szybszym systemem, na przykład RaspberryPi. Generalnie z powodu problemów z transferem danych Arduino nie nadaje się do sensownej współpracy z dużymi wyświetlaczami.

Trzeba jednak przyznać, że problem szybkości transferu, opóźnień i artefaktów na ekranie został tu przedstawiony bardzo ostro. W praktyce, w bardzo wielu zastosowaniach wyświetlane mają być tylko obrazy statyczne, zawartość ekranu jest zmieniana sporadycznie, a chwilowe drobne błędy wyświetlania, trwające ułamki sekund, są do zaakceptowania.

Podkreślamy tu problem szybkości i opóźnień z dwóch powodów. Po pierwsze: w pełni satysfakcjonująca obsługa wyświetlaczy graficznych nie jest sprawą łatwą, i to nie tylko przy korzystaniu z Arduino.

Po drugie, poszczególne wyświetlacze mają różne interfejsy, różne możliwości sterowników, co często też wpływa na cenę.

Tu wracamy do wcześniejszego pytania o sens zastosowania w sterowniku wyświetlacza podwójnej pamięci GRAM: otóż podwójne buforowanie okazuje się błogosławieństwem, bo pozwala do końca transferu nowych danych wyświetlać „stary obraz” z drugiej pamięci, a zmieniać zawartość obrazu dopiero po zakończeniu transmisji nowych danych.

Tu widać też uzasadnienie, dlaczego zamiast wygodnego łącza I2C czasem wręcz konieczne jest zastosowanie wyświetlaczy z szybszym łączem SPI albo nawet z na pozór przestarzałym 8-bitowym łączem równoległym (tzw. M6800 albo I8080) lub nawet łączem 16-bitowym.

W drugiej części artykułu omówimy między innymi łącza równoległe M6800 i I8080. Zajmiemy się także podstawowymi zasadami, wspólnymi dla obsługi wszystkich wyświetlaczy graficznych.

Tematyka materiału: wyświetlacze graficzne, OLED, lampy Nixie, wyświetlacze VFD, multiplexing
AUTOR
Źródło
Elektronika dla Wszystkich wrzesień 2019
Udostępnij
UK Logo