import machine
import time
from rp2 import PIO, StateMachine, asm_pio
# --- Configuration ---
ZERO_CROSS_PIN = 16 # Input from zero-crossing detector
TRIAC_PIN = 17 # Output to TRIAC gate driver
AC_FREQUENCY = 60 # 60Hz or 50Hz
# --- Calculate Timing ---
HALF_CYCLE_US = int(1_000_000 / (2 * AC_FREQUENCY)) # 8333us for 60Hz
# --- PIO Assembly Program ---
@asm_pio(
set_init=PIO.OUT_LOW,
in_shiftdir=PIO.SHIFT_LEFT,
autopull=True,
pull_thresh=32
)
def ac_phase_control():
# 1. Wait for the Zero-Crossing edge to go HIGH
wait(1, pin, 0)
# 2. Read the 32-bit delay count from the TX FIFO into the X register
pull(noblock) # Copies OSR to X if a new value is sent, else retains old value
mov(x, osr)
# 3. Microsecond Delay Loop (X register acts as down-counter)
label("delay_loop")
jmp(x_dec, "delay_loop") [1] # Each loop takes 2 clock cycles (jmp + 1 delay cycle)
# 4. Fire the TRIAC Gate Pulse
set(pins, 1) # Set TRIAC pin HIGH
# 5. Hold the pulse duration (approx 100 microseconds)
# At 1 MHz clock, 1 cycle = 1 us. We loop 50 times with a 1-cycle delay = 100 us.
set(x, 49)
label("pulse_loop")
jmp(x_dec, "pulse_loop") [1]
set(pins, 0) # Set TRIAC pin LOW (Pulse ends)
# 6. Wait for the Zero-Crossing edge to go LOW before restarting
wait(0, pin, 0)
# --- Initialize PIO State Machine ---
# We configure the PIO clock frequency to exactly 1,000,000 Hz (1 MHz).
# This makes 1 clock cycle = 1 microsecond.
sm = StateMachine(
0,
ac_phase_control,
freq=1_000_000,
set_base=machine.Pin(TRIAC_PIN),
in_base=machine.Pin(ZERO_CROSS_PIN)
)
# Active the state machine
sm.active(1)
# --- Helper Functions ---
def set_power_percentage(percent):
"""Sets power from 0% (off) to 100% (full on)."""
percent = max(0, min(100, percent)) # Clamp between 0 and 100
# Inverse relationship: 100% power = 0us delay, 0% power = Max delay
delay_us = int(HALF_CYCLE_US * (100 - percent) / 100)
# Prevent logic collisions at the very extremes
delay_us = max(50, min(HALF_CYCLE_US - 200, delay_us))
# The PIO loop takes 2 cycles per iteration.
# Therefore, the value sent must be divided by 2.
pio_value = delay_us // 2
# Push the countdown value to the PIO TX FIFO buffer
sm.put(pio_value)
print(f"Power: {percent}%, PIO Countdown: {pio_value}")
# --- Main Demo Loop ---
try:
while True:
# Fade up
for p in range(0, 101, 2):
set_power_percentage(p)
time.sleep(0.05)
# Fade down
for p in range(100, -1, -2):
set_power_percentage(p)
time.sleep(0.05)
except KeyboardInterrupt:
sm.active(0)
# Force safe shutdown of the TRIAC pin
machine.Pin(TRIAC_PIN, machine.Pin.OUT).low()
print("PIO Program stopped safely.")