Serwisy partnerskie:
Close icon
Serwisy partnerskie

Kurs Arduino odcinek 26 - wykorzystanie bitmapowych fontów GFX

Zgodnie z zapowiedzią, w tym odcinku omówimy ulepszone... Bitmapowe fonty GFX Wiemy już, że biblioteka Adafruit GFX zawiera też katalog /Fonts z licznymi fontami, które są znacznie ładniejsze niż podstawowy font 5×7, w którym każdy znak określony jest przez matrycę (bitmapę) 5×8, czyli składa się tylko z 40 punktów.
Article Image
1. Kurs Arduino: wprowadzenie 2. Kurs Arduino odcinek 1 - co to jest i jak zacząć? (sprzęt, schematy, programowanie) 3. Kurs Arduino odcinek 2 - termometry: 'diodowy', pokojowy oraz 'scalony' analogowy 4. Kurs Arduino odcinek 3 - moduł wyświetlacza LCD (HD44780) 5. Kurs Arduino odcinek 4 - czujnik BME280 (pomiar ciśnienia, wilgotności i temperatury) 6. Kurs Arduino odcinek 5 - biblioteki do trzyfunkcyjnego czujnika BME280 7. Kurs Arduino odcinek 6 - wykorzystanie łącza I2C (TWI) 8. Kurs Arduino odcinek 7 - wykorzystanie łącza SPI (karty pamięci, MAX31865, MAX31855) 9. Kurs Arduino odcinek 8 - wykorzystanie czujnika dwutlenku węgla (MH-Z19) 10. Kurs Arduino odcinek 9 - wykorzystanie modułów do odmierzania czasu 11. Kurs Arduino odcinek 10 - wykorzystanie czujników temperatury (DS18B20) 12. Kurs Arduino odcinek 11 - budowa loggera (rejestratora danych) 13. Kurs Arduino odcinek 12 - rejestracja i prezentacja danych oraz obsługa karty SD 14. Kurs Arduino odcinek 13 - rejestracja danych (wykorzystanie modułu OpenLog) 15. Kurs Arduino odcinek 14 - czym zastąpić płytkę Arduino Uno? 16. Kurs Arduino odcinek 15 - budowa rejestratora (chiński klon Arduino Pro Mini) 17. Kurs Arduino odcinek 16 - problemy z kwadratowym modułem Strong 18. Kurs Arduino odcinek 17 - czujniki do pomiaru wilgotności gleby (instalacja, konfiguracja) 19. Kurs Arduino odcinek 18 - protokół MODBUS i łącze RS-485 20. Kurs Arduino odcinek 19 - wyświetlacze od podstaw (matrycowe, 7-segmentowe, MAX7219) 21. Kurs Arduino odcinek 20a - wyświetlacze matrycowe i biblioteki MD_MAX 22. Kurs Arduino odcinek 20b - przyciski i biblioteki do obsługi wyświetlaczy 23. Kurs Arduino odcinek 20c - biblioteki do obsługi wyświetlaczy graficznych 24. Kurs Arduino odcinek 21 - moduł wyświetlacza OLED 25. Kurs Arduino odcinek 22 - moduł wyświetlacza 1,3 cala 26. Kurs Arduino odcinek 23 - przygotowanie bitmap do wyświetlania 27. Kurs Arduino odcinek 24 - omówienie fontu podstawowego (Adafruit GFX) 28. Kurs Arduino odcinek 25 - font podstawowy biblioteki Adafruit GFX 29. Kurs Arduino odcinek 26 - wykorzystanie bitmapowych fontów GFX 30. Kurs Arduino odcinek 27 - polskie znaki w glcdfont.c 31. Kurs Arduino odcinek 28 - zmiana ulepszonych fontów GFX

Bitmapowe fonty GFX - Arudino

Trzeba jednak podkreślić, że ładniejsze fonty można stworzyć tylko wtedy, gdy znak składa się z większej liczby pikseli, niż zawiera matryca 5×7 (czym więcej, tym lepiej).

