Go Down

Topic: Libreria per matrice led 32x8 MAX7219 (Read 1 time) previous topic - next topic

Federico66

Nov 20, 2019, 07:56 pm Last Edit: Dec 10, 2019, 05:55 pm by Federico66
Buonasera
ispirato da questo post, ho cercato di capire meglio come gestire i moduli a matrice di led 32x8 (4 MAX7219).
Purtroppo, ingenuamente, avevo pensato di ruotare i caratteri nella matrice di byte, ma questo oltre a non permettere lo scorrimento corretto, fa in modo che i caratteri occupino sempre 8x8 bit, aumentando lo spazio tra i caratteri di una parola; l'unico modo corretto (a mio parere) è ruotare ogni carattere prima di stamparlo.
Naturalmente ho trovato un'ottima libreria (MD_MAX72XX), ma onestamente è un mattone!
Quindi ho deciso di studiare e provare a realizzare una semplice libreria (minimalista)!
Sono partito dalla libreria MaxMatrix, ma solo come spunto, in quanto non c'era niente di utilizzabile!

Naturalmente (per me) non è cosi banale, in quanto bisogna agire sui singoli bit e conoscete i miei limiti, ma sono testardo quindi ho preso un bel foglio a quadretti e ho iniziato a studiare.

Obiettivi:
- ruotare la matrice di bit che rappresenta il carattere
- testo scorrevole a sinistra, ed eventualmente a destra
- possibilità di usare caratteri estesi (in particolare le accentate)

Mi sono procurato una matrice dei caratteri (copiata dalla libreria MD_MAX72XX), che rappresenta i caratteri in questo modo (per comodità i byte sono scritti nella forma binaria, un bit corrisponde ad un led)

Code: [Select]
3, B01000010, B01111111, B01000000, B00000000, B00000000, // 49 - '1'

dove il 3 indica il numero di bit occupati una volta ruotato, cioè
Code: [Select]
010
110
010
010
010
010
111
000

e quindi permette l'avvicinamento del carattere successivo.

Per la rotazione, dopo molti fogli a quadretti, ho risolto in questo modo, naturalmente grazie a vari articoli sulle operazioni bit a bit :(

Code: [Select]

 for (uint8_t i = 0; i < sprite[0]; i++) {
  for (uint8_t row = 0; row < 8; row++) {
   //porta a sinistra il bit che mi interessa
   uint8_t bit_value = sprite[i + 1] << (7 - row);
   //estrae il bit più a sinistra (ottavo)
   bit_value = bit_value & 0x80;
   uint8_t max_num = (col / 8);
   uint8_t bit_col = 7 - (col % 8);
   bitWrite(_matrix[row][max_num], bit_col, bit_value);
  }
  col++;
 }

dove sprite è la matrice che rappresenta il carattere (vedi sopra), col è la colonna (bit) corrente, e _matrix è una matrice bidimensionale (8 righe per 80 colonne) che è una rappresentazione virtuale di 10 matrici 8x8.

A questo punto, una volta riempita la matrice virtuale, per lo scorrimento avrei dovuto far scorrere i singoli bit, il che implica spostare un bit da un byte al precedente o al successivo!!
Anche qui dopo vari tentativi, ho risolto grazie alle operazioni bit a bit.

Scorrimento a sinistra
Code: [Select]

 for (uint8_t row = 0; row < 8; row++) {
  for (uint8_t i = 0; i < MAX_DIGITS - 1; i++) {
   unsigned char b = 0;
   if ( i < MAX_DIGITS - 1) {
    //Legge il valore del bit più a sinistra
    b = bitRead(_matrix[row][i + 1], 7);
   }
   //Sposta di una posizione verso sinistra
   _matrix[row][i] <<= 1;
   //Imposta il valore del bit più a destra
   _matrix[row][i] |= b;
  }
 }

Scorrimento a destra
Code: [Select]

 for (uint8_t row = 0; row < 8; row++) {
  for (uint8_t i = MAX_DIGITS; i > 0; i--) {
   unsigned char b = 0;
   if ((i - 1) > 0) {
    //Legge il valore del bit più a destra
    b = bitRead(_matrix[row][i - 1 - 1], 0);
   }
   //Sposta di una posizione verso destra
   _matrix[row][i - 1] >>= 1;
   //Imposta il valore del bit più a sinistra
   _matrix[row][i - 1] |= (b * 0x80);
  }
 }


A questo punto sembrava risolto, sparo la matrice virtuale sui moduli e il gioco è fatto!
Invece no, o almeno, non per lo scorrimento a destra, in quanto verso sinistra, i caratteri escono dalla matrice virtuale in colonna zero, ma verso destra dalla colonna zero devono entrare!

Dopo vari tentativi, ho avuto una illuminazione ho risolto in questo modo:
Code: [Select]

 for (uint8_t row = 0; row < 8; row++) {
  digitalWrite(_load, LOW);
  for (uint8_t i = start; i < start + _num; i++) {
   shiftOut(_data, _clock, MSBFIRST, B00000001 + row);
   shiftOut(_data, _clock, MSBFIRST, _matrix[row][i]);
  }
  digitalWrite(_load, HIGH);
 }


Dove start indica la colonna (byte) da dove cominciare a leggere la matrice virtuale, che nel caso di scorrimento a sx, è uguale a zero, ma nel caso di scorrimento a dx è uguale alla posizione dell'ultimo led acceso (ultimo carattere scritto).

Per finire, grazie a @docdoc e questo post, ho inserito questa funzioncina

Code: [Select]

 void Max7219_Matrix::unicodeFilter(char *string) {
  for (uint16_t i = 0; i < strlen(string); i++) {
   if ((uint8_t)string[i] == 0xC3) {
    string[i] = string[i + 1] + 0x40;
    memcpy(string + i + 1, string + i + 2, strlen(string) - i);
   }
  }
 }

che processa la stringa sostituendo i due byte del carattere Unicode nel relativo ascii code esteso (128-255)


In definitiva, ho scritto una libreria minimalista che sembra funzionare correttamente (video) e che condivido con piacere.

Al solito:
- se ho scritto idiozie
- se trovate errori
- se pensate possa essere ottimizzato/migliorato
vi prego di dirmelo.

Grazie
Federico

PS
Utile lettura:
capire il MAX7219
operatori bit a bit sintetico, ma riassuntivo

UPDATE 10/12/2019
Versione 2.0. Informazioni qui (#67)
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

Datman

Grazie, Federico! :) :) :)
Appena posso faccio qualche prova, anche se abbiamo cominciato a lavorare di sera in parrocchia per il presepe, quindi sarò un po' impegnato.

