This is an old revision of the document!
Scopul proiectului este realizarea unui joc entertaining.
Implementarea jocului Minesweeper pe un LCD. Controlul se va face printr-un joystick și butoane. Va avea un timer și un buzzer care scoate sunete la câștigarea / pierderea jocului.
Placa de dezvoltare compatibilă cu Arduino UNO (ATmega328p) controlează buzzer-ul și display-ul, primind input de la două butoane și un joystick. Pentru comunicarea cu LCD-ul folosește protocolul SPI. Prin joystick se controlează mișcările jucătorului pe tabla de joc, iar prin cele două butoane se poate selecta tipul celulei: clear sau flagged. Obiectivul jocului este descoperirea tuturor celulelor libere. Atunci când pe tablă rămân doar mine nedescoperite, jocul este câștigat, iar buzzer-ul va emite un sunet specific. Alternativ, la o mișcare greșită, jocul se încheie, iar sunetul emis de buzzer anunță înfrângerea.
Lista de componente:
Nume componentă | Link achiziție | Cantitate | Preț unitar |
---|---|---|---|
Arduino UNO R3 | Placă de dezvoltare | 1 | 39,37 lei |
2.8” SPI LCD module cu controller ILI9341 | Display | 1 | 69,99 lei |
Modul joystick biaxial | Joystick | 1 | 5,35 lei |
Modul cu buzzer activ | Buzzer | 1 | 2,99 lei |
Push button | Buton | 2 | 1,99 lei |
Rezistor 10kΩ | Rezistor 10kΩ | 6 | 0.10 lei |
Rezistor 100kΩ | Rezistor 100kΩ | 3 | 0.10 lei |
Diodă 1N4007 | Diodă 1N4007 | 2 | 0.49 lei |
Breadboard HQ (400 points) | Breadboard | 2 | 4,56 lei |
Fire rigide | Set fire rigide | 1 | 12,49 lei |
Fire tată-tată | Set fire tată-tată | 2 | 2,85 lei |
Cost total: 150,87 lei |
Mediu de dezvoltare: Visual Studio Code + PlatformIO
Librării:
Timer
Microcontroller-ul ATmega328p conține 3 unități de timer, două pe 8 biți (Timer0 și Timer2) și unul pe 16 biți (Timer1).
Am folosit Timer1 pentru a genera întreruperi la intervale fixe de 1 secundă, pentru a afișa un timer pe display. La expirarea timpului înainte ca jocul să se fi încheiat, jucătorul pierde.
Am folosit formula de calcul:
timer_count = clock_frequency / (prescaler * interrupt_frequency) - 1
Conform datasheet-ului ATmega328p:
Frecvența de funcționare a procesorului este 16 MHz.
#define CLOCK_FREQUENCY 16000000 /* 16 MHz */ #define INTERRUPT_FREQUENCY 1 /* 1Hz corresponds to 1 second period */ #define PRESCALER 256 #define TIMER_COUNT (CLOCK_FREQUENCY / (PRESCALER * INTERRUPT_FREQUENCY) - 1)
void init_timer1() { /* Reset control registers for Timer 1 */ TCCR1A = 0; TCCR1B = 0; /* The prescaler value is 256 -> set the CS12 bit */ TCCR1B |= (1 << CS12); /* Make the comparator trigger Timer/Counter1 input capture interrupt -> set the OCIE1A bit in the timer interrupt mask register (TIMSK1). */ TIMSK1 |= (1 << OCIE1A); /* Set the threshold value (timer_count) for Timer 1 */ OCR1A = TIMER_COUNT; }
Întreruperi
La inițializare, am activat mecanismul de întreruperi prin activarea bitului I din registrul SREG.
/* Activate interrupts */ sei();
Am definit două rutine pentru tratarea întreruperilor externe (pentru apăsarea butoanelor și pentru apăsarea butonului de la joystick) și una pentru tratarea întreruperilor interne folosind Timer1.
Configurarea componentelor care vor trimite întreruperi:
/* initialize button pins */ pinMode(BLUE_BUTTON, INPUT); pinMode(RED_BUTTON, INPUT); pinMode(BUTTON_INTERRUPT, INPUT); attachInterrupt(digitalPinToInterrupt(BUTTON_INTERRUPT), ISR_button, RISING);
Am folosit funcția attachInterrupt
pentru a atașa rutina ISR_button evenimentelor de pe pinul corespunzător butoanelor. Parametrul RISING
definește momentul în care va fi declanșată întreruperea - atunci când valoarea pinului trece de la LOW la HIGH (la apăsarea unuia dintre butoane).
/* initialize joystick pins */ pinMode(JOYSTICK_X, INPUT); pinMode(JOYSTICK_Y, INPUT); pinMode(JOYSTICK_INTERRUPT, INPUT); digitalWrite(JOYSTICK_INTERRUPT, HIGH); attachInterrupt(digitalPinToInterrupt(JOYSTICK_INTERRUPT), ISR_joystick, RISING);
Definirea rutinelor de tratare a întreruperilor:
void ISR_button() { buttonPressTime = millis(); if (digitalRead(BLUE_BUTTON) && buttonPressTime - lastPressBlue > debounceTime) { lastPressBlue = buttonPressTime; blueButtonFlag = true; } else if (digitalRead(RED_BUTTON) && buttonPressTime - lastPressRed > debounceTime) { lastPressRed = buttonPressTime; redButtonFlag = true; } }
void ISR_joystick() { joystickButtonFlag = true; }
ISR(TIMER1_COMPA_vect){ TCNT1 = 0; /* Reset counter register */ timer--; timerFlag = true; }
Parametrul TIMER1_COMPA_vect indică faptul că se face Compare Match cu pragul A al timerului.
Fişierele se încarcă pe wiki folosind facilitatea Add Images or other files. Namespace-ul în care se încarcă fişierele este de tipul :pm:prj20??:c? sau :pm:prj20??:c?:nume_student (dacă este cazul). Exemplu: Dumitru Alin, 331CC → :pm:prj2009:cc:dumitru_alin.
Probleme întâmpinate: