Commande d’une matrice 32×32 avec un MSP430

Article du blog ouilogique.com sur l’IoT, Arduino, ESP, Raspberry Pi et l’électronique.
Auteur·rice

Nico

Date de publication

11 septembre 2015

ouilogique.com

ouilogique.com

Référence de la matrice sur AliExpress

Launchpad

Launchpad-MSP430 rev 1.5

Brochage

brochage

Le brochage est décrit au début du programme ci-dessous.

Image du connecteur sur Adafruit.com

ouilogique.com

ouilogique.com

ouilogique.com

ouilogique.com

Article sur Adafruit

Brochage des différents connecteurs de matrices

https://www.ec086.com/Technical_support.html

Programme

Le programme ci-dessous m’a été gracieusement transmis par Pierre-Yves Rochat. La seule chose que j’ai modifiée est la description du brochage pour qu’elle corresponde à ma matrice. Voir l’article d’Adafruit ci-dessus au sujet des différentes descriptions de brochage.

 

// Brochage de la matrice (HUB75)

//     (P2.0) |  R0 [ 1]         [ 2] G0  | (P2.2)
//     (P2.4) |  B0 [ 3]         [ 4]     | (GND)
//     (P2.1) |  R1 [ 5]         [ 6] G1  | (P2.3)
//     (P2.5) |  B1 [ 7]         [ 8]     | (GND)
//     (P1.6) |   A [ 9]         [10] B   | (P1.7)
//      (XIN) |   C [11]         [12] D   | (XOUT)
//     (P1.1) | CLK [13]         [14] STB | (P1.2)
//     (P1.3) |  OE [15]         [16]     | (GND)



// Afficheur 32 x 32 RGB
// Utilisation en monochrome, 1 bit par couleur par pixel
//-------------------------------------------------------
// Exemple : lancer de drapeau...
// (2015) Pierre-Yves Rochat, pyr@pyr.ch

// Définitions matérielles :
//==========================


// Données des couleurs :
#define R1On P2OUT |= (1<<0)
#define R1Off P2OUT &=~(1<<0)
#define G1On P2OUT |= (1<<2)
#define G1Off P2OUT &=~(1<<2)
#define B1On P2OUT |= (1<<4)
#define B1Off P2OUT &=~(1<<4)

#define R2On P2OUT |= (1<<1)
#define R2Off P2OUT &=~(1<<1)
#define G2On P2OUT |= (1<<3)
#define G2Off P2OUT &=~(1<<3)
#define B2On P2OUT |= (1<<5)
#define B2Off P2OUT &=~(1<<5)

// Données des lignes :
#define AOn P1OUT |= (1<<6)
#define AOff P1OUT &=~(1<<6)
#define BOn P1OUT |= (1<<7)
#define BOff P1OUT &=~(1<<7)
#define COn P2OUT |= (1<<6)
#define COff P2OUT &=~(1<<6)
#define DOn P2OUT |= (1<<7)
#define DOff P2OUT &=~(1<<7)

// Signaux de contrôle :
#define ClockHaut P1OUT |= (1<<1)
#define ClockBas P1OUT &=~(1<<1)

#define LatchHaut P1OUT |= (1<<2)
#define LatchBas P1OUT &=~(1<<2)

#define OeHaut P1OUT |= (1<<3)
#define OeBas P1OUT &=~(1<<3)

void SetABCD(uint16_t ligne) {
  if (ligne&1) AOn; else AOff;
  if (ligne&2) BOn; else BOff;
  if (ligne&4) COn; else COff;
  if (ligne&8) DOn; else DOff;
}

// Définition d'une matrice de 32 lignes de 32 points, 3 couleurs :
#define MAX_X 32
#define MAX_Y 32
#define TAILLE_MOT 16
#define LG_MATRICE_1COUL MAX_X * MAX_Y / TAILLE_MOT
#define NB_COUL 3 // Rouge-Vert-Bleu
#define LG_MATRICE LG_MATRICE_1COUL * NB_COUL
uint16_t Matrice [LG_MATRICE];

// Routine de balayage :
//----------------------
void BalayeMatrice(uint16_t nbScan) {
  uint16_t b, i, rouge, vert, bleu, n;
  uint32_t masque;
  for (n=0; n<nbScan; n++) {
    for (i=0; i<LG_MATRICE_1COUL/2; i++) {
      SetABCD(i>>1);
      masque = 1;
      for (b=0; b<TAILLE_MOT; b++) {
        rouge = i; vert = i + LG_MATRICE_1COUL;  bleu = vert + LG_MATRICE_1COUL;
        if (Matrice[rouge] & masque) R1On; else R1Off;
        if (Matrice[vert] & masque) G1On; else G1Off;
        if (Matrice[bleu] & masque) B1On; else B1Off;

        if (Matrice[rouge + ((MAX_Y / 2) * MAX_X / TAILLE_MOT)] & masque) R2On; else R2Off;
        if (Matrice[vert + ((MAX_Y / 2) * MAX_X / TAILLE_MOT)] & masque) G2On; else G2Off;
        if (Matrice[bleu + ((MAX_Y / 2) * MAX_X / TAILLE_MOT)] & masque) B2On; else B2Off;
        ClockHaut;
        masque = masque << 1;
        ClockBas;
      }
      if (i & 1) {
        LatchBas; LatchHaut;
        OeBas;
        delayMicroseconds(200);
        OeHaut;
        // delayMicroseconds(100);
      }
    }
  }
}

#define NOIR 0
#define ROUGE 1
#define VERT 2
#define BLEU 4
#define BLANC ROUGE+VERT+BLEU

uint16_t couleur; // Variable globale pour la couleur

// Adaptation de la géométrie :
//-----------------------------
uint16_t CalculeIndex(int16_t x, int16_t y) {
  uint16_t index;
  // permutation des y2 et y3 :
  uint16_t yy = y;
  if (y & (1<<2)) yy |= (1<<3); else yy&=~(1<<3);
  if (y & (1<<3)) yy |= (1<<2); else yy&=~(1<<2);

  index = (yy<<1) | (x>>4); // 5 bits : y4 y3 y2 y1 y0 x4
  return index;
}

uint16_t CalculeNoBit(int16_t x, int16_t y) {
  uint16_t noBit;
  noBit = x & 0xF; // 4 bits : x4 x3 x2 x1 x0
  return noBit;
}

void AllumePoint(int16_t x, int16_t y) {
  // tient compte de la variable globale couleur
  uint16_t index = CalculeIndex(x, y);
  uint16_t noBit = CalculeNoBit(x, y);
  if ((x>=0) && (x<MAX_X) && (y>=0) && (y<MAX_Y)){
    if (couleur & ROUGE) {
      Matrice[index] |= (1<<noBit);
    } else {
      Matrice[index] &=~(1<<noBit);
    }
    if (couleur & VERT) {
      Matrice[index + LG_MATRICE_1COUL] |= (1<<noBit);
    } else {
      Matrice[index + LG_MATRICE_1COUL] &=~(1<<noBit);
    }
    if (couleur & BLEU) {
      Matrice[index + (LG_MATRICE_1COUL * 2)] |= (1<<noBit);
    } else {
       Matrice[index + (LG_MATRICE_1COUL * 2)] &=~(1<<noBit);
    }
  }
}

// Primitives de dessin :
//-----------------------
void EteintPoint(int16_t x, int16_t y) {
  uint16_t coul = couleur;
  couleur = NOIR;
  AllumePoint(x, y);
  couleur = coul;
}

void EffaceMatrice() { // met tous les points à 0
  int16_t x, y;
  for (y=0; y<MAX_Y; y++) {
    for (x=0; x<MAX_X; x++) {
      EteintPoint(x, y);
    }
  }
}

void DroiteHorizontale (int16_t x, int16_t y, int16_t lg) {
  uint16_t i;
  for (i=0; i<lg; i++) {
    AllumePoint (x+i, y);
  }
}

void DroiteVerticale (int16_t x, int16_t y, int16_t lg) {
  uint16_t i;
  for (i=0; i<lg; i++) {
    AllumePoint (x, y+i);
  }
}

// Interpréteur de commandes graphiques :
//=======================================

