Raspberry Pi Pico: PIO и два ядра — _thread, setup1, WS2812B
Руководство по PIO Raspberry Pi Pico: конечные автоматы, меандр и пример с WS2812B, работа двух ядер через MicroPython _thread и Arduino setup1/loop1, изменения в Pico 2.
Блок PIO Raspberry Pi Pico и два ядра — две особенности, которые отличают эту маленькую плату от большинства микроконтроллеров в её ценовой категории. Микросхема RP2040 в основе Pico содержит два отдельных процессорных ядра и 8 конечных автоматов PIO. Благодаря ядрам можно читать датчики на одном, пока на другом плавно работает дисплей или светодиодная анимация. А PIO — это набор миниатюрных устройств точного формирования временных сигналов, работающих независимо от процессора: они создают необходимый светодиодам WS2812B сигнал с наносекундной точностью, дополнительный UART или меандр, не нагружая CPU.
В этом руководстве ты сначала запустишь оба ядра с помощью модуля _thread в MicroPython, затем с помощью setup1() и loop1() в Arduino IDE. После этого разберёшься, как работает PIO, сколько в нём конечных автоматов и какие команды они понимают, и напишешь две настоящие программы: генератор меандра, играющий ноты на зуммере, и радугу на WS2812B. В последних разделах рассмотрим, что изменилось в Pico 2 (RP2350) и когда эти возможности действительно нужны. Если ты только знакомишься с Pico, рекомендую сначала прочитать «Что такое Raspberry Pi Pico?» и «Что такое MicroPython?».

