import machine
import time
from rp2 import PIO, StateMachine, asm_pio
# --- Configuración de Pines ---
ZERO_CROSS_PIN = 16 # Entrada del detector de cruce por cero
TRIAC_PIN = 17 # Salida hacia el gatillo del TRIAC
POT_PIN = 26 # Pin ADC0 para el potenciómetro
# --- Configuración de Red Eléctrica ---
AC_FREQUENCY = 60 # Cambiar a 50 si tu red es de 50Hz
HALF_CYCLE_US = int(1_000_000 / (2 * AC_FREQUENCY)) # 8333us para 60Hz
# --- Configuración del Filtro ADC ---
FILTER_SIZE = 8 # Número de muestras para el promedio móvil
DEADBAND = 400 # Histéresis para evitar parpadeos por ruido (escala 0-65535)
# --- Programa Ensamblador PIO ---
@asm_pio(
set_init=PIO.OUT_LOW,
in_shiftdir=PIO.SHIFT_LEFT,
autopull=True,
pull_thresh=32
)
def ac_phase_control():
# 1. Esperar a que el pulso de Cruce por Cero suba (HIGH)
wait(1, pin, 0)
# 2. Leer el tiempo de retraso desde el búfer FIFO al registro X
pull(noblock)
mov(x, osr)
# 3. Bucle de retraso en microsegundos (X cuenta hacia abajo)
label("delay_loop")
jmp(x_dec, "delay_loop")
# 4. Encender el pulso del TRIAC
set(pins, 1)
# 5. Mantener el pulso encendido (~100 microsegundos)
set(x, 49)
label("pulse_loop")
jmp(x_dec, "pulse_loop")
# 6. Apagar el pulso del TRIAC (El TRIAC se queda enganchado solo)
set(pins, 0)
# 7. Esperar a que el pulso de Cruce por Cero baje (LOW) antes de reiniciar
wait(0, pin, 0)
# --- Inicialización de Hardware ---
# Inicializar Máquina de Estados PIO a 1 MHz (1 ciclo = 1 microsegundo)
sm = StateMachine(
0,
ac_phase_control,
freq=1_000_000,
set_base=machine.Pin(TRIAC_PIN),
in_base=machine.Pin(ZERO_CROSS_PIN)
)
sm.active(1)
# Inicializar ADC para el potenciómetro
pot = machine.ADC(machine.Pin(POT_PIN))
# --- Variables de Control ---
adc_history = [0] * FILTER_SIZE
history_index = 0
last_stable_adc = 0
# --- Funciones de Soporte ---
def read_filtered_pot():
"""Lee el ADC, aplica filtro de promedio móvil y devuelve el valor suavizado."""
global history_index
# Leer valor crudo (0 a 65535)
raw_val = pot.read_u16()
# Actualizar historial
adc_history[history_index] = raw_val
history_index = (history_index + 1) % FILTER_SIZE
# Calcular promedio
return sum(adc_history) // FILTER_SIZE
def set_power_from_adc(adc_value):
"""Convierte el valor del ADC en el retraso de microsegundos para el PIO."""
# Mapear escala de 0-65535 a porcentaje de potencia 0-100%
percent = (adc_value / 65535.0) * 100
# Relación inversa: 100% potencia = 0us retraso, 0% potencia = Max retraso
delay_us = int(HALF_CYCLE_US * (100 - percent) / 100)
# Márgenes de seguridad para no perder los extremos del cruce por cero
delay_us = max(50, min(HALF_CYCLE_US - 200, delay_us))
# El bucle PIO consume 2 ciclos por iteración; dividimos el retraso entre 2
pio_value = delay_us // 2
# Enviar el conteo directamente al FIFO del PIO
sm.put(pio_value)
# --- Bucle Principal ---
try:
# Llenar el filtro inicialmente
for _ in range(FILTER_SIZE):
read_filtered_pot()
last_stable_adc = read_filtered_pot()
set_power_from_adc(last_stable_adc)
while True:
current_adc = read_filtered_pot()
# Aplicar zona muerta (deadband) para ignorar fluctuaciones mínimas por ruido
if abs(current_adc - last_stable_adc) > DEADBAND:
last_stable_adc = current_adc
set_power_from_adc(last_stable_adc)
# Calcular porcentaje aproximado para depuración en consola
calculated_percent = int((last_stable_adc / 65535.0) * 100)
print(f"Potenciómetro: {last_stable_adc} | Potencia: {calculated_percent}%")
# Pequeña pausa para no saturar el CPU con lecturas analógicas
time.sleep(0.05)
except KeyboardInterrupt:
# Apagado seguro
sm.active(0)
machine.Pin(TRIAC_PIN, machine.Pin.OUT).low()
print("\nSistema detenido de forma segura.")