Guide · 11 min di lettura

Raspberry Pi Pico PIO e doppio core: _thread, setup1 e WS2812B

Guida a Raspberry Pi Pico PIO: macchine a stati, onda quadra ed esempio WS2812B, i due core con MicroPython _thread e Arduino setup1/loop1, novità del Pico 2.

Il blocco PIO del Raspberry Pi Pico e i suoi due core sono le due caratteristiche che distinguono questa piccola scheda dalla maggior parte dei microcontrollori della stessa fascia di prezzo. Il chip RP2040, il cuore del Pico, ha due core di processore separati e 8 macchine a stati PIO. Grazie ai core puoi leggere i sensori da una parte mentre un display o un'animazione di LED continua a girare senza scatti dall'altra. Le PIO invece sono minuscoli motori di temporizzazione che lavorano indipendentemente dal processore: generano il segnale preciso al nanosecondo richiesto dai LED WS2812B, una UART in più o un'onda quadra senza caricare la CPU.

In questa guida avvierai prima i due core con il modulo _thread di MicroPython e poi con setup1() e loop1() nell'Arduino IDE. Poi imparerai come funziona la PIO, quante macchine a stati ci sono e quali istruzioni capiscono, e scriverai due programmi reali: un generatore di onda quadra che suona note su un buzzer e un arcobaleno di WS2812B. Alla fine vedremo cosa cambia con il Pico 2 (RP2350) e quando servono davvero queste funzioni. Se il Pico è nuovo per te, ti consiglio di leggere prima Che cos'è il Raspberry Pi Pico? e Che cos'è MicroPython?.

Scheda di sviluppo Raspberry Pi Pico
Raspberry Pi Pico: il chip RP2040 al centro contiene due core Cortex-M0+ e due blocchi PIO. Tutti i 26 pin GPIO possono essere collegati alla PIO.

Cosa sono i due core e la PIO del Raspberry Pi Pico?

L'RP2040 ha due processori Cortex-M0+ identici chiamati core 0 e core 1. All'avvio del programma lavora solo il core 0; il core 1 dorme finché non lo avvii tu. I due core condividono gli stessi 264 KB di RAM e gli stessi pin, quindi scambiarsi dati è facile, ma non devono scrivere la stessa variabile nello stesso istante. Per questo il chip ha una piccola coda hardware bidirezionale (FIFO, 8 parole per direzione) e 32 lock hardware (spinlock). La PIO è un mondo completamente separato: ciascuno dei due blocchi PIO ha 4 macchine a stati, una memoria di programma condivisa da 32 istruzioni e accesso diretto ai pin. Una volta avviata, una macchina a stati esegue il proprio programma ciclo dopo ciclo anche mentre il processore è occupato con altro.

RisorsaRP2040 (Pico, Pico W)A cosa serve?
Core del processore2 x Cortex-M0+, 133 MHz (fino a 200 MHz nella documentazione attuale)Eseguire due programmi distinti davvero nello stesso momento
FIFO tra i core8 x 32 bit per direzioneInviare numeri e comandi tra i core
Lock hardware (spinlock)32Accedere a turno ai dati condivisi
Blocco PIO2 (PIO0, PIO1)Togliere al processore il lavoro sui pin con tempi critici
Macchina a stati4 per blocco, 8 in totaleOgnuna esegue il proprio programma PIO
Memoria di programma PIO32 istruzioni per bloccoCondivisa dalle 4 macchine del blocco
FIFO della PIOTX 4 + RX 4 parole per macchina (8 se unite)Dati tra il processore e la macchina a stati

Il secondo core in MicroPython: il modulo _thread

In MicroPython il core 1 si usa con la chiamata _thread.start_new_thread(funzione, argomenti). Sul Pico questa chiamata avvia la funzione sul secondo core, mentre il programma principale continua sul core 0. Può girare un solo thread aggiuntivo alla volta, perché non esiste un terzo core. Nell'esempio seguente il core 0 legge un potenziometro su GP26 e stampa il valore, mentre il core 1 fa lampeggiare il LED integrato alla velocità impostata dal potenziometro. L'attesa sleep_ms() del core 1 non influisce per niente sul core 0; con un solo core servirebbero un timer o asyncio.

MicroPython
1# Pico - iki çekirdek (MicroPython _thread) / two cores (MicroPython _thread)
2import _thread
3import time
4from machine import Pin, ADC
5
6led = Pin("LED", Pin.OUT)          # Pico ve Pico W dahili LED / onboard LED on Pico and Pico W
7pot = ADC(26)                      # GP26 = ADC0, potansiyometre / potentiometer
8
9lock = _thread.allocate_lock()     # ortak veri kilidi / shared data lock
10blink_ms = 500                     # çekirdek 0 yazar, çekirdek 1 okur / core 0 writes, core 1 reads
11running = True                     # durdurma bayrağı / stop flag
12
13
14def core1_task():
15    # Çekirdek 1: LED'i kendi hızında yakıp söndürür / Core 1: blinks the LED at its own pace
16    while running:
17        with lock:
18            period = blink_ms
19        led.toggle()
20        time.sleep_ms(period)
21    led.off()
22
23
24_thread.start_new_thread(core1_task, ())   # ikinci çekirdekte başlat / start on the second core
25
26try:
27    while True:
28        # Çekirdek 0: sensörü okur ve hızı ayarlar / Core 0: reads the sensor and sets the speed
29        raw = pot.read_u16()                     # 0-65535
30        with lock:
31            blink_ms = 50 + raw * 950 // 65535   # 50-1000 ms
32        print("ham / raw:", raw, "ms:", blink_ms)
33        time.sleep_ms(200)
34except KeyboardInterrupt:
35    running = False                              # çekirdek 1'e dur de / tell core 1 to stop
36    time.sleep_ms(1100)
  • Il lock creato con allocate_lock() garantisce che dentro un blocco with lock: un solo core tocchi la variabile condivisa.
  • Il flag running dice al core 1 di uscire dal ciclo quando fermi il programma con Ctrl+C; altrimenti il LED continua a lampeggiare.
  • Scrivere Pin("LED") trova il LED integrato sia sul Pico sia sul Pico W; sul Pico W il LED è collegato al chip wireless, quindi Pin(25) non funziona.
  • Una buona divisione è lasciare al core 1 i calcoli lunghi, il disegno del display o le temporizzazioni precise, e al core 0 il WiFi, i menu e l'interazione con l'utente.
Il pulsante Stop di Thonny interrompe solo il programma sul core 0. Se sul core 1 resta un ciclo infinito, la scheda sembra occupata e non puoi caricare nuovo codice. In quel caso scollega e ricollega il cavo USB o resetta la scheda toccando il pin RUN con GND. Usa sempre un flag di arresto come nell'esempio ed evita grandi allocazioni di memoria (liste enormi, lunghe concatenazioni di stringhe) nel thread aggiuntivo; il supporto multicore in MicroPython è ancora considerato sperimentale.

Due core nell'Arduino IDE: setup1() e loop1()

Il pacchetto per Pico più usato nell'Arduino IDE è il core Raspberry Pi Pico/RP2040/RP2350 di Earle Philhower (arduino-pico). Con questo, usare entrambi i core è sorprendentemente facile: le solite setup() e loop() girano sul core 0; se aggiungi setup1() e loop1() allo sketch, il core 1 parte da solo ed esegue queste due funzioni. I dati tra i core si inviano con l'oggetto rp2040.fifo. Il pacchetto ufficiale "Arduino Mbed OS RP2040" non supporta setup1(), quindi assicurati di scegliere il pacchetto giusto nel Gestore schede.

C++ (Arduino IDE)
1// Raspberry Pi Pico - iki çekirdek (Arduino-Pico çekirdeği) / two cores (Arduino-Pico core)
2const int POT_PIN = 26;        // GP26 = ADC0, potansiyometre / potentiometer
3
4// ---- Çekirdek 0 / Core 0 ----
5void setup() {
6  Serial.begin(115200);
7}
8
9void loop() {
10  int raw = analogRead(POT_PIN);   // 0-1023 (varsayılan 10 bit / default 10-bit)
11  rp2040.fifo.push_nb(raw);        // FIFO doluysa bekleme, değeri atla / skip the value if the FIFO is full
12  Serial.print("pot: ");
13  Serial.println(raw);
14  delay(100);
15}
16
17// ---- Çekirdek 1 / Core 1 ----
18void setup1() {
19  pinMode(LED_BUILTIN, OUTPUT);
20}
21
22void loop1() {
23  static uint32_t periodMs = 500;
24  uint32_t value;
25  while (rp2040.fifo.pop_nb(&value)) {           // FIFO'yu boşalt, sonuncuyu kullan / drain the FIFO, keep the last one
26    periodMs = map(value, 0, 1023, 50, 1000);
27  }
28  digitalWrite(LED_BUILTIN, HIGH);
29  delay(periodMs);                               // yalnız çekirdek 1'i bekletir / only blocks core 1
30  digitalWrite(LED_BUILTIN, LOW);
31  delay(periodMs);
32}
FunzioneCosa fa
rp2040.fifo.push(x)Invia un valore a 32 bit all'altro core; aspetta spazio se la coda è piena
rp2040.fifo.push_nb(x)Prova a inviare senza attendere; restituisce false se la coda è piena
rp2040.fifo.pop()Legge un valore dall'altro core; aspetta se la coda è vuota
rp2040.fifo.pop_nb(&x)Se c'è un valore lo scrive in x e restituisce true, altrimenti restituisce subito false
rp2040.fifo.available()Indica quanti valori aspettano di essere letti
rp2040.idleOtherCore() / resumeOtherCore()Mette in pausa per poco l'altro core e lo riavvia (per esempio durante la scrittura in flash)
Non usare la stessa periferica, come la porta seriale, un display o un bus SPI, da entrambi i core nello stesso momento. Come nell'esempio, assegna ogni periferica a un core e lascia che l'altro le invii numeri tramite la FIFO. Così eviti testi mescolati e schermate corrotte senza bisogno di lock.

Cos'è la PIO? Come funziona l'I/O programmabile

Nella maggior parte dei microcontrollori i blocchi di comunicazione e di segnale come UART, SPI, I2C e PWM sono fissi nel silicio; se ti serve un protocollo che non è nella lista, il processore lo gestisce accendendo e spegnendo i pin uno per uno (bit-banging). Questo tiene occupato il processore e la temporizzazione salta appena si mette in mezzo un interrupt. La PIO (Programmable Input/Output) risolve il problema alla radice: ogni macchina a stati è un minuscolo processore con nove istruzioni pensato solo per lavorare sui pin. Ogni istruzione dura esattamente un ciclo di clock e alla fine si può aggiungere un ritardo da 0 a 31 cicli. Così sai con precisione, contando i cicli, dopo quanti nanosecondi arriva ogni fronte del segnale. La velocità della macchina si imposta dividendo il clock di sistema con un divisore tra 1 e 65536.

IstruzioneFunzione
set(pins, v)Scrive una costante da 0 a 31 sui pin o nei registri X/Y
out(dest, n)Sposta n bit dal registro a scorrimento di uscita (OSR) ai pin o a X/Y
in_(src, n)Porta n bit dai pin o dai registri nel registro a scorrimento di ingresso (ISR)
pull() / push()Preleva una parola dalla FIFO TX nell'OSR, spinge la parola dell'ISR nella FIFO RX
jmp(cond, label)Salta in base a una condizione (X zero, pin alto, X-- ecc.)
wait(polarità, src, n)Aspetta che un pin o un flag IRQ raggiunga il livello voluto
mov(dest, src)Copia tra registri, invertendo i bit se richiesto
irq(n)Invia un flag di interrupt alla CPU o a un'altra macchina a stati
nop() e .side(v)Istruzione vuota; il side-set cambia un pin nello stesso ciclo di qualsiasi istruzione

In MicroPython i programmi PIO sono funzioni Python scritte con il decoratore @rp2.asm_pio; la funzione non viene chiamata, MicroPython la assembla in codice macchina. Poi la carichi in una macchina a stati con rp2.StateMachine(n, programma, freq=..., set_base=...). Sul Pico i numeri 0-3 appartengono a PIO0 e 4-7 a PIO1. Il valore freq è il numero di cicli di istruzione al secondo e non può scendere sotto 1/65536 del clock di sistema; a 125 MHz sono circa 1,9 kHz, a 150 MHz circa 2,3 kHz. Se ti serve un segnale più lento, usa ritardi o cicli con i contatori X/Y.

Onda quadra con la PIO: il tuo primo programma a macchina a stati

Nel primo esempio suoneremo una scala su un buzzer passivo collegato a GP15 e alla fine lasceremo attiva un'onda quadra continua a 1 kHz. Il programma ha solo due istruzioni: porta il pin a HIGH e aspetta 9 cicli, portalo a LOW e aspetta 9 cicli. Poiché ogni istruzione dura 1 ciclo, un periodo è di 20 cicli: basta dare alla macchina a stati 20 volte la frequenza del tono che vuoi. Collega il positivo del buzzer a GP15 e il negativo a GND. Al posto del buzzer puoi usare un oscilloscopio o un LED con una resistenza da 220 ohm; scegli la resistenza con il calcolatore di resistenza per LED.

MicroPython
1# PIO ile kare dalga ve buzzer notaları / square wave and buzzer notes with PIO
2import time
3import rp2
4from machine import Pin
5
6
7@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
8def square():
9    wrap_target()
10    set(pins, 1)   [9]     # 1 + 9 = 10 döngü HIGH / 10 cycles HIGH
11    set(pins, 0)   [9]     # 1 + 9 = 10 döngü LOW / 10 cycles LOW
12    wrap()                 # başa dön, işlemci karışmaz / loop back, no CPU involved
13
14
15def tone(freq_hz):
16    # Bir periyot 20 döngü: durum makinesi frekansı = 20 x ton
17    # One period is 20 cycles: state machine frequency = 20 x tone
18    sm = rp2.StateMachine(0, square, freq=freq_hz * 20, set_base=Pin(15))
19    sm.active(1)
20    return sm
21
22
23notes = [262, 294, 330, 349, 392, 440, 494, 523]   # Do-Re-Mi ... / C4 ... C5
24for n in notes:
25    sm = tone(n)
26    time.sleep_ms(300)        # işlemci boşta, ses PIO'dan / CPU is idle, PIO makes the sound
27    sm.active(0)
28
29sm = tone(1000)               # 1 kHz sürekli kare dalga / continuous 1 kHz square wave
30print("GP15: 1 kHz kare dalga / square wave")

La formula è semplice: frequenza del tono = frequenza della macchina a stati / numero di cicli. Per 1000 Hz la macchina lavora a 20.000 Hz, ben sopra il limite inferiore. Se togli i ritardi e lasci solo le due istruzioni set(), un periodo scende a 2 cicli; con la macchina a 125 MHz ottieni un'onda quadra di 62,5 MHz sul pin. Ricorda che il PWM hardware può fare lo stesso lavoro: per un tono semplice o la luminosità di un LED basta il PWM. Ciò che distingue la PIO è che decidi tu la forma esatta del segnale; con poche righe generi impulsi a larghezza variabile, un numero preciso di impulsi o uscite che reagiscono a un ingresso.

Pilotare LED WS2812B (NeoPixel) con la PIO

Anello RGB WS2812B da 24 LED
Anello WS2812B da 24 LED: ogni LED ha il proprio controller e riceve il colore come 24 bit su un'unica linea dati.

I LED WS2812B si aspettano un impulso di circa 1,25 microsecondi per bit; se il bit è 1 o 0 lo si capisce da quanto tempo l'impulso resta HIGH. È un caso da manuale per la PIO. Il programma seguente si basa sull'esempio ufficiale MicroPython di Raspberry Pi: la macchina a stati lavora a 8 MHz e ogni bit dura esattamente 10 cicli, quindi vengono inviati 800.000 bit al secondo. Grazie ad autopull la macchina preleva un nuovo colore dalla FIFO ogni 24 bit; il processore si limita a depositare l'array nella FIFO con sm.put(). Cambia il numero di LED in base alla tua striscia; per il funzionamento del WS2812B e l'ordine dei colori consulta la guida ai WS2812B NeoPixel.

Pin WS2812BPin del PicoNota
DIN (ingresso dati)GP16 (pin fisico 21)Una resistenza da 330 ohm in serie protegge il segnale
5V / VCCVBUS (pin 40)5 V dall'USB; con molti LED usa un alimentatore esterno da 5 V
GNDGND (es. pin 38)Anche il GND dell'alimentatore esterno va collegato qui
MicroPython
1# PIO ile WS2812B (NeoPixel) gökkuşağı / WS2812B (NeoPixel) rainbow with PIO
2import array
3import time
4import rp2
5from machine import Pin
6
7NUM_LEDS = 24          # halka ya da şeritteki LED sayısı / LEDs in the ring or strip
8LED_PIN = 16           # DIN bağlantısı / DIN connection
9BRIGHTNESS = 0.2       # 0.0-1.0, akımı düşük tutar / keeps current low
10
11
12@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT,
13             autopull=True, pull_thresh=24)
14def ws2812():
15    T1 = 2
16    T2 = 5
17    T3 = 3
18    wrap_target()
19    label("bitloop")
20    out(x, 1)               .side(0)    [T3 - 1]   # 1 biti al, hat LOW / take 1 bit, line LOW
21    jmp(not_x, "do_zero")   .side(1)    [T1 - 1]   # hat HIGH / line HIGH
22    jmp("bitloop")          .side(1)    [T2 - 1]   # bit 1: uzun HIGH / long HIGH
23    label("do_zero")
24    nop()                   .side(0)    [T2 - 1]   # bit 0: kısa HIGH / short HIGH
25    wrap()
26
27
28# Bit başına 10 döngü x 8 MHz = 800 kHz / 10 cycles per bit x 8 MHz = 800 kHz
29sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(LED_PIN))
30sm.active(1)
31
32pixels = array.array("I", [0] * NUM_LEDS)
33
34
35def set_pixel(i, r, g, b):
36    r = int(r * BRIGHTNESS)
37    g = int(g * BRIGHTNESS)
38    b = int(b * BRIGHTNESS)
39    pixels[i] = (g << 16) | (r << 8) | b    # WS2812B sırası GRB / byte order is GRB
40
41
42def show():
43    sm.put(pixels, 8)      # 24 biti sola hizala / left-align the 24 bits
44
45
46def wheel(pos):
47    # 0-255 arası değeri renge çevir / turn 0-255 into a colour
48    pos %= 255
49    if pos < 85:
50        return 255 - pos * 3, pos * 3, 0
51    if pos < 170:
52        pos -= 85
53        return 0, 255 - pos * 3, pos * 3
54    pos -= 170
55    return pos * 3, 0, 255 - pos * 3
56
57
58step = 0
59while True:
60    for i in range(NUM_LEDS):
61        set_pixel(i, *wheel(i * 255 // NUM_LEDS + step))
62    show()
63    step = (step + 3) % 255
64    time.sleep_ms(30)      # kare arası bekleme, aynı zamanda reset / frame gap, also the reset time

Seguiamo il programma riga per riga. out(x, 1) prende un bit dall'OSR e lo mette nel registro X mentre il side-set tiene la linea LOW (3 cicli). Poi la linea va HIGH e si controlla X (2 cicli). Se il bit è 1, la linea resta HIGH per altri 5 cicli; se è 0, la linea torna LOW e la macchina aspetta 5 cicli. Entrambi i percorsi fanno 10 cicli, cioè 1,25 µs. pull_thresh=24 dice alla macchina di usare solo 24 bit di ogni parola; per questo in Python il colore viene spostato a sinistra (sm.put(pixels, 8)). L'ordine dei colori è GRB, non RGB; se rosso e verde sono scambiati, il motivo è questo. Poiché 24 LED in bianco pieno possono assorbire fino a 1,4 A, nell'esempio la luminosità è limitata al 20%.

Se vuoi solo accendere dei LED, va bene anche il modulo neopixel integrato in MicroPython: np = neopixel.NeoPixel(Pin(16), 24), np[0] = (255, 0, 0), np.write(). Il vantaggio della versione PIO è che il processore resta libero durante l'invio e che puoi adattare la stessa tecnica ad altri protocolli.

Cosa cambia con il Pico 2 (RP2350)?

Il Raspberry Pi Pico 2 è la nuova generazione, con le stesse dimensioni e la stessa piedinatura ma con un chip RP2350. Ha sempre due core, ma questa volta puoi scegliere all'avvio tra due core Arm Cortex-M33 o due core RISC-V Hazard3. Tutto il codice di questo articolo funziona senza modifiche sul Pico 2: _thread è identico in MicroPython e nel core Arduino-Pico setup1()/loop1() e rp2040.fifo mantengono gli stessi nomi. Sul lato PIO si aggiunge un terzo blocco, quindi le macchine a stati diventano 12 (0-11 in MicroPython), più alcune nuove opzioni di istruzione.

CaratteristicaPico (RP2040)Pico 2 (RP2350)
Core2 x Cortex-M0+2 x Cortex-M33 o 2 x Hazard3 RISC-V
Frequenza di clock133 MHz150 MHz
RAM264 KB520 KB
Flash sulla scheda2 MB4 MB
PIO2 blocchi, 8 macchine a stati3 blocchi, 12 macchine a stati
Virgola mobileSoftware (routine veloci in ROM)FPU hardware (singola precisione) + coprocessore a doppia precisione
SicurezzaNessunaTrustZone, avvio sicuro, 8 KB di OTP
File MicroPythonRPI_PICO (.uf2)RPI_PICO2 (.uf2)

In pratica la differenza più evidente del Pico 2 è la velocità: grazie alla FPU hardware i calcoli con molti numeri in virgola mobile (filtri, fusione di sensori, elaborazione audio) diventano molto più rapidi, e il doppio della RAM lascia spazio a grandi buffer per il display. L'unica attenzione è caricare il file UF2 giusto per la scheda; quello del Pico non funziona sul Pico 2. Le prime versioni dell'RP2350 avevano un difetto hardware noto (E9) con gli ingressi che usano il pull-down interno, corretto nella revisione A4; usare una resistenza esterna per i pulsanti resta comunque la scelta sicura.

Quando servono due core e quando la PIO?

Cosa ti serveSoluzione migliorePerché?
Luminosità LED, servo, tono semplicePWM hardwareBlocco pronto, una riga di codice
Qualche sensore + display + pulsanteUn core, millis() o asyncioNon serve altra complessità
Animazione fluida durante calcoli lunghiDue coreIl calcolo sul core 1 non blocca il display
Misura continua insieme al WiFiDue coreI ritardi di rete non disturbano l'intervallo di misura
WS2812B, UART in più, trasmettitore IR, encoderPIOPrecisione al nanosecondo, CPU libera
Video VGA/DVI, audio I2S, segnali molto velociPIO + DMAVelocità di dati che la CPU non reggerebbe

In breve: pensa al secondo core quando i compiti si ostacolano nel software, e alla PIO quando i pin richiedono una precisione sotto il microsecondo. La maggior parte dei progetti hobbistici non ha bisogno di nessuno dei due; ma se un giorno la tua animazione WS2812B inizia a scattare durante una richiesta WiFi o la scheda resta senza UART, la soluzione ti aspetta già dentro il Pico.

Idee di progetti con PIO e doppio core del Pico

  • Anello LED che reagisce alla musica: il core 1 elabora i campioni del microfono, la PIO pilota l'anello WS2812B e il core 0 ascolta i pulsanti.
  • Generatore di impulsi a otto canali: ogni macchina a stati produce un'onda quadra a una frequenza diversa per driver di motori passo-passo o banchi di prova.
  • Data logger senza buchi: il core 1 legge l'ADC ogni 10 ms mentre il core 0 invia i dati via WiFi; anche se la rete rallenta non si perde nessun campione.
  • Porte seriali extra: apri una terza e una quarta UART con la PIO e collega insieme un GPS e un modulo Bluetooth.
  • Contatore per encoder rotativo: la PIO conta gli impulsi dell'encoder senza perderne nemmeno uno; il processore legge solo il risultato.
  • Luce notturna che respira: affida le dissolvenze morbide in PWM al core 1 e il sensore di luce al core 0.

Errori comuni e soluzioni

SintomoCausaSoluzione
La scheda non risponde dopo lo stopIl ciclo sul core 1 è ancora attivoUsa un flag di arresto; se serve scollega e ricollega l'USB
start_new_thread fallisce la seconda voltaIl Pico consente un solo thread aggiuntivoNon avviarne un altro prima che finisca il precedente
Le variabili a volte hanno valori assurdiI due core scrivono dati condivisi senza lockUsa allocate_lock() o la FIFO
StateMachine genera ValueErrorfreq è sotto clock di sistema / 65536Aumenta la frequenza e rallenta con i ritardi
PIOASMError: delay too largeI bit di side-set riducono il campo del ritardoDividi il ritardo o aggiungi un nop()
Rosso e verde invertiti sui WS2812BL'ordine dei colori è GRBUsa il formato (g << 16) | (r << 8) | b
setup1() non parte maiÈ selezionato il pacchetto Arduino Mbed RP2040Installa il pacchetto Raspberry Pi Pico/RP2040 di Earle Philhower
Quando scrivi un programma PIO, annota i cicli di ogni istruzione e controlla che il totale corrisponda al tempo voluto. Un oscilloscopio o un analizzatore logico economico aiutano molto; altrimenti abbassa temporaneamente la frequenza della macchina a 2-3 kHz e osserva il pin con un LED per vedere gli errori a occhio nudo.

I due core e la PIO trasformano il Pico da semplice lampeggiatore di LED in un vero sistema embedded, ma tutto poggia sulla stessa base: conoscere bene pin, temporizzazioni e LED. Nell'app BlueGrays trovi progetti con il Raspberry Pi Pico come accendere un LED, le luci in sequenza, l'effetto Supercar, una macchina a stati con pulsante e l'effetto respiro in PWM, ognuno con schema e codice MicroPython passo passo, pronti per aggiungere le tecniche di questo articolo.

Domande frequenti

Cos'è la PIO del Raspberry Pi Pico?

La PIO (I/O programmabile) è un insieme di piccole macchine a stati dei chip RP2040 e RP2350 che lavorano indipendentemente dal processore. Ognuna esegue con precisione al ciclo un programma scritto in un linguaggio semplice di nove istruzioni. Si occupa di compiti con tempi critici come LED WS2812B, UART aggiuntive, trasmissione IR o lettura di encoder senza tenere occupata la CPU.

Quanti core ha il Raspberry Pi Pico?

Il chip RP2040 del Pico e del Pico W ha due core Cortex-M0+. Anche l'RP2350 del Pico 2 ha due core e all'avvio si può scegliere tra core Cortex-M33 o Hazard3 RISC-V. Quando il programma parte lavora solo il core 0; il core 1 lo avvii tu.

Quante macchine a stati PIO ha il Pico?

L'RP2040 ha due blocchi PIO con 4 macchine a stati ciascuno, quindi 8 in totale. La memoria di programma da 32 istruzioni di ogni blocco è condivisa tra le sue macchine. L'RP2350 del Pico 2 ha tre blocchi PIO, cioè 12 macchine a stati.

MicroPython può usare entrambi i core del Pico?

Sì. Sul Pico una funzione avviata con _thread.start_new_thread() gira sul secondo core. Può esistere un solo thread aggiuntivo alla volta e i dati condivisi vanno protetti con un lock creato da _thread.allocate_lock(). Poiché il supporto multicore in MicroPython è ancora sperimentale, conviene evitare operazioni pesanti sulla memoria nel thread aggiuntivo.

Come si usa il secondo core del Pico nell'Arduino IDE?

Dopo aver installato il pacchetto Raspberry Pi Pico/RP2040/RP2350 di Earle Philhower basta aggiungere allo sketch le funzioni setup1() e loop1(); il core 1 le esegue automaticamente. I valori a 32 bit passano tra i core con rp2040.fifo.push() e rp2040.fifo.pop(). Il pacchetto RP2040 di Arduino basato su Mbed non offre questa funzione.

Per i LED WS2812B è obbligatorio usare la PIO?

No. Il modulo neopixel integrato in MicroPython e la libreria Adafruit NeoPixel in Arduino funzionano senza problemi sul Pico. Il vantaggio di scriverla da te con la PIO è che il processore resta libero durante l'invio e puoi applicare la stessa tecnica ad altri protocolli con tempi critici.

Vedi tutti i progetti
Lampeggio LED Integrato Raspberry Pi Pico (Pico e Pico W)
#147Principiante

Lampeggio LED Integrato Raspberry Pi Pico (Pico e Pico W)

Il progetto di apertura della serie Raspberry Pi Pico! Facciamo lampeggiare il LED verde integrato per verificare che la scheda funzioni — un classico primo esperimento "Hello World". NESSUN componente esterno, solo Pico e cavo USB. Il codice è scritto in MicroPython (il linguaggio ufficialmente supportato dalla Raspberry Pi Foundation). Impara la classe machine.Pin, la modalità Pin.OUT, i metodi led.on()/off() e time.sleep() in Thonny IDE. Segui ogni commutazi

5.0(1)
10
Accensione di un LED esterno con Raspberry Pi Pico (breadboard)
#148Principiante

Accensione di un LED esterno con Raspberry Pi Pico (breadboard)

Il passo successivo dopo il LED integrato di Pico: collegare un LED esterno alla breadboard e controllarlo tramite il pin GP14. Con questo progetto imparerai come utilizzare qualsiasi pin GPIO generico del Pico come uscita, la polarità del LED (stelo lungo = anodo/+, stelo corto = catodo/–) e perché è indispensabile un resistore di limitazione della corrente (220 Ω). Se non inserisci il resistore, l’LED riceverà una corrente eccessiva e potrebbe bruciarsi. Il circuito è molto semplice: Pico GP14

4.5(2)
7
Simulatore Semaforo Raspberry Pi Pico (3 LED Sequenziali)
#150Principiante

Simulatore Semaforo Raspberry Pi Pico (3 LED Sequenziali)

Simuliamo un vero semaforo con LED rosso, giallo e verde. Verde 4 sec "vai", giallo 1,5 sec "preparati", rosso 3 sec "fermati" e rosso+giallo 1 sec "partenza" (pattern europeo). Impara la definizione di funzione, la logica della macchina a stati e la sincronizzazione multi-GPIO. Circuito semplice: 3 LED su GP11/GP12/GP13.

4.0(1)
12
Raspberry Pi Pico LED Sequenziale (6 LED)
#153Principiante

Raspberry Pi Pico LED Sequenziale (6 LED)

Animazione LED sequenziale classica con Pico — versione 6 LED! 6 LED si accendono da sinistra a destra sequenzialmente. Impara la lista Python, la comprensione di liste ([Pin(p, Pin.OUT) for p in LED_PINS]), il ciclo for con controllo pin e enumerate(). 6 LED su GP9-GP14, ciascuno con resistenza 220Ω. Codice molto pulito: invece di definire 6 LED uno per uno, usiamo una lista — puoi aggiungere LED fino al numero di GPIO liberi e utilizzabili, ciascuno con la propria resistenza. Per controllare 1

0.0(0)
8
Raspberry Pi Pico Knight Rider LED Scanner (6 LED Avanti-Indietro)
#154Principiante

Raspberry Pi Pico Knight Rider LED Scanner (6 LED Avanti-Indietro)

Effetto scanner LED "Knight Rider" classico con Pico! 6 LED vanno da sinistra a destra, poi da destra a sinistra, all'infinito. Impara reversed() e due cicli for separati per pattern avanti-indietro.

0.0(0)
13
Semaforo Raspberry Pi Pico con selettore a 4 posizioni (macchina a stati)
#158Intermedio

Semaforo Raspberry Pi Pico con selettore a 4 posizioni (macchina a stati)

4 diverse modalità del semaforo con un solo pulsante! Ad ogni pressione del pulsante, la modalità cambia: Ciclo normale (rosso → giallo → verde in sequenza), Solo rosso (segnale di stop), Giallo lampeggiante (attenzione/modalità notturna), Spento (manutenzione). Concetti: state machine (macchina a stati — la variabile mode determina quale comportamento verrà eseguito), modulo mod cycling ((mode + 1) % 4 → 0→1→2→3→0 ciclo infinito), temporizzazione non bloccante (l

5.0(1)
15
Raspberry Pi Pico Pulsante Debounce: IRQ vs Software Polling
#159Intermedio
Premium

Raspberry Pi Pico Pulsante Debounce: IRQ vs Software Polling

Una pressione e il codice conta 3-4? È contact bounce! Confronta 2 metodi di conteggio in Serial: polling RAW (cattura bounce, conta male) vs IRQ + software debounce (preciso). Impara fisica del bounce, software debounce, Pin.irq() interrupt, IRQ_FALLING trigger, ISR handler, sicurezza delle variabili globali ISR.

0.0(0)
12
Raspberry Pi Pico PWM LED Fade (Effetto Respiro)
#160Principiante

Raspberry Pi Pico PWM LED Fade (Effetto Respiro)

Il LED non lampeggia più — respira! Con il PWM (modulazione di larghezza d'impulso) del Raspberry Pi Pico, la luminosità sale e scende lentamente. Impara la classe machine.PWM, duty_u16() (luminosità a 16 bit 0–65535), la frequenza PWM, i cicli range() e l'uscita quasi analogica. Base per miscelazione colori RGB, controllo motori, servo e buzzer.

0.0(0)
4