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)
3, B01000010, B01111111, B01000000, B00000000, B00000000, // 49 - '1'
dove il 3 indica il numero di bit occupati una volta ruotato, cioè
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
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
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
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:
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
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)
UPDATE 20/12/2019
Versione 2.1. Informazioni qui (#116)
Max7219_Matrix_v2.1.zip (7.88 KB)