MICHELEPISANI.IT
 

Una demo del gioco anni '80 PAC-MAN su LCD

Caratteri Personalizzati su Display LCD 16x2 e LCD 20x4 con Arduino

Creare Caratteri Personalizzati su Display LCD 16x2 e LCD 20x4 con Arduino
Marzo 29
07:222017

Cercando di ottenere un deterrminato carattere da utilizzare in un display LCD per Arduino, in particolare quello che mi serviva era una sorta di cursore lampeggiante, mi sono fatto prendere la mano ed imbattendomi nei caratteri personalizzati per LCD ho deciso di volerne capire il funzionamento finendo per realizzare una simpatica demo del famoso gioco anni'80... PAC-MAN che gira su un display LCD 20x4 (20 caratteri per 4 righe). Premetto che lo stesso tipo di carattere personalizzato è ottenibile anche su display LCD 1602 (16 caratteri per 2 righe).

Senza scendere in particolari tecnicismi, per i quali esistomo già guide accurate in rete, la possibilità di creare dei caratteri personalizzati è data dalla caratteristica di alcuni display LCD di avere una porzione di memoria vuota, e pertanto scrivibile, di 64byte. Considerando che ogni carattere occupa 8 byte abbiamo la possibilità di utilizzare fino a 8 caratteri personalizzati (richiamabili con le posizioni da 0 a 7 come indicato poco sotto nell'esempio dell'articolo).

Ogni carattere è formato da 8 righe contenenti 5 pixel ciascuno. Per la realizzazione dei caratteri creativi esistono diversi tool online che agevolano il lavoro permettendo, tramite interfaccia, di accendere e spegnere i singoli pixel fino all'ottenimento del carattere desiderato e copiare il relativo codice generato automaticamente, personalmente ne ho provati due o tre e alla fine mi sono trovato bene con questo: http://maxpromer.github.io/LCD-Character-Creator/.

Con tale strumento ho creato i miei caratteri personalizzati per comporre la demo di PAC-MAN, nel caso specifico: 3 caratteri per lo scenario, 2 per il personaggio principale (uno orientato verso destra e uno verso sinistra), 2 per i fantasmi (uno per il fantasma cattivo e l'altro per il fantasma spaventato) e 2 per il cibo (uno per il cibo piccolo e l'altro per il cibo speciale). Penserete a questo punto che ho sbagliato a fare i miei conti perchè 3+2+2+2 fa 9 quando invece ho detto poco fa che il numero massimo di caratteri personalizzati utilizzabili è 8... Ebbene si, ho detto 'utilizzabili' di proposito, perchè di caratteri di questo tipo ne posso impostare quanti ne voglio, sarò io poi ad andare a definire in quale delle 8 posizioni dovranno trovarsi e considerando che i fantasmi in PAC-MAN possono essere o tutti cattivi o tutti spaventati (dopo che PAC-MAN ha mangiato il cibo speciale), vado a sostituire all'occorrenza l'uno e l'altro carattere a runtime nella funzione loop di Arduino.

Dopo questo doveroso preambolo ecco un breve video di 18 secondi che mostra la demo realizzata:

E finalmente la parte più interessante, lo sketch completo per Arduino (per il collegamento del display LCD ad Arduino ho utilizzato una scheda I2C ma chi non ne avesse una a disposizione può collegare semplicemente i cavi a ciascun pin tenendo presente di modificare lo sketch per quanto riguarda la libreria LiquidCrystal e le sue opportune inizializzazioni):

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

// caratteri pacman: personaggi e cibo

byte pacmanopenrightChar[8] = {
  B00000,
  B01110,
  B11101,
  B11111,
  B11000,
  B11111,
  B01110,
  B00000
};


byte pacmanopenleftChar[8] = {
  B00000,
  B01110,
  B10111,
  B11111,
  B00011,
  B11111,
  B01110,
  B00000
};

byte ghostChar[8] = {
  B00000,
  B01110,
  B10101,
  B11111,
  B11111,
  B11111,
  B10101,
  B00000
};

byte ghostfearChar[] = {
  B00000,
  B01110,
  B10101,
  B11111,
  B10001,
  B11111,
  B10101,
  B00000
};

