Zde můžete vidět rozdíly mezi vybranou verzí a aktuální verzí dané stránky.
Obě strany předchozí revize Předchozí verze Následující verze | Předchozí verze Poslední revize Obě strany příští revize | ||
2014:teeter-game [2015/01/19 00:46] Roman Deneš |
2014:teeter-game [2015/01/19 02:03] Roman Deneš |
||
---|---|---|---|
Řádek 5: | Řádek 5: | ||
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). | 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í ==== | ==== 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" displej QVGA TFT s rozlišením 240x320. Dále byl použit akcelerometr [[http://www.dipmicro.com/store/GY521-MOD|GY-521]]. | 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" 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ů: | Zapojení PINů: | ||
Řádek 32: | Řádek 37: | ||
==== Realizace: ==== | ==== 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žívali tento prostor a docházelo ke konfliktům. Z toho důvodu je matice v paměti posunuta o přibližně 7 MB. | + | 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. |
<code cpp> | <code cpp> | ||
Řádek 42: | Řádek 47: | ||
#define MATRIX_GET_VALUE(x, y) TM_SDRAM_Read8(OFFSET + x * Y_SIZE + y) | #define MATRIX_GET_VALUE(x, y) TM_SDRAM_Read8(OFFSET + x * Y_SIZE + y) | ||
</code> | </code> | ||
+ | |||
+ | 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ředu 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. | ||
+ | |||
+ | <code> | ||
+ | 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); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | 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). Od těchto hodnot je 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. | ||
<code cpp> | <code cpp> | ||
- | // cteni dat z akceletometru | ||
if (sensor1) | if (sensor1) | ||
TM_MPU6050_ReadAll(&MPU6050_Data0); | TM_MPU6050_ReadAll(&MPU6050_Data0); | ||
- | |||
- | Xpos_old = Xpos; | ||
- | Ypos_old = Ypos; | ||
// Nacteni vychozi pozice | // Nacteni vychozi pozice | ||
Řádek 62: | Řádek 82: | ||
Y_axis = (Q*Y_axis + (10-Q)*(MPU6050_Data0.Accelerometer_Y - Y_axis_ref))/10; | Y_axis = (Q*Y_axis + (10-Q)*(MPU6050_Data0.Accelerometer_Y - Y_axis_ref))/10; | ||
- | // zmena polohy | + | </code> |
- | if(X_axis < -th1){ | + | |
+ | 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. | ||
+ | |||
+ | <code> | ||
+ | if(X_axis < -th1){ | ||
Xpos++; | Xpos++; | ||
- | if(X_axis < -th2) | + | if(X_axis < -th2) |
Xpos++; | Xpos++; | ||
- | if(X_axis < -th3) | + | if(X_axis < -th3) |
Xpos++; | Xpos++; | ||
if(Xpos > 228) | if(Xpos > 228) | ||
Xpos = 228; | Xpos = 228; | ||
} | } | ||
+ | | ||
+ | // dalsi tri smery ... | ||
+ | | ||
+ | </code> | ||
- | if(X_axis > th1){ | + | 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ě. |
- | Xpos--; | + | |
- | if(X_axis > th2) | + | |
- | Xpos--; | + | |
- | if(X_axis > th3) | + | |
- | Xpos--; | + | |
- | if(Xpos < 11) | + | |
- | Xpos = 11; | + | |
- | } | + | |
- | if(Y_axis > th1){ | + | 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. |
- | Ypos++; | + | |
- | if(Y_axis > th2) | + | |
- | Ypos++; | + | |
- | if(Y_axis > th3) | + | |
- | Ypos++; | + | |
- | if(Ypos > 308) | + | |
- | Ypos = 308; | + | |
- | } | + | |
- | if(Y_axis < -th1){ | + | <code> |
- | Ypos--; | + | void DrawBall(uint16_t Xpos, uint16_t Ypos, uint16_t Xpos_old, uint16_t Ypos_old) |
- | if(Y_axis < -th2) | + | { |
- | Ypos--; | + | |
- | if(Y_axis < -th3) | + | |
- | Ypos--; | + | |
- | if(Ypos < 11) | + | |
- | Ypos = 11; | + | |
- | } | + | |
- | // setrvacnost | ||
- | Xpos += (Xpos-Xpos_old)/2; | ||
- | Ypos += (Ypos-Ypos_old)/2; | ||
- | // pohyb podel zdi | + | TM_ILI9341_DrawFilledCircle(Xpos_old, Ypos_old, 10, ILI9341_COLOR_BLACK); |
- | if((MATRIX_GET_VALUE(Xpos, Ypos)) > 10){ | + | TM_ILI9341_DrawFilledCircle(Xpos_old, Ypos_old, 10, ILI9341_COLOR_BLACK); |
- | if((MATRIX_GET_VALUE(Xpos, Ypos_old)) < 11){ | + | |
- | Ypos = Ypos_old; | + | |
- | } | + | |
- | else if((MATRIX_GET_VALUE(Xpos_old, Ypos)) < 11){ | + | |
- | Xpos = Xpos_old; | + | |
- | } | + | |
- | else{ | + | |
- | Xpos = Xpos_old; | + | |
- | Ypos = Ypos_old; | + | |
- | } | + | |
- | } | + | |
- | // konec hry | + | TM_ILI9341_DrawFilledCircle(Xpos, Ypos, 10, ILI9341_COLOR_BLUE); |
- | if((MATRIX_GET_VALUE(Xpos, Ypos)) == 5){ | + | TM_ILI9341_DrawFilledCircle(Xpos, Ypos, 10, ILI9341_COLOR_BLUE); |
- | DrawDrop(Xpos, Ypos, Xpos_old, Ypos_old, LVL_count); | + | |
- | } | + | |
- | // dalsi kolo | + | Delayms(15); |
- | if((MATRIX_GET_VALUE(Xpos, Ypos)) == 6){ | + | } |
+ | </code> | ||
- | DrawDrop(Xpos, Ypos, Xpos_old, Ypos_old, LVL_count); | + | 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 smazány procházecí kuličkou v těsném okolí. |
- | LVL_count++; | + | |
- | LVL_change = 1; | + | |
- | Xpos = 220; | + | <code> |
- | Ypos = 300; | + | void ReDrawLayout(uint16_t Xpos, uint16_t Ypos){ |
- | Xpos_old = 220; | + | |
- | Ypos_old = 300; | + | |
- | } | + | 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); | ||
- | ReDrawLayout(Xpos, Ypos); | + | if((MATRIX_GET_VALUE(i, j)) == 11) |
- | DrawBall(Xpos, Ypos, Xpos_old, Ypos_old); | + | TM_ILI9341_DrawPixel(i, j, ILI9341_COLOR_YELLOW); |
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
- | if(LVL_change == 1){ | + | 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ů. |
- | LVL_change = 0; | + | <code> |
- | DrawLayout(LVL_count); | + | 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. | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | </code> | ||
+ | |||
+ | ==== 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í. | ||
+ | |||
+ | 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. | ||
- | } | ||
- | </code> |