Autres articles / Other articles

Tirage de dés avec l'afficheur 8x8 pixels MAX7219

publication: 17 juin 2022 / mis à jour 11 juillet 2022

Read this page in english

 

Appel à collaboration

Vous développez des montages, simples ou complexes avec ESP32 et ESP32forth.

Partagez-les ici sur ce site.

ESP32forth ne pourra se développer qu'avec la collaboration active de toutes les bonnes volontés.

Vos montages peuvent aider d'autres développeurs.

Les montages des autres développeurs peuvent vous aider.

Pour proposer un article ou un montage, cliquez ici


 

Listing complet: Dice roll on 8x8 LED matrix

Raccordement de l'afficheur LED 8x8 MAX7219

L'afficheur reçoit les signaux SPI depuis la carte ESP32 vi les fils vert jaune et orange:

L'afficheur MAX7219 est alimenté en 5V depuis une alimentation externe indépendante de celle de la carte ESP32: fils rouge et noir. Cette alimentation figure sur notre montage en rouge. Elle partage le connecteur GND avec le connecteur GND de la carte ESP32: fil noir.

Gestion de l'afficheur MAX7219

On commence par paramétrer notre port SPI:

\ define VSPI pins 
19 constant VSPI_MISO 
23 constant VSPI_MOSI 
18 constant VSPI_SCLK 
05 constant VSPI_CS 
 
\ define SPI port frequency 
4000000 2 / constant SPI_FREQ 

La ligne 19 constant VSPI_MISO est nécessaire pour l'initialisation du port SPI. Mais la ligne MISO (Master In Slave Out) ne sera pas câblée.

Les valeurs des constantes correspondant aux ports GPIO exploités sur la carte ESP32: 05 constant VSPI_CS signifie que la ligne CS/SS est raccordée à la sortie GPIO5 sur la carte ESP32.

Puis on définit les mots permettant de communiquer avec l'afficheur LED 8x8 MAX7219:

\ select SPI vocabulary 
only FORTH  SPI also 
 
\ initialize HSPI port 
: init.VSPI ( -- ) 
    VSPI_CS OUTPUT pinMode 
    VSPI_SCLK VSPI_MISO VSPI_MOSI VSPI_CS SPI.begin 
    SPI_FREQ SPI.setFrequency 
  ; 
 
\ send two bytes to MAX7219 thru SPI port 
: MAX7219.send ( c1 c2 -- )  
    1 SPI.setHwCs  
    swap 8 lshift + SPI.write16  
    0 SPI.setHwCs  
  ;  
  
 
: MAX7219.normal   ( -- )  
    $0c $01 MAX7219.send  
  ; 
 
: MAX7219.shutdown ( -- )  
    $0c $00 MAX7219.send 
  ; 
 
: MAX7219.intensity  ( c -- )   
    $0a swap MAX7219.send  
  ; 
 
: MAX7219.decode     ( c -- )   
    $09 swap MAX7219.send  
  ; 
 
: MAX7219.scan.limit ( c -- )   
    $0b swap MAX7219.send  
  ; 
 
: MAX7219.set.digit  ( cbits cdigit -- )  
    swap MAX7219.send  
  ; 

Le mot le plus important est MAX7219.send. Il exploite une variante du mot SPI.write, le mot SPI.write16, lequel permet d'envoyer 2 octets successifs vers l'afficheur MAX7219.

Gestion des sprites à afficher

Notre afficheur MAX7218 est constitué de 8 rangées de LEDs, avec 8 LEDs par rangée. Ca tombe bien, un octet fait 8 bits. Il nous faut donc 8 octets pour afficher un motif 8x8, c'est à dire un sprite comme le nomment les développeurs de jeux vidéos.

\ *** Array with alphanumerics characters *** 
\ caracters ..0..9A..Z in array 
create DICE_SPRITES 
  $00 c, $00 c, $00 c, $18 c, $18 c, $00 c, $00 c, $00 c,   \ 1 
  $00 c, $00 c, $00 c, $c3 c, $c3 c, $00 c, $00 c, $00 c,   \ 2 
  $00 c, $c0 c, $c0 c, $18 c, $18 c, $03 c, $03 c, $00 c,   \ 3 
  $00 c, $c3 c, $c3 c, $00 c, $00 c, $c3 c, $c3 c, $00 c,   \ 4 
  $00 c, $c3 c, $c3 c, $18 c, $18 c, $c3 c, $c3 c, $00 c,   \ 5 
  $00 c, $db c, $db c, $00 c, $00 c, $db c, $db c, $00 c,   \ 6 

Ici, chaque ligne est un sprite. La première ligne est notre sprite 0, lequel est censé afficher le numéro 1 de notre dé.

Voici la manière dont est codée chaque sprite:

On dessine, dans un carré 8x8, notre sprite. Puis on pivote ce sprite et on encode chaque ligne en sa valeur hexadécimale: pixel éteint = 0, pixel allumé = 1. Dans notre figure, la seconde ligne sera 01111110 qui donne en hexadécimal la valeur $7e.

: getNum ( n ---)          \ get nth caracters from CHARACTERS table 
    8 * DICE_SPRITES + 
  ; 
 
\ Only for test encoding characters 
: tstChar ( n ---) 
    getNum { addr } 
    8 0 do 
        addr i + c@  dup 
        hex <# # # #> cr type ."  - " 
        binary <# # # # # # # # # #> type 
        decimal 
    loop 
  ; 

Ici, le mot tstChar nous permet de visualiser le bon codage de chaque sprite:

--> 2 tstChar
00 - 00000000
C0 - 11000000
C0 - 11000000
18 - 00011000
18 - 00011000
03 - 00000011
03 - 00000011
00 - 00000000 ok

Une fois chaque sprite défini, on peut en gérer l'affichage. C'est ce que fait le mot disp.char:

\ display a character from it address 
: disp.char { addr -- } 
    8 0 do 
        addr i + c@             \ fetch character byte 
        i 1+ MAX7219.set.digit  \ send byte at addr i 
    loop 
  ; 
 
: dice ( n -- ) 
    getNum disp.char 
  ; 

Le mot dice récupère le numéro du sprite à afficher, qui nous le rappelons sont numérotés de 0 à 5 (voir la vidéo).

On prépare ensuite la génération de nombres aléatoires. ESP32 intègre ce générateur aléatoire au travers du registre RNG_DATA_REG

\ Random number data  
$3FF75144 constant RNG_DATA_REG  
  
\ get 32 bits random b=number  
: rnd  ( -- x )  
    RNG_DATA_REG UL@  
  ;  
  
\ get random number in interval [0..n-1]  
: random ( n -- 0..n-1 )  
    rnd swap mod  
  ;  

Le mot random est exécuté en le faisant précédé de la valeur maximale à générer. Exemple: 6 random génère un nombre entier aléatoire dans l'intervalle [0..5].

Le mot before-dice génère une petite animation qui précède le tirage de dé:

: before-dice ( -- ) 
    5 0 do 
        8 0 do 
            $ff i 1+ MAX7219.set.digit j 10 * ms 
            $00 i 1+ MAX7219.set.digit 
        loop 
        8 0 do 
            $ff 7 i - 1+ MAX7219.set.digit j 10 * ms 
            $00 7 i - 1+ MAX7219.set.digit 
        loop 
    loop 
  ; 

Enfin, on construit le mot dice-roll qui est utilisé pour le tirage de dé (voir vidéo):

: MAX7219.init ( -- ) 
    init.VSPI 
    MAX7219.normal 
    $01 MAX7219.intensity 
    $07 MAX7219.scan.limit 
    $00 MAX7219.decode 
  ; 
 
MAX7219.init 
 
: dice-roll ( -- ) 
    before-dice 
    6 random dice 
  ; 

En vidéo, les étapes de contrôle du bon affichage des sprites, suivi de trois tirages de dé:

En conclusion, ce premier exemple d'utilisation du port SPI montre encore une fois la grande souplesse du langage FORTH.


Legal: site web personnel sans commerce / personal site without seling