Koniecznie trzeba też dodać, że w „prawdziwych komputerach” wykorzystujemy fonty wektorowe, których definicje nie są bitmapami, ale matematycznymi przepisami, równaniami określającymi kształt znaków. Wprawdzie znaki fontów wektorowych finalnie wyświetlane są na ekranie także jako bitmapy, jednak bitmapy te są wyświetlane na samym końcu procesu.

Stan poszczególnych pikseli takiej finalnej bitmapy jest precyzyjnie wyliczany matematycznie z użyciem określonego wzoru – przepisu. Pozwala to dowolnie skalować wielkość znaków na ekranie. My w systemie Arduino wykorzystujemy proste fonty bitmapowe, gdzie dostępne jest tylko skalowanie bardzo uproszczone – możliwe jest jedynie powiększenie: 2-krotne, 3-krotne, itd. W poprzednim odcinku dokładniej omawialiśmy, jak taki powiększony znak jest rysowany.

Fot.7 Przykład skalowania litery 'a' podstawowego fontu 5×7 ze szkicu A2402.ino

Fotografia 7 pokazuje przykład skalowania litery 'a' podstawowego fontu 5×7 ze szkicu A2402.ino). Gdy znak jest większy, jego kształt pozostaje tak samo brzydki, jak fontu o wielkości podstawowej. Ponadto kolejne litery „zjeżdżają w dół” po dwa piksele.

Właśnie dlatego, że dostępne jest jedynie skalowanie uproszczone (powiększanie z mnożnikiem całkowitym), aby uzyskać ładny wygląd znaków, trzeba zrezygnować ze skalowania i przygotować oddzielne definicje znaków o różnych wielkościach.

I właśnie dlatego w katalogu /Fonts znajdziemy tak dużo plików, zawierających definicje fontów o różnym kroju i o różnej wielkości, których nazwa zaczyna się od Free....

Mamy trzy podstawowe kroje czcionek:

  • Mono, podobny do popularnego fontu Courier- New,
  • Sans – podobny do fontu Arial
  • Serif – podobny do TimesNewRoman.

Określenie Mono pochodzi od monospaced i wskazuje, że szerokość każdego znaku jest jednakowa (tak też jest w omawianym wcześniej foncie podstawowym 5×7). Co ważne, w fontach Sens i Serif szerokość poszczególnych znaków nie jest jednakowa: przykładowo literka i jest w nich znacznie cieńsza niż literki w czy m. Takie zróżnicowanie szerokości znaków poprawia wygląd tekstu, ale też tworzy istotne kłopoty, które trzeba jakoś rozwiązać.

Nazwa Serif wskazuje, że chodzi o tak zwany font szeryfowy, w którym literki mają ozdobne zakończenia tworzących je kresek (szeryfy). Sans to skrót od Sans Serif – po francusku bez szeryfów, czyli pismo bezszeryfowe, z prostymi zakończeniami linii, bez ozdobników.

W każdym z tych trzech podstawowych krojów mamy odmiany: normalną, pogrubioną (Bold), pochyloną (Italic lub Oblique) oraz pogrubioną pochyloną (Bold Italic, Bold Oblique). Dla użytkownika zupełnie nieistotna jest teoretyczna różnica między Italic – pochylony, a Oblique – słabiej pochylony. Rysunek 8 pokazuje dostępne odmiany fontów GFX.

Rys.8 Dostępne odmiany fontów GFX

W przypadku fontów bitmapowych zawsze jest duży kłopot ze skalowaniem, dlatego każda odmiana jest oddzielnie definiowana dla różnych wielkości. W przypadku fontów Adafruit GFX mamy do wyboru cztery wielkości: 9, 12, 14 i 24 punkty.

