Objetivo y caso de uso
Qué construirás: Un controlador de servos PWM de bajo jitter utilizando Raspberry Pi Pico-ICE y MicroPython.
Para qué sirve
- Controlar la posición de servos en proyectos de robótica.
- Implementar sistemas de automatización del hogar con control preciso de actuadores.
- Desarrollar prototipos de dispositivos interactivos que requieren movimiento controlado.
Resultado esperado
- Latencia de respuesta del servo inferior a 10 ms.
- Precisión de control de posición de ±1 grado.
- Capacidad de controlar hasta 8 servos simultáneamente con un jitter menor a 5 ms.
Público objetivo: Desarrolladores de hardware y entusiastas de la robótica; Nivel: Medio
Arquitectura/flujo: Controlador de servos conectado a Raspberry Pi Pico-ICE mediante PWM, gestionado a través de MicroPython.
Nivel: Medio
Prerrequisitos
Sistema operativo y entorno base
- Equipo anfitrión: una Raspberry Pi (cualquier modelo de 64 bits) con:
- Raspberry Pi OS Bookworm 64‑bit (versión probada: 2024-10-22)
- Kernel y firmware por defecto de Bookworm
- Python 3.11 (versión probada: 3.11.2)
Toolchain exacta (probada en este caso práctico)
La siguiente toolchain y versiones son las utilizadas y verificadas para este tutorial, orientado a programar el RP2040 de la Pico‑ICE en MicroPython y a operar desde la Raspberry Pi:
- Firmware de dispositivo:
- MicroPython para RP2040 (UF2): v1.22.2 (build rp2-pico-20240222-v1.22.2.uf2)
- Herramientas en la Raspberry Pi (host):
- mpremote (CLI): 1.23.0
- pyserial (biblioteca Python): 3.5
- gpiozero (opcional): 1.6.2
- pip: 24.2 (o posterior)
- venv de Python 3.11 (módulo estándar)
- Utilidades del sistema:
- udev y grupos de acceso serie (dialout) por defecto en Raspberry Pi OS Bookworm
Notas:
– No es necesario compilar C/C++ ni usar el SDK del RP2040 para este caso. El objetivo “pwm-servo-control” se implementa con MicroPython y PIO del RP2040, aprovechando la placa Pico‑ICE como hardware.
– Si más adelante deseas explorar el FPGA iCE40UP5K, la toolchain típica sería: Yosys (≥0.28), nextpnr-ice40 (≥0.4) e icestorm; no es requerida para este tutorial.
Habilitar interfaces y preparar el host
- Este proyecto no requiere activar SPI/I2C en la Raspberry Pi. Sin embargo:
- Asegúrate de que tu usuario pertenece al grupo dialout para acceder a /dev/ttyACM0 sin sudo.
- Opcionalmente, verifica configuración de consola serie (no usada aquí).
Comandos exactos (ejecutar en la Raspberry Pi):
# 1) Actualiza índices de paquetes
sudo apt update
# 2) Instala herramientas base
sudo apt install -y python3.11 python3-venv python3-pip python3-gpiozero
# 3) Añade tu usuario al grupo "dialout" para acceso a puertos serie
sudo usermod -aG dialout "$USER"
# 4) Cierra sesión y vuelve a entrar (o reinicia) para aplicar el grupo
# Alternativamente:
newgrp dialout
Crear un entorno virtual y paquetes Python requeridos
Recomendado para aislar dependencias:
# Crea y activa un entorno virtual
python3 -m venv ~/venvs/picoice
source ~/venvs/picoice/bin/activate
# Actualiza pip y instala paquetes con versiones exactas
pip install --upgrade pip==24.2
pip install mpremote==1.23.0 pyserial==3.5
# gpiozero viene por apt (1.6.2), no es necesario instalarla por pip
Materiales
- 1x Placa Pico‑ICE (RP2040 + iCE40UP5K). Modelo exacto: “Pico‑ICE (RP2040 + iCE40UP5K)”
- 1x Servomotor de radiocontrol (ejemplos compatibles):
- Micro‑servo SG90 (consumo bajo; puede funcionar con 5 V, señal a 3.3 V)
- O servo estándar de 5 V con señal compatible 3.3 V (recomendado revisar hoja de datos)
- 1x Fuente de alimentación 5 V externa para el servo (mínimo 1 A para servos pequeños; 2–3 A para servos más demandantes)
- 1x Cable USB (USB‑A a micro‑USB) para la Pico‑ICE
- Cables Dupont macho‑macho (x3 mínimo: señal, 5 V, GND)
- (Opcional) Protoboard
- (Opcional) Multímetro/analizador lógico/osciloscopio para validar el PWM
- (Opcional) Resistencia de 100 Ω en serie con la señal si el cable al servo es muy largo (reduce ringing)
Notas de seguridad:
– No alimentes el servo desde el 5 V del USB de la Pico‑ICE si no estás seguro de su corriente máxima; la fuente USB puede no soportarlo y provocar reinicios o daño.
– Conecta siempre masa común (GND) entre la Pico‑ICE y la fuente del servo.
Preparación y conexión
Mapa de conexiones (servomotor de 3 hilos)
Los servos típicamente usan:
– Marrón/Negro: GND
– Rojo: +5 V
– Naranja/Amarillo/Blanco: Señal PWM (3.3 V compatible en la mayoría de servos)
En la Pico‑ICE (que mantiene el pinout compatible con Raspberry Pi Pico), usaremos el pin GPIO15 como salida PWM de control.
Tabla de cableado:
| Elemento | Cable servo | Conectar a | Detalle |
|---|---|---|---|
| Masa servo | Negro/Marrón | GND de la Pico‑ICE y GND de la fuente 5 V | Masa común obligatoria |
| +5 V servo | Rojo | +5 V de la fuente externa | No alimentar desde USB de la Pico‑ICE si el servo consume >200–300 mA |
| Señal servo | Naranja/Amarillo/Blanco | GPIO15 de la Pico‑ICE | Señal 3.3 V del RP2040 |
Pasos detallados:
1) Desconecta la Pico‑ICE del USB (sin alimentación).
2) Conecta el cable de señal del servo al pin GPIO15 de la Pico‑ICE.
– La Pico‑ICE usa el mismo pinout que la Raspberry Pi Pico; el pin GPIO15 está rotulado en la serigrafía del módulo.
3) Conecta el GND del servo al GND de la Pico‑ICE.
4) Conecta el +5 V del servo a la salida de tu fuente 5 V externa.
5) Une GND de la fuente externa con GND de la Pico‑ICE (común).
6) Verifica polaridad y firmeza de las conexiones.
7) Conecta la Pico‑ICE al USB de la Raspberry Pi con el cable micro‑USB. Aún no enciendas la fuente 5 V del servo.
8) Una vez cargado el firmware, enciende la fuente 5 V del servo.
Recomendación: si es tu primera prueba y tu servo es pequeño (p. ej., SG90), usa una fuente 5 V externa de 1–2 A con protección. Evita alimentarlo desde el puerto USB del ordenador.
Código completo
Objetivo: generar una señal PWM específica para servos (50 Hz, periodo 20 ms) con ancho de pulso entre ~1.0 ms (≈0°) y ~2.0 ms (≈180°), usando PIO del RP2040 en MicroPython. Implementaremos:
– Un programa PIO que recibe por FIFO dos valores: tiempo alto (high_us) y tiempo bajo (low_us), en microsegundos.
– Un lazo en MicroPython que expone una pequeña interfaz por USB CDC (serial) para:
– Fijar ángulo en grados.
– Fijar ancho de pulso en microsegundos.
– Calibrar rangos min/max.
El archivo se grabará como main.py en la Pico‑ICE.
servo_pwm.py (para grabar como main.py en la Pico‑ICE)
# main.py — Control PWM de servo con PIO (RP2040) en la Pico-ICE
# Versión probada con MicroPython v1.22.2 (rp2)
#
# Protocolo simple por USB CDC (ttyACM):
# - "A <grados>" -> Establece ángulo (0-180)
# - "U <microsegundos>" -> Establece pulso en us (500-2500 típ.)
# - "C <min_us> <max_us>" -> Calibra rango en us (p. ej., 1000 2000)
# - "Q" -> Reporta estado actual
# Devuelve lineas "OK ..." o "ERR ..."
from rp2 import PIO, StateMachine, asm_pio
from machine import Pin
import sys
import select
# Config: pin de señal del servo
SERVO_PIN = 15 # GPIO15 en Pico-ICE
# Rango por defecto (ajustable por "C")
MIN_US = 1000
MAX_US = 2000
PERIOD_US = 20000 # 20 ms -> 50 Hz
# Estado actual
_state = {
"angle": 90,
"pulse_us": 1500,
"min_us": MIN_US,
"max_us": MAX_US,
}
# Programa PIO:
# - Pull #1: high_us -> X
# - Mantiene pin alto X ciclos (frecuencia de SM = 1 MHz => 1 ciclo = 1 us)
# - Pull #2: low_us -> Y
# - Mantiene pin bajo Y ciclos
# - Repite
@asm_pio(set_init=PIO.OUT_LOW)
def servo_prog():
pull(block) # high_us
mov(x, osr)
set(pins, 1)
label("high")
jmp(x_dec, "high")
pull(block) # low_us
mov(y, osr)
set(pins, 0)
label("low")
jmp(y_dec, "low")
jmp("servo_prog") # bucle
# Inicializa PIO a 1 MHz para que 1 instrucción = ~1 us
sm = StateMachine(0, servo_prog, freq=1_000_000, set_base=Pin(SERVO_PIN))
sm.active(1)
def _clamp(v, vmin, vmax):
return max(vmin, min(vmax, v))
def angle_to_us(angle, min_us=None, max_us=None):
if min_us is None:
min_us = _state["min_us"]
if max_us is None:
max_us = _state["max_us"]
angle = _clamp(angle, 0, 180)
us = int(min_us + (max_us - min_us) * (angle / 180.0))
return us
def set_pulse_us(pulse_us):
pulse_us = _clamp(int(pulse_us), 300, PERIOD_US - 300)
low_us = PERIOD_US - pulse_us
# Publica a la FIFO del PIO (dos palabras de 32 bits)
sm.put(pulse_us)
sm.put(low_us)
_state["pulse_us"] = pulse_us
# Actualiza "angle" si está dentro del rango calibrado
if _state["min_us"] < _state["max_us"]:
span = _state["max_us"] - _state["min_us"]
# Si span > 0, mapea su inversa dentro de 0-180 (aproximado)
if span > 0:
rel = _clamp((pulse_us - _state["min_us"]) / span, 0.0, 1.0)
_state["angle"] = int(round(180 * rel))
def set_angle(angle_deg):
pulse_us = angle_to_us(angle_deg)
set_pulse_us(pulse_us)
def calibrate(min_us, max_us):
min_us = int(min_us)
max_us = int(max_us)
if min_us < 300 or max_us > 2700 or (max_us - min_us) < 400:
return False
_state["min_us"] = min_us
_state["max_us"] = max_us
return True
def report():
return f'OK angle={_state["angle"]} pulse_us={_state["pulse_us"]} range=({ _state["min_us"] },{ _state["max_us"] }) pin={SERVO_PIN}'
# Inicialización: centra el servo a 90°
set_angle(90)
# Bucle de comandos por USB CDC
poller = select.poll()
poller.register(sys.stdin, select.POLLIN)
def _handle_line(line):
line = line.strip()
if not line:
return
parts = line.split()
cmd = parts[0].upper()
try:
if cmd == "A" and len(parts) == 2:
angle = int(parts[1])
set_angle(angle)
print(report())
elif cmd == "U" and len(parts) == 2:
us = int(parts[1])
set_pulse_us(us)
print(report())
elif cmd == "C" and len(parts) == 3:
min_us = int(parts[1])
max_us = int(parts[2])
if calibrate(min_us, max_us):
print(report())
else:
print("ERR calibrate_out_of_range")
elif cmd == "Q":
print(report())
else:
print("ERR unknown_command")
except Exception as e:
print("ERR", str(e))
# Lazo principal
print("OK Pico-ICE servo controller ready")
print(report())
while True:
# No bloqueante: comprueba si hay datos entrantes por USB CDC
ev = poller.poll(50) # 50 ms
if ev:
try:
line = sys.stdin.readline()
_handle_line(line)
except Exception as e:
print("ERR read:", e)
Puntos clave:
– El PIO corre a 1 MHz, por lo que cada iteración del bucle “high”/“low” equivale a 1 microsegundo.
– El RP2040 mantiene la estabilidad de 50 Hz dividiendo explícitamente el periodo: high_us + low_us = 20 000 us.
– Se expone una mínima interfaz de texto por USB CDC para poder conducir el servo desde un script host.
Script host en la Raspberry Pi (control por serie)
Este script envía comandos a la Pico‑ICE a través de /dev/ttyACM0, variando el ángulo y midiendo las respuestas.
Guárdalo como host_control.py en la Raspberry Pi (en tu home o en el proyecto):
#!/usr/bin/env python3
# host_control.py — Control del servo via USB CDC (ttyACM)
# Requiere: pyserial==3.5
import sys
import time
import serial
import argparse
def open_port(dev, baud=115200, timeout=1.0):
return serial.Serial(dev, baudrate=baud, timeout=timeout)
def send(ser, cmd):
ser.write((cmd.strip() + "\n").encode("ascii"))
ser.flush()
line = ser.readline().decode("utf-8", errors="replace").strip()
return line
def main():
ap = argparse.ArgumentParser(description="Control PWM servo (Pico-ICE RP2040)")
ap.add_argument("--dev", default="/dev/ttyACM0", help="Dispositivo serie (por defecto: /dev/ttyACM0)")
ap.add_argument("--angle", type=int, default=None, help="Fijar ángulo (0-180)")
ap.add_argument("--us", type=int, default=None, help="Fijar pulso en microsegundos (500-2500)")
ap.add_argument("--cal", nargs=2, type=int, metavar=("MIN_US","MAX_US"), help="Calibrar rango en us")
ap.add_argument("--sweep", action="store_true", help="Barrido 0-180-0 continuo")
args = ap.parse_args()
with open_port(args.dev) as ser:
# Lee líneas de bienvenida
for _ in range(2):
try:
print(ser.readline().decode().strip())
except:
pass
if args.cal:
resp = send(ser, f"C {args.cal[0]} {args.cal[1]}")
print(resp)
if args.angle is not None:
resp = send(ser, f"A {args.angle}")
print(resp)
if args.us is not None:
resp = send(ser, f"U {args.us}")
print(resp)
if args.sweep:
while True:
for ang in range(0, 181, 10):
print(send(ser, f"A {ang}"))
time.sleep(0.3)
for ang in range(180, -1, -10):
print(send(ser, f"A {ang}"))
time.sleep(0.3)
else:
# Consulta estado y termina
print(send(ser, "Q"))
if __name__ == "__main__":
sys.exit(main())
Puntos clave:
– No requiere permisos root si tu usuario pertenece a dialout.
– Por defecto busca /dev/ttyACM0, el cual aparece cuando la Pico‑ICE corre MicroPython y expone USB CDC.
Compilación/flash/ejecución
No hay compilación en este caso (microcontrolador con MicroPython). Pasos para cargar el firmware de MicroPython y tu script:
1) Descargar el firmware UF2 de MicroPython (RP2040)
Ejecuta en la Raspberry Pi:
# Ubicación de trabajo
mkdir -p ~/picoice-servo && cd ~/picoice-servo
# Descarga MicroPython v1.22.2 para RP2040 (RPi Pico compatible):
wget https://micropython.org/resources/firmware/rp2-pico-20240222-v1.22.2.uf2 -O micropython-rp2040-v1.22.2.uf2
2) Entrar en modo BOOTSEL y copiar el UF2
1) Desconecta la Pico‑ICE del USB.
2) Mantén pulsado el botón BOOTSEL de la Pico‑ICE.
3) Conecta el cable USB a la Raspberry Pi y suelta BOOTSEL.
4) Debería montarse una unidad USB de nombre RPI-RP2.
5) Copia el archivo UF2:
# Sustituye /media/$USER/RPI-RP2 por el punto de montaje real detectado en tu sistema
cp micropython-rp2040-v1.22.2.uf2 /media/$USER/RPI-RP2/
sync
6) La Pico‑ICE se reiniciará automáticamente en MicroPython.
Verifica el dispositivo serie:
ls -l /dev/ttyACM*
# Deberías ver algo como /dev/ttyACM0
3) Copiar el código main.py a la Pico‑ICE
Con el venv activado y mpremote instalado:
# Asegúrate de estar en el venv:
source ~/venvs/picoice/bin/activate
# Crea el archivo con el código de MicroPython (desde antes) en local
nano servo_pwm.py
# (pega el contenido de main.py mostrado en la sección de código completo y guarda)
# Copia a la Pico-ICE como main.py usando mpremote
mpremote connect /dev/ttyACM0 fs cp servo_pwm.py :main.py
# Reinicia suave para ejecutar main.py
mpremote connect /dev/ttyACM0 soft-reset
Si todo es correcto, al reconectar por serie verás:
– “OK Pico‑ICE servo controller ready”
– El estado inicial con ángulo 90°.
4) Ejecutar el script host y mover el servo
Conecta la fuente 5 V del servo, asegurando masa común con la Pico‑ICE. Luego:
# Crea el script host
nano host_control.py
# (pega el contenido mostrado en la sección de código completo y guarda)
# Asegura permisos de ejecución
chmod +x host_control.py
# Prueba: consulta estado
./host_control.py --dev /dev/ttyACM0 --angle 90
# Salida esperada: "OK angle=90 pulse_us=1500 range=(1000,2000) pin=15" y línea de bienvenida
# Mueve a 0°, 90°, 180° (tres comandos separados):
./host_control.py --dev /dev/ttyACM0 --angle 0
./host_control.py --dev /dev/ttyACM0 --angle 90
./host_control.py --dev /dev/ttyACM0 --angle 180
# Barrido continuo:
./host_control.py --dev /dev/ttyACM0 --sweep
# Interrumpe con Ctrl+C
Validación paso a paso
1) Verificación inicial (consola):
– Conecta por serie y verifica banners:
– “OK Pico‑ICE servo controller ready”
– Estado actual: p. ej., “OK angle=90 pulse_us=1500 range=(1000,2000) pin=15”.
– Ejecuta:
– ./host_control.py –dev /dev/ttyACM0 –angle 90
– Debes recibir “OK angle=90 …”.
2) Verificación del servo (física):
– Con el servo alimentado por 5 V externo y masa común con la Pico‑ICE:
– –angle 0: el servo se mueve hacia un extremo mecánico.
– –angle 90: el servo se centra.
– –angle 180: el servo se mueve hacia el otro extremo.
– Si notas zumbidos o vibraciones excesivas en los extremos, calibra el rango a 1000–2000 us (o ajusta según tu modelo):
– ./host_control.py –cal 1000 2000
– Luego repite 0–90–180.
3) Validación eléctrica (osciloscopio/analizador lógico):
– Mide en GPIO15 respecto a GND.
– Periodo constante 20 ms (50 Hz).
– Pulso alto:
– ≈1.00 ms a 0°
– ≈1.50 ms a 90°
– ≈2.00 ms a 180°
– El nivel alto debe ser ~3.3 V (tolerado por la mayoría de servos como señal).
4) Validación de estabilidad:
– Inicia un barrido:
– ./host_control.py –sweep
– Observa que el movimiento es fluido y sin reinicios de la Pico‑ICE (si hay reinicios, tu fuente 5 V del servo puede ser insuficiente).
5) Validación del protocolo:
– Envía:
– ./host_control.py –us 1200
– Espera: “OK pulse_us=1200 …”
– Envía consulta:
– ./host_control.py
– Espera: “OK angle=… pulse_us=… range=(min,max) pin=15”.
6) Validación de rango/calibración:
– Si tu servo satura antes de 0° o 180°, prueba valores p. ej., 900–2100 us.
– ./host_control.py –cal 900 2100
– ./host_control.py –angle 0
– ./host_control.py –angle 180
– Ajusta hasta lograr recorrido útil sin forzar el servo.
Troubleshooting (5–8 casos típicos)
1) No aparece /dev/ttyACM0:
– Causa: la Pico‑ICE no está en MicroPython o no enumeró USB CDC.
– Solución:
– Repite el proceso BOOTSEL y copia el UF2 de MicroPython.
– Prueba otro cable USB (de datos, no solo carga).
– Comprueba dmesg: dmesg | tail -n 50.
2) Permisos denegados al abrir el puerto:
– Causa: tu usuario no está en el grupo dialout.
– Solución:
– sudo usermod -aG dialout «$USER»
– Cierra sesión y vuelve a entrar (o newgrp dialout).
3) Servo vibra, se mueve a saltos o la Pico‑ICE se reinicia:
– Causa: fuente 5 V insuficiente para el servo o sin masa común.
– Solución:
– Usa una fuente dedicada 5 V de 1–3 A según el servo.
– Asegura GND común entre la fuente y la Pico‑ICE.
– Evita alimentar el servo desde el 5 V del USB de la placa.
4) El servo no se mueve o se mueve muy poco:
– Causa: pulso fuera del rango útil del servo o pin de señal incorrecto.
– Solución:
– Verifica que el cable de señal está en GPIO15.
– Calibra rango (p. ej., 1000–2000 us): ./host_control.py –cal 1000 2000
– Prueba con –angle 0 / 90 / 180 y observa.
5) Señal PWM no mide 50 Hz o el ancho no corresponde:
– Causa: frecuencia del PIO mal configurada.
– Solución:
– Verifica que en main.py el StateMachine está a freq=1_000_000.
– Regraba el script: mpremote fs cp servo_pwm.py :main.py; mpremote soft-reset.
6) El host_control.py no recibe “OK …”:
– Causa: el puerto no lee la línea de bienvenida o hay buffering.
– Solución:
– Añade una pausa breve tras abrir el puerto (sleep 1 s) o simplemente ignora las primeras líneas.
– Comprueba el dispositivo: ./host_control.py –dev /dev/ttyACM1 si hay múltiples dispositivos.
7) Ruidos eléctricos o movimientos erráticos con cables largos:
– Causa: integridad de señal degradada.
– Solución:
– Añade una resistencia de ~100 Ω en serie con la línea de señal cerca del servo.
– Mantén cortos los cables y usa trenzado o cables de mejor calidad.
8) El servo golpea contra topes mecánicos:
– Causa: rango de pulso demasiado amplio para ese servo.
– Solución:
– Reduce el rango (p. ej., 1100–1900 us): ./host_control.py –cal 1100 1900.
Mejoras/variantes
- Control de múltiples servos:
- Replicar el programa PIO en distintas StateMachines o usar una PIO con múltiples pines y turnos, cuidando el periodo de 20 ms por canal.
- Suavizado y rampas:
- Implementar rampas de aceleración/desaceleración en el lado MicroPython (p. ej., incrementos de 5° cada 20 ms) para movimientos más suaves.
- Lectura de un potenciómetro:
- Añadir un potenciómetro conectado a un ADC del RP2040 y mapear su lectura a un ángulo del servo, manteniendo el mismo motor PIO de PWM.
- Persistencia de calibración:
- Guardar en flash (p. ej., en un archivo JSON) los valores min_us y max_us establecidos con “C”.
- Interfaz de usuario en el host:
- Aplicación Tkinter simple que mueva una barra deslizante y envíe comandos “A
” por pyserial. - Uso del FPGA iCE40UP5K (variante avanzada):
- Implementar el generador de PWM en el FPGA (iCE40UP5K) y usar el RP2040 como maestro SPI para enviar nuevos anchos de pulso. Requiere toolchain Yosys/nextpnr/icestorm y mapeo de pines a los conectores disponibles en la Pico‑ICE.
- Telemetría:
- Enviar por USB CDC lecturas de tensión de la fuente (si añades un divisor y ADC) o registro de eventos para diagnóstico.
Checklist de verificación
- [ ] SO: Raspberry Pi OS Bookworm 64‑bit (2024‑10‑22 o similar) con Python 3.11 operativo.
- [ ] Entorno virtual creado y activo: ~/venvs/picoice.
- [ ] Paquetes instalados: mpremote==1.23.0, pyserial==3.5, gpiozero (1.6.2 por apt).
- [ ] Usuario en grupo dialout y acceso a /dev/ttyACM0 sin sudo.
- [ ] Firmware MicroPython v1.22.2 cargado en la Pico‑ICE (UF2 correcto).
- [ ] Conexiones:
- [ ] Señal del servo al GPIO15 de la Pico‑ICE.
- [ ] GND del servo unido a GND de la Pico‑ICE y a GND de la fuente 5 V.
- [ ] +5 V del servo desde la fuente externa (no desde el USB de la placa, salvo micro-servos muy ligeros bajo tu responsabilidad).
- [ ] Código main.py (servo_pwm.py) copiado a la Pico‑ICE y ejecutándose (mensajes “OK …” por USB).
- [ ] Script host_control.py ejecuta y reporta estado con “Q”.
- [ ] El servo responde a –angle 0/90/180 sin vibraciones excesivas.
- [ ] Validación de PWM (opcional con instrumento): 50 Hz y 1.0–2.0 ms según ángulo.
- [ ] Si fue necesario, calibración aplicada (p. ej., 1000–2000 us) y guardada en tu flujo de trabajo.
Apéndice: Tabla-resumen de herramientas y versiones (este proyecto)
| Componente | Versión exacta | Comentario |
|---|---|---|
| Raspberry Pi OS | Bookworm 64‑bit (2024‑10‑22) | Host de desarrollo |
| Python | 3.11.2 | Instalación por defecto de Bookworm |
| MicroPython (RP2040 UF2) | v1.22.2 (rp2-pico-20240222) | Firmware cargado en la Pico‑ICE |
| mpremote | 1.23.0 | Gestión de archivos y ejecución en MicroPython |
| pyserial | 3.5 | Comunicación serie desde el host |
| gpiozero | 1.6.2 | Opcional, instalada por apt |
| Hardware | Pico‑ICE (RP2040 + iCE40UP5K) | Modelo exacto utilizado |
| Servomotor | 5 V señal 3.3 V‑compatible | SG90 u otro similar |
Con este caso práctico has implementado un control de servomotor por PWM en la Pico‑ICE, apoyándote en PIO del RP2040 con MicroPython y manejándolo desde una Raspberry Pi con Bookworm 64‑bit y Python 3.11. La arquitectura resultante es robusta para ampliarla a múltiples canales, añadir rampas de movimiento e incluso llevar la generación a hardware en el FPGA iCE40UP5K en una variante más avanzada.
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.



