Obsługa wyświetlacza graficznego Nokii 3310. Problemy i ich rozwiązania

Obsługa wyświetlacza graficznego Nokii 3310. Problemy i ich rozwiązania

Wyświetlacze LCD od telefonów komórkowych idealnie nadają się do zastosowania w układach z mikrokontrolerem. Telefon Nokia 3310 nie jest już oferowany przez operatorów sieci komórkowych, ale olbrzymia liczba sprzedanych egzemplarzy spowodowała, że można już za  ok. 10 złotych kupić do niego wyświetlacz oferowany jako część zamienną. Matryca wyświetlacza ma rozdzielczość 84×48 piksele. W trybie tekstowym można wyświetlić 14 znaków w 6 liniach. W porównaniu z możliwościami popularnych wyświetlaczy alfanumerycznych (najczęściej 2×16 lub 2×20 znaków) jest to dosyć sporo. Ponieważ wyświetlacz został zaprojektowany do urządzenia przenośnego, to pole wyświetlania matrycy LCD nie jest zbyt duże (30×24mm). Grubość modułu jest również niewielka (ok. 3 mm).

Sterowanie wyświetlaczem było już wielokrotnie opisywane w Internecie. Można też znaleźć sporo opisów z gotowymi procedurami. Sam wykorzystywałem ten wyświetlacz jako część interfejsu użytkownika w kilku swoich projektach. I pewnie jego popularność byłaby niczym nie zagrożona, gdyby nie to, że pojawiły się problemy ze sterowaniem niektórych egzemplarzy.
Ostatnio w moje ręce trafiło kilka wyświetlaczy, z których część zachowywała się zupełnie poprawnie, a część nie chciała działać z moimi, wielokrotnie sprawdzonymi procedurami sterującymi. Po wykonaniu serii prób modyfikacji tych procedur zacząłem szukać w sieci czy ktoś jeszcze nie ma podobnych problemów. Okazało się, że jest to znany problem i co gorsza nikt nie potrafił podać sposobu by go rozwiązać. Wszystkie  znane mi opisy wyświetlacza od telefonu Nokia 3310 podają, że ma wbudowany sterownik firmy NXP typu PCD8544 i na podstawie dokumentacji do tego sterownika są wykonane procedury sterujące. Przypomnijmy pokrótce budowę i sposób sterowania sterownika PCD8544.
Sterownik w swojej strukturze ma wbudowany:
-  interfejs do sterowania matrycy LCD, zawierający wzmacniacze kolumn i wierszy, układ wytwarzania napięć zasilających matrycę oraz układ kompensacji ustawienia kontrastu w f. temperatury,
-  pamięć RAM,
-  szeregowy interfejs użytkownika.
Nas będzie interesował fizyczny interfejs użytkownika, organizacja pamięci RAM i sposób wymiany informacji pomiędzy mikrokontrolerem, a sterownikiem. Przesyłanie danych pomiędzy sterownikiem wyświetlacza i mikrokontrolerem odbywa się przez standardowy, 8-bitowy interfejs szeregowy SPI. Większość bardziej rozbudowanych mikrokontrolerów ma wbudowany sprzętowy interfejs SPI. Również programowa implementacja interfejsu nie jest skomplikowana.

Rysunek 1. Połączenie dwóch układów interfejsem SPI

Interfejs SPI jest zbudowany z trzech linii: danych wejściowych DIN, danych wyjściowych DOUT, linii zegarowej SCLK. Opcjonalnie jest wykorzystywana linie SS (Slave Select). Połączenie dwóch układów interfejsem SPI pokazano na rysunku 1. SPI pracuje w układzie MASTER-SLAVE. Układ MASTER (najczęściej mikrokontroler) generuje sygnał zegarowy SCK. Sterownik wyświetlacza jest układem SLAVE. Ponieważ dane są tylko wpisywane do sterownika, to wykorzystywana jest tylko linia danych wejściowych (z punktu widzenia sterownika PCD8544).
Interfejs szeregowy PCD8544 został uzupełniony o dodatkowe linie:
-  SCE: linia wyboru układu – odpowiednik Chip Select,
-  D/C: linia wyboru przeznaczenia wysyłanych danych: pamięć RAM wyświetlacza – dane (D/C=1) lub rejestr sterujący- rozkazy (D/C=0),
-  RES: linia zerowania sterownika wyświetlacza.
Na rysunku 2 pokazano przebiegi czasowe przy przesyłaniu bajtu do wyświetlacza. Transmisja rozpoczyna się od uaktywnienia linii SCE (stan niski). Dane z linii SDIN są wpisywane do rejestru sterownika przy narastającym zboczu zegara SCLK. Na rysunku 1, po wpisaniu pojedynczego bajtu, SCE staje się nieaktywne. Sterownik akceptuje wpisywanie bloku bajtów z aktywnym SCE ( bez konieczności przechodzenia w stan wysoki tej linii po każdym przesłanym bajcie). Przyspiesza to przesyłanie danych, ale może powodować przekłamania przy większych blokach i dużej prędkości transmisji. Każde opadające zbocze SCE synchronizuje transmisje i jeżeli pojawiają się problemy, to lepiej zastosować sterowanie linią SCE tak jak na rysunku 2.

Rysunek 2. Przesłanie bajtu do wyświetlacza

Na rysunku 3 pokazano wyprowadzenia wyświetlacza telefonu Nokia 3310. Napięcie zasilające wyświetlacz ( styk Vdd) może mieć wartość z zakresu 2,7V….3,3V. Napięcie zasilające matrycę wyświetlacza (6…8V) wytwarzane w wewnętrznej przetwornicy sterownika jest wyprowadzone na styk Vout. Do tego styku podłącza się zewnętrzny kondensator filtrujący 1uF. Wbudowana w sterownik pamięci RAM jest zapisywana poleceniami zapisywania danych  - linia D/C w stanie wysokim. Matryca wyświetlacza może wyświetlić 48 linii. Każda linia ma 84 piksele. Pamięć jest zorganizowana w 6 banków po 84 bajty. Taka organizacja wymusza logiczny podział pola wyświetlacza na 6 wierszy po 84 kolumny – rysunek 4. Wiersze są numerowane od 0 do 5, a kolumny od 0 do 83.
W czasie wpisywania danej do pamięci najpierw określamy, w którym będzie banku ( zmienna Y określająca wiersz na rysunku 4), a potem, który to będzie bajt w wierszu ( zmienna X określająca kolumnę na rysunku 4). Zapisanie całego banku odpowiada wyświetleniu jednego wiersza wyświetlacza. W trybie graficznym odpowiada to wyświetleniu jednej linijki tekstu.

1 - Vdd, 2 - SCLK, 3 - SDIN, 4 - D/C, 5- SCE, 6 - GND, 7 - Vout, 8 - RES

Rysunek 3. Widok wprowadzeń wyświetlacza

Każdy bajt pamięci RAM jest wyświetlany jako pionowy pasek 8 pikseli. Najwyżej położony piksel w pasku odpowiada najmniej znaczącemu bitowi, a najniżej położony piksel odpowiada najbardziej znaczącemu bitowi bajtu pamięci. W standardowym trybie wyświetlania ustawienie bitu w bajcie pamięci odpowiada „zapaleniu” piksela, a wyzerowanie odpowiada zgaszeniu piksela.
Przy wpisywaniu bajtów do pamięci RAM wykonywana jest przez sterownik PCD8544 automatyczna inkrementacja liczników wierszy i kolumn. Dostępne są ustawiane komendą Function Set dwa tryby automatycznego modyfikowania liczników: pionowy i poziomy. Tryb pionowy pokazano na rys. 5.

Rysunek 4. Organizacja pamięci RAM PCD8544

Rysunek 5. Adresowanie pionowe

Po każdym zapisie bajtu zwiększa się licznik wierszy. Po osiągnięciu maksymalnego numeru wiersza licznik jest zerowany, a licznik kolumn zwiększany o 1. Wysłanie 6 bajtów 0xFF. rozpoczynając od wiersza numer 0, powoduje wyświetlenie w pierwszej, lewej kolumnie pionowego paska przez całą wysokość wyświetlacza. Tryb adresowania pionowego można stosować  przy wpisywaniu pełnoekranowej bitmapy. Tryb adresowania poziomego pokazano na rysunku 6. Po zapisaniu każdego bajtu zwiększany jest licznik kolumn. Po osiągnięciu maksymalnego numeru kolumny jest zerowany on, a licznik wierszy jest zwiększany o 1. Ten tryb jest bardzo wygodny przy wyświetlaniu tekstu. Tryby można dowolnie przełączać w trakcie pracy wyświetlacza. Nie ma to wpływu na wyświetlaną informację.

Rysunek 6. Tryb adresowania poziomego

Sterownik akceptuje dwa zestawy komend: standardowy i rozszerzony. Komenda Function Set jest przeznaczona do programowania wyświetlacza w tryb obniżonego poboru energii, sposobu adresowania pamięci RAM i ustawiania zestawu komend (normalny lub rozszerzony). Kontrast wyświetlacza zależy od napięcia zasilającego matrycę LCD i jest ustawiany komendą Set Vop. Można ustawić 128 wartości, ale użyteczny zakres regulacji to wartości od 30 do 90. Komenda Temperature Control programuje jedną 4 predefiniowanych charakterystyk kompensacji temperaturowej kontrastu. Dla większości zastosowań można zastosować współczynnik 2 (TC1=1, TC0=0). Dla wyświetlacza od telefonu Nokia 3310 BIAS powinien mieć wartość 3.

 

Instrukcja

D/C

Bajt komendy

Opis

 

 

DB 7

DB 6

DB 5

DB 4

DB3

DB 2

DB 1

DB 0

 

H=0 lub H=1

NOP

0

0

0

0

0

0

0

0

0

Nic nie rób

Function Set

0

0

0

1

0

0

PD

V

H

Tryb obniżonego poboru energii (PD), adresowania (V) i komendy rozszerzone (H)

Write Data

1

D7

D6

D5

D4

D3

D2

D1

D0

Zapisanie pamięci RAM

H=0 podstawowy zestaw komend

Rezerwa

0

0

0

0

0

0

1

X

X

Nie używać

Display Control

0

0

0

0

0

1

D

0

E

Konfiguracja wyświetlacza

Rezerwa

0

0

0

0

1

x

x

X

X

Nie używać

Set Y address

0

0

1

0

0

0

Y2

Y1

Y0

Ustawienie licznika wierszy 0…5

Set X address

0

1

X6

X5

X4

X3

X2

X1

X0

Ustawienie licznika kolumn 0…83

H=1 Rozszerzony zestaw komend

Rezerwa

0

0

0

0

0

0

0

0

1

Nie używać

Rezerwa

0

0

0

0

0

0

0

1

X

Nie używać

Temperature Control

0

0

0

0

0

0

1

TC1

TC0

Współczynnik temperaturowy

Rezerwa

0

0

0

0

0

1

x

X

X

Nie używać

Bias System

0

0

0

0

1

0

BS2

BS1

BS0

Ustawienie BIAS

Rezerwa

0

0

1

X

X

X

x

X

X

Nie używać

Set Vop

0

1

Vop6

Vop5

Vop4

Vop3

Vop2

Vop1

Vop0

Napięcie zasilania matrycy (kontrast)

 

Bit

0

1

PD

Układ aktywny

Tryb obniżonego poboru mocy

V

Adresowanie poziome

Adresowanie pionowe

H

Podstawowy zestaw instrukcji

Rozszerzony zestaw instrukcji

D        E
0        0
0        1
1        0
1        1

Wyświetlacz wygaszony
Tryb normalny
Wszystkie segmenty zapalone
Wyświetlanie w inwersji

TC1  TC0
0          0
0          1
1          0
1          1

VLCD współczynnik temperaturowy 0
VLCD współczynnik temperaturowy 1
VLCD współczynnik temperaturowy 2 ( standardowy)
VLCD współczynnik temperaturowy 3

 

Rysunek 7. Zestaw komend PCD8544

 

Problemy

Do testowania wyświetlaczy użyłem modułu KAmodLCD1 (jednej z tych prostych rzeczy, które ułatwiają życie) i płyty ewaluacyjnej ZL30ARM z procesorem STM32. Pierwszym problemem jaki napotkałem było niewłaściwe zerowane pamięci wyświetlacza. Po włączeniu zasilania sterownik pamięci  nie inicjalizuje i są tam przypadkowe wartości. Dlatego zerowanie jest jednym ze składników procedury inicjalizacji. Pamięć sterownika PCD8544 ma 504 bajty (rysunek 5 i 6). Wystarczy wpisać do niej kolejno 504 bajty zerowe, żeby ekran wyświetlacza po inicjalizacji był czysty (listing 1). Jednak po wykonaniu takiego zerowania nie cały wyświetlacz jest czysty, co pokazano na fotografii 1. Wyglądało to tak jakby nie cała pamięć została zapisana zerami. Skoro tak to w pierwszej próbie zacząłem zwiększać liczbę wpisywanych danych. Zwiększanie ilości wpisywanych danych o niewielką wartość nie dawało żadnych efektów. Dopiero wpisanie ok. 550 bajtów zerowych spowodowało, że obszar na dole zaczynał się czyścić. Po wpisaniu 612 bajtów ekran został wyczyszczony, jednak nie w całości.

Listing 1. Zerowanie pamięci LCD

for(i=0;i<504;i++)
WriteData(0x00);  //zerowanie pamięci
                  //RAM wyświetlacza 

 

Fotografia 1. Problem z czyszczeniem ekranu wyświetlacza

Jeżeli dokładnie przyjrzeć się fot. 1 to można zauważyć, że obszar z przypadkowymi wartościami ma szerokość 11 pikseli. Pozostał wąski fragment o szerokości 3 pikseli. Wpisanie kolejnych 102 bajtów zlikwidowało również ten pasek. Z tych doświadczeń wynikały pewne wnioski.
Pierwszy to taki, że sterownik wyświetlacza ma inną organizację pamięci niż PCD8544, a zatem  nie może to być PCD8544. Z wyliczeń wpisywanych bajtów w trakcie zerowania wynika, że pamięć wyświetlacza tego sterownika odpowiada rozdzielczości w poziomie 102 pikseli. Najprawdopodobniej sterownik może sterować matrycami o rozmiarze 102×64 piksele, lub 102×65 pikseli. Wyjaśniałoby to też dlaczego procedury wyświetlania bitmap dla PCD 8544  nie działają w z tym wyświetlaczem – fotografia 2.
Procedura wyświetlania bitmapy działa podobnie jak zerowanie wyświetlacza. Z tablicy w której zapisana jest bitmapa pobieranych jest i wpisywanych do pamięci wyświetlacza kolejnych  504 bajtów. Jeżeli liczniki wierszy się inkrementują po zapisaniu 102 bajtów, a nie 84 bajtów jak w PCD8544, to wyświetlanie bitmap w ten sposób nie mogło się udać.

Fotografia 2. Nieprawidłowe wyświetlania bitmapy

Mając świadomość, że sterownik ma inna organizację można zmodyfikować procedurę  tak by  bitmapy były wyświetlane prawidłowo. Po ustawieniu licznika kolumn i wierszy na wartości zerowe trzeba wpisać pierwsze 84 bajty bitmapy. Potem trzeba wyzerować licznik kolumn, inkrementować licznik wierszy i wpisać kolejne 84 bajty bitmapy, i tak dalej. Zmodyfikowana procedura wpisywania bitmapy została pokazana na listingu 2, a procedura pozycjonowania na wyświetlaczu na listingu 3
Wróćmy jeszcze do zerowania wyświetlacza. Po wpisaniu 612 bajtów pozostał nie wyzerowany pasek o szerokości 3 pikseli. Biorąc pod uwagę organizację pamięci taki pasek był dość niepokojący, bo wpisanie każdego bajtu zajmuje pasek o szerokości 8 pikseli, co dla ekranu o wysokości 48 pikseli daje równo 6 pasków ( wierszy) i nie ma tutaj miejsca na wiersz i innej szerokości niż 8 pikseli.
Po próbach z wyświetlaniem tekstu okazało się, że rzeczywiście  jest przesunięcie w pionie o 3 piksele pomiędzy informacją na ekranie , a zawartością pamięci sterownika. (fotografia 3).

Listing 2. Zmodyfikowana funkcja zapisu bitmapy

//ustawia pozycję wyświetlania (14 kolumn, 6 wierszy)
void WriteBmp(const unsigned char *buffer)
{
  char j,k;
  for(k=0;k<6;k++){

  Poz(0,k);//pierwsza linijka
  for(j=0;j<84;j++)     //wpisywanie 84

                        //bajtów bmp
    WriteData(*buffer++);
}


Fotografia 3. Przesuniecie w pionie

Wyglądało to na kolejny programowy problem, ale tym razem o wiele trudniejszy do pokonania. Żeby mieć pewność, że wyświetlacz nie jest uszkodzony, został podłączony do telefonu Nokia 3310. Zgodnie z oczekiwaniami współpraca z telefonem przebiegała bez zarzutu. Było oczywiste, że sterownik wyświetlacza potrzebuje uzupełnienia o komendę lub komendy w trakcie inicjalizacji. Przeprowadzane próby wykazały, że prawidłowo działają wszystkie komendy z zestawu PCD8544, oprócz prawidłowego pozycjonowania w pionie pierwszej linijki. Ponadto, sterownik nie chciał wejść w tryb adresowania pionowego.

Listing 3. Pozycjonowanie na wyświetlaczu

void Poz(char x, char y)
{
    WriteCmd(y|0x40);     //l.wierszy=0

    WriteCmd(x|0x80);     //l.kolumn=0
}

Ponieważ nie wiadomo jaki sterownik zastosowano w wyświetlaczu żeby nie szukać przysłowiowej igły w stogu siana  pozostał tylko jeden sposób: „szpiegostwo przemysłowe”.  Do pól kontaktowych telefonu zostały przylutowane kable z przewodu wstążkowego zakończonego zaciskanym złączem IDC10.  Pomiędzy płytkę wyświetlacza, a kabel od telefonu została włączona przejściówka, do której zostały podłączone kable kanałów analizatora stanów logicznych LH1116 od oscyloskopu cyfrowego Rigol DS1052D (fotografia 4).

Fotografia 4. Zestaw do odczytywania komend wysyłanych do sterownika wyświetlacza

Do analizatora dołączono wszystkie linie sterujące wyświetlaczem. Na rysunku 8 pokazano zrzuty ekranu oscyloskopu w trakcie analizy sygnałów wysyłanych przez płytkę sterującą wyświetlaczem.

a)cały proces inicjalizacji z zerowaniem pamięci

b) fragment wysyłania komend inicjalizacyjnych (bez zerowania

c)szczegóły wysyłania komendy
Rysunek 8. Zrzuty ekranu analizatora logicznego

Na rysunku 8a można zauważyć, że zerowanie pamięci jest przeprowadzane przed jej inicjowaniem i jest podzielone na 6. Przebiega podobnie jak zapisywanie bitmapy pokazane na listingu 2. Zamiast wpisywać do sterownika 612 bajtów zerowych, można wykonać zerowanie w wierszach po 84 bajty, tak jak pokazano na listingu 4. Takie zerowanie będzie działało  w przypadku, kiedy sterownik wyświetlacza będzie miał jeszcze inną rozdzielczość, niż 102 piksele w poziomie.

Listing 4. Zmodyfikowana  procedura zerowania pamięci

void ClrDisp (void)
{
  char j,k;
  for(k=0;k<6;k++){

  Poz(0,k);//pierwsza linijka
  for(j=0;j<84;j++) //wpisywanie 84
                    //bajtów zerowych
  WriteData(0);
}

Analiza sygnałów odczytanych przez analizator w końcu dała odpowiedź na pytanie o prawidłową inicjalizację sterownika. Telefon wysyła do sterownika wyświetlacza sekwencję komend:
-  0x21: komendy rozszerzone, adr. poziome,
-  0x05: ustawienie współczynnika temperaturowego lub nieznana komenda,
-  0x14: ustawienie wsp. multipleksowania (BIAS),
-  0xBF: komenda ustawiania kontrastu (napięcia zasilania matrycy),
-  0x20: wybór zestawu komend podstawowych,
-  0x0C: konfiguracja wyświetlacza (tryb normalny).
Jedyną różnicą pomiędzy inicjalizacją PCD8544 stosowaną przeze mnie, a inicjalizacją wysłaną przez telefon, była inna komenda współczynnika temperaturowego. Przy wysłaniu komendy 0x06  wyświetlacz zachowywał się jak na fot. 3, ale po wysłaniu 0x05 przesunięcie znikło – fot. 6 . Podejrzewam ze w badanym sterowniku wyświetlacza komenda o kodzie 0x05 ma inne znaczenie niż ustawienie współczynnika temperaturowego. Na fotografiach 5 i 6 pokazano wyświetlanie bitmapy i tekstu przy  prawidłowo zainicjalizowanym wyświetlaczu. Procedurę inicjalizacji umieszczono na listingu 4.

Fotografia 5. Wyświetlanie bitmapy

Fotografia 6. Wyświetlanie tekstu

 

Listing 5 Inicjalizacja wyświetlacza

//inicjalizacja sterownika
void InitDisNok(void)
{
  int i;     
  WriteCmd(0x21);  //komendy rozszerzone
  WriteCmd(0x05);  //komenda 
  WriteCmd(0xc8);  //Ustawienie Vop
  WriteCmd(0x06);  //korekcja temperatury dla PCD8544
  WriteCmd(0x14);  //współczynnik multipleksowania
  WriteCmd(0x20);  //komendy standardowe, adresowanie poziome
  WriteCmd(0x01);
  WriteCmd(0x0c);  //tryb wyświetlania: standard mode
  WriteCmd(0x40);  //zerowanie l.wierszy
  WriteCmd(0x80);  //zerowanie l.kolumn
  ClrDisp();       //zerowanie pamięci
  //for(i=0;i<612;i++)//612
  //WriteData(0x00);//zerowanie pamięci RAM wyswietlacza
}

Tak zainicjalizowany wyświetlacz pracuje poprawnie i z wyświetlaczem z PCD8544, i z testowanym wyświetlaczem. Przykład tego wyświetlacza pokazuje, że nawet powszechnie znane dane urządzeń stosowanych w technice profesjonalnej nie zawsze są kompletne. Takie elementy jak wyświetlacze od telefonów, są  oferowane tylko jako podzespoły  przeznaczone dla serwisu. Wykorzystanie ich w innym celu może powodować problemy takie, jak tutaj opisywane. Na szczęście w tym przypadku wszystkie udało się rozwiązać.

Tomasz Jabłoński
tomasz.jablonski@easy-soft.net.pl

http://www.tomaszbogusz.blox.pl/

Dodaj nowy komentarz

Zawartość pola nie będzie udostępniana publicznie.