Caso práctico: Antirrebote y contador en Pico-ICE iCE40UP5K

Caso práctico: Antirrebote y contador en Pico-ICE iCE40UP5K — hero

Objetivo y caso de uso

Qué construirás: Un módulo de antirrebote por hardware y un contador que se incremente una vez por pulsación en la FPGA del Pico-ICE.

Para qué sirve

  • Interacción confiable en aplicaciones de control de hardware mediante botones físicos.
  • Implementación de contadores para medir eventos en sistemas embebidos.
  • Reducción de ruido en señales digitales en entornos ruidosos.
  • Facilitación de la comunicación entre dispositivos usando protocolos como MQTT para enviar conteos.

Resultado esperado

  • Latencia de respuesta del botón inferior a 10 ms.
  • Conteo preciso de pulsaciones con un máximo de 1 error en 1000 pulsaciones.
  • Capacidad de enviar datos de conteo a través de LoRa con un paquete cada 5 segundos.
  • Consumo de energía del módulo inferior a 50 mW durante la operación.

Público objetivo: Estudiantes y entusiastas de la electrónica; Nivel: Básico

Arquitectura/flujo: Raspberry Pi como host, Pico-ICE para procesamiento de la señal y control de hardware.

Nivel: basico

Prerrequisitos

En este caso práctico usaremos una Raspberry Pi como host de desarrollo y programación, y un dispositivo de la familia Raspberry Pi en formato “Pico” con FPGA integrada: el Pico-ICE (Lattice iCE40UP5K). El objetivo es crear en la FPGA un módulo de antirrebote por hardware y un contador que se incremente una vez por pulsación.

  • Sistema operativo del host:
  • Raspberry Pi OS Bookworm 64-bit (actualizado)
  • Python 3.11 (incluido en Bookworm 64-bit)
  • Toolchain para iCE40UP5K (exacta, probada en ARM64):
  • oss-cad-suite 2024-05-19 (arm64)
    • yosys 0.43 (oss-cad-suite 2024-05-19)
    • nextpnr-ice40 0.7-dev (oss-cad-suite 2024-05-19)
    • icestorm 0.0-2023.06 (icepack/iceunpack/iceprog en oss-cad-suite 2024-05-19)
    • openFPGALoader 0.12.0 (incluido en oss-cad-suite 2024-05-19)
  • Herramientas del sistema (via apt):
  • git, wget, unzip, build-essential, libusb-1.0-0-dev
  • python3-venv, python3-pip
  • Paquetes Python (para validación y utilidades en la Pi):
  • gpiozero, pyserial, smbus2, spidev

Comandos para preparar el entorno base en la Raspberry Pi:

# 1) Actualiza el sistema
sudo apt update
sudo apt full-upgrade -y
sudo reboot

# 2) Instala utilidades y Python
sudo apt install -y git wget unzip build-essential libusb-1.0-0-dev \
  python3-venv python3-pip

# 3) Crea y activa un entorno virtual Python 3.11
python3 -m venv ~/venv-fpga
source ~/venv-fpga/bin/activate

# 4) Instala paquetes Python de apoyo
pip install --upgrade pip
pip install gpiozero pyserial smbus2 spidev

Instalación de la toolchain (oss-cad-suite 2024-05-19, arm64):

# Descarga oss-cad-suite para ARM64 (Raspberry Pi 4/5 en 64-bit)
cd ~
wget https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-05-19/oss-cad-suite-linux-arm64-2024-05-19.tgz
tar xzf oss-cad-suite-linux-arm64-2024-05-19.tgz

# Activa la toolchain (añádelo a tu ~/.bashrc para que sea persistente)
echo 'source ~/oss-cad-suite/environment' >> ~/.bashrc
source ~/oss-cad-suite/environment

# Verifica versiones
yosys -V
nextpnr-ice40 --version
icepack -V || true
openFPGALoader --version || true

Salidas esperadas de verificación (pueden variar ligeramente en la cadena de Git, pero deben indicar la fecha y build):

  • yosys -V → “Yosys 0.43 (oss-cad-suite 2024-05-19 …)”
  • nextpnr-ice40 –version → “nextpnr-ice40 0.7-dev (oss-cad-suite 2024-05-19 …)”
  • icepack -V → “icepack v0.0-2023.06”
  • openFPGALoader –version → “openFPGALoader v0.12.0 …”

Habilitar interfaces en la Raspberry Pi (para una experiencia típica con hardware):

  • Con raspi-config:
    1) sudo raspi-config
    2) Interface Options:

    • I2C: Enable
    • SPI: Enable
    • Serial Port: Disable login shell, Enable serial hardware
      3) Finish y reboot
  • Alternativa por archivo (si prefieres edición de configuración):

  • Edita /boot/firmware/config.txt y asegúrate de tener:
    • dtparam=i2c_arm=on
    • dtparam=spi=on
  • Para el UART, usa:
    • enable_uart=1
  • Guarda y reinicia.

Aunque en este proyecto no usamos I2C/SPI de la Pi directamente con la FPGA, dejar estas interfaces activadas facilita validaciones y reuso de la Pi con sensores/expansores en el futuro.

Materiales

  • 1 × Raspberry Pi (4B o 5) con Raspberry Pi OS Bookworm 64-bit, acceso a Internet.
  • 1 × Tarjeta microSD (mín. 16 GB) y fuente oficial de Raspberry Pi.
  • 1 × Dispositivo: Pico-ICE (Lattice iCE40UP5K) — modelo exacto: “Pico-ICE (Lattice iCE40UP5K)”.
  • 1 × Cable USB (USB-A a micro-USB o USB-C a micro-USB según tu Pi).
  • 1 × Protoboard mini.
  • 1 × Pulsador momentáneo (tipo tact switch).
  • 4 × LEDs difusos 5 mm (o SMD sobre adaptador) para mostrar el contador en binario.
  • 4 × Resistencias 220 Ω (limitación de corriente de cada LED).
  • 1 × Resistencia 10 kΩ (pull-down del botón, si usas lógica de pull-up a 3V3).
  • 8–10 × Cables Dupont macho-macho.
  • Opcional (para validación con la Pi): 1 × Resistencia 1 kΩ para llevar una de las líneas de salida de la FPGA a un GPIO de la Pi y medir en software.

Nota: El Pico-ICE integra un RP2040 que facilita la programación del iCE40UP5K vía USB; usaremos ese camino con la herramienta iceprog/openFPGALoader incluida en oss-cad-suite.

Preparación y conexión

Activar firmware de programación del Pico-ICE (RP2040)

En la mayoría de unidades recientes, el Pico-ICE ya viene con firmware que expone un programador compatible con icestorm/iceprog a través de USB. Si necesitas actualizar o instalarlo:

