Przejdź do treści

Lopaka Basics 07: Animacje ​

Moduł OLED SSD1306 pokazujący białą sylwetkę galopującego konia z jeźdźcem

Ten koń został sfotografowany w 1887 roku. Teraz jest GIF-em, galopującym na malutkim ekranie Arduino: piętnaście klatek i mieszczą się. Zrób go na cały ekran i trochę płynniejszego, a szkic w ogóle się nie zmieści.

W tym odcinku dowiesz się, jak działa animacja na małym ekranie i ile pamięci potrzebuje. Następnie zamienisz GIF w kod w Lopaka, uruchomisz go na symulowanym Arduino UNO i umieścisz cały miesiąc faz księżyca na ESP32.

Co zbudujesz: galopującego konia z GIF-a na OLED 128×64 z Arduino UNO oraz miesiąc faz księżyca z wideo na tym samym OLED z ESP32. Czego potrzebujesz: przeglądarki i konfiguracji symulatora z odcinka 1: OLED SSD1306 i biblioteki U8g2. W przypadku konia użyliśmy galopującego konia Eadwearda Muybridge’a (Animal Locomotion, 1887, domena publiczna); każdy krótki GIF działa.

Klatki i liczba klatek ​

Animacja to stos obrazków ​

Animacja to stos nieruchomych obrazków. Pokaż je jeden po drugim, wystarczająco szybko, a twoje oko przestanie widzieć obrazki i zacznie widzieć ruch.

Ta prędkość to liczba klatek na sekundę, w klatkach na sekundę (FPS). Przy dwóch klatkach na sekundę koń jest pokazem slajdów. Przy dziesięciu galopuje.

15 zdjęć galopującego konia, z jednym z nich dużym i licznikiem 10 klatek na sekundę: galopuje

Pętla odtwarza ją klatka po klatce ​

Mikrokontroler odtwarza animację w ten sam sposób. Każde przejście loop() rysuje bieżącą klatkę, wysyła ją na ekran, czeka chwilę i przechodzi do następnej klatki. Po ostatniej klatce wraca do pierwszej.

Pętla animacji wokół OLED pokazującego konia: narysuj, wyślij, poczekaj, następna, z licznikiem klatka 5 z 15

Każda klatka kosztuje pamięć ​

Jedna klatka na cały ekran to 1024 bajty ​

OLED ma 128 na 64 piksele, a każdy piksel to jeden bit: włączony lub wyłączony. To 8192 bity, czyli 1024 bajty na jedną klatkę na cały ekran. Klatka o dowolnym rozmiarze zajmuje szerokość × wysokość ÷ 8 bajtów.

128 × 64 = 8192 piksele, 1 piksel = 1 bit, 8192 bity = 1024 bajty: jedna klatka na cały ekran

Arduino UNO przechowuje 23 klatki na cały ekran ​

Klatki żyją w pamięci flash, obok twojego programu. Arduino UNO ma 32 KB flash, a biblioteka U8g2 i szkic zajmują już około 8 KB z tego. Pozostawia to miejsce na 23 klatki na cały ekran. Sprawdziliśmy to kompilatorem: przy 23 klatkach szkic używa 99% flash UNO, a 24. klatka sprawia, że jest za duży.

Flash Arduino UNO, 32 KB: biblioteka i szkic zajmują około 8 KB, a następnie 23 klatki na cały ekran wypełniają go do 99%, a klatka 24 się nie zmieści

Dlaczego koń się mieści, a cały ekran nie ​

Koń ma 96 na 64 piksele, więc klatka zajmuje 96 × 64 ÷ 8 = 768 bajtów. Piętnaście klatek to około 11 KB, a szkic mieści się w 62% flash.

Flash UNO z 15 klatkami konia po 768 bajtów: około 11 KB, 62%, mieści się

Cały ekran przy dwudziestu klatkach na sekundę potrzebuje trzydziestu klatek po 1024 bajty, a szkic rośnie do 121% flash. Kompilator zatrzymuje się z komunikatem „Sketch too big”. Ale GIF ma tylko piętnaście rysunków. Aby odtworzyć go przy dwudziestu klatkach na sekundę, szkic pokazuje każdy rysunek dwa razy, więc połowa z tych trzydziestu klatek to kopie.

Flash UNO przepełnia się na 121% przy 30 klatkach na cały ekran, co druga klatka jest kopią poprzedniej

Trzy sposoby, aby animacja się zmieściła ​

  1. Mniej klatek. Zachowaj własną liczbę klatek GIF-a: wyższa dodaje tylko kopie.
  2. Mniejsze klatki. Każdy przycięty wiersz i kolumna oszczędza bajty w każdej klatce.
  3. Większa płytka. ESP32 ma 1,25 MB flash na twój program: miejsce na około tysiąc klatek na cały ekran.

Trzy sposoby dopasowania animacji: mniej klatek, mniejsze klatki, większa płytka; ESP32 ma 1,25 MB flash aplikacji, miejsce na 1000 klatek na cały ekran, podczas gdy cały flash UNO to mały pasek

Krok 1: zaimportuj GIF ​

Utwórz nowy projekt jak w odcinku 1: + Nowy projekt, platforma U8g2, rozmiar ekranu 128×64.

Biblioteka animacji ​

Otwórz kartę Animacje po lewej. Lopaka zawiera gotowe animowane ikony, a szacunkowy rozmiar znajduje się obok przycisków rozmiaru i jakości. Ikona 64×64 w jakości Wysoka zajmuje około 14 KB; przy 32×32 i jakości Niska to mniej niż jeden kilobajt. Wybierz tam ikonę, gdy potrzebujesz tylko małej animacji.

Prześlij swój GIF ​

W przypadku własnego GIF-a kliknij Prześlij u góry panelu i upuść plik w oknie. Wideo też działa, jak zobaczysz w kroku 5.

Lopaka dzieli GIF na klatki i otwiera okno importu. Klatki są po lewej, podgląd pośrodku, ustawienia po prawej:

  • Dithering: przełącz go z Floyd-Steinberg na Brak ditheringu. Zdjęcie zamienia się w czystą białą sylwetkę zamiast chmury kropek. Dithering pomaga przy odcieniach, jak w odcinku 2; sylwetka go nie potrzebuje.
  • Klatki na sekundę: zachowaj 10, własną prędkość GIF-a.

Wiersz na dole wykonuje za ciebie matematykę pamięci: 15 klatek źródłowych, 15 klatek wyjściowych, rozmiar klatki 96×64, około 11,3 KiB. Lopaka przeskalowała GIF 300×200 do wysokości ekranu, 64 pikseli, co daje mu szerokość 96 pikseli. Jedna klatka to 768 bajtów, liczba na górze ustawień.

Okno importu z GIF-em konia: 15 klatek po lewej, biała sylwetka w podglądzie, Dithering ustawiony na Brak ditheringu, 10 klatek na sekundę, 15 klatek źródłowych i wyjściowych, rozmiar klatki 96×64 i szacunkowy rozmiar 11,3 KiB

Częstym błędem jest podnoszenie liczby klatek, aby GIF był płynniejszy. Przy 20 FPS okno pokazuje 15 klatek źródłowych, ale 30 klatek wyjściowych, a szacunek podwaja się do 22,5 KiB: tych samych piętnaście rysunków, każdy dwa razy.

Kliknij Importuj.

Krok 2: edytor klatek ​

Klatki otwierają się w edytorze klatek, ze wszystkimi piętnastoma na osi czasu na dole.

  • Odtwórz (przycisk trójkąta) sprawdza pętlę.
  • Przy włączonym przełączniku Cebula poprzednia klatka prześwituje na szaro, więc widzisz, jak daleko nogi przesuwają się z jednej klatki do następnej.

Stare zdjęcia mają liczby wzdłuż dolnej krawędzi, a import zamienił je w białe znaki. Usuń je ze wszystkich klatek naraz: kliknij rozmiar klatki obok liczby klatek (96×64 px), ustaw Wysokość na 60 i kliknij Zmień rozmiar. Piksele pozostają zakotwiczone w lewym górnym rogu, więc dolne cztery wiersze znikają na każdej klatce, a licznik pamięci spada z 11,3 do 10,6 KiB. Jeśli klatka nadal ma kilka kropek liczby na dolnej krawędzi, przejdź do niej i zetrzyj je gumką.

Edytor klatek na klatce 2 z 15: koń o rozmiarze 96×60 pikseli z poprzednią klatką prześwitującą na szaro, 10 FPS, 96×60 px i 10,6 KiB na dole, Cebula włączona

Kliknij Zapisz. Animacja znajduje się na ekranie jak każda inna warstwa, o nazwie horse, na X 16 i Y 0: (128 − 96) ÷ 2 = 16 umieszcza ją pośrodku.

Krok 3: kod ​

Klatki wprost do kodu ​

W przełącznikach obok kodu odznacz Pomocnicy animacji. Teraz Lopaka zapisuje klatki wprost do kodu, a twoje własne loop() decyduje, kiedy je narysować:

cpp
// [BEGIN lopaka generated]
static const unsigned char animation_horse_frames[15][720] U8X8_PROGMEM = {
  {0x00,0x00,0x00, /* ... */ 0x00,0x00},   // frame 1: 720 bytes, shortened here
  // ... 13 more frames ...
  {0x00,0x00,0x00, /* ... */ 0xff,0xff}    // frame 15
};
static int animation_horse_frame = 0;
void drawScreen_1(void) {
    u8g2.setFontMode(1);
    u8g2.setBitmapMode(1);
    // horse
    u8g2.drawXBMP(16, 0, 96, 60, animation_horse_frames[animation_horse_frame]);
    animation_horse_frame = (animation_horse_frame + 1) % 15;
}
// [END lopaka generated]

Co oznaczają liczby:

  • [15][720]: jedna tablica piętnastu klatek, po 720 bajtów każda. Wiersz 96 pikseli zajmuje 96 ÷ 8 = 12 bajtów, a 60 wierszy daje 720. Łącznie to 10 800 bajtów, te 10,6 KiB z edytora klatek.
  • U8X8_PROGMEM przechowuje klatki we flash, jak w przypadku obrazów w odcinku 2. Bez niego UNO próbowałoby skopiować je do swojego 2 KB RAM.
  • drawXBMP(16, 0, 96, 60, ...) rysuje bieżącą klatkę na pozycji i rozmiarze warstwy.
  • (animation_horse_frame + 1) % 15 przechodzi do następnej klatki. % 15 to reszta z dzielenia przez 15, więc po klatce 14 (piętnastej, licząc od zera) licznik wraca do 0.

Reszta szkicu ​

Wokół wygenerowanego bloku napisz resztę szkicu. Możesz wpisać ją w panelu kodu Lopaka: linie nad // [BEGIN lopaka generated] i pod // [END lopaka generated] są twoje, pozostają w projekcie, gdy zmienisz projekt, a opcja Kopiuj zabiera je ze sobą.

cpp
#include <U8g2lib.h>

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

// Paste the Lopaka code here

void setup() {
  u8g2.begin();
}

void loop() {
  u8g2.clearBuffer();
  drawScreen_1();
  u8g2.sendBuffer();
  delay(56);
}

Każde przejście loop() to jedna klatka: wyczyść, narysuj, wyślij, poczekaj. To pętla z początku artykułu.

Panel kodu w Lopaka z dyrektywą include i wyświetlaczem nad wygenerowanym blokiem, tablicą klatek animation_horse_frames[15][720] U8X8_PROGMEM, funkcją rysowania oraz setup i loop z delay(56) poniżej; Pomocnicy animacji odznaczeni

Dlaczego opóźnienie wynosi 56 milisekund ​

Wysłanie pełnej ramki na OLED przez I2C zajmuje około 44 milisekund na symulowanym UNO. Dodaj opóźnienie 56 milisekund, a każda klatka zajmuje 100 milisekund: dziesięć klatek na sekundę, prędkość GIF-a. Na prawdziwej płytce czas wysłania ramki zależy od prędkości I2C; jeśli koń biegnie za szybko lub za wolno, zmień opóźnienie.

Kliknij Kopiuj.

Krok 4: uruchom to na Arduino UNO ​

Otwórz swój projekt symulatora z odcinka 1: Arduino UNO i OLED SSD1306 podłączony do GND, 5V, A5 (SCL) i A4 (SDA), z biblioteką U8g2. Wklej kod do sketch.ino i naciśnij Play.

Koń galopuje. Po skompilowaniu dla UNO szkic raportuje:

Sketch uses 19324 bytes (59%) of program storage space. Maximum is 32256 bytes.
Global variables use 1558 bytes (76%) of dynamic memory, leaving 490 bytes for local variables. Maximum is 2048 bytes.

To 59% flash, mniej niż 62% sprzed przycięcia czterech wierszy. Jak w odcinku 1, większość RAM to bufor ramki o rozmiarze 1024 bajtów.

Krok 5: wideo na ESP32 ​

Zaimportuj wideo ​

Lopaka importuje też wideo (MP4, MOV i WebM). Użyliśmy klipu NASA Scientific Visualization Studio, który pokazuje cały miesiąc faz księżyca w dziesięć sekund.

Utwórz kolejny projekt U8g2 na 128×64, otwórz Animacje i kliknij Prześlij. Okno importu wideo wykonuje obliczenia, gdy zmieniasz ustawienia:

  1. Przy 30 klatkach na sekundę wideo dziesięć sekund daje 300 klatek na 64×64: 150 KiB.
  2. Powolny księżyc wygląda dobrze przy 12 klatkach na sekundę: wpisz 12, a stanie się to 120 klatek, 60 KiB.
  3. Włącz Odwróć wynik. Lopaka zapala ciemne części obrazka, dlatego ciemny koń na jasnym tle wyszedł biały. Księżyc jest odwrotny, jasny na ciemnym niebie, więc odwróć wynik: teraz księżyc się zapala, a niebo pozostaje ciemne.

Tym razem zachowaj dithering Floyd-Steinberg: kropki rysują cieniowanie powierzchni księżyca.

Okno importu wideo z księżycem: księżyc w pełni w podglądzie jako wzór kropek, dithering Floyd-Steinberg, włączona opcja Odwróć wynik, 12 klatek na sekundę, czas trwania 0:10, 120 klatek, rozmiar klatki 64×64 i szacunkowy rozmiar 60,0 KiB

Kliknij Importuj, a następnie Zapisz w edytorze klatek. Lopaka wyśrodkowuje animację 64×64 na ekranie, na X 32. Ponownie odznacz Pomocników animacji, a kod otrzyma animation_moon_frames[120][512] oraz linię animation_moon_frame = (animation_moon_frame + 1) % 120;.

60 KB: dwa razy więcej niż UNO, poniżej 5% ESP32 ​

Przy 64 na 64 piksele każda klatka to 64 × 64 ÷ 8 = 512 bajtów, więc 120 klatek zajmuje 61 440 bajtów, 60 KB. To około dwa razy więcej niż cały flash UNO.

Klip księżyca 60 KB, 120 klatek, leżący na 32 KB flash Arduino UNO: około 2 razy więcej niż całe UNO

ESP32 ma 1 310 720 bajtów flash na program, a ten sam klip zajmuje 4,7% z tego.

Uruchom to na ESP32 ​

Użyj tego samego szkicu z kodem księżyca w jego miejscu i zmień opóźnienie na delay(32);. ESP32 wysyła ramkę w około 51 milisekund w symulatorze, więc każda klatka zajmuje około 83 milisekund: 12 klatek na sekundę.

W symulatorze utwórz projekt ESP32, dodaj OLED SSD1306 i bibliotekę U8g2 oraz podłącz wyświetlacz do pinów I2C ESP32: GND do GND, VCC do 3V3, SCL do GPIO 22 i SDA do GPIO 21. Wklej szkic i naciśnij Play. Kompilator raportuje 352 240 bajtów, 26% flash, a księżyc rośnie i kurczy się przez cały miesiąc.

ESP32 podłączone do OLED SSD1306 w symulatorze; wyświetlacz pokazuje księżyc w pełni narysowany kropkami

Podsumowanie ​

  1. Animacja to stos klatek, pokazywanych wystarczająco szybko, aby wyglądały jak ruch. Każde przejście loop() czyści, rysuje jedną klatkę, wysyła ją i czeka.
  2. Każda klatka kosztuje szerokość × wysokość ÷ 8 bajtów flash. Klatka na cały ekran na tym OLED to kilobajt, a UNO mieści ich około dwudziestu.
  3. Aby GIF się zmieścił, użyj mniejszej liczby klatek, mniejszych klatek lub większej płytki.

Następny odcinek: Interfejs dotykowy z LVGL. Ekrany dotykowe, z przyciskami i suwakami, jak w telefonie. Przeczytaj odcinek 8 lub obejrzyj go na YouTube.

Gotowy, aby spróbować? Otwórz Lopaka i ożyw własny GIF.