Raspberry Pi Pico – maszyna stanów (FSM) w MicroPython i PIO – przykłady, sygnalizacja, impulsy

Pytanie

raspberry pi pico, maszyna stanu przykład

Odpowiedź Sztucznej Inteligencji


Bezpośrednia odpowiedź na pytanie

  • Raspberry Pi Pico pozwala tworzyć maszyny stanów na dwa sposoby:
    1. czysto programowo (np. w MicroPythonie lub C) – klasyczna „Finite State Machine”,
    2. sprzętowo w blokach PIO – dedykowanych, programowalnych maszynach stanów wbudowanych w układ RP2040.
  • Poniżej znajdziesz dwa kompletne, skompilowane przykłady:
    • sygnalizacja świetlna w MicroPythonie (FSM w kodzie głównego rdzenia),
    • generator impulsów w assemblerze PIO (FSM sprzętowa).

Kluczowe punkty
• FSM upraszcza logikę sterowania i poprawia czytelność kodu.
• PIO daje deterministyczne timingi, niezależne od rdzeni CPU.
• Warto unikać funkcji blokujących i wykorzystywać przerwania lub flagi.

Szczegółowa analiza problemu

1. Teoretyczne podstawy

Finite State Machine (FSM) = {S, Σ, δ, s₀, F}

  • S – skończony zbiór stanów,
  • Σ – zbiór zdarzeń/wejść,
  • δ – funkcja przejść S × Σ → S,
  • s₀ – stan startowy,
  • F – zbiór stanów końcowych (opcjonalnie).

W RP2040 możemy:
• zaimplementować δ w kodzie wysokiego poziomu (MicroPython / C),
• opisać δ jako program PIO (do 32 instrukcji), który wykonuje się z precyzją pojedynczych cykli 133 MHz.

2. Praktyczna implementacja – MicroPython (sygnalizacja świetlna)

# RPi Pico – MicroPython 1.21 (2024-02)
from machine import Pin, Timer
import utime
from enum import IntEnum
class State(IntEnum):
    GREEN   = 0
    YELLOW  = 1
    RED     = 2
    RED_YEL = 3
LED = {                 # słownik pinów
    State.GREEN:  Pin(13, Pin.OUT, value=0),
    State.YELLOW: Pin(14, Pin.OUT, value=0),
    State.RED:    Pin(15, Pin.OUT, value=0)
}
BTN = Pin(16, Pin.IN, Pin.PULL_UP)
MIN_GREEN_MS = 5000
tim_ref   = utime.ticks_ms()
request   = False
state     = State.GREEN
def all_off():
    for led in LED.values(): led.value(0)
def btn_irq(pin):
    global request
    if not pin.value():      # zbocze opadające
        request = True
BTN.irq(trigger=Pin.IRQ_FALLING, handler=btn_irq)
def change(st):
    global state, tim_ref
    state, tim_ref = st, utime.ticks_ms()
    print("→", state.name)
def elapsed(ms):           # pomocniczy licznik bez blokowania
    return utime.ticks_diff(utime.ticks_ms(), tim_ref) >= ms
print("Start FSM")
while True:
    if state == State.GREEN:
        all_off(); LED[State.GREEN].value(1)
        if request and elapsed(MIN_GREEN_MS):
            request = False
            change(State.YELLOW)
    elif state == State.YELLOW:
        all_off(); LED[State.YELLOW].value(1)
        if elapsed(2000):
            change(State.RED)
    elif state == State.RED:
        all_off(); LED[State.RED].value(1)
        if elapsed(5000):
            change(State.RED_YEL)
    elif state == State.RED_YEL:
        all_off(); LED[State.RED].value(1); LED[State.YELLOW].value(1)
        if elapsed(1000):
            change(State.GREEN)
    utime.sleep_ms(10)

Cechy: brak sleep() w stanie, debounce w przerwaniu, łatwa rozbudowa o kolejne stany (np. tryb nocny).

3. Praktyczna implementacja – PIO (sprzętowa FSM)

Cel: wygenerować podany z CPU ciąg N impulsów 50 % duty na GP16.

import rp2, time
from machine import Pin
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)   # 1 instr = 1 cykl PIO
def pulse_n():
    pull()                 # pobierz N z FIFO
    mov(x, osr)            # x = N
    label("loop")
    set(pins, 1) [1]       # wysoki stan 2 cykle
    set(pins, 0) [1]       # niski  2 cykle
    jmp(x_dec, "loop")     # dekrementuj x
    irq(block, 0)          # zgłoś CPU, że skończono
sm = rp2.StateMachine(0, pulse_n, freq=1_000_000, set_base=Pin(16))
sm.active(1)
sm.put(10)          # 10 impulsów
sm.irq(lambda s: print("Impulsy zakończone"))
while sm.rx_fifo(): pass
time.sleep(0.1)

• Dokładny okres = 4 cykle / f_clk = 4 µs → 250 kHz.
• CPU zajmuje się tylko wpisaniem wartości i obsługą IRQ – czasu rzeczowego nie traci.

Aktualne informacje i trendy

• MicroPython 1.21 (luty 2024) dodał natywny moduł machine.Timer z callbackiem współdzielonym między rdzeniami.
• Coraz częściej łączy się FSM w kodzie wysokiego poziomu z modułem asyncio, uzyskując współbieżną obsługę wielu zadań.
• PIO wykorzystywane jest do dekodowania protokołów (DALI, DMX512, CAN PHY-less), co zmniejsza konieczność stosowania koprocesorów.
• Trend: gotowe biblioteki „statechart” (np. micropy-hsm) przenoszą wzorce UML StateChart na RP2040.

Wspierające wyjaśnienia i detale

• Debounce sprzętowy (RC, układ Schmitta) nadal zalecany w krytycznych aplikacjach.
utime.ticks_ms() w RP2040 przepełnia się co ~49 dni – w aplikacjach 24/7 trzeba stosować ticks_diff.
• W PIO każdy program zajmuje do 32 instrukcji; można współdzielić jeden program przez kilka maszyn poprzez różne offsety (wrap_target / wrap).

Aspekty etyczne i prawne

• Sygnalizacja świetlna używana w ruchu publicznym podlega normom PN-EN 12368 – kod hobbystyczny nie spełnia wymogów bezpieczeństwa; do celów edukacyjnych korzystamy wyłącznie w warunkach laboratoryjnych.
• Oprogramowanie wbudowane w urządzenia sterujące mechanizmami wykonawczymi powinno przechodzić analizę FMEA i testy zgodne z IEC 61508 (Safety Integrity Level).

Praktyczne wskazówki

• Projektując większy system, rozważ FSM hierarchiczną – główna maszyna „TRYB PRACY”, pod-FSM dla każdego trybu.
• Do testów integracyjnych użyj symulacji GPIO (np. pico-sim) lub rejestrowania stanów w RQ Spy.
• Na etapie PCB warto wyprowadzić piny PIO jako test-points – ułatwia logic-probing sygnałów generowanych sprzętowo.

Ewentualne zastrzeżenia lub uwagi dodatkowe

• MicroPython nie wspiera przerwań PIO; obsługę IRQ trzeba pisać w C lub korzystać z rp2.PIO.irq.
• W dwurdzeniowym RP2040 kod MicroPython działa tylko na rdzeniu 0; rdzeń 1 można wykorzystać w C (pico-sdk) – wymaga to własnego firmware (UF2).
• PIO nie ma dostępu do DMA w MicroPythonie – dla szybkich strumieni (SPI, I²S) lepiej użyć C/SDK.

Sugestie dalszych badań

• Zapoznaj się z projektem „pico-can” – sprzętowy kontroler CAN zbudowany wyłącznie w PIO.
• Przeanalizuj bibliotekę tinyusb_device_fsm – stanowa obsługa USB Device na RP2040.
• Zobacz publikację „Async State Machines on RP2040” (Raspberry Pi Forum, XI 2023) – integracja uasyncio + FSM.

Krótkie podsumowanie

Zaprezentowano dwa komplementarne podejścia do maszyn stanów na Raspberry Pi Pico:

  1. klasyczną FSM w MicroPythonie – czytelna, łatwa do edycji;
  2. deterministyczną FSM sprzętową w PIO – precyzyjny timing, małe obciążenie CPU.
    Wybór metody zależy od wymagań czasowych i złożoności projektu; w praktyce często łączy się obie techniki. Dzięki temu RP2040 pozostaje jedną z najbardziej elastycznych platform w segmencie mikrokontrolerów hobbystycznych i profesjonalnych.

Oceń odpowiedź:

Zadaj dodatkowe pytanie Sztucznej Inteligencji

Czekaj (1min)...
Zastrzeżenie: Odpowiedzi udzielone przez sztuczną inteligencję (model językowy) mogą być niedokładne i wprowadzające w błąd. Elektroda nie ponosi odpowiedzialności za dokładność, rzetelność ani kompletność prezentowanych informacji. Wszystkie odpowiedzi powinny być zweryfikowane przez użytkownika.