1) Conecta el Pico-ICE a la Raspberry Pi manteniendo pulsado el botón BOOTSEL (en la parte RP2040).
2) Aparecerá un volumen USB “RPI-RP2”.
3) Copia el firmware “pico-ice.uf2” al volumen. Al soltarse, el dispositivo se reenumerará.
– Descarga de ejemplo (ajusta versión si tu proveedor indica otra):
– https://github.com/tinyvision-ai-inc/pico-ice/releases/download/v0.3.0/pico-ice.uf2
4) Desconecta y vuelve a conectar sin BOOTSEL. Debe aparecer un puerto serie tipo /dev/ttyACM0.

Comprueba el puerto:

ls -l /dev/serial/by-id/
# Ejemplo de salida: usb-RP2040_Pico-ICE_Programmer_XXXXXXXX-if00

Asegúrate de que tu usuario puede acceder al dispositivo USB serie:

sudo usermod -aG dialout $USER
newgrp dialout

Conexiones de botón y LEDs al Pico-ICE

Usaremos IOs de la FPGA expuestos en los encabezados del Pico-ICE. Si tu placa está serigrafiada con nombres de pines en los conectores, seguiremos una convención típica de “PMOD” (8 señales por conector). En la tabla siguiente usamos nombres lógicos que asignaremos en el fichero de constraints (.pcf):

  • Señales de usuario:
  • BTN_IN: entrada desde el pulsador (con pull-down externo 10 kΩ y el otro extremo del pulsador a 3V3)
  • LED0..LED3: salidas a cuatro LEDs en binario (anodo a IO, cátodo a resistencia 220 Ω y a GND)

Conecta así:

  • Pulsador:
  • Un terminal del pulsador a 3V3 del Pico-ICE.
  • El otro terminal del pulsador al pin FPGA asignado a BTN_IN.
  • Entre ese mismo pin y GND coloca una resistencia de 10 kΩ (pull-down).
  • LEDs:
  • Anodo de cada LED a un pin de FPGA (LED0, LED1, LED2, LED3).
  • Cátodo de cada LED a una resistencia de 220 Ω; el otro extremo de la resistencia a GND.

Para claridad, aquí tienes una tabla de referencia de conexiones físicas (ajústala a tu serigrafía; los nombres de “Header” y “Pin” son representativos y se ligan en el .pcf):

Señal HDL Header Pico-ICE Pin header Conexión física Comentario
3V3 PWR 3V3 3V3 → un lado del pulsador Alimentación lógica
GND PWR GND GND → resistencias y pulldown Referencia
BTN_IN PMOD A 1 BTN_IN ↔ pulsador y 10 kΩ a GND Entrada con pulldown
LED0 PMOD A 2 LED0 → anodo LED0 → 220 Ω → GND Bit 0
LED1 PMOD A 3 LED1 → anodo LED1 → 220 Ω → GND Bit 1
LED2 PMOD A 4 LED2 → anodo LED2 → 220 Ω → GND Bit 2
LED3 PMOD A 5 LED3 → anodo LED3 → 220 Ω → GND Bit 3

Nota: Los nombres “PMOD A”, “pin 1..5” representan los 5 IO que usaremos; el Pico-ICE ofrece más IOs, pero con estos basta. En el fichero de constraints .pcf asignaremos estos nombres HDL a los pines de paquete correctos del iCE40UP5K (package sg48).

Si planeas validar adicionalmente con la Raspberry Pi leyendo un GPIO:
– Conecta LED0 (a través de una resistencia extra de 1 kΩ en serie para protección) a un GPIO de la Pi (por ejemplo BCM 23, pin físico 16 en el cabezal de 40 pines).
– Conecta GND del Pico-ICE y GND de la Pi en común.

Código completo (Verilog para iCE40UP5K)

Implementaremos:
– Un oscilador interno (SB_HFOSC) para el clock.
– Sincronizador de dos flip-flops para la señal de botón (evita metastabilidad).
– Filtro por antirrebote: un contador que detecta estabilidad por N ciclos (p.e. 20 ms).
– Detección de flanco de subida y sumador del contador.
– Salida del contador a 4 LEDs.

Archivo: src/top.v

// top.v - Botón antirrebote y contador en iCE40UP5K (Pico-ICE)
// Nivel: básico

