// Utilizzare la libreria MD_MAX72XX per far scorrere il testo sul display
//
// Dimostra l'uso della funzione di callback per controllare cosa
// viene visualizzato come testo scorrevole sul display.
//
// L'utente può inserire il testo nel monitor seriale e questo verrà visualizzato
// come un messaggio scorrevole sul display.
// La velocità del display è controllata da un potenziometro sull'ingresso analogico SPEED_IN.
//
#include <MD_MAX72xx.h>
#include <SPI.h>
#define IMMEDIATE_NEW 0 // se 1 mostrerà immediatamente un nuovo messaggio
#define USE_POT_CONTROL 1
#define PRINT_CALLBACK 0
#define PRINT(s, v) { Serial.print(F(s)); Serial.print(v); }
// Definire il numero di dispositivi nella catena e l'interfaccia hardware
// NOTA: Questi numeri di pin probabilmente non funzioneranno con il tuo hardware e potrebbero
// dover essere adattati
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define MAX_DEVICES 8
#define CLK_PIN 13 // o SCK
#define DATA_PIN 11 // o MOSI
#define CS_PIN 10 // o SS
// Interfaccia hardware SPI
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Pin arbitrari
//MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
// Parametri di scorrimento
#if USE_POT_CONTROL
#define SPEED_IN A5
#else
#define SCROLL_DELAY 75 // in millisecondi
#endif // USE_POT_CONTROL
#define CHAR_SPACING 1 // pixel tra i caratteri
// Buffer dei messaggi globali condivisi dalle funzioni Serial e Scrolling
#define BUF_SIZE 75
uint8_t curMessage[BUF_SIZE] = { "4ne Electronics - Reggio Calabria " };
uint8_t newMessage[BUF_SIZE];
bool newMessageAvailable = false;
uint16_t scrollDelay; // in millisecondi
void readSerial(void)
{
static uint8_t putIndex = 0;
while (Serial.available())
{
newMessage[putIndex] = (char)Serial.read();
if ((newMessage[putIndex] == '\n') || (putIndex >= BUF_SIZE-3)) // carattere di fine messaggio o buffer pieno
{
// inserire un separatore di messaggio e terminare la stringa
newMessage[putIndex++] = ' ';
newMessage[putIndex] = '\0';
// riavviare l'indice per la prossima sequenza di riempimento e segnalare che abbiamo un messaggio in attesa
putIndex = 0;
newMessageAvailable = true;
}
else if (newMessage[putIndex] != '\r')
// Salvare semplicemente il prossimo carattere nella posizione successiva
putIndex++;
}
}
void scrollDataSink(uint8_t dev, MD_MAX72XX::transformType_t t, uint8_t col)
// Funzione di callback per i dati che vengono scorsi fuori dal display
{
#if PRINT_CALLBACK
Serial.print("\n cb ");
Serial.print(dev);
Serial.print(' ');
Serial.print(t);
Serial.print(' ');
Serial.println(col);
#endif
}
uint8_t scrollDataSource(uint8_t dev, MD_MAX72XX::transformType_t t)
// Funzione di callback per i dati che sono richiesti per il caricamento nel display
{
static uint8_t* p = curMessage;
static enum { NEW_MESSAGE, LOAD_CHAR, SHOW_CHAR, BETWEEN_CHAR } state = LOAD_CHAR;
static uint8_t curLen, showLen;
static uint8_t cBuf[15];
uint8_t colData = 0; // colonna vuota è il default
#if IMMEDIATE_NEW
if (newMessageAvailable) // c'è un nuovo messaggio in attesa
{
state = NEW_MESSAGE;
mx.clear(); // pulire il display
}
#endif
// macchina a stati finiti per controllare cosa fare nella callback
switch(state)
{
case NEW_MESSAGE: // Caricare il nuovo messaggio
memcpy(curMessage, newMessage, BUF_SIZE); // copiarlo
newMessageAvailable = false; // usato!
p = curMessage;
state = LOAD_CHAR;
break;
case LOAD_CHAR: // Caricare il prossimo carattere dalla tabella dei font
showLen = mx.getChar(*p++, sizeof(cBuf)/sizeof(cBuf[0]), cBuf);
curLen = 0;
state = SHOW_CHAR;
// se abbiamo raggiunto la fine del messaggio, opportunità di caricare il prossimo
if (*p == '\0')
{
p = curMessage; // ripristinare il puntatore all'inizio del messaggio
#if !IMMEDIATE_NEW
if (newMessageAvailable) // c'è un nuovo messaggio in attesa
{
state = NEW_MESSAGE; // lo caricheremo qui
break;
}
#endif
}
// !! cadere deliberatamente nello stato successivo per iniziare a visualizzare
case SHOW_CHAR: // visualizzare la prossima parte del carattere
colData = cBuf[curLen++];
if (curLen == showLen)
{
showLen = CHAR_SPACING;
curLen = 0;
state = BETWEEN_CHAR;
}
break;
case BETWEEN_CHAR: // visualizzare la spaziatura tra i caratteri (colonne vuote)
colData = 0;
curLen++;
if (curLen == showLen)
state = LOAD_CHAR;
break;
default:
state = LOAD_CHAR;
}
return(colData);
}
void scrollText(void)
{
static uint32_t prevTime = 0;
// È ora di far scorrere il testo?
if (millis()-prevTime >= scrollDelay)
{
mx.transform(MD_MAX72XX::TSL); // scorrere - la callback caricherà tutti i dati
prevTime = millis(); // punto di partenza per la prossima volta
}
}
uint16_t getScrollDelay(void)
{
#if USE_POT_CONTROL
uint16_t t;
t = analogRead(SPEED_IN);
t = map(t, 0, 1023, 25, 250);
return(t);
#else
return(SCROLL_DELAY);
#endif
}
void setup()
{
mx.begin();
mx.setShiftDataInCallback(scrollDataSource);
mx.setShiftDataOutCallback(scrollDataSink);
#if USE_POT_CONTROL
pinMode(SPEED_IN, INPUT);
#else
scrollDelay = SCROLL_DELAY;
#endif
newMessage[0] = '\0';
Serial.begin(57600);
Serial.print("\n[MD_MAX72XX Display Messaggi]\nDigitare un messaggio per il display a scorrimento\nTerminare la riga del messaggio con un a capo");
}
void loop()
{
scrollDelay = getScrollDelay();
readSerial();
scrollText();
}