41
Programowanie
Elektronika dla Wszystkich
Dziś chcę zaproponować Ci pracę z wyświet-
laczem z telefonu NOKIA3310. Ponieważ
wyświetlacz ten wyposażony jest w interfejs
SPI, dowiemy się także, jak go obsługiwać.
Telefon Nokia3310, a także jego prosty,
monochromatyczny wyświetlacz graficzny
zapewne są znane wielu Czytelnikom.
Aktualna cena nowego wyświetlacza waha
się w granicach 7 zł za najprostszy model. Jest
to cena naprawdę rewelacyjna, biorąc pod
uwagę, co otrzymujemy w zamian. Wybrane
właściwości wyświetlacza zebrałem w tabeli
12. Na szczególną uwagę zasługuje szeroki
zakres temperatur pracy oraz niewielki pobór
prądu. Porównanie możliwości sprawia, że
typowe wyświetlacze alfanumeryczne pozo-
stają daleko w tyle.
Sterownik PCD8544
– podstawy
Na zewnątrz modułu wyświetlacza wyprowa-
dzono złącze umożliwiające programowanie
sterownika z wykorzystaniem interfejsu sze-
regowego. Interfejs ten jest w dużej mierze
zgodny z SPI. Możliwy jest
jedynie zapis danych i
instrukcji do sterownika. Ste-
rownik nie posiada możliwoś-
ci wysłania jakiejkolwiek
informacji zwrotnej. Interfejs
tworzą wyprowadzenia ozna-
czone jako SCLK – zegar
danych – dane są próbkowane
na narastającym zboczu,
SDIN – dane wejściowe, SCE
– stan niski powoduje wybra-
nie urządzenia, D/C – aktualnie przesyłany
bajt jest daną (1) lub instrukcją (0), RES –
zerowanie sterownika. Układ wyprowadzeń
modułu przedstawia rysunek 55.
Po włączeniu zasilania wyświetlacza jego
stan jest nieokreślony. W czasie maksymalnie
30ms należy przeprowadzić jego zerowanie po-
przez podanie krótkiego, niskiego impulsu na
wejście RES. W tym momencie wyświetlacz
zostanie wyłączony, a sterownik będzie gotowy
do przyjmowania komend. Dostępny zestaw
komend sterujących przedstawia tabela 13.
Komendami rozszerzonymi zajmiemy się
za chwilę. Teraz warto poznać samą ideę dzia-
łania sterownika. Pewną niedogodnością
naszego wyświetlacza jest to, że nie posiada
on wbudowanego generatora znaków. Ozna-
cza to, że jeśli będziemy chcieli napisać coś
na wyświetlaczu, to na procesor spada zada-
nie pamiętania wzorców znaków oraz ich
malowanie. Pamięć wyświetlacza jest zorga-
nizowana tak, aby wypisywanie tekstu, stan-
dardową czcionką o wysokości 8 pikseli, było
możliwie proste – spójrz na rysunek 56.
W podstawo-
wym zestawie
komend możemy
wybrać aktualny
adres zapisu w
postaci numeru
kolumny oraz
bajtu odpowiada-
jącego ośmiu
pikselom w ko-
lumnie. Po doko-
naniu zapisu każ-
dej danej, aktual-
ny adres zapisu
jest przesuwany.
Istnieją dwie
możliwości, które
PP
PP
rr
rr
oo
oo
gg
gg
rr
rr
aa
aa
m
m
m
m
oo
oo
w
w
w
w
aa
aa
nn
nn
ii
ii
ee
ee
pp
pp
rr
rr
oo
oo
cc
cc
ee
ee
ss
ss
oo
oo
rr
rr
óó
óó
w
w
w
w
w
w
w
w
jj
jj
ęę
ęę
zz
zz
yy
yy
kk
kk
uu
uu
CC
CC
część 13
Właściwości wyświetlacza z telefonu NOKIA3310
Sterownik:
PCD8544 (Philps)
Rozdzielczość:
48x84 (6x14)*
Zalecane napięcie zasilania:
2,7 – 3,3V (max. 5V)
Pobór prądu w trybie aktywnym
300
µA
Zakres temperatur pracy:
od -25 do +70°C
Interfejs:
Szeregowy, zgodny z SPI, 3 linie + 2 sterujące
Maksymalna częstotliwość SCLK:
4MHz
* liczba znaków 5x8
Rys. 55 Wyprowadze-
nia wyświetla-
cza
Rys. 56 Organizacja
pamięci
Komendy sterujące
Instrukcja
Bajt komendy
Opis
D7 D6 D5 D4 D3 D2 D1 D0
Każdy zestaw komend
NOP
Function Set
0
0
1
0
0 PD V
H
PD – tryb power down
V – adresowanie w trybie pionowym
H – rozszerzony zestaw komend
Zestaw komend podstawowych
Zarezerwowana
0
0
0
0
0
1
-
-
Nie wywoływać
Display Control
0
0
0
0
1
D
0
E
DE = 00 wyświetlacz wygaszony
DE = 01 test (wszystkie piksele włączone)
DE = 10 tryb standardowy
DE = 11 tryb negatywowy
Zarezerwowana
0
0
0
1
-
-
-
-
Nie wywoływać
Set Y address
0
1
0
0
0 Y2 Y1 Y0
0
≤ Y < 6
Set X address
1 X6 X5 X4 X3 X2 X1 X0
0
≤ X < 84
Zestaw komend rozszerzonych
Zarezerwowana
0
0
0
0
0
0
0
1
Nie wywoływać
Zarezerwowana
0
0
0
0
0
0
1
-
Nie wywoływać
Temperature Control
0
0
0
0
0
1
T1 T0
T – krzywa kompensacji temperaturowej
Standardowo T=2
Zarezerwowana
0
0
0
0
1
-
-
-
Nie wywoływać
Bias System
0
0
0
1
0 B2 B1 B0
Sposób sterowania elektrodami
Dla 48 linii B = 3
Zarezerwowana
0
1
-
-
-
-
-
-
Nie wywoływać
Set VOP
1 V6 V5 V4 V3 V2 V1 V0
Napięcie zasilania matrycy (kontrast)
Bezpieczny zakres: 32-88
Standardowo: 72
Tabela 12 Wybrane właściwości wyświetlacza
Tabela 13 Zestaw komend sterujących wyświetla-
czem
przedstawiają rysunki 57 i 58. Adresowanie
w trybie poziomym jest bardzo wygodne do
pisania tekstu. Adresowanie w trybie piono-
wym może okazać się przydatne w niektórych
przypadkach rysowania, my jednak teraz nie
będziemy z niego korzystać.
Sterownik PCD8544
– konfiguracja
Aby przejść do konfiguracji wyświetlacza,
należy przełączyć się na zbiór instrukcji roz-
szerzonych. Robimy to poprzez wydanie
komendy Function Set z ustawionym bitem
H. Od tej chwili aż do ponownego wywołania
Function Set z wyzerowanym bitem H lub do
chwili zerowania sterownika, aktywny jest
rozszerzony zestaw instrukcji. Do pełnej kon-
figuracji służą trzy, znajdujące się tutaj
komendy.
Pierwsza to Temperature Control. Umożli-
wia ona wybranie jedej z czterech krzywych
korekcji napięcia zasilania matrycy w zależ-
ności od temperatury. W założeniu ma to
zapewnić stały kontrast niezależnie od warun-
ków, w jakich wyświetlacz pracuje. Nasz
wyświetlacz najlepiej współpracuje z krzywą,
której dano numer 2 i taką wartość też wybie-
rzemy.
Przy kolejnej komendzie – Bias System,
nie mamy praktycznie żadnego wyboru.
Zgodnie z dokumentacją, dla 48 rzędowych
wyświetlaczy najlepszym ustawieniem jest 3.
Ostatnia komenda – Set V
OP
, będzie nam
już znacznie bliższa. Za jej pomocą ustawia-
my kontrast wyświetlacza. Mimo tego, że
fizycznie istnieje możliwość wybrania war-
tości 0-127, nie należy przekraczać zakresu
32-88. Przy zmniajszaniu się temperatury
napięcie matrycy rośnie. Jeśli wybierzemy
zbyt dużą wartość dla temperatury pokojowej,
może ono przekroczyć wartość dopuszczalną
po wyniesieniu urządzenia na mróz. W poda-
nych „bezpiecznych” granicach można usta-
wianą wartość zmieniać wedle uznania.
Jak się okazuje, mimo tego, że mamy moż-
liwość ustawiania bardzo niskopoziomowych
parametrów pracy wyświetlacza, nastaw
nie ma przerażająco dużo i wyświetlacz
można szybko doprowadzić do działania.
Przygotowanie sprzętu
Najpopularniejsza wersja wyświetlacza
do NOKII3310 jest dostępna w postaci
całego modułu, zawierającego sam wy-
świetlacz, ramkę oraz głośnik. Jest to
dość spory element i pewnie kusi Cię,
aby wydobyć wyświetlacz z ramki. Jed-
nak, przynajmniej na początku, tego nie
rób. Ramka zapewnia prawidłowy kon-
takt metalowych blaszek ze ścieżkami
znajdującymi się na szkle wyświetlacza.
Do czasu, aż przekonasz się, że część
sprzętowa działa bez zarzutu, nie pozby-
waj się tego ułatwienia. Fotografia 3
pokazuje wygląd omawianego modułu.
Fotografia 4 natomiast przedstawia złą-
cze, do którego będziemy lutować prze-
wody.
Wyświetlacz będzie przyłączony do złącza
LV-OUT. Zanim to zrobimy, ustaw przy po-
mocy potencjometru PR1 napięcie zasilające
wyświetlacz na wartość około 3V. Napięcie to
możesz mierzyć między 1 a 2 wyprowadze-
niem złącza LV-OUT.
Przygotuj przewód taśmowy zakończony
z jednej strony gniazdem do złącza LV-OUT.
Schemat połączeń pokazuje rysunek 59.
Połączenie zostało wykonane w taki sposób,
aby możliwe było wykorzystanie sprzętowe-
go interfejsu SPI do komunikacji z wyświetla-
czem.
Wykonywanie połączenia rozpocznij od
wlutowania kondensatora między pinami 6 a
7. Następnie pocynuj styki oraz końcówki
przewodów (fotografia 5). Połączenie tych
elementów wymaga trochę zręczności. Jeśli
pechowo jakieś punkty lutownicze się zleją,
proponuję od razu łapać za odsysacz. Blaszki
mają taki kształt, że inaczej dość trudno usu-
nąć z nich tak wykonane zwarcie.
Gdy uda Ci się szczęśliwie przebrnąć
przez etap montażu układu, pozostaje tylko
formalne podpięcie wyświetlacza oraz usta-
wienie zworki PullUP w pozycji ON.
42
Programowanie
Elektronika dla Wszystkich
Rys. 57 Adresowanie w trybie pozio-
mym
Rys. 58 Adresowanie w trybie piono-
wym
Rys. 59 Połączenie wyświetlacza
z AVT3505
Fot. 5 Wyświetlacz i przewód gotowe
do lutowania
Fot. 3 Wyświetlacz wraz z ramką oraz
przygotowanym przewodem
Fot. 4 Złącze wyświetlacza
43
Elektronika dla Wszystkich
Uruchomienie wyświetlacza
Proponuję teraz napisanie szybko programu,
który umożliwi przekonanie się, że „to żyje”.
Projekt jednak będziemy od początku pisać
tak, aby móc go stopniowo rozwijać, aż do
pełnej aplikacji.
Utwórz nowy projekt. W moim przypadku
nazwałem go lcd3310test. Od razu skopiuj
z poprzedniej części plik makra.h. Utwórz
plik definicji sprzętu: harddef.h, części głów-
nej programu: main.c oraz pliki zawierające
funkcje obsługi wyświetlacza: lcd3310.c oraz
lcd3310.h. Pamiętaj o dodaniu obu plików
źródłowych w pliku makefile.
Od razu wypełnij plik harddef.h zgodnie
z listingiem 160.
Do pliku lcd3310.c dodajemy potrzebne
pliki nagłówkowe, zgodnie z listingiem 161.
Dla własnej wygody, w pliku nagłówkowym
dodajemy definicje nazw komend, zgodnie
z listingiem 162. Listing 163 pokazuje pro-
pozycję procedur niskiego poziomu. Założe-
nie jest takie, że linia D/C jest w spoczynku
utrzymywana w stanie wysokim. Jeśli w do-
wolnym momencie wywołamy funkcję
lcd_Send, zostanie to potraktowane jako
wysłanie danych. Jeśli wyślemy komendę,
zaraz po zakończeniu wysyłania linia D/C
powraca w stan wysoki. To założenie uprasz-
cza trochę pisanie programu, jednocześnie
oszczędzając wykonywania dodatkowej
instrukcji przy wysyłaniu danych. Trzeba
wziąć pod uwagę, że statystycznie do
wyświetlacza będziemy wysyłać znacznie
więcej danych niż komend.
Wróć teraz do pliku nagłówkowego.
Zadeklarujemy tutaj obie funkcje: lcd_Send
oraz lcd_Command. Ta druga pojawia się w
pliku nagłówka po to, aby możliwe było
umieszczenie niżej statycznej funkcji
widocznej na listingu 164. Zawartość listingu
powinna pojawić się w pliku lcd3310.h. Sta-
tyczność tej funkcji lcd_GoTo ma spore zna-
czenie dla optymalizacji. Najczęściej będzie
ona wywoływana ze stałymi wartościami x
i y. W takim przypadku wszystkie obliczenia
wykona kompilator, a funkcja zostanie rozwi-
nięta tylko do dwóch wywołań funkcji
lcd_Command.
Teraz jesteśmy gotowi, aby w prosty i czy-
telny sposób przeprowadzić inicjację wy-
świetlacza zgodnie z listingiem 165. Pamiętaj
o umieszczeniu deklaracji funkcji inicjującej
wyświetlacz w pliku nagłówkowym.
Przerwijmy na chwilę pisanie biblioteki
LCD i przejdźmy do pliku głównego. Prosty
kod z listingu 166 umożliwi włączenie
wyświetlacza. Jeśli nie zerujemy pamięci wy-
świetlacza, po włączeniu jej zawartość jest
przypadkowa. Oznacza to w praktyce, że po
wykonaniu aktualnego programu na wyświet-
Idea rozwiązania
Oszczędne sterowanie wyświetlaczem z
telefonu NOKIA3310
Bardzo prawdopodobne, że spotkałeś się już z wyświetla-
czem, który dziś przedstawiam. Ze względu na prostotę
sterowania oraz cenę jest to element dość chętnie stoso-
wany przez elektroników. Większość propozycji sterowa-
nia, jakie udało mi się do tej pory znaleźć, zakładało zało-
żenie w pamięci RAM bufora danych wyświetlacza oraz
ich przesyłanie dopiero po wcześniejszym przygotowaniu
obrazu. Jest to rozwiązanie bardzo wygodne, dające
ponadto możliwości rysowania dość złożonych figur.
Problemem w rysowaniu na naszym wyświetlaczu jest to,
że nie możemy odczytać wartości wcześniej wpisanej do
komórki pamięci. Chcąc narysować jakiś obiekt, zmienia-
my zawsze całe 8 pikseli.
Jeśli jednak będziemy chcieli pisać zawsze bezpośred-
nio na wyświetlacz, uzyskamy program zużywający mało
zasobów. Ponadto program ten często okaże się szybszy
od programu korzystającego z bufora. Chcę pokazać dziś,
że obsługa naszego wyświetlacza ma szanse zmieścić się
nawet do procesora ATtiny2313.
Bez bufora – bez czyszczenia
Gdy wszystkie dane są wysyłane na ekran bez wcześniej-
szego buforowania, trzeba zdać sobie sprawę z pewnego
nieoczywistego ograniczenia. Jeśli będziemy wypisywać
dane na wyświetlaczu w trybie ciągłym (na przykład war-
tości pomiarów), nie powinniśmy przed każdym wypisa-
niem czyścić wyświetlacza. Takie działanie sprawi, że
wypisywany tekst stanie się nieczytelny (efekt na
wyświetlaczu LCD jest gorszy niż migotanie – obraz staje
się zupełnie nieczytelny). Jeśli piszemy bezpośrednio na
ekran, należy unikać jego czyszczenia.
Zauważ, że gdybyśmy mieli bufor obrazu w pamięci
RAM, także nie czyścilibyśmy wyświetlacza. Czyszczony
byłby najwyżej bufor, a następnie przesyłany na zapełnio-
ny wyświetlacz.
Listing 160 Plik harddef.h
#ifndef HARDDEF_H_INCLUDED
#define HARDDEF_H_INCLUDED
// LCD
#define LCD_PORT B
#define LCD_RES 2
#define LCD_SCK 7
#define LCD_SD 5
#define LCD_DC 3
#define LCD_SCE 4
#endif /
/HARDDEF_H_INCLUDED
Listing 161 Pliki nagłówkowe w lcd3310.c
#include <avr/io.h>
#include <inttypes.h>
#include "harddef.h"
#include "makra.h"
#include "lcd3310.h"
Listing 162 Definicja komend
// H = X
#define LCDC_FS 0x20
#define LCDC_FS_BASIC 0
#define LCDC_FS_EXTENDED 0x01
#define LCDC_FS_HORADDR 0
#define LCDC_FS_VERTADDR 0x02
#define LCDC_FS_PD 0x04
// H = 0 (BASIC)
#define LCDC_DC 0x08
#define LCDC_DC_BLANK 0
#define LCDC_DC_NORMAL 0x04
#define LCDC_DC_ALLON 0x01
#define LCDC_DC_INVERT 0x05
#define LCDC_XADR 0x80
#define LCDC_YADR 0x40
// H = 1 (EXTENDED)
#define LCDC_TEMP 0x04
#define LCDC_BIAS 0x10
#define LCDC_VOP 0x40
Listing 163 Procedury niskiego poziomu
void
lcd_Send(uint8_t data)
{
uint8_t n;
// SCK w stan niski
PORT(LCD_PORT) &= ~(
1
<<
LCD_SCK);
// Aktywacja interfejsu
PORT(LCD_PORT) &= ~(
1
<<
LCD_SCE);
for
(
n=
8
;
n!=
0
; —
n)
{
// Wyprowadzenie bitu
if
(
data >=
128
)
PORT(LCD_PORT) |=
1
<<
LCD_SD;
else
PORT(LCD_PORT) &= ~(
1
<<
LCD_SD);
// Sygnał zegarowy i przesunięcie
PORT(LCD_PORT) |=
1
<<
LCD_SCK;
data <<=
1
;
PORT(LCD_PORT) &= ~(
1
<<
LCD_SCK);
}
// Wyłączenie interfejsu
PORT(LCD_PORT) |=
1
<<
LCD_SCE;
}
// Wysłanie komendy do wyświetlacza
void
lcd_Command(uint8_t command)
{
PORT(LCD_PORT) &= ~(
1
<<
LCD_DC);
lcd_Send(command);
// natychmiast do trybu danych
PORT(LCD_PORT) |=
1
<<
LCD_DC;
}
Listing 164 Funkcja lcd_GoTo w lcd3310.h
static inline void
lcd_GoTo(uint8_t x, uint8_t y)
{
lcd_Command(LCDC_XADR|x);
lcd_Command(LCDC_YADR|y);
}
Listing 165 Inicjacja wyświetlacza
void
lcd_Init(
void
)
{
// Zerowanie.
PORT(LCD_PORT) &= ~(
1
<<
LCD_RES);
asm volatile
(
„nop\n\t nop\n\t“
::);
PORT(LCD_PORT) |=
1
<<
LCD_RES;
// Wysyłanie sekwencji startowej
lcd_Command(LCDC_FS|LCDC_FS_EXTENDED);
lcd_Command(LCDC_VOP|
72
);
lcd_Command(LCDC_TEMP|
2
);
lcd_Command(LCDC_BIAS|
3
);
lcd_Command(LCDC_FS|LCDC_FS_BASIC|
LCDC_FS_HORADDR);
lcd_Command(LCDC_DC|LCDC_DC_NORMAL);
lcd_GoTo(
0
,
0
);
}
Listing 166 Pierwsze uruchomienie
#include <avr/io.h>
#include <inttypes.h>
#include „harddef.h“
#include „makra.h“
#include „lcd3310.h“
int
main(
void
)
{
// Inicjacja portów
DDR(LCD_PORT) =
1
<<
LCD_RES |
1
<<
LCD_SCK |
1
<<
LCD_SD |
1
<<
LCD_DC |
1
<<
LCD_SCE;
PORT(LCD_PORT) =
1
<<
LCD_SCK |
1
<<
LCD_SD |
1
<<
LCD_DC |
1
<<
LCD_SCE;
lcd_Init();
return
0
;
}
44
Programowanie
Elektronika dla Wszystkich
laczu powinny pojawić się dość fantazyjne
wzory. Jeśli na ekranie nic się nie pojawiło –
skontroluj część sprzętową.
Uwaga: Jeśli w poprzedniej części pisa-
łeś program bootloadera, to aktualnie masz
dwie możliwości wpisania programu do
procesora:
1. Przez bootloader – w takim przypad-
ku, jeśli bootloader został prawidłowo
skonfigurowany, możesz pracować na nim
bez zmian.
2. Przez programator szeregowy
– w takim przypadku pamiętaj o wyzero-
waniu bitu konfiguracyjnego BOOTRST.
Rysowanie obrazka
Gdy tylko udało nam się uruchomić wyświet-
lacz, wszystkie jego możliwości stoją przed
nami otworem. Zacznijmy od prostego ryso-
wania obrazka z pamięci FLASH na ekranie.
Dane z obrazka będą odwzorowywać w bez-
pośredni sposób dane, które mają być prze-
słane do wyświetlacza. Naszym zadaniem
jest jedynie odpowiednie ustawianie adre-
sów oraz przesyłanie danych pobranych
z pamięci. Tak zdefiniowany obrazek w za-
sadzie nie nadaje się do ręcznej edycji. Aby
ułatwić sobie i czytelnikom zadanie, napisa-
łem specjalną aplikację realizującą zamianę
bitmapy na dane inicjujące odpowiednią tab-
licę dwuwymiarową. Program ten będzie
dostępny na stronie Elportalu razem z ko-
dem źródłowym.
Funkcję wyświetlającą tak spreparowane
obrazki przedstawia listing 167. Aby była ona
kompilowana prawidłowo, w pliku lcd3310.h
dołącz nagłówek <avr/pgmspace.h> Funkcja
ta została zbudowana w taki sposób, że umoż-
liwia wyświetlenie w dowolnym miejscu
ekranu obrazka o dowolnym rozmiarze. Nale-
ży zdawać sobie sprawę, że w tak prostym
rozwiązaniu „dowolność” w pionie jest ogra-
niczona przez sposób działania wyświetlacza.
Adres y jest podawany w bajtach, a nie bez-
pośrednio w liniach. Funkcja nie pilnuje tego,
czy obrazek nie wystaje poza obszar ekranu.
Jeśli tak się stanie, obrazek będzie „zawinię-
ty” na drugą stronę wyświetlacza.
Wywołanie funkcji lcd_Load może wyglą-
dać tak jak na listingu 168. W przykładzie
img to dwuwymiarowa tablica utworzona
przez wspomniany program realizujący kon-
wersję. Makro ELEMS nie powinno Ci być
już obce. W tym przypadku umożliwia ono
odzyskanie informacji o wymiarach kopiowa-
nego obrazka na podstawie wymiarów tablicy.
Pisanie tekstu
Rysowanie obrazków daje bardzo ciekawe
efekty. Zmieniając obrazki kilka, kilkanaście
razy na sekundę uzyskamy wrażenie animacji.
Można sobie nawet wyobrazić program, które-
mu taka możliwość wystarczy. Często jednak
cenna okazuje się możliwość napisania dowol-
nego tekstu. Jak już wspominałem, nasz
wyświetlacz nie posiada wbudowanego gene-
ratora znaków. Oznacza to, że każdą literkę
będziemy rysować w podobny sposób, jak
rysowaliśmy dowolny obrazek. Po szczegóły
odsyłam do odpowiedniej ramki. Listing 169
pokazuje funkcję realizującą wyświetlenie jed-
nego znaku. Stąd do wypisania całego tekstu
już prosta droga, co udowadnia listing 170.
Bardziej dociekliwi Czytelnicy zauważą,
że drukujemy napis z pamięci RAM. Zwykle
tworzyliśmy też bliźniaczą funkcję drukującą
napisy bezpośrednio z pamięci FLASH. Tym
razem utworzenie odpowiedniej funkcji pozo-
stawiam chętnym. Jest to bardzo proste zada-
nie.
Wspomniałem o tym, że do końca naszej
tablicy możemy dodać polskie znaki. Raczej
nie ma sensu umieszczać polskich znaków,
tak aby były dostępne bezpośrednio w łańcu-
chach. Lepiej będzie posłużyć się w łańcuchu
podaniem odpowiedniego kodu. Polskie znaki
co prawda znajdują się w tabeli ASCII, ale są
one rozsiane po końcowych kodach. Ostatnia
litera ,ó’ ma kod 0xF3. Oryginalnie nasza tab-
lica kończy się znakiem ,z’ o kodzie 0x7A.
Na pewno jednak w dobry nastrój powinno
wprowadzić nas to, że własnych znaków
możemy zdefiniować w zasadzie dowolną
ilość i nie będzie konieczności stosowania
sztuczek, rodzaju dynamicznego ich przydzie-
lania.
Spróbuj samodzielnie rozszerzyć tablice
o własne znaki. Propozycja rozwiązania,
pojawi się na stronie Elportalu. Aktualna tab-
lica znaków nie będzie rozwijana w ramach
kursu – następnym razem będziemy pisać
w nieco inny sposób.
Sprzętowy SPI
Do tej pory nie wykorzystaliśmy tego, że
procesor zestawu AVT3505 posiada sprzę-
towy interfejs SPI, który może być wyko-
rzystany do komunikacji z wyświetla-
czem. W celach eksperymentalnych pro-
ponuję napisać teraz program w taki spo-
sób, aby umożliwiał przełączanie się mię-
dzy opcją ze sprzętowym i programowym
interfejsem szeregowym. Umożliwi to
porównanie wydajności obu rozwiązań.
Zmiany w funkcji lcd_Send przedstawia
listing 171. Listing 172 przedstawia
natomiast część kodu odpowiedzialną za
inicjację sprzętowego interfejsu SPI.
W ustawionym trybie pracy, przy taktowa-
niu procesora zegarem 8MHz, dane będą
wysyłane interfejsem szeregowym z zega-
rem 4MHz. Jest to maksymalna szybkość,
jaką przyjmuje PCD8544.
Idea rozwiązania
Pisanie tekstu
Najprostszy chyba sposób pisania tekstu na wyświetlaczu
z telefonu NOKIA3310 opiera się na założeniu, że każdy
znak ma ściśle określony wymiar. W naszym przypadku
będzie to 8x5 pikseli. Wszystkie znaki zebrane są w tym
przypadku w sporą tablicę, składającą się z elementów w
postaci pięciu ośmiobitowych liczb.
Przyznam szczerze, że nie tworzyłem tablicy znaków
samodzielnie. W sieci jest wystarczająco dużo kodów
zawierających gotowe tablice. Zawsze istnieje możliwość
dopisania znaków, które będą nam potrzebne, a jest to
dużo łatwiejsze niż tworzenie tablicy znaków od podstaw.
Za każdym razem, gdy trzeba wypisać znak na ekra-
nie, przeprowadzana jest analiza drukowanego symbolu.
Definicja znaków w tablicy zaczyna się od spacji, która
ma kod 32. Brak definicji niższych znaków jest związany
z tym, że w standardzie ANSI są to znaki niedrukowalne.
Z drugiej strony, zakres możliwych do wypisania znaków
jest ograniczony rozmiarem tablicy. Jeśli znak który ma
być wydrukowany, wykracza poza którąkolwiek z tych
granic, jest zamieniany na symbol hash (‘#’).
Gdy do wyświetlacza zostaną przesłane już wszystkie
dane znaku, dodawany jest jeszcze jeden bajt pusty, reali-
zujący odstęp między znakami. Brzmi to prosto? Listingi
169 oraz 170 udowadniają, że to jest proste.
Listing 167 Wczytywanie obrazka z pamięci FLASH
void
lcd_Load(prog_uint8_t* pData, uint8_t x,
uint8_t y, uint8_t sx, uint8_t sy)
{
uint8_t xcnt;
for
(;
sy!=
0
;
sy—)
{
// Ustawienie adresu
lcd_GoTo(x, y);
// Od razu zwiększam y na przyszłość
++
y;
// Przesyłam linię
for
(
xcnt=sx; xcnt!=
0
;
xcnt—)
{
lcd_Send(pgm_read_byte(pData++));
}
}
}
Listing 169 Wypisanie jednego znaku
void
lcd_char(
char
znak)
{
// Sprawdzenie znaku
if
(
znak <
‘ ‘
||
znak > ((
char
)
’ ‘
+
ELEMS(lcd_Font)))
znak =
‘#’
;
// Wysłanie 5 kolejnych bajtów z tablicy
uint8_t n;
prog_uint8_t* pData = lcd_Font[znak -
‘ ‘
];
for
(
n=
0
;
n<
5
;
n++)
{
lcd_Send(pgm_read_byte(pData++));
}
}
Listing 168 Wywołanie funkcji lcd_Load
lcd_Load((prog_uint8_t*)img,
0
,
0
,
ELEMS(img[
0
]),
ELEMS(img));
Listing 170 Wypisanie łańcucha
void
lcd_str(
char
*
str)
{
while
(*
str !=
0
)
{
lcd_char(*str++);
// Wypisanie przerwy
lcd_Send(
0
);
}
}
45
Elektronika dla Wszystkich
Teraz, aby aktywować wykorzystanie
sprzętowego interfejsu, w pliku harddef.h
dodaj wpis widoczny na listingu 173. Jeśli
zechcesz wrócić do opcji korzystającej z
interfejsu programowego, wystarczy wstawić
znak komentarza na początku dodanej linii.
Po napisaniu nowej wersji programu wyko-
nałem doświadczenie mające na celu spraw-
dzenie czasu wykonania instrukcji lcd_Send.
Zmierzony czas uwzględnia skok oraz powrót
z podprogramu. Wersja sprzętowa, niezależnie
od wysyłanej danej, wymaga 49 cykli na swoje
wykonanie. Czas wykonywania wersji progra-
mowej zależy od wysyłanej wartości.
Dla samych zer wynosi 119, a dla
samych jedynek 127 cykli. Widzimy,
że uzyskaliśmy ponaddwukrotne przy-
śpieszenie wysyłania danych.
Próby usunięcia
ramki z wyświetlacza
Jeśli posiadasz wyświetlacz z ramką,
możliwe, że zechcesz go z tej ramki
wydostać. Ja wpadłem właśnie na taki
pomysł. Chcę jednak w tym punkcie
Cię ostrzec. Ramka zawiera elementy
umożliwiające dobre przyleganie
metalowych kontaktów do wyświet-
lacza. Wystarczy przylutować przewody do
złącza i wszystko działa. Punkty przewodzące,
wykonane na szkle, z którymi należy zapewnić
kontakt, są duże i, pod światło, dość widoczne.
Daje to wrażenie, że łatwo będzie trafić blasz-
kami w odpowiednie miejsce. Ogólnie jest to
prawda. Problem jest na innym poziomie.
Blaszki nie tylko muszą trafić w odpowiednie
miejsca, ale muszą także być odpowiednio
dociśnięte. Osobiście nie udało mi się tego
zapewnić żadnym klejem. W ten sposób
uszkodziłem dwa wyświetlacze. Wydaje się
więc, że jedynym rozsądnym sposobem jest
obcięcie z ramki elementu trzymającego głoś-
nik i klawiaturę, natomiast nieruszanie samego
wyświetlacza. Z drugiej strony, wyświetlacz
wyjęty z ramki prezentuje się znacznie ładniej
(patrz fotografia 6)... same wyświetlacze są
tanie – może ktoś znajdzie sposób ;)
Podsumowanie
Na stronie www znajdzie się program z koda-
mi z dzisiejszego odcinka. Program ten po
starcie wyświetla logo razem z animowaną,
śmiejącą się buzią. Po pewnym czasie ekran
jest czyszczony i wypisywane są trzy linijki
tekstu. Cały program, w wersji z programo-
wym interfejsem SPI, zajął w pamięci 1782B.
Z tego 504B to pamięć zajmowana przez
obrazek startowy, tablica znaków to około
450B. Widać z tego, że prostą wersję obsługi
wyświetlacza telefonu komórkowego można
zmieścić nawet w mikrokontrolerze zawiera-
jącym 2KB pamięci programu.
Poznany dziś wyświetlacz będzie nam tro-
chę jeszcze towarzyszył. Zachęcam do za-
opatrzenia się w ten niezwykle ciekawy ele-
ment, tym bardziej że jego aktualna cena jest
na poziomie czteropaku... koncentratu pomi-
dorowego.
Radosław Koppel
radoslaw.koppel@elportal.pl
Szczegóły techniczne
Sprzętowy interfejs SPI
Nasz procesor wyposażono w sprzętowy interfejs SPI.
Jego wykorzystanie umożliwia znaczące przyspieszenie
transmisji. Poprzez interfejs sprzętowy procesor jest w sta-
nie przesłać jeden bit w ciągu dwóch taktów zegarowych.
Nasza programowa implementacja potrzebuje na to znacz-
nie więcej czasu.
Interfejs SPI posługuje się 4 wyprowadzeniami. Są to
MOSI (Master Output Slave Input – wyjście mastera, wej-
ście slave); MISO (Master Input Slave Output – analo-
gicznie); SCK (Serial ClocK – zegar); /SS (Slave Select –
aktywacja urządzenia typu slave).
Pełna praca magistrali SPI umożliwia działanie wielu
urządzeń na jednej szynie, w tym kilku urządzeń typu
master. Nie będziemy jednak omawiać SPI aż tak dokład-
nie. Aktualnie interesuje nas fakt, że od strony sprzętowej,
interfejs SPI to rejestr przesuwny o konfigurowalnych
możliwościach wyszeregowywania i wszeregowywania
danych. Zajmiemy się jedynie pracą interfejsu w trybie
master. W trybie tym to procesor generuje sygnał zegaro-
wy. Rejestr przesuwny, będący centrum interfejsu, służy
jednocześnie jako wejście oraz wyjście. Dane są wczyty-
wane jednocześnie z ich wysyłaniem. Jeśli więc chcesz
wczytać bajt danych, należy w tym celu wysłać jakąś
pustą wartość (zalecane 0xff lub 0);
Mnogość opcji konfiguracji umożliwia dopasowanie
działania interfejsu do praktycznie każdego urządzenia
wykonawczego. Większość nastaw odbywa się z poziomu
rejestru SPCR. Drugim rejestrem, którym powinniśmy się
zainteresować, jest SPSR. Znajduje się w nim bit umożli-
wiający podwojenie prędkości transmisji w trybie master.
Ostatnim elementem, na jaki
należy zwrócić uwagę, jest konfigu-
racja wyprowadzeń. Okazuje się, że
samo włączenie SPI nie nadpisuje
automatycznie ustawień wszystkich
portów. W trybie pracy jako master,
jedynie wyprowadzenie MISO jest
automatycznie konfigurowane jako
wejście. O konfiguracje pozostałych
wyprowadzeń powinniśmy zadbać
samodzielnie. Szczególna uwaga
należy się tutaj wyprowadzeniu /SS.
Uwaga: w trybie pracy jako
master, może nas spotkać pewna
zaskakująca niespodzianka dotyczą-
ca wyprowadzenia /SS. Jeśli chce-
my traktować wyprowadzenie /SS
jako wyjście do wybrania odbiorni-
ka, interfejs SPI nie ma jakiegokol-
wiek wpływu na to wyprowadzenie
– musimy sterować nim samodziel-
nie. Jednak gdy nierozważnie usta-
wimy je jako wejście, okaże się,
że z chwilą pojawienia się na nim
stanu niskiego, interfejs SPI zosta-
nie natychmiast przełączony w tryb
slave. Po takim przełączeniu ponowne wejście w tryb
master wymaga programowego ustawienia bitu MSTR
w rejestrze SPCR.
Listing 171 Wykorzystanie sprzętowego SPI
void
lcd_Send(uint8_t data)
{
#if defined LCD_HARDWARE_SPI
PORT(LCD_PORT) &= ~(
1
<<
LCD_SCE);
SPDR = data;
while
(!(
SPSR &
1
<<
SPIF)) {}
PORT(LCD_PORT) |=
1
<<
LCD_SCE;
#else
(...)
#endif
}
Listing 172 Konfiguracja sprzętowego SPI
int
main(
void
)
{
// Inicjacja portów
DDR(LCD_PORT) = (...)
PORT(LCD_PORT) = (...)
#if defined LCD_HARDWARE_SPI
// Aktywacja interfejstu SPI
SPCR =
1
<<
SPE |
0
<<
DORD |
1
<<
MSTR |
0
<<
CPOL |
0
<<
CPHA;
SPSR =
1
<<
SPI2X;
#endif
(...)
Listing 173 Wpis w pliku harddef.h włączający sprzęto-
wy SPI
#define LCD_HARDWARE_SPI
Fot. 6 wyświetlacz wyjęty z ramki