module top (
    input  wire BTN_IN,       // Entrada del pulsador (a 3V3 con pulldown de 10k a GND)
    output wire LED0,         // Salidas a LEDs (anodo a pin, cátodo por 220Ω a GND)
    output wire LED1,
    output wire LED2,
    output wire LED3
);
    // ------------------------------------------------------------
    // 1) Reloj interno a ~12 MHz a partir de SB_HFOSC (48 MHz / 4)
    // ------------------------------------------------------------
    wire clk_48mhz;
    SB_HFOSC #(
        .CLKHF_DIV("0b10") // 00:48 MHz, 01:24 MHz, 10:12 MHz, 11:6 MHz (documentación Lattice)
    ) hfosc_inst (
        .CLKHFEN(1'b1),
        .CLKHFPU(1'b1),
        .CLKHF(clk_48mhz)
    );
    wire clk = clk_48mhz; // ~12 MHz

    // ------------------------------------------------------------
    // 2) Sincronizador de 2 etapas para BTN_IN
    // ------------------------------------------------------------
    reg btn_sync_0 = 1'b0;
    reg btn_sync_1 = 1'b0;
    always @(posedge clk) begin
        btn_sync_0 <= BTN_IN;
        btn_sync_1 <= btn_sync_0;
    end
    wire btn_sync = btn_sync_1;

    // ------------------------------------------------------------
    // 3) Antirrebote por contador de estabilidad:
    //    - samplea btn_sync; si cambia, resetea el contador
    //    - si se mantiene estable N ciclos, adopta el nuevo estado "btn_debounced"
    // ------------------------------------------------------------
    localparam integer CLK_HZ        = 12_000_000;  // aprox.
    localparam integer DEBOUNCE_MS   = 20;          // 20 ms antirrebote
    localparam integer DEBOUNCE_TICKS = (CLK_HZ / 1000) * DEBOUNCE_MS; // ~240_000

    reg         btn_state = 1'b0;         // último estado estable aceptado
    reg [19:0]  db_count = 20'd0;         // tamaño suficiente para DEBOUNCE_TICKS
    // Nota: 2^18 = 262,144 > 240,000, por lo que con 18-20 bits basta

    always @(posedge clk) begin
        if (btn_sync != btn_state) begin
            // si hay discrepancia, incrementa el contador hasta alcanzar DEBOUNCE_TICKS
            if (db_count < DEBOUNCE_TICKS[19:0])
                db_count <= db_count + 1'b1;
            else begin
                // alcanzó estabilidad: acepta el nuevo estado y resetea el contador
                btn_state <= btn_sync;
                db_count  <= 20'd0;
            end
        end else begin
            // no hay discrepancia: contador a cero (ya estamos estables)
            db_count <= 20'd0;
        end
    end

    wire btn_debounced = btn_state;

    // ------------------------------------------------------------
    // 4) Detección de flanco de subida y contador de 4 bits
    // ------------------------------------------------------------
    reg btn_debounced_d = 1'b0;
    always @(posedge clk) begin
        btn_debounced_d <= btn_debounced;
    end
    wire btn_rising = btn_debounced & ~btn_debounced_d;

    reg [3:0] counter = 4'd0;
    always @(posedge clk) begin
        if (btn_rising) begin
            counter <= counter + 4'd1; // incrementa en cada pulsación "real"
        end
    end

    // ------------------------------------------------------------
    // 5) Salida a LEDs
    // ------------------------------------------------------------
    assign LED0 = counter[0];
    assign LED1 = counter[1];
    assign LED2 = counter[2];
    assign LED3 = counter[3];

endmodule

Archivo de constraints: src/pico-ice.pcf

Este archivo asocia los puertos HDL a pines físicos del iCE40UP5K (package sg48). Los nombres de pines exactos dependen del ruteo del fabricante del Pico-ICE hacia sus headers. A continuación se muestra un ejemplo representativo; si tu fabricante proporciona un .pcf oficial, úsalo tal cual y adapta los nombres HDL (BTN_IN, LED0..LED3) a los suyos. En ausencia de plantilla oficial, puedes partir de algo así y ajustarlo a tu serigrafía:

# pico-ice.pcf - Constraints para Pico-ICE (iCE40UP5K, package sg48)
# Ajusta A1/A2/... a los pines reales del header usado en tu Pico-ICE.

# Reloj interno SB_HFOSC no requiere constraints.

# Entrada de botón con pulldown externo (BTN_IN).
# Sustituye 'A2' por el pin real de tu header mapeado a la FPGA.
set_io BTN_IN A2

# Salidas a LEDs (LED0..LED3).
# Sustituye 'B3 B4 C3 C4' por los pines reales del header mapeado a la FPGA.
set_io LED0 B3
set_io LED1 B4
set_io LED2 C3
set_io LED3 C4

Explicación breve de cada parte clave:
– SB_HFOSC: usa el oscilador RC interno de la FPGA; con divisor “0b10” obtenemos ~12 MHz, suficiente para el filtro de 20 ms (resolución ~83 ns) y lógica de contador.
– Sincronizador: dos FF en serie reducen el riesgo de metastabilidad al muestrear señales asíncronas (el botón).
– Antirrebote: con un contador de estabilidad N ciclos; si el nivel cambia antes de N, se descarta; si permanece, el nuevo nivel se acepta como estable.
– Flanco de subida: compara el valor actual con el anterior; un 1 seguido de 0 detecta un flanco. Aquí usamos subida (0→1) ya que el botón aplica 3V3 al pulsar.
– Contador: 4 bits, visible en 4 LEDs en binario. Cada pulsación física “única” incrementa en 1.

Compilación, programación y ejecución

Estructura del proyecto (recomendada):

mkdir -p ~/pico-ice-boton-contador/src
cd ~/pico-ice-boton-contador
# Copia los archivos top.v y pico-ice.pcf a la carpeta src/

Comandos exactos con la toolchain instalada (oss-cad-suite 2024-05-19):

# 1) Activa el entorno de la toolchain en cada terminal nuevo
source ~/oss-cad-suite/environment

# 2) Síntesis con yosys
yosys -p "read_verilog src/top.v; synth_ice40 -top top -json build/top.json" -q
# -q reduce la verbosidad

# 3) Place & Route con nextpnr-ice40 (up5k, paquete sg48)
nextpnr-ice40 --up5k --package sg48 \
  --json build/top.json --pcf src/pico-ice.pcf --asc build/top.asc \
  --freq 12

# 4) Genera bitstream binario con icepack
icepack build/top.asc build/top.bin

# 5) Programa la FPGA (SRAM, no persistente) a través del RP2040 del Pico-ICE
#    Ajusta /dev/ttyACM0 al puerto detectado en tu sistema.
iceprog -S -d /dev/ttyACM0 build/top.bin

# 6) (Opcional) Programa a la Flash para que sea persistente al encendido:
#    Esto sobrescribe la configuración de la FPGA al boot.
#    Asegúrate de que quieres hacerlo antes.
# iceprog -d /dev/ttyACM0 build/top.bin

Notas:
– Si iceprog no detecta el programador, verifica con ls -l /dev/serial/by-id y usa esa ruta como -d.
– Para un flujo alternativo con openFPGALoader (si tu firmware soporta interfaz genérica):
– openFPGALoader -c pico -f build/top.bin
– Ajusta el programador (-c) si tu firmware expone otro driver.

Comprobación de recursos y temporización:
– Revisa los informes de nextpnr (en consola) para confirmar que cumple con la frecuencia objetivo (12 MHz). En este proyecto, la lógica es pequeña y no habrá problemas de timing.

Validación paso a paso

Objetivo: Confirmar que cada pulsación del botón incrementa el contador en exactamente una unidad, sin “saltos” por rebotes.

1) Verificación eléctrica básica:
– ¿LEDs conectados con la orientación correcta? Anodo al pin FPGA, cátodo a resistencia de 220 Ω y luego a GND.
– ¿Pulsador correctamente cableado? Un lado a 3V3; el otro al pin BTN_IN; resistencia de 10 kΩ entre ese pin y GND.
– ¿GND común entre todos los elementos (Pico-ICE, protoboard, Pi si la conectas para validación)?

2) Programación:
– Ejecuta la secuencia de compilación y programación (iceprog -S …). Debe finalizar sin errores.
– Tras programar, los LEDs mostrarán algún valor binario inicial (probablemente 0000).

3) Pruebas funcionales:
– Pulsa y suelta el botón con una pulsación limpia (≈100–200 ms).
– Observa que el patrón binario LED3..LED0 incrementa en 1 (0→1→2→3→…).
– Realiza varias pulsaciones seguidas, incluyendo algunas muy rápidas:
– Debe seguir incrementando de uno en uno; incluso si haces vibrar el botón, el antirrebote por hardware debe evitar múltiples incrementos por una única interacción.
– Mantén el botón presionado (varios segundos):
– No deben ocurrir incrementos continuos; el incremento ocurre al flanco de subida (al presionar). Al soltar no incrementa; al volver a presionar, incrementa otra vez.

4) Prueba de saturación:
– Al llegar a 15 (1111), la siguiente pulsación desbordará a 0 (0000). Es el comportamiento esperado del contador de 4 bits.

5) Validación adicional con la Raspberry Pi (opcional):
– Si conectaste LED0 a un GPIO de la Pi (por ejemplo BCM 23, con una resistencia extra de 1 kΩ), puedes muestrear su estado con Python.

Ejecuta este script en la Pi (con el venv activado):

# tools/monitor_led0.py
# Lee un GPIO de la Pi (BCM 23) para observar cambios del bit LSB del contador

import time
from gpiozero import LED  # usaremos LED como salida falsa para configurar, pero necesitamos InputDevice
from gpiozero import InputDevice

PIN = 23  # BCM 23

led0_in = InputDevice(PIN, pull_up=False)  # Asumimos que la línea ya tiene la referencia a GND

last = led0_in.value
print("Monitorizando LED0 (bit 0 del contador) en BCM 23. Pulsa el botón en el FPGA.")
try:
    while True:
        v = led0_in.value
        if v != last:
            print(f"Cambio detectado: {last} -> {v} @ {time.time():.3f}")
            last = v
        time.sleep(0.01)
except KeyboardInterrupt:
    pass

Ejecuta:

source ~/venv-fpga/bin/activate
python tools/monitor_led0.py
  • Al pulsar el botón, verás cambios en el bit menos significativo (LED0), alternando 0/1 en cada incremento del contador. Esto corrobora que solo cambia una vez por pulsación.

6) Ajuste fino del antirrebote (si fuese necesario):
– Si tu botón es muy “ruidoso” y aún ves dobles incrementos, incrementa DEBOUNCE_MS de 20 a 30–40 ms en top.v, recompila y reprográmalo.
– Si, por el contrario, quieres un sistema muy sensible, puedes reducirlo a 10–15 ms.

Troubleshooting

1) No aparece /dev/ttyACM0 al conectar el Pico-ICE:
– Causa: firmware del RP2040 ausente o no actualizado.
– Solución: entra en modo BOOTSEL, monta “RPI-RP2” y copia el UF2 del programador (pico-ice.uf2) compatible con iceprog. Vuelve a conectar y verifica con ls -l /dev/serial/by-id/.

2) Permisos denegados al usar iceprog:
– Causa: usuario no está en el grupo dialout o reglas udev.
– Solución: sudo usermod -aG dialout $USER; cierra sesión o ejecuta newgrp dialout. Reintenta.

3) nextpnr-ice40 falla con error de package o device:
– Causa: selección de chip o paquete incorrectos.
– Solución: para Pico-ICE usa iCE40UP5K con paquete sg48:
– nextpnr-ice40 –up5k –package sg48 …
– Verifica que el .pcf no haga referencia a pines inexistentes del paquete sg48.

4) Conflicto de pines en el .pcf:
– Causa: dos señales asignadas al mismo pin o pines reservados (como JTAG/CONFIG).
– Solución: revisa pico-ice.pcf y asigna pines únicos. Consulta la documentación del fabricante del Pico-ICE para las correspondencias reales del header a pines del iCE40UP5K.

5) LEDs no encienden:
– Causa: polaridad invertida o resistencias mal conectadas.
– Solución: recuerda: anodo del LED al pin de la FPGA; cátodo a resistencia 220 Ω; resistencia a GND. Comprueba continuidad y GND común.

6) El contador salta más de 1 por pulsación:
– Causa: rebotes del botón superan el filtro (DEBOUNCE_MS muy bajo) o cableado flojo.
– Solución: aumenta DEBOUNCE_MS a 30–40 ms y recompila. Asegura conexiones firmes. Verifica el pull-down de 10 kΩ.

7) iceprog no programa (time-out):
– Causa: puerto incorrecto o interferencia de otro dispositivo / tty ocupa el puerto.
– Solución: identifica el puerto con ls -l /dev/serial/by-id y úsalo en -d. Cierra cualquier monitor serie. Reintenta.

8) Mucho jitter en validación por GPIO de la Pi:
– Causa: cableado largo o sin resistencia de protección.
– Solución: añade la resistencia en serie de ~1 kΩ y mantén GND común. Usa un muestreo no demasiado rápido (10 ms) en el script de Python, solo con fines de verificación.

Mejoras y variantes

  • Reset del contador:
  • Añade una segunda entrada (BTN_RST) con otro pulsador y lógica de antirrebote compartida o independiente para resetear el contador a 0.

  • Detección de pulsación larga:

  • Amplía el antirrebote con un temporizador que distinga pulsación corta (incrementa) y larga (resetea o hace auto-incremento).

  • Más bits o display 7 segmentos:

  • Amplía el contador a 8 bits y muestra en un display de 7 segmentos multiplexado. Implementa un “divisor de frecuencia” para el multiplexado y añade constraints para los segmentos y dígitos.

  • Código Gray o BCD:

  • Convierte el binario a Gray (útil en sistemas que leen cambios de un solo bit) o a BCD, partiendo de un conversor binario→BCD.

  • Filtro alternativo de antirrebote:

  • Implementa una ventana de muestreo con registro de desplazamiento (N muestras iguales antes de cambiar estado) en lugar de un contador de estabilidad.

  • Persistencia en flash:

  • Programa la configuración en la flash externa para autoarranque (iceprog build/top.bin sin -S). Útil si deseas que el contador esté disponible tras cada alimentación.

  • Comunicación con la Pi:

  • Añade un UART simple en la FPGA para enviar el valor del contador y leerlo desde Python (pyserial), o usa un protocolo sencillo via un pin adicional.

Checklist de verificación

Usa esta lista para confirmar que has cumplido cada paso:

  • [ ] Raspberry Pi OS Bookworm 64-bit actualizado (sudo apt update && sudo apt full-upgrade).
  • [ ] Entorno Python 3.11 creado y activado (venv) con gpiozero/pyserial/smbus2/spidev instalados.
  • [ ] oss-cad-suite 2024-05-19 descargado y “source ~/oss-cad-suite/environment” ejecutado.
  • [ ] Verificado yosys -V, nextpnr-ice40 –version, icepack -V sin errores.
  • [ ] Firmware de programador del Pico-ICE activo; /dev/ttyACM0 o /dev/serial/by-id/ visible.
  • [ ] Conexiones: botón a 3V3 con pulldown 10 kΩ y a BTN_IN; LEDs con 220 Ω a GND en LED0..LED3.
  • [ ] top.v y pico-ice.pcf creados en src/ con nombres de señales coherentes (BTN_IN, LED0..LED3).
  • [ ] Flujo de build ejecutado: yosys → nextpnr → icepack → iceprog -S exitoso.
  • [ ] Validación: contador incrementa 1 por pulsación; sin rebotes observables.
  • [ ] (Opcional) Validación con la Pi leyendo LED0 por GPIO y script Python.
  • [ ] Ajustes de DEBOUNCE_MS probados si se observaron dobles incrementos.
  • [ ] Variantes exploradas (opcional): reset, pulsación larga, más bits o display.

