Dzisiaj kontynuujemy przygodę z płytką ESP32-2432S028R. W poprzedniej części uruchomiliśmy wyświetlacz oraz ekran dotykowy, więc dzisiaj z tego skorzystamy. Zobaczymy jakie mamy dostępne możliwości i kształty do rysowania a potem rozważymy jakie są sposoby na wydajne rysowanie tak, aby częstotliwość odświeżania ekranu była wysoka. Rozważymy tu kilka sposobów rysowania, w tym odświeżanie tylko tego, co się zmieniło oraz użycie DMA.
Poprzedni temat z serii:
https://www.elektroda.pl/rtvforum/topic4058635.html#21111347
Podstawowe kolory i kształty
Przede wszystkim mamy tu dwa rodzaje funkcji - draw (funkcje rysujące bez wypełnienia) oraz fill (funkcje rysujące i wypełniające kolorem kształt). Szczegóły znajdziemy w dokumentacji:
https://www.arduino.cc/reference/en/libraries/tft_espi/
Na bazie dokumentacji zebrałem w jedno miejsce różne funkcje rysujące. Możemy rysować różne kształty, wypełniać je kolorem bądź nie, wypełniać gradientami, a dla niektórych nawet zaokrąglać rogi. Kod powinien być zrozumiały, pierwsze argumenty to z reguły pozycja, kolejne - to zależy, odsyłam do dokumentacji bądź podpowiedzi Visual Code.
Kod: C / C++
Rezultat:
Z tych funkcji możemy tworzyć własne animacje i interfejsy, ale nie jest to konieczne - w dalszej części poznamy LVGL, który zrobi za nas całą robotę.
Fraktal Julii i szybkość rysowania
Zaczerpnęliśmy już nieco wiedzy o samym rysowaniu, to może teraz coś bardziej zaawansowanego. Sprawdźmy jak na tym wyświetlaczu będzie prezentować się fraktal. Czym fraktal jest - tego tu nie będę omawiać, ale w dużym skrócie, małe obliczenia mogą dać zaskakujące efekty:
Kod: C / C++
Rysowanie realizuję w dwóch pętlach, czyli dla każdego piksela.
Rezultat, ale to nie najciekawsze:
Film ładnie przedstawia, że całość jednak dość powoli się oblicza i rysuje:
Pewnie dałoby się to zoptymalizować, np. tworząc grafikę do bitmapy a potem raz wywołując wyświetlanie tej bitmapy...
Czy można rysować szybciej? Demo Bouncy Circles
Tu z pomocą przychodzi przykład od samego autora używanej przez nas biblioteki.
Źródło:
https://github.com/Bodmer/TFT_eSPI/blob/maste.../DMA%20test/Bouncy_Circles/Bouncy_Circles.ino
Przeanalizujmy ten kod:
Kod: C / C++
Widzimy tutaj, że autor... osobno rysuje górną połowę ekranu, a osobno dolną:
Kod: C / C++
Funkcja rysująca najpierw osobno, bez wyświetlacza, czyści daną bitmapę (danej połowy ekranu) i rysuje na niej nasze koła (bez wyświetlacza):
Kod: C / C++
Potem odbywa się już wyświetlanie na ekranie - narysowana połowa ekranu z bitmapy jest wysyłana do niego bezpośrednio przez DMA:
Kod: C / C++
Potem autor aktualizacje pozycje kół, ale dla nas to już mniej ważne:
Kod: C / C++
Tutaj przydałby się filmik jak to działa, ale na razie mi się zagubił, będę mieć chwilę to uzupełnię.
Najważniejsze są tu dwie funkcje - initDMA oraz pushImageDMA.
Nazwa funkcji już sugeruje nam, że korzysta ona z DMA - Direct Memory Access, czyli szybkiego, bezpośredniego dostępu do pamięci. Ale jak ona działa?
Można podejrzeć do kodu źródłowego by znaleźć na to pytanie odpowiedź:
Kod: C / C++
Powyższy fukcja tylko dodaje transfer DMA do kolejki. Potem normalnie wykonuje się reszta kodu, a w ten czas jednocześnie wysyłane są piksele do wyświetlacza.
Demo zegar
Oczywiście nie każdy program jednak zrealizowany jest w oparciu o DMA. Bez DMA też da się wyświetlić ciekawe animacje.
Rozważmy tutaj demko-zegar, demo pochodzi z:
https://git.wiyixiao4.com/Learning/TFT_eSPI/s...xamples/320%20x%20240/TFT_Clock/TFT_Clock.ino
Przejrzyjmy kod źródłowy:
Kod: C / C++
Powyżej zamieszczony fragment kodu to głównie funkcja setup, ona wykonywana jest raz. Mimo to w niej rysowany jest prawie cały zegar - bez wskazówek. Nie jest to przypadek, gdyż w praktyce nie ma potrzeby rysować tego więcej niż raz. W celu optymalizacji tutaj pętla loop tylko kolejno kasuje stare wskazówki (zamalowuje je kolorem tła) a potem rysuje nowe. Zobaczmy:
Kod: C / C++
Dodatkowo odświeżanie jest dodatkowo podzielone, bo np. gdy porusza się wskazówka sekundowa, a godzinowa stoi w miejscu, to tej godzinowej nie trzeba ponownie rysować.
Zostały funkcje pomocnicze:
Kod: C / C++
Rezultat:
Podsumowanie
Najpierw pokazałem tutaj podstawy samego rysowania (najmniejsze klocki budulcowe, rysowanie kształtów, itd.) a potem zaprezentowałem też różne metodyki rysowania. Można:
- albo rysować niewydajnie całość, najgorzej jest robić to piksel po pikselu (patrz przykład z fraktalem)
- albo zaprząc do całości DMA, np. dzieląc ekran na 2 części tak aby w trakcie wysyłania jednej części już renderowała się druga część (przykład 'bouncy circles')
- albo narysować raz statyczne tło a dynamiczne obiekty kolejno wymazywać kolorem tła a potem na nowo rysować (przykład zegar)
Wszystkie te metody mają swoje plusy i minusy, ale raczej wiadomo, że pierwsze podejście jest najprostsze i też najmniej wydajne. Wszystko zależy od tego co i w jakiej formie chcemy narysować.
Czy spotkaliście się z problemem wydajności rysowania? Jak go rozwiązaliście u siebie? Zapraszam do komentowania.
Fajne? Ranking DIY Pomogłem? Kup mi kawę.