Avevo provato la MD_max, ma mi sono fermato perché non capisco che problema c'è: rimane tutto spento o si accende tutto, ma non appaiono caratteri né sgorbi. Sembra un problema di comunicazione, ma i collegamenti sono esatti... Proprio ora mi viene il sospetto di aver caricato il programma con l'IDE impostato a 16MHz, mentre lì uso l'oscillatore interno a 8MHz! :)
Hi,I'm Gianluca from Roma.I play&work with electronics since I was16(1984).
After 25yrs of maintenance on cameras&video mixers,since 2013myJob is HDTVstudios design.
Since Jan2015 IPlayWith Arduino:bit.ly/2F3LPWP
Thanks 4 a Karma if U like my answer

Federico66

All'ora di pranzo, mi son fatto prendere la mano e ho aggiunto:
- inversione testo (negativo)
- testo scorrevole verso l'alto
- testo scorrevole verso il basso

File aggiornati nel primo post.

Federico
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

Federico66

Avevo provato la MD_max, ma mi sono fermato perché non capisco che problema c'è: ...
Hai visto che devi impostare l'hardware corretto.

Code: [Select]

//nel mio caso, ma immagino anche il tuo :)

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW


Federico
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

Datman

Era un problema dei driver dell'USBASP sul computer che stavo usando.
Hi,I'm Gianluca from Roma.I play&work with electronics since I was16(1984).
After 25yrs of maintenance on cameras&video mixers,since 2013myJob is HDTVstudios design.
Since Jan2015 IPlayWith Arduino:bit.ly/2F3LPWP
Thanks 4 a Karma if U like my answer

Datman

#5
Nov 22, 2019, 12:09 am Last Edit: Nov 22, 2019, 12:11 am by Datman
Funziona! :)
Per integrarlo, però, temo che dovrò sforzarmici un po' e con calma, perché durante lo scorrimento avevo messo la lettura dell'encoder, proprio dentro alla funzione...
Code: [Select]
void printStringWithShift(unsigned char*s, int tempo_scorr, byte visual_ver)
{ //                                            da 1 a 10.
while(*s!=0)
  {
  if(!digitalRead(6) && !visual_ver) {selezione(); while(!digitalRead(6)); delay(200); Riavvia();}
  printCharWithShift(*s, tempo_scorr);
  s++;
  }
if(*s==0)
  {
  t0=millis();
  while(millis()-t0<pausa*1000)
    {   // Se visual_ver==1, significa che è stata chiamata dal setup() per visualizzare la versione,
        // perciò, pur essendo ancora premuto il pulsante, non deve saltare a selezione().
    if(!digitalRead(6) && !visual_ver) {selezione(); while(!digitalRead(6)); delay(200); Riavvia();}
    printCharWithShift(' ', tempo_scorr);
    }
  }
}
Hi,I'm Gianluca from Roma.I play&work with electronics since I was16(1984).
After 25yrs of maintenance on cameras&video mixers,since 2013myJob is HDTVstudios design.
Since Jan2015 IPlayWith Arduino:bit.ly/2F3LPWP
Thanks 4 a Karma if U like my answer

Federico66

Funziona! :)
Per integrarlo, però, temo che dovrò sforzarmici un po' e con calma, perché durante lo scorrimento avevo messo la lettura dell'encoder, proprio dentro alla funzione...
Io ho eliminato la possibilità di stampare un char, nel senso che questa è una funzione privata della libreria per una mia scelta, ma in effetti a te è comodo interrompere lo scorrimento prima di stampare il carattere e non aspettare tutta la stringa e in generale potrebbe essere comodo.

Ci lavoro nel fine settimana e vedo cosa riesco a fare.

Federico
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

Datman

Hi,I'm Gianluca from Roma.I play&work with electronics since I was16(1984).
After 25yrs of maintenance on cameras&video mixers,since 2013myJob is HDTVstudios design.
Since Jan2015 IPlayWith Arduino:bit.ly/2F3LPWP
Thanks 4 a Karma if U like my answer

Federico66

Grazie!!! :)
Ho aggiornato la libreria.
Nel tuo caso dovresti provare cosi, ma sono scettico :(
Code: [Select]

void printStringWithShift(char* string, uint16_t scroll_speed) {
  m.unicodeFilter(string);
  uint16_t col = 8 * MAX_NUM;
  for (uint16_t i = 0; i < strlen(string); i++) {
    //fai quello che vuoi
    printChar(&col, string[i]);
  }
  for (uint16_t i = 0; i < col; i++) {
    delay(scroll_speed);
    m.scrollLeft();
  }
}

void printChar(uint16_t *col, char scroll_char) {
  if (scroll_char < 32 || scroll_char > 255) return;
  uint8_t b = scroll_char - 32;
  uint8_t buf[7];
  memcpy_P(buf, CH + 6 * b, 6);
  m.writeChar(*col, buf);
  *col += buf[0] + 1;
}
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

Datman

Grazie, Federico! Credo che proverò domani :)
Hi,I'm Gianluca from Roma.I play&work with electronics since I was16(1984).
After 25yrs of maintenance on cameras&video mixers,since 2013myJob is HDTVstudios design.
Since Jan2015 IPlayWith Arduino:bit.ly/2F3LPWP
Thanks 4 a Karma if U like my answer

Datman

#10
Nov 24, 2019, 05:16 pm Last Edit: Nov 24, 2019, 05:20 pm by Datman
Ciao, Federico
E' da un po' che sto provando, ma non riesco a integrare la tua libreria. Credevo che avessi fatto delle funzioni con gli stessi nomi...
Ho modificato l'inizio, ho tolto la definizione dei caratteri, ma non va... Puoi aiutarmi? :)
Hi,I'm Gianluca from Roma.I play&work with electronics since I was16(1984).
After 25yrs of maintenance on cameras&video mixers,since 2013myJob is HDTVstudios design.
Since Jan2015 IPlayWith Arduino:bit.ly/2F3LPWP
Thanks 4 a Karma if U like my answer

Federico66

Credevo che avessi fatto delle funzioni con gli stessi nomi...
Non credo che il problema siano i nomi, ma la diversa logica di "scrittura del testo" tra le due librerie.

Ho dato un'occhiata al tuo programma, ma faccio fatica a seguirlo, quindi colgo l'occasione per prendere un encoder, e provo ;)
A proposito, va bene uno di questi qui?

Federico
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

Datman

Sì, va benissimo.
Grazie! :)
Ho faticato parecchio, anche con molto vostro aiuto, per arrivare a quel punto. Da solo non ce l'avrei fatta!
Hi,I'm Gianluca from Roma.I play&work with electronics since I was16(1984).
After 25yrs of maintenance on cameras&video mixers,since 2013myJob is HDTVstudios design.
Since Jan2015 IPlayWith Arduino:bit.ly/2F3LPWP
Thanks 4 a Karma if U like my answer

Federico66

Sì, va benissimo.
Preso, mi arriva tra qualche giorno ;)

Nel frattempo avevo qualche minuto e ho provato a fare modifiche alla cieca  ::)

Prova con quella in allegato e fammi sapere.
Per mia comodità ho messo la libreria nella stessa cartella.
Ho commentato le funzioni che ho sostituito.
Compila correttamente, ma non so se funziona ;)

A proposito di compilazione, ma tu hai disabilitato o non hai mai abilitato i warning? ::)

Federico
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

Datman

#14
Nov 25, 2019, 04:17 pm Last Edit: Nov 25, 2019, 04:19 pm by Datman
Tana! :)
Ieri, infatti, su un computer su cui avevo sostituito l'hard disk e reinstallato tutto mi apparivano, inspiegabilmente, un sacco di warning! Non capivo dove avevo sbagliato... Quella versione funzionava con il display singolo... Ho disabilitato i warning e ora non ho più messaggi! :)

Più tardi provo. Grazie
Hi,I'm Gianluca from Roma.I play&work with electronics since I was16(1984).
After 25yrs of maintenance on cameras&video mixers,since 2013myJob is HDTVstudios design.
Since Jan2015 IPlayWith Arduino:bit.ly/2F3LPWP
Thanks 4 a Karma if U like my answer

Go Up