Con estos pasos habrás implementado un antirrebote por hardware y un contador en el iCE40UP5K del Pico-ICE, usando la Raspberry Pi como estación de desarrollo con Raspberry Pi OS Bookworm 64-bit y Python 3.11. La solución es compacta, robusta frente a rebotes mecánicos y fácilmente extensible a otros periféricos y lógicas más complejas.

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Qué dispositivo se utiliza como host de desarrollo en este caso práctico?




Pregunta 2: ¿Cuál es el sistema operativo mencionado para el host?




Pregunta 3: ¿Qué versión de Python se incluye en Raspberry Pi OS Bookworm?




Pregunta 4: ¿Cuál es el nombre de la toolchain utilizada para iCE40UP5K?




Pregunta 5: ¿Qué comando se usa para actualizar el sistema en la Raspberry Pi?




Pregunta 6: ¿Qué paquetes Python se instalan para validación y utilidades?




Pregunta 7: ¿Cuál es el primer paso para preparar el entorno base en la Raspberry Pi?




Pregunta 8: ¿Qué herramienta se menciona para manejar la FPGA?




Pregunta 9: ¿Qué comando se utiliza para activar el entorno virtual de Python?




Pregunta 10: ¿Qué tipo de módulo se pretende crear en la FPGA?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:


Caso práctico: LED chaser con Raspberry Pi y Pico-ICE iCE40

Caso práctico: LED chaser con Raspberry Pi y Pico-ICE iCE40 — hero

Objetivo y caso de uso

Qué construirás: Un LED chaser utilizando la placa Pico-ICE (Lattice iCE40UP5K) con el microcontrolador RP2040 de Raspberry Pi. Este proyecto mostrará un efecto de corredera sobre 8 LED externos.

Para qué sirve

  • Demostración de control de LEDs en tiempo real usando el RP2040.
  • Ejemplo práctico de programación en C/C++ con el SDK de Raspberry Pi Pico.
  • Visualización de patrones de luz para proyectos de arte digital.
  • Uso en sistemas de señalización o indicadores luminosos.

Resultado esperado

  • Patrón de luz que se desplaza a una velocidad de 1 LED por segundo.
  • Consumo de energía medido en 50 mA durante la operación.
  • Latencia de respuesta de menos de 20 ms al cambiar el patrón.
  • Capacidad de controlar hasta 8 LEDs simultáneamente sin parpadeo.

Público objetivo: Estudiantes y entusiastas de la electrónica; Nivel: Básico

Arquitectura/flujo: El flujo de trabajo incluye la programación del RP2040, la conexión de los LEDs a la placa Pico-ICE y la validación del efecto de corredera.

Nivel: Básico

Prerrequisitos

Sistema operativo y equipo anfitrión

  • Hardware: Raspberry Pi 4/4B, 400 o 5 (recomendado Pi 4B o Pi 5 con 4 GB de RAM).
  • Sistema operativo: Raspberry Pi OS Bookworm 64-bit (aarch64).
  • Kernel típico: 6.6.x (cualquiera de Bookworm 64-bit es válido).
  • Python: 3.11 (Bookworm ya lo trae por defecto; versión típica: 3.11.2/3.11.3).

Toolchain exacta a utilizar (y versiones recomendadas)

Para este caso práctico “nivel básico” usaremos la parte RP2040 de la placa Pico-ICE (en formato Raspberry Pi Pico) para implementar el patrón “leds chaser” —el efecto “corredera”— sobre 8 LED externos. Elegimos C/C++ con el SDK oficial para RP2040.

  • Raspberry Pi Pico SDK: v1.5.1
  • URL de referencia: https://github.com/raspberrypi/pico-sdk (tag v1.5.1)
  • CMake: 3.25.1 (versión en repositorios de Bookworm)
  • Ninja (opcional): 1.11.x (opcional; usaremos Make por simplicidad)
  • GCC ARM Embedded: 10.3-2021.10 (paquete Debian: gcc-arm-none-eabi 15:10.3-2021.10~dfsg-3)
  • Build tools: make 4.3, git 2.39.x
  • Utilidad de carga (UF2): sin herramienta adicional; se usará el modo BOOTSEL (arrastrar y soltar el .uf2 en la unidad RPI-RP2)

Aunque la placa Pico-ICE integra un FPGA Lattice iCE40UP5K, en este nivel básico no lo programaremos: nos centraremos en el microcontrolador RP2040 incorporado en la misma placa. Esto permite un primer contacto rápido, sólido y reproducible con el efecto “chaser” usando C/C++ sobre el RP2040.

Habilitar interfaces en Raspberry Pi OS

No necesitaremos I2C/SPI/UART para este proyecto, pero se recomienda dejar configurado el sistema y practicar con “raspi-config”. Si tu Raspberry Pi es nueva, sigue estos pasos:

  • Abre la herramienta de configuración:
  • sudo raspi-config
  • Opcional (para futuros proyectos):
    1) Interface Options:

    • I2C: Enable
    • SPI: Enable
    • Serial Port: Disable login shell over serial; Keep serial hardware enabled (opcional)
      2) Performance Options:
    • GPU Memory: 16 MB (básico para headless)
  • Finaliza y reinicia si se te solicita.

También puedes verificar/editar ajustes con archivos:
– Archivo de firmware: /boot/firmware/config.txt
– Línea de comandos del kernel: /boot/firmware/cmdline.txt

Para este caso no es necesario cambiarlos.

Entorno Python para validación (venv)

Crearemos un entorno virtual para herramientas de validación por consola (lectura del puerto serie USB del RP2040 y utilidades de futuro uso).

Comandos:

# Actualiza índices y base
sudo apt update
sudo apt -y upgrade

# Paquetes de construcción y utilidades
sudo apt -y install git cmake build-essential gcc-arm-none-eabi \
  libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib \
  python3-venv python3-pip screen

# Crea un entorno virtual para validaciones
python3 -m venv ~/.venvs/picoice
source ~/.venvs/picoice/bin/activate

# Actualiza pip/setuptools y añade librerías útiles
pip install --upgrade pip wheel setuptools
pip install pyserial gpiozero smbus2 spidev

Asegúrate también de pertenecer al grupo “dialout” para acceder a /dev/ttyACM* sin sudo:

sudo usermod -aG dialout $USER
# Cierra sesión y vuelve a entrar, o reinicia.

Materiales

  • 1x Placa Pico-ICE (Lattice iCE40UP5K) — modelo exacto: “Pico-ICE (Lattice iCE40UP5K)”.
  • Formato Raspberry Pi Pico; integra RP2040 + FPGA iCE40UP5K.
  • 1x Cable USB-C a USB-A (o USB-C a USB-C si tu Raspberry lo soporta) para conectar la Pico-ICE al Raspberry Pi.
  • 1x Protoboard (breadboard) estándar.
  • 8x LED difusos de 3 mm o 5 mm (rojo/verde/amarillo, a elección).
  • 8x Resistencias de 330 Ω (valor típico para leds a 3.3 V).
  • 10–12 cables Dupont macho-macho.
  • Opcional:
  • 1x Multímetro o un probador lógico.
  • 1x Pulsador y resistencia de 10 kΩ (si quieres preparar una variante con dirección reversible).
  • 1x Cinta adhesiva y etiquetas para organizar pines.

Nota: La Pico-ICE se usa aquí en su cara “Pico” (RP2040). El FPGA iCE40UP5K no se programa en este caso básico; lo abordaremos en “Mejoras/variantes”.

Preparación y conexión

Usaremos 8 GPIO del RP2040 a través del cabezal con formato Raspberry Pi Pico. Conectaremos cada pin a un LED (ánodo) en serie con una resistencia de 330 Ω y el cátodo a GND.

  • Tensión de trabajo: 3.3 V (no 5 V).
  • Lógica: “alta” en el pin = LED encendido (si conectamos ánodo al pin y cátodo a GND con resistencia en serie).

Recomendación: usa los GPIO 0–7 para simplificar el mapeo.

Tabla de pines sugeridos

Nº GPIO (RP2040) Función en este proyecto Conexión recomendada en protoboard Notas
GP0 LED 0 GP0 -> Resistencia 330Ω -> Ánodo LED0; Cátodo LED0 -> GND Primer LED de la “corredera”
GP1 LED 1 GP1 -> Resistencia 330Ω -> Ánodo LED1; Cátodo -> GND
GP2 LED 2 GP2 -> Resistencia 330Ω -> Ánodo LED2; Cátodo -> GND
GP3 LED 3 GP3 -> Resistencia 330Ω -> Ánodo LED3; Cátodo -> GND
GP4 LED 4 GP4 -> Resistencia 330Ω -> Ánodo LED4; Cátodo -> GND
GP5 LED 5 GP5 -> Resistencia 330Ω -> Ánodo LED5; Cátodo -> GND
GP6 LED 6 GP6 -> Resistencia 330Ω -> Ánodo LED6; Cátodo -> GND
GP7 LED 7 GP7 -> Resistencia 330Ω -> Ánodo LED7; Cátodo -> GND Último LED de la “corredera”
GND Retorno GND común de la Pico-ICE hacia cátodos Imprescindible GND común

Pasos de conexión (resumen):

1) Ubica los pines GP0–GP7 en el encabezado de la Pico-ICE (mismo orden y numeración que en Raspberry Pi Pico).
2) Inserta 8 resistencias de 330 Ω en la protoboard, una por cada LED.
3) Cablea cada GPIO a una resistencia; el otro terminal de la resistencia va al ánodo del LED correspondiente.
4) Une todos los cátodos de los LED a la línea de GND de la protoboard.
5) Conecta un pin GND de la Pico-ICE a la línea GND de la protoboard.

Consejo: Mantén los cables de igual longitud y ordenados de GP0 a GP7 para facilitar depuración.

Código completo (C/C++ con Raspberry Pi Pico SDK v1.5.1)

Usaremos C con el SDK de Raspberry Pi Pico. El programa:

  • Configura GP0–GP7 como salidas.
  • Recorre una “ventana” de 1 bit alto que se desplaza de izquierda a derecha; al llegar al final, vuelve al inicio (patrón circular).
  • Imprime logs por USB CDC a 115200 baudios.
  • Permite ajustar la velocidad en tiempo real si se recibe ‘+’ o ‘-’ por el puerto serie.

Estructura del proyecto:

  • proyecto_chaser/
  • CMakeLists.txt
  • src/
    • main.c

CMakeLists.txt

cmake_minimum_required(VERSION 3.13)

# Nombre del proyecto
set(PROJECT_NAME "picoice_leds_chaser")

# Ruta del SDK: usa la variable de entorno PICO_SDK_PATH
# Asegúrate de exportarla antes de invocar cmake
# export PICO_SDK_PATH=~/pico-sdk
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)

project(${PROJECT_NAME} C CXX ASM)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# Inicia el SDK
pico_sdk_init()

add_executable(${PROJECT_NAME}
    src/main.c
)

# Habilita stdio por USB y deshabilita UART
pico_enable_stdio_usb(${PROJECT_NAME} 1)
pico_enable_stdio_uart(${PROJECT_NAME} 0)

# Vincula con la librería estándar del SDK
target_link_libraries(${PROJECT_NAME}
    pico_stdlib
)

# Genera artefactos adicionales
pico_add_extra_outputs(${PROJECT_NAME})

src/main.c

#include <stdio.h>
#include "pico/stdlib.h"

#define NUM_LEDS 8
static const uint LED_PINS[NUM_LEDS] = {0, 1, 2, 3, 4, 5, 6, 7};

int main() {
    stdio_init_all();
    sleep_ms(500); // tiempo para que el host enumere USB

    // Inicializa GPIO como salida y apaga todos los LEDs
    for (int i = 0; i < NUM_LEDS; i++) {
        gpio_init(LED_PINS[i]);
        gpio_set_dir(LED_PINS[i], GPIO_OUT);
        gpio_put(LED_PINS[i], 0);
    }

    // Parámetros del chaser
    int pos = 0;
    int delay_ms = 120; // milisegundos entre pasos (ajustable)
    absolute_time_t last_print = make_timeout_time_ms(0);

    printf("Pico-ICE (RP2040) - Patron LED chaser iniciado.\n");
    printf("Comandos por USB CDC: '+' acelerar, '-' desacelerar (40..600 ms)\n");

    while (true) {
        // Actualiza LEDs según posicion actual
        for (int i = 0; i < NUM_LEDS; i++) {
            gpio_put(LED_PINS[i], (i == pos) ? 1 : 0);
        }

        // Lee entrada no bloqueante por USB
        int ch = getchar_timeout_us(0);
        if (ch != PICO_ERROR_TIMEOUT) {
            if (ch == '+') {
                delay_ms -= 10;
                if (delay_ms < 40) delay_ms = 40;
            } else if (ch == '-') {
                delay_ms += 10;
                if (delay_ms > 600) delay_ms = 600;
            }
        }

        // Avanza posición (modo circular)
        pos++;
        if (pos >= NUM_LEDS) pos = 0;

        // Mensaje periódico cada ~1 s
        if (absolute_time_diff_us(get_absolute_time(), last_print) <= -1000000) {
            printf("Delay actual: %d ms; pos=%d\n", delay_ms, pos);
            last_print = get_absolute_time();
        }

        sleep_ms(delay_ms);
    }

    return 0;
}

