Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pm:prj2025:fstancu:david_ionut.luca [2025/05/25 23:39]
david_ionut.luca
pm:prj2025:fstancu:david_ionut.luca [2025/05/28 09:53] (current)
david_ionut.luca
Line 1: Line 1:
 ====== Snake Game - Luca David-Ionut 334CD ====== ====== Snake Game - Luca David-Ionut 334CD ======
 +
 +**__MILESTONE 1 - TEMA PROIECT__**
  
 Acest proiect constă într-o implementare a jocului Snake clasic pe platforma Arduino Uno R3.  O caracteristică importantă este controlul vitezei șarpelui prin intermediul unui potențiometru,​ iar scorul obținut de jucător este vizualizat pe un display LCD. Acest proiect constă într-o implementare a jocului Snake clasic pe platforma Arduino Uno R3.  O caracteristică importantă este controlul vitezei șarpelui prin intermediul unui potențiometru,​ iar scorul obținut de jucător este vizualizat pe un display LCD.
  
-Milestone ​(HARDWARE):+**__MILESTONE ​- HARDWARE__**
  
 Componente: LCD 12864 Albastru ST7920, Modul potentiometru rotativ RV09, 4 x Buton Mini 6x6x5 4 pini. Componente: LCD 12864 Albastru ST7920, Modul potentiometru rotativ RV09, 4 x Buton Mini 6x6x5 4 pini.
Line 26: Line 28:
  
 Pinii LCD-ului ST7920 128x64 sunt conectati de placa Arduino in felul urmator: Pinii LCD-ului ST7920 128x64 sunt conectati de placa Arduino in felul urmator:
-  * GND -> GND (Pinul de masă al LCD-ului este conectat la masa Arduino. ​Acesta este punctul de referință pentru toate tensiunile din circuit.) +  * GND -> GND (Acesta este punctul de referință pentru toate tensiunile din circuit) 
- +  * Vcc -> 5V (Pinul de alimentare al LCD-ului este conectat la 5V de pe Arduino) 
-  * Vcc -> 5V (Pinul de alimentare al LCD-ului este conectat la 5V de pe Arduino.) +  * RS -> PIN 10 (Activează/​dezactivează comunicarea SPI cu display-ulCând e LOW, display-ul e gata să primească date
- +  * R/W -> PIN 11 (Pinul ​prin care microcontrolerul trimite date seriale către display
-  * RS -> PIN 10 (Pinul "​Register Select"​ (RS) al LCD-ului este conectat la pinul 10 al ArduinoAcest pin este folosit pentru a diferenția între datele de comandă și datele de afișat trimise către LCD.) +  * E -> PIN 13 (Generează semnalul de ceas pentru a sincroniza transferul de date seriale
- +  * PSB -> GND (Setează display-ul în modul de comunicare serial ​(SPI), fiind conectat la masă) 
-  * R/W -> PIN 11 (Pinul ​"​Read/​Write"​ (R/W) al LCD-ului este conectat la pinul 11 al Arduino. Acest pin controlează direcția de transfer al datelor.+  * RST -> PIN 8 (Resetează controlerul display-ului la o stare inițială, ștergând conținutul și reconfigurând setările
- +  * BLA -> 3.3V (Acesta este pinul pozitiv de alimentare pentru iluminarea de fundal a LCD-ului) 
-   * E -> PIN 13 (Pinul "​Enable"​ (E) al LCD-ului este conectat la pinul 13 al Arduino. Acest pin este folosit ​pentru a sincroniza transferul de date între Arduino și LCD.+  * BLK -> GND (Acesta este pinul negativ de alimentare pentru iluminarea de fundal a LCD-ului)
- +
-   * PSB -> GND (Pinul "​Parallel/​Serial Select" ​(PSBal LCD-ului este conectat la GND. Acest pin selectează modul de comunicare cu LCD-ul.+
- +
-   * RST -> PIN 8 (Pinul "​Reset"​ (RST) al LCD-ului este conectat ​la pinul 8 al Arduino. Acest pin este folosit pentru a reseta LCD-ul, aducându-l într-o stare inițială ​cunoscută.+
- +
-   * BLA -> 3.3V (Pinul "​Backlight Anode" (BLA) al LCD-ului este conectat la 3.3V. Acesta este pinul pozitiv de alimentare pentru iluminarea de fundal a LCD-ului.+
- +
- +
-   * BLK -> GND (Pinul "​Backlight Cathode"​ (BLK) al LCD-ului este conectat la GND. Acesta este pinul negativ de alimentare pentru iluminarea de fundal a LCD-ului.)+
  
 LCD-ul ST7920 comunică cu placa de dezvoltare Arduino prin protocolul SPI. LCD-ul ST7920 comunică cu placa de dezvoltare Arduino prin protocolul SPI.
Line 71: Line 64:
  
  
-MILESTONE ​3 - SOFTWARE+**__MILESTONE ​3 - SOFTWARE__**
  
 **Stadiul proiectului** **Stadiul proiectului**
  
-Proiect finalizat cu toate functiile precizate de mai sus completeSarpele ​poate fi controlat, ​crestese poate schimba ​viteza ​si de asemenea se tine cont de scorul curent ​si recordul ​din sesiunea curenta din joc.+Proiectul este complet, ​toate funcțiile menționate fiind implementateȘarpele ​poate fi controlat, ​crește în lungime, viteza ​este ajustabilă,​ iar scorul curent ​și recordul ​sesiunii de joc sunt înregistrate.
  
 **Biblioteci** **Biblioteci**
  
-Biblioteca pe care am folosit-o este u8g2. Aceasta faciliteaza ​accesul la functiile ​LCD-ului, ​se poate afisa text usor, forme etc.+Am utilizat biblioteca u8g2, care a simplificat semnificativ ​accesul la funcțiile ​LCD-ului, ​permițând afișarea facilă de text și forme grafice.
  
 **Elemente originale** **Elemente originale**
  
-Elementul de unicitate ​este schimbarea ​vitezei, nemaintalnind aceasta functionalitate cand am jucat diverite ​versiuni ale jocului ​"Snake".+Un aspect distinctiv al proiectului nostru ​este funcționalitatea de schimbare a vitezei. Aceasta nu am întâlnit-o în alte versiuni ale jocului ​'Snake' pe care le-am experimentat.
  
 **Concepte aplicate** **Concepte aplicate**
  
-Ca si concepte dobandite de la laborator am folosit intreruperi, generate de butoane cand sunt apasateRutinele de intreruperi schimba directia in care se misca sarpele. Am folosit si SPI, pentru comunicarea dintre ​placa si LCD, si de asemenea ​ADC, pentru citirea inputul-ui analog al potentiometrului.+Dintre conceptele asimilate ​la laboratoram aplicat întreruperile, generate de apăsarea butoanelor pentru a modifica direcția șarpeluiDe asemeneaam utilizat SPI pentru comunicarea dintre ​placă și LCD, iar ADC-ul pentru a citi valorile analogice de la potențiometru.
  
 **Sumarul scheletului de cod** **Sumarul scheletului de cod**
  
-In functia de loop, capul sarpelui isi actualizeaza ​coordonatele ​in functie de directia curenta. +În funcția principală a programului (loop), capul șarpelui își actualizează ​coordonatele ​conform direcției sale curenteImediat după această actualizare ​poziției, sistemul efectuează o serie de verificări cruciale. În primul rând, dacă șarpele atinge ​un punct de scor, lungimea sa este extinsă și scorul ​din joc este incrementat. Apoi, se verifică dacă player-ul s-a lovit de o parte a șarpelui ​sau de un perete, ​caz în care jocul se termină ​(GAME OVER); ​scorul ​este resetat, iar dacă acesta ​este mai mare decât ​recordul curent ​al sesiuniirecordul este actualizat. La finalul ​buclei, ​un delay controlat ​de 100ms + map(valoare_ADC) ​asigură că viteza ​șarpelui rămâne reglabilă.
-Dupa actualizarea pozitiei se verifica urmatoarele:​ +
-  -Player-ul ​atins un punct de scor -> Lungimea sarpelui va cresteiar scorul ​jocului va fi incrementat +
-  -Player-ul s-a lovit de o parte a sarpelui ​sau perete ​-> GAME OVER, se reseteaza scor-ul ​(in cazul in care scorul este mai mare decat recordul curent, ​acesta va fi actualizat+
- +
-De asemenea la finalul ​loop-ului exista ​un delay de 100ms + map(valoare_ADC) ​pentru ca viteza ​sarpelui sa poata fi reglabila.+
  
 **Un scurt demo al jocului** **Un scurt demo al jocului**
  
-<​HTML><​iframe width="​560"​ height="​315"​ src="//​www.youtube.com/​embed/​aUAevvRBBr4" frameborder="​0"​ allowfullscreen></​iframe></​HTML>​+<​HTML><​iframe width="​560"​ height="​315"​ src="//​www.youtube.com/​embed/​eJmF104xEIE" frameborder="​0"​ allowfullscreen></​iframe></​HTML>​
  
 +
 +
 +
 +**Calibrarea elementelor de senzoristica**
 +
 +Am utilizat funcția map() pentru a converti valorile citite de la ADC într-un interval de milisecunde ajustabil.
 +
 +
 +    #include <​U8g2lib.h>​
 +    #include <​avr/​io.h>​
 +    #include <​avr/​interrupt.h>​
 +    #include <​Arduino.h>​
 +    ​
 +    const int buttonRight = 2;
 +    const int buttonDown = 3;
 +    const int buttonUp = 4;
 +    const int buttonLeft = 9;
 +    const int analogPin = A0;
 +
 +    #define LEFT 1
 +    #define DOWN 2
 +    #define UP 3
 +    #define RIGHT 4
 +
 +    #define CS_PIN 10
 +    #define RST_PIN 8
 +
 +    U8G2_ST7920_128X64_F_HW_SPI u8g2(U8G2_R0,​ CS_PIN, RST_PIN);
 +
 +    volatile int direction = 0;
 +    int analogValue = 0;
 +    int delay_ms = 0;
 +
 +    int dotX;
 +    int dotY;
 +    int newHeadX;
 +    int newHeadY;
 +    const int dotRadius = 4;
 +    const int moveStep = 4;
 +    const int gameWidth = 96;
 +    ​
 +    #define MAX_SNAKE_LENGTH 50
 +    int snakeX[MAX_SNAKE_LENGTH];​
 +    int snakeY[MAX_SNAKE_LENGTH];​
 +    int snakeLength = 1;
 +
 +    int scoreX;
 +    int scoreY;
 +    int score = 0;
 +    int record = 0;
 +
 +    int manhattanDistance(int x1, int y1, int x2, int y2) {
 +        return abs(x1 - x2) + abs(y1 - y2);
 +    }
 +
 +    int isCloseToSnake(int x, int y) {
 +        for (int i = 0; i < snakeLength;​ i++) {
 +            if (manhattanDistance(x,​ y, snakeX[i], snakeY[i]) < 4) {
 +                return 1;
 +            }
 +        }
 +        return 0;
 +    }
 +
 +    void generateScorePoint() {
 +        int newX, newY;
 +        do {
 +            newX = random(dotRadius,​ gameWidth - dotRadius);
 +            newY = random(dotRadius,​ u8g2.getHeight() - dotRadius);
 +            newX -= newX % moveStep;
 +            newY -= newY % moveStep;
 +        } while (isCloseToSnake(newX,​ newY));
 +
 +        scoreX = newX;
 +        scoreY = newY;
 +    }
 +
 +    void resetGame() {
 +        dotX = gameWidth / 2 - (gameWidth / 2) % moveStep;
 +        dotY = u8g2.getHeight() / 2 - (u8g2.getHeight() / 2) % moveStep;
 +        direction = 0;
 +        score = 0;
 +        snakeLength = 1;
 +        generateScorePoint();​
 +    }
 +
 +    void showGameOver() {
 +        u8g2.clearBuffer();​
 +        u8g2.setFont(u8g2_font_ncenB14_tr);​
 +        const char* gameOverText = "GAME OVER";
 +        int textWidth = u8g2.getStrWidth(gameOverText);​
 +        int textX = (u8g2.getWidth() - textWidth) / 2;
 +        int textY = u8g2.getHeight() / 2;
 +        u8g2.drawStr(textX,​ textY, gameOverText);​
 +        u8g2.sendBuffer();​
 +        delay(3000);​
 +    ​
 +        if (score > record) {
 +            record = score;
 +            u8g2.clearBuffer();​
 +            u8g2.setFont(u8g2_font_ncenB14_tr);​
 +            const char* highScoreText = "​RECORD";​
 +            textWidth = u8g2.getStrWidth(highScoreText);​
 +            textX = (u8g2.getWidth() - textWidth) / 2;
 +            int textY = u8g2.getHeight() / 2;
 +            u8g2.drawStr(textX,​ textY, highScoreText);​
 +            u8g2.sendBuffer();​
 +            delay(3000);​
 +        }
 +    }
 +
 +    int readADC(uint8_t channel) {
 +        ADMUX = (0 << REFS1) | (1 << REFS0) | (channel & 0x07);
 +        ADCSRA |= (1 << ADSC);
 +        while (ADCSRA & (1 << ADSC));
 +        return ADCW;
 +    }
 +
 +    void SPI_MasterInit(void) {
 +        DDRB |= (1 << DDB3) | (1 << DDB5) | (1 << DDB2);
 +        SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
 +    }
 +
 +    void setup() {
 +        DDRD &= ~((1 << PIND2) | (1 << PIND3) | (1 << PIND4));
 +        PORTD |= (1 << PIND2) | (1 << PIND3) | (1 << PIND4);
 +    ​
 +        DDRB &= ~(1 << PINB1);
 +        PORTB |= (1 << PINB1);
 +        ​
 +        UBRR0H = (uint8_t)(51 & 0xF00);
 +        UBRR0L = (uint8_t)(51 & 0x0FF);
 +        UCSR0B = (1 << RXEN0) | (1 << TXEN0);
 +        UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
 +
 +        ADMUX = (0 << REFS1) | (1 << REFS0) | (0 << ADLAR);
 +        ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
 +
 +        randomSeed(readADC(1));​
 +
 +        EICRA |= (1 << ISC11) | (1 << ISC01);
 +        EIMSK |= (1 << INT1) | (1 << INT0);
 +    ​
 +        PCICR |= (1 << PCIE2) | (1 << PCIE0);
 +        PCMSK0 |= (1 << PCINT1);
 +        PCMSK2 |= (1 << PCINT20);
 +    ​
 +        sei();
 +        Serial.begin(9600);​
 +
 +        SPI_MasterInit(); ​
 +
 +        u8g2.begin();​
 +
 +        resetGame();​
 +    }
 +
 +    ISR(INT0_vect) {
 +        if (direction != LEFT)
 +            direction = RIGHT;
 +        delayMicroseconds(20000);​
 +    }
 +
 +    ISR(INT1_vect) {
 +        if (direction != UP)
 +            direction = DOWN;
 +        delayMicroseconds(20000);​
 +    }
 +
 +    ISR(PCINT2_vect) {
 +        if (!((PIND >> PIND4) & 1)) {
 +            if (direction != DOWN)
 +                direction = UP;
 +        }
 +        delayMicroseconds(20000);​
 +    }
 +
 +    ISR(PCINT0_vect) {
 +        if (!((PINB >> PINB1) & 1)) {
 +            if (direction != RIGHT)
 +                direction = LEFT;
 +        }
 +        delayMicroseconds(20000);​
 +    }
 +
 +    void loop() {
 +        u8g2.clearBuffer();​
 +    ​
 +        analogValue = readADC(0);
 +        delay_ms = map(analogValue,​ 0, 1023, 0, 200);
 +    ​
 +        switch (direction) {
 +            case LEFT:
 +                dotX -= moveStep;
 +                break;
 +            case RIGHT:
 +                dotX += moveStep;
 +                break;
 +            case UP:
 +                dotY -= moveStep;
 +                break;
 +            case DOWN:
 +                dotY += moveStep;
 +                break;
 +        }
 +
 +        if (dotX < 0 || dotX >= gameWidth || dotY < 0 || dotY >= u8g2.getHeight()) {
 +            showGameOver();​
 +            resetGame();​
 +        }
 +
 +        newHeadX = dotX;
 +        newHeadY = dotY;
 +    ​
 +        if (dotX == scoreX && dotY == scoreY) {
 +            score++;
 +            snakeLength++;​
 +            if (snakeLength > MAX_SNAKE_LENGTH) {
 +                snakeLength = MAX_SNAKE_LENGTH;​
 +            }
 +            generateScorePoint();​
 +        }
 +
 +        for (int i = 1; i < snakeLength;​ i++) {
 +            if (snakeX[i] == dotX && snakeY[i] == dotY) {
 +                showGameOver();​
 +                resetGame();​
 +            }
 +        }
 +    ​
 +        for (int i = snakeLength - 1; i > 0; i--) {
 +            snakeX[i] = snakeX[i - 1];
 +            snakeY[i] = snakeY[i - 1];
 +        }
 +        snakeX[0] = newHeadX;
 +        snakeY[0] = newHeadY;
 +
 +        u8g2.setDrawColor(1);​
 +        for (int i = 0; i < snakeLength;​ i++) {
 +            u8g2.drawBox(snakeX[i],​ snakeY[i], dotRadius, dotRadius);
 +        }
 +        u8g2.drawBox(scoreX,​ scoreY, dotRadius, dotRadius);
 +        u8g2.drawVLine(gameWidth,​ 0, u8g2.getHeight());​
 +        u8g2.setFont(u8g2_font_6x10_tf);​
 +        char scoreTextDisplay[10];​
 +        sprintf(scoreTextDisplay,​ "​Scor"​);​
 +        u8g2.drawStr(gameWidth + (u8g2.getWidth() - gameWidth - u8g2.getStrWidth(scoreTextDisplay)) / 2, 10, scoreTextDisplay);​
 +        u8g2.setFont(u8g2_font_7x13_tf);​
 +        char scoreString[5];​
 +        sprintf(scoreString,​ "​%d",​ score);
 +        u8g2.drawStr(gameWidth + (u8g2.getWidth() - gameWidth - u8g2.getStrWidth(scoreString)) / 2, 25, scoreString);​
 +        u8g2.drawHLine(gameWidth,​ u8g2.getHeight() / 2, u8g2.getWidth() - gameWidth);
 +        u8g2.setFont(u8g2_font_6x10_tf);​
 +        char recordText[10];​
 +        sprintf(recordText,​ "​Best"​);​
 +        u8g2.drawStr(gameWidth + (u8g2.getWidth() - gameWidth - u8g2.getStrWidth(recordText)) / 2, u8g2.getHeight() / 2 + 11, recordText);​
 +        u8g2.setFont(u8g2_font_7x13_tf);​
 +        char recordString[5];​
 +        sprintf(recordString,​ "​%d",​ record);
 +        u8g2.drawStr(gameWidth + (u8g2.getWidth() - gameWidth - u8g2.getStrWidth(recordString)) / 2, u8g2.getHeight() / 2 + 26, recordString);​
 +    ​
 +        u8g2.sendBuffer();​
 +        Serial.println(delay_ms);​
 +    ​
 +        delay(100 + delay_ms);
 +    }
  
  
  
pm/prj2025/fstancu/david_ionut.luca.1748205564.txt.gz · Last modified: 2025/05/25 23:39 by david_ionut.luca
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0