Serwisy partnerskie:
Close icon
Serwisy partnerskie

Wokół Arduino: wyświetlacze graficzne cz.2 - standardy, szybkość transferu, interfejsy

W pierwszym odcinku omówiliśmy szereg podstawowych zagadnień, dotyczących wyświetlaczy graficznych (matrycowych). Absolutnie nie wyczerpały one zagadnienia, ponieważ jest ono bardzo obszerne i ma szereg aspektów, które trudno objąć jednocześnie. Spróbujemy je omówić po kolei, co w końcu wyjaśni tematykę wyświetlaczy graficznych i znakomicie ułatwi ich praktyczne wykorzystanie.
Article Image

Czy istnieje jednolity standard sterowania wyświetlaczy? 

Zapewne można byłoby opracować uniwersalne wytyczne, a wręcz ogólnoświatowy standard sterowania dla dowolnych wyświetlaczy monochromatycznych i kolorowych. I jedno powszechnie przyjęte, standardowe łącze transmisyjne między mikroprocesorem a sterownikiem.

Dane z reguły są przesyłane w bajtach, więc naturalnym rozwiązaniem wydaje się 8-liniowa równoległa szyna danych. Ale można byłoby albo zwiększyć liczbę linii danych, np. do 16 czy nawet 24, albo odwrotnie – liczbę linii poważnie zredukować. W skrajnym przypadku można sobie wyobrazić, że między mikroprocesorem a sterownikiem wyświetlacza graficznego wystarczyłaby tylko jedna linia danych! Każde z rozwiązań miałoby zalety i wady. Niestety, kilkadziesiąt lat temu, w czasach gdy pojawiły się wyświetlacze graficzne, NIE POWSTAŁ uniwersalny ogólnoświatowy standard!

Z kilku powodów mamy dziś do czynienia z różnymi sposobami przesyłania danych z mikroprocesora do modułu wyświetlacza. Owszem, od kilkunastu lat podjęto próby wprowadzenia jednolitych ogólnoświatowych zaleceń (MIPI), ale w zakresie małych wyświetlaczy było to przede wszystkim porządkowanie istniejącej sytuacji. Poszczególne współczesne wyświetlacze mają rozmaite interfejsy. Aby poznać przyczyny i rozjaśnić obraz sytuacji, musimy powrócić do zasygnalizowanej wcześniej kwestii szybkości transferu

Dla czego szybkość transferu jest istotna – coraz szybsze interfejsy

Mówiąc najprościej, po pierwsze chcemy stosować coraz większe, kolorowe wyświetlacze graficzne. Po drugie, chcemy dołączać te wyświetlacze za pomocą jak najmniejszej liczby przewodów, najlepiej od dwóch do czterech.

Przyjmuje się, że aby uniknąć migotania i innych niepożądanych artefaktów, zawartość wyświetlacza trzeba odświeżać co najmniej 50 razy na sekundę, czyli cykl odświeżania nie powinien być dłuższy niż 1/50 sekundy, czyli 20 milisekund. Otóż możemy teraz w dużym uproszczeniu przyjąć, że transfer kompletu informacji z procesora do wyświetlacza nie powinien trwać dłużej niż te 20 milisekund.

Nie ma problemu, gdy chodzi o mały wyświetlacz monochromatyczny. Przykładowo treść popularnego dziś wyświetlacza OLED, zawierającego 128×64 (8192) zaświecanych i gaszonych, czyli dwustanowych pikseli, jest opisana za pomocą 8192 bitów.

Jeżeli te 8 kilobitów trzeba przesłać jakimś łączem szeregowym w ciągu 20 milisekund, czyli 20000 mikrosekund, na każdy bit przypada prawie 2,5 mikrosekundy, co daje wymaganą prędkość transmisji około 400 kilobitów na sekundę. Wymaganie to jest dość łatwe do spełnienia, nawet w najprostszych mikroprocesorach.

Fot.1 Kolorowy wyświetlacz 128×128 - kolor każdego piksela określany liczbą 18-bitową

