This shows you the differences between two versions of the page.
|
pm:prj2026:alexandru.jipa2803:valentina.ceban [2026/05/12 11:03] valentina.ceban |
pm:prj2026:alexandru.jipa2803:valentina.ceban [2026/05/24 20:26] (current) valentina.ceban [Concluzii] |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Simon Says - Joc de memorie cu LEDs și LCD ====== | + | ====== Simon Says - Joc de memorie cu LEDs, LCD si Buzzer ====== |
| ===== Introducere ===== | ===== Introducere ===== | ||
| - | Proiectul reprezinta un joc de memorie de tip Simon Says implementat pe placa ATmega328P Xplained Mini. Jocul afiseaza o secventa de lumini prin 4 LED-uri colorate (rosu, verde, albastru, galben), iar jucatorul trebuie sa reproduca secventa apasand butoanele corespunzatoare. La fiecare runda reusita, secventa creste cu un pas, iar viteza de afisare creste progresiv, facand jocul din ce in ce mai dificil. | + | Proiectul reprezinta o implementare extinsa a jocului clasic **Simon Says** pe placa **ATmega328P Xplained Mini**. |
| + | Jocul afiseaza secvente de lumini prin 4 LED-uri colorate, iar jucatorul trebuie sa reproduca | ||
| + | secventa in ordinea corecta, apasand butoanele corespunzatoare. Cu fiecare nivel reusit, secventa | ||
| + | creste in lungime, iar viteza de afisare creste progresiv. | ||
| - | Un ecran LCD 16x2 afiseaza scorul curent, mesaje de stare si cel mai bun scor (pastrat in EEPROM intre resetari). Un buzzer pasiv ofera feedback sonor, cu note diferite pentru fiecare culoare si un sunet de eroare la greseala. | + | Proiectul integreaza: |
| + | * **LCD 16x2 cu modul I2C** pentru afisarea scorului, nivelului, countdown-ului si mesajelor de stare | ||
| + | * **Animatii LCD** cu 8 caractere custom (inima, skull, trofeu, note muzicale, progress bar) | ||
| + | * **Selectare dificultate** la start — Easy / Medium / Hard, cu viteze si timpi diferiti | ||
| + | * **Dificultate adaptiva bidiectionala** — viteza creste la succese si scade la greseli | ||
| + | * **Countdown vizibil** pe LCD in timpul in care jucatorul replica secventa | ||
| + | * **Feedback sonor** prin buzzer activ, cu note diferite per culoare | ||
| + | * **Melodie "We Are the Champions"** la baterea recordului, cu LED-uri sincronizate | ||
| + | * **Celebrare record in timp real** — fara sa intrerupa jocul | ||
| + | * **EEPROM persistent** pentru salvarea celui mai bun scor intre resetari | ||
| - | ===== Descriere generala ===== | + | **Schema bloc:** |
| - | Jocul functioneaza dupa urmatoarea logica: | + | {{pm:prj2026:alexandru.jipa2803:schema_electrica_valentinaceban.png?300}} |
| - | - LCD afiseaza "Pregateste-te..." si un countdown | ||
| - | - Microcontrolerul genereaza un numar random (1-4) si il adauga la secventa | ||
| - | - LED-urile se aprind pe rand in ordinea secventei, cu pauze intre ele | ||
| - | - Jucatorul apasa butoanele in aceeasi ordine | ||
| - | - Daca inputul este corect → nivel urmator (secventa mai lunga, viteza mai mare) | ||
| - | - Daca inputul este gresit → buzzer suna, scorul e afisat, jocul se reseteaza | ||
| - | **Schema bloc:** | + | ===== Descriere Generala ===== |
| + | |||
| + | ==== Logica jocului ==== | ||
| + | |||
| + | - La pornire, LCD afiseaza animatie de intro cu scroll si cel mai bun scor din EEPROM | ||
| + | - LED-urile executa o animatie de bun-venit (wave in ambele directii) | ||
| + | - Jucatorul selecteaza dificultatea: **Easy** (B1), **Medium** (B2), **Hard** (B3) | ||
| + | - La apasarea oricarui buton, jocul incepe | ||
| + | - Microcontrolerul genereaza o secventa aleatoare si o afiseaza prin LED-uri cu sunet specific fiecarei culori | ||
| + | - Jucatorul reproduce secventa; pe LCD se afiseaza countdown-ul de timp ramas | ||
| + | - La nivel corect: animatie inima care bate, scor actualizat, dificultate crescuta | ||
| + | - Daca scorul depaseste recordul: celebrare imediata cu trofeu pe LCD si melodie, jocul **continua** | ||
| + | - La raspuns gresit sau timeout: skull animat, Game Over, scor afisat, selectie dificultate din nou | ||
| + | - Recordul persista in EEPROM intre resetari si deconectari | ||
| + | |||
| + | ==== Masina de stari ==== | ||
| + | |||
| + | <code> | ||
| + | IDLE (ecran intro + animatie) | ||
| + | │ apasare orice buton | ||
| + | ▼ | ||
| + | SELECT_DIFFICULTY (Easy / Medium / Hard) | ||
| + | │ apasare B1 / B2 / B3 | ||
| + | ▼ | ||
| + | SHOW_SEQUENCE (afisare secventa LED-uri cu sunet) | ||
| + | │ secventa terminata | ||
| + | ▼ | ||
| + | WAIT_INPUT (asteapta input jucator, countdown vizibil pe LCD) | ||
| + | ├─ corect, scor > record → RECORD (trofeu + melodie) → SHOW_SEQUENCE | ||
| + | ├─ corect, scor <= record → CORRECT (animatie inima) → SHOW_SEQUENCE | ||
| + | └─ gresit / timeout → GAME_OVER (skull animat + salvare scor) → SELECT_DIFFICULTY | ||
| + | </code> | ||
| - | [De adaugat] | ||
| ===== Hardware Design ===== | ===== Hardware Design ===== | ||
| - | **Lista de piese:** | + | ==== Lista de componente ==== |
| - | ^ Componenta ^ Model ^ Rol ^ | + | ^ Componenta ^ Model/Valoare ^ Cantitate ^ Rol in proiect ^ |
| - | | Microcontroller | ATmega328P Xplained Mini | Unitate centrala de control | | + | | Microcontroller | ATmega328P Xplained Mini | 1 | Unitate centrala de control | |
| - | | Display LCD | 16x2 | Afisare scor si mesaje | | + | | Display LCD | 16x2 cu modul I2C (PCF8574) | 1 | Afisare scor, nivel, countdown, animatii | |
| - | | Potentiometru | 10kΩ | Reglaj contrast LCD | | + | | LED rosu | 5mm, 2V, 20mA | 1 | Culoarea 1 a jocului | |
| - | | LED rosu | 5mm + rezistor 220Ω | Culoarea 1 | | + | | LED verde | 5mm, 2.1V, 20mA | 1 | Culoarea 2 a jocului | |
| - | | LED verde | 5mm + rezistor 220Ω | Culoarea 2 | | + | | LED galben | 5mm, 2V, 20mA | 1 | Culoarea 3 a jocului | |
| - | | LED albastru | 5mm + rezistor 220Ω | Culoarea 3 | | + | | LED albastru | 5mm, 3.2V, 20mA | 1 | Culoarea 4 a jocului | |
| - | | LED galben | 5mm + rezistor 220Ω | Culoarea 4 | | + | | Rezistenta | 220Ω | 4 | Protectie LED-uri (limiteaza curentul la ~15mA) | |
| - | | Butoane tactile | x4 | Input jucator | | + | | Butoane tactile | 6x6mm, 4 pini | 4 | Input jucator | |
| - | | Buzzer pasiv | 5V | Feedback sonor | | + | | Buzzer activ | 5V | 1 | Feedback sonor, note diferite per culoare | |
| - | | Breadboard + fire | — | Prototipare | | + | | Breadboard | 830 puncte | 1 | Prototipare circuit | |
| + | | Fire jumper | M-M | ~25 | Conexiuni intre componente | | ||
| - | **Conexiuni principale:** | + | ==== Pinii folositi ==== |
| + | |||
| + | ^ Componenta ^ Pin ATmega ^ Pin Arduino ^ Motivatie alegere ^ | ||
| + | | LED rosu | PD2 | D2 | GPIO simplu, fara functii speciale | | ||
| + | | LED verde | PD3 | D3 | GPIO simplu, fara functii speciale | | ||
| + | | LED galben | PD4 | D4 | GPIO simplu, fara functii speciale | | ||
| + | | LED albastru | PD5 | D5 | GPIO simplu, fara functii speciale | | ||
| + | | Buton rosu | PD6 | D6 | GPIO cu pull-up intern disponibil | | ||
| + | | Buton verde | PD7 | D7 | GPIO cu pull-up intern disponibil | | ||
| + | | Buton galben | PB0 | D8 | GPIO cu pull-up intern disponibil | | ||
| + | | Buton albastru | PB1 | D9 | GPIO cu pull-up intern disponibil | | ||
| + | | Buzzer activ | PB2 | D10 | Compatibil cu functia tone() | | ||
| + | | LCD SDA | PC4 | A4 | Pin hardware TWI/I2C al ATmega328P | | ||
| + | | LCD SCL | PC5 | A5 | Pin hardware TWI/I2C al ATmega328P | | ||
| + | |||
| + | **Motivatia alegerii pinilor:** | ||
| + | * **PD0/PD1 au fost evitati** — sunt RX/TX, folositi de comunicatia seriala USB cu mEDBG; conectarea LED-urilor pe acesti pini blocheaza upload-ul de cod | ||
| + | * **PC4/PC5** sunt pinii hardware TWI (I2C) ai ATmega328P, oferind comunicatie stabila cu LCD-ul prin protocol hardware, nu software bitbang | ||
| + | * **Butoanele folosesc INPUT_PULLUP intern** — elimina necesitatea rezistentelor externe de pull-down, simplificand circuitul si reducand numarul de componente | ||
| + | |||
| + | ==== Schema electrica ==== | ||
| + | |||
| + | **LED-uri (x4, conectare identica):** | ||
| + | <code> | ||
| + | Pin GPIO (D2-D5) ── Rezistenta 220Ω ── Anod LED (+, picior lung) | ||
| + | Catod LED (-, picior scurt) ── GND | ||
| + | </code> | ||
| + | |||
| + | **Butoane (x4, cu pull-up intern, fara rezistente externe):** | ||
| + | <code> | ||
| + | Pin GPIO (D6-D9) ── Pin 1 buton (o latura) | ||
| + | GND ─────────────── Pin 2 buton (latura opusa, de cealalta parte a canalului central) | ||
| + | Pull-up intern activat prin INPUT_PULLUP → citeste LOW la apasare, HIGH in repaus | ||
| + | </code> | ||
| + | |||
| + | **Buzzer activ:** | ||
| + | <code> | ||
| + | D10 ── Pin + buzzer (marcat cu +) | ||
| + | GND ── Pin - buzzer (marcat cu -) | ||
| + | </code> | ||
| + | |||
| + | **LCD I2C:** | ||
| + | <code> | ||
| + | A4 (SDA/PC4) ── SDA modul I2C | ||
| + | A5 (SCL/PC5) ── SCL modul I2C | ||
| + | 5V ──────────── VCC modul I2C | ||
| + | GND ─────────── GND modul I2C | ||
| + | Potentiometru albastru pe modul = reglaj contrast | ||
| + | </code> | ||
| + | |||
| + | {{pm:prj2026:alexandru.jipa2803:5211176995717324748.jpg?300}} | ||
| - | * LCD RS → D2 (PD2) | ||
| - | * LCD EN → D3 (PD3) | ||
| - | * LCD D4-D7 → D4-D7 (PD4-PD7) | ||
| - | * LCD VDD → 5V, VSS → GND, RW → GND | ||
| - | * LCD V0 → wiper potentiometru (intre 5V si GND) | ||
| - | * LED rosu → D8 (PB0) prin rezistor 220Ω | ||
| - | * LED verde → D9 (PB1) prin rezistor 220Ω | ||
| - | * LED albastru → D10 (PB2) prin rezistor 220Ω | ||
| - | * LED galben → D11 (PB3) prin rezistor 220Ω | ||
| - | * Buton 1 → A0 (PC0) → GND (pull-up intern activat) | ||
| - | * Buton 2 → A1 (PC1) → GND (pull-up intern activat) | ||
| - | * Buton 3 → A2 (PC2) → GND (pull-up intern activat) | ||
| - | * Buton 4 → A3 (PC3) → GND (pull-up intern activat) | ||
| - | * Buzzer + → D12 (PB4), Buzzer - → GND | ||
| ===== Software Design ===== | ===== Software Design ===== | ||
| - | **Mediu de dezvoltare:** PlatformIO + framework Arduino (MiniCore) | + | ==== Mediu de dezvoltare ==== |
| - | ^ Periferic ^ Utilizare ^ | + | * **Arduino IDE** cu framework Arduino |
| - | | Timer1 | Generare timinguri non-blocante pentru secventa | | + | * Limbaj: **C++** cu API Arduino |
| - | | GPIO output | Control LED-uri | | + | * Upload prin USB (mEDBG integrat pe placa Xplained Mini) |
| - | | GPIO input (pull-up) | Citire butoane | | + | * **Serial Monitor** la 9600 baud pentru debugging si logging |
| - | | PWM (tone()) | Generare note buzzer pasiv | | + | |
| - | | EEPROM | Stocare high score intre resetari | | + | |
| - | **Structura software:** | + | ==== Biblioteci folosite ==== |
| - | * Masina de stari: IDLE → SHOW_SEQUENCE → WAIT_INPUT → GAME_OVER | + | ^ Biblioteca ^ Sursa ^ Motivatie ^ |
| - | * Secventa stocata intr-un array, lungime crescatoare la fiecare nivel | + | | Wire.h | Built-in Arduino | Comunicatie I2C hardware cu LCD | |
| - | * Viteza de afisare scade cu fiecare nivel (dificultate adaptiva) | + | | LiquidCrystal_I2C.h | Frank de Brabander | Abstractizare comenzi LCD: initializare, cursor, print, caractere custom | |
| - | * Debounce software pentru butoane | + | | EEPROM.h | Built-in Arduino | Acces la memoria EEPROM pentru persistenta high score | |
| - | * Note diferite per culoare: DO, RE, MI, SOL (frecvente fixe) | + | |
| - | * High score salvat in EEPROM, persistent intre resetari | + | |
| + | ==== Elementul de noutate ==== | ||
| - | ===== Bibliografie/Resurse ===== | + | * **Selectare dificultate la start**: jucatorul alege Easy / Medium / Hard inainte de fiecare joc, cu viteze si timpi de replica diferiti; selectia reapare dupa fiecare Game Over |
| + | * **Dificultate adaptiva bidiectionala**: viteza creste la succese (−20ms/nivel) si scade la greseli (+30ms), cu limite 150ms–600ms, mentinand jocul accesibil indiferent de nivel | ||
| + | * **Celebrare record in timp real**: daca scorul depaseste recordul in timpul jocului (nu doar la Game Over), se afiseaza trofeu pe LCD, se canta melodia si se salveaza in EEPROM — jocul **continua** fara intrerupere | ||
| + | * **Animatii LCD cu caractere custom**: 8 simboluri definite ca matrici 5x8 pixeli (inima, skull, trofeu, note muzicale, sageata, progress bar) folosite in animatii procedurale | ||
| + | * **Melodie la record**: primele 15 note din "We Are the Champions" cantate pe buzzer, cu LED-urile clipind sincronizat cu fiecare nota | ||
| + | * **Countdown vizibil**: in timpul replicii jucatorului, LCD afiseaza secundele ramase, actualizate in timp real | ||
| - | **Hardware:** | + | ==== Functionalitati din laborator utilizate ==== |
| - | * [[https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-42743-ATmega328P_Datasheet.pdf|Datasheet ATmega328P]] | + | ^ Functionalitate ^ Utilizare in proiect ^ |
| - | * [[https://www.microchip.com/en-us/development-tool/atmega328p-xmini|ATmega328P Xplained Mini User Guide]] | + | | GPIO output | Control LED-uri pe D2-D5 pentru afisarea secventei si animatii | |
| + | | GPIO input cu pull-up | Citire butoane fara rezistente externe: INPUT_PULLUP pe D6-D9 | | ||
| + | | Timer (tone()) | Generare frecvente audio pentru buzzer — intern foloseste Timer2 | | ||
| + | | TWI/I2C (Wire.h) | Comunicatie cu modulul LCD prin 2 fire (SDA/SCL) | | ||
| + | | EEPROM | Stocare persistenta a high score-ului la adresa 0 cu EEPROM.put/get | | ||
| + | | Debounce software | Eliminarea bouncing-ului mecanic: asteptare release + delay 50ms | | ||
| - | **Software & Tutoriale:** | + | ==== Scheletul proiectului si portiuni de cod relevante ==== |
| - | * [[https://ocw.cs.pub.ro/courses/pm/tutorial/proiect|Ghid proiect PM]] | + | === Selectare dificultate === |
| - | * [[https://docs.arduino.cc/libraries/liquidcrystal/|Arduino LiquidCrystal Library]] | + | |
| + | La pornire si dupa fiecare Game Over, jucatorul selecteaza dificultatea prin primele 3 butoane. | ||
| + | LED-urile se aprind ca indicatori vizuali in timp ce asteapta selectia: | ||
| + | |||
| + | ^ Dificultate ^ Buton ^ Viteza initiala ^ Timp replica ^ | ||
| + | | Easy | B1 (rosu) | 600ms per LED | 12 secunde | | ||
| + | | Medium | B2 (verde) | 500ms per LED | 10 secunde | | ||
| + | | Hard | B3 (galben) | 300ms per LED | 7 secunde | | ||
| + | |||
| + | <code cpp> | ||
| + | void selecteazaDificultate() { | ||
| + | lcd.write(CHAR_NOTE); lcd.print("E "); // Easy | ||
| + | lcd.write(CHAR_SAGEATA); lcd.print("M "); // Medium | ||
| + | lcd.write(CHAR_SKULL); lcd.print("H"); // Hard | ||
| + | |||
| + | // Aprinde LED-urile ca indicatori vizuali | ||
| + | for (int i = 0; i < 3; i++) digitalWrite(ledPins[i], HIGH); | ||
| + | |||
| + | while (!ales) { | ||
| + | if (digitalRead(btnPins[0]) == LOW) { viteza=600; timpReplica=12000; ales=true; } | ||
| + | if (digitalRead(btnPins[1]) == LOW) { viteza=500; timpReplica=10000; ales=true; } | ||
| + | if (digitalRead(btnPins[2]) == LOW) { viteza=300; timpReplica=7000; ales=true; } | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Dificultate adaptiva bidiectionala === | ||
| + | |||
| + | Dupa fiecare nivel, viteza si timpul de replica se ajusteaza automat. | ||
| + | Limitele explicite previn ca jocul sa devina imposibil sau prea lent: | ||
| + | |||
| + | <code cpp> | ||
| + | void ajusteazaDificultate(bool corect) { | ||
| + | if (corect) { | ||
| + | viteza = max(150, viteza - 20); // LED-urile se aprind mai repede | ||
| + | timpReplica = max(5000, timpReplica - 300); // mai putin timp de replica | ||
| + | } else { | ||
| + | viteza = min(600, viteza + 30); // LED-urile se aprind mai lent | ||
| + | timpReplica = min(12000, timpReplica + 500);// mai mult timp de replica | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Countdown vizibil pe LCD === | ||
| + | |||
| + | In bucla de asteptare a butoanelor, LCD-ul este actualizat continuu cu secundele ramase. | ||
| + | Functia returneaza −1 la timeout, tratata ca greseala: | ||
| + | |||
| + | <code cpp> | ||
| + | int waitForButton() { | ||
| + | unsigned long start = millis(); | ||
| + | while (millis() - start < (unsigned long)timpReplica) { | ||
| + | int secRamase = (timpReplica - (millis() - start)) / 1000; | ||
| + | lcd.setCursor(9, 1); | ||
| + | lcd.print("T:"); lcd.print(secRamase); lcd.print("s "); | ||
| + | |||
| + | for (int i = 0; i < 4; i++) { | ||
| + | if (digitalRead(btnPins[i]) == LOW) { | ||
| + | lightUp(i, 300); | ||
| + | while (digitalRead(btnPins[i]) == LOW); // asteapta release | ||
| + | delay(50); // debounce | ||
| + | return i; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | return -1; // timeout | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Animatii LCD cu caractere custom === | ||
| + | |||
| + | LCD-ul 16x2 suporta pana la 8 caractere custom definite ca matrici de pixeli 5x8. | ||
| + | Proiectul defineste 8 simboluri inregistrate la initializare cu ''lcd.createChar()'': | ||
| + | |||
| + | ^ Index ^ Simbol ^ Utilizat in ^ | ||
| + | | 0 | Inima plina | Nivel corect, record | | ||
| + | | 1 | Inima goala | Animatie batai inima | | ||
| + | | 2 | Skull | Game Over | | ||
| + | | 3 | Note muzicale | Intro, selectie Easy | | ||
| + | | 4 | Sageata | Nivel curent, selectie Medium | | ||
| + | | 5 | Trofeu | Record, castig | | ||
| + | | 6 | Progres plin | Progress bar | | ||
| + | | 7 | Progres gol | Progress bar | | ||
| + | |||
| + | Exemplu definitie si animatie inima care bate: | ||
| + | |||
| + | <code cpp> | ||
| + | byte inima[] = {0b00000,0b01010,0b11111,0b11111,0b11111,0b01110,0b00100,0b00000}; | ||
| + | byte inimaGoala[] = {0b00000,0b01010,0b10101,0b10001,0b10001,0b01010,0b00100,0b00000}; | ||
| + | |||
| + | void animatieInima() { | ||
| + | for (int i = 0; i < 3; i++) { // 3 batai | ||
| + | lcd.setCursor(15, 0); | ||
| + | lcd.write(CHAR_INIMA); // inima plina | ||
| + | tone(buzzer, 800); delay(80); noTone(buzzer); | ||
| + | delay(100); | ||
| + | lcd.setCursor(15, 0); | ||
| + | lcd.write(CHAR_INIMA_GOALA); // inima goala | ||
| + | delay(200); | ||
| + | } | ||
| + | lcd.setCursor(15, 0); | ||
| + | lcd.write(CHAR_INIMA); | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | Progress bar pe randul 2 al LCD-ului, care arata progresul prin cele 20 de nivele: | ||
| + | |||
| + | <code cpp> | ||
| + | void afiseazaProgressBar() { | ||
| + | lcd.setCursor(0, 1); | ||
| + | int pasi = map(level, 1, MAXLEVEL, 0, 16); // 1-20 nivele → 0-16 caractere | ||
| + | for (int i = 0; i < 16; i++) { | ||
| + | if (i < pasi) lcd.write(CHAR_PROG_FULL); | ||
| + | else lcd.write(CHAR_PROG_EMPTY); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Celebrare record in timp real === | ||
| + | |||
| + | Spre deosebire de implementarile clasice (care verifica recordul doar la Game Over), | ||
| + | aceasta implementare verifica dupa fiecare nivel corect. La depasirea recordului, | ||
| + | se afiseaza trofeu, se salveaza in EEPROM si se canta melodia — apoi jocul **continua**: | ||
| + | |||
| + | <code cpp> | ||
| + | // In loop(), dupa completarea unui nivel: | ||
| + | scor += level * 10; | ||
| + | ajusteazaDificultate(true); | ||
| + | |||
| + | if (scor > highScore) { | ||
| + | celebreazaRecord(); // trofeu + melodie + salveaza EEPROM | ||
| + | } else { | ||
| + | animatieInima(); // animatie normala nivel corect | ||
| + | } | ||
| + | |||
| + | level++; | ||
| + | if (level > MAXLEVEL) win(); | ||
| + | else playSequence(); // jocul continua normal | ||
| + | |||
| + | void celebreazaRecord() { | ||
| + | lcd.write(CHAR_TROPHY); lcd.print(" NOU RECORD! "); lcd.write(CHAR_TROPHY); | ||
| + | highScore = scor; | ||
| + | EEPROM.put(0, highScore); // persistenta intre resetari | ||
| + | cantaMelodie(); // We Are the Champions | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Melodie la record === | ||
| + | |||
| + | La baterea recordului, buzzerul canta primele 15 note din "We Are the Champions". | ||
| + | LED-urile clipesc sincronizat cu melodia (cate un LED diferit per nota, in cicluri de 4): | ||
| + | |||
| + | <code cpp> | ||
| + | const int melodieNote[] = { 392,392,349,392,0,523,494,440,392, | ||
| + | 659,659,659,587,523,392 }; | ||
| + | const int melodieDurata[] = { 200,200,200,400,200,400,200,200,600, | ||
| + | 200,200,200,200,200,600 }; | ||
| + | |||
| + | void cantaMelodie() { | ||
| + | for (int i = 0; i < melodieLen; i++) { | ||
| + | if (melodieNote[i] == 0) { noTone(buzzer); } // pauza intre note | ||
| + | else { | ||
| + | tone(buzzer, melodieNote[i]); | ||
| + | digitalWrite(ledPins[i % 4], HIGH); // LED sincronizat cu nota | ||
| + | } | ||
| + | delay(melodieDurata[i]); | ||
| + | noTone(buzzer); | ||
| + | digitalWrite(ledPins[i % 4], LOW); | ||
| + | delay(30); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Persistenta EEPROM === | ||
| + | |||
| + | Scorul maxim este salvat la adresa 0 din EEPROM. Validarea la citire protejeaza | ||
| + | impotriva datelor corupte sau a primei porniri (cand EEPROM contine valori nedefinite): | ||
| + | |||
| + | <code cpp> | ||
| + | // La pornire — citire cu validare: | ||
| + | EEPROM.get(0, highScore); | ||
| + | if (highScore < 0 || highScore > 9999) highScore = 0; | ||
| + | |||
| + | // La baterea recordului — scriere imediata: | ||
| + | EEPROM.put(0, highScore); | ||
| + | </code> | ||
| + | |||
| + | === Debounce butoane === | ||
| + | |||
| + | Butoanele mecanice genereaza bouncing la apasare si eliberare. | ||
| + | Debounce-ul software combina doua tehnici complementare: | ||
| + | |||
| + | <code cpp> | ||
| + | if (digitalRead(btnPins[i]) == LOW) { | ||
| + | lightUp(i, 300); // feedback vizual imediat | ||
| + | while (digitalRead(btnPins[i]) == LOW); // asteapta release complet | ||
| + | delay(50); // elimina bouncing la eliberare | ||
| + | return i; | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | ==== Validare functionare ==== | ||
| + | |||
| + | Validarea s-a realizat in mai multe etape: | ||
| + | |||
| + | **1. Testare LED-uri individual:** | ||
| + | <code cpp> | ||
| + | // Aprinde fiecare LED pe rand cu delay, verifica orientarea si conexiunile | ||
| + | void loop() { | ||
| + | for (int i = 0; i < 4; i++) { | ||
| + | digitalWrite(ledPins[i], HIGH); delay(500); | ||
| + | digitalWrite(ledPins[i], LOW); delay(200); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | **2. Testare butoane cu Serial Monitor:** | ||
| + | <code cpp> | ||
| + | // Afiseaza butonul apasat — a identificat problema de inversare GND/semnal | ||
| + | void loop() { | ||
| + | for (int i = 0; i < 4; i++) { | ||
| + | if (digitalRead(btnPins[i]) == LOW) { | ||
| + | Serial.print("Buton "); Serial.print(i+1); Serial.println(" apasat!"); | ||
| + | delay(300); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | **3. Validare prin Serial Monitor in joc:** | ||
| + | La fiecare eveniment se afiseaza mesaje la 9600 baud: | ||
| + | <code> | ||
| + | === SIMON SAYS === | ||
| + | Dificultate: MEDIUM | ||
| + | ------------------ | ||
| + | NIVEL 3 | Viteza: 460ms | ||
| + | Secventa: | ||
| + | 1. Rosu | ||
| + | 2. Verde | ||
| + | 3. Albastru | ||
| + | Repeta secventa! | ||
| + | Apasat: Rosu -> Corect! (1/3) | ||
| + | Apasat: Verde -> Corect! (2/3) | ||
| + | Apasat: Albastru -> Corect! (3/3) | ||
| + | Nivel completat! Scor: 60 | ||
| + | *** NOU HIGH SCORE! *** | ||
| + | Viteza noua: 440 | Timp replica: 9400 | ||
| + | </code> | ||
| + | |||
| + | **4. Testare EEPROM:** | ||
| + | Dupa game over cu scor nou, s-a verificat ca la resetare hardware scorul persista | ||
| + | (citit cu EEPROM.get la pornire si afisat pe LCD). | ||
| + | |||
| + | ==== Optimizari realizate ==== | ||
| + | |||
| + | * **Debounce software**: asteptare release + delay 50ms, eliminand bouncing-ul mecanic fara timere hardware | ||
| + | * **randomSeed(analogRead(A0))**: pinul A0 neconectat citeste zgomot analogic, asigurand secvente cu adevarat aleatorii la fiecare pornire | ||
| + | * **INPUT_PULLUP**: elimina rezistentele externe de pull-down pentru butoane, simplificand circuitul | ||
| + | * **Validare EEPROM la citire**: protejeaza impotriva valorilor corupte sau nedefinite la prima pornire | ||
| + | * **Celebrare record inline**: verificarea si salvarea recordului se face dupa fiecare nivel, nu doar la Game Over, fara a intrerupe fluxul jocului | ||
| + | |||
| + | |||
| + | ===== Rezultate Obtinute ===== | ||
| + | |||
| + | Proiectul functioneaza conform specificatiilor extinse: | ||
| + | * Selectarea dificultaii este functionala cu feedback LED si LCD, reapare dupa fiecare Game Over | ||
| + | * Secventele sunt generate aleator si afisate corect pe LED-uri cu sunet specific per culoare | ||
| + | * Butoanele sunt detectate corect cu debounce, fara false pozitive sau omisiuni | ||
| + | * LCD afiseaza nivel, scor, countdown timp ramas si animatii in timp real | ||
| + | * Dificultatea se ajusteaza automat dupa fiecare nivel in ambele directii | ||
| + | * Recordul se salveaza in EEPROM si persista intre resetari hardware si deconectari | ||
| + | * Celebrarea recordului se declanseaza imediat, fara sa intrerupa jocul | ||
| + | * Melodia "We Are the Champions" se canta corect cu LED-urile sincronizate pe note | ||
| + | * Progress bar-ul reflecta fidel progresul prin cele 20 de nivele | ||
| + | |||
| + | |||
| + | ===== Concluzii ===== | ||
| + | |||
| + | Proiectul Simon Says a fost implementat cu succes pe placa ATmega328P Xplained Mini, | ||
| + | depasind specificatiile de baza prin adaugarea mai multor functionalitati care imbunatatesc | ||
| + | experienta de joc si complexitatea tehnica. | ||
| + | |||
| + | Principalele provocari intampinate: | ||
| + | * Compatibilitatea librariei LiquidCrystal_I2C cu toolchain-ul MiniCore — rezolvata prin implementare manuala Wire.h | ||
| + | * Conflicte de pini intre RX/TX si LED-uri — rezolvate prin relocarea LED-urilor pe PD4-PD7 | ||
| + | * Calibrarea debounce-ului butoanelor pentru raspuns fluid | ||
| + | * High score blocat — valoare corupta in EEPROM dintr-o sesiune anterioara; rezolvat prin validare la citire si reset manual | ||
| + | |||
| + | Lectii invatate: | ||
| + | * Documentarea pinilor inainte de cablare evita conflicte greu de depanat | ||
| + | * Testarea incrementala (LED, buton, LCD, buzzer separat) economiseste timp considerabil | ||
| + | * INPUT_PULLUP simplifica semnificativ circuitul butoanelor | ||
| + | * Verificarea recordului in timp real (nu doar la Game Over) ofera o experienta de joc mai fluida | ||
| + | |||
| + | |||
| + | ===== Bibliografie ===== | ||
| + | |||
| + | * [[https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-42743-ATmega328P_Datasheet.pdf|Datasheet ATmega328P - Microchip]] | ||
| + | * [[https://www.microchip.com/en-us/development-tool/atmega328p-xmini|ATmega328P Xplained Mini User Guide]] | ||
| + | * [[https://docs.arduino.cc/learn/built-in-libraries/eeprom/|Arduino EEPROM Library Documentation]] | ||
| + | * [[https://docs.arduino.cc/learn/communication/wire/|Arduino Wire Library (I2C)]] | ||
| + | * [[https://github.com/johnrickman/LiquidCrystal_I2C|LiquidCrystal_I2C Library - Frank de Brabander]] | ||
| + | * [[https://ocw.cs.pub.ro/courses/pm/tutorial/proiect|Ghid proiect PM - OCW UPB]] | ||