Serwisy partnerskie:
Close icon
Serwisy partnerskie

Kurs Arduino odcinek 23 - przygotowanie bitmap do wyświetlania

W poprzednim odcinku przygotowaliśmy „ręcznie” obrazek do wyświetlenia, niestety, po wgraniu programu do Arduino ekran wyglądał jak na fotografii 8. Aby wyjaśnić problem, musimy zajrzeć do biblioteki GFX, a konkretnie do pliku Adafruit_GFX.cpp.
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

Znajdziemy tam definicję metody drawBitmap(), co jest pokazane w szkicu 2. Wykorzystujemy dwie pomocnicze zmienne: byteWidth będzie przechowywać informację, ile pełnych bajtów potrzeba do zapisania jednej linii, a do zmiennej byte będą kopiowane z pamięci programu kolejne bajty treści obrazka. Zmienna licznikowa j posłuży do zliczania linii – wierszy, natomiast zmienna i – do określania kolejności wyświetlania pikseli w jednym wierszu.

Rys.9 Uzupełnienie informacji do pełnych bajtów

Sama procedura rysowania bardzo negatywnie zaskakuje, bo polega na żmudnym i dość czasochłonnym zaświecaniu pojedynczych punktów za pomocą elementarnej metody writePixel()! Wyświetlanie kolejnych punktów, zgodnie z rysunkiem 6, realizują dwie pętle: „mniejsza” z licznikiem i wyświetla punkty jednej linii, a „większa” pętla z licznikiem j wyświetla kolejne linie – wiersze.

Gdy trzy najmłodsze bity licznika i są równe zeru, polecenie pgm_read_byte() odczytuje z pamięci FLASH kolejne bajty, umieszcza je w zmiennej byte i sprawdza stan najstarszego bitu tej zmiennej. Jeżeli bit ma wartość 1 (byte & 0x80), polecenie writePixel() wysyła do wyświetlacza rozkaz modyfikacji stanu komórki bufora wyświetlającego piksel o współrzędnych x+i,y.

Fot.8 Błędnie wyświetlany obraz po wgraniu programu do Arduino
Fot.10 Prawidłowo wyświetlany obraz

Problem ze szkicem 1 jest taki, że niezależnie od szerokości obrazu (w) pętla z licznikiem i zawsze zaczyna pracę od najstarszego piksela któregoś z odczytanych bajtów. Inaczej mówiąc, bity nowej linii – wiersza muszą zaczynać się w nowym, kolejnym bajcie. Trzeba uzupełnić do pełnych bajtów (wielokrotności ośmiu) nie całość obrazka, jak to zrobiliśmy, tylko opis wszystkich linii tego obrazka. Poprawioną wersję pokazuje rysunek 9.

Uzyskane liczby możemy wpisać do szkicu 1 (A2201.ino) i wtedy program wyświetli prawidłowy obrazek, jak na fotografii 10.

/*!
@brief Draw a PROGMEM-resident 1-bit image (...)
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with monochrome bitmap
@param w Width of bitmap in pixels
@param h Height of bitmap in pixels
@param color 16-bit 5-6-5 Color to draw with
*/
void Adafruit_GFX::drawBitmap(int16_t x, int16_t y,
const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color) {
int16_t byteWidth = (w+7)/8; //Bitmap scanline pad = whole byte
uint8_t byte = 0;
startWrite();
for(int16_t j=0; j<h; j++, y++) {
for(int16_t i=0; i<w; i++) {
if(i & 7) byte <<= 1;
else byte = pgm_read_byte(&bitmap[j*byteWidth + i/8]);
if(byte & 0x80) writePixel(x+i, y, color); } }
endWrite(); }

Oczywiście istnieją programy, które znakomicie ułatwiają tworzenie tablic, zawierających obrazki – grafikę. W szczególności w Internecie można znaleźć szereg stron, które zasadniczo przeznaczone są do tworzenia dowolnych własnych znaków – symboli, które będą używane do wyświetlania tekstu. Jest to ułatwienie między innymi podczas tworzenia „polskich liter”, a raczej symboli przedstawiających znaki polskiego alfabetu, których nie ma w kodzie ASCII. Do tego szczegółu wrócimy, a na razie inna ważna sprawa.

Otóż często potrzebna jest monochromatyczna, „zero-jedynkowa” grafika o różnej wielkości. Dla małych wyświetlaczy OLED zwykle ma ona 128×64 piksele.

Rys.11 Zmiana rozmiarów i trybów koloru w Paint
Rys.12 Zmiana rozmiarów i trybów koloru w Paint

Dla najmniej zorientowanych: obrazek można przygotować w różnych programach, np. za pomocą windowsowego programu Paint. Obraz automatycznie otwierany po uruchomieniu programu jest duży i kolorowy, dlatego trzeba kliknąć: Plik i potem Właściwości (rysunek 11) i w okienku zmienić rozmiar i tryb kolorów według rysunku 12. Potem trzeba powiększyć na ekranie maleńki obraz lupką, wybrać narzędzie Ołówek, kolor czarny i rysować. Przykład na rysunku 13.

Taki obraz trzeba zapisać (Ctrl+S) jako plik .png albo dwukolorowa bitmapa – rysunek 14. Już wiemy, że do szkicu Arduino potrzebna jest też tablica w pamięci FLASH: 

const uint8_t nazwa [] PROGMEM = {...};
Rys.13 Rysunek przygotowany do wyswietlacza z Arduino
Rys.14 Zapisywanie obrazka jako plik png lub dwykolorową bitmapę

Potrzebną tablicę (array) uzyskamy z jednego z wielu dostępnych konwerterów, które potrafią przeanalizować plik w którymś z popularnych formatów (.jpg, .gif, .png, .bmp), a także przeskalować – zmniejszyć i według ustawień użytkownika zamienić go na potrzebny ciąg bitów (Przykład). Jeden z takich konwerterów – program polskiego autora, można pobrać ze strony. Natomiast polecany też przez Adafruit skrypt image2cpp można znaleźć na stronie. Ten skrypt działa też off-line, po zapisaniu strony jako pliku HTML. Jak pokazuje rysunek 15, najpierw wczytujemy plik, w naszym przypadku stworzony właśnie KursArduino.png.

Rys.15 Konwerter do zamiany grafiki na ciąg bitów

Napis ma świecić, a nie być czarny, więc zaznaczamy okienko: Invert image colors. W okienku Code output format zamiast plain bytes wybieramy Arduino code (co pozwoliłoby wczytać wiele obrazków i scalić je w jednym pliku). Po tych dwóch zmianach klikamy Generate code i w dolnym okienku otrzymujemy gotowy kod, który kopiujemy wprost do szkicu Arduino. W Elportalu można znaleźć pliki obrazka oraz szkic A2203.ino, który wyświetla taki obrazek na wyświetlaczu – fotografia 16.

Fot.16 Obrazek wyświetlany na podstawie A2203.ino szkicu z Elportalu

To było dla mniej zaawansowanych. Natomiast bardziej zaawansowani zapewne spróbują też przeskalować i przekonwertować na dwukolorową bitmapę obrazy pełnokolorowe i półtonowe, w tym fotografie. Zasadniczo konwersja jest łatwa, ale często wyniki nie są optymalne. Zwłaszcza przy próbie sensownego odwzorowania półtonów. W skrypcie z rysunku 15 można zmieniać (0...255) tylko próg Brightness/alpha threshold, który domyślnie ma wartość 128.

Osoby, które mają wiedzę i dostęp do lepszych programów graficznych, najpierw przygotują obrazek bitmapowy (czarny/biały) w finalnej postaci i docelowej rozdzielczości i poddadzą go opisanej „najprostszej zamianie na bity”. Szkic A2204.ino zawiera szkic, którego efekt działania pokazany jest na fotografii 17.

Fot.17 Efekt działania szkicu A2204.ino z pliku png.ino
Fot.18 Wersja ze szkicu A2205.ino zrobiona z pliku jpg

Szału nie ma, ale można bez trudu rozpoznać fragment najsłynniejszego na świecie obrazu. Obraz został skonwertowany za pomocą skryptu image2cpp z dostępnego w Elportalu pliku Mona_Lisa4_128_64.png, który powstał w programie Photoshop przy wykorzystaniu tzw. roztrząsania dyfuzyjnego (diffusion dithering) przy zamianie obrazu (półtonowego, szarego) z trybu RGB na indeksowany o liczbie kolorów 2 z eksperymentalnie dobranymi ustawieniami palety i roztrząsania. Można też przy zamianie obrazka w skali szarości na czarno-białą bitmapę wybrać opcję: roztrząsanie dyfuzyjne.

Natomiast fotografia 18 pokazuje wersję ze szkicu A2205.ino, zrobioną przez skrypt image2cpp wprost z (większego i lepszego) pliku Mona_Lisa2.jpg. Jak widać, skrypt bardzo słabo radzi sobie z półtonami.

Na fotografii 19 można znaleźć podobny przykład z obrazkiem wstępnie przygotowanym w Photoshopie. Fotografia 20 zrobiona wprost z pliku E0.png potwierdza, że skrypt image2cpp nie radzi sobie z półtonami. Zawierające te obrazki szkice A2206.ino oraz A2207.ino też są dostępne w Elportalu w wersjach na SSD1305 oraz SH1106.

Jak widać, nasze maleńkie zero-jedynkowe wyświetlacze 128×64 nie pozwolą wiernie odwzorować półtonów, niemniej do wielu celów wystarczą.

Fot.19 Efekt szkicu porzygotowanego w Photoshopie
Fot.20 Efekt szkicu porzygotowanego przez skrypt image2cpp

Zachęcam do ćwiczeń!

Na koniec wspomnę jeszcze o interesujących dla elektronika przykładach realizacji VU-metru, wiele przykładów można znaleźć w Internecie. Fotografia 21 pokazuje przykład ze strony, czyli użytkownika o znajomym brzmieniu adamples.

Fot.21 Przykład VU-metru

Inny przykład można znaleźć na stronie, gdzie wykorzystny jest szyld z mikrofonem i wzmacniaczem, a sygnał podawany jest na przetwornik ADC. Przy wyświetlaniu wyniku pomiaru najpierw na ekranie rysowany jest obrazek skali, będący bitmapą 128×64. Potem na tym obrazku dorysowana jest linia – wskazówka za pomocą metody .drawLine(). Aby linia odpowiadała wychyleniu wskazówki, należy podać odpowiednie współrzędne jej początku i końca. Łatwo sprawdzić w udostępnionym tam szkicu, że są one wyliczane w zaskakująco prosty sposób.

Omawiane kwestie są ważne także dlatego, że opanowanie podstaw grafiki mikroprocesorowej otwiera drogę do zrozumienia problemu czcionek – fontów.

Będziemy się tym zajmować w dalszych odcinkach kursu.

Do pobrania
Download icon Materiały do: Kurs Arduino odcinek 23 - przygotowanie bitmap do wyświetlania
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
AUTOR
Źródło
Elektronika dla Wszystkich kwiecień 2020
Udostępnij
UK Logo