/* ==========================================================================
STACKER 5 x 4 - Raspberry Pi Pico 2
--------------------------------------------------------------------------
MAPA DE PINES
--------------------------------------------------------------------------
Señal GPIO Dirección Pull Propósito
L1C0 0 salida deshabilitado Nivel 1 (abajo), columna 0 (izq)
L1C1 1 salida deshabilitado Nivel 1, columna 1
L1C2 2 salida deshabilitado Nivel 1, columna 2
L1C3 3 salida deshabilitado Nivel 1, columna 3 (der)
L2C0 4 salida deshabilitado Nivel 2, columna 0
L2C1 5 salida deshabilitado Nivel 2, columna 1
L2C2 6 salida deshabilitado Nivel 2, columna 2
L2C3 7 salida deshabilitado Nivel 2, columna 3
L3C0 8 salida deshabilitado Nivel 3, columna 0
L3C1 9 salida deshabilitado Nivel 3, columna 1
L3C2 10 salida deshabilitado Nivel 3, columna 2
L3C3 11 salida deshabilitado Nivel 3, columna 3
L4C0 12 salida deshabilitado Nivel 4, columna 0
L4C1 13 salida deshabilitado Nivel 4, columna 1
L4C2 14 salida deshabilitado Nivel 4, columna 2
L4C3 15 salida deshabilitado Nivel 4, columna 3
L5C0 16 salida deshabilitado Nivel 5 (arriba), columna 0
L5C1 17 salida deshabilitado Nivel 5, columna 1
L5C2 18 salida deshabilitado Nivel 5, columna 2
L5C3 19 salida deshabilitado Nivel 5, columna 3
BTN_STP 20 entrada pull-up interno Botón STOP, a GND, IRQ flanco de bajada
BTN_RST 21 entrada pull-up interno Botón RESTART, a GND, IRQ flanco de bajada
Conexión de cada LED: GPIO --- R 220 ohm --- ánodo LED --- cátodo --- GND
Conexión de cada botón: GPIO --- botón --- GND (pull-up interno activado)
--------------------------------------------------------------------------
CÁLCULO DE LAS RESISTENCIAS (220 ohm, una por LED)
--------------------------------------------------------------------------
Datos:
Vcc (salida del GPIO en alto) = 3.3 V
Vf (caída directa del LED rojo) = 2.0 V
R = 220 ohm
Ley de Ohm sobre la resistencia:
V_R = Vcc - Vf = 3.3 - 2.0 = 1.3 V
I = V_R / R = 1.3 / 220 = 0.0059 A = 5.9 mA por LED
Potencia disipada:
P_R = I^2 * R = (0.0059)^2 * 220 = 7.7 mW (resistencia de 1/4 W: sobra)
P_LED = Vf * I = 2.0 * 0.0059 = 11.8 mW
Peor caso de consumo (los 20 LEDs encendidos en el parpadeo de victoria):
I_total = 20 * 5.9 mA = 118 mA
El riel de 3V3 de la Pico 2 entrega bastante más que eso, así que es seguro.
--------------------------------------------------------------------------
MAPEO DE BITS
--------------------------------------------------------------------------
El tablero completo son 20 bits dentro de un solo uint32_t:
bit = 4 * nivel + columna (nivel 0 = abajo, columna 0 = izquierda)
El bit N está cableado al GPIO N.
Nivel 1 -> bits 0-3 Nivel 2 -> bits 4-7 Nivel 3 -> bits 8-11
Nivel 4 -> bits 12-15 Nivel 5 -> bits 16-19
Patrón inicial del nivel 1: 0b0011 (columnas 0 y 1).
========================================================================== */
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#define L1C0 0
#define L1C1 1
#define L1C2 2
#define L1C3 3
#define L2C0 4
#define L2C1 5
#define L2C2 6
#define L2C3 7
#define L3C0 8
#define L3C1 9
#define L3C2 10
#define L3C3 11
#define L4C0 12
#define L4C1 13
#define L4C2 14
#define L4C3 15
#define L5C0 16
#define L5C1 17
#define L5C2 18
#define L5C3 19
#define BTN_STP 20
#define BTN_RST 21
#define COLUMNAS 4
#define NIVELES 5
#define MASCARA_NIVEL 0xFu
#define PATRON_INICIAL 0b0011u
#define VEL_N1 320
#define VEL_N2 260
#define VEL_N3 200
#define VEL_N4 150
#define VEL_N5 100
#define ANTIRREBOTE_MS 200
#define JUGANDO 0
#define GANO 1
#define PERDIO 2
const uint32_t LED_MASK =
1u << L1C0 | 1u << L1C1 | 1u << L1C2 | 1u << L1C3
| 1u << L2C0 | 1u << L2C1 | 1u << L2C2 | 1u << L2C3
| 1u << L3C0 | 1u << L3C1 | 1u << L3C2 | 1u << L3C3
| 1u << L4C0 | 1u << L4C1 | 1u << L4C2 | 1u << L4C3
| 1u << L5C0 | 1u << L5C1 | 1u << L5C2 | 1u << L5C3;
volatile bool flag_stop = false;
volatile bool flag_rst = false;
static void button_isr(uint gpio, uint32_t events) {
static uint32_t ultimo = 0;
uint32_t ahora = to_ms_since_boot(get_absolute_time());
if (ahora - ultimo < ANTIRREBOTE_MS) return;
ultimo = ahora;
if (gpio == BTN_STP) {
flag_stop = true;
}
if (gpio == BTN_RST) {
flag_rst = true;
}
}
static void mostrar(uint32_t tablero) {
sio_hw->gpio_set = tablero & LED_MASK;
sio_hw->gpio_clr = ~tablero & LED_MASK;
}
int main(void) {
gpio_init(L1C0); gpio_set_dir(L1C0, GPIO_OUT); gpio_disable_pulls(L1C0);
gpio_init(L1C1); gpio_set_dir(L1C1, GPIO_OUT); gpio_disable_pulls(L1C1);
gpio_init(L1C2); gpio_set_dir(L1C2, GPIO_OUT); gpio_disable_pulls(L1C2);
gpio_init(L1C3); gpio_set_dir(L1C3, GPIO_OUT); gpio_disable_pulls(L1C3);
gpio_init(L2C0); gpio_set_dir(L2C0, GPIO_OUT); gpio_disable_pulls(L2C0);
gpio_init(L2C1); gpio_set_dir(L2C1, GPIO_OUT); gpio_disable_pulls(L2C1);
gpio_init(L2C2); gpio_set_dir(L2C2, GPIO_OUT); gpio_disable_pulls(L2C2);
gpio_init(L2C3); gpio_set_dir(L2C3, GPIO_OUT); gpio_disable_pulls(L2C3);
gpio_init(L3C0); gpio_set_dir(L3C0, GPIO_OUT); gpio_disable_pulls(L3C0);
gpio_init(L3C1); gpio_set_dir(L3C1, GPIO_OUT); gpio_disable_pulls(L3C1);
gpio_init(L3C2); gpio_set_dir(L3C2, GPIO_OUT); gpio_disable_pulls(L3C2);
gpio_init(L3C3); gpio_set_dir(L3C3, GPIO_OUT); gpio_disable_pulls(L3C3);
gpio_init(L4C0); gpio_set_dir(L4C0, GPIO_OUT); gpio_disable_pulls(L4C0);
gpio_init(L4C1); gpio_set_dir(L4C1, GPIO_OUT); gpio_disable_pulls(L4C1);
gpio_init(L4C2); gpio_set_dir(L4C2, GPIO_OUT); gpio_disable_pulls(L4C2);
gpio_init(L4C3); gpio_set_dir(L4C3, GPIO_OUT); gpio_disable_pulls(L4C3);
gpio_init(L5C0); gpio_set_dir(L5C0, GPIO_OUT); gpio_disable_pulls(L5C0);
gpio_init(L5C1); gpio_set_dir(L5C1, GPIO_OUT); gpio_disable_pulls(L5C1);
gpio_init(L5C2); gpio_set_dir(L5C2, GPIO_OUT); gpio_disable_pulls(L5C2);
gpio_init(L5C3); gpio_set_dir(L5C3, GPIO_OUT); gpio_disable_pulls(L5C3);
sio_hw->gpio_clr = LED_MASK;
gpio_init(BTN_STP);
gpio_set_dir(BTN_STP, GPIO_IN);
gpio_pull_up(BTN_STP);
gpio_init(BTN_RST);
gpio_set_dir(BTN_RST, GPIO_IN);
gpio_pull_up(BTN_RST);
gpio_set_irq_enabled_with_callback(BTN_STP, GPIO_IRQ_EDGE_FALL, true, &button_isr);
gpio_set_irq_enabled(BTN_RST, GPIO_IRQ_EDGE_FALL, true);
while (true) {
flag_rst = false;
flag_stop = false;
uint32_t tablero = 0;
uint32_t sobrevive = PATRON_INICIAL;
int estado = JUGANDO;
mostrar(tablero);
for (int nivel = 0; nivel < NIVELES; nivel++) {
uint32_t patron = sobrevive;
uint32_t hueco;
int desp = COLUMNAS * nivel;
int direccion = 1;
int velocidad;
hueco = MASCARA_NIVEL << desp;
if (nivel == 0) {
velocidad = VEL_N1;
} else if (nivel == 1) {
velocidad = VEL_N2;
} else if (nivel == 2) {
velocidad = VEL_N3;
} else if (nivel == 3) {
velocidad = VEL_N4;
} else {
velocidad = VEL_N5;
}
while (flag_stop == false && flag_rst == false) {
tablero = (tablero & ~hueco) | (patron << desp);
mostrar(tablero);
sleep_ms(velocidad);
if (flag_stop == true || flag_rst == true) {
break;
}
if (direccion > 0 && (patron & (1u << (COLUMNAS - 1)))) {
direccion = -1;
} else if (direccion < 0 && (patron & 1u)) {
direccion = 1;
}
if (direccion > 0) {
patron = patron << 1;
} else {
patron = patron >> 1;
}
patron = patron & MASCARA_NIVEL;
}
if (flag_rst == true) {
break;
}
flag_stop = false;
if (nivel == 0) {
sobrevive = patron;
} else {
uint32_t abajo = (tablero >> (desp - COLUMNAS)) & MASCARA_NIVEL;
sobrevive = patron & abajo;
}
tablero = (tablero & ~hueco) | (sobrevive << desp);
mostrar(tablero);
if (sobrevive == 0) {
estado = PERDIO;
break;
}
if (nivel == NIVELES - 1) {
estado = GANO;
}
}
while (estado != JUGANDO && flag_rst == false) {
flag_stop = false;
if (estado == GANO) {
mostrar(LED_MASK);
sleep_ms(120);
mostrar(0);
sleep_ms(120);
} else {
mostrar(tablero);
sleep_ms(400);
mostrar(0);
sleep_ms(400);
}
}
}
}