Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2014:led-tetris

Toto je starší verze dokumentu!


Tetris na RGB LED matici

Zadanie:

Realizujte pomocí FRDM-KL25Z a RGB LED matice 8×8 hru Tetris ovládanou akcelerometrem. Při realizaci využijte barevnost matice (např. každá kostka bude mít jinou barvu).

TETRIS

Čo to ten tetris vlastne je: (naozaj sa mi stalo, že to niekto nevedel)

Hardware

Okrem FRDM-KL25Z (ktorej popis sem nebudem dávať, pretože google má doma teraz už skoro každý) a RG LED matice 8×8, ktorá má tak trochu neznáme zapojenie, ale chovanie sa po pripojení k napájaniu dá relatívne jednoducho zistiť (o čo sa postaral Tomáš Jankech), som vytvoril redukciu medzi túto maticu a KL25Z (Redukcia matice 8x8 pre KL25Z).

Čo sa týka redukcie, dôvodov prečo nie je úplne ideálne navrhnutá je niekoľko. Jednak som čakal že bude doska vyrobená s prekovmi, ale kôli rozloženiu pinov na to nebolo moc miesta, za ďalšie som čakal, že pri použití mbed.org bude možné jednoduché maskovanie pinov, preto som sa snažil o zapojenie spodnej časti pinov na jednu farbu a vrchnú časť pinov na druhú, čo sa nakoniec ukázalo ako zbytočné.

Nakoniec som ešte „nabastlil“ dva 68k rezistory (hodnota daná „šuplíkom“), vytvoril tak napäťový delič a pripojil ich k PTB3 čož je A/D vstup. Tým som si môhol ďalej v programe vytvoriť random generátor pre výber segmentov. Napätie Vcc je 3,3V. V tomto projekte sa z redukcie nevyužíva UART a ani tlačítka, nakoľko by boli úplne zbytočné.

Firmware

Firmware je písaný v mbed.org. Celý zdroják je možné nájsť tu Nasledujúce riadky popisujú ako celý projekt funguje.

MAIN

Funkciu celeho systému sa pokúsim vysvetliť pomocou hlavnej funkcie:

int main() {
    SegmentInit();                          //nahranie informacii do struktur segmentov (struct segments)
    OutPortInit();                          //porty vypnute - zhasnuta matica
    disptick.attach(&Display, 0.002);       //nastavenie prerusenia pre vysvecovanie displeja
    while(1) {
            choice = RandSegment();         //nahodny vyber segmentu
            while(collision != 1){          //pokial nenastane kolizia spodnej hrany segmentu
                SegmentMove(&seg[choice]);  //pohybuj segmentov - v pravo, v lavo, dole, rotacia
            }
            LineCheck();                    //skontroluje riadoky ci nie su zaplnene ak hej posunie sa vrchna cast dole
            collision = 0;                  
    }
}

Skôr než budú vysvetlené celkova funkcia je nutné podotknúť, že pohyby segmentov, ich ukladadanie prípadne sledovanie kolízií sa sleduje v matici int dispArray[8][16] na začiatku plnej núl. Riadkov je 8, keďže LED matica je 8×8, no 16 stĺpcov je definovaných z dôvodu, že každý segment si nesie farbu ako vlastnosť. V tomto prípade sú segmenty definované pevne nasledovným spôsobom:

int segment9[2][4] = {{1,1, 1,1},   //oranzova
                     {1,1, 0,0},};

Tento segment sa bude po zobrazení javiť ako oranžové L otočené o 90° do prava. Ak by boli páry v poli definované ako 0,1 išlo by o červený segment, ak 1,0 islo by o zelený segment. Pár 0,0 sa nevysvieti. Všetky segmenty sú definované pevne, ale na základe tohto princípu by sa dalo napríklad jednoducho pomocou náhodnej generácie čísel nulovať každý prvý, druhý alebo žiadny člen poľa a meniť tak farby. Túto možnosť som nevyužil nakoľko je značne jednoduchšie inicializovať všetky polia v štruktúre pred začatím behu samotnej hry a ďalej s nimi pracovať. Základných segmentov je 12, pričom sa jedná o 4 tvary s 3 rôznymi farebnými kombináciami. Tvary sú kocka, L, I a bodka.


SegmentInit();

Stará sa nahranie polí a informácií do poľa štruktúry:

void SegmentInit(void){
    //...
    seg[6].line = 1;    
    seg[6].colum = 4;
    seg[6].nextRot = 12;
    SegmentCopy(&seg[6], segment6);  //oranzova ciara 
    seg[12].line = 2;    
    seg[12].colum = 2;
    seg[12].nextRot = 6;
    SegmentCopy(&seg[12], segment61);  //oranzova ciara rot
    //...
}

Jedná sa len o názornú ukážku funkcie, takže nie je uvedená celá. Ako je vidno, štruktúra obsahuje počet riadkov, stĺpcov segmentu, číslo poľa v prípade rotovania segmentu a samotný segment, ktorý sa kopíruje do poľa definovaného v štruktúre pomocou funkcie SegmentCopy, pričom ta obsahuje len 2 for cykly.

OutPortInit();

Jedná sa o jednoduchú funkciu, ktorá privedie na porty ovládajúce farby úroveň 1, aby bola LED matica v počiatočnom stave zhasnutá.

disptick.attach(&Display, 0.002);

Jedná sa o časové prerušenie, volané každé 2ms. Je ňou volaná funkcia Display, ktorá pri volaní vysvieti jeden riadok matice zároveň sa inkrementuje riadok, ktorý sa bude vysvecovať pri ďalšom prerušení. Je to veľmi jednoduchá funkcia, takže pravdepodobne nie je nutné ju vpisovať do tohoto dokumentu.


choice = RandSegment();

Z funkcie sa do globálnej premennej int choice; vráti náhodná hodnota. Tá je získaná jednak vyššie popisovaným deličom a ďalej zvyškom funkcie:

short RandSegment(void){                    
    static short num = 0;
    num = randAdc.read_u16()%12;    //precitanie A/D s %12 - zvysok od 0 do 11 <-> zakladne segmenty od 0 do 11
    return(num);                            
}

kde randAdc.read_u16() číta 16 bitovú hodnotu z A/D prevodníka. Modulo 12 zaistí, že vrátená hodnota bude v rozsahu od 0 do 11. Tento rozsah je daný počtom základných segmentov.


SegmentMove(&seg[choice]);

Táto funkcia sa stará v podstate o celú hrateľnosť. Beží v cykle pokial premenna collision nebude rovná 1. Tento stav nastane pokiaľ segment dopadne na spodnú časť poľa alebo na vysvietenú časť. Nakoľko je funkcia pomerne dlhá bude jednoduchšie vysvetliť ako funguje. Funkcia pracuje s premennými x a y, ktoré predstavujú súradnice lavého horného segmentu, pričom y predstavuje riadok, x predstavuje stĺpec.

Každý nový segment sa vždy objaví na súradnici y=0, x=6.

Od tejto súradnice sa skontroluje, či sa neprekryje nový segment s vysvietenou časťou (čiže predchádzajúce segmenty, prípadne ich zvyšky). Ak dojde ku kolízií pole int dispArray[8][16] sa vynuluje a začína nová hra. Ak nenastane kolízia, segment sa na určitu dobu zapíše to tohoto poľa a tým sa vysvieti.

V tomto momente sa začína inkrementovať premenná counter. Vždy keď je counter násobkom 4000, testuje sa návratová hodnota z funkcie pre obsluhu akcelerometra. Podľa návratovej hodnoty z funkcie obsluhujúcej akcelerometer sa rozhodne:

  • či sa má segment pohnúť v pravo - realizované náklonom dosky do prava
  • či sa má segment pohnúť v ľavo - realizované náklonom dosky do ľava
  • či má segment prudko klesať na dol - náklon dosky k sebe
  • či má segment rotovať - náklon od seba

Ak vyhovie niektorá z podmnienok, vymaže sa segment z poľa, odtestuje sa či segment po zmene súradníc, prípadne po orotovaní nezmizne z poľa, pripadne sa neprekryje s vysvietenou časťou. Ak je všetko v poriadku zmenia sa súradnice/rotuje sa a segment sa znova zapiše do poľa. To neplatí ale pre klesanie na dol.

Pri klesaní nadol sa totižto do counter nahrá hodnota 400001. To z dôvodu, že ak counter == 400001, inkrementuje sa súradnica y, skontroluje sa, či nedojde ku kolízií s vysvietenou časťou alebo segment nevypadne z poľa. Taktiež dochádza k vymazaniu a zápisu segmentu do poľa. V tejto časti sa teda rozhoduje o tom či:

  • segment sa posunie o riadok nižšie a funkcia je v cykle znova volaná
  • segment nemôže posunúť nižšie - segment sa na tejto pozicií zapíše do poľa, do collision sa zapíše 1 a tak sa vystúpi z cyklu while(collision != 1){…

LineCheck();

Funkcia skontroluje, či po dopade segmentu nie je zaplnený riadok. Ak je riadok zaplnený vymaže ho a časť nad týmto riadkom presunie smerom na dol.

void LineCheck(void){
    static int ch=0;
    static int lnCh, colCh;
 
    for(lnCh = 0; lnCh < 8; lnCh++){
        ch = 0;
        for(colCh=0; colCh < 15; colCh+=2){
            if((dispArray[lnCh][colCh]==1) || (dispArray[lnCh][colCh+1]==1)){       //ak je bod vysvieteny
                ch++;}                                                              //pripocita k premennej
            else {
                colCh = 15;       //ak bod v riadku nie je vysvieteny nema cenu ho cely kontrolovat a skoci sa 
                                  //tak na dalsi riadok
                ch = 0;
                }    
        }
        if((ch == 8)){                               //ak je cely riadok vysvieteny
            for(colCh=0; colCh < 16; colCh++){       //nuluj riadok
                dispArray[lnCh][colCh] = 0;
            }
            MoveDown(dispArray, lnCh);  //posunie zvysok pola dole a kontroluje sa zvysok a teda proces sa
                                        // opakuje kym sa neskontroluje cele pole
        }
    }    
}
2014/led-tetris.1421525523.txt.gz · Poslední úprava: 2015/01/17 21:12 autor: Matej Hojdík