===== Hra Teeter ===== ==== Zadání: ==== Na vývojovém kitu [[http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF259090|32F429IDISCOVERY]] realizujte androidovou hru teeter. Na základě údajů z gyroskopu se na displeji bude pohybovat kulička. Cílem hry je dopravit kuličku na požadované místo. Při pohybu je nutné se vyhnout případným překážkám (stěny, díry). ==== Použitá zařízení ==== Projekt je realizován na vývojovém kitu [[http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF259090|32F429IDISCOVERY]], který obsahuje 2.4 palcový displej QVGA TFT s rozlišením 240x320. Dále byl použit akcelerometr [[http://www.dipmicro.com/store/GY521-MOD|GY-521]]. {{ :2014:teeter-game:imag_teeter.jpg?300 |Použitá zařízení}} Zapojení PINů: ^ GY-521 ^ 32F429IDISCOVERY ^ Popis ^ | SCL | PA8 | Hodinový signál | | SDA | PC9 | Data | | AD0 | GND | Adresa zařízení | | VCC | 3V | Napájecí napětí 3.3V | | GND | GND | Uzemnění | ==== Software: ==== Pro vývoj aplikace bylo využito volně dostupné vývojové prostředí [[http://www.coocox.org/|CooCox CoIDE]], které využívá [[https://launchpad.net/gcc-arm-embedded|GCC kompiler]] určený pro ARM procesory. Dále bylo využito několika knihoven z webu http://stm32f4-discovery.com/. Seznam použitých knihoven: ^ Název knihovny ^ Popis ^ | stm32f4xx | Obecná knihovna pro daný vývojový kit | | tm_stm32f4_delay | Přesné zpoždění | | tm_stm32f4_ili9341_ltdc | Ovladač k displeji | | tm_stm32f4_mpu6050 | Ovladač k akcelerometru | | tm_stm32f4_i2c | knihovna pro použití I2C | | tm_stm32f4_sdram | knihovna pro přístup do paměti | ==== Realizace: ==== Celý layout hry je uložen v SDRAM pamětí a při interakci vykreslován na displej zařízení. Nejprve bylo nutné definovat makro pro přístup do paměti na základě souradnic x, y. Pamět není mapována hned od prvního dostupného místa, jelikož některé z dalších knihoven taktéž využívaly tento prostor a docházelo ke konfliktům. Z toho důvodu je matice v paměti posunuta o přibližně 7 MB. #define X_SIZE 240 #define Y_SIZE 320 #define OFFSET 7000000 #define MATRIX_SET_VALUE(x, y, val) TM_SDRAM_Write8(OFFSET + x * Y_SIZE + y, val) #define MATRIX_GET_VALUE(x, y) TM_SDRAM_Read8(OFFSET + x * Y_SIZE + y) Na jednotlivé pozice se pomocí funkce DrawLayout "vykreslil" požadovaný layout v závislosti na aktuální úrovni. Pod číslem 11 jsou uloženy stěny. Pod číslem 12 pak rozšířený prostor do kterého kulička nemůže vstoupit. Tento prostor odpovídá poloměru kuličky. Pod čísly 5 a 6 jsou jednotlivé díry. K nim odpovídají středy jsou uloženy pod čísly 4 a 7. Pro vykreslování děr (kruhů) je použita modifikovaná funkce z knihovny [[http://stm32f4-discovery.com/2014/06/library-18-ili9341-ltdc-stm32f429-discovery/|tm_stm32f4_ili9341_ltdc]]. Místo vykreslování jednotlivých pixelů funkce provádí zápis do paměti. Na konci funkce je pak dvojitá smyčka, která na základě číselných údajů z matice vykresluje na displej jednotlivé barevné pixely. for(i = 0; i < 240; i++){ for(j = 0; j < 320; j++){ if((MATRIX_GET_VALUE(i, j)) < 6) TM_ILI9341_DrawPixel(i, j, ILI9341_COLOR_GREEN); if((MATRIX_GET_VALUE(i, j)) == 6) TM_ILI9341_DrawPixel(i, j, ILI9341_COLOR_YELLOW); if((MATRIX_GET_VALUE(i, j)) == 11) TM_ILI9341_DrawPixel(i, j, ILI9341_COLOR_YELLOW); } } V následující supersmyčce jsou opakovaně čteny data z připojeného akcelerometru, která jsou dále zpracována. Nejprve jsou váhována (60 % předchozí hodnoty + 40% z nově načtené hodnoty) a dále je od těchto hodnot vždy odečtena referenční hodnota, která se načte při zapnutí přistroje. Toto opatření je zejména kvůli nedokonalému připevnění sensoru, který nemusí být v naprosto stejné poloze jako deska. if (sensor1) TM_MPU6050_ReadAll(&MPU6050_Data0); // Nacteni vychozi pozice if(start == 1){ X_axis_ref = MPU6050_Data0.Accelerometer_X; Y_axis_ref = MPU6050_Data0.Accelerometer_Y; start = 0; } // Setrvacnost X_axis = (Q*X_axis + (10-Q)*(MPU6050_Data0.Accelerometer_X - X_axis_ref))/10; Y_axis = (Q*Y_axis + (10-Q)*(MPU6050_Data0.Accelerometer_Y - Y_axis_ref))/10; Dále se uloží současná poloha a provede se změna na základě údajů z akcelerometru. Zde ukázka pouze pro jeden směr. Poslední podmínka ošetřuje okraj displeje. if(X_axis < -th1){ Xpos++; if(X_axis < -th2) Xpos++; if(X_axis < -th3) Xpos++; if(Xpos > 228) Xpos = 228; } // dalsi tri smery ... Za předpokladu, že nově získaná poloha není dostupná (zeď), provede se načtení staré polohy. Tato akce probíhá pro oba směry (x, y) jednotlivě. Nově získaná poloha se vykresluje pomocí funkce DrawBall. Nejprve se smaže stará kulička a následně se vykreslí na nových souřadnicích. void DrawBall(uint16_t Xpos, uint16_t Ypos, uint16_t Xpos_old, uint16_t Ypos_old) { TM_ILI9341_DrawFilledCircle(Xpos_old, Ypos_old, 10, ILI9341_COLOR_BLACK); TM_ILI9341_DrawFilledCircle(Xpos_old, Ypos_old, 10, ILI9341_COLOR_BLACK); TM_ILI9341_DrawFilledCircle(Xpos, Ypos, 10, ILI9341_COLOR_BLUE); TM_ILI9341_DrawFilledCircle(Xpos, Ypos, 10, ILI9341_COLOR_BLUE); Delayms(15); } Před každým vykreslením kuličky je ještě volána funkce ReDrawLayout, která překresluje (obnovuje) layout v okolí kuličky. Jde především o vykreslení děr, které mohou být částečně smazány procházející kuličkou. void ReDrawLayout(uint16_t Xpos, uint16_t Ypos){ static uint16_t i, j; for(i = (Xpos-20); i < (Xpos+20); i++){ for(j = (Ypos-20); j < (Ypos+20); j++){ if((MATRIX_GET_VALUE(i, j)) < 6) TM_ILI9341_DrawPixel(i, j, ILI9341_COLOR_GREEN); if((MATRIX_GET_VALUE(i, j)) == 6) TM_ILI9341_DrawPixel(i, j, ILI9341_COLOR_YELLOW); if((MATRIX_GET_VALUE(i, j)) == 11) TM_ILI9341_DrawPixel(i, j, ILI9341_COLOR_YELLOW); } } } V případě, že kulička spadne do díry (načtená souřadnice má hodnotu 5 nebo 6) je volána funkce DrawDrop, která provede animaci kuličky do díry a vykreslení (mezi)výsledků. void DrawDrop(uint16_t Xpos, uint16_t Ypos, uint16_t Xpos_old, uint16_t Ypos_old, uint8_t level) { TM_ILI9341_DrawFilledCircle(Xpos_old, Ypos_old, 10, ILI9341_COLOR_BLACK); TM_ILI9341_DrawFilledCircle(Xpos, Ypos, 10, ILI9341_COLOR_BLUE); uint16_t i, j; uint16_t Xmid, Ymid; int16_t deltaX, deltaY; for(i = (Xpos-22); i < (Xpos+22); i++){ for(j = (Ypos-22); j < (Ypos+22); j++){ if((MATRIX_GET_VALUE(i, j) == 4 )|(MATRIX_GET_VALUE(i, j) == 7 )){ Xmid = i; Ymid = j; } } } deltaX = (Xmid-Xpos); deltaY = (Ymid-Ypos); TM_ILI9341_DrawFilledCircle(Xpos, Ypos, 10, ILI9341_COLOR_BLUE); Delayms(50); TM_ILI9341_DrawFilledCircle(Xpos, Ypos, 10, ILI9341_COLOR_BLACK); TM_ILI9341_DrawFilledCircle(Xpos+deltaX/4, Ypos+deltaY/4, 10, ILI9341_COLOR_BLUE); Delayms(50); TM_ILI9341_DrawFilledCircle(Xpos+deltaX/4, Ypos+deltaY/4, 10, ILI9341_COLOR_BLACK); TM_ILI9341_DrawFilledCircle(Xpos+2*deltaX/4, Ypos+2*deltaY/4, 10, ILI9341_COLOR_BLUE); Delayms(50); TM_ILI9341_DrawFilledCircle(Xpos+2*deltaX/4, Ypos+2*deltaY/4, 10, ILI9341_COLOR_BLACK); TM_ILI9341_DrawFilledCircle(Xpos+3*deltaX/4, Ypos+3*deltaY/4, 10, ILI9341_COLOR_BLUE); Delayms(50); TM_ILI9341_DrawFilledCircle(Xpos+3*deltaX/4, Ypos+3*deltaY/4, 10, ILI9341_COLOR_BLACK); TM_ILI9341_DrawFilledCircle(Xmid, Ymid, 10, ILI9341_COLOR_BLUE); Delayms(50); // if((MATRIX_GET_VALUE(Xpos, Ypos)) == 5){ TM_ILI9341_DrawFilledCircle(Xmid, Ymid, 12, ILI9341_COLOR_BLUE2); TM_ILI9341_DrawFilledCircle(Xmid, Ymid, 10, ILI9341_COLOR_BLUE); Delayms(1250); TM_ILI9341_Fill(ILI9341_COLOR_RED); Delayms(250); TM_ILI9341_Fill(ILI9341_COLOR_BLACK); Delayms(250); TM_ILI9341_Fill(ILI9341_COLOR_RED); Delayms(250); TM_ILI9341_Fill(ILI9341_COLOR_BLACK); Delayms(250); TM_ILI9341_Fill(ILI9341_COLOR_RED); Delayms(250); TM_ILI9341_Fill(ILI9341_COLOR_BLACK); Delayms(250); TM_ILI9341_Fill(ILI9341_COLOR_RED); TM_ILI9341_Puts(50, 100, "Game Over", &TM_Font_16x26, 0x0000, ILI9341_COLOR_RED); while(1){} } // Obdobne i pro MATRIX_GET_VALUE(Xpos, Ypos)) == 6 -> dalsi kolo. } ==== Závěr ==== V projektu byl místo interního gyroskopu využit externí akcelerometr, jelikož se na danou aplikace hodí lépe. Zadání bylo splněno, hra je plně funkční. Při popisu byly vynechány některé zřejmé části kódu, které jsou jasné už z jejich popisu. Dále pak některé definice proměnných a další méně podstatné funkce. Hra obsahuje dvě úrovně a je nastíněn systém pro přechod mezi jednotlivými úrovněmi. Případné rozšíření o další úrovně je tedy poměrně snadná záležitost. Video je k dispozici na adrese https://www.youtube.com/watch?v=NFYOeizr5jU&feature=youtu.be. Výsledný soubor je uložen v souborovém systému.