Proiectul consta in implementarea jocului Minesweeper, folosind pentru afisare un display Nokia 3310 PCD 8854. Jocul are 6 x 7 casute si 5 bombe. Bombele sunt plasate in mod aleator pe tabela de joc. Scopul jocului este descoperirea si marcarea bombelor. Se va putea salva un joc neterminat.
Placa de baza:
Pentru realizarea proiectului, am realizat o placuta suplimentara pe care sunt lipite circuitele pentru LCD si butoanele prin care se realizeaza comanda asupra jocului.
Lista de piese:
Pentru conectarea LCD-ului, am realizat o placuta imprimata cu 8 trasee. LCD-ul face contact direct cu placuta imprimata si este lipit de aceasta cu banda adeziva.
Pentru a aduce tensiunea de alimentare de la 5V oferiti de ATMega16 la 3.3V pentru LCD am folosit 4 diode 1N4007. Teoretic este nevoie de 2 diode (5V - 2 * 0.7V = 3.6V), insa microcontrollerul meu ofera 5.2V, iar diodele au o cadere de tensiune de 0.4V. Astfel, folosind 4 diode am obtinut o tensiune de alimentare de 3.7-3.8V. LCD-ul functioneaza corect.
Pentru LCD, pinout-ul este urmatorul:
Este nevoie de divizoare de tensiune pentru fiecare intrare din LCD (SCK, SDI, D/C, SCE, RES) pentru ca tensiunea sa fie de 3.3V. Divizorul este realizat cu ajutorul rezistentelor de 2k2 si 4k6.
Cele 7 butoane sunt puse pe portul C si au urmatoarea semnificatie:
A fost montat si circuitul pentru slotul de card SD pe placa de baza.
Schema in Eagle:
Screenshot simulare in Proteus:
Proiectul a fost realizat utilizand WinAVR.
Pentru interfatarea LCD-ului am folosit driver-ul de aici cu modificari: http://www.quantumtorque.com/content/view/32/37/
Tabela de joc cu informatiile aferente jocului este retinuta intr-o matrice unsigned char de dimensiuni 6 x 7 - table[NR_ROW][NR_COL]. In fiecare table[i][j] se retin urmatoarele informatii: in primii 3 biti se retine numarul de bombe din jurul sau (adica din cei 8 vecini), urmatorul bit este nefolosit, bitul 5 este setat daca casuta contine o bomba sau nu, bitul 6 este setat daca utilizatorul a marcat casuta respectiva ca fiind bomba si bitul 7 este setat daca casuta a fost descoperita sau nu (bitul 8 este nefolosit).
#define MINES 0x0F #define IS_MINE 0x10 #define IS_MARKED 0x20 #define IS_UNCOVERED 0x40
Daca utilizatorul selecteaza o casuta pentru a o descoperi, se apeleaza functia recursiva uncover_cell. Aceasta se opreste din recursivitate cand in jurul casutei curente exista bombe.
Dupa marcarea unei celule drept bomba, in casuta respectiva va aparea semnul +. O bomba este marcata cu X.
Daca utilizatorul pierde sau castiga, se descopera toata tabla si se afiseaza un mesaj, dandu-se posibilitatea inceperii unui nou joc sau incarcarii jocului salvat din memorie.
Pentru generarea numerelor aleatoare, folosesc rand(), cu seed o variabila citita din memoria EEPROM si incrementata. Dupa aceea, scriu in memoria EEPROM noua valoare, pentru ca la urmatoarea rulare a jocului sa fie o dispunere a bombelor diferita de cele precedente. La inceput, nu se gaseste o valoare in memoria EEPROM (pentru acest lucru ar fi trebuit sa incarc si un fisier .eep, separat de fisierul .hex). Pentru a nu trebui sa incarc initial si un fisier .eep, prima data variabila seed va fi initializata cu valoarea 1, iar la urmatoarele rulari nu se va mai efectua aceasta initializare.
unsigned char EEMEM seed; if (eeprom_read_byte((uint8_t*)E_INIT_SEED) !='T') { i = 1; eeprom_write_byte(&seed, i); eeprom_write_byte((uint8_t*)E_INIT_SEED,'T'); } else { s = eeprom_read_byte(&seed); if (s == 255) s = 0; else s ++; eeprom_write_byte(&seed, s); }
Salvarea jocului se realizeaza in memoria EEPROM, deoarece jocul ocupa doar 42 bytes. Se salveaza matricea table.
Bufferul de afisare are dimensiunea 6×84.
static unsigned char cursor_row = 0; /* 0-5 */ static unsigned char cursor_col = 0; /* 0-83 */ static unsigned char buffer[6][84];
Pentru LCD:
void lcd_init(void); void lcd_contrast(unsigned char contrast); void lcd_clear(void); void lcd_putchr(unsigned char ch); void lcd_str(char str[14], uint8_t len); void lcd_gotoXY(uint8_t x, uint8_t y); void lcd_update( void ); void lcd_setpixel(unsigned char x, unsigned char y); void lcd_clearpixel(unsigned char x, unsigned char y); void lcd_border(void); void dark_cell(unsigned char y, unsigned char x); void mark_cell(unsigned char y, unsigned char x); void unmark_cell(unsigned char y, unsigned char x); void uncovercell(unsigned char y, unsigned char x, unsigned char value); void uncovermine(unsigned char y, unsigned char x);
Proiectul este functional, indeplinind toate cerintele initiale.
Realizarea unui astfel de proiect este o experienta interesanta. Conectarea LCD-ului este foarte anevoiasa. Deoarece nu am folosit suruburi, exista posibilitatea de departare a LCD-ului de trasee si se poate ajunge sa nu mai faca contact cu placa.