Forza4 PIC microcontroller
-
Upload
davide-mercanti -
Category
Self Improvement
-
view
254 -
download
0
description
Transcript of Forza4 PIC microcontroller
(PIC16LF877A, 8x8 BiColor DotLedMatrix)
In un mondo di iPad, iPhone, ixxxx, tutto questo sembra ancronistico però... ecco la trasposizione elettronica del
famoso gioco Forza4.
Descrizione
Tutto ebbe inizio nel momento in cui trovai in una bancarella un display a matrice di LED 8x8
bicolore ad un prezzo irrisorio (3.50EU): questo era sufficiente per creare lo schema di gioco
che è in realtà 7 colonne per 6 righe.
L'idea è sempre quella di usare componenti da costi contenuti (o free samples) e, anche in
questo caso penso di aver ottenuto un buon compromesso.
Il PIC utilizzato per esempio è un comune P16F877A, tanti PIN a disposizione, vari mA di
corrente disponibile
in ogni PIN...
Per risparmiare spazio, un ULN2803 per assorbire la corrente dei LED mi è sembrata la
soluzione più efficace, nulla vieta di sostituirlo con transistors equivalenti;
tasti di recupero per la tastiera di comando (3 tasti comando : dx, sx, giu, 1 tasto power)... ad
onor del vero avevo pensato ad una versione con accellerometro a due assi, magari nella
versione 2012!
un paio di transistor per l'autopower off, un semplice diodo per un rudimentale controllo livello
carica batteria e una cella Li-Ion recuperata da una vecchia batteria per notebook.
Anche la scelta del liguaggio/compilatore non è stata semplice. Avendo precedentemente
realizzato in VB6 una versione per PC dell'omonimo gioco, pensavo di poter convertire in modo
"semplice" il codice scritto in Visual Basic. In realtà 2 sono stati i problemi principali
1) Implementazione dell'algoritmo senza ricorsione
2) Stack limitato a 8 chiamate (nel P16xxxx)
ALGORITMO IMPLEMENTATO
Anteprima http://www.grix.it/editor/FCK26/editor/fckeditor.html?InstanceName=...
1 di 12 21/04/11 10.10
Questo come altri giochi (tris, scacchi, etc...) sono di solito risolti usando diversi algoritmi, ma
tutti ricorsivi, dato che tendono a calcolare tutte le possibili mosse dei giocatori eseguendo la
costruzione di un "albero" di mosse possibili a cui viene assegnato un "peso".
L'algoritmo usato in questa implementazione è riconducibile ad un algoritmo MiniMax modificato
comunemente
chiamato NegaMax. Se vediamo il sorgente VB6, notiamo come la funzione Pensa :
- ha il compito di analizzare lo schema di gioco , eseguendo una mossa "virtuale",
- controllare un eventuale vittoria (ed uscire)
- richiamare se stessa se il livello lo consente, moltiplicando per -1 il risultato ottenuto
(da qui il nome NEGAmax)
Questo "livello" di pronfodità di controllo determina la bontà della giocata, ma anche un aumento
dei tempi di esecuzione della mossa. Nei PC di oggi non ci sono particolari problemi di stack,
quindi il livello di chiamate alle procedure lo possiamo considerare "infinito", nei PIC16 però il
numero di chiamate non può superare 8 livelli di stack.
Un rapido calcolo porta a definire un numero fisso per il livello, impostato in fase di
progettazione del
codice:
main 1° livello di stack
Pensa3 2° livello di stack
Display_PutPixel 3° livello di stack (per gestione del display)
mettiPezzo 3° livello di stack
Win4 3° livello di stack
togliPezzo 3° livello di stack
nextcolore 3° livello di stack
Pensa2 4° livello di stack
Pensa1 5° livello di stack
Pensa0 6° livello di stack
Da questo elenco si capisce che è difficile riuscire a chiamare per più di 5 volte la procedura
"pensa", dato che al suo interno sono comunque richiamate altre funzioni di supporto.
Per esempio la stessa Win4 è stata riscritta in un unico corpo (differentemente dal programma
VB6, che richiamava diverse funzioni:Win4Diag,Win4Oriz,Win4Vert) proprio per evitare di
sovraaffollare il ristretto stack a disposizione. Anche in questo caso la chiamata a 4 livelli di
"Pensa" è un giusto compromesso tra spazio a disposizione ,velocità della mossa e bontà della
giocata (che consente una visione sucessiva alla possibile mossa dell'avversario).
Proprio su questo punto inziano alcune note dolenti, visto che il quarzo utilizzato è un
"conservativo" 8Mhz: nel calcolare tutte le possibili mosse il tempo impiegato (a causa anche
della procedura di interrupt) non è dei più rapidi (qualche mio amico sembrava proprio scocciato
da questa attesa!) e lo possiamo rilevare in circa 20 secondi: ma perchè quando si gioca con un
avversario in carne ed ossa, si avverte meno questo senso di lentezza ???
Va da sè che inserendo un quarzo da 20 Mhz i tempi si dimezzano ampiamente !
Nell'assegnare un valore alla mossa, viene usata una tabella di pesi prefissata, che
"caratterizza" in un qualche modo la "personalità" del PIC, portandolo a preferire, per esempio,
le colonne centrali; volendo modificare questo comportamento dovrete assegnare dei valori
differenti all'array "Weight".
Anteprima http://www.grix.it/editor/FCK26/editor/fckeditor.html?InstanceName=...
2 di 12 21/04/11 10.10
Risolto il problema della ricorsione (di fatto è stata replicata 4 volte la funzione Pensa) e dello
stack (limitando il numero di procedure nidificate),si passa alla scelta del compilatore.
Il BASIC di OshonSoft sembrava poter gestire matrici e la ricorsione, a patto di usare variabili
globali; in realtà in fase di simulazione però, trovandomi valori sballati nelle variabili, sono stato
costretto a passare su MikroC 7.0 di Mikroelektronica; quest'ultimo tuttavia non gestendo
agevolmente le matrici mi ha costretto a rappresentare la matrice di gioco in un array di 6x7=42
elementi "Board".
Due livelli di gioco consentono di semplificare/velocizzare la mossa del PIC: nella modalità facile
si prevede la possibilità di generare "MaxMosseCasuali" (5) mosse casuali durante la sessione
di gioco, mentre nella difficile solo una, la prima.
CONSIDERAZIONI ELETTRICHE
Ci troviamo a dover gestire 8x8x2=128 LED con meno di 40 pin (in realtà ne avanzano) e con
una cella al litio da circa 2200mAh.
Innanzi tutto il PIC è del tipo funzionante a bassa tensione, questo ci permette "virtualmente" di
funzionare fino 2V... questo però non è possibile dato che la cella al Litio non dovrebbe
scendere sotto ai 3V: meglio così, daremo un segnale di batteria scarica al momento opportuno.
E' ovvio che la gestione dei LED deve essere multiplexata: abbiamo 8 righe, la persistenza sulla
retina del nostro occhio la inganniamo se scendiamo sotto i 20ms... 20ms:8=>2.5 ms.
Va da sè che la procedura di interrupt che gestirà la scansione delle righe dovrà essere inferiore
ai 2.5 ms. Per avere meno senso si sfarfallio è stato scelto un valore di circa 1.7ms:
' Timer0 Registers:' Prescaler=1:32; TMR0 Preset=150; Freq=589,62264Hz; Period=1,696 msOPTION_REG.T0CS = 0 ' bit 5 TMR0 Clock Source Select bit:0=Internal Clock (CLKO) /1=Transition on T0CKI pinOPTION_REG.T0SE = 0 ' bit 4 TMR0 Source Edge Select bit: 0=low/high / 1=high/lowOPTION_REG.PSA = 0 ' bit 3 Prescaler Assignment bit: 0=Prescaler is assigned tothe Timer0OPTION_REG.PS2 = 1 ' bits 2-0 PS2:PS0: Prescaler Rate Select bitsOPTION_REG.PS1 = 0OPTION_REG.PS0 = 0TMR0 = 150 ' preset for timer register
Per ogni riga assorbiremo la corrente (tramite l'ULN2803) dei led alimentati direttamente dalla
Anteprima http://www.grix.it/editor/FCK26/editor/fckeditor.html?InstanceName=...
3 di 12 21/04/11 10.10
porta del microcontrollore.
Avendo la possibilità di avere Led multicolore dovremo gestire una porta per gli 8 led di colore
rosso e una porta per gli 8 led di colore verde: accendendo entrambi i led di uno stesso "pixel"
otterremo il colore giallo.
Nel datasheet del Display si capisce che alimentando con una corrente di circa 20mA i LED
abbiamo una caduta di tensione di circa 2.1V (sia per il rosso che per il verde), a questa
dobbiamo aggiungere la tensione dell'ULN2803 (circa 0.85V): avendo 2.95V possiamo calcolare
il valore della resistenza da inserire in serie alla "riga" dei LED anche se a dire il vero sarebbe
meglio usare una resistenza per ogni colonna (quindi 8+8=16 resistenze in totale).
Fissando a 3.3V la minima tensione della batteria otteniamo
(3.3V-2.95V)/20mA=17.5 ohm
In realtà non essendo contento della luminosità ottenuta dai calcoli, ho "leggermente"
aumentato la corrente (stressando forse le porte del PIC) fissando una rLed di 6.8ohm :
considerato l'utilizzo non continuativo e multiplexato il tutto viene ampiamente ridimensionato e
digerito dal PIC (l'utilizzo in ambienti molto luminosi o all'esterno in presenza di giornata
soleggiata è praticamente impossibile!)
Fate attenzione però alla potenza dissipata: se accendiamo tutti i LED abbiamo 20mA x 8 =
160mA P=RxIxI=17x0.16x0.16=0.43 W, anche se multiplexata nel caso peggiore potremmo
trovarci con tutti i led sempre accesi, quindi direi di scegliere un accomodante 1/2 Watt.
la gestione dell'autopower off avviene con una coppia di normali transistor NPN-PNP. La
corrente gestita è di 100mA e di 200mA di picco e quindi viene (abbastanza) rispettata: dopo
circa un minuto e mezzo di inattività da parte dell'utente il gioco si spegne (senza salvare !!)
Anteprima http://www.grix.it/editor/FCK26/editor/fckeditor.html?InstanceName=...
4 di 12 21/04/11 10.10
il calcolo del livello di tensione delle cella viene valutato tramite l'ADC leggendo la tensione
presente sul diodo 1N4148. La tensione in questione possiamo considerarla "fissa" ed
approssimarla ad un valore di 0.75V. Quello che varia nel nostro caso è la VREF , essendo
collegata alla batteria, che rifletterà direttamente le sue variazioni.
Quindi nel caso di batteria carica
4.1 : 0.75 = 1024 : x x= (0.75*1024)/4.1 = 187
nel caso di batteria scarica
3 : 0.75= 1024 : x x= (0.75*1024)/3=256
Da qui si capisce come dopo qualche prova e una timida taratura possiamo avvisare l'utente sul
livello di carica della batteria gestendo il pixel in posizione 7,8:
pixel spento : carica normale
pixel verde : batteria in esaurimento
pixdel rosso : ricaricare batteria
verificando il valore letto dall ADC (AN0) e confrontandolo con dei valori di finestra fissi.
IL PROGRAMMA
Ecco la rappresentazione in memoria dello schema di gioco (array "Board")
Colonna 1 7 ---------------------- Riga 6 | 35 36 37 38 39 40 41 | 27 29 30 31 32 33 34 | 21 22 23 24 25 26 27 | 14 15 16 17 18 19 20 | 07 08 09 10 11 12 13Riga 1 | 00 01 02 03 04 05 06
Due funzioni specifiche consentono di passare dalla forma matriciale a quella vettoriale
degli indici, in particolare:
iTOrc => converte l'indice di Board in una coppia di valori indice riga, indice colonna
rcTOi => converte una coppia di indici riga,colonna in un indice del vettore Board
Come già accennato il vettore "Weight" contiene il "carattere" dell'algoritmo ossia un peso per
ogni casella dello schema: questo viene usato dalla funzione "Pensa".
In una futura versione si potrebbero gestire diversi array Weight prelevandoli magari dalla
Eeprom del PIC in modo da simulare un comportamento differente tra una sessione di gioco e
l'altra.
Un funzione importante è quella che verifica la vittoria di un certo colore "Win4". Questa
controlla prima le posizioni orizzontali, poi le verticali quindi le due diagonali seguendo uno
schema fisso di casella di partenza specificati in W4Oriz,W4Vert,W4Diag1,W4Diag2.
Questi contengono gli indici di Board da cui iniziare a contare le pedine dello stesso colore.
Per esempio i primi valori di "W4Oriz" sono
Anteprima http://www.grix.it/editor/FCK26/editor/fckeditor.html?InstanceName=...
5 di 12 21/04/11 10.10
0,1,2,3,7,...
significa che la procedura verificherà le 4 caselle adiacenti partendo dalla 0,1,2,3... la posizione
4 non dovrà essere verificata poichè nel senso orizzontale non è possibile mettere più di 3
pedine in fila; pertanto la prossima casella di partenza che dovrà essere verificata risulta essere
la 7.
Come si calcolano le caselle adiacenti ?
Beh dallo schemino si capisce che
per le orizzontali - => basta sommare 1 all'indice precedente
per le verticali | => basta sommare 7 all'indice precedente
per la diagonale / => basta sommare 8 all'indice precedente
per la diagonale => basta sommare 6 all'indice precedente
Due funzioni speciali permettono di
1) ricominciare una nuova partita
2) salvare la situazione corrente
Per ricominciare ci si deve spostare con il tasto sx sulla colonna 1 e tenendo premuto il tasto sx,
premere contemporaneamente il tasto dx (attenzione questo annulla anche l'ultimo salvataggio).
In modo analogo per salvare una partita ci si deve spostare con il tasto dx sulla colonna 7 e
tenendo premuto il tasto dx, premere contemporaneamente il tasto sx: dopo circa mezzo
secondo l'array "Board" viene salvato nella Eeprom del PIC (posizioni da 0 a 41) (vedere
"SaveBoard" e "LoadBoard").
All'accensione successiva verrà ripristinata la situazione salvata.
UTILIZZO DEL DISPLAY
Come già accennato il display viene gestito tramite la multiplazione. Le righe sono attivate
sequenzialmente una dopo l'altra (vedi "interrupt") in modo automatico dal PIC ogni 1.7ms: in
questa fase sono attivati i Led che sono scritti nella "memoria video". La matrice (8x8)
"Display" viene infatti utilizzata come una rudimentale memoria video di 64 byte: ogni byte
rappresenta un pixel della matrice e può assumere i valori:
0 : led spenti
1 : led rosso ON
2 : led verde ON
3 : led rosso ON & led verde ON
Per semplificare l'accesso al display, apposite funzioni permettono di accedere alla memoria
video:
Display_PutPixel : accende un pixel alla posizione riga,colonna
Display_CLS : accende tutti i pixel della matrice con un certo colore
Display_bar : mostra la barra di scelta livello
Display_SceltaLivello : mostra la schemata di scelta livello
SCHEMA ELETTRICO
Anteprima http://www.grix.it/editor/FCK26/editor/fckeditor.html?InstanceName=...
6 di 12 21/04/11 10.10
In realtà sono 2 gli schemi elettrici che generano 2 board differenti: una per la parte MCU e
l'altra per la parte display: questa è stata più una necessità iniziale di sviluppo.
Schema display
Prevede la connessione con la parte MCU con 8 fili per i led rossi, 8 fili per i led verdi e 9 per la
gestione delle righe (8+1massa). La pedinatura del display LED 8x8 presente nel PDF risulta
essere:
1 Cat Row 8 Cat Row 1 24
2 Cat Row 7 Cat Row 2 23
3 Cat Row 6 Cat Row 3 22
4 Cat Row 5 Cat Row 4 21
5 Anode Col1 Gr Anode Col8 Rd 20
6 Anode Col2 Gr Anode Col8 Rd 19
7 Anode Col3 Gr Anode Col8 Rd 18
8 Anode Col4 Gr Anode Col8 Rd 17
9 Anode Col5 Gr Anode Col8 Rd 16
10 Anode Col6 Gr Anode Col8 Rd 15
11 Anode Col7 Gr Anode Col8 Rd 14
12 Anode Col8 Gr Anode Col8 Rd 13
Le resistenze utilizzate sono da 6.8 Ohm 1/2 watt.
Anteprima http://www.grix.it/editor/FCK26/editor/fckeditor.html?InstanceName=...
7 di 12 21/04/11 10.10
Schema MCU
Sono previsti una serie di connettori (strip line femmina) su cui innestare i 3 cavi "flat"
autocostruiti
(uno per i led rossi, uno per i verdi , uno per le righe).
Anche il quarzo è innestato su un connettore femmina, così da poterlo cambiare per velocizzare
le operazioni di gioco.
Nessun componente è critico.
Le resistenze sono da 1/4 di watt.
Schema elettrico eagle.
Anteprima http://www.grix.it/editor/FCK26/editor/fckeditor.html?InstanceName=...
8 di 12 21/04/11 10.10
Schema LochMaster.
Realizzazione su millefori. Lato componenti
Anteprima http://www.grix.it/editor/FCK26/editor/fckeditor.html?InstanceName=...
9 di 12 21/04/11 10.10
Realizzazione su millefori. Lato Rame
Anteprima http://www.grix.it/editor/FCK26/editor/fckeditor.html?InstanceName=...
10 di 12 21/04/11 10.10
Realizzazione Cablaggio
REALIZZAZIONE PRATICA
Il tutto è stato inserito in una scatola di derivazione Gewiss 10x15 abbassata di circa 2,5 cm.
Sulla parte superiore è stato praticato un buco rettangolare della dimensione del display e sono
stati "appoggiati" i tasti dx,giu,sx, mentre il tasto power è posto lateralmente.
Tagliando la scatola ho dovuto inserire dei tasselli (D=4mm) nei fori dei 4 angoli per poter
riagganciare
il coperchio.
La batteria risulta quindi inserita all'interno e chiusa dalle 4 viti: per ricaricarla si deve
necessariamente aprire il coperchio svitandole.
Anteprima http://www.grix.it/editor/FCK26/editor/fckeditor.html?InstanceName=...
11 di 12 21/04/11 10.10
BUON DIVERTIMENTO !
Sono a disposizione
gli schemi,
i sorgenti,
il programma Win32 del gioco da me sviluppato in VB6.
Anteprima http://www.grix.it/editor/FCK26/editor/fckeditor.html?InstanceName=...
12 di 12 21/04/11 10.10