W poprzedniej części kursu poznaliśmy działanie interfejsu SPI. W praktyce wykorzystaliśmy go do komunikacji z ekspanderem portów MCP23S08.
Teraz zajmiemy się ciekawszym tematem, czyli wyświetlaczem graficznym z telefonu Nokia 5110. Jest to tani i popularny moduł, który może ułatwić interakcję z budowanym urządzeniem.
Uwaga! Ten kurs został zarchiwizowany. Sprawdź najnowszy kurs STM32 »
Wyświetlacz LCD - Nokia 5110
Zanim zaczniemy podłączać przewody i programować, powinniśmy poświęcić chwilę na zapoznanie się z naszym modułem. Ten typ wyświetlacza był kiedyś wykorzystywany w telefonach Nokia 5110, stąd nazwa modułu. Ma on rozdzielczość 84 na 48 pikseli i monochromatyczną matrycę LCD. Wchodzi on w skład rozszerzonych zestawów dedykowanych do tego kursu.
W załączniku do tego artykułu można znaleźć dokumentację sterownika PCD8544, na którym bazuje wyświetlacz. Jak widać na zdjęciu (oraz w dokumentacji), jest on sterowany przez interfejs SPI oraz kilka dodatkowych linii.
RST - linia resetująca rejestry wyświetlacza. Przed rozpoczęciem pracy z wyświetlaczem, należy wygenerować na niej stan zero przez co najmniej 100ns. Podczas normalnej pracy wyświetlacza, na tej linii powinien być ciągle stan wysoki.
CE - jest to linia CS interfejsu SPI, nazwa pochodzi od Chip Enable
DC - linia ustalająca, czy przesyłamy dane (stan wysoki), czy komendy dla wyświetlacza (stan niski)
Din - linia danych, czyli MOSI interfejsu SPI
CLK - linia zegarowa SPI, odpowiada SCLK
VCC - napięcie zasilające moduł (3.3V)
BL - podświetlanie wyświetlacza
GND - masa
Jak widzimy znajdziemy tutaj jednokierunkowy interfejs SPI (nie możemy odczytywać danych z wyświetlacza). Producent zastosował inne oznaczenia linii (CE zamiast CS, Din zamiast MOSI i CLK zamiast SCLK), jednak sam interfejs działa bez zmian.
Maksymalna częstotliwość pracy tego interfejsu SPI, to 4 MHz.
Dodatkowe linie sterujące to: RST, która resetuje zawartość pamięci i rejestrów sterownika wyświetlacza oraz DC, która pozwala na wybór typu przesyłanych danych. Można przesyłać dane przeznaczone do wyświetlania lub komendy nim sterujące. Poza tym mamy trzy linie zasilające: masę (GND), zasilanie 3.3V (VCC) oraz zasilanie dla podświetlania (BL).
Czas na praktykę!
Podłączenie wyświetlacza z płytką Nucleo
Gdy już poznaliśmy nasz moduł, czas połączyć go z płytką Nucleo-STM32. Wykorzystamy ten sam interfejs SPI, co w poprzedniej części kursu. Nowe linie sterujące (DC, CE, RST) podłączymy do portu C. Zostawimy pin PC0 wolny - dzięki temu, będziemy mogli w przyszłości podłączyć jednocześnie MCP23S08 oraz wyświetlacz.
Wykorzystamy więc kolejne piny PC1, PC2 i PC3. Pin sterujący podświetlaniem możemy podłączyć do PC4, ale jeśli chcemy regulować jasność świecenia, wykorzystajmy do tego wyjście PWM - omawialiśmy je w 7 części kursu. Podłączymy więc linię BL do pinu PB6.
Gotowe zestawy do kursów Forbota
Komplet elementów Gwarancja pomocy Wysyłka w 24h
Zestaw ponad 120 elementów do przeprowadzenia wszystkich ćwiczeń z kursu można nabyć u naszych dystrybutorów! Dostępne są wersje z płytką Nucleo lub bez niej!
Masz już zestaw? Zarejestruj go wykorzystując dołączony do niego kod. Szczegóły »
Kompletny schemat montażowy wygląda następująco:
Podłączenie wyświetlacza graficznego do STM32.
Komunikacja z wyświetlaczem
Ponieważ nowych linii sterujących jest nieco więcej, zdefiniujemy stałe, które będą określały, do których pinów podłączyliśmy wyświetlacz (wszystkie dotyczą portu C):
C
1
2
3
#define LCD_DC GPIO_Pin_1
#define LCD_CE GPIO_Pin_2
#define LCD_RST GPIO_Pin_3
Konfiguracja SPI została omówiona w poprzedniej części, teraz kod wygląda dokładnie tak samo. Dodajemy tylko konfigurację nowych linii:
C
1
2
3
4
gpio.GPIO_Pin=LCD_DC|LCD_CE|LCD_RST;
gpio.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_Init(GPIOC,&gpio);
GPIO_SetBits(GPIOC,LCD_CE|LCD_RST);
Linia CE oraz RST jest aktywowana stanem niskim, więc wystawiamy na nich logiczną jedynkę. Pierwszym krokiem podczas komunikacji z wyświetlaczem jest zresetowanie jego kontrolera. W tym celu, na wyjściu RST musimy wygenerować stan 0 przez co najmniej 100 ns.
Napiszmy więc prostą procedurę:
C
1
2
3
4
5
voidlcd_reset()
{
GPIO_ResetBits(GPIOC,LCD_RST);
GPIO_SetBits(GPIOC,LCD_RST);
}
Opóźnienie nie było konieczne, ale w razie problemów zawsze można dodać małe opóźnienie do procedury resetowania.
Następny etap to wysyłanie komend sterujących pracą wyświetlacza. Do rozróżnienia między danymi, a komendami służy linia DC, stan niski informuje o wysyłaniu komendy sterującej. Teraz możemy napisać prostą procedurę, która wyśle komendę do wyświetlacza:
C
1
2
3
4
5
6
voidlcd_cmd(uint8_t cmd)
{
GPIO_ResetBits(GPIOC,LCD_CE|LCD_DC);
spi_sendrecv(cmd);
GPIO_SetBits(GPIOC,LCD_CE);
}
Ostatni etap, to wysłanie danych do wyświetlania. Przygotujemy procedurę, która za jednym wywołaniem prześle cały bufor z pamięci RAM lub Flash do wyświetlacza. Jako parametry będziemy podawali adres bufora oraz liczbę bajtów do przesłania:
C
1
2
3
4
5
6
7
8
9
voidlcd_data(constuint8_t*data,intsize)
{
inti;
GPIO_SetBits(GPIOC,LCD_DC);
GPIO_ResetBits(GPIOC,LCD_CE);
for(i=0;i<size;i++)
spi_sendrecv(data[i]);
GPIO_SetBits(GPIOC,LCD_CE);
}
Skąd weźmiemy dane do wyświetlania? Możemy je wygenerować programowo. Inna możliwość to wykorzystanie konwertera bitmap do przetłumaczenia obrazka na tablicę wartości. Wykorzystamy drugą opcję i wyświetlimy teraz logo Forbota.
Konwerter, po wczytaniu mapy bitowej z grafiką i ustawieniem odpowiedniej rozdzielczości ekranu, przygotuje dla nas tablicę, taką jak poniższa. Obsługa programu jest bardzo intuicyjna. Gdyby ktoś miał z nią jednak problemy, to proszę pytać w komentarzach.
Fragment tablicy, w której zapisany został kawałek loga Forbota:
Mamy już wszystkie dane potrzebne do obsługi wyświetlacza. Nie będziemy w tej chwili wnikać w wartości rejestrów sterownika ekranu, napiszemy więc kod z magicznymi liczbami. Jednak ciekawskich odsyłam do wspomnianej wcześniej dokumentacji.
C
1
2
3
4
5
6
7
8
9
lcd_reset();
lcd_cmd(0x21);
lcd_cmd(0x14);
lcd_cmd(0x80|0x2f);//Ustawienie kontrastu
lcd_cmd(0x20);
lcd_cmd(0x0c);
lcd_data(logo_mini_mono,sizeof(logo_mini_mono));
Tylko tyle kodu jest niezbędne, aby zobaczyć logo na wyświetlaczu. Oczywiście musieliśmy wcześniej przygotować sporo funkcji pomocniczych, ale teraz obsługa LCD jest już łatwa.
Efekt działania programu w praktyce wygląda następująco:
STM32 i wyświetlacz graficzny.
W razie problemów z kontrastem (cały jasny lub ciemny ekran) należy eksperymentować z ustawieniami kontrastu, za który odpowiada pogrubiony fragment poniższej linijki:
lcd_cmd(0x80 | 0x2f); //Ustawienie kontrastu
W celu ustawienia kontrastu nie należy edytować wartości 0x80!
Zadanie domowe 10.1
Przygotuj program, który będzie na ekranie wyświetlał inną bitmapę (np.: Twój avatar). Zdjęciem działającego układu pochwal się w komentarzu!
Reorganizacja kodu
Dotychczas wszystkie przykłady były pisane w jednym pliku. Taki styl programowania jest odpowiedni do prostych i krótkich przykładów, ale w miarę jak programy stają się bardziej rozbudowane jest bardzo niewygodny i nieczytelny.
Znacznie lepszym rozwiązaniem jest podzielenie dużego programu na mniejsze moduły. Takie moduły są czytelniejsze, a co więcej możemy je wykorzystywać w kolejnych programach.
Tworzą więc pewnego rodzaju bibliotekę potrzebnych nam funkcji.
Spróbujemy podzielić program sterujący wyświetlaczem na mniejsze moduły, a jednocześnie dodać więcej funkcji do samego sterowania wyświetlaczem.
W pierwszym przykładzie po prostu skopiowaliśmy gotowy bufor danych z pamięci Flash do wyświetlacza. Teraz będziemy postępować dwustopniowo - w pamięci RAM mikrokontrolera zadeklarujemy tablicę z buforem danych obrazu. Będziemy w niej przygotowywać dane do wyświetlenia, a następnie w jednym kroku kopiować całą tablicę do LCD.
Takie działanie ma kilka zalet. Po pierwsze dostęp do pamięci RAM jest o wiele szybszy niż do danych wyświetlacza. Po drugie nie możemy odczytywać danych z wyświetlacza, więc manipulacja zawartością ekranu byłaby dużo trudniejsza.
Najpierw podzielmy nasz program na mniejsze moduły. W poprzednich częściach kursu, często wykorzystywaliśmy procedurę delay_ms(). Jest ona bardzo wygodna, ale za każdym razem musieliśmy ją wklejać do kodu. Zamiast tego przygotujemy dla niej moduł, który w przyszłości może jeszcze nam się przydać.
W języku C moduł składa się z dwóch części: pliku nagłówkowego z rozszerzeniem .h oraz kodu źródłowego, z rozszerzeniem .c. Plik nagłówkowy powinien zawierać tylko deklaracje zmiennych i funkcji, natomiast cały kod powinien zostać wstawiony do pliku .c.
Zobaczmy to na przykładzie. Najpierw przygotowujemy plik delay.h. W celu dodania nowego pliku najlepiej w widoku projektu, kliknąć na katalog src i wybrać opcję New, a następnie Header File lub Source File (zależnie, czy dodajemy plik .h, czy .c):
Tworzenie plików nagłówkowych.
Następnie treść wypełniamy w poniższy sposób:
C
1
2
3
4
5
6
#ifndef __DELAY__
#define __DELAY__
externvoiddelay_ms(inttime);
#endif // __DELAY__
Dyrektywy #ifndef służą unikaniu wielokrotnego włączania tego samego pliku nagłówkowego. Natomiast, to co ważne, to deklaracja procedury delay_ms(). Jest opatrzona słówkiem kluczowym extern, żeby zasygnalizować iż jest to tylko deklaracja - kod procedury znajdzie się w innym pliku.
Słówko extern jest niezbędne przy deklarowaniu zmienych. W przypadku procedur, szczególnie osoby programujące w C++ mogą je omijać. Ale ponieważ programujemy w C, zostawimy je dla zwiększenia czytelności kodu.
Kod procedury oraz wszystko co jest jej potrzebne do działania (zmienną licznikową, procedurę obsługi przerwania) przenosimy do pliku delay.c, który tworzymy analogicznie do wcześniejszego, musimy tylko wybrać odpowiedni typ, czyli Source File:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdint.h>
#include "delay.h"
volatilestaticuint32_t timer_ms=0;
voidSysTick_Handler()
{
if(timer_ms)
timer_ms--;
}
voiddelay_ms(inttime)
{
timer_ms=time;
while(timer_ms);
}
Jaka jest zaleta takiego postępowania? Najważniejsza to podział większego problemu na mniejsze. Kiedy używamy modułu delay, interesuje nas tylko plik nagłówkowy delay.h - implementacja jest dla nas nieistotna. Zakładamy, że to, co zadeklarowane zostało przetestowane i działa. Jak zobaczymy później pliki z kodem mogą być o wiele dłuższe niż pliki nagłówkowe. Dużo łatwiej jest wiec pamiętać tylko o zawartości pliku delay.h niż zastanawiać się która funkcja do czego służy.
W poprzednim programie, bardzo dużo miejsca zajęła deklaracja danych z logo Forbot-a. Czytanie takiego kodu nie jest najprzyjemniejsze. Gdybyśmy dodali kolejne obrazki, plik main.c byłby prawie nieczytelny. Wydzielmy więc dane bitmap do oddzielnego modułu. Nazwijmy go bitmap.h:
C
1
2
3
4
5
6
7
8
#ifndef BITMAP_H_
#define BITMAP_H_
#include <stdint.h>
externconstuint8_t forbot_logo[];
#endif /* BITMAP_H_ */
Jak widać jest to właściwie jedna linijka - łatwo do przeczytania i zrozumienia. Natomiast implementacja wygląda następująco (bitmap.c):
Ponieważ rysowanie gotowych bitmap może nam się wydać niewystarczające, dodamy procedurę wyświetlającą napisy. Niestety w tym celu będziemy potrzebowali fontów. Wykorzystamy gotowy font o wymiarach 5x8 pikseli.
Pamiętajcie proszę o licencji - Beerware, którą znajdziecie w pliku z kodem!
Plik font.h:
C
1
2
3
4
5
6
7
8
#ifndef FONT_H_
#define FONT_H_
#include <stdint.h>
externconstuint8_t font_ASCII[][5];
#endif /* FONT_H_ */
Implementacja w pliku font.c:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
7-17-2011
Spark Fun Electronics 2011
Nathan Seidle
This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
This code writes a series of images and text to the Nokia 5110 84x48 graphic LCD:
http://www.sparkfun.com/products/10168
Do not drive the backlight with 5V. It will smoke. However, the backlight on the LCD seems to be
happy with direct drive from the 3.3V regulator.
Although the PCD8544 controller datasheet recommends 3.3V, the graphic Nokia 5110 LCD can run at 3.3V or 5V.
No resistors needed on the signal lines.
You will need 5 signal lines to connect to the LCD, 3.3 or 5V for power, 3.3V for LED backlight, and 1 for ground.
*/
#include "font.h"
//This table contains the hex values that represent pixels
//for a font that is 5 pixels wide and 8 pixels high
constuint8_t font_ASCII[][5]={
{0x00,0x00,0x00,0x00,0x00}// 20
,{0x00,0x00,0x5f,0x00,0x00}// 21 !
,{0x00,0x07,0x00,0x07,0x00}// 22 "
,{0x14,0x7f,0x14,0x7f,0x14}// 23 #
,{0x24,0x2a,0x7f,0x2a,0x12}// 24 $
,{0x23,0x13,0x08,0x64,0x62}// 25 %
,{0x36,0x49,0x55,0x22,0x50}// 26 &
,{0x00,0x05,0x03,0x00,0x00}// 27 '
,{0x00,0x1c,0x22,0x41,0x00}// 28 (
,{0x00,0x41,0x22,0x1c,0x00}// 29 )
,{0x14,0x08,0x3e,0x08,0x14}// 2a *
,{0x08,0x08,0x3e,0x08,0x08}// 2b +
,{0x00,0x50,0x30,0x00,0x00}// 2c ,
,{0x08,0x08,0x08,0x08,0x08}// 2d -
,{0x00,0x60,0x60,0x00,0x00}// 2e .
,{0x20,0x10,0x08,0x04,0x02}// 2f /
,{0x3e,0x51,0x49,0x45,0x3e}// 30 0
,{0x00,0x42,0x7f,0x40,0x00}// 31 1
,{0x42,0x61,0x51,0x49,0x46}// 32 2
,{0x21,0x41,0x45,0x4b,0x31}// 33 3
,{0x18,0x14,0x12,0x7f,0x10}// 34 4
,{0x27,0x45,0x45,0x45,0x39}// 35 5
,{0x3c,0x4a,0x49,0x49,0x30}// 36 6
,{0x01,0x71,0x09,0x05,0x03}// 37 7
,{0x36,0x49,0x49,0x49,0x36}// 38 8
,{0x06,0x49,0x49,0x29,0x1e}// 39 9
,{0x00,0x36,0x36,0x00,0x00}// 3a :
,{0x00,0x56,0x36,0x00,0x00}// 3b ;
,{0x08,0x14,0x22,0x41,0x00}// 3c <
,{0x14,0x14,0x14,0x14,0x14}// 3d =
,{0x00,0x41,0x22,0x14,0x08}// 3e >
,{0x02,0x01,0x51,0x09,0x06}// 3f ?
,{0x32,0x49,0x79,0x41,0x3e}// 40 @
,{0x7e,0x11,0x11,0x11,0x7e}// 41 A
,{0x7f,0x49,0x49,0x49,0x36}// 42 B
,{0x3e,0x41,0x41,0x41,0x22}// 43 C
,{0x7f,0x41,0x41,0x22,0x1c}// 44 D
,{0x7f,0x49,0x49,0x49,0x41}// 45 E
,{0x7f,0x09,0x09,0x09,0x01}// 46 F
,{0x3e,0x41,0x49,0x49,0x7a}// 47 G
,{0x7f,0x08,0x08,0x08,0x7f}// 48 H
,{0x00,0x41,0x7f,0x41,0x00}// 49 I
,{0x20,0x40,0x41,0x3f,0x01}// 4a J
,{0x7f,0x08,0x14,0x22,0x41}// 4b K
,{0x7f,0x40,0x40,0x40,0x40}// 4c L
,{0x7f,0x02,0x0c,0x02,0x7f}// 4d M
,{0x7f,0x04,0x08,0x10,0x7f}// 4e N
,{0x3e,0x41,0x41,0x41,0x3e}// 4f O
,{0x7f,0x09,0x09,0x09,0x06}// 50 P
,{0x3e,0x41,0x51,0x21,0x5e}// 51 Q
,{0x7f,0x09,0x19,0x29,0x46}// 52 R
,{0x46,0x49,0x49,0x49,0x31}// 53 S
,{0x01,0x01,0x7f,0x01,0x01}// 54 T
,{0x3f,0x40,0x40,0x40,0x3f}// 55 U
,{0x1f,0x20,0x40,0x20,0x1f}// 56 V
,{0x3f,0x40,0x38,0x40,0x3f}// 57 W
,{0x63,0x14,0x08,0x14,0x63}// 58 X
,{0x07,0x08,0x70,0x08,0x07}// 59 Y
,{0x61,0x51,0x49,0x45,0x43}// 5a Z
,{0x00,0x7f,0x41,0x41,0x00}// 5b [
,{0x02,0x04,0x08,0x10,0x20}// 5c
,{0x00,0x41,0x41,0x7f,0x00}// 5d ]
,{0x04,0x02,0x01,0x02,0x04}// 5e ^
,{0x40,0x40,0x40,0x40,0x40}// 5f _
,{0x00,0x01,0x02,0x04,0x00}// 60 `
,{0x20,0x54,0x54,0x54,0x78}// 61 a
,{0x7f,0x48,0x44,0x44,0x38}// 62 b
,{0x38,0x44,0x44,0x44,0x20}// 63 c
,{0x38,0x44,0x44,0x48,0x7f}// 64 d
,{0x38,0x54,0x54,0x54,0x18}// 65 e
,{0x08,0x7e,0x09,0x01,0x02}// 66 f
,{0x0c,0x52,0x52,0x52,0x3e}// 67 g
,{0x7f,0x08,0x04,0x04,0x78}// 68 h
,{0x00,0x44,0x7d,0x40,0x00}// 69 i
,{0x20,0x40,0x44,0x3d,0x00}// 6a j
,{0x7f,0x10,0x28,0x44,0x00}// 6b k
,{0x00,0x41,0x7f,0x40,0x00}// 6c l
,{0x7c,0x04,0x18,0x04,0x78}// 6d m
,{0x7c,0x08,0x04,0x04,0x78}// 6e n
,{0x38,0x44,0x44,0x44,0x38}// 6f o
,{0x7c,0x14,0x14,0x14,0x08}// 70 p
,{0x08,0x14,0x14,0x18,0x7c}// 71 q
,{0x7c,0x08,0x04,0x04,0x08}// 72 r
,{0x48,0x54,0x54,0x54,0x20}// 73 s
,{0x04,0x3f,0x44,0x40,0x20}// 74 t
,{0x3c,0x40,0x40,0x20,0x7c}// 75 u
,{0x1c,0x20,0x40,0x20,0x1c}// 76 v
,{0x3c,0x40,0x30,0x40,0x3c}// 77 w
,{0x44,0x28,0x10,0x28,0x44}// 78 x
,{0x0c,0x50,0x50,0x50,0x3c}// 79 y
,{0x44,0x64,0x54,0x4c,0x44}// 7a z
,{0x00,0x08,0x36,0x41,0x00}// 7b {
,{0x00,0x00,0x7f,0x00,0x00}// 7c |
,{0x00,0x41,0x36,0x08,0x00}// 7d }
,{0x10,0x08,0x08,0x10,0x08}// 7e ~
,{0x78,0x46,0x41,0x46,0x78}// 7f DEL
};
Mając już wszystkie moduły pomocnicze możemy napisać naszą bibliotekę graficzną. Plik nagłówkowy lcd.h wygląda następująco:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef __LCD__
#define __LCD__
#include <stdint.h>
#include "stm32f10x.h"
#define LCD_DC GPIO_Pin_1
#define LCD_CE GPIO_Pin_2
#define LCD_RST GPIO_Pin_3
voidlcd_setup(void);
voidlcd_clear(void);
voidlcd_draw_bitmap(constuint8_t*data);
voidlcd_draw_text(introw,intcol,constchar*text);
voidlcd_copy(void);
#endif // __LCD__
Jak widzimy, przygotowujemy 5 procedur:
lcd_setup - konfiguruje wyświetlacz, powinna być wywołana na początku programu
lcd_clear - czyści bufor ekranu
lcd_draw_bitmap - rysuje bitmapę na całym ekranie
lcd_draw_text - wypisuje tekst na ekranie
lcd_copy - przesyła zawartość bufora do wyświetlacza
Jest to prosta biblioteka, ale jej kod jest już dość długi. Tutaj pojawia się jednak zaleta modułowości - nie musimy go czytać. A nawet jeśli przeczytamy (do czego zachęcam), nie będziemy musieli pamiętać wszystkich jego detali. Do używania wystarczy znajomość pliku nagłówkowego.
Mamy już przygotowaną bibliotekę. Napiszmy teraz prosty programik, który będzie jej używał. Najpierw wyświetlimy logo Forbot-a. Poczekamy 1 s, po czym wypiszemy w kolejnych wierszach napis "Forbot!". Kod jest bardzo prosty:
Efekt działania powyższego programu widoczny jest na poniższym zdjęciu:
Wyświetlanie napisów na wyświetlaczu przez SPI na STM32.
Zadanie domowe 10.2
Napisz program, który na ekranie wyświetlacza umieści komunikat przesłany do mikrokontrolera przez UART. Zadbaj o odpowiednie łamanie wierszy!
Rozbudowa biblioteki graficznej
Poprzednia wersja biblioteki jest bardzo uboga. Dodajmy więc jeszcze dwie funkcje:
lcd_draw_pixel - do zapalania wybranego punktu na ekranie
lcd_draw_line - rysującą linię między wybranymi punktami
Dzięki temu będziemy mogli rozszerzyć możliwości naszego wyświetlacza. Do pliku nagłówkowego warto również dodać stałe opisujące wymiary ekranu - będzie się z nich łatwiej korzystać. Nowy nagłówek biblioteki wygląda następująco (lcd.h):
Działanie programu widoczne jest na poniższej animacji (słabo jakość wynika z kompresji obrazu):
Prosta animacja na wyświetlaczu.
Zadanie domowe 10.3
Napisz program, który zapełni wszystkie punkty na ekranie (piksel po pikselu).
Podświetlanie ekranu
Dotychczas nie wykorzystywaliśmy podświetlania ekranu. Niewątpliwą zaletą technologii LCD w porównaniu z TFT jest możliwość pracy wyświetlacza nawet bez podświetlania (czyli wykorzystując światło z otoczenia).
Możemy jednak dodać odpowiedni kod i sprawdzić jak łatwo jest sterować jasnością podświetlania. Sterowanie PWM omawialiśmy już w kursie, więc po prostu wykorzystamy omówiony tam kod:
Napisz program, który dostosuje jasność podświetlenia do informacji odczytanej z podłączonego fotorezystora. Im otoczenie jest ciemniejsze, tym jasność podświetlenia powinna być większa.
Zadanie domowe 10.5
Rozbuduj program z ćwiczenia 10.4 tak, aby ilustrował na ekranie aktualny poziom światła. Np.: w formie paska postępu lub skali.
Podsumowanie
W tej części napisaliśmy własną bibliotekę obsługi wyświetlacza graficznego. Poznaliśmy w sumie dwa układy peryferyjne podłączane za pomocą interfejsu SPI: I/O expander oraz wyświetlacz LCD. Podłączanie innych układów przebiega podobnie - samo SPI działa właściwie zawsze tak samo. Natomiast jakie są protokoły komunikacyjne trzeba doczytać w dokumentacji układów.
W kolejnym odcinku kursu omówimy kolejny interfejs - I2C. Dzięki jego pomocy będzie mogli użyć zewnętrznej pamięci EEPROM oraz w wersji bardziej zaawansowanej będziemy odczytywać dane z akcelerometru oraz magnetometru!
Dołącz do 20 tysięcy osób, które otrzymują powiadomienia o nowych artykułach! Zapisz się, a otrzymasz PDF-y ze ściągami (m.in. na temat mocy, tranzystorów, diod i schematów) oraz listę inspirujących DIY na bazie Arduino i Raspberry Pi.
Dołącz do 20 tysięcy osób, które otrzymują powiadomienia o nowych artykułach! Zapisz się, a otrzymasz PDF-y ze ściągami (m.in. na temat mocy, tranzystorów, diod i schematów) oraz listę inspirujących DIY z Arduino i RPi.
Trwa ładowanie komentarzy...