Caso práctico: Antirrebotes y LED en iCEBreaker iCE40UP5K

Caso práctico: Antirrebotes y LED en iCEBreaker iCE40UP5K — hero

Objetivo y caso de uso

Qué construirás: Implementar un sistema de antirrebotes para botones y control de LED en la placa Lattice iCEBreaker utilizando Verilog.

Para qué sirve

  • Controlar el encendido y apagado de un LED en respuesta a la pulsación de un botón.
  • Evitar lecturas erróneas debido a rebotes mecánicos en botones físicos.
  • Integrar el sistema con protocolos de comunicación como MQTT para reportar el estado del LED.
  • Utilizar el bus TWAI para la comunicación entre dispositivos en un entorno de IoT.

Resultado esperado

  • Latencia de respuesta del sistema de antirrebotes inferior a 50 ms.
  • Reducción de errores de lectura de botones a menos del 1% en condiciones de uso normal.
  • Capacidad de enviar mensajes de estado del LED a través de MQTT con un tiempo de entrega de menos de 100 ms.
  • Consumo de energía del sistema inferior a 100 mW durante la operación.

Público objetivo: Ingenieros y estudiantes de electrónica; Nivel: Básico

Arquitectura/flujo: Diseño en Verilog, síntesis con Yosys, implementación con nextpnr-ice40, y programación con openFPGALoader.

Nivel: Básico

Prerrequisitos

Sistema operativo y versiones probadas

  • GNU/Linux x86_64 (recomendado). Validado en:
  • Ubuntu 24.04 LTS (64 bits)
  • Debian 12 (Bookworm, 64 bits)
  • También viable en macOS 13/14 (Apple Silicon o Intel) y Windows 10/11 (vía WSL2), pero los comandos mostrados a continuación están escritos y comprobados para GNU/Linux.

Toolchain exacta (iCE40)

Usaremos la cadena de herramientas de código abierto recomendada para iCE40:
– Yosys (síntesis): 0.40
– nextpnr-ice40 (place & route): 0.7
– Project IceStorm (empacado): icestorm 0.0+20240107 (icepack/iceunpack/icetime)
– openFPGALoader (programación): 0.13.0

Para simplificar la instalación, se recomienda usar la distribución “OSS CAD Suite” que incluye los binarios anteriores en una sola descarga.

  • OSS CAD Suite (build): 2024-10-20
  • yosys 0.40 (oss-cad-suite 20241020)
  • nextpnr-ice40 0.7 (oss-cad-suite 20241020)
  • icestorm 0.0+20240107 (oss-cad-suite 20241020)
  • openFPGALoader 0.13.0 (oss-cad-suite 20241020)

Comandos de verificación de versiones (ejecútalos tras instalar y exportar el PATH del paquete):

yosys -V
nextpnr-ice40 --version
icepack -h
openFPGALoader --version

Ejemplo de salida esperada (puede variar ligeramente):
– yosys 0.40 (git sha…, oss-cad-suite 20241020)
– nextpnr-ice40 0.7
– icepack (Project IceStorm) usage… (no siempre muestra versión directa, pero proviene del bundle 20241020)
– openFPGALoader v0.13.0

Materiales

  • 1 x FPGA board: Lattice iCEBreaker (iCE40UP5K, paquete SG48, oscilador de 12 MHz integrado)
  • 1 x Cable USB (USB-C o micro‑USB según revisión del iCEBreaker; usa el cable oficial del kit si lo tienes)
  • 1 x PC con GNU/Linux (Ubuntu 24.04 recomendado)
  • Toolchain instalada (OSS CAD Suite 2024-10-20)
  • Opcional:
  • Multímetro (para comprobar 5 V/3.3 V y tierra en el USB si hiciera falta)
  • Lupa o buena iluminación (para identificar serigrafía de LED y botón en la placa)

Notas específicas del iCEBreaker:
– El iCEBreaker integra:
– Un oscilador de 12 MHz
– Al menos un botón de usuario (BTN1)
– Un LED de usuario
– En este caso práctico usaremos exclusivamente el botón BTN1 y el LED de usuario integrados en la placa, sin hardware adicional.

Preparación y conexión

Instalación de la toolchain (OSS CAD Suite 2024-10-20)

  1. Crea un directorio de herramientas en tu HOME (opcional):
  2. mkdir -p ~/tools
  3. Descarga el paquete (Linux x64):
  4. wget https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-10-20/oss-cad-suite-linux-x64-20241020.tgz -O ~/tools/oss-cad-suite-20241020.tgz
  5. Descomprime:
  6. tar -xzf ~/tools/oss-cad-suite-20241020.tgz -C ~/tools
  7. Exporta el PATH (temporal para la sesión actual):
  8. export PATH=~/tools/oss-cad-suite/bin:$PATH
  9. Verifica versiones con los comandos mostrados en la sección de prerrequisitos.

Sugerencia: añade la línea de export PATH a tu ~/.bashrc o ~/.zshrc para no tener que repetirla.

Conexión física del iCEBreaker

  • Conecta la placa iCEBreaker al PC con el cable USB. El LED de “power” (si presente) debería encenderse.
  • No conectes ningún otro periférico. Usaremos el botón y el LED integrados.

Mapeo de pines (señales usadas)

Para la Lattice iCEBreaker (iCE40UP5K, SG48), en este proyecto usaremos:
– Reloj principal: oscilador de 12 MHz
– Un botón de usuario (BTN1)
– Un LED de usuario (LED)

Tabla de pines/puertos del diseño:

Señal lógica (port) Función Pin FPGA (SG48) Notas importantes
clk12 Reloj de sistema 12 MHz 35 Entrada global de reloj (oscilador onboard)
btn_n Botón de usuario (activo-bajo) 10 BTN1: pulsado = 0 (pull-up)
led_n LED de usuario (activo-bajo) 39 LED ON cuando señal = 0
  • btn_n: la “n” indica activo-bajo. Lo normal en iCEBreaker es que el botón use pull-up y al pulsar vaya a GND.
  • led_n: el LED suele estar cableado para encenderse cuando el pin se pone a 0 (activo-bajo).

Importante: Mantén estos nombres exactos en el archivo de constraints (.pcf) y en el código Verilog para coherencia.

Código completo

En este caso práctico implementaremos:
– Un sincronizador de dos etapas para la entrada del botón (mitigación de metastabilidad).
– Un filtro antirrebotes basado en contador saturado (tiempo de estabilización configurable; usaremos ~10 ms a 12 MHz).
– El LED refleja el estado del botón “limpio” (LED encendido mientras el botón está presionado), teniendo en cuenta que el LED es activo-bajo.

Estructura de archivos propuesta (puedes crearla en tu carpeta de trabajo):
– src/top.v (módulo principal con antirrebotes)
– constraints/icebreaker.pcf (mapeo de pines para iCEBreaker)
– build/ (carpeta de salida; se creará automáticamente)

Archivo de constraints: constraints/icebreaker.pcf

# iCEBreaker (iCE40UP5K-SG48) - Mapeo mínimo para este proyecto
# Reloj de 12 MHz
set_io clk12 35

# Botón de usuario (BTN1) - activo-bajo
# Habilitamos pull-up interno como refuerzo (la placa ya suele tener pull-up).
set_io -pullup yes btn_n 10

# LED de usuario - activo-bajo (LED encendido con '0')
set_io led_n 39

Código Verilog: src/top.v

/*
 * Proyecto: button-debouncing-and-led en iCEBreaker (iCE40UP5K)
 * Toolchain: yosys 0.40 + nextpnr-ice40 0.7 + icestorm (20240107) + openFPGALoader 0.13.0
 * Reloj: 12 MHz
 * Descripción:
 *   - Sincroniza y des-rebota un botón activo-bajo (btn_n).
 *   - Controla un LED activo-bajo (led_n) que refleja el estado limpio del botón.
 */