Owszem, ale gdybyśmy chcieli analogicznie w ciągu 20ms przesłać łączem szeregowym dane do pokazanego na fotografii 1 malutkiego kolorowego wyświetlacza 128×128 (16384 pikseli), gdzie kolor każdego piksela określa liczba 18-bitowa, to musielibyśmy w tym czasie przesłać 294912 bitów. Na jeden bit przypadnie wtedy 0,068 mikrosekundy (68 nanosekund), co w przypadku łącza szeregowego dawałoby częstotliwość taktowania prawie 15MHz. A jak pamiętamy, niektóre płytki Arduino mają procesor taktowany zegarem 16MHz lub 20MHz, ale niektóre tylko 8MHz... Jest problem...

Weźmy kolejny, niewielki kolorowy wyświetlacz QVGA o rozdzielczości 320×240 (76800 pikseli) z kolorem 18-bitowym. Treść ekranu opisana jest za pomocą 1382400 bitów. Znów chcemy je przesłać łączem szeregowym w ciągu 20 milisekund: na jeden bit wypada czas tylko 15 nanosekund, co daje częstotliwość taktowania około 70 megaherców. To jest zupełnie nieosiągalne dla Arduino.

Tak, ale gdybyśmy wykorzystali nie łącze szeregowe, tylko jakieś 8-bitowe łącze równoległe, to można byłoby 8-bitowe paczki danych przesyłać z częstotliwością taktowania około 8,7 megaherca. A gdyby zastosować łącze równoległe z 24 liniami (trzy porty 8-bitowe), wymagana częstotliwość taktowania wynosiłaby niecałe 3MHz.

Wyświetlacze kolorowe stają się coraz tańsze i bez problemu kupimy wyświetlacz z ponad milionem pikseli, na przykład WXGA 1280×800 z 1024000 pikseli (fotografia 2), gdzie kolor każdego piksela określają 24 bity, czyli trzy bajty. Na takim ekranie można z powodzeniem wyświetlać filmy. Do opisania zawartości ekranu potrzebne są 24576000 bity. Gdybyśmy chcieli przesłać je szeregowo w ciągu 20ms, to na jeden bit mamy tylko około 0,8 nanosekundy, a potrzebna częstotliwość taktowania przekraczałaby 1GHz.

Fot.2 Wyświetlacz WXGA 1280×800

Trochę pomogłoby zastosowanie łącza równoległego. Przy łączu równoległym 8-bitowym częstotliwość taktowania musiałaby wynosić około 154MHz, a przy łączu 24-bitowym ponad 51MHz. Wszystko to jest absolutnie nieosiągalne w Arduino!

A co najmniej taka szybkość przepływu danych byłaby konieczna w przypadku filmów i szybkich animacji.

W przypadku obrazów nieruchomych i powolnych animacji pod pewnymi warunkami można byłoby zdecydowanie obniżyć prędkość transmisji. Ale bez przesady: jeżeli treść pełnokolorowego ekranu WXGA 1280×800 (×24), czyli 24192000 bitów, chcielibyśmy przesłać z procesora do wyświetlacza łączem szeregowym w ciągu powiedzmy czterech sekund, to na jeden bit mamy 0,17 mikrosekundy, co wymaga częstotliwości taktowania ponad 6MHz. Teoretycznie można to od biedy zrealizować w Arduino. Ale w ilu zastosowaniach można zaakceptować system, w którym odświeżanie ekranu trwa cztery sekundy?

Wyświetlacze do Arduino - najważniejsze wnioski

Mamy tu dwa bardzo ważne wnioski.

  • Po pierwsze Arduino nie nadaje się do obsługi wyświetlaczy o dużej rozdzielczości.
  • Po drugie, nawet gdybyśmy zastosowali dużo szybszy procesor, większe wyświetlacze wymagają szybkiej transmisji dużej ilości danych. Potrzebny jest i odpowiednio szybki procesor, i odpowiednio szybkie łącze.

Rodzaje interfejsów wykorzystywanych do współpracy z wyświetlaczami

Nie ulega wątpliwości, że najwygodniejsze są interfejsy szeregowe, bowiem zawierają niewiele linii – przewodów. Dlatego o ile to możliwe, wykorzystujemy różne interfejsy szeregowe. Skrajnym przypadkiem jest jednożyłowy, dwukierunkowy interfejs 1-Wire (1W), jednak z kilku powodów nie jest on wykorzystywany do współpracy z wyświetlaczami.

I2C - opis interfejsu

Znanym i lubianym interfejsem jest I2C oraz jego odmiany. Zaletą jest to, że dwukierunkową komunikację zapewniają tylko dwie linie (SCL, SDA). Do jednego interfejsu – do tych dwóch linii, można podłączyć ponad setkę urządzeń o różnych adresach. W kontekście wyświetlaczy graficznych kluczowym ograniczeniem jest mała szybkość transmisji.

W wersji podstawowej częstotliwość sygnału zegarowego to tylko 100kHz i to niezależnie od wykorzystywanego procesora, w wersji szybszej 400kHz, a bardzo szybkiej 3,4MHz. Dlatego tylko wyświetlacze o najmniejszej rozdzielczości mogą mieć interfejs I2C. Przykładem są najpopularniejsze dziś wyświetlacze OLED 128×64 – wiele z nich ma właśnie interfejs I2C – fotografia 3. Ale takie wyświetlacze mogą też mieć interfejs...

Fot.3 Wyświetlacz OLED 128×64 z interfejsem I2C

SPI - opis interfejsu

Fotografia 4 pokazuje taki sam wyświetlacz OLED 128×64 z typowym interfejsem SPI, gdzie transmisja danych może być szybsza oraz wersję, która może mieć interfej SPI lub I2C.

W różnych odmianach interfejsu szeregowego, nazywanych SPI (Serial Peripherial Interface), podstawą są dwie linie: zegarowa i danych. Mikroprocesor wysyła kolejne bity danych do wyświetlacza w takt sygnału zegarowego. Częstotliwość sygnału zegarowego nie jest określona, może być teoretycznie bardzo duża, rzędu setek megaherców, a nawet więcej. W praktyce ograniczeniem są możliwości obwodów odbiorczych i przy sterowaniu małymi wyświetlaczami częstotliwość zegarowa sięga kilku megaherców.

W zasadzie transmisję danych realizują tylko dwie linie, ale z reguły interfejs szeregowy, nazywany SPI, zawiera 3 albo 4 linie. Nie wchodząc w szczegóły: prosty w realizacji i elastyczny interfejs SPI pozwala uzyskać szybkość transferu zdecydowanie większą niż I2C. W Arduino przy taktowaniu procesora 20MHz sygnał zegarowy może mieć częstotliwość kilku megaherców. A przy innych procesorach mógłby mieć częstotliwość wielokrotnie większą, byle tylko wyświetlacz nadążył odbierać dane.

Fot.4 Wyświetlacze OLED 128×64 z interfejsem SPI oraz I2C

Zwiększanie częstotliwości zegarowej związane jest jednak z różnymi kłopotami. Przy dużych częstotliwościach rośnie problem generowania zakłóceń. Częściowym rozwiązaniem problemu jest wykorzystanie łącza równoległego, gdzie podczas jednego taktu zegara przekazywanych jest tyle bitów, ile linii ma ten równoległy interfejs.

MCU, MPU, M6800, I8080, RGB - opisy interfejsów

Czym większa liczba linii w łączu równoległym, tym szybszy może być transfer danych. Już cztery linie zdecydowanie (czterokrotnie) zwiększają prędkość, a standardowe wydaje się łącze z ośmioma liniami. Wykorzystywane są też łącza 16-bitowe. A do współpracy z wyświetlaczami dość często także 24-bitowe (3×8 bitów), które wraz z linią zegarową i pomocniczymi zawierają do 29 linii.

Tu koniecznie trzeba wspomnieć, że na początku epoki informatycznej procesory „głównego nurtu” były „małe i słabe” i wtedy byliśmy świadkami walki konkurencyjnej głównie między mikroprocesorami Intela i Motoroli (tak, Motoroli, a nie AMD, a o Samsungu nikt wtedy jeszcze nie słyszał). Ówczesne pierwsze bardzo popularne mikroprocesory: M6800 Motoroli oraz 8080 Intela były „gołe”, współpracowały z zewnętrznymi pamięciami i układami peryferyjnymi.

Systemy M6800 oraz I8080 wykorzystywały podobne, ale nie jednakowe interfejsy, zawierające 8-bitową szynę danych oraz kilka linii sterujących. Gdy już wtedy pojawiło się zapotrzebowanie na wyświetlacze graficzne i ich sterowniki, wręcz oczywiste było, że powinny one nadawać się do bezpośredniego dołączenia do magistrali M6800 i/lub I8080.

Warto przypomnieć, że w tamtych zamierzchłych czasach dominowały monitory i telewizory z lampą kineskopową. Ówczesna telewizja analogowa dawała obraz, gdzie na ekranie widocznych było mniej niż 600 linii: maksymalnie 486 w amerykańskim systemie NTSC i do 576 w nieco nowocześniejszym europejskim PAL. O rozdzielczości obrazu w poziomie decydowało pasmo toru analogowego. Nie było tam pikseli, tylko linia, którą od biedy można potraktować jako składającą się z 600...700 pikseli.

Nic dziwnego, że pod koniec lat 80. jako standard dla monitorów komputerowych przyjęto rozdzielczość 640×480, co na kineskopowym ekranie dawało 480 linii, tyle co w amerykańskim obrazie telewizyjnym. Tak było we wprowadzonym w roku 1987 przez IBM standardzie komputerowych kart graficznych, zwanych VGA (Video Graphic Array). VGA to absolutnie przestarzały dziś standard kart graficznych wprowadzony przez IBM, poprzedzony przez standardy CGA (320...640×200) i EGA (640×350).

Ale nadal często używamy skrótu VGA, który dziś oznacza stosowane w tych kartach łącze analogowe i charakterystyczne 15-pinowe złącza. Jednak przede wszystkim skrót VGA oznacza dziś właśnie rozdzielczość 640×480, a skróty pokrewne wskazują na większą lub mniejszą rozdzielczość. Na przykład QVGA to Quarter VGA – ćwiartka VGA, czyli rozdzielczość 320×240. A SVGA (SuperVGA) 800×600.

Temat analogowego łącza VGA (D-SUB) do dziś powszechnie stosowanego w monitorach to oddzielna sprawa. My koncentrujemy się na wyświetlaczach dołączanych do mikroprocesorów. I wiemy, że już kilkadziesiąt lat temu pojawiły się dwa główne rozwiązania, dwa interfejsy równoległe, promowane przez najpopularniejsze wtedy firmy.

Interfejs M6800 (M68) oprócz 8 (albo 4, albo 16) dwukierunkowych linii danych zawiera cztery linie sterujące: D/C\ (Data/ Control\), R/W\ Read/Write\), E (Enable) oraz CS\ (Chip Select).

Interfejs Intel 8080 (I80) oprócz dwukierunkowej szyny danych zawiera linie sterujące D/C\, WR\. RD\ oraz CS\.

Działanie interfejsów M68 i I80 nie jest identyczne, jednak dziś nie ma problemu, żeby programowo przeprowadzić emulację linii sterujących z wykorzystaniem dowolnych pinów I/O. Dla użytkownika różnice M6800/I8080 nie są więc istotne, bo można je bardzo łatwo zniwelować programowo.

Co ciekawe, gwałtowny rozwój w dziedzinie procesorów spowodował zniknięcie mikroprocesorów i systemów M6800 i I8080, jednak do dziś dostępne są sterowniki wyświetlaczy znakowych i graficznych z takimi 8-bitowymi równoległymi interfejsami.

