Projektrapport Digitala System - Token Ring LTH Ingenjörshögskolan vid Campus Helsingborg Institutionen för datavetenskap Ferit Bölezek Nicolo Ridulfo Aisha Kujovic Ugljanin Li Zhu
Copyright Ferit Bölezek, Aisha Kujovic Ugljanin, Nicolo Ridulfo, Li Zhu LTH Ingenjörshögskolan vid Campus Helsingborg Lunds universitet Box 882 251 08 Helsingborg LTH School of Engineering Lund University Box 882 SE-251 08 Helsingborg Sweden Tryckt i Sverige Lunds universitet Lund 2019
Sammanfattning Syftet med följande rapport är att presentera en prototyp av ett arkadspel. I rapporten beskrivs förfarandet från idéjakt till slutprodukt. Huvudsyftet med projektet har varit att förstå integrationen mellan hårdvara och mjukvara. Det har resulterat i en enkel men välfungerande prototyp som kan fungera som underlag för vidareutveckling. Nyckelord: hårdvara, mjukvara, spel, programmering
Abstract The purpose of this report is to present a prototype of an arcade game. Description follows in detail the whole procedure from brainstorming to the final product. The main intention with the project has been to receive an understanding of the integration between hardware and software. It has resulted in a simple yet well-functioning prototype which can be used as material for further development. Keywords: hardware, software, game, programming
1 Inledning... 2 1.1 Bakgrund och syfte... 2 1.2 Problemformulering... 2 2 Produkt... 3 2.1 Övergripande... 3 2.2 Kravspecifikation... 3 2.2.1 Funktionella krav... 3 2.2.2 Kvalitetskrav... 3 3 Hårdvara... 5 4 Mjukvara... 6 5 Utförande... 7 5.1 Planering... 7 5.2 Konstruktion... 7 5.3 Programmering... 7 6 Resultat... 9 7 Diskussion och slutsats... 10 8 Referenser... 11 9 Bilagor... 12 9.1 Bilaga 1. Kopplingschema för Token Ring.... 12 9.2 Bilaga 2. Källkod till Token Ring.... 13 9.3 Bilaga 3. Ursprunglig kravspecifikation.... 28 1
1 Inledning 1.1 Bakgrund och syfte Projektarbetet är ett resultat av det sista momentet som ingår i kursen Digitala System. Momentet går ut på att en grupp ingenjörsstudenter använder kunskaper som erhållits under kursens gång för att bygga en enkel men välfungerande konstruktion. Konstruktionen som kommer presenteras i följande rapport är baserat på ett arkadspel. Syftet med projektarbetet är att illustrera en konstruktionsprocess. Det ämnar skapa förståelse för integrationen mellan hård- och mjukvara, uppbyggnad av en helhet med dess olika komponenter, användning av informations- och datablad för kopplingar, ritning och användning av kretsschema för konstruktionen, lödning och användning av programmeringsspråket C. Målet är att skapa en användarvänlig prototyp av ett spel. Syftet med rapporten är att presentera prototypens uppbyggnad som ett underlag inför en eventuell vidareutveckling. 1.2 Problemformulering Projektet ifråga valdes från början som en variant av spelet fyra i rad. Utmed projektets gång stöttes det på problem med en del komponenter som inte gick att kommunicera med och fick anpassa problemformuleringen efter existerande och fungerande komponenter. Spelet har således gått från fyra i rad till ett arkadspel. Valet av projektet möjliggjorde utveckling av en funktionell konstruktion. Till konstruktionen adderades även flera enkla komponenter vilket ger spelet extra funktioner. Konstruktionen programmerades i språket C i utvecklingsmiljön Atmel Studio. För kommunikationen mellan mjukvaran och processorn samt felsökning användes Atmel ICE debugger. 2
2 Produkt 2.1 Övergripande Syftet med spelet är att utmana spelarens reaktions- och precisionsförmåga. Spelaren ska försöka samla så många poäng som möjligt. Det uppnås när token stannar på samma ställe i varje efterföljande varv. Vid miss nollställs poängresultatet. Högsta uppnådda poäng står med på en rad på LCD-displayen för jämförelse. 2.2 Kravspecifikation Den ursprungliga kravspecifikationen togs fram för spelet fyra i rad vilken kan återfinnas i bilaga 3. Denna fick ändras och anpassas under arbetets gång. Planen var att skapa en 4x5-matris med neopixlar som skulle agera speldisplay, men eftersom en del neopixlar inte fungerade i matrisen byttes matrisen ut mot en ring. Nedan följer den reviderade kravspecifikationen: 2.2.1 Funktionella krav 1. Samtliga följande komponenter skall vara välfungerande: neopixelring, LCD display, knappar och potentiometer. 2. Neopixlarna ska tändas och släckas en i taget. Upplyst neopixel kallas token. 3. Token i neopixelringen ska stanna på kommando via knapptryck 4. Display ska visa två rader med poängställningen: första raden för nuvarande poängställning (Points) och andra raden för hittills högsta uppnådda poäng (Hi score). 5. Reset ska återställa poängställningen till noll och neopixlarna ska återgå till att tändas och släckas i ringen. 6. Potentiometer ska justera ljusstyrkan på displayen 2.2.2 Kvalitetskrav 1. Samtliga komponenter ska vara synkroniserade med varandra. 2. Neopixlarna ska tändas och släckas med samma hastighet sinsemellan. Endast en neopixel får vara upplyst. 3. Neopixeln ska vara upplyst omedelbart vid knapptryck. Ingen annan neopixel får lysa samtidigt. 4. Om neopixeln lyser upp på samma ställe vid varje efterföljande varv ska detta detekteras och registreras som ett lyckat drag. 5. LCD-displayen uppvisar det lyckade draget som ökat antal poäng. 3
6. Vid ej lyckat drag, dvs neopixeln lyser upp på ett annat ställe än vid förra varvet, nollställs poängställningen och spelet börjar om. 7. Svårighetsgrad innebär hastigheten med vilken neopixlarna tänds och släcks i ringen, dvs med vilken token färdas. 4
3 Hårdvara ATMEGA1284 Processorn som är kärnan i konstruktionen. Atmegan består av 40 pinnar varav ingångarna 24-27 är reserverade för JTAG. Processorn omklockades så att den körs med en inre klocka på 8 MHz [1]. Sharp Dot Matrix LCD-display Som display används LCD-displayen av typen GDM1602K. Varje tecken består av 5x8 pixlar och två rader med plats för 16 tecken per rad [2]. Displayen används som ett verktyg för kommunikationen mellan konstruktionen och spelaren. Spelaren får information om nuvarande poängställning samt högsta uppnådda poängen hittills.- Neopixlar Som display för spelet används en ring av 16 neopixlar av typen SK6812. Varje neopixel består av ett LED-chip med en egen inbyggd krets som möjliggör färgkombinationer i 256 olika färgskalor [3]. Knappar Två knappar används. Den ena fungerar som kommando för ljusets avstannande. Den andra används för återställning av spelet till ursprungsformatet (reset). Detektering av knapptryck löses med avbrott. Knappen som styr reset använder pull up, resterande knappar använder pull down. ATMEL ICE Debugger och JTAG Används för kommunikationen mellan mjukvaran och processorn. Potentiometer Används för att reglera ljusstyrkan hos LCD-displayen. 5
4 Mjukvara Programmering utfördes i språket C [4]. Koden innehåller följande funktioner: Vid knapptryck: Tända och släcka neopixel. Neopixeln lyser medan resterande neopixlar är släckta. Vid tryck på resetknappen raderas nuvarande poängställning och ser till att neopixlarna återgår till att en i taget tändas och släckas. LCD-display: Uppvisar poängställning. Färgsättning: Färgen skiftar beroende på svårighetsgrad. Övrigt: Hi score sparas i EEPROM vilket gör att den finns kvar på displayen oberoende av strömtillförsel. Svårighetsgraden ökar vart 5:e poäng. Fler detaljer kring programmeringen återfinns i bilaga 2. 6
5 Utförande 5.1 Planering I projektets början hölls diskussioner kring vad som skulle byggas. Bland de projekt som föreslogs, med inspiration från tidigare års projektarbeten, genom diskussioner med handledaren och genom en omröstning kom författarna överens om att ett spel skulle konstrueras. Författarna delade upp arbetsuppgifterna sinsemellan. Ett kopplingsschema ritades och visades för handledaren. Kopplingsschemat återfinns i bilaga 1. 5.2 Konstruktion När kopplingsschemat godkändes av handledaren kunde författarna begära komponenterna och påbörja konstruktionen. Komponenterna löddes fast på mönsterkortet. Under byggets gång upptäcktes fel i komponenter som fick tas bort och bytas ut. Konstruktionen ändrades efterhand som nya idéer uppkom. Andra felaktigheter kunde upptäckas med hjälp av logikpenna, multimeter och debugger-funktionen i Atmel Studio. Även handledaren bistod med feedback. Nedan kan ses bild på konstruktionen: Bild 1. Bild på konstruktionen underifrån. 5.3 Programmering När komponenterna visade på funktionalitet kunde programmeringen påbörjas. Utvecklingen av koden skedde i Atmel Studio i språket C. Kod i Assembler skrevs också för exakt timing på delays som neopixlarna krävde. Överföringen 7
av mjukvaran till processorn skedde via JTAG. Därmed kunde mjukvaran utvecklas efterhand som den testades. Koden återfinns i bilaga 2. 8
6 Resultat Projektet resulterade i en fungerande prototyp som uppfyller den reviderade kravspecifikationen enligt bilaga 3, se bild nedan: Bild 2. Bild på konstruktionen ovanifrån. 9
7 Diskussion och slutsats Prototypen som presenterats tidigare i rapporten är ett resultat av nyförvärvade kunskaper inom digitalteknik, ellära och programmering i språket C och Assembler. Prototypen avviker något från ursprungsidén vilket har lett till en del ändringar. Den största förändringen från ursprungsidén är displayen för spelet där en ring med 16 neopixlar har ersatt 4x5-matrisen. Anledningen är att en del neopixlar i matrisen inte tycktes fungera som önskat. Det försvårade ytterligare utveckling av spelet fyra i rad eftersom det innebar att displayen för spelet inte kunde leverera önskat resultat. Den reviderade prototypen med 16 neopixlar i ring har även medfört att spelreglerna fick ändras. Typen av spel ändrades från strategiskt till arkad. Exempelvis gick spelreglerna från two player mode till one player mode med ett highest score som jämförelse. De nya spelreglerna innebar även att flera knappar fick plockas bort samt att LCD-displayen fick en ny funktion. LCD-displayen skulle nu enbart visa poängställning. Den största utmaningen har varit att försöka få matrisen att fungera. Efter flera omgångar av felsökning och efter diskussion med handledaren kunde utvecklarna inte hitta orsaken till problemet varför projektet fick ta en ny riktning. När displayen för spelet byttes ut kunde projektet fortgå med programmering, testning och debugging. Projektarbetet har varit en lärorik upplevelse vilket har bidragit med många nyttiga erfarenheter. För en del individer i gruppen har erfarenheten av att konstruera och programmera en prototyp från början varit en helt ny. Således är prototypen en imponerande albeit enkel konstruktion som kan fungera som underlag för en vidareutveckling. 10
8 Referenser Datablad [1] Atmel, ATmega1284, 42718C datablad, oktober 2016. https://www.eit.lth.se/fileadmin/eit/courses/edi021/datablad/process ors/atmega1284.pdf?fbclid=iwar1jsimfgfsb5xiosouvmkldgr yc5ne0hbez1i589b7ir_c0uvvvg5rszom [2] Xiamen Ocular, Specifications of LCD Module GDM1602K, datablad, december 2001. https://www.eit.lth.se/fileadmin/eit/courses/edi021/datablad/display/ LCD.pdf?fbclid=IwAR3IMHJ4EToPq1Cba8hBD3xy3kgNubRh9b- X0b655JL1Wb_PS_mzbXEaeQs [3] Adafruit, LED color SK6812, hämtad maj 2019. https://cdn-shop.adafruit.com/productfiles/1138/sk6812+led+datasheet+.pdf Manualer [4] Lunds universitet, Programmera i C, hämtad maj 2019. https://www.eit.lth.se/fileadmin/eit/courses/eita15/laborationer/dato rteknik/lab1/programmera_i_c.pdf 11
9 Bilagor 9.1 Bilaga 1. Kopplingschema för Token Ring. 12
9.2 Bilaga 2. Källkod till Token Ring. GAME STRINGS /** This file will only consist of hardcoded strings. */ #ifndef GAMESTRINGS_H_ #define GAMESTRINGS_H_ // if \n calculate for new line, if \CONSTANT add padding. {xx is the time. extern const char START_TEXT[29] = "4 in a row\n Press 1 to start"; extern const char PLAYER_ONE_SETUP[23] = "Player 1\n Choose color"; extern const char PLAYER_TWO_SETUP[23] = "Player 2\n Choose color"; extern const char P1_CHOICE[] = "P1 Choose column \n {xx"; extern const char P2_CHOICE[] = "P2 Choose column \n {xx"; extern const char P1_WON[] = "Player 1 WON! \n {xx"; extern const char P2_WON[] = "Player 2 WON! \n {xx"; extern const char GAME_TIE[] = "TIE! \n {xx"; extern const char GAME_PAUSE[] = "Game Paused \n Press 1 to start"; extern const int npixels = 16; #endif /* GAMESTRINGS_H_ */ 13
DISPLAY // Comments for the subroutines can be found in their.c file. extern void SendCommand(unsigned char cmd); extern void SendChar(unsigned char data); extern void SendString(unsigned char data[], int size); extern void ClearDisplay(); void SkipToNextRow(int size, int atindex); void SendError(); void DisplayRoundTimer(char data[], int atindex, int size); // Don't know the size of array, so we pass it around instead. uint8_t ExtractEscapeCommand(char data[], int atindex, int size); extern void init_led(); void EnableTimerInterrupt(); extern void EditLCDText(uint8_t index, char newdata[], int size); #endif /* DISPLAY_H_ */ #define F_CPU 16000000 #define REGISTER_SELECT 7 #define READ_WRITE_SELECT 6 #define ENABLE_SELECT 1 #define SECOND_ROW_FIRST_COLUMN_LCD_ADDRESS 40 #define INCREASE_ARRAY_BY_GAME_TIME_BITS 2 #define DDRAM_ADDRESS_COMMAND 7 #define LCD_SECOND_ROW_FIRST_ADDRESS 40 // DDRD == Data port // DDRC == Control port #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> uint8_t newindex = 0; int timercountactive = 0; // If it is active, the string currently shown should be updated. 14
int interruptsenabled = 0; int timerindex = 0; // The index of where the last timer was shown int timertextsize = 0; // Size of the text that the timer is shown in int timercount = 60; int linebreakindex = 0; // index where linebreak occurs char timercountstr[2]; /** Compare Match Timer interrupt used to get precise timings. This is used to send updates to the LCD at precisely at 1 update per second. */ ISR(TIMER1_COMPA_vect) { timercount--; if (timercount!= 0) { sprintf(timercountstr, "%d", timercount); EditLCDText(timerIndex,timerCountStr, 2); if (timercount == 10) { ClearDisplay(); SendString("Yay!",4); /** Send command to the display (RS = 0) */ void SendCommand(char cmd) { PORTB = cmd; PORTC &= ~(1<<REGISTER_SELECT); PORTC &= ~(1<<READ_WRITE_SELECT); PORTC = (1<<ENABLE_SELECT); _delay_us(1); PORTC &= ~(1<<ENABLE_SELECT); _delay_ms(1); /** Send data to the display (RS = 1) */ void SendChar(unsigned char data) { PORTB = data; PORTC = 1<<REGISTER_SELECT; PORTC &= ~(1<<READ_WRITE_SELECT); 15
PORTC = (1<<ENABLE_SELECT); _delay_us(1); PORTC &= ~(1<<ENABLE_SELECT); _delay_us(50); // Hops to row 2. void SkipToNextRow(int size, int atindex) { SendCommand((1<<DDRAM_ADDRESS_COMMAND) + LCD_SECOND_ROW_FIRST_ADDRESS); /** Displays the time for a round. */ void DisplayRoundTimer(char data[], int atindex, int size) { timerindex = atindex; timertextsize = size; data[atindex] = '6'; data[atindex + 1] = '0'; timercountactive = 1; /** Clears all data from the LCD DDRAM. */ ClearDisplay() { SendCommand(0b00000001); // TODO: Setup macros for binary commands. /** Extracts the escape command and performs actions based on what command was sent in. Returns the next index of char to read once the action is done. */ uint8_t ExtractEscapeCommand(char data[], int atindex, int size) { if (data[atindex + 1] == 'n') { SkipToNextRow(size,atIndex); linebreakindex = atindex; return atindex = atindex + 2; else if (data[atindex + 1] == 't') { DisplayRoundTimer(data,atIndex,size); return atindex - 1; // The escape character has been replaced by timer, write out new chars. 16
else { // if it isn't a letter then it's likely a number indicating the padding, NOTE: Padding should be a integer value between 0-9 if (isdigit(data[atindex + 1])) { // TODO: Program logic of padding. else { return atindex; /** Sends a string to the LCD and displays it. */ void SendString(unsigned char data[], int size) { if (timercountactive) { TCCR1B &= ~(1 << CS12); timercountactive = 0; interruptsenabled = 0; for (int i = 0; i < size;i++) { if (data[i] == 'Q') { newindex = ExtractEscapeCommand(data,i,size); i = newindex; continue; PORTB = data[i]; PORTC = 1<<REGISTER_SELECT; PORTC &= ~(1<<READ_WRITE_SELECT); PORTC = (1<<ENABLE_SELECT); _delay_us(1); PORTC &= ~(1<<ENABLE_SELECT); _delay_ms(1); if (timercountactive &&!interruptsenabled) { EnableTimerInterrupt(); 17
/** Changes a portion of the text displayed on the LCD at the specified index. */ void EditLCDText(uint8_t index, char newdata[], int size) { // Second row is from 40 to 67 if (index > 16) { SendCommand((1<<DDRAM_ADDRESS_COMMAND) + LCD_SECOND_ROW_FIRST_ADDRESS + (timerindex - linebreakindex) - 3); // -3 offset to skip escape chars. else { // First row... for (int i = 0; i < size; i++) { SendChar(newData[i]); /** Enables timer interrupts. */ EnableTimerInterrupt() { sei(); TCCR1B = 1 << WGM12; // CTC select TCCR1B = 1 << CS12; // prescaler 256 TIMSK1 = 1 << OCIE1A; // the Timer/Counter1 Output Compare A Match interrupt is enabled OCR1A = (F_CPU/(1*2*256) - 1); // 0.48 is the time for calculation, calculated to achieve appx 1 interrupt per second. 16_000_000/(0.48*2*256) equal appx = 65_000/ 2^16 (16 bit timer) interruptsenabled = 1; /** Initialize the display */ void LCD_init(void) { DDRC = 0b11000010; DDRB = 0xFF; SendCommand(0b00111110); // Function set / 8 bit SendCommand(0b00001110); // Display on SendCommand(0b00000010); // Entry Mode // auto increase //SendCommand(0b00000010); // return home SendCommand(0b00000001); // Clear display 18
NEOPIXLAR #ifndef NEOPIXEL_H_ #define NEOPIXEL_H_ void AddPixel(int x, int r, int g, int b); void clearpixels(); #endif /* NEOPIXEL_H_ */ extern void Reset(); extern void Send_One(); extern void Send_Zero(); #include "../../Constants/GameStrings.h" #include <math.h> /** Struct to define the RGB values for a pixel. Used to enumerate through an array of pixels. */ typedef struct { int r; int g; int b; pixel_t; pixel_t leds[16]; /** Resets all pixels to their default values. */ void clearpixels() { for (int i = 0; i < 20; i++) { leds[i].r = 0; leds[i].g = 0; leds[i].b = 0; for (int j = 0; j < 24 * 16; j++) { Send_Zero(); 19
Reset(); int bitcompare; /** Sets the specified pixel to the specified RGB values. */ void setpixel(int x, int r, int g, int b) { leds[x].r = r; leds[x].g = g; leds[x].b = b; for (int i = 0; i < 16; i++) { //Green int n = leds[i].g; int d = 0b10000000; for(int j = 0;j<8;j++){ bitcompare = (n&d); if(bitcompare>0){ Send_One(); else{ Send_Zero(); d=d>>1; //Red n = leds[i].r; d = 0b10000000; for(int j = 0;j<8;j++){ bitcompare = (n&d); if(bitcompare>0){ Send_One(); else{ Send_Zero(); d=d>>1; 20
//BLUE n = leds[i].b; d = 0b10000000; for(int j = 0;j<8;j++){ bitcompare = (n&d); if(bitcompare>0){ Send_One(); else{ Send_Zero(); d=d>>1; Reset(); 21
GAME MANAGER #ifndef GAMEMANAGER_H_ #define GAMEMANAGER_H_ extern uint8_t difficulty; void InitializeGame(); void ResetGame(); void UpdatePoints(); void EndGame(); void difficultydelay(); void updatepixel(); uint8_t diffrvalue(); uint8_t diffgvalue(); #endif /* GAMEMANAGER_H_ */ #define F_CPU 8000000UL #include <avr/io.h> #include <stdint-gcc.h> #include <util/delay.h> #include "Components/Display/Display.h" uint32_t points = 0; uint32_t highscore = 0; difficulty = 1; /** Initializes game and expects that display is already initialized. Make sure to initialize display before game initialization. */ void InitializeGame() { points = 0; highscore = ReadFromEEPROM(0xFF); updatetext(); /** Write data to permanent memory. */ void WriteToEEPROM(unsigned int uiaddress, unsigned char ucdata) { while(eecr & (1<<EEPE)); EEAR = uiaddress; 22
EEDR = ucdata; EECR = (1<<EEMPE); EECR = (1<<EEPE); /** Read data from permanent memory. */ void ReadFromEEPROM(unsigned int uiaddress) { while(eecr & (1<<EEPE)); EEAR = uiaddress; EECR = (1<<EERE); return EEDR; /** Will reset the game back to it's initial state. */ void ResetGame() { points = 0; difficulty = 1; updatetext(); /** Updates the points on the LCD display. Will increment points. */ void UpdatePoints() { points = (points + 1); if(points>highscore){ highscore = points; WriteToEEPROM(0xFF,highscore); if(points%5==0 && difficulty!= 7){difficulty++; updatetext(); /** Internal function that will do the updating of the text. */ void updatetext(){ ClearDisplay(); char txt09[1+9]=""; 23
char txt10[2+9]=""; if (points<10){ sprintf(txt09,"score: %dqn",points); SendString(txt09,1+9); else{ sprintf(txt10,"score: %dqn",points); SendString(txt10,2+9); txt09[1+11]=""; txt10[2+11]=""; if (highscore<10){ sprintf(txt09,"hi-score: %d",highscore); SendString(txt09,1+10); else{ sprintf(txt10,"hi-score: %d",highscore); SendString(txt10,2+10); /** Delay that is based on difficulty. Meaning that the use of this during a neopixel animation will make the neopixels spin faster the harder it gets! */ void difficultydelay(){ for (int i = 8;i>difficulty;i--) { _delay_ms(40); /** R portion of the RGB value based on difficulty. */ uint8_t diffrvalue(){ uint8_t r = 0; for (int i = 0; i < difficulty; i++) { if ((r == 100)) return r; 24
r += 10; return r; /** G portion of the RGB value based on difficulty. */ uint8_t diffgvalue(){ uint8_t g = 80; for (int i = 0; i < difficulty; i++) { if ((g == 0)) return g; g -= 10; return g; /** Ends the game. Updates High score and resets the game. */ void EndGame() { if(points>highscore){ highscore = points; ResetGame(); 25
MAIN int pressed = 0; int currentlight = 0; ISR(PCINT0_vect) { clearpixels(); setpixel(currentlight,10,10,10); if (PINA & (1 << PINA0)) { if (pressed) { pressed = 0; else { pressed = 1; if(currentlight == 14) { UpdatePoints(); else { EndGame(); PCIFR = PCIF0; // Do NOT remove! Will make sure we trigger button interrupt only once! int main(void) { cli(); LCD_init(); InitializeGame(); PCICR = 1 << PCIE0; sei(); PCMSK0 = 1 << PCINT0; // NEOPIXELS AFTER THIS 26
DDRD = 1 << PORTD7; PORTD &= ~(1 << PORTD7); while (1) { for(int i = currentlight; i<16 ;i++){ if (pressed) break; currentlight = i; setpixel(i, diffrvalue(),diffgvalue(),0); difficultydelay(); setpixel(i,0,0,0); if (pressed) setpixel(i,10,10,10); // Fixes the case where the pixel is set to zero if a button press is faster than the diffdelay. if (!pressed) currentlight = 0; 27
9.3 Bilaga 3. Ursprunglig kravspecifikation. Kravspecifikation Fyra i rad: Övergripande Bygga en krets med ATMEGA så att det går att programmera den med JTAG Bygga en matris bestående av neopixlar Kunna kontrollera matrisen med ATMEGA Lägga till knappar Kontrollera knappar Koda spelet och kunna interagera med den med hårdvaran (knappar och matris) Two player mode Funktioner Framför varje kolumn ska det finnas en knapp Knapptryck motsvarar en markör som faller ner i kolumnen och lägger sig i första lediga plats så långt ner som möjligt En markör motsvarar en lysande neopixel, medan släckta neopixlar ger en ledig plats i matrisen Fyra lysande neopixlar i rad ska ge vinst Displayen visar vinst eller oavgjort Matris 4x5 = 20 neopixlar LED-matris uppbyggd av separata neopixlar Knappar En knapp framför varje kolumn (4 knappar) Reset Display Visar vems drag det är Tid per drag Visar vinst eller oavgjort 28
29