module top (
    input  wire clk12,   // 12 MHz
    input  wire btn_n,   // botón activo-bajo
    output wire led_n    // LED activo-bajo
);

    // 1) Sincronizador de dos etapas para la señal del botón (evita metastabilidad)
    reg [1:0] btn_sync;
    always @(posedge clk12) begin
        // Invertimos btn_n aquí para trabajar con '1' = pulsado, '0' = no pulsado
        btn_sync <= { btn_sync[0], ~btn_n };
    end
    wire btn_sample = btn_sync[1];

    // 2) Filtro antirrebotes por contador saturado
    //    - Consideramos "estable" cuando la muestra coincide durante al menos DEBOUNCE_MS
    //    - A 12 MHz, 10 ms ≈ 120_000 ciclos
    localparam integer CLK_HZ        = 12_000_000;
    localparam integer DEBOUNCE_MS   = 10;                // puedes ajustar 5..20 ms
    localparam integer CNTR_MAX      = (CLK_HZ/1000)*DEBOUNCE_MS;
    localparam integer CNTR_WIDTH    = $clog2(CNTR_MAX+1);

    reg [CNTR_WIDTH-1:0] cnt = {CNTR_WIDTH{1'b0}};
    reg                  btn_stable = 1'b0;  // estado "limpio" del botón (1 = pulsado)

    always @(posedge clk12) begin
        // Si la muestra concuerda con el estado estable actual, reseteamos el contador
        // porque no hay "transición" sostenida que debamos confirmar.
        if (btn_sample == btn_stable) begin
            cnt <= {CNTR_WIDTH{1'b0}};
        end else begin
            // La muestra difiere del estado estable; incrementamos el contador hasta CNTR_MAX
            if (cnt == CNTR_MAX[CNTR_WIDTH-1:0]) begin
                // Suficiente tiempo con la nueva muestra: confirmamos el cambio de estado
                btn_stable <= btn_sample;
                cnt        <= {CNTR_WIDTH{1'b0}};
            end else begin
                cnt <= cnt + 1'b1;
            end
        end
    end

    // 3) LED activo-bajo: encender LED cuando btn_stable = 1 (botón pulsado)
    assign led_n = ~btn_stable;

endmodule

Puntos clave del código:
– Sincronizador de doble flip-flop (btn_sync) para reducir riesgos de metastabilidad en la entrada asíncrona del botón.
– Antirrebotes por tiempo: el estado solo cambia si la lectura distinta se mantiene N ciclos (10 ms a 12 MHz).
– Manejo de activos-bajo:
– btn_n se invierte al entrar (btn_sample = 1 cuando se pulsa).
– led_n se asigna a la negación de btn_stable (para encender el LED cuando btn_stable = 1).

Alternativas:
– Si prefieres que el LED “togglee” con cada pulsación (en vez de reflejar el estado sostenido), puedes agregar detección de flanco en btn_stable y conmutar un registro de LED. En este caso básico mantenemos la relación directa para centrar el foco en el antirrebotes.

Compilación, grabación y ejecución

Estructura de directorios (ejemplo):
– Tu carpeta de proyecto: ~/proyectos/icebreaker-debounce-led
– src/top.v
– constraints/icebreaker.pcf
– build/ (se creará para outputs)

Desde la carpeta raíz del proyecto (donde está la carpeta src/ y constraints/), ejecuta:

1) Síntesis (Yosys 0.40):

yosys -p "read_verilog src/top.v; synth_ice40 -top top -json build/top.json"

2) Place & Route (nextpnr-ice40 0.7) para iCE40UP5K, paquete SG48, frecuencia objetivo 12 MHz:

nextpnr-ice40 --up5k --package sg48 --json build/top.json --pcf constraints/icebreaker.pcf --asc build/top.asc --freq 12

3) Empaquetado (IceStorm / icepack):

icepack build/top.asc build/top.bin

4) Programación (openFPGALoader 0.13.0) para iCEBreaker:

openFPGALoader -b iCEBreaker build/top.bin

Notas y opciones:
– openFPGALoader detecta el cable FTDI y programa la FPGA del iCEBreaker. Si tienes permisos de udev restringidos, puede requerir sudo:
– sudo openFPGALoader -b iCEBreaker build/top.bin
– Para verificar la conexión antes de programar:
– openFPGALoader -c
– Para limpiar la carpeta de builds (opcional):
– rm -rf build && mkdir build

Salida esperada:
– Yosys: informe de síntesis con recuento de celdas y topología.
– nextpnr: informe de enrutamiento, uso de recursos y “Max frequency”.
– icepack: genera el binario (.bin) sin errores.
– openFPGALoader: mensaje “Done” o “Success” tras programación.

Validación paso a paso

1) Preparación visual:
– Identifica el LED de usuario (serigrafiado como “LED” o “USR LED”) y el botón BTN1 en la placa iCEBreaker.
– Asegúrate de que la placa está alimentada por el USB (LED de power encendido).

2) Estado inicial:
– Sin pulsar el botón, el LED debería estar apagado. Recuerda que led_n es activo-bajo y nuestro diseño lo apaga cuando btn_stable = 0 (no pulsado).

3) Prueba de pulsación lenta:
– Pulsa y mantén presionado BTN1 durante 1–2 segundos: el LED debe encenderse de forma estable, sin parpadeos.
– Suelta el botón: el LED debe apagarse sin parpadeos.

4) Prueba de pulsaciones rápidas:
– Haz pulsaciones rápidas (pero reales, no “golpes” mecánicos): el LED debería encenderse y apagarse limpiamente sin destellos.
– Si la pulsación es tan corta que dura menos de ~10 ms, es posible que el LED no llegue a encenderse. Esto es normal dadas las constantes del antirrebotes: DEBOUNCE_MS = 10 ms.

5) Observación del retardo:
– Notarás un pequeño retardo (hasta ~10 ms) entre el acto de pulsar/soltar y el encendido/apagado del LED. Ese es el efecto del filtro: evita que rebotes de varios milisegundos causen falsos cambios.

6) Verificación con temporización (opcional):
– Puedes reducir DEBOUNCE_MS a 5 ms (edita src/top.v) para notar menor retardo, recompila y reprograma.
– También puedes subirlo a 20 ms para endurecer el filtro.

7) Confirmación final:
– La funcionalidad queda validada si:
– El LED sigue el estado “real” del botón sin parpadeos espurios.
– No se ven transiciones falsas al pulsar/soltar.

Troubleshooting

1) El LED se ve “invertido” (encendido sin pulsar, apagado al pulsar)
– Causa: Inversión de lógica no acorde a activo-bajo.
– Solución: Verifica que led_n se asigne como led_n = ~btn_stable. Si tu LED estuviera cableado activo-alto (no es el caso típico en iCEBreaker), usa led = btn_stable y ajusta el pcf al nombre correcto de puerto.

2) No se enciende nunca el LED al pulsar
– Causas posibles:
– Cable USB o alimentación inadecuadas.
– Asignación de pines incorrecta en constraints/icebreaker.pcf.
– Botón no mapeado (nombre del port distinto en Verilog y PCF).
– DEBOUNCE_MS demasiado grande para tus pruebas (pulsas menos tiempo).
– Soluciones:
– Revisa la tabla de pines y que tus nombres coinciden: clk12, btn_n, led_n.
– Vuelve a sintetizar y programar.
– Prueba manteniendo 1–2 s el botón.
– Baja DEBOUNCE_MS a 5 ms para pruebas.

3) openFPGALoader no detecta la placa
– Causas:
– Falta de permisos (udev).
– Cable USB solo carga (sin datos) o dañado.
– Puerto USB en mal estado.
– Soluciones:
– Ejecuta con sudo: sudo openFPGALoader -b iCEBreaker build/top.bin
– Prueba otro puerto USB y/o otro cable.
– Verifica con openFPGALoader -c que aparece el FTDI.

4) nextpnr-ice40 falla por target/device
– Causa: Parámetros de dispositivo/paquete erróneos.
– Solución: Usa exactamente: –up5k –package sg48. Si usas otra variante de placa iCE40, adapta device y package, pero para iCEBreaker debes mantener estos.

5) Errores de nombres de puertos en Yosys/nextpnr
– Causa: Diferencia entre los nombres en top.v y en el .pcf.
– Solución: Asegúrate de que los puertos del módulo top son clk12, btn_n, led_n y que el .pcf usa esos mismos nombres.

6) El LED parpadea ligeramente al pulsar/soltar
– Causa: DEBOUNCE_MS demasiado bajo para el botón o jitter en la muestra.
– Solución: Aumenta DEBOUNCE_MS a 10–20 ms. Vuelve a sintetizar y cargar.

7) Violaciones de timing en nextpnr
– Causa: Configuración de –freq demasiado exigente o lógica adicional añadida.
– Solución: Para este diseño simple a 12 MHz no deberían aparecer. Verifica que usas –freq 12 y no has agregado lógica costosa en el camino del reloj.

8) Advertencias de pull-up o pines reservados
– Causa: Algunas herramientas emiten advertencias si el pin ya tiene pull-up externo o si hay restricciones de bancos.
– Solución: Las pull-up internas son “extra” y en general no molestan. Si la advertencia molesta, puedes quitar “-pullup yes” del botón en el .pcf (la placa ya tiene pull-up).

Mejoras/variantes

  • Toggle por pulsación:
  • En lugar de reflejar el estado debounced, detecta el flanco de subida de btn_stable y conmuta un registro led_state. Asigna led_n = ~led_state. Así cada pulsación alterna encendido/apagado.

  • Indicador de “pulsación larga”:

  • Implementa un temporizador adicional: si btn_stable permanece 1 durante >1 s, cambia el modo del LED (por ejemplo, parpadeo lento con un divisor de reloj).

  • Multiplicidad de botones:

  • Instancia el antirrebotes por cada botón (reutiliza el módulo o vectoriza el filtro) y controla múltiples salidas (más LEDs, pines PMOD, etc.).

  • Debouncer por muestreo periódico:

  • En lugar de evaluar en cada ciclo a 12 MHz, crea un “tick” a 1 kHz y muestrea el botón N veces consecutivas iguales para confirmar el estado. Ahorra recursos si escalas a muchos botones.

  • PWM en el LED:

  • Aplica un modulador PWM simple para variar el brillo cuando el botón está pulsado, ilustrando conceptos de temporización.

  • Uso del oscilador interno:

  • iCE40UP5K dispone de SB_HFOSC (oscilador interno). Para proyectos sin dependencia del reloj externo, podrías activarlo y prescindir del pin de reloj. Para iCEBreaker usamos el de 12 MHz integrado, que es estable y simple.

  • Verificación formal:

  • Con SymbiYosys puedes definir propiedades (por ejemplo: “btn_stable solo cambia si la entrada permanece distinta por N ciclos”) y comprobarlas, reforzando calidad del diseño.

Checklist de verificación

  • [ ] Sistema operativo: GNU/Linux (Ubuntu 24.04 o similar).
  • [ ] Toolchain instalada: OSS CAD Suite 2024-10-20 en PATH.
  • [ ] Versiones comprobadas:
  • [ ] yosys -V → 0.40
  • [ ] nextpnr-ice40 –version → 0.7
  • [ ] openFPGALoader –version → 0.13.0
  • [ ] Estructura de proyecto creada: src/, constraints/, build/.
  • [ ] Archivo constraints/icebreaker.pcf con pines:
  • [ ] clk12 → 35
  • [ ] btn_n → 10 (pull-up yes)
  • [ ] led_n → 39
  • [ ] Código Verilog en src/top.v con:
  • [ ] Sincronizador de 2 etapas
  • [ ] Antirrebotes con CNTR_MAX ≈ 120000 (10 ms a 12 MHz)
  • [ ] led_n = ~btn_stable
  • [ ] Síntesis (Yosys) ejecutada sin errores.
  • [ ] Place & Route (nextpnr-ice40) sin errores, device up5k y package sg48.
  • [ ] Empaquetado (icepack) generó build/top.bin.
  • [ ] Programación con openFPGALoader -b iCEBreaker exitosa.
  • [ ] Validación:
  • [ ] LED apagado sin pulsar.
  • [ ] LED encendido al pulsar, sin parpadeos.
  • [ ] Al soltar, LED se apaga sin rebotes visibles.
  • [ ] Ajuste de DEBOUNCE_MS probado (opcional), recompilación y reprogramación OK.
  • [ ] Comprensión de al menos una mejora/variante (opcional).

Apéndice: Comandos agrupados (copiar/pegar)

1) Preparar build:

mkdir -p build

2) Síntesis:

yosys -p "read_verilog src/top.v; synth_ice40 -top top -json build/top.json"

3) Place & Route:

nextpnr-ice40 --up5k --package sg48 --json build/top.json --pcf constraints/icebreaker.pcf --asc build/top.asc --freq 12

4) Empaquetado:

icepack build/top.asc build/top.bin

5) Programación:

openFPGALoader -b iCEBreaker build/top.bin

Con esto, tienes un flujo completo y reproducible para el caso práctico “button-debouncing-and-led” en Lattice iCEBreaker (iCE40UP5K), usando la toolchain abierta yosys + nextpnr-ice40 + icestorm + openFPGALoader, con versiones concretas y comandos exactos.

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: ¿Cuál es el sistema operativo recomendado para la instalación?




Pregunta 2: ¿Cuál es la versión de Yosys recomendada para la cadena de herramientas iCE40?




Pregunta 3: ¿Qué herramienta se utiliza para la síntesis en la cadena de herramientas de iCE40?




Pregunta 4: ¿Qué distribución se recomienda para simplificar la instalación?




Pregunta 5: ¿Cuál es la versión de openFPGALoader mencionada en el artículo?




Pregunta 6: ¿Qué tipo de placa FPGA se menciona en el artículo?




Pregunta 7: ¿Qué comando se utiliza para verificar la versión de nextpnr-ice40?




Pregunta 8: ¿Qué tipo de oscilador tiene la placa Lattice iCEBreaker?




Pregunta 9: ¿Cuál es la versión de nextpnr-ice40 recomendada?




Pregunta 10: ¿Qué herramienta se utiliza para el empacado en la cadena de herramientas de iCE40?




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:
error: Contenido Protegido / Content is protected !!
Scroll to Top