byte eatsmallChar[8] = {
  B00000,
  B00000,
  B00000,
  B00110,
  B00110,
  B00000,
  B00000,
  B00000
};

byte eatbigChar[8] = {
  B00000,
  B00000,
  B01111,
  B01111,
  B01111,
  B01111,
  B00000,
  B00000
};

// percorso

byte horizontalChar[] = {
  B11111,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B11111
};

byte verticalrightChar[] = {
  B11111,
  B00001,
  B00001,
  B00001,
  B00001,
  B00001,
  B00001,
  B11111
};

byte verticalleftChar[] = {
  B11111,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B10000,
  B11111
};

void setup() {

  lcd.begin(20, 4);

  lcd.setCursor(5,0);
  lcd.write("PACMAN demo");
  lcd.setCursor(0,1);
  lcd.write("www.michelepisani.it");
  for (int i=0; i<20; i++) {
    lcd.setCursor(i,2);
    lcd.write(byte(5));
  }  
  lcd.setCursor(7,3);
  lcd.write(byte(0));
  lcd.setCursor(8,3);
  lcd.write(byte(3));
  lcd.setCursor(9,3);
  lcd.write(byte(3));
  lcd.setCursor(10,3);
  lcd.write(byte(3));
  lcd.setCursor(11,3);
  lcd.write(byte(3));
  lcd.setCursor(12,3);
  lcd.write(byte(2));

  // creazione caratteri speciali
  lcd.createChar(0, pacmanopenrightChar);
  lcd.createChar(1, pacmanopenleftChar);
  lcd.createChar(2, ghostChar);
  lcd.createChar(3, eatsmallChar);
  lcd.createChar(4, eatbigChar);
  lcd.createChar(5, horizontalChar);
  lcd.createChar(6, verticalrightChar);
  lcd.createChar(7, verticalleftChar);  
  
}

void loop() {

  delay(4000);

  // scenario
  for (int i=0; i<20; i++) {
    lcd.setCursor(i,0);
    lcd.write(byte(3));
    lcd.setCursor(i,1);
    lcd.write(byte(5));
    lcd.setCursor(i,2);
    lcd.write(byte(3));
    lcd.setCursor(i,3);
    lcd.write(byte(5));
  }

  lcd.setCursor(12,3);
  lcd.write(byte(6));
  lcd.setCursor(13,3);
  lcd.write(3);
  lcd.setCursor(14,3);
  lcd.write(byte(7));

  lcd.setCursor(10,1);
  lcd.write(byte(6));
  lcd.setCursor(11,1);
  lcd.write(3);
  lcd.setCursor(12,1);
  lcd.write(byte(7));

  lcd.setCursor(6,1);
  lcd.write(byte(6));
  lcd.setCursor(7,1);
  lcd.write(3);
  lcd.setCursor(8,1);
  lcd.write(byte(7));   

  // cibo speciale
  lcd.setCursor(0,0);
  lcd.write(byte(4));
  lcd.setCursor(19,2);
  lcd.write(byte(4));   

  // pacman
  lcd.setCursor(4,0);
  lcd.write(byte(0));

  // fantasma
  lcd.setCursor(8,0);
  lcd.write(byte(2));

  lcd.setCursor(2,2);
  lcd.write(byte(2));

  lcd.setCursor(15,2);
  lcd.write(byte(2));
  
  delay(500);
  // step 1
  lcd.setCursor(5,0);
  lcd.write(byte(0));
  lcd.setCursor(4,0);
  lcd.write(" ");
  lcd.setCursor(7,0);
  lcd.write(byte(2));
  lcd.setCursor(8,0);
  lcd.write(byte(3));

  lcd.setCursor(3,2);
  lcd.write(byte(2));
  lcd.setCursor(2,2);
  lcd.write(byte(3)); 

  lcd.setCursor(14,2);
  lcd.write(byte(2));
  lcd.setCursor(15,2);
  lcd.write(byte(3)); 

  delay(500);
  // step 2
  lcd.setCursor(4,0);
  lcd.write(byte(1));
  lcd.setCursor(5,0);
  lcd.write(" ");
  lcd.setCursor(6,0);
  lcd.write(byte(2));
  lcd.setCursor(7,0);
  lcd.write(byte(3));

  lcd.setCursor(4,2);
  lcd.write(byte(2));
  lcd.setCursor(3,2);
  lcd.write(byte(3));

  lcd.setCursor(13,2);
  lcd.write(byte(2));
  lcd.setCursor(14,2);
  lcd.write(byte(3)); 

  delay(500);
  // step 3
  lcd.setCursor(3,0);
  lcd.write(byte(1));
  lcd.setCursor(4,0);
  lcd.write(" ");
  lcd.setCursor(5,0);
  lcd.write(byte(2));
  lcd.setCursor(6,0);
  lcd.write(byte(3));

  lcd.setCursor(5,2);
  lcd.write(byte(2));
  lcd.setCursor(4,2);
  lcd.write(byte(3));

  lcd.setCursor(12,2);
  lcd.write(byte(2));
  lcd.setCursor(13,2);
  lcd.write(byte(3));   
 
  delay(500);
  // step 4
  lcd.setCursor(2,0);
  lcd.write(byte(1));
  lcd.setCursor(3,0);
  lcd.write(" ");
  lcd.setCursor(4,0);
  lcd.write(byte(2));
  lcd.setCursor(5,0);
  lcd.write(" ");

  lcd.setCursor(6,2);
  lcd.write(byte(2));
  lcd.setCursor(5,2);
  lcd.write(byte(3));

  lcd.setCursor(11,2);
  lcd.write(byte(2));
  lcd.setCursor(12,2);
  lcd.write(byte(3)); 

  delay(500);
  // step 5
  lcd.setCursor(1,0);
  lcd.write(byte(1));
  lcd.setCursor(2,0);
  lcd.write(" ");
  lcd.setCursor(3,0);
  lcd.write(byte(2));
  lcd.setCursor(4,0);
  lcd.write(" ");

  lcd.setCursor(7,2);
  lcd.write(byte(2));
  lcd.setCursor(6,2);
  lcd.write(byte(3));

  lcd.setCursor(11,1);
  lcd.write(byte(2));
  lcd.setCursor(11,2);
  lcd.write(byte(3));   

  delay(500);
  // step 6
  lcd.setCursor(0,0);
  lcd.write(byte(1));
  lcd.setCursor(1,0);
  lcd.write(" ");
  lcd.setCursor(2,0);
  lcd.write(byte(2));
  lcd.setCursor(3,0);
  lcd.write(" ");

  lcd.setCursor(7,1);
  lcd.write(byte(2));
  lcd.setCursor(7,2);
  lcd.write(byte(3));

  lcd.setCursor(11,0);
  lcd.write(byte(2));
  lcd.setCursor(11,1);
  lcd.write(byte(3)); 

  delay(250);
  lcd.noBacklight();
  lcd.createChar(2, ghostfearChar);
  delay(250);
  lcd.backlight();
  delay(250);
  lcd.noBacklight();
  delay(250);
  lcd.backlight();
  delay(250);
  lcd.noBacklight();
  delay(250);
  lcd.backlight();    

  delay(500);
  // step 7
  lcd.setCursor(1,0);
  lcd.write(byte(0));
  lcd.setCursor(0,0);
  lcd.write(" ");

  delay(500);
  // step 8
  lcd.setCursor(2,0);
  lcd.write(byte(0));
  lcd.setCursor(1,0);
  lcd.write(" ");
  lcd.setCursor(3,0);
  lcd.write(byte(2));

  lcd.setCursor(7,2);
  lcd.write(byte(2));
  lcd.setCursor(7,1);
  lcd.write(byte(3));

  lcd.setCursor(12,0);
  lcd.write(byte(2));
  lcd.setCursor(11,0);
  lcd.write(byte(3));    

  delay(500);
  // step 9
  lcd.setCursor(3,0);
  lcd.write(byte(0));
  lcd.setCursor(2,0);
  lcd.write(" ");

  delay(500);
  // step 10
  lcd.setCursor(4,0);
  lcd.write(byte(0));
  lcd.setCursor(3,0);
  lcd.write(" ");

  delay(500);
  // step 11
  lcd.setCursor(5,0);
  lcd.write(byte(0));
  lcd.setCursor(4,0);
  lcd.write(" "); 
 
  delay(500);
  // step 12
  lcd.setCursor(6,0);
  lcd.write(byte(0));
  lcd.setCursor(5,0);
  lcd.write(" ");

  lcd.setCursor(6,2);
  lcd.write(byte(2));
  lcd.setCursor(7,2);
  lcd.write(byte(3));

  lcd.setCursor(13,0);
  lcd.write(byte(2));
  lcd.setCursor(12,0);
  lcd.write(byte(3));

  delay(500);
  // step 13
  lcd.setCursor(7,0);
  lcd.write(byte(0));
  lcd.setCursor(6,0);
  lcd.write(" ");  

  delay(500);
  // step 14
  lcd.setCursor(8,0);
  lcd.write(byte(0));
  lcd.setCursor(7,0);
  lcd.write(" ");

  delay(500);
  // step 15
  lcd.setCursor(9,0);
  lcd.write(byte(0));
  lcd.setCursor(8,0);
  lcd.write(" ");

  delay(500);
  // step 16
  lcd.setCursor(10,0);
  lcd.write(byte(0));
  lcd.setCursor(9,0);
  lcd.write(" ");  

  delay(250);
  lcd.noBacklight();
  lcd.createChar(2, ghostChar);
  delay(250);
  lcd.backlight();
  delay(250);
  lcd.noBacklight();
  delay(250);
  lcd.backlight();
  delay(250);
  lcd.noBacklight();
  delay(250);
  lcd.backlight();  

  delay(500);
  // step 17
  lcd.setCursor(9,0);
  lcd.write(byte(1));
  lcd.setCursor(10,0);
  lcd.write(" ");

  lcd.setCursor(7,2);
  lcd.write(byte(2));
  lcd.setCursor(6,2);
  lcd.write(byte(3));

  lcd.setCursor(12,0);
  lcd.write(byte(2));
  lcd.setCursor(13,0);
  lcd.write(byte(3));

  delay(500);
  // step 18
  lcd.backlight();
  lcd.setCursor(8,0);
  lcd.write(byte(1));
  lcd.setCursor(9,0);
  lcd.write(" ");

  lcd.setCursor(7,1);
  lcd.write(byte(2));
  lcd.setCursor(7,2);
  lcd.write(byte(3));

  lcd.setCursor(11,0);
  lcd.write(byte(2));
  lcd.setCursor(12,0);
  lcd.write(byte(3));

  delay(500);
  // step 19
  lcd.setCursor(7,0);
  lcd.write(byte(2));
  lcd.setCursor(8,0);
  lcd.write(" ");

  lcd.setCursor(7,1);
  lcd.write(byte(3));

  lcd.setCursor(10,0);
  lcd.write(byte(2));
  lcd.setCursor(11,0);
  lcd.write(byte(3));

  lcd.setCursor(5,2);
  lcd.write("GAME OVER");

  delay(1000);
}

Sicuramente il codice è migliorabile ed integrabile ma credo che per coloro che sono finiti in questa pagina dopo una ricerca relativa alla realizzazione di un carattere personalizzato per display LCD possa in qualche modo tornare utile.

Tags
Condividi

Autore

Michele Pisani

Michele Pisani

Ho uno spiccato orientamento al problem-solving, se è troppo facile non mi diverto :)
Credo nella volontà e nel cambiamento perchè hanno fatto della mia passione il mio pane quotidiano.
Se devo descrivermi con una sola parola direi... "Concretezza", la mia stretta di mano è una garanzia.

0 Commenti

Non ci sono commenti

Nessuno ha ancora commentato questo articolo, fallo tu per primo!

Scrivi un Commento

Scrivi un Commento

Il tuo indirizzo email non sarà pubblicato.
I campi contrassegnati da un * sono obbligatori

Articoli e Argomenti correlati

Categorie popolari

Iscriviti alla mia newsletter

La tua e-mail con me sarà al sicuro.
Non fornirò mai le tue informazioni a nessuno!

Ultimi commenti

Michele Pisani

Ciao alessandro,
questa regex dovrebbe fare al caso tuo:

alessandro

ciao sto cercando una regex per numeri interi o decimali, positivi o negativi; la tua regex mi pare …

Michele Pisani

Ciao Nicola,
ti riferisci per caso alla situazione di gianluigi?
In tutti i casi la via …

Nicola

Ciao Michele, mi trovo nella medesima situazione in cui si trovava Gianluca un anno fa. Sei poi …