Dodatkowo literki 7b w nazwie wskazują, że jest to „font 7-bitowy”, obejmujący co najwyżej 127 symboli, a ściślej tylko drukowalne symbole kodu ASCII (32...126), nieobejmujący kodów o numerach powyżej 127. To akurat nie jest dobra wiadomość z punktu widzenia problemu „polskich liter”.

Podstawowe zasady wykorzystania takich fontów GFX są bardzo proste.

Domyślnie biblioteka Adafruit GFX korzysta z podstawowego fontu 5×7. Aby wykorzystać ulepszony font GFX, przede wszystkim w szkicu trzeba dodać informację, z jakiego fontu lub kilku fontów będziemy korzystać. Teoretycznie w jednym programie możemy wykorzystać mnóstwo fontów, ale w przypadku Arduino Uno i podobnych z ATmega328P poważnym ograniczeniem jest to, że cała definicja każdego użytego fontu musi zostać zapisana w (niezbyt dużej 32kB) pamięci programu procesora. I to także wtedy, jeśli z tego fontu wykorzystamy tylko kilka znaków do stworzenia jakiegoś bardzo krótkiego napisu.

Wykorzystanie fontów GFX - Arduino

Po pierwsze na początku szkicu trzeba dodać dyrektywę kompilatora, która dołączy wskazany font lub kilka fontów do procesu kompilacji. Potem w szkicu – programie przed wyświetleniem napisu trzeba niejako przełączyć się na potrzebny font.

Szkic 3

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>
#include <Fonts/FreeSans9pt7b.h>
Adafruit_SH1106 wysw(-1);
void setup() { //jednorazowo:
wysw.begin(SH1106_SWITCHCAPVCC, 0x3C);
wysw.setFont(&FreeSans9pt7b); //font
wysw.setTextColor(1, 0);
wysw.display(); delay(1000); //logo
wysw.clearDisplay(); //czyść ekran
wysw.setCursor(0, 0); //kursor
wysw.println("font Sans 9");
wysw.display(); //wyświetl na ekranie
}
void loop() { } //pusta pętla

Jak pokazuje szkic 3, w dyrektywie #include trzeba podać nie tylko nazwę pliku fontu, ale też ścieżkę: Fonts/. Potem w treści szkicu można wielokrotnie odwoływać się do tego fontu, a dokładniej do miejsca w pamięci, gdzie zostanie umieszczony, podając w argumencie metody .setFont() tylko nazwę fontu bez rozszerzenia, ale poprzedzoną znakiem ampersand: &.

Niestety, po skompilowaniu i załadowaniu szkicu 3 (w Elportalu A2403.ino tylko w wersji dla SH1106), na ekranie zobaczymy żałosny obrazek jak na fotografii 9...

Fot.9 Nieprawidłowo wyświetlany obrazek

Drobny problem polega na tym, jak na ekranie umieszczane są znaki względem kursora. Zagadkę rozszyfrujesz, jeżeli wyświetlisz znacznie dłuższy napis – obcięte zostanie pierwszych kilkanaście znaków tego napisu. A to, co widzimy na fotografii 9, to dolne piksele właśnie tych kilkunastu znaków prawidłowo wyświetlanego napisu. W materiałach Adafruit wyjaśnia to rysunek 10.

Rys.10 Wyświetlanie napisu

Wcześniej wykorzystywaliśmy podstawowy font, którego jednakowe znaki – glify, mają rozmiar 5×7. W punkcie, gdzie aktualnie znajduje się kursor, umieszczany jest górny lewy róg znaku – glifu. Dokładnie tak samo jest przy wykorzystaniu „zwykłych obrazków”. Jest to dobra prosta zasada, ale fotografia 7 udowadnia, że powoduje ona kłopoty przy wyświetlaniu tekstu o różnej wielkości.

Teraz okazuje się, że ulepszenie fontów GFX polega także na tym, że niezależnie od wielkości, znaki są niejako ustawiane na linii bazowej (baseline), czyli znajdują się nad linią bazową. Błąd w szkicu 3 polega na tym, że linią bazową jest najwyższy rządek pikseli o współrzędnej y równej zeru. Poprawimy to, obniżając linię bazową. Symbole, czy jak mówimy: glify, użytego fontu mają wysokość do 13 pikseli, więc linię bazową trzeba obniżyć co najmniej o 12 pikseli. P wyczyszczeniu ekranu instrukcja:

wysw.setCursor(0, 12); //kursor

ustawi kursor na początku 13 linii, a tak poprawiony szkic wyświetli prawidłowy obraz, jak widać na fotografii 11.

Fot.11 Prawidłowo wyświetlany obrazek

Możemy w programie wykorzystać kilka fontów. Przykładem jest szkic 4 (A2304.ino).

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSerif9pt7b.h>
#include <Fonts/FreeMono9pt7b.h>
Adafruit_SH1106 wysw(-1);
void setup() {
wysw.begin(SH1106_SWITCHCAPVCC, 0x3C);
wysw.display(); delay(1000); //logo
wysw.clearDisplay(); //czyść ekran
wysw.setTextColor(1, 0);
// wysw.setTextSize(1); //domyślnie
wysw.setFont(&FreeSans9pt7b);
wysw.setCursor(0, 12); // kursor
wysw.println("font Sans 9");
wysw.setFont(&FreeSerif9pt7b);
wysw.println("font Serif 9");
wysw.setFont(&FreeMono9pt7b);
wysw.println("font Mono 9");
wysw.display(); //wyświetl na ekranie
}
void loop() { } //pusta pętla

Wyświetli on napisy trzema fontami – fotografia 12.

Rozochoceni takimi sukcesami, próbujemy wykorzystać w programie także inne fonty z większymi znakami według szkicu 5 (A2405.ino).

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSerif9pt7b.h>
#include <Fonts/FreeMono9pt7b.h>
#include <Fonts/FreeSans24pt7b.h>
#include <Fonts/FreeSerif24pt7b.h>
#include <Fonts/FreeMono24pt7b.h>
Adafruit_SH1106 wysw(-1);
(...) i tak dalej ....

Tak jak poprzednio, w szkicu tym przez sekundę także wyświetlamy obrazek powitalny z logo, a raczej z napisem Adafruit. A następnie w pętli loop() próbujemy przez 3 sekundy wyświetlać napis jak na fotografii 12, a potem po dwie sekundy kolejno trzy krótsze napisy trzema znacznie większymi fontami (24pkt).

Fot.12 Wyświetlanie napisów trzema fontami (szkic 4)

Niestety, już podczas kompilacji czeka nas przykra niespodzianka! Kompilator daje komunikat, pokazany na rysunku 13. Fonty wymagają więcej miejsca, niż ma pamięć programu (32kB)!

Program ze szkicu A2405.ino można zrealizować, ale etapami, wykorzystując w szkicu tylko jeden z fontów o wielkości 24pkt. Aby nie zapomnieć, że oprócz fontów, do pamięci programu zapisywany jest też bez naszej wiedzy obrazek – logo Adafruit, w dotychczasowych szkicach wyświetlamy go na początku przez 1 sekundę.

Rys.13 Komunikat z kompilatora

