UNO-Q - utiliser un tableau ASCII pour la fonction matrixWrite()

Bonjour à tous,

Voici un moyen "simple" pour allumer les leds de la UNO-Q. Je vous propose un tableau ASCII.

Comment encoder un dessin ASCII de LED 13×8 en 4 mots de 32 bits pour matrixWrite() (UNO Q) :

La matrice LED de la UNO Q possède 13 colonnes × 8 lignes = 104 LEDs.
La fonction interne matrixWrite() attend un buffer de 4 mots de 32 bits, soit 128 bits, dont 104 uniquement sont utilisés pour les LEDs.

1/ Structure des 4 mots (128 bits) pour la fonction matrixWrite() :
Voici la répartition des bits dans les 4 mots de 32 bits :

mot 0 – bits 0 à 31 :

bit 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

mot 1 – bits 32 à 63 :

bit 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
bit 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32

mot 2 – bits 64 à 95 :

bit 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80
bit 79 78 77 76 75 74 73 72 71 70 69 68 67 66 65 64

mot 3 – bits 96 à 127 :

bit 127 126 125 124 123 122 121 120 119 118 117 116 115 114 113 112
bit 111 110 109 108 107 106 105 104 103 102 101 100 99 98 97 96

Dans le mot 3 les bits 104 à 127 sont inutilisés.

2/ Correspondance physique LED/ index :
Les LEDs sont numérotées ligne par ligne :

  • voici les formules utiles à la conversion :
    index = y * 13 + x;
    mot = index / 32;
    bit = index % 32;

3/ Exemple : allumer uniquement la LED 46

LED 46 :

index = 46
x = 46 % 13 = 7
y = 46 / 13 = 3
mot = index / 32 = 1
bit = index % 32 = 14

LED 46 = bit 14 du mot 1

Voici le tableau ASCII qui représente cette LED :

const char *tabLed[8] = {
    "_____________",  // y=0
    "_____________",  // y=1
    "_____________",  // y=2
    "_______#_____",  // y=3, x=7
    "_____________",  // y=4
    "_____________",  // y=5
    "_____________",  // y=6
    "_____________"   // y=7
};

4/ Fonction d’encodage ASCII : 4 mots de 32 bits :

Cette fonction transforme un dessin ASCII (8×13, avec "_" = 0 et "#" = 1) en un buffer de 4 × 32 bits compatible avec matrixWrite() :

void encodeAsciiToFrame(const char *ascii[8], uint32_t out[4]) {

out[0] = out[1] = out[2] = out[3] = 0; // mise à 0 des 128 bits

for (int y = 0; y < MATRIX_HEIGHT; y++) {
    for (int x = 0; x < MATRIX_WIDTH; x++) {

        char c = ascii[y][x];
        if (c != '#') continue;  // '_' = éteint - reste à ‘0´

        int index = y * MATRIX_WIDTH + x;   // index 0 à 103
        int mot   = index / 32;             // mot 0 à 3
        int bit   = index % 32;             // bit 0 à 31

        out[mot] |= (1u << bit); // mise à 1 du bit correspondant
    }
  }
}

5/ Programme complet :

extern "C" void matrixWrite(const uint32_t *buf); // on importe la fonction C : matrixWrite()
extern "C" void matrixBegin(); // on importe la fonction C : matrixBegin()

const int MATRIX_WIDTH  = 13; // (13 colones de leds)
const int MATRIX_HEIGHT = 8; // (8 lignes de leds )

const char *tabLed[8] = { // tableau ASCII qui sera ransformé
    "_____________",  // y=0
    "_____________",  // y=1
    "_____________",  // y=2
    "_______#_____",  // y=3, x=7
    "_____________",  // y=4
    "_____________",  // y=5
    "_____________",  // y=6
    "_____________"   // y=7
};



uint32_t tabMatrix[4]; // Tableau 


void encodeAsciiToFrame(const char *ascii[8], uint32_t out[4]) {

    out[0] = out[1] = out[2] = out[3] = 0; // on met tous les bits à 0

    for (int y = 0; y < MATRIX_HEIGHT; y++) {
        for (int x = 0; x < MATRIX_WIDTH; x++) {

            char c = ascii[y][x];
            if (c != '#') continue;  // '_' = éteint - reste à ‘0’

            int index = y * MATRIX_WIDTH + x;   // 0..103 - on obtient l'index simplement en multipliant le nombre de la colonne par le nombre de colones du tableau ascii plus le nombre de la ligne
            int mot  = index / 32;             // 0..3 - on détermine dans quel mot en divisant l'index par 32
            int bit   = index % 32;             // 0..31 - on détermine le numéro de bit dans le mot avec le modulo 32 de l’index

            out[mot] |= (1u << bit); // on met le bit à 1 dans les 32 bits (0 à 31) en effectuant un décalage à gauche du 1 non signé
        }
    }
}

void setup() {
    matrixBegin();
    encodeAsciiToFrame(tabLed, tabMatrix);
    matrixWrite(tabMatrix);
}

void loop() {
}


–> Du coup il suffit de transformer les “_” par des “#” dans le tableau ASCII pour créer votre dessin de leds.

bonne journée à tous

PS : Bon j’espère avoir été clair mais je n’en suis pas certain :blush: l’algorithme provient de ChatGPT

C’est clair mais c’est cher :slight_smile:
Chaque caractère ascii prend 1 octet….

Autant utiliser la représentation binaire d’un uint16_t pour lesquels ont utilise que les 13 premiers bits avec des 1 et des 0 pour faire le dessin non ?

uint16_t bitmap[8] = {
  0b0000000000000,
  0b0000000000000,
  0b0000000000000,
  0b0000001000000,
  0b0000000000000,
  0b0000000000000,
  0b0000000000000,
  0b0000000000000
};

La fonction de mapping se fait par des copies, masquage et décalage

Bonjour @J-M-L

C’est vrai et dans ce cas il faut changer un peu le code de sorte que les bits par lignes soient inversés :
int bitInMot = 12 - x;

extern "C" void matrixWrite(const uint32_t *buf);
extern "C" void matrixBegin();

const int MATRIX_WIDTH  = 13;
const int MATRIX_HEIGHT = 8;

// Mapping UNO Q : bit 12 = x=0 (gauche), bit 0 = x=12 (droite)
uint16_t bitmap[8] = {
    0b1000000000000,  // y=0  x=0
    0b0000000000000,
    0b0000000000000,
    0b0000010000000,  // y=3  x=7
    0b0000000000000,
    0b0000000000000,
    0b0000000000000,
    0b0000000000001   // y=7  x=12
};

uint32_t tabMatrix[4];

void encodeBitmapToFrame(const uint16_t bmp[8], uint32_t out[4]) {

    out[0] = out[1] = out[2] = out[3] = 0;

    for (int y = 0; y < MATRIX_HEIGHT; y++) {
        for (int x = 0; x < MATRIX_WIDTH; x++) {

            // IMPORTANT : inversion du bit
            int bitInMot = 12 - x;

            if ((bmp[y] & (1u << bitInMot)) == 0)
                continue;

            int index = y * MATRIX_WIDTH + x;
            int mot   = index / 32;
            int bit   = index % 32;

            out[mot] |= (1u << bit);
        }
    }
}

void setup() {
    matrixBegin();
    encodeBitmapToFrame(bitmap, tabMatrix);
    matrixWrite(tabMatrix);
}

void loop() {}

Au final c’était une manière de visualiser plus simplement pour un débutant mais c’est vrai que c’est plus cher quoique sur la UNO Q ce n’est pas la mémoire qui manque.

Mais vous avez entièrement raison !

Bonne journée à vous.

Effectivement, l'ordre des bits est inversé, c'est "pénible" pour refabriquer les 4 uint32_t en sortie

Bonjour @J-M-L

Non c’est la matrice de led qui est connectée physiquement de cette façon mais ça vous le savez parfaitement :

y = 0 : 0 1 2 3 4 5 6 7 8 9 10 11 12
y = 1 : 13 14 15 16 17 18 19 20 21 22 23 24 25
y = 2 : 26 27 28 29 30 31 32 33 34 35 36 37 38
y = 3 : 39 40 41 42 43 44 45 46 47 48 49 50 51
y = 4 : 52 53 54 55 56 57 58 59 60 61 62 63 64
y = 5 : 65 66 67 68 69 70 71 72 73 74 75 76 77
y = 6 : 78 79 80 81 82 83 84 85 86 87 88 89 90
y = 7 : 91 92 93 94 95 96 97 98 99 100 101 102 103

et comme on lit les bits de gauche à droite

uint16_t bitmap[8] = {
  0b0000000000000,
  0b0000000000000,
  0b0000000000000,
  0b0000001000000,
  0b0000000000000,
  0b0000000000000,
  0b0000000000000,
  0b0000000000000
};

Ceci étant dans les deux cas le code aurait pu être :

extern "C" void matrixWrite(const uint32_t *buf); // on importe la fonction C : matrixWrite()
extern "C" void matrixBegin(); // on importe la fonction C : matrixBegin()

const int MATRIX_WIDTH  = 13; // (13 colones de leds)
const int MATRIX_HEIGHT = 8; // (8 lignes de leds )

const char *tabLed[8] = { // tableau ASCII qui sera ransformé
    "#___________#",  // y=0
    "_____________",  // y=1
    "_____________",  // y=2
    "_______#_____",  // y=3 
    "_____________",  // y=4
    "_____________",  // y=5
    "_____________",  // y=6
    "_____________"   // y=7
};



uint32_t tabMatrix[4]; // Tableau 


void encodeAsciiToFrame(const char *ascii[8], uint32_t out[4]) {

    out[0] = out[1] = out[2] = out[3] = 0; // on met tous les bits à 0

    for (int y = 0; y < MATRIX_HEIGHT; y++) {
        for (int x = 0; x < MATRIX_WIDTH; x++) {

            char c = ascii[y][x];
            if (c == '#') { 

            int index = y * MATRIX_WIDTH + x;   
            int mot  = index / 32;            
            int bit   = index % 32;             

            out[mot] |= (1u << bit); // on met le bit à 1 dans les 32 bits (0 à 31) en effectuant un décalage à gauche du 1 non signé
              }
        }
    }
}

void setup() {
    matrixBegin();
    encodeAsciiToFrame(tabLed, tabMatrix);
    matrixWrite(tabMatrix);
}

void loop() {
}

ou :

extern "C" void matrixWrite(const uint32_t *buf);
extern "C" void matrixBegin();

const int MATRIX_WIDTH  = 13;
const int MATRIX_HEIGHT = 8;

// Mapping UNO Q : bit 12 = x=0 (gauche), bit 0 = x=12 (droite)
uint16_t bitmap[8] = {
    0b1000000000000, 
    0b0000000000000,
    0b0000000000000,
    0b0000010000000,  
    0b0000000000000,
    0b0000000000000,
    0b0001000000000,
    0b0000000000001   
};

uint32_t tabMatrix[4];

void encodeBitmapToFrame(const uint16_t bmp[8], uint32_t out[4]) {

    out[0] = out[1] = out[2] = out[3] = 0;

    for (int y = 0; y < MATRIX_HEIGHT; y++) {
        for (int x = 0; x < MATRIX_WIDTH; x++) {

            // IMPORTANT : inversion du bit
            int bitInMot = 12 - x;

         
            if (bmp[y] & (1u << bitInMot)) {
               

            int index = y * MATRIX_WIDTH + x;
            int mot   = index / 32;
            int bit   = index % 32;

            out[mot] |= (1u << bit);
            }
        }
    }
}

void setup() {
    matrixBegin();
    encodeBitmapToFrame(bitmap, tabMatrix);
    matrixWrite(tabMatrix);
}

void loop() {}

Le code est plus lisible sans l’instruction continue;

bonne journée.

oui c'est pour ça que je dis que c'est pénible :slight_smile:

on a nos pixels comme la partie en vert et il faut bâtir les uint32_t (la vue du bas / couleurs) en lisant "à l'envers" et des bouts de uint16_t

Pas mal vos dessins, on voit que vous êtes sous MacOS :+1:

4 fois 32 bits avec un MCU 32 bits, c’est en adéquation avec le matériel : registres, ALU, bus internes, tout fonctionne nativement sur 32 bits. Donc transmettre un buffer de 4 × 32 bits est le format le plus efficace.

Du moins c’est ce que je comprends.

Bon dimanche

Oui - l’ordre matériel aurait juste pu être plus adapté à la représentation little endian et la vue graphique

Oui effectivement d’ailleurs ça me donne une idée, je reviendrai ici dans quelques jours avec peut-être un petit programme python :wink:

Des cases avec un fond coloré et des caractéres dedans ?

c'est super high tech ! :slight_smile:
y'a que sur Mac qu'on peut faire cela, c'est sûr !

cela dit, on peut s'amuser
Sans titre 2

"Avec ChatGPT et MacOS, je n'ai besoin de personne!"

Bonjour @J-M-L

Sympa votre animation. J’ai une petite ancienneté sur le forum et tout le monde a remarqué depuis longtemps la qualité de vos représentations graphiques ; ma remarque était un clin d’oeil pour cette capacité que vous avez à exploiter votre matériel que j’utilise également. De plus, il se trouve que vos tableaux en #7 montrent que vous avez parfaitement compris (comme d’habitude) mais en plus, ils mettent en valeur ce qui motive ce fil de discussion. D’ailleurs ceux qui souhaitent discuter sur les sujets qui concernent MacOS ou ChatGPT, peuvent ouvrir une discussion (encore une) dans le bar mais s’il vous plait pas ici - merci !

—

Pour en revenir à nos moutons et comme la création manuelle des tableaux uint32_t[4] à transmettre à matrixWrite() n’est pas très pratique, j’ai déposé un petit éditeur de matrice 13×8 en Python (Tkinter).

  • Il permet de :
    cliquer sur les LEDs pour dessiner un motif, voir le rendu directement, générer automatiquement les 4 mots de 32 bits attendus par matrixWrite(), copier/coller le code C++ directement dans votre sketch. Voici le lien du dépôt GitHub contenant le script Python et la documentation :

Bonne journée à tous.

Cool :+1:

Un autre challenge : rajoutez à votre dépôt GitHub une page web dynamique qui utilise JavaScript et qui fait la même chose sans avoir à installer quoi que ce soit sur son ordi.

Oui mais je ne connais pas suffisamment le langage pour le faire.

Et puis je travaille toujours sur SWIFT, c’est déjà énorme pour moi car il y a tellement à apprendre.

Je pourrai demander à ChatGPT de le faire pour moi mais comme je ne connais pas, je ne le fais pas.

Donc pour l’instant j’en suis bien incapable.

Bonne journée.

OK - c'est une bonne raison. Oui Swift et toutes les APIs Apple c'est un monde à découvrir... De quoi vous occuper des années !

Bonjour @J-M-L

Du coup j’ai demandé à ChatGPT de me fournir le code. L’IA me l’a fourni instantanément. J’ai activé Github pages et voilà le résultat :

C’est ChatGPT qui a généré entièrement le code. Il me reste quand même à y jeter un oeil.

Bonne journée.

PS : Du coup ce n’est pas complètement incompréhensible même si je ne pouvais pas générer le code moi même :

 // Générer les 4 mots de 32 bits et le code C++
    function generateWords() {
      // out[0..3] : 4 mots de 32 bits (utilisation de >>> 0 pour forcer l'unsigned)
      const out = [0, 0, 0, 0];

      for (let y = 0; y < MATRIX_HEIGHT; y++) {
        for (let x = 0; x < MATRIX_WIDTH; x++) {
          if (!ledState[y][x]) continue;

          const index = y * MATRIX_WIDTH + x;  // 0..103
          const mot = Math.floor(index / 32);  // 0..3
          const bit = index % 32;              // 0..31

          // Mise à 1 du bit correspondant (unsigned 32 bits)
          out[mot] = (out[mot] | (1 << bit)) >>> 0;
        }
      }

      let text = "";
      text += "// Tableau à transmettre à matrixWrite()\n";
      text += "const uint32_t frame[4] = {\n";
      for (let i = 0; i < 4; i++) {
        const val = out[i];
        const hex = val.toString(16).toUpperCase().padStart(8, "0");
        const comma = i < 3 ? "," : "";
        text += `    0x${hex}${comma}\n`;
      }
      text += "};\n";

      output.textContent = text;
    }

Au final il y a beaucoup de similitudes entre les langages !

Bravo chatGPT !

Oui c’est pas bien complexe, mais bon c’est un autre monde donc si vous êtes dans Swift, gardez le web pour plus tard !

Oui c’est vrai mais quand même il y a beaucoup de mondes différents :wink:

Il aurait fallu un langage universel pour tout :joy:

Bonne journée @J-M-L

Java a essayé mais c’est devenu « code once, debug everywhere » comme disent mes amis américains :slight_smile: