#include <stdio.h>
#include "driver/gpio.h"
#include "driver/gptimer.h"
#include "esp_rom_sys.h"
#define A GPIO_NUM_22
#define B GPIO_NUM_5
#define C GPIO_NUM_2
#define D GPIO_NUM_4
#define E GPIO_NUM_16
#define F GPIO_NUM_21
#define G GPIO_NUM_15
#define P GPIO_NUM_0
#define D1 GPIO_NUM_23
#define D2 GPIO_NUM_19
#define D3 GPIO_NUM_18
#define D4 GPIO_NUM_12
#define PA GPIO_NUM_26
gpio_num_t segmentos[8] = {A, B, C, D, E, F, G, P};
gpio_num_t digitos[4] = {D1,D2,D3,D4};
// ===== Variables de tiempo =====
volatile int hora = 0;
volatile int minuto = 0;
volatile int segundo = 0;
int dp_enabled = 1;
// ===== Tabla de números para CÁTODO COMÚN =====
// 1 = ON, 0 = OFF
int numeros[10][8] = {
{0,0,0,0,0,0,1,1}, //0
{1,0,0,1,1,1,1,1}, //1
{0,0,1,0,0,1,0,1}, //2
{0,0,0,0,1,1,0,1}, //3
{1,0,0,1,1,0,0,1}, //4
{0,1,0,0,1,0,0,1}, //5
{0,1,0,0,0,0,0,1}, //6
{0,0,0,1,1,1,1,1}, //7
{0,0,0,0,0,0,0,1}, //8
{0,0,0,0,1,0,0,1} //9
};
// ===== Variables de alarma =====
int alarma[3] = {0, 0, 10};
volatile int alarma_activa = 0;
volatile int alarma_contador = 0;
volatile int alarma_blink = 0;
// ===== Configuración de pines =====
void configuracion_pines() {
for (int i = 0; i < 4; i++) {
gpio_reset_pin(digitos[i]);
gpio_set_direction(digitos[i], GPIO_MODE_OUTPUT);
gpio_set_level(digitos[i], 1); // dígitos apagados
}
for (int i = 0; i < 8; i++) {
gpio_reset_pin(segmentos[i]);
gpio_set_direction(segmentos[i], GPIO_MODE_OUTPUT);
gpio_set_level(segmentos[i], 1); // segmentos apagados en ánodo común
}
}
void mostrar_digito(int Ndigito, int valor, int dp_on) {
// Apagar todos los dígitos antes de actualizar segmentos
for (int i = 0; i < 4; i++) gpio_set_level(digitos[i], 0);
// Cargar el valor de los segmentos (0 = ON, 1 = OFF en ánodo común)
for (int s = 0; s < 7; s++) gpio_set_level(segmentos[s], numeros[valor][s]);
// Control del punto decimal
gpio_set_level(segmentos[7], dp_on ? 0 : 1);
// Encender el dígito correspondiente
gpio_set_level(digitos[Ndigito], 1);
// Mantenerlo encendido un tiempo muy corto para que se vea
esp_rom_delay_us(2000); // ~2 ms, ajusta según parpadeo
// Apagar el dígito para el siguiente ciclo
gpio_set_level(digitos[Ndigito], 0);
}
// ===== Mostrar la hora =====
void mostrar_hora() {
int h1, h2, m1, m2;
//portENTER_CRITICAL(&mux);
h1 = hora / 10;
h2 = hora % 10;
m1 = minuto / 10;
m2 = minuto % 10;
//portEXIT_CRITICAL(&mux);
mostrar_digito(0, h1, 0);
mostrar_digito(1, h2, dp_enabled); // con el DP encendido como ":"
mostrar_digito(2, m1, 0);
mostrar_digito(3, m2, 0);
}
// ===== Timers =====
gptimer_handle_t contador;
gptimer_handle_t señal_alarma;
gptimer_handle_t medio_segundo;
// Config timer para segundos
gptimer_config_t conf_segundos = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 2000, // igual que código 1
};
// Config timer para señal alarma
gptimer_config_t conf_alarma = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 2500, // igual que código 1
};
// Timer de segundos
static bool contar_segundos(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
{
if(!alarma_activa){
dp_enabled=!dp_enabled;
}
segundo++;
if (segundo == 60) {
minuto++;
segundo = 0;
if (minuto == 60) {
hora++;
minuto = 0;
if (hora == 24) hora = 0;
}
}
// Verificar alarma
if (!alarma_activa && hora == alarma[0] && minuto == alarma[1] && segundo == alarma[2]) {
alarma_activa = 1;
alarma_contador = 0;
gptimer_start(señal_alarma);
}
if (alarma_activa) {
alarma_blink = !alarma_blink;
alarma_contador++;
if (alarma_contador >=60) {
alarma_activa = 0;
alarma_blink = 0;
dp_enabled=1;
gptimer_stop(señal_alarma);
}
}
return false;
}
// Config alarma segundos
gptimer_alarm_config_t cfg_alarma_segundos={
.reload_count = 0,
.alarm_count = 2000, // con resolución 2000 Hz = 1s
.flags.auto_reload_on_alarm = true,
};
gptimer_event_callbacks_t cb={
.on_alarm=contar_segundos,
};
// Generar señal alarma
static bool generar_senal(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx){
static bool alto=true;
gpio_set_level(PA,alto);
alto=!alto;
return false;
};
gptimer_alarm_config_t cfg_señal={
.reload_count=0,
.alarm_count=6,
.flags.auto_reload_on_alarm=true,
};
gptimer_event_callbacks_t iniciar_senal={
.on_alarm=generar_senal,
};
// Mostrar alarma (todos los segmentos parpadeando)
void mostrar_alarma(int on) {
for (int i = 0; i < 4; i++) gpio_set_level(digitos[i], 0);
for (int s = 0; s < 8; s++) gpio_set_level(segmentos[s], on ? 1 : 0);
for (int i = 0; i < 4; i++) gpio_set_level(digitos[i], 1);
esp_rom_delay_us(2000);
}
// ===== MAIN =====
void app_main(void) {
// Timer contador
ESP_ERROR_CHECK(gptimer_new_timer(&conf_segundos,&contador));
ESP_ERROR_CHECK(gptimer_set_alarm_action(contador,&cfg_alarma_segundos));
ESP_ERROR_CHECK(gptimer_register_event_callbacks(contador,&cb,NULL));
ESP_ERROR_CHECK(gptimer_enable(contador));
ESP_ERROR_CHECK(gptimer_start(contador));
// Timer señal alarma
ESP_ERROR_CHECK(gptimer_new_timer(&conf_alarma,&señal_alarma));
ESP_ERROR_CHECK(gptimer_set_alarm_action(señal_alarma,&cfg_señal));
ESP_ERROR_CHECK(gptimer_register_event_callbacks(señal_alarma,&iniciar_senal,NULL));
gptimer_enable(señal_alarma);
configuracion_pines();
while(1) {
printf("%02i:%02i:%02i\n", hora, minuto, segundo);
if (alarma_activa) {
mostrar_alarma(alarma_blink);
} else {
mostrar_hora();
}
}
}