Jak już wiemy, biblioteka Adafruit (a ściślej procedura w pliku sprzętowego sterownika modułu wyświetlacza) domyślnie ładuje logo Adafruit do pamięci FLASH procesora. Niestety, kompilator nie jest na tyle inteligenty, by sprawdzić, czy ten obrazek jest wyświetlany i jeżeli nie, nie obciążać nim procesora. Możemy to sprawdzić, komentując w szkicu 4 dwie linie odpowiadające za wyświetlanie logo, jak pokazuje szkic 6.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSerif9pt7b.h>
#include <Fonts/FreeMono9pt7b.h>
Adafruit_SH1106 wysw(-1);
void setup() {
wysw.begin(SH1106_SWITCHCAPVCC, 0x3C);
//wysw.display(); delay(1000); //logo
//wysw.clearDisplay(); //czyść ekran
wysw.setTextColor(1, 0);
// wysw.setTextSize(1);
wysw.setFont(&FreeSans9pt7b);
wysw.setCursor(0, 12); // kursor
wysw.println("font Sans 9");
wysw.setFont(&FreeSerif9pt7b);
wysw.println("font Serif 9");
wysw.setFont(&FreeMono9pt7b);
wysw.println("font Mono 9");
wysw.display(); //wyświetl na ekranie
}
void loop() { } //pusta pętla

Objętość obu wersji programu będzie różnić się o nieznaczące 88 bajtów, jak pokazuje rysunek 14.

Rys.14 Objętość obu wersji programu będzie różnić się o 88 bajtów

Czym większy rozmiar fontu, tym ładniejsze są znaki, ale też ich definicje zajmą w pamięci programu więcej miejsca. Rysunek 15 wskazuje, że definicja dużego fontu o rozmiarze 24pkt jest około cztery razy większa niż definicja małego fontu 9pkt.

Wprawdzie do pamięci mikroprocesora wstawianych jest mniej informacji, niż wynosi objętość opisującego font pliku .h, jednak chodzi o kilka do kilkudziesięciu kilobajtów.

Rys.15 Definicja dużego fontu jest większa niż definicja małego fontu

Dla porównania: logo Adafruit to jednokolorowa bitmapa, która dla całego ekranu miałaby 128*64 punktów, co opiszą 8192 bity – jeden kilobajt. Natomiast pliki zawierające informacje o fontach mają objętość dużo większą. Dlatego jeżeli potrzebnych jest kilka stałych krótkich napisów o różnych krojach i wielkości, korzystniej byłoby tworzyć je nie jako napisy z użyciem fontu, tylko wyświetlać jako wcześniej przygotowane obrazki – bitmapy. Ale jeżeli treść napisów ma się zmieniać zależnie od przebiegu programu – trzeba wykorzystać fonty.

Być może wystarczy jeden font GFX. Warto wiedzić, że także ulepszone fonty GFX można skalować za pomocą metody .setTextSize(); tak samo jak font podstawowy 5×7. Przykład znajdziesz na fotografii 16. Jest to wynik działania szkicu A2406.ino, który jest dostępny w Elportalu (także tylko w wersji dla SH1106).

Skalowanie na fotografii 16 wygląda na prawidłowe, jednak biorąc pod uwagę treść komentarza w pliku bibliotecznym Adafruti_GFX.cpp, opisującym metodę drawChar(), nie należy się dziwić, gdyby przy skalowaniu pojawiły się jakieś problemy.

Fot.16 Skalowanie ulepszonych fontów GFX za pomocą metody .setTextSize()

Na koniec wspomnę, że w Internecie można znaleźć sporo narzędzi do tworzenia fontów „od zera” (from scratch), co jednak nie jest wcale sprawą łatwą, nie tyle ze względu na aspekty techniczne, tylko estetyczne i artystyczne. Zdecydowanie łatwiej wykorzystać już istniejące fonty. W sieci można znaleźć potrzebne konwertery, które potrafią zamieniać współczesne fonty wektorowe (głównie typu TTF – True Type Fonts) na bitmapowe. Niektóre potrafią od razu zapisać wynik takiej konwersji jako gotowy plik w formacie GFX. Taki plik czcionki z rozszerzeniem .h można wykorzystać równie prosto, jak te dostarczone w bibliotece Adafruit GFX. Do tego szczegółu wrócimy przy omawianiu polonizacji fontów.

Oczywiście nie wyczerpaliśmy tematu wyświetlania tekstu i wykorzystania fontów. Gorąco zachęcam, żebyś w ramach ćwiczeń modyfikował szkice i tworzył nowe. W ten sposób nie tylko utrwalisz sobie umiejętności. Zapewne natkniesz się na przeszkody, a próby ich rozwiązywania to bardzo cenna część procesu uczenia się. Dlatego zachęcam do samodzielnych działań, w tym do łączenia wyświetlania podstawowych elementów graficznych, bitmap oraz tekstu. A w następnym odcinku zaczniemy omawiać problem „polskich liter”.

Do pobrania
Download icon Materiały do: Kurs Arduino cz.26 - wykorzystanie bitmapowych fontów GFX
1. Kurs Arduino: wprowadzenie 2. Kurs Arduino odcinek 1 - co to jest i jak zacząć? (sprzęt, schematy, programowanie) 3. Kurs Arduino odcinek 2 - termometry: 'diodowy', pokojowy oraz 'scalony' analogowy 4. Kurs Arduino odcinek 3 - moduł wyświetlacza LCD (HD44780) 5. Kurs Arduino odcinek 4 - czujnik BME280 (pomiar ciśnienia, wilgotności i temperatury) 6. Kurs Arduino odcinek 5 - biblioteki do trzyfunkcyjnego czujnika BME280 7. Kurs Arduino odcinek 6 - wykorzystanie łącza I2C (TWI) 8. Kurs Arduino odcinek 7 - wykorzystanie łącza SPI (karty pamięci, MAX31865, MAX31855) 9. Kurs Arduino odcinek 8 - wykorzystanie czujnika dwutlenku węgla (MH-Z19) 10. Kurs Arduino odcinek 9 - wykorzystanie modułów do odmierzania czasu 11. Kurs Arduino odcinek 10 - wykorzystanie czujników temperatury (DS18B20) 12. Kurs Arduino odcinek 11 - budowa loggera (rejestratora danych) 13. Kurs Arduino odcinek 12 - rejestracja i prezentacja danych oraz obsługa karty SD 14. Kurs Arduino odcinek 13 - rejestracja danych (wykorzystanie modułu OpenLog) 15. Kurs Arduino odcinek 14 - czym zastąpić płytkę Arduino Uno? 16. Kurs Arduino odcinek 15 - budowa rejestratora (chiński klon Arduino Pro Mini) 17. Kurs Arduino odcinek 16 - problemy z kwadratowym modułem Strong 18. Kurs Arduino odcinek 17 - czujniki do pomiaru wilgotności gleby (instalacja, konfiguracja) 19. Kurs Arduino odcinek 18 - protokół MODBUS i łącze RS-485 20. Kurs Arduino odcinek 19 - wyświetlacze od podstaw (matrycowe, 7-segmentowe, MAX7219) 21. Kurs Arduino odcinek 20a - wyświetlacze matrycowe i biblioteki MD_MAX 22. Kurs Arduino odcinek 20b - przyciski i biblioteki do obsługi wyświetlaczy 23. Kurs Arduino odcinek 20c - biblioteki do obsługi wyświetlaczy graficznych 24. Kurs Arduino odcinek 21 - moduł wyświetlacza OLED 25. Kurs Arduino odcinek 22 - moduł wyświetlacza 1,3 cala 26. Kurs Arduino odcinek 23 - przygotowanie bitmap do wyświetlania 27. Kurs Arduino odcinek 24 - omówienie fontu podstawowego (Adafruit GFX) 28. Kurs Arduino odcinek 25 - font podstawowy biblioteki Adafruit GFX 29. Kurs Arduino odcinek 26 - wykorzystanie bitmapowych fontów GFX 30. Kurs Arduino odcinek 27 - polskie znaki w glcdfont.c 31. Kurs Arduino odcinek 28 - zmiana ulepszonych fontów GFX
Tematyka materiału: bitmapy, fonty
AUTOR
Źródło
Elektronika dla Wszystkich lipiec 2020
Udostępnij
UK Logo