Przykładem może być popularny moduł wyświetlacza znakowego z HD4780, który ma interfejs M6800 z liniami pomocniczymi zazwyczaj oznaczanymi RS, RW, En. Jak pamiętamy, można pracować z 8 liniami danych, ale częściej pracujemy z 4 liniami danych, żeby zmniejszyć liczbę linii i zajętych portów procesora.

Przypomnieliśmy tu początki i ośmiobitowe procesory M6800, I8080. Błyskawiczny postęp spowodował wprowadzenie procesorów 16-, 32- a nawet 64-bitowych. Także wyświetlacze stawały się coraz większe i miały coraz większą rozdzielczość. Aby wykorzystać takie większe wyświetlacze, wymagające szybszego transferu danych, najprostszym sposobem było zastosowanie łącza równoległego o większej liczbie linii.

Fotografia 5 pokazuje moduły wyświetlaczy z interfejsem o dużej liczbie linii. W przypadku wyświetlaczy kolorowych, gdzie mamy kolory RGB, naturalne wydaje się zastosowanie łącza 24-bitowego (3×8 bitów).

Fot.5 Moduły wyświetlaczy z interfejsem o dużej liczbie linii

I rzeczywiście, tak szerokie łącza równoległe są dość często stosowane. Tylko znów dochodzimy do pewnych niejasności. A wyjaśnienie zależy od tego, co tak naprawdę jest przez takie 24-bitowe łącze transmitowane (a łącze może mieć mniej linii niż 24).

Jeśli spotkamy w opisie, że wyświetlacz ma interfejs RGB, to najczęściej łącze ma 24 linie, niejako trzy tory (3×8 bitów), transmitujące w ustalony sposób informacje o kolorach RGB kolejnych pikseli. Jeśli natomiast spotkamy opis MCU albo MPU, to wskazuje on tylko ogólnie, że łącze służy do transmisji danych z mikroprocesora. Nawet gdy szerokość interfejsu wynosi 24 bity (a może być inna), nie muszą to być informacje o kolorach RGB poszczególnych pikseli, a mogą to być odmienne dane, na przykład rozkazy, w zupełnie inny sposób przekazujące informacje, co ma zostać pokazane na ekranie. Do tych szczegółów jeszcze wrócimy.

A na razie dokończmy temat interfejsów. Otóż nawet zastosowanie równoległego 24-bitowego interfejsu w przypadku wyświetlaczy o najwyższej rozdzielczości, zawierających kilka milionów pikseli (ponad 4 miliony w Samsung Galaxy S10), oznaczałoby konieczność taktowania z bardzo wysoką częstotliwością. Generalnie zwiększanie częstotliwości zegarowej nie jest problemem, ale niestety wiąże się z wytwarzaniem coraz silniejszych zakłóceń, które w skrajnym przypadku uniemożliwiłyby prace sąsiednich obwodów i układów. Zakłócenia są tym większe, im większa jest częstotliwość i im większa jest amplituda sygnałów zegara i danych.

Dawniej w układach logicznych standardem były sygnały o amplitudzie bliskiej 5V. Później standardem zasilania układów cyfrowych stało się napięcie 3,3V. Z czasem wprowadzono układy zasilane niższymi napięciami: 3,0V, 2,7V i jeszcze mniejszymi, nawet 1,5V. Zmniejszenie amplitudy nieco zmniejsza zakłócenia, ale kolejnym problemem jest to, że sygnały są niesymetryczne, dodatnie względem masy. Przy dłuższych liniach dochodzi problem odbić szybkiego sygnału od końców linii, co oznacza dodatkowe błędy i wymusza minimalizację odbić przez dodawanie na końcach linii rezystancji dopasowującej do oporności falowej toru przesyłowego.

Problem odbić, wytwarzania zakłóceń i wrażliwości na zewnętrzne zakłócenia jest bardzo poważny. Już we wczesnych latach 90. zaproponowano rozwiązanie skutecznie minimalizujące problem zakłóceń. Omówimy je w następnym odcinku.

Tematyka materiału: mikroprocesory, OLED, I2C, SPI, RGB
AUTOR
Źródło
Elektronika dla Wszystkich grudzień 2019
Udostępnij
UK Logo