/****************************************************************************************/
/***** Tema: Temporización *****/
/***** Proyecto: Letrero Matriz de Leds *****/
/****************************************************************************************/
/***** EvalBoard: ESP32 S3 DevKitC C1 *****/
/***** Autor: Rodia Santivañez Santivañez *****/
/***** Fecha: octubre 2024 *****/
/****************************************************************************************/
/***** Enunciado: *****/
/****************************************************************************************/
/* Panel matricial de leds utilizando 3 módulos MAX7219 aprovechando su conexión en */
/* cascada, en total, 3x7x8 leds utilizados. */
/* Utilice BTN1 (pin 1) para alternar el modo entre RELOJ y MENSAJE */
/* Utilice BTN2 (pin 40) para configurar velocidad máxima de desplazamiento del mensaje */
/* Utilice BTN3 (pin 37) para configurar velocidad mínima de desplazamiento del mensaje */
/****************************************************************************************/
// Incluimos la librería SPI
#include <SPI.h>
// Incluimos la biblioteca RTClib
#include <RTClib.h>
// Pines asignados para el protocolo SPI
const int CS_PIN = 14;
const int MOSI_PIN = 11;
const int MISO_PIN = 13;
const int SCK_PIN = 12;
#define PSH_BTN_MODO 1
#define PSH_BTN_VMAX 40
#define PSH_BTN_VMIN 37
// Registros de la matriz LED MAX7219
#define MAX7219_REG_NOOP 0x00
#define MAX7219_REG_DIGIT0 0x01
#define MAX7219_REG_DIGIT1 0x02
#define MAX7219_REG_DIGIT2 0x03
#define MAX7219_REG_DIGIT3 0x04
#define MAX7219_REG_DIGIT4 0x05
#define MAX7219_REG_DIGIT5 0x06
#define MAX7219_REG_DIGIT6 0x07
#define MAX7219_REG_DIGIT7 0x08
#define MAX7219_REG_DECODEMODE 0x09
#define MAX7219_REG_INTENSITY 0x0A
#define MAX7219_REG_SCANLIMIT 0x0B
#define MAX7219_REG_SHUTDOWN 0x0C
#define MAX7219_REG_DISPLAYTEST 0x0F
// Constantes utilizadas por el programa
#define RELOJ 1
#define MENSAJE 2
#define MMSS_DOTS 250 // 250ms entre refresco del reloj
#define MMSS_MSG_MAX 50 // frecuencia = 1/50ms = 20 pixels/seg
#define MMSS_MSG_MIN 200 // frecuencia = 1/200ms = 5 pixels/seg
RTC_DS1307 miRTC; // Declaramos el objeto miRTC
DateTime now; // objeto de clase DateTime para registrar la hora actual
uint8_t estadoPanel = RELOJ; // registra el estado actual del panel
uint8_t dots = 1; // indica si los 2 puntos del reloj se dibujarán
uint16_t pixel = 0; // indica el desplazamiento en bits del mensaje
uint16_t dots_speed = MMSS_DOTS;
uint16_t message_speed = MMSS_MSG_MIN;
unsigned long mmss_actuales = 0; //registra los milisegundos actuales
unsigned long mmss_iniciales = 0; //registra los milisegundos de referencia (iniciales)
uint8_t letras[2][7] = {
{0b0010, 0b0000, 0b0010, 0b0010, 0b0010, 0b0010, 0b0010}, //letra i
{0b0000, 0b0000, 0b0110, 0b1001, 0b1111, 0b0001, 0b1110}, //letra e
};
uint8_t dos_puntos[2][7] = {
{0b0000, 0b1000, 0b1000, 0b0000, 0b1000, 0b1000, 0b0000}, //dos puntos izq
{0b0000, 0b0001, 0b0001, 0b0000, 0b0001, 0b0001, 0b0000}, //dos puntos der
};
uint8_t digitos[10][7] = {
{0b0110, 0b1001, 0b1001, 0b1001, 0b1001, 0b1001, 0b0110}, //digito 0
{0b0010, 0b0011, 0b0010, 0b0010, 0b0010, 0b0010, 0b0111}, //digito 1
{0b0110, 0b1001, 0b1000, 0b0100, 0b0010, 0b0001, 0b1111}, //digito 2
{0b0110, 0b1001, 0b1000, 0b0110, 0b1000, 0b1001, 0b0110}, //digito 3
{0b0100, 0b0110, 0b0101, 0b0101, 0b1111, 0b0100, 0b0100}, //digito 4
{0b1111, 0b0001, 0b0001, 0b0111, 0b1000, 0b1000, 0b0111}, //digito 5
{0b0110, 0b1001, 0b0001, 0b0111, 0b1001, 0b1001, 0b0110}, //digito 6
{0b1111, 0b1000, 0b0100, 0b0010, 0b0010, 0b0010, 0b0010}, //digito 7
{0b0110, 0b1001, 0b1001, 0b0110, 0b1001, 0b1001, 0b0110}, //digito 8
{0b0110, 0b1001, 0b1001, 0b1110, 0b1000, 0b1001, 0b0110}, //digito 9
};
// Función para rotar b bts de un registro n de 32 bits
uint32_t rotl(uint32_t n, uint32_t b) {
return (n << b) | (n >> (32 - b));
}
// Función para enviar datos al MAX7219 por protocolo PSI (3 matrices)
void sendToMax7219x3(uint8_t reg, uint32_t data) {
digitalWrite(CS_PIN, LOW); // Seleccionar el dispositivo (CS bajo)
SPI.transfer(reg); // Enviar fila
SPI.transfer(data & 0x000000FF); // Enviar dato para la matriz más significativa (izquierda)
SPI.transfer(reg); // Enviar fila
SPI.transfer((data & 0x0000FF00) >> 8); // Enviar dato para la matriz central
SPI.transfer(reg); // Enviar fila
SPI.transfer((data & 0x00FF0000) >> 16); // Enviar dato para la matriz menos significativa (dereecha)
digitalWrite(CS_PIN, HIGH); // Deseleccionar el dispositivo (CS alto)
}
// Función que muestra mensaje fijo: "1iee27" en la matriz y lo desplaza el número
// de pixels indicado por la variable pxl
void ShowMessage3x7x8(uint16_t pxl=0) {
uint32_t dato;
for(int i = 0; i<7; i++) {
dato = digitos[1][i] | rotl(letras[0][i], 4) | rotl(letras[1][i], 8) | rotl(letras[1][i], 12) | rotl(digitos[2][i], 16) | rotl(digitos[8][i], 20);
sendToMax7219x3(i+1, dato<<pxl);
}
}
// Función que muestra minuto y segundo en formato reloj y dibuja los dos puntos
// de acuerdo a lo indicado por la variable dots
void ShowDigitalClock3x7x8(uint8_t minu, uint8_t seg, uint8_t dots = 1) {
uint32_t dato;
for(int i = 0; i<7; i++) {
if(dots)
dato = digitos[minu/10][i] | rotl(digitos[minu%10][i], 4) | rotl(dos_puntos[0][i], 8) | rotl(dos_puntos[1][i], 12) | rotl(digitos[seg/10][i], 16) | rotl(digitos[seg%10][i], 20);
else
dato = digitos[minu/10][i] | rotl(digitos[minu%10][i], 4) | 0b00000000 | 0b00000000 | rotl(digitos[seg/10][i], 16) | rotl(digitos[seg%10][i], 20);
sendToMax7219x3(i+1, dato);
}
}
void setup() {
// Configurar pines SPI
SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CS_PIN);
// Configurar el pin CS
pinMode(CS_PIN, OUTPUT);
// Configurar pines de entrada para los pulsadores
pinMode(PSH_BTN_MODO, INPUT_PULLUP);
pinMode(PSH_BTN_VMAX, INPUT_PULLUP);
pinMode(PSH_BTN_VMIN, INPUT_PULLUP);
digitalWrite(CS_PIN, HIGH); // Desactivar el chip select
//Inicializar los 3 MAX7219
sendToMax7219x3(MAX7219_REG_SCANLIMIT, 0x00070707); // Usar todos los dígitos (0-7)
sendToMax7219x3(MAX7219_REG_DECODEMODE, 0x00000000); // Sin decodificación BCD
sendToMax7219x3(MAX7219_REG_SHUTDOWN, 0x00010101); // Salir del modo shutdown
sendToMax7219x3(MAX7219_REG_INTENSITY, 0x000F0F0F); // Máxima intensidad (0x00 a 0x0F)
// Inicializamos comunicación serial
Serial.begin(115200);
Serial.println("Sistemas Digitales B - LAB08");
Serial.println("Panel matricial 3x7x8 leds utilizados");
Serial.print('\n');
Serial.println("BTN1 (pin 1): utilice para cambiar el modo de panel RELOJ/MENSAJE");
Serial.println("BTN2 (pin 40): En modo MENSAJE, utilice para configurar velocidad máxima de scroll");
Serial.println("BTN2 (pin 37): En modo MENSAJE, utilice para configurar velocidad mínima de scroll");
Serial.print('\n');
// Inicializamos el RTC
miRTC.begin();
miRTC.adjust(DateTime(2024, 10, 18, 13, 11, 0));
// El panel inicia en modo RELOJ
now = miRTC.now();
ShowDigitalClock3x7x8(now.minute(), now.second());
estadoPanel = RELOJ;
}
void loop() {
mmss_actuales = millis();
if(estadoPanel == RELOJ) {
if(mmss_actuales - mmss_iniciales >= dots_speed){
now = miRTC.now();
ShowDigitalClock3x7x8(now.minute(), now.second(), dots);
dots^= 1; // intercambia el primer bit de la variable para dar el efecto de parpadeo de los 2 puntos
mmss_iniciales = mmss_actuales;
}
else {
if(digitalRead(PSH_BTN_MODO) == LOW) {
delay(10);
while(digitalRead(PSH_BTN_MODO) == LOW);
delay(10);
estadoPanel = MENSAJE;
pixel = 0; // inicia el modo mensaje con desplazamiento 0
mmss_iniciales = mmss_actuales;
}
}
}
else if(estadoPanel == MENSAJE) {
if(mmss_actuales - mmss_iniciales >= message_speed){
ShowMessage3x7x8(pixel++);
mmss_iniciales = mmss_actuales;
}
else {
if(digitalRead(PSH_BTN_MODO) == LOW) {
delay(10);
while(digitalRead(PSH_BTN_MODO) == LOW);
delay(10);
estadoPanel = RELOJ;
mmss_iniciales = mmss_actuales;
dots = 1; // inicia el modo reloj mostrando los 2 puntos
}
if(digitalRead(PSH_BTN_VMAX) == LOW) {
delay(10);
while(digitalRead(PSH_BTN_VMAX) == LOW);
delay(10);
message_speed = MMSS_MSG_MAX;
mmss_iniciales = mmss_actuales;
}
if(digitalRead(PSH_BTN_VMIN) == LOW) {
delay(10);
while(digitalRead(PSH_BTN_VMIN) == LOW);
delay(10);
message_speed = MMSS_MSG_MIN;
mmss_iniciales = mmss_actuales;
}
}
}
}