#define DrH 0x30 // + dx (sur 4 bits) : droite horizontale, depuis le curseur courant
#define DrV 0x40 // + dy (sur 4 bits) : droite verticale, depuis le curseur courant
#define PlusX 0x50 // + dx (sur 4 bits) : avance le curseur en X
#define PlusY 0x60 // + dy (sur 4 bits) : avance le curseur en Y
#define MoinsX 0x70 // + dx (sur 4 bits) : recule le cureur en X
#define MoinsY 0x80 // + y (sur 4 bits) : recule le curseur en Y
#define Repete 0x90 // + 4 bits : préfixe de répétition pour l'instruction suivante (souvent un call)
#define Delai 0xA0 // + 4 bits : Attente, valeur exposant de 2
#define SetAccu 0xB0 // + 4 bits : Charge l'accumulateur (utilisé pour Couleur, à compléter)
#define Label 0xC0 // + 5 bits (32 routines max)
#define Call 0xE0 // + 5 bits

#define Fin 0 // fin du programme
#define Vide 1 // efface l'écran
#define Ret 2 // retour de sous-routine (saut à l'adresse sur la pile)
#define Origine 3 // place le curseur à 0,0
#define ZeroX 4 // met à zéro X
#define Couleur 5 // détermine la couleur, selon la valeur de l'accumulateur
#define Masque 0x9
#define InvMasque 0xA // inverse le masque courant
#define SetDelai 0xB // définit du délai utilisé entre l'affichage de chaque point dses droites
#define SetDelaiDef 0xC // définit la valeur du délai 0
#define Effet 0xD
#define Libre2 0xE // instructions non utilisées
#define Libre1 0xF

// Label des routines :
#define Croix7x7 1
#define Croix7x7plusDel 2
#define Croix7x7passeX 3
#define Croix7x7Tourne 4
#define CadreCroix23 5
#define CadreCroix4 15
#define CadreCroix5 6
#define CroixGrandit 7
#define CadreEtCroix 16
#define DroitesArrivent 13
#define CadreSimpleCarre 14
#define Caducee 8
#define CadreTrait 20
#define CadreTraitX4 18
#define DroiteHori 9
#define DroiteVert2P 10
#define DroitesHoriDescendent 11
#define DroitesVertDeGauche 12
#define TeteFleche 21
#define LangueSerpent 19
#define CadreTrait3 22
#define CadreTourneVite 23

// Programme :
const uint16_t Prog [] = {
  Label+Croix7x7, // Affiche une croix 7x7 (1/4 de la surface), curseur courant
  PlusX+2,
  DrH+2, PlusY+1, MoinsX+2,
  DrH+2, PlusY+1, MoinsX+4,
  DrH+6, PlusY+1, MoinsX+6,
  DrH+6, PlusY+1, MoinsX+6,
  DrH+6, PlusY+1, MoinsX+4,
  DrH+2, PlusY+1, MoinsX+2,
  DrH+2, MoinsY+6, MoinsX+4,
  Ret,

  Label+Croix7x7plusDel, // Afficheur une croix 7x7, déplace le curseur en X et attend 2
  Vide, Call+Croix7x7, PlusX+1, Delai+2, Ret,

  Label+Croix7x7passeX, // Fait passer une croix X
  ZeroX, MoinsX+8, Repete+12, Call+Croix7x7plusDel,
  Repete+12, Call+Croix7x7plusDel, Vide, Delai+6, PlusY+2, Ret,

  Label+Croix7x7Tourne, // Fait tourner une croix 7x7 au 4 coins du carré
  Origine, Call+Croix7x7, Delai+6,
  PlusX+9, Call+Croix7x7, Delai+6,
  PlusY+9, Call+Croix7x7, Delai+6,
  MoinsX+9, Call+Croix7x7, Delai+7,
  SetAccu+BLEU, Couleur,
  Origine,
  Call+Croix7x7, Delai+6,
  PlusX+9, Call+Croix7x7, Delai+6,
  PlusY+9, Call+Croix7x7, Delai+6,
  MoinsX+9, Call+Croix7x7, Delai+7,
  SetAccu+VERT, Couleur,
  Ret,

  Label+CadreCroix23, // Cadre de croix, largeur 2 et 3
  Origine, PlusX+7, PlusY+3, DrV+9, MoinsY+9, PlusX+1, DrV+9,
  MoinsY+5, MoinsX+5, DrH+9, MoinsX+9, PlusY+1, DrH+9, Delai+6,
  Origine, PlusX+6, PlusY+2, DrV+11, DrH+3, MoinsY+11, DrV+11, MoinsX+3, MoinsY+11, DrH+3,
  Origine, PlusY+6, PlusX+2, DrH+11, DrV+3, MoinsX+11, DrH+11, MoinsY+3, MoinsX+11, DrV+3, Delai+6,
  Ret,

  Label+CadreCroix4, // Cadre de croix, largeur 4
  Origine, PlusX+5, PlusY+1, DrV+13, DrH+5, MoinsY+13, DrV+13, MoinsX+5, MoinsY+13, DrH+5,
  Origine, PlusY+5, PlusX+1, DrH+13, DrV+5, MoinsX+13, DrH+13, MoinsY+5, MoinsX+13, DrV+5, Delai+6,
  Ret,

  Label+CadreCroix5, // Cadre de croix, largeur 5
  Origine, PlusX+4, DrV+15, DrH+7, MoinsY+15, DrV+15, MoinsX+7, MoinsY+15, DrH+7,
  Origine, PlusY+4, DrH+15, DrV+7, MoinsX+15, DrH+15, MoinsY+7, MoinsX+15, DrV+7, Delai+7,
  Ret,

  Label+CroixGrandit, // Croix grandit
  Call+CadreCroix23, Call+15, Call+6, SetAccu+ROUGE, Couleur, Call+CadreCroix23, Call+15, Delai+8,
  SetAccu+VERT, Couleur, Vide, Delai+6,
  Ret,

  Label+LangueSerpent, // langue du serpent
  SetAccu+NOIR, Couleur, DrH+0, SetAccu+VERT, Delai+6, Couleur, DrH+0, Ret,

  Label+Caducee, // caducée
  Origine, PlusY+4, PlusX+2, DrH+11,
  PlusY+1, MoinsX+9, DrH+7,
  PlusY+1, MoinsX+5, DrH+3,
  PlusY+8, MoinsX+3, DrH+3,
  PlusY+1, MoinsX+5, DrH+7,
  MoinsY+8, MoinsX+4, DrV+6,
  MoinsY+6, PlusX+1, DrV+6,
   Delai+6, SetAccu+BLEU, Couleur, SetAccu+3, SetDelai,
  PlusX+2, PlusY+1, DrH+0, MoinsY+1, DrH+0, MoinsY+1, MoinsX+1, DrH+0, Delai+4,
  MoinsY+1, MoinsX+3, DrH+0, MoinsY+1, MoinsX+1, DrH+0,
  MoinsY+1, DrH+0, MoinsY+1, PlusX+1, DrH+0, Delai+4,
  PlusX+3, DrH+1,
   MoinsY+1, PlusX+1, DrH+0, MoinsY+1, DrH+0, Delai+4,
  MoinsY+3, DrH+0, MoinsY+1, MoinsX+1, DrH+0,
  MoinsY+1, MoinsX+1, DrH+0, MoinsX+1, DrH+0, MoinsX+1, DrH+0,
  SetAccu+0, SetDelai, MoinsX+2, DrH+1, MoinsX+1, PlusY+1, DrH+1,
  SetAccu+6, SetDelai, MoinsX+2, PlusY+1, DrH+0,
  Repete+5, Call+LangueSerpent, // langue
  SetAccu+0, SetDelai, Vide, Delai+6,
  Ret,

  Label+DroiteHori, // droite horizontale
  SetAccu+NOIR, Couleur, DrH+15, MoinsX+15, PlusY+1,
  SetAccu+VERT, Couleur, DrH+15, MoinsX+15, Delai+1,
  Ret,

  Label+DroiteVert2P, // droite verticale (en 2 parties)
  SetAccu+NOIR, Couleur, DrV+4, PlusY+7, DrV+4, MoinsY+15, PlusX+1,
  SetAccu+VERT, Couleur, DrV+4, PlusY+7, DrV+4, MoinsY+15, Delai+1,
  Ret,

  Label+DroitesHoriDescendent, // droites horizontales qui descendent
  Repete+8, Call+DroiteHori, MoinsY+9, Ret,

  Label+DroitesVertDeGauche, // droites verticales qui viennent de la gauche
  Repete+8, Call+DroiteVert2P, MoinsX+9, Ret,

  Label+DroitesArrivent, // Croix composée par des droites qui arrivent
  Origine, PlusY+2,
  Repete+6, Call+DroitesHoriDescendent,
  Origine, PlusX+2,
  Repete+6, Call+DroitesVertDeGauche, Delai+8, SetAccu+ROUGE, Couleur,
  Call+CadreCroix23, SetAccu+VERT, Couleur, Delai+8, Vide, Ret,

  Label+CadreSimpleCarre, // Cadre simple du carré
  Origine, DrH+15, DrV+15,
  Origine, DrV+15, DrH+15,
  Ret,

  Label+CadreEtCroix, // cadre et croix
  SetAccu+1, SetDelai, Call+CadreCroix23, Call+CadreSimpleCarre, Delai+8,
  SetAccu+BLEU, Couleur, Call+CadreSimpleCarre, Delai+8, SetAccu+VERT, Couleur,
  Vide,
  Ret,

  Label+CadreTraitX4, // Cadre traitillé x4
  SetAccu+1, Masque, Call+CadreTrait, Call+CadreTrait, Call+CadreTrait, Call+CadreTrait, Ret,

  Label+CadreTrait, // Cadre traitillé
  Origine, DrH+15,
  InvMasque, DrV+15,
  Origine, InvMasque, DrV+15,
  InvMasque, DrH+15,
  Delai+5,
  Ret,

  // à terminer...
  Label+TeteFleche, // tête de flèche et avance
  SetAccu+VERT, Couleur, DrV+8, DrH+8, MoinsX+9, MoinsY+7, Delai+6,
  DrV+8, DrH+8, MoinsX+9, MoinsY+7,
  SetAccu+NOIR, Couleur, PlusX+3, MoinsY+3,  DrV+4, PlusY+4, PlusX+4, DrH+4, MoinsX+10, MoinsY+8,
  Delai+7,
  Ret,

  Label+CadreTrait3,
  Origine, SetAccu+3, Masque, DrH+15, DrV+15, SetAccu+4, Masque, Origine, DrV+15, DrH+15, Delai,
  Origine, SetAccu+4, Masque, DrH+15, DrV+15, SetAccu+3, Masque, Origine, DrV+15, DrH+15, Delai,
  Origine, SetAccu+5, Masque, DrH+15, DrV+15, SetAccu+5, Masque, Origine, DrV+15, DrH+15, Delai,
  SetAccu+0, Masque,
  Ret,

  Label+CadreTourneVite,
  Repete+15, Call+CadreTrait3,
  Ret,

  // programme principal :
  Vide,
  Repete+4, Call+CroixGrandit, // Croix grandit
  Vide, Repete+4, Call+Croix7x7Tourne, Vide, // 4 croix tournent
  Repete+3, Call+CadreEtCroix, Vide, Delai+7, SetAccu+0, SetDelai, // Cadre et croix
  Origine, Repete+5, Call+Croix7x7passeX, // Croix passe
  // Call+CadreCroix23, SetAccu+2, SetDelaiDef, Repete+8, Call+CadreTourneVite, Vide, Delai+7,
  Origine, Repete+3, Call+DroitesArrivent, Delai+7, Vide, Delai+6, // Droites qui arrivent
  Repete+3, Call+Caducee,  Vide, Delai+6,  // caducée
  // Call+CadreCroix23, Repete+15, Call+CadreTraitX4, Vide, Delai+6, SetAccu+0, Masque,
  // Vide, Origine, PlusX+8, Repete+7, Call+TeteFleche, Delai+7, Vide, Delai+8,
  Fin
};

const uint16_t TableDelais[] = {
  1, 2, 3, 5, 9, 15, 25, 40, 70, 120, 180, 255
};

// Accès par les coordonnées X et Y :
// Variables globales, pour gagner de la place...
int DelaiPoint; // attente entre chaque point dessiné
unsigned int MasquePoints; // masque de dessin
unsigned int PointeurMasque;
unsigned int X, Y;

void SetXyMasque () {
  AllumePoint(X*2, Y*2); AllumePoint(X*2+1, Y*2);
  AllumePoint(X*2, Y*2+1); AllumePoint(X*2+1, Y*2+1);
}


int Pile [10]; // stack pour l'adresse de retour et le comptage courant

void Interprete () { // version avec plusieurs switch, selon la longueur de l'op-code
  int pc; // compteur de programme
  int sp; // pointeur de pile (qui monte)
  pc=0; sp=0;
  unsigned int Accu = 0; // accumulateur, pour le passage des valeurs
  unsigned int DelaiDef = 0; //délai par défaut (valeur 0)
  unsigned char instr; // instruction en cours
  unsigned char instr04, instr05; // paramètres (4 et 5 bits à droite)
  int nb = 1; // compteur des répétitions
  int i; // pour les droites
  MasquePoints = 0xFFFF; // pas de masque actif
  while (Prog[pc]!=Fin) {
    instr = Prog[pc]; // lecture de l'instruction
    PointeurMasque = 1; // (pour les droites seulement, mais ne gène pas ici)
    instr05 = instr & 0x1F; // paramètre sur 5 bits
    instr04 = instr & 0x0F; // paramètres sur 4 bits
    switch (instr) { // décodage des instructions sans paramètres
      case Vide : EffaceMatrice(); break;
      case Ret : pc = Pile[--sp];  nb = Pile[--sp]; break;
      case Origine : Y = 0;  // X = 0; break; astuce : pas de break
      case ZeroX : X = 0; break;
      case Couleur : couleur = Accu & 7; break; // Couleur sur 3 bits
      case Masque :
        if (Accu==0) { MasquePoints = 0xFFFF; }
        if (Accu==1) { MasquePoints = 0x5555; }
        if (Accu==2) { MasquePoints = 0xAAAA; }
        if (Accu==3) { MasquePoints = 0xB6DB; } // 1011 0110 1101 1011
        if (Accu==4) { MasquePoints = 0xDB6D; } // 1101 1011 0110 1101
        if (Accu==5) { MasquePoints = 0x6DB6; } // 0110 1101 1011 0110
        if (Accu==6) { MasquePoints = 0x0000; }
        break;
      case InvMasque : MasquePoints = ~MasquePoints; break; // 0xB6DB; break; // 101
      case SetDelai : DelaiPoint = (1 << Accu) & (~1); break;
      case SetDelaiDef : DelaiDef = Accu; break;
      case Effet : break; // pas implémenté
      default : break;
    }

    // Décodage des  instructions avec des paramètres de 4 bits
    // et 5 bits (double "case") (gagne 4 octets...)
    switch (instr & 0xF0) { // 4 bits de poids fort
      case Label :
      case Label + 0x10 :
        do {
          pc++;
        } while ((Prog[pc]!=Fin) && (Prog[pc]!=Ret));
        break;
      case Call :
      case Call + 0x10 :
        Pile [sp++] = nb;
        Pile [sp++] = pc;
        pc = 0; // recherche depuis le début
        while (Prog[pc] != (Label | instr05)) {
          pc++;
        }
        nb = 1;
        break;
      case DrH :  // PointeurMasque = 1; // au début
            for(i=0; i<(instr04+1); i++){ // droite horizontale
              SetXyMasque(); X++;
            }
            X--; break;
      case DrV :  // PointeurMasque = 1;
            for(i=0; i<(instr04+1); i++){ // droite verticale
              SetXyMasque(); Y++;
            }
            Y--; break;
      case PlusX : X+=instr04; break;
      case PlusY : Y+=instr04; break;
      case MoinsX : X-=instr04; break;
      case MoinsY : Y-=instr04; break;
      case Repete : nb=instr04+1; pc++; break;
      case Delai :
        if (instr04==0) { instr04 = DelaiDef; } // valeur par défaut
        BalayeMatrice(TableDelais[instr04]); break;
      case SetAccu : Accu = instr04; break;
      default : break;
    }
    if (nb==1) { pc++; } else { nb--; }
  }
}

// Programme principal :
//======================

void setup() { // Initialisations :
  P1DIR = 0xFF; P2DIR = 0xFF; // tous les ports en sortie
  P1OUT = 0; P2OUT = 0; // toutes les sorties à 0
  EffaceMatrice(); // met tous les points à 0
  couleur = VERT;
}

void loop() { // Boucle de l'animation
  Interprete();
}