Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2014:teeter-game

Hra Teeter

Zadání:

Na vývojovém kitu 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 32F429IDISCOVERY, který obsahuje 2.4 palcový displej QVGA TFT s rozlišením 240×320. Dále byl použit akcelerometr GY-521.

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í CooCox CoIDE, které využívá 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 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.

2014/teeter-game.txt · Poslední úprava: 2015/01/19 02:17 autor: Roman Deneš