Implementacja dotykowej wersji „Snake” na STM32F4DISCOVERY
Złącze do kart SD zamontowane jest już na płytce bazowej (rysunek 3). Komunikację w trybie magistrali jednobitowej zapewnia gotowa biblioteka. Zarówno płytka zestawu, jak i biblioteka, umożliwiają także komunikację w trybie magistrali czterobitowej, ale obsługa tego trybu nie jest wymagana przez standard SD, więc nie wszystkie karty muszą ją obsługiwać. Przy konfiguracji zegara transmisji na 48 MHz komunikacja z kartą nie będzie wąskim gardłem. Projekt obsługuje karty z systemem plików FAT korzystając z modułu FatFs (w wersji R0.08a).
W projekcie do obsługi sprzętu wykorzystuje gotowe biblioteki dostarczone przez STMicroelectronics. Takie rozwiązanie ma zalety, ale czasami zdarzają się problemy spowodowane błędami w bibliotece. Jednym z błędów – wykrytych podczas prac nad implementacją gry w bibliotece – obsługi wyświetlacza (stm32f4_discovery_lcd.c) jest niepoprawne użycie funkcji do ustawiania kolorów tekstu i tła. Funkcje te (LCD_SetTextColor() i LCD_SetBackColor()) modyfikują zmienne globalne odpowiednio: TextColor i BackColor.
Dlatego użycie:
LCD_SetTextColor(TextColor);
lub
LCD_SetBackColor(BackColor);
nie ma większego sensu, ponieważ jest to w rzeczywistości wykonanie polecenia odpowiednio:
TextColor = TextColor;
lub
BackColor = BackColor;
Użycie funkcji modyfikacji koloru tekstu, w celu wyświetlenia elementu w kolorze tła, w sposób następujący:
LCD_SetTextColor(BackColor);
spowoduje ustawienie TextColor i BackColor na tą samą wartość i zamierzony efekt zostanie osiągnięty. Jednak ponowne użycie tej funkcji z parametrem TextColor, w celu przywrócenia koloru tekstu:
LCD_SetTextColor(TextColor);
nie powiedzie się, ponieważ zmienna TextColor nie zawiera już pierwotnej wartości barwy tekstu.
Oprócz wspomnianej biblioteki wyświetlacza, wykorzystane zostały jeszcze biblioteka do obsługi panelu dotykowego: stmpe811qtr.c oraz obsługa systemu plików FatFs. Do biblioteki wyświetlacza LCD dodana została funkcja umożliwiająca wyświetlanie obrazków prosto z karty SD (listing 1).
List. 1.
void LCD_DispPic_SD(u16 x, u16 y, u16 h, u16 w, const TCHAR * f_name) { u32 temp, data_length; FRESULT rc; UINT br, i; FIL Fil; /* File object */ rc = f_open(&Fil, f_name, FA_READ); if (rc) die(rc); LCD_SetDisplayWindow(x, y, h - 1, w - 1); LCD_WriteRAM_Prepare(); data_length = ((h*w)<<1); for (temp = 0; temp < data_length;) { rc = f_read(&Fil, Buff, sizeof Buff, &br); /* Read a chunk of file */ if (rc || !br) break; /* Error or end of file */ for (i = 0; i < br;) /* Send the data */ { LCD_WriteRAM(*(u16 *)(&Buff[i])); i+=2; } temp+=br; } if (rc) die(rc); rc = f_close(&Fil); if (rc) die(rc); }
Plik obrazka podany w parametrze funkcji jest otwierany na karcie i dane prosto z karty czytane są do bufora, z którego następnie wysyłane są do pamięci wyświetlacza. Obrazki muszą mieć 16-bitową paletę barw w formacie RGB565 (rysunek 4), który odpowiada zapisowi wartości kolorów czerwonego, zielonego i niebieskiego na 16 bitach dla każdego piksela: 5 bitów – czerwony, 6 bitów – zielony i 5 bitów – niebieski
Rys. 4. Sposób bajtowego zapisu kolorów w formacie RGB565
W artykule przedstawiamy sposób zapisu grafik w plikach BMP oraz udostępniamy oprogramowanie umożliwiające ich używanie w aplikacjach mikrokontrolerowych. |
Obsługa panelu dotykowego jest bardzo prosta, ponieważ funkcja IOE_TS_GetState() zwraca nam parametry dotkniętego punktu (listing 2).
List. 2.
TS_STATE *pstate = 0; // touch screen state (…) /* Touch screen initial config */ IOE_Config(); (…) pstate = IOE_TS_GetState(); if (pstate->TouchDetected) { /* pstate->X, pstate->Y usage */ }
Standardowa biblioteka wymaga kalibracji panelu przed pierwszym użyciem. Jest to cena za uniwersalność biblioteki i możliwość używania jej na różnych panelach dotykowych. W celu uproszczenia działania gry i pominięcia etapu kalibracji przy każdym włączeniu, dane kalibracyjne dla użytego panelu zostały wpisane na stałe do funkcji obliczającej współrzędne punktu dotknięcia.
Fot. 5. Ekran informujący o sposobie sterowania w grze z zaznaczonymi obszarami zmiany kierunków ruchu węża
W przypadku prezentowanego projektu precyzja dotknięcia nie jest krytyczna, więc nawet jeśli są jakieś rozbieżności pomiędzy panelami, nie powinno to mieć wpływu na działanie gry, ponieważ sterowanie wężem polega na dotykaniu odpowiednich fragmentów ekranu, które są dość duże (fotografia 5). Sposób wyznaczania nowego kierunku ruchu węża nie jest skomplikowany, pokazano go na listingu 3.
List. 3.
void calcDirection(u16 x, u16 y) { #define MARGIN 10 if ((x < MARGIN) || (x > (DISP_HOR_RESOLUTION - MARGIN)) || (y < MARGIN) || (y > (DISP_VER_RESOLUTION - MARGIN))) { return; } // position calculation if(y < ((HEADER_SIZE + DISP_VER_RESOLUTION)>>1)) { // upper position if (x + HEADER_SIZE< y) { // left position g_direction = left; } else if ((DISP_HOR_RESOLUTION - x + HEADER_SIZE) < y) { // right position g_direction = right; } else { // middle position g_direction = up; } } else { // lower position if ((DISP_VER_RESOLUTION - y) > x) { // left position g_direction = left; } else if ((DISP_VER_RESOLUTION - y) > (DISP_HOR_RESOLUTION - x)) { // right position g_direction = right; } else { // middle position g_direction = down; } } }