raspberry pi pico, maszyna stanów przykład w C++ arduino
delay()
. Skończona maszyna stanów opisuje układ poprzez:
if-else
,LED → GP15 przez 220 Ω → GND
SW → GP14 → GND (INPUT_PULLUP)
Płytki Pico W mają na GP25 wbudowaną diodę – można ją wykorzystać zamiast zewnętrznej (zmodyfikuj LED_PIN
).
/**********************************************************************
* Raspberry Pi Pico – prosta FSM 3-stanowa (OFF / SOLID / BLINK) *
*********************************************************************/
const uint8_t LED_PIN = 15; // GP15 lub 25 dla Pico W
const uint8_t BUTTON_PIN = 14;
enum class State : uint8_t { LED_OFF, LED_SOLID, LED_BLINK };
State currentState = State::LED_OFF;
constexpr uint32_t debounceMs = 40;
constexpr uint32_t blinkPeriod = 500;
bool buttonStable = HIGH, lastButtonHw = HIGH;
uint32_t lastChangeMs = 0;
uint32_t lastBlinkMs = 0;
bool ledBlinkLvl = LOW;
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
bool readButtonPressed() { // zwraca TRUE tylko raz na wciśnięcie
bool hw = digitalRead(BUTTON_PIN);
if (hw != lastButtonHw) { // drganie – restart timera
lastChangeMs = millis();
lastButtonHw = hw;
}
if (millis() - lastChangeMs > debounceMs) buttonStable = hw;
static bool lastStable = HIGH;
bool pressed = (lastStable == HIGH && buttonStable == LOW);
lastStable = buttonStable;
return pressed;
}
void loop() {
if (readButtonPressed()) { // warunek przejścia
switch (currentState) {
case State::LED_OFF: currentState = State::LED_SOLID; break;
case State::LED_SOLID: currentState = State::LED_BLINK; break;
case State::LED_BLINK: currentState = State::LED_OFF; break;
}
Serial.printf("⇒ nowy stan: %d\n", static_cast<int>(currentState));
}
switch (currentState) { // akcje w stanach
case State::LED_OFF:
digitalWrite(LED_PIN, LOW);
break;
case State::LED_SOLID:
digitalWrite(LED_PIN, HIGH);
break;
case State::LED_BLINK:
if (millis() - lastBlinkMs >= blinkPeriod) {
lastBlinkMs = millis();
ledBlinkLvl = !ledBlinkLvl;
digitalWrite(LED_PIN, ledBlinkLvl);
}
break;
}
}
Przy większej liczbie stanów wygodniej użyć tablicy struktur z wskazówkami do funkcji (pattern „table-driven FSM”):
struct FsmState {
void (*onEnter)(); // akcja jednorazowa po wejściu
void (*onLoop)(); // akcja cykliczna
uint8_t (*condition)(); // zwraca ID stanu docelowego lub własny
};
/* …definicje funkcji onEnter/onLoop/condition dla każdego stanu… */
FsmState fsm[] = {
{ ledOffEnter, ledOffLoop, ledOffCond }, // 0
{ ledSolidEnter, ledSolidLoop, ledSolidCond }, // 1
{ ledBlinkEnter, ledBlinkLoop, ledBlinkCond } // 2
};
uint8_t state = 0;
void loop() {
uint8_t next = fsm[state].condition();
if (next != state) { fsm[next].onEnter(); state = next; }
fsm[state].onLoop();
}
Taki układ ułatwia testowanie jednostkowe i dodawanie kolejnych trybów bez modyfikacji rdzenia.
RP2040 posiada 2 × (4 SM) = 8 programowalnych maszyn stanów PIO; każda wykonuje instrukcje 1-cyklowe (8 ns przy 125 MHz). Przykład – wygeneruj 1 kHz Square:
led_square.pio
.program led_square
.wrap_target
set pins, 1 [7] ; ustaw 1, czekaj 8*8 ns
set pins, 0 [7] ; ustaw 0
.wrap
main.ino
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "led_square.pio.h"
const uint LED_PIN = 15;
PIO pio = pio0; // można też pio1
uint sm = 0;
void setup() {
pinMode(LED_PIN, OUTPUT);
uint offset = pio_add_program(pio, &led_square_program);
led_square_program_init(pio, sm, offset, LED_PIN);
}
void loop() { /* CPU nic nie robi, PIO generuje sygnał */ }
• W Arduino-Pico wbudowany jest komplet PICO-SDK v1.5.x; można więc korzystać z nagłówków hardware/*
dokładnie tak jak w CMake-projects.
• Część logiki (np. precyzyjne PWM, enkoder, protokół I²S) można off-loadować do PIO, a warstwę sterującą realizować jako klasyczną FSM na CPU.
asm
bez oddzielnych plików. Automaton
, FSM
(renato-maciel/Arduino-FSM) już portowane na RP2040. millis()
) jest wystarczający do kilku kHz; przy szybszych wejściach warto użyć przerwań i filtra RC. LED_OFF
warto usypiać rdzeń (sleep_ms()
), lecz pamiętaj o wyjściu z uśpienia przy przerwaniu GPIO.Serial.printf()
lub pico-probe
(SWD) do śledzenia stanu. onEntry
/onExit
– prosto poprzez flagę stateChanged
. switch-case
przy > 15 stanach staje się nieczytelny; rozważ wzorzec „state pattern” (klasy dziedziczące po IState
). Zaprezentowano: