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
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.



