Guides · 12 min de lecture

Raspberry Pi Pico PIO et double cœur : _thread, setup1, WS2812B

Guide Raspberry Pi Pico PIO : machines à états, signal carré et WS2812B, les deux cœurs avec MicroPython _thread et Arduino setup1/loop1, différences du Pico 2.

Le bloc PIO du Raspberry Pi Pico et ses deux cœurs sont les deux atouts qui distinguent cette petite carte de la plupart des microcontrôleurs du même prix. La puce RP2040, au cœur du Pico, possède deux cœurs de processeur distincts et 8 machines à états PIO. Grâce aux deux cœurs, tu peux lire des capteurs d'un côté pendant qu'un écran ou une animation de LED tourne sans à-coups de l'autre. Les PIO, eux, sont de minuscules moteurs de synchronisation qui fonctionnent indépendamment du processeur : ils produisent le signal précis à la nanoseconde qu'exigent les LED WS2812B, un UART supplémentaire ou un signal carré sans charger le CPU.

Dans ce guide, tu vas d'abord faire tourner les deux cœurs avec le module _thread de MicroPython, puis avec setup1() et loop1() dans l'Arduino IDE. Ensuite tu verras comment fonctionne le PIO, combien il y a de machines à états et quelles instructions elles comprennent, et tu écriras deux vrais programmes : un générateur de signal carré qui joue des notes sur un buzzer et un arc-en-ciel de WS2812B. Les dernières parties montrent ce que change le Pico 2 (RP2350) et quand tu as vraiment besoin de ces fonctions. Si tu débutes avec le Pico, lis d'abord Qu'est-ce que le Raspberry Pi Pico ? et Qu'est-ce que MicroPython ?.

Carte de développement Raspberry Pi Pico
Raspberry Pi Pico : la puce RP2040 au centre contient deux cœurs Cortex-M0+ et deux blocs PIO. Les 26 broches GPIO peuvent toutes être reliées au PIO.

Que sont les deux cœurs et le PIO du Raspberry Pi Pico ?

Le RP2040 contient deux processeurs Cortex-M0+ identiques appelés cœur 0 et cœur 1. Au démarrage du programme, seul le cœur 0 travaille ; le cœur 1 dort jusqu'à ce que tu le lances. Les deux cœurs partagent les mêmes 264 Ko de RAM et les mêmes broches : échanger des données est donc facile, mais ils ne doivent pas écrire la même variable au même instant. Pour cela, la puce dispose d'une petite file matérielle bidirectionnelle (FIFO, 8 mots dans chaque sens) et de 32 verrous matériels (spinlocks). Le PIO est un monde totalement à part : chacun des deux blocs PIO possède 4 machines à états, une mémoire de programme commune de 32 instructions et un accès direct aux broches. Une fois lancée, une machine à états exécute son programme cycle après cycle, même quand le processeur est occupé ailleurs.

RessourceRP2040 (Pico, Pico W)À quoi ça sert ?
Cœur de processeur2 x Cortex-M0+, 133 MHz (jusqu'à 200 MHz dans la documentation actuelle)Exécuter deux programmes distincts vraiment en même temps
FIFO entre les cœurs8 x 32 bits dans chaque sensEnvoyer des nombres et des ordres entre les cœurs
Verrou matériel (spinlock)32Accéder à tour de rôle aux données partagées
Bloc PIO2 (PIO0, PIO1)Décharger le processeur du travail sur broches à timing critique
Machine à états4 par bloc, 8 au totalChacune exécute son propre programme PIO
Mémoire de programme PIO32 instructions par blocPartagée par les 4 machines du bloc
FIFO PIOTX 4 + RX 4 mots par machine (8 si fusionnées)Données entre le processeur et la machine à états

Le second cœur en MicroPython : le module _thread

En MicroPython, on utilise le cœur 1 avec l'appel _thread.start_new_thread(fonction, arguments). Sur le Pico, cet appel lance la fonction sur le second cœur, tandis que ton programme principal continue sur le cœur 0. Un seul thread supplémentaire peut tourner à la fois, puisqu'il n'y a pas de troisième cœur. Dans l'exemple ci-dessous, le cœur 0 lit un potentiomètre sur GP26 et affiche la valeur, pendant que le cœur 1 fait clignoter la LED intégrée à la vitesse fixée par le potentiomètre. L'attente sleep_ms() du cœur 1 n'affecte pas du tout le cœur 0 ; avec un seul cœur, il te faudrait un timer ou 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)
  • Le verrou créé avec allocate_lock() garantit qu'à l'intérieur d'un bloc with lock: un seul cœur touche la variable partagée.
  • Le drapeau running indique au cœur 1 de quitter sa boucle quand tu arrêtes le programme avec Ctrl+C ; sinon la LED continue de clignoter.
  • L'écriture Pin("LED") trouve la LED intégrée sur le Pico comme sur le Pico W ; sur le Pico W la LED est reliée à la puce sans fil, donc Pin(25) ne marche pas.
  • Un bon partage : les calculs longs, le dessin de l'écran ou les timings précis sur le cœur 1, le Wi-Fi, les menus et l'interaction avec l'utilisateur sur le cœur 0.
Le bouton Arrêter de Thonny n'interrompt que le programme du cœur 0. Si une boucle infinie reste active sur le cœur 1, la carte paraît occupée et tu ne peux plus téléverser de code. Dans ce cas, débranche puis rebranche le câble USB ou réinitialise la carte en reliant brièvement la broche RUN à GND. Utilise toujours un drapeau d'arrêt comme dans l'exemple et évite les grosses allocations mémoire (grandes listes, longues concaténations de chaînes) dans le thread supplémentaire ; le support multicœur de MicroPython est encore considéré comme expérimental.

Deux cœurs dans l'Arduino IDE : setup1() et loop1()

Le paquet Pico le plus utilisé dans l'Arduino IDE est le core Raspberry Pi Pico/RP2040/RP2350 d'Earle Philhower (arduino-pico). Avec lui, utiliser les deux cœurs est étonnamment simple : les fonctions habituelles setup() et loop() tournent sur le cœur 0 ; si tu ajoutes setup1() et loop1() au croquis, le cœur 1 démarre tout seul et exécute ces deux fonctions. Les données passent d'un cœur à l'autre avec l'objet rp2040.fifo. Le paquet officiel "Arduino Mbed OS RP2040" ne gère pas setup1(), vérifie donc que tu as choisi le bon paquet dans le gestionnaire de cartes.

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}
FonctionCe qu'elle fait
rp2040.fifo.push(x)Envoie une valeur 32 bits à l'autre cœur ; attend de la place si la file est pleine
rp2040.fifo.push_nb(x)Essaie d'envoyer sans attendre ; renvoie false si la file est pleine
rp2040.fifo.pop()Lit une valeur venant de l'autre cœur ; attend si la file est vide
rp2040.fifo.pop_nb(&x)Écrit la valeur dans x et renvoie true s'il y en a une, sinon renvoie false tout de suite
rp2040.fifo.available()Indique combien de valeurs attendent d'être lues
rp2040.idleOtherCore() / resumeOtherCore()Met brièvement l'autre cœur en pause puis le relance (par exemple pendant une écriture en flash)
N'utilise pas un même périphérique, comme le port série, un écran ou un bus SPI, depuis les deux cœurs à la fois. Comme dans l'exemple, confie chaque périphérique à un seul cœur et laisse l'autre lui envoyer des nombres par la FIFO. Tu évites ainsi les textes mélangés et les écrans corrompus sans avoir besoin de verrous.

Qu'est-ce que le PIO ? Fonctionnement des E/S programmables

Sur la plupart des microcontrôleurs, les blocs de communication et de signal comme l'UART, le SPI, l'I2C et la PWM sont figés dans le silicium ; si tu as besoin d'un protocole absent de la liste, le processeur s'en charge en allumant et éteignant les broches une à une (bit-banging). Cela occupe le processeur, et le timing se dégrade dès qu'une interruption s'intercale. Le PIO (Programmable Input/Output) règle le problème à la racine : chaque machine à états est un minuscule processeur à neuf instructions conçu uniquement pour piloter des broches. Chaque instruction s'exécute en exactement un cycle d'horloge, et on peut lui ajouter un délai de 0 à 31 cycles. Tu sais donc précisément, en comptant les cycles, au bout de combien de nanosecondes arrive chaque front du signal. La vitesse de la machine se règle en divisant l'horloge système par un diviseur compris entre 1 et 65536.

InstructionRôle
set(pins, v)Écrit une constante de 0 à 31 sur des broches ou dans les registres X/Y
out(dest, n)Transfère n bits du registre de décalage de sortie (OSR) vers des broches ou X/Y
in_(src, n)Fait entrer n bits venant de broches ou de registres dans le registre d'entrée (ISR)
pull() / push()Tire un mot de la FIFO TX vers l'OSR, pousse le mot de l'ISR dans la FIFO RX
jmp(cond, label)Saute selon une condition (X nul, broche haute, X-- etc.)
wait(polarité, src, n)Attend qu'une broche ou un drapeau IRQ atteigne le niveau voulu
mov(dest, src)Copie entre registres, avec inversion des bits si besoin
irq(n)Envoie un drapeau d'interruption au CPU ou à une autre machine à états
nop() et .side(v)Instruction vide ; le side-set change une broche dans le même cycle que n'importe quelle instruction

En MicroPython, les programmes PIO sont des fonctions Python écrites avec le décorateur @rp2.asm_pio ; la fonction n'est pas appelée, MicroPython l'assemble en code machine. Tu la charges ensuite dans une machine à états avec rp2.StateMachine(n, programme, freq=..., set_base=...). Sur le Pico, les numéros 0 à 3 appartiennent au PIO0 et 4 à 7 au PIO1. La valeur freq est le nombre de cycles d'instruction par seconde et ne peut pas descendre sous 1/65536 de l'horloge système : à 125 MHz cela fait environ 1,9 kHz, à 150 MHz environ 2,3 kHz. Pour un signal plus lent, utilise des délais ou des boucles avec les compteurs X/Y.

Un signal carré avec le PIO : ton premier programme de machine à états

Dans le premier exemple, on joue une gamme sur un buzzer passif relié à GP15, puis on laisse tourner un signal carré continu de 1 kHz. Le programme ne contient que deux instructions : mettre la broche à HIGH et attendre 9 cycles, la mettre à LOW et attendre 9 cycles. Comme chaque instruction dure elle-même 1 cycle, une période fait 20 cycles : il suffit de donner à la machine à états 20 fois la fréquence voulue. Relie la patte positive du buzzer à GP15 et la négative à GND. À la place du buzzer, tu peux utiliser un oscilloscope ou une LED avec une résistance de 220 ohms ; choisis la résistance avec le calculateur de résistance pour 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 formule est simple : fréquence du son = fréquence de la machine à états / nombre de cycles. Pour 1000 Hz, la machine tourne à 20 000 Hz, bien au-dessus de la limite basse. Si tu supprimes les délais et ne gardes que les deux set(), une période tombe à 2 cycles ; avec la machine à 125 MHz, tu obtiens un signal carré de 62,5 MHz sur la broche. N'oublie pas que la PWM matérielle sait faire la même chose : pour un son simple ou la luminosité d'une LED, la PWM suffit. La différence du PIO, c'est que tu décides entièrement de la forme du signal ; en quelques lignes tu génères des impulsions de largeur variable, un nombre précis d'impulsions ou des sorties qui réagissent à une entrée.

Piloter des LED WS2812B (NeoPixel) avec le PIO

Anneau RGB WS2812B à 24 LED
Anneau WS2812B à 24 LED : chaque LED contient son propre contrôleur et reçoit sa couleur sous forme de 24 bits sur une seule ligne de données.

Les LED WS2812B attendent une impulsion d'environ 1,25 microseconde par bit ; la durée pendant laquelle l'impulsion reste à HIGH indique si le bit vaut 1 ou 0. C'est un cas d'école pour le PIO. Le programme ci-dessous s'inspire de l'exemple MicroPython officiel de Raspberry Pi : la machine à états tourne à 8 MHz et chaque bit dure exactement 10 cycles, soit 800 000 bits par seconde. Grâce à autopull, la machine récupère une nouvelle couleur dans la FIFO tous les 24 bits ; le processeur se contente de déposer le tableau dans la FIFO avec sm.put(). Adapte le nombre de LED à ton ruban ; pour le fonctionnement du WS2812B et son ordre des couleurs, consulte le guide WS2812B NeoPixel.

Broche WS2812BBroche du PicoRemarque
DIN (entrée de données)GP16 (broche physique 21)Une résistance série de 330 ohms protège le signal
5V / VCCVBUS (broche 40)5 V de l'USB ; avec beaucoup de LED, utilise une alimentation 5 V externe
GNDGND (ex. broche 38)Le GND de l'alimentation externe doit aussi être relié ici
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

Suivons le programme ligne par ligne. out(x, 1) prend un bit de l'OSR dans le registre X pendant que le side-set maintient la ligne à LOW (3 cycles). Ensuite la ligne passe à HIGH et X est testé (2 cycles). Si le bit vaut 1, la ligne reste HIGH 5 cycles de plus ; s'il vaut 0, la ligne repasse à LOW et la machine attend 5 cycles. Les deux chemins totalisent 10 cycles, soit 1,25 µs. pull_thresh=24 indique à la machine de n'utiliser que 24 bits de chaque mot ; c'est pourquoi la couleur est décalée à gauche côté Python (sm.put(pixels, 8)). L'ordre des couleurs est GRB et non RGB ; si le rouge et le vert sont inversés, c'est pour ça. Comme 24 LED en blanc total peuvent tirer jusqu'à 1,4 A, la luminosité est limitée à 20 % dans l'exemple.

Si tu veux seulement allumer des LED, le module neopixel intégré à MicroPython fait aussi l'affaire : np = neopixel.NeoPixel(Pin(16), 24), np[0] = (255, 0, 0), np.write(). L'intérêt de la version PIO, c'est que le processeur reste libre pendant l'envoi et que tu peux adapter la même technique à d'autres protocoles.

Qu'est-ce qui change avec le Pico 2 (RP2350) ?

Le Raspberry Pi Pico 2 est la nouvelle génération, de même taille et avec le même brochage, mais équipée d'une puce RP2350. Il a toujours deux cœurs, mais tu peux cette fois choisir au démarrage entre deux cœurs Arm Cortex-M33 ou deux cœurs RISC-V Hazard3. Tout le code de cet article fonctionne sans modification sur le Pico 2 : _thread est identique en MicroPython et, dans le core Arduino-Pico, setup1()/loop1() et rp2040.fifo gardent les mêmes noms. Côté PIO, un troisième bloc s'ajoute, ce qui donne 12 machines à états (0 à 11 en MicroPython), plus quelques nouvelles options d'instructions.

CaractéristiquePico (RP2040)Pico 2 (RP2350)
Cœurs2 x Cortex-M0+2 x Cortex-M33 ou 2 x Hazard3 RISC-V
Fréquence133 MHz150 MHz
RAM264 Ko520 Ko
Flash sur la carte2 Mo4 Mo
PIO2 blocs, 8 machines à états3 blocs, 12 machines à états
Virgule flottanteLogicielle (routines rapides en ROM)FPU matérielle (simple précision) + coprocesseur double précision
SécuritéAucuneTrustZone, démarrage sécurisé, 8 Ko d'OTP
Fichier MicroPythonRPI_PICO (.uf2)RPI_PICO2 (.uf2)

En pratique, la différence la plus sensible du Pico 2 est la vitesse : grâce à la FPU matérielle, les calculs riches en flottants (filtres, fusion de capteurs, traitement audio) vont plusieurs fois plus vite, et le double de RAM laisse de la place pour de gros tampons d'écran. Il faut seulement flasher le bon fichier UF2 pour la carte ; celui du Pico ne fonctionne pas sur le Pico 2. Les premières versions du RP2350 avaient un défaut matériel connu (E9) avec les entrées utilisant le pull-down interne, corrigé dans la révision A4 ; une résistance externe pour câbler les boutons reste le choix sûr dans tous les cas.

Quand utiliser deux cœurs, et quand le PIO ?

Ton besoinMeilleure solutionPourquoi ?
Luminosité de LED, servo, son simplePWM matérielleBloc prêt à l'emploi, une ligne de code
Quelques capteurs + écran + boutonUn cœur, millis() ou asyncioInutile d'ajouter de la complexité
Animation fluide pendant de longs calculsDeux cœursLe calcul sur le cœur 1 ne fige pas l'écran
Mesure continue à côté du Wi-FiDeux cœursLes délais réseau ne perturbent pas l'intervalle de mesure
WS2812B, UART supplémentaire, émetteur IR, encodeurPIOPrécision à la nanoseconde, CPU libre
Vidéo VGA/DVI, audio I2S, signaux très rapidesPIO + DMADébits que le CPU ne pourrait pas suivre

En bref : pense au second cœur quand des tâches se gênent dans le logiciel, et au PIO quand les broches demandent une précision inférieure à la microseconde. La plupart des projets de loisir n'ont besoin ni de l'un ni de l'autre ; mais si un jour ton animation WS2812B saccade pendant une requête Wi-Fi ou que la carte manque d'UART, la solution t'attend déjà dans le Pico.

Idées de projets avec le PIO et les deux cœurs du Pico

  • Anneau de LED réactif à la musique : le cœur 1 traite les échantillons du micro, le PIO pilote l'anneau WS2812B et le cœur 0 surveille les boutons.
  • Générateur d'impulsions à huit voies : chaque machine à états produit un signal carré à une fréquence différente pour des drivers de moteurs pas à pas ou des bancs de test.
  • Enregistreur de données sans trous : le cœur 1 lit l'ADC toutes les 10 ms pendant que le cœur 0 envoie les données en Wi-Fi ; même avec un réseau lent, aucune mesure n'est perdue.
  • Ports série supplémentaires : ouvre un troisième et un quatrième UART avec le PIO pour brancher un GPS et un module Bluetooth en même temps.
  • Compteur d'encodeur rotatif : le PIO compte les impulsions de l'encodeur sans en rater une seule ; le processeur lit seulement le résultat.
  • Veilleuse qui respire : confie les fondus doux en PWM au cœur 1 et le capteur de lumière au cœur 0.

Erreurs fréquentes et solutions

SymptômeCauseSolution
La carte ne répond plus après l'arrêtLa boucle du cœur 1 tourne encoreUtilise un drapeau d'arrêt ; au besoin, débranche et rebranche l'USB
start_new_thread échoue la deuxième foisLe Pico n'accepte qu'un thread supplémentaireN'en lance pas un nouveau avant la fin du précédent
Les variables prennent parfois des valeurs absurdesLes deux cœurs écrivent sans verrouUtilise allocate_lock() ou la FIFO
StateMachine lève une ValueErrorfreq est sous horloge système / 65536Augmente la fréquence et ralentis avec des délais
PIOASMError: delay too largeLes bits de side-set réduisent le champ de délaiDécoupe le délai ou ajoute un nop()
Rouge et vert inversés sur les WS2812BL'ordre des couleurs est GRBUtilise le format (g << 16) | (r << 8) | b
setup1() ne s'exécute jamaisLe paquet Arduino Mbed RP2040 est sélectionnéInstalle le paquet Raspberry Pi Pico/RP2040 d'Earle Philhower
Quand tu écris un programme PIO, note le nombre de cycles de chaque instruction et vérifie que le total correspond au temps voulu. Un oscilloscope ou un analyseur logique bon marché aide beaucoup ; sinon, baisse provisoirement la fréquence de la machine à 2-3 kHz et observe la broche avec une LED pour voir les erreurs à l'œil nu.

Les deux cœurs et le PIO transforment le Pico d'un simple clignoteur de LED en véritable système embarqué, mais tout repose sur la même base : bien connaître ses broches, ses timings et ses LED. Dans l'application BlueGrays, tu trouveras des projets Raspberry Pi Pico comme le clignotement de LED, le chenillard, l'effet K2000, une machine à états pilotée par bouton et un effet de respiration en PWM, chacun avec schéma et code MicroPython pas à pas, prêts à accueillir les techniques de cet article.

Questions fréquentes

Qu'est-ce que le PIO du Raspberry Pi Pico ?

Le PIO (E/S programmables) regroupe de petites machines à états des puces RP2040 et RP2350 qui fonctionnent indépendamment du processeur. Chacune exécute au cycle près un programme écrit dans un langage simple de neuf instructions. Il prend en charge les tâches à timing critique comme les LED WS2812B, des UART supplémentaires, l'émission IR ou la lecture d'encodeurs sans occuper le CPU.

Combien de cœurs a le Raspberry Pi Pico ?

La puce RP2040 du Pico et du Pico W possède deux cœurs Cortex-M0+. Le RP2350 du Pico 2 en a aussi deux, et l'on choisit au démarrage entre des cœurs Cortex-M33 et Hazard3 RISC-V. Au lancement du programme, seul le cœur 0 tourne ; c'est toi qui démarres le cœur 1.

Combien de machines à états PIO possède le Pico ?

Le RP2040 a deux blocs PIO de 4 machines à états chacun, soit 8 au total. La mémoire de programme de 32 instructions de chaque bloc est partagée entre ses machines. Le RP2350 du Pico 2 compte trois blocs PIO, donc 12 machines à états.

MicroPython peut-il utiliser les deux cœurs du Pico ?

Oui. Sur le Pico, une fonction lancée avec _thread.start_new_thread() s'exécute sur le second cœur. Un seul thread supplémentaire peut tourner à la fois, et les données partagées doivent être protégées par un verrou créé avec _thread.allocate_lock(). Le support multicœur restant expérimental dans MicroPython, mieux vaut éviter les grosses opérations mémoire dans ce thread.

Comment utiliser le second cœur du Pico dans l'Arduino IDE ?

Après avoir installé le paquet Raspberry Pi Pico/RP2040/RP2350 d'Earle Philhower, il suffit d'ajouter les fonctions setup1() et loop1() au croquis ; le cœur 1 les exécute automatiquement. Les valeurs 32 bits passent d'un cœur à l'autre avec rp2040.fifo.push() et rp2040.fifo.pop(). Le paquet RP2040 d'Arduino basé sur Mbed ne propose pas cette fonction.

Faut-il obligatoirement le PIO pour les LED WS2812B ?

Non. Le module neopixel intégré à MicroPython et la bibliothèque Adafruit NeoPixel sous Arduino fonctionnent très bien sur le Pico. L'intérêt d'écrire ta propre version PIO est de laisser le processeur libre pendant l'envoi et de pouvoir appliquer la même technique à d'autres protocoles à timing critique.

Voir tous les projets
Clignotement LED Intégrée Raspberry Pi Pico (Pico et Pico W)
#147Débutant

Clignotement LED Intégrée Raspberry Pi Pico (Pico et Pico W)

Le projet d'ouverture de la série Raspberry Pi Pico ! Nous faisons clignoter la LED verte intégrée pour vérifier que la carte fonctionne — une première expérience classique "Hello World". AUCUN composant externe, juste Pico et câble USB. Le code est écrit en MicroPython (le langage officiellement soutenu par la Raspberry Pi Foundation). Apprends la classe machine.Pin, le mode Pin.OUT, les méthodes led.on()/off() et time.sleep() dans Thonny IDE. Suis chaque basc

5.0(1)
10
Allumage d'une LED externe sur le Raspberry Pi Pico (plaque d'essai)
#148Débutant

Allumage d'une LED externe sur le Raspberry Pi Pico (plaque d'essai)

Après la LED intégrée du Pico : connecter une LED externe à la maquette et la contrôler via la broche GP14. Ce projet vous permet d’apprendre comment utiliser n’importe quelle broche GPIO à usage général du Pico en sortie, de comprendre la polarité de la LED (grande patte = anode/+, petite patte = cathode/–) et de découvrir pourquoi une résistance de limitation de courant (220 Ω) est indispensable. Si vous n'installez pas cette résistance, un courant excessif traversera la LED et celle-ci risque

4.5(2)
7
Simulateur de Feu de Circulation Raspberry Pi Pico (3 LEDs séquentielles)
#150Débutant

Simulateur de Feu de Circulation Raspberry Pi Pico (3 LEDs séquentielles)

Nous simulons un vrai feu de circulation avec des LEDs rouge, jaune et verte. Vert 4 sec "passez", jaune 1,5 sec "préparez", rouge 3 sec "arrêtez" et rouge+jaune 1 sec "départ" (motif européen). Apprends la définition de fonction, la logique de machine à états et la synchronisation multi-GPIO. Circuit simple : 3 LEDs sur GP11/GP12/GP13, chacune avec 220Ω.

4.0(1)
12
Raspberry Pi Pico Chenillard LED (6 LEDs)
#153Débutant

Raspberry Pi Pico Chenillard LED (6 LEDs)

Animation chenillard classique avec Pico — version 6 LEDs ! 6 LEDs s'allument séquentiellement de gauche à droite. Apprends la liste Python, la compréhension de liste ([Pin(p, Pin.OUT) for p in LED_PINS]), la boucle for avec contrôle de broches et enumerate(). 6 LEDs sur GP9-GP14, chacune avec résistance 220Ω. Code très propre : au lieu de définir 6 LEDs une par une, nous utilisons une liste — tu peux ajouter des LED dans la limite des GPIO libres et utilisables, chacune avec sa propre résistanc

0.0(0)
8
Raspberry Pi Pico Knight Rider LED Scanner (6 LED Aller-Retour)
#154Débutant

Raspberry Pi Pico Knight Rider LED Scanner (6 LED Aller-Retour)

Effet scanner LED "Knight Rider" classique avec Pico ! 6 LEDs vont de gauche à droite, puis de droite à gauche, à l'infini. Apprends reversed() et deux boucles for séparées pour motif aller-retour.

0.0(0)
13
Feu de signalisation Raspberry Pi Pico avec sélecteur à 4 modes (machine à états)
#158Intermédiaire

Feu de signalisation Raspberry Pi Pico avec sélecteur à 4 modes (machine à états)

4 modes différents pour le feu tricolore avec un seul bouton ! À chaque pression sur le bouton, le mode change : Cycle normal (rouge → orange → vert dans cet ordre), Rouge uniquement (ordre d'arrêt), Orange clignotant (attention/mode nuit), Éteint (entretien). Notions : state machine (machine à états — la variable mode détermine le comportement à adopter), modulo mod cycling ((mode + 1) % 4 → 0→1→2→3→0, boucle infinie), synchronisation non bloquante (les phases de

5.0(1)
15
Raspberry Pi Pico Bouton Debounce : IRQ vs Software Polling
#159Intermédiaire
Premium

Raspberry Pi Pico Bouton Debounce : IRQ vs Software Polling

Une pression, le code compte 3-4 ? C'est le contact bounce ! Compare 2 méthodes de comptage dans Serial : RAW polling (attrape bounce, compte faux) vs IRQ + software debounce (précis). Apprends physique du bounce, software debounce, Pin.irq() interrupt, IRQ_FALLING trigger, ISR handlers, sécurité des variables globales ISR.

0.0(0)
12
Raspberry Pi Pico PWM LED Fade (Effet Respiration)
#160Débutant

Raspberry Pi Pico PWM LED Fade (Effet Respiration)

La LED ne clignote plus — elle respire ! Grâce au PWM (modulation de largeur d'impulsion) du Raspberry Pi Pico, la luminosité monte puis descend lentement. Apprends la classe machine.PWM, duty_u16() (luminosité 16 bits 0–65535), la fréquence PWM, les boucles range() et la sortie quasi analogique. Base du mélange de couleurs RGB, du contrôle moteur, des servos et du buzzer.

0.0(0)
4