Что представляют собой два ядра и PIO в Raspberry Pi Pico?
В RP2040 два одинаковых процессора Cortex-M0+, которые называются ядро 0 и ядро 1. При запуске программы работает только ядро 0; ядро 1 спит, пока ты его не запустишь. Оба ядра используют общие 264 КБ ОЗУ и одни и те же пины, поэтому передавать данные между ними легко, но записывать одну переменную одновременно нельзя. Для этого в микросхеме есть небольшая двунаправленная аппаратная очередь (FIFO, по 8 слов в каждом направлении) и 32 аппаратные блокировки (spinlock). PIO — совершенно отдельный мир: каждый из двух блоков PIO содержит 4 конечных автомата, общую память программы на 32 команды и имеет прямой доступ к пинам. После запуска конечный автомат выполняет свою программу такт за тактом, даже когда процессор занят чем-то другим.
| Ресурс | RP2040 (Pico, Pico W) | Для чего нужен? |
|---|---|---|
| Процессорное ядро | 2 x Cortex-M0+, 133 MHz (до 200 MHz в актуальной документации) | Действительно одновременное выполнение двух отдельных программ |
| Межъядерная FIFO | 8 x 32 бита в каждом направлении | Передача чисел и команд между ядрами |
| Аппаратная блокировка (spinlock) | 32 | Поочерёдный доступ к общим данным |
| Блок PIO | 2 (PIO0, PIO1) | Перенос работы с пинами, требующей точного времени, с процессора |
| Конечный автомат | 4 на блок, всего 8 | Каждый выполняет собственную программу PIO |
| Память программы PIO | 32 команды на блок | Общая для 4 конечных автоматов этого блока |
| FIFO PIO | TX 4 + RX 4 слова на автомат (8 при объединении) | Обмен данными между процессором и конечным автоматом |
Второе ядро в MicroPython: модуль _thread
В MicroPython для использования ядра 1 служит вызов _thread.start_new_thread(function, args). На Pico он запускает функцию на втором ядре, а основная программа продолжает работать на ядре 0. Одновременно может работать только один дополнительный поток, ведь третьего ядра нет. В примере ниже ядро 0 считывает потенциометр на GP26 и выводит результат, а ядро 1 мигает встроенным светодиодом со скоростью, заданной потенциометром. Ожидание sleep_ms() на ядре 1 никак не влияет на ядро 0; на одном ядре для этого понадобился бы таймер или asyncio.
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)- Блокировка, созданная через allocate_lock(), гарантирует, что внутри блока with lock: к общей переменной обращается только одно ядро.
- Флаг running сообщает ядру 1, что нужно выйти из цикла, когда ты останавливаешь программу сочетанием Ctrl+C; иначе светодиод продолжит мигать.
- Запись Pin("LED") находит встроенный светодиод и на Pico, и на Pico W; на Pico W светодиод подключён к микросхеме беспроводной связи, поэтому Pin(25) не работает.
- Удобно поручить ядру 1 долгие вычисления, отрисовку дисплея или точную работу со временем, а ядру 0 — Wi-Fi, меню и взаимодействие с пользователем.
Два ядра в Arduino IDE: setup1() и loop1()
Самый распространённый пакет для Pico в Arduino IDE — ядро Raspberry Pi Pico/RP2040/RP2350 от Earle Philhower (arduino-pico). Использовать с ним оба ядра удивительно просто: привычные setup() и loop() работают на ядре 0; если добавить в скетч setup1() и loop1(), ядро 1 автоматически запустится и будет выполнять эти две функции. Данные между ядрами передаются через объект rp2040.fifo. Официальный пакет Arduino «Arduino Mbed OS RP2040» не поддерживает setup1(), поэтому проверь, что в «Менеджере плат» (Boards Manager) выбран нужный пакет.
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}| Функция | Что делает |
|---|---|
| rp2040.fifo.push(x) | Передаёт 32-битное значение другому ядру; если очередь заполнена, ждёт свободного места |
| rp2040.fifo.push_nb(x) | Пробует отправить без ожидания; возвращает false, если очередь заполнена |
| rp2040.fifo.pop() | Читает значение от другого ядра; ждёт, если очередь пуста |
| rp2040.fifo.pop_nb(&x) | Если значение есть, записывает его в x и возвращает true, иначе сразу возвращает false |
| rp2040.fifo.available() | Возвращает количество значений, ожидающих чтения |
| rp2040.idleOtherCore() / resumeOtherCore() | Ненадолго приостанавливает и возобновляет работу другого ядра (например, при записи во флеш-память) |
Что такое PIO? Как работает программируемый ввод-вывод
В большинстве микроконтроллеров блоки связи и формирования сигналов, такие как UART, SPI, I2C и ШИМ, жёстко заданы в кремнии; если нужен протокол, которого в списке нет, процессор реализует его, поочерёдно включая и выключая пины (bit-banging). Это занимает процессор, а любое прерывание нарушает временные интервалы. PIO (Programmable Input/Output, программируемый ввод-вывод) решает проблему в корне: каждый конечный автомат — крошечный процессор с девятью командами, предназначенными только для работы с пинами. Каждая команда выполняется ровно за один такт, а в конце к ней можно добавить задержку от 0 до 31 такта. Поэтому, просто считая такты, ты точно знаешь время каждого фронта сигнала в наносекундах. Скорость конечного автомата задаётся делением системной тактовой частоты на коэффициент от 1 до 65 536.
| Команда | Назначение |
|---|---|
| set(pins, v) | Записывает константу 0–31 в пины или регистры X/Y |
| out(dest, n) | Переносит n бит из выходного сдвигового регистра (OSR) на пины или в X/Y |
| in_(src, n) | Сдвигает n бит с пинов или из регистров во входной сдвиговый регистр (ISR) |
| pull() / push() | Забирает слово из TX FIFO в OSR, отправляет слово из ISR в RX FIFO |
| jmp(cond, label) (команда условного перехода) | Выполняет переход по условию (X равен нулю, высокий уровень пина, X-- и так далее) |
| wait(polarity, src, idx) (команда ожидания) | Ждёт, пока пин или флаг IRQ примет нужный уровень |
| mov(dest, src) (команда копирования между регистрами) | Копирует данные между регистрами, при необходимости инвертируя биты |
| irq(idx) | Передаёт флаг прерывания CPU или другому конечному автомату |
| nop() и .side(v) | Пустая команда; side-set изменяет состояние пина в том же такте, что и любая команда |
В MicroPython программы PIO — это функции Python с декоратором @rp2.asm_pio; функция не вызывается, а ассемблируется MicroPython в машинный код. Затем ты загружаешь её в конечный автомат через rp2.StateMachine(id, program, freq=..., set_base=...). На Pico автоматы с номерами 0–3 относятся к PIO0, а 4–7 — к PIO1. Значение freq — число тактов выполнения команд в секунду, и оно не может быть ниже 1/65536 системной частоты; при 125 MHz это около 1,9 kHz, при 150 MHz — около 2,3 kHz. Если нужен более медленный сигнал, используй задержки или циклы со счётчиками X/Y.
Меандр с PIO: твоя первая программа конечного автомата
В первом примере сыграем звукоряд на пассивном зуммере, подключённом к GP15, а затем оставим непрерывный меандр частотой 1 kHz. В программе всего две команды: установить HIGH на пине и подождать 9 тактов, установить LOW на пине и подождать 9 тактов. Поскольку сама каждая команда занимает 1 такт, один период составляет 20 тактов, поэтому достаточно задать конечному автомату частоту в 20 раз выше нужной частоты звука. Подключи положительный вывод зуммера к GP15, а отрицательный — к GND. Вместо зуммера можно использовать осциллограф или светодиод с резистором на 220 Ω; подбери резистор с помощью калькулятора резистора для светодиода.
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")Формула здесь простая: частота звука = частота конечного автомата / число тактов. Для 1000 Hz автомат работает на 20 000 Hz, что намного выше нижнего предела. Если убрать задержки и оставить только две команды set(), один период сократится до 2 тактов; тогда при частоте автомата 125 MHz на пине получится меандр 62,5 MHz. Помни, что аппаратная ШИМ может делать то же самое: для простого звука или яркости светодиода достаточно ШИМ. Отличие PIO в том, что точную форму сигнала определяешь ты; несколькими строками можно создать импульсы переменной ширины, точное число импульсов или выходные сигналы, реагирующие на вход.
Управление светодиодами WS2812B (NeoPixel) через PIO

Светодиоды WS2812B ожидают импульс длительностью около 1,25 микросекунды на бит; будет ли бит равен 1 или 0, определяется длительностью уровня HIGH в импульсе. Это классическая задача для PIO. Программа ниже основана на официальном примере MicroPython от Raspberry Pi: конечный автомат работает на 8 MHz, а каждый бит занимает ровно 10 тактов, поэтому в секунду передаётся 800 000 бит. Благодаря autopull автомат забирает новый цвет из FIFO каждые 24 бита; процессор лишь помещает массив в FIFO через sm.put(). Измени число светодиодов под свою ленту; о принципе работы WS2812B и порядке цветов читай в руководстве по WS2812B NeoPixel.
| Пин WS2812B | Пин Pico | Примечание |
|---|---|---|
| DIN (вход данных) | GP16 (физический пин 21) | Последовательный резистор на 330 Ω защищает сигнальную линию |
| 5V / VCC | VBUS (пин 40) | 5 V от USB; для большого числа светодиодов используй внешний источник 5 V |
| GND | GND (например, пин 38) | GND внешнего источника тоже нужно подключить сюда |
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Разберём программу построчно. out(x, 1) берёт один бит из OSR в регистр X, а side-set удерживает линию в LOW (3 такта). Затем линия переходит в HIGH и проверяется X (2 такта). Если бит равен 1, линия остаётся в HIGH ещё 5 тактов; если он равен 0, линия переводится в LOW и автомат ждёт 5 тактов. Оба пути в сумме занимают 10 тактов, то есть 1,25 µs. pull_thresh=24 указывает автомату использовать только 24 бита каждого слова, поэтому на стороне Python цвет сдвигается влево (sm.put(pixels, 8)). Порядок цветов — GRB, а не RGB; если красный и зелёный поменялись местами, причина в этом. Поскольку 24 светодиода на полной яркости белого могут потреблять до 1,4 A, в примере яркость ограничена 20%.
Что изменилось в Pico 2 (RP2350)?
Raspberry Pi Pico 2 — новое поколение с теми же размерами и распиновкой, но с микросхемой RP2350 внутри. Ядер по-прежнему два, но теперь при загрузке можно выбрать два ядра Arm Cortex-M33 или два ядра Hazard3 RISC-V. Весь код из этой статьи работает на Pico 2 без изменений: _thread в MicroPython тот же, а в ядре Arduino-Pico setup1()/loop1() и rp2040.fifo сохраняют прежние имена. В PIO добавлен третий блок, поэтому конечных автоматов стало 12 (0–11 в MicroPython), а также появились новые варианты команд.
| Характеристика | Pico (RP2040) | Pico 2 (RP2350) |
|---|---|---|
| Ядра | 2 x Cortex-M0+ | 2 x Cortex-M33 или 2 x Hazard3 RISC-V |
| Тактовая частота | 133 MHz | 150 MHz |
| ОЗУ | 264 КБ | 520 КБ |
| Флеш-память на плате | 2 МБ | 4 МБ |
| PIO | 2 блока, 8 конечных автоматов | 3 блока, 12 конечных автоматов |
| Плавающая точка | Программно (быстрые процедуры в ПЗУ) | Аппаратный FPU (одинарная точность) + сопроцессор двойной точности |
| Безопасность | Нет | TrustZone, безопасная загрузка, 8 КБ OTP |
| Файл MicroPython | RPI_PICO (.uf2) | RPI_PICO2 (.uf2) |
На практике самое заметное отличие Pico 2 — скорость: благодаря аппаратному FPU задачи с большим числом вычислений с плавающей точкой (фильтры, объединение данных датчиков, обработка звука) выполняются в несколько раз быстрее, а удвоенный объём ОЗУ оставляет место для больших буферов дисплея. Важно загрузить правильный UF2-файл для платы: файл для Pico не работает на Pico 2. В ранних версиях RP2350 была известная аппаратная проблема (E9) со входами, использующими встроенную подтяжку к земле, которую исправили в ревизии A4; внешний резистор при подключении кнопок — безопасный выбор в любом случае.
Когда нужны два ядра, а когда PIO?
| Что нужно | Лучший вариант | Почему? |
|---|---|---|
| Яркость светодиода, сервопривод, простой звук | Аппаратная ШИМ | Готовый блок, одна строка кода |
| Несколько датчиков + дисплей + кнопка | Одно ядро, millis() или asyncio | Дополнительная сложность не нужна |
| Плавная анимация во время долгих вычислений | Два ядра | Вычисления на ядре 1 не замораживают дисплей |
| Непрерывные измерения одновременно с Wi-Fi | Два ядра | Сетевые задержки не нарушают интервал дискретизации |
| WS2812B, дополнительный UART, ИК-передатчик, энкодер | PIO | Наносекундная точность, CPU остаётся свободным |
| Видео VGA/DVI, звук I2S, очень быстрые сигналы | PIO + DMA | Скорость передачи данных, с которой CPU не справится |
Итак, о втором ядре стоит подумать, когда задачи мешают друг другу в программе, а о PIO — когда пинам нужна точность меньше микросекунды. В большинстве любительских проектов не нужно ни то ни другое; но если однажды анимация WS2812B начнёт дёргаться во время Wi-Fi-запроса или на плате закончатся UART, решение уже есть внутри Pico.
Идеи проектов с PIO и двумя ядрами Pico
- Светодиодное кольцо, реагирующее на музыку: ядро 1 обрабатывает отсчёты микрофона, PIO управляет кольцом WS2812B, а ядро 0 опрашивает кнопки.
- Восьмиканальный генератор импульсов: каждый конечный автомат создаёт меандр на своей частоте для драйверов шаговых двигателей или испытательных стендов.
- Регистратор данных без пропусков: ядро 1 считывает АЦП каждые 10 мс, а ядро 0 отправляет данные по Wi-Fi; медленная сеть не приводит к потере ни одного отсчёта.
- Дополнительные последовательные порты: создай третий и четвёртый UART с помощью PIO и подключи GPS и Bluetooth-модуль одновременно.
- Счётчик поворотного энкодера: PIO считает импульсы энкодера, не пропуская ни одного; процессор только читает результат.
- Ночник с эффектом дыхания: поручить плавное изменение яркости через ШИМ можно ядру 1, а датчик освещённости — ядру 0.
Частые ошибки и способы их устранения
| Симптом | Причина | Решение |
|---|---|---|
| Плата не отвечает после остановки | Цикл на ядре 1 продолжает работать | Используй флаг остановки; при необходимости отключи и снова подключи USB |
| start_new_thread не срабатывает во второй раз | Pico допускает только один дополнительный поток | Не запускай новый, пока не завершится предыдущий |
| Переменные иногда получают бессмысленные значения | Оба ядра записывают общие данные без блокировки | Используй allocate_lock() или FIFO |
| StateMachine вызывает ValueError | freq ниже системной частоты / 65536 | Повысь частоту и замедли выполнение задержками |
| PIOASMError: delay too large (сообщение об ошибке: слишком большая задержка) | Биты side-set сократили поле задержки | Раздели задержку или добавь nop() |
| Красный и зелёный на WS2812B поменялись местами | Порядок цветов — GRB | Используй формат (g << 16) | (r << 8) | b |
| setup1() никогда не выполняется | Выбран пакет Arduino Mbed RP2040 | Установи пакет Raspberry Pi Pico/RP2040 от Earle Philhower |
Два ядра и PIO превращают Pico из простой мигалки в серьёзную встраиваемую систему, но всё опирается на одну основу: хорошее понимание пинов, временных интервалов и светодиодов. В приложении BlueGrays ты найдёшь проекты для Raspberry Pi Pico: мигание светодиодом, бегущие огни, эффект Knight Rider, конечный автомат с управлением кнопкой и эффект дыхания на ШИМ. Каждый проект содержит схему и код MicroPython, разобран пошагово и готов к добавлению приёмов из этой статьи.
Частые вопросы
Что такое PIO в Raspberry Pi Pico?
PIO (программируемый ввод-вывод) — набор небольших конечных автоматов в микросхемах RP2040 и RP2350, работающих независимо от процессора. Каждый выполняет программу на простом языке из девяти команд с точностью до такта. PIO берёт на себя задачи, требующие точного времени: управление светодиодами WS2812B, дополнительные UART, ИК-передачу или чтение энкодера, не занимая CPU.
Сколько ядер у Raspberry Pi Pico?
Микросхема RP2040 на Pico и Pico W имеет два ядра Cortex-M0+. RP2350 на Pico 2 тоже имеет два ядра, а при загрузке можно выбрать между Cortex-M33 и Hazard3 RISC-V. При запуске программы работает только ядро 0; ядро 1 ты запускаешь самостоятельно.
Сколько конечных автоматов PIO у Pico?
В RP2040 два блока PIO по 4 конечных автомата, всего 8. Память программы каждого блока на 32 команды используется совместно автоматами этого блока. В RP2350 на Pico 2 три блока PIO, то есть 12 конечных автоматов.
Может ли MicroPython использовать оба ядра Pico?
Да. На Pico функция, запущенная через _thread.start_new_thread(), работает на втором ядре. Одновременно может быть запущен только один дополнительный поток, а общие данные нужно защищать блокировкой из _thread.allocate_lock(). Поддержка нескольких ядер в MicroPython всё ещё экспериментальная, поэтому в дополнительном потоке лучше избегать интенсивной работы с памятью.
Как использовать второе ядро Pico в Arduino IDE?
После установки пакета Raspberry Pi Pico/RP2040/RP2350 от Earle Philhower просто добавь в скетч функции setup1() и loop1(); ядро 1 выполнит их автоматически. 32-битные значения передаются между ядрами через rp2040.fifo.push() и rp2040.fifo.pop(). У пакета RP2040 от Arduino на основе Mbed этой возможности нет.
Обязательно ли использовать PIO для светодиодов WS2812B?
Нет. И встроенный модуль neopixel в MicroPython, и библиотека Adafruit NeoPixel в Arduino хорошо работают на Pico. Преимущество собственной реализации на PIO в том, что процессор остаётся свободным во время передачи, а тот же приём можно применить к другим протоколам, требующим точного времени.
Что собрать после прочтения
Все проекты
Мигание встроенным светодиодом Raspberry Pi Pico (Pico и Pico W)
Первый проект серии Raspberry Pi Pico! Мигаем зелёным встроенным светодиодом, чтобы проверить, что плата работает, — классический первый опыт «Hello World». НИКАКИХ внешних компонентов, только Pico и USB-кабель. Код написан на MicroPython (официальном языке, поддерживаемом Raspberry Pi Foundation). Изучи в Thonny IDE класс machine.Pin, режим Pin.OUT, методы led.on()/off() и time.sleep(). Каждое переключение видно в выводе (REPL).

Raspberry Pi Pico: управление внешним светодиодом (макетная плата)
Следующий шаг после встроенного светодиода Pico: подключить внешний светодиод на макетной плате и управлять им через пин GP14. В этом проекте ты узнаешь, как использовать любой пин GPIO общего назначения у Pico как выход, какая полярность у светодиода (длинная ножка = анод/+, короткая = катод/–) и зачем нужен токоограничивающий резистор (220Ω). Без резистора через светодиод пойдёт слишком большой ток, и он может сгореть. Схема очень простая: Pico GP14 → резистор 220Ω → светодиод → GND.

Симулятор светофора на Pico (3 светодиода по очереди)
Настоящий светофор на красном, жёлтом и зелёном светодиодах — первый проект серии Pico с 3 светодиодами! Зелёный 4 с «едем», жёлтый 1,5 с «внимание», красный 3 с «стоп» и красный + жёлтый 1 с «приготовиться» (европейская схема светофора). Изучи объявление функций (def all_off()), логику конечного автомата (последовательность) и синхронизацию нескольких GPIO. Простая схема: 3 светодиода на GP11 (зелёный), GP12 (жёлтый), GP13 (красный), у каждого токоограничивающий резистор 220Ω.

Бегущие огни на Pico (6 светодиодов)
Классическая анимация «бегущие огни» на Pico — версия на 6 светодиодов! 6 светодиодов загораются по очереди слева направо — будто свет идёт по ряду. Изучи список Python, генератор списка ([Pin(p, Pin.OUT) for p in LED_PINS]), цикл for с управлением пинами и enumerate(). 6 светодиодов на GP9–GP14, у каждого токоограничивающий резистор 220Ω. Очень чистый код: вместо объявления 6 светодиодов по одному используется список, и можно добавлять светодиоды по числу свободных пинов GPIO.

Сканер Knight Rider на Pico (6 светодиодов туда-обратно)
Классический эффект светодиодного сканера Knight Rider на Pico! 6 светодиодов бегут слева направо, затем справа налево, и так бесконечно. Как передняя решётка машины KITT из культового сериала 80-х. Этот проект — доработка проекта «Бегущие огни»: та же схема из 6 светодиодов (GP9–GP14), но другой узор в коде. Изучи функцию reversed() для разворота списка, два отдельных цикла for для движения туда-обратно и несколько анимаций на одной схеме.

Светофор на Pico с выбором 4 режимов кнопкой (конечный автомат)
4 режима светофора всего одной кнопкой! Каждое нажатие меняет режим: обычный цикл (красный → жёлтый → зелёный по очереди), только красный (сигнал стоп), мигающий жёлтый (внимание/ночной режим) и выключено (обслуживание). Понятия: конечный автомат (переменная mode определяет, какое поведение выполняется), цикл режимов по модулю ((mode + 1) % 4 → 0→1→2→3→0), неблокирующий тайминг (фазы светодиодов отсчитываются через time.ticks_ms() + time.ticks_diff()).

Антидребезг кнопки на Pico: IRQ против программного опроса
Нажимаешь кнопку один раз, а код насчитывает 3–4? Это дребезг контактов — металлические контакты колеблются доли миллисекунды! Сравни в мониторе два способа подсчёта рядом: сырой опрос (ловит дребезг и считает лишнее) и IRQ + программный антидребезг (точно). Изучи физику дребезга, программный антидребезг (ticks_diff() > 50ms), регистрацию прерывания Pin.irq(), триггер IRQ_FALLING, обработчики ISR и безопасность global в ISR.

Плавное затухание светодиода с PWM на Pico (эффект дыхания)
Светодиод больше не просто мигает — он дышит! С помощью PWM (широтно-импульсной модуляции) Raspberry Pi Pico яркость светодиода медленно растёт и медленно падает, как огонёк спящего MacBook. Изучи класс machine.PWM, duty_u16() (16-битная яркость 0–65535), частоту PWM (freq(1000) — 1000 циклов вкл/выкл в секунду, мерцания не видно), циклы range() с шагом (вверх и вниз) и почти аналоговый выход с цифрового пина.
Другие статьи
- Что такое Raspberry Pi Pico? Установка MicroPython, распиновка и первые проекты
- Что такое MicroPython? Основные команды, REPL и первые программы
- Светодиодная лента WS2812B NeoPixel и Arduino: руководство по адресным RGB-светодиодам
- Что такое PWM? analogWrite, коэффициент заполнения и частота на Arduino
Приложение BlueGrays
Собери эту схему шаг за шагом в телефоне
Схема подключения, код и список деталей на одном экране. 200+ проектов на Arduino, ESP32 и Pico готовы к сборке.
Наведи камеру телефона
Наведи камеру на код — откроется магазин приложений на телефоне.
или открыть магазин