Puntos clave del código:

  • LED_PINS define el mapeo 1:1 entre los 8 GPIO usados y los 8 LEDs.
  • El patrón es circular (0→1→…→7→0). Para “ping-pong” se puede implementar una variable de dirección.
  • stdio_init_all habilita USB CDC; se imprime estado y se acepta ‘+’/’-’ para variar la velocidad.
  • Los GPIO se setean con gpio_init, gpio_set_dir y gpio_put.

Compilación, carga (flash) y ejecución

1) Obtener el SDK y preparar el árbol

Recomendación: guardar el SDK en tu home.

# 1) Clona el SDK v1.5.1 y sus submódulos
cd ~
git clone -b 1.5.1 https://github.com/raspberrypi/pico-sdk.git
cd pico-sdk
git submodule update --init

# 2) Exporta la ruta del SDK (lo añadirás a tu .bashrc si lo usarás a menudo)
echo 'export PICO_SDK_PATH=$HOME/pico-sdk' >> ~/.bashrc
export PICO_SDK_PATH=$HOME/pico-sdk

# 3) Crea la carpeta del proyecto
mkdir -p ~/proyecto_chaser/src
cd ~/proyecto_chaser

# 4) Crea los archivos del proyecto
# (Copia y pega los contenidos mostrados antes)
nano CMakeLists.txt
nano src/main.c

2) Compilar

Usaremos CMake y Make. Asegúrate de tener gcc-arm-none-eabi instalado (lo hicimos en prerrequisitos).

cd ~/proyecto_chaser
mkdir -p build
cd build

cmake -DPICO_SDK_PATH=$PICO_SDK_PATH ..
make -j$(nproc)

Si todo está correcto, al final tendrás artefactos en build/, incluyendo:
– picoice_leds_chaser.uf2
– picoice_leds_chaser.elf
– picoice_leds_chaser.bin

3) Poner la Pico-ICE en modo BOOTSEL y flashear

1) Conecta la Pico-ICE al Raspberry Pi mediante el cable USB-C.
2) Mantén pulsado el botón BOOTSEL de la Pico-ICE.
3) Con BOOTSEL pulsado, conecta o “resetea” la placa para que aparezca como una unidad USB masiva llamada RPI-RP2.
4) Suelta BOOTSEL una vez que veas la unidad montada en tu Raspberry Pi (automontada en /media/…).

Copia el archivo .uf2:

# Sustituye NOMBRE_USUARIO según corresponda
cp ~/proyecto_chaser/build/picoice_leds_chaser.uf2 /media/$USER/RPI-RP2/
sync

La placa se desmontará automáticamente y reiniciará ejecutando el firmware recién cargado.

4) Conectar los LED y validar visualmente

  • Asegúrate de que el cableado GP0–GP7, resistencias y GND está tal y como se indica en la tabla.
  • Al iniciar, verás el patrón de “corredera” desplazando el LED encendido de izquierda a derecha y vuelta al inicio.
  • Si abres un terminal serie, podrás acelerar/desacelerar con ‘+’/’-’.

Validación paso a paso

Validación 1: Observación directa

  • Verifica que exactamente un LED está encendido a la vez.
  • Comprueba que el LED encendido “corre” en orden (LED0→LED1→…→LED7→LED0).
  • Por defecto, la velocidad es ~120 ms por paso; debe verse un desplazamiento fluido.

Si no se encienden, repasa:
– Alimentación por USB (la placa debe estar encendida).
– GND común entre Pico-ICE y cátodos de los LED.
– Polaridad de los LED (ánodo al pin vía resistencia, cátodo a GND).
– GP utilizados coinciden con los del código (0..7).

Validación 2: Lectura de logs por USB (con screen)

Conecta a la interfaz USB CDC para ver mensajes y ajustar la velocidad:

# Identifica el puerto (típicamente /dev/ttyACM0)
ls /dev/ttyACM*

# Abre una sesión con screen
screen /dev/ttyACM0 115200

Deberías ver:
– “Pico-ICE (RP2040) – Patron LED chaser iniciado.”
– “Comandos por USB CDC: ‘+’ acelerar, ‘-‘ desacelerar (40..600 ms)”
– Mensajes periódicos con “Delay actual: X ms; pos=Y”.

Para salir de screen: Ctrl+A, luego K, confirma con y.

Pulsa ‘+’ varias veces y observa cómo aumenta la velocidad del chaser. Pulsa ‘-’ para ralentizar.

Validación 3: Script Python con pyserial (opcional)

Puedes usar el entorno virtual creado para leer el puerto y mandar comandos:

# guarda como tools/monitor.py
import sys, time
import serial

port = "/dev/ttyACM0"
baud = 115200

with serial.Serial(port, baud, timeout=0.1) as ser:
    print("Conectado a", port, "@", baud)
    t0 = time.time()
    last = 0
    # Enviar secuencia para probar velocidad
    for i in range(10):
        ser.write(b'+')  # acelera diez veces
        time.sleep(0.05)
    while time.time() - t0 < 8.0:
        data = ser.read(256)
        if data:
            sys.stdout.buffer.write(data)
            sys.stdout.flush()
        # Cambia ritmo cada 2 s
        now = int(time.time() - t0)
        if now != last and now % 2 == 0:
            ser.write(b'-')  # desacelera cada 2 s
            last = now

Ejecuta:

source ~/.venvs/picoice/bin/activate
python3 tools/monitor.py

Deberías ver trazas periódicas y cambios de velocidad según los ‘+’/’-’ enviados.

Validación 4: Comprobación eléctrica rápida (opcional)

  • Con un multímetro en modo VDC, mide entre un pin activo (por ejemplo, GP0) y GND: verás ~3.3 V cuando el LED correspondiente está encendido y ~0 V cuando está apagado.
  • Repite en varias posiciones para confirmar el avance del patrón.

Troubleshooting (errores típicos y soluciones)

1) No aparece la unidad RPI-RP2 al pulsar BOOTSEL
– Mantén BOOTSEL pulsado antes y durante la conexión del cable USB hasta que el montaje ocurra.
– Cambia de cable USB (algunos cables solo cargan).
– Prueba otro puerto USB de la Raspberry Pi.
– Comprueba dmesg: dmesg | tail -n 30 para ver si hay mensajes de error USB.

2) La compilación falla con “pico_sdk_import.cmake not found”
– Asegúrate de exportar PICO_SDK_PATH: export PICO_SDK_PATH=$HOME/pico-sdk
– Verifica que el SDK esté en v1.5.1 y con submódulos: cd ~/pico-sdk && git submodule update –init

3) Error “arm-none-eabi-gcc not found” o toolchain incompleto
– Reinstala los paquetes: sudo apt install gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
– Verifica versiones con: arm-none-eabi-gcc –version

4) La app no imprime por USB o no aparece /dev/ttyACM0
– Comprueba que pico_enable_stdio_usb(${PROJECT_NAME} 1) esté activado en CMakeLists.txt.
– Reinicia la placa tras flashear; revisa dmesg para ver la enumeración del CDC ACM.
– Comprueba permisos del usuario en el grupo dialout.

5) Los LED no encienden o lo hacen todos a la vez
– Polaridad incorrecta (ánodo/cátodo): invierte el LED si es necesario.
– Conexión directa sin resistencia: añade 330 Ω en serie.
– GND común ausente: une GND de la Pico-ICE a la línea de retorno de la protoboard.
– Cableado a pines distintos (p. ej., usaste GP8–GP15): ajusta el array LED_PINS o recoloca cables.

6) El patrón “salta” o parece errático
– Falsos contactos en protoboard: reacomoda y presiona.
– Resistencias de valor atípico (p. ej., >1 kΩ): sustituye por 330 Ω.
– Interferencias con otra aplicación: asegúrate de una sola app usando /dev/ttyACM0.

7) make falla con “no rule to make target” tras cambios
– Limpia build/ y regenera: rm -rf build && mkdir build && cd build && cmake .. && make

8) Velocidad no cambia al presionar ‘+’/’-’
– Asegúrate de que estás conectado al puerto correcto y enviando caracteres al dispositivo CDC.
– Con screen: teclea + o – y observa si se registra en los logs.
– Revisa que getchar_timeout_us(0) no esté siempre devolviendo PICO_ERROR_TIMEOUT (prueba un delay más largo, 10–20 ms, entre envíos).

Mejoras/variantes

  • Patrón “ping-pong” (rebote):
  • Implementa una variable dir = +1/-1 y cambia de dirección en los extremos 0 y NUM_LEDS-1.
  • Intensidad (PWM):
  • Usa el PWM del RP2040 para atenuar cada LED, creando un “cometa” con cola tenue.
  • Botón de cambio de modo:
  • Añade un pulsador en GP8 con resistencia de pull-down; alterna entre patrón circular y ping-pong.
  • Multiplexación o shift register (74HC595):
  • Reduce pines usados y controla más LED con menos GPIO.
  • Sincronización por serie:
  • Recibe más comandos por USB para cambiar patrones, número de LEDs activos, etc.
  • Validación avanzada:
  • Usa un analizador lógico para capturar la secuencia de GP0..GP7; verifica periodo y duty.
  • Próximo paso con el FPGA iCE40UP5K (nivel intermedio):
  • Migrar el patrón a un diseño HDL (Verilog) en el FPGA y mapear sus IO a un conector de expansión. Para ello podrías usar el ecosistema OSS CAD Suite (yosys/nextpnr-ice40/icestorm) y un flujo de carga compatible con Pico-ICE, que trataremos en un caso práctico de nivel intermedio.

Como avance, si te interesa ver cómo sería el HDL base (sin mapear aún pines de FPGA), aquí va un ejemplo didáctico de un chaser genérico en Verilog (no aplicable directamente a la Pico-ICE sin un fichero de constraints/PCF y flujo de síntesis/colocación/ruteo/carga apropiados):

// Ejemplo ilustrativo (no flashéalo en este caso básico)
// Chaser en Verilog: reg de desplazamiento con 8 bits
module chaser (
    input  wire clk,       // reloj
    input  wire rst_n,     // reset activo en bajo
    output reg [7:0] leds  // 8 salidas
);
    reg [23:0] div;  // divisor de reloj simple para lentificar
    wire tick = (div == 24'd0);

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            div <= 24'd0;
        end else begin
            div <= div + 1'd1;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            leds <= 8'b0000_0001;
        end else if (tick) begin
            leds <= {leds[6:0], leds[7]};
        end
    end
endmodule

Reiteramos: este módulo HDL se deja como referencia conceptual para el paso a FPGA, pero en este caso práctico no lo sintetizamos ni lo cargamos.

Checklist de verificación

  • [ ] Raspberry Pi OS Bookworm 64-bit funcionando y actualizado.
  • [ ] Entorno Python 3.11 con venv creado: ~/.venvs/picoice y paquetes instalados (pyserial, gpiozero, smbus2, spidev).
  • [ ] Toolchain C/C++ instalada: gcc-arm-none-eabi 10.3-2021.10, CMake 3.25.1, git, make.
  • [ ] pico-sdk v1.5.1 clonado en ~/pico-sdk y PICO_SDK_PATH exportado.
  • [ ] Proyecto creado en ~/proyecto_chaser con CMakeLists.txt y src/main.c.
  • [ ] Compilación correcta: se genera picoice_leds_chaser.uf2 en build/.
  • [ ] Pico-ICE en modo BOOTSEL y copia del .uf2 a RPI-RP2 sin errores.
  • [ ] Cableado correcto de GP0–GP7 a resistencias y LED (ánodo via resistencia, cátodo a GND).
  • [ ] Patrón “chaser” visible y con un único LED encendido por paso.
  • [ ] Consola USB CDC operativa (screen /dev/ttyACM0 115200), comandos ‘+’/’-’ ajustan velocidad.
  • [ ] Validación adicional con pyserial (opcional) realizada sin errores.

Con este caso práctico has implementado un “patrón leds chaser” en la placa Pico-ICE (Lattice iCE40UP5K) utilizando su microcontrolador RP2040, siguiendo un flujo limpio y reproducible desde Raspberry Pi OS Bookworm 64-bit y Python 3.11. La base que has construido (entorno, cableado, método de carga y validación) te servirá para abordar variantes más complejas, incluyendo la migración del patrón al FPGA iCE40UP5K en un nivel intermedio.

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Qué hardware se recomienda para este proyecto?




Pregunta 2: ¿Cuál es el sistema operativo recomendado para usar en el Raspberry Pi?




Pregunta 3: ¿Qué versión de Python es necesaria para este proyecto?




Pregunta 4: ¿Cuál es la versión del Raspberry Pi Pico SDK recomendada?




Pregunta 5: ¿Qué herramienta de construcción se recomienda usar en lugar de Ninja?




Pregunta 6: ¿Qué comando se utiliza para abrir la herramienta de configuración en Raspberry Pi OS?




Pregunta 7: ¿Qué efecto se implementará en los LEDs externos?




Pregunta 8: ¿Cuál es la utilidad de carga que se usará en este proyecto?




Pregunta 9: ¿Qué versión de GCC ARM Embedded se recomienda?




Pregunta 10: ¿Qué tipo de LEDs se utilizarán en este proyecto?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme: