Rehberler · 8 dk okuma

Raspberry Pi Pico PIO ve İki Çekirdek: _thread, setup1, WS2812B

Raspberry Pi Pico PIO rehberi: durum makineleri, kare dalga ve WS2812B örneği, MicroPython _thread ve Arduino setup1/loop1 ile iki çekirdek, Pico 2 farkları.

Raspberry Pi Pico PIO birimi ve iki çekirdeği, bu küçük kartı aynı fiyattaki çoğu mikrodenetleyiciden ayıran iki özelliktir. Pico'nun kalbindeki RP2040 yongasında iki ayrı işlemci çekirdeği ve 8 adet PIO durum makinesi bulunur. Çekirdekler sayesinde bir yanda sensör okurken öbür yanda ekranı ya da LED animasyonunu hiç takılmadan sürdürebilirsin. PIO ise işlemciden bağımsız çalışan minik zamanlama motorlarıdır: WS2812B LED'lerinin istediği nanosaniye hassasiyetindeki sinyali, ek bir UART'ı ya da kare dalgayı işlemciye hiç yük bindirmeden üretir.

Bu rehberde önce iki çekirdeği MicroPython'da _thread modülüyle, sonra Arduino IDE'de setup1() ve loop1() ile çalıştıracaksın. Ardından PIO'nun nasıl çalıştığını, kaç durum makinesi olduğunu ve hangi komutları bildiğini öğrenip iki gerçek program yazacaksın: buzzer'dan nota çalan bir kare dalga üreteci ve bir WS2812B gökkuşağı. Son bölümde Pico 2'nin (RP2350) getirdiği farklara ve bu özelliklere gerçekten ne zaman ihtiyaç duyduğuna bakacağız. Pico'ya yeniysen önce Raspberry Pi Pico Nedir? ve MicroPython Nedir? yazılarını okumanı öneririm.

Raspberry Pi Pico geliştirme kartı
Raspberry Pi Pico: ortadaki RP2040 yongasının içinde iki Cortex-M0+ çekirdeği ve iki PIO bloğu bulunur. 26 GPIO pininin hepsi PIO'ya bağlanabilir.

Raspberry Pi Pico'nun iki çekirdeği ve PIO birimi nedir?

RP2040'ta çekirdek 0 ve çekirdek 1 adında iki eş Cortex-M0+ işlemci vardır. Program açıldığında yalnız çekirdek 0 çalışır; çekirdek 1 sen onu başlatana kadar uyur. İki çekirdek aynı 264 KB RAM'i ve aynı pinleri paylaşır, bu yüzden aralarında veri taşımak kolaydır ama aynı değişkene aynı anda yazmamaları gerekir. Bunun için yongada iki yönlü küçük bir donanım kuyruğu (FIFO, her yönde 8 sözcük) ve 32 adet donanım kilidi (spinlock) bulunur. PIO ise çekirdeklerden tamamen ayrı bir dünyadır: iki PIO bloğunun her birinde 4 durum makinesi, ortak 32 komutluk program belleği ve pinlere doğrudan erişim vardır. Durum makinesi bir kez başlatıldıktan sonra işlemci başka işle uğraşırken bile kendi programını döngü döngü yürütür.

KaynakRP2040 (Pico, Pico W)Ne işe yarar?
İşlemci çekirdeği2 x Cortex-M0+, 133 MHz (güncel belgelerde 200 MHz'e kadar)İki ayrı programı gerçekten aynı anda çalıştırmak
Çekirdekler arası FIFOHer yönde 8 x 32 bitÇekirdekler arasında sayı ve komut göndermek
Donanım kilidi (spinlock)32 adetOrtak veriye sırayla erişmek
PIO bloğu2 adet (PIO0, PIO1)Zamanlaması hassas pin işlerini işlemciden almak
Durum makinesiBlok başına 4, toplam 8Her biri ayrı bir PIO programı yürütür
PIO program belleğiBlok başına 32 komutO bloktaki 4 durum makinesi paylaşır
PIO FIFOMakine başına TX 4 + RX 4 sözcük (birleştirilince 8)İşlemci ile durum makinesi arasında veri

MicroPython'da ikinci çekirdek: _thread modülü

MicroPython'da çekirdek 1'i kullanmanın yolu _thread.start_new_thread(fonksiyon, argümanlar) çağrısıdır. Pico'da bu çağrı fonksiyonu ikinci çekirdekte başlatır; ana programın ise çekirdek 0'da devam eder. Aynı anda yalnız bir ek iş parçacığı çalışabilir, çünkü ikinci bir çekirdek yoktur. Aşağıdaki örnekte çekirdek 0 GP26'ya bağlı bir potansiyometreyi okuyup sonucu ekrana yazar, çekirdek 1 ise dahili LED'i potansiyometrenin belirlediği hızda yakıp söndürür. Çekirdek 1'deki sleep_ms() beklemesi çekirdek 0'ı hiç etkilemez; tek çekirdekte bunu yapmak için zamanlayıcı ya da asyncio gerekirdi.

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)
  • allocate_lock() ile oluşturulan kilit, with lock: bloğu içinde yalnız bir çekirdeğin ortak değişkene dokunmasını sağlar.
  • running bayrağı, Ctrl+C ile programı durdurduğunda çekirdek 1'e döngüden çıkmasını söyler; aksi halde LED yanıp sönmeye devam eder.
  • Pin("LED") yazımı hem Pico'da hem Pico W'de dahili LED'i bulur; Pico W'de LED kablosuz yongaya bağlı olduğu için Pin(25) çalışmaz.
  • Çekirdek 1'de uzun süren hesap, ekran çizimi ya da hassas zamanlama gibi işler, çekirdek 0'da ise Wi-Fi, menü ve kullanıcı etkileşimi tutmak iyi bir paylaşımdır.
Thonny'deki Durdur düğmesi yalnız çekirdek 0'daki programı keser. Çekirdek 1'de sonsuz döngü kalmışsa kart meşgul görünür ve yeni kod yüklenemez. Bu durumda USB kablosunu çıkarıp tak ya da RUN pinini GND'ye dokundurarak kartı sıfırla. Kodlarında her zaman örnekteki gibi bir durdurma bayrağı kullan ve ekstra iş parçacığından ağır bellek ayırma (büyük listeler, uzun metin birleştirme) yapmaktan kaçın; MicroPython'da çok çekirdek desteği hâlâ deneysel kabul edilir.

Arduino IDE'de iki çekirdek: setup1() ve loop1()

Arduino IDE'de Pico için en yaygın kullanılan paket, Earle Philhower'ın hazırladığı Raspberry Pi Pico/RP2040/RP2350 çekirdeğidir (arduino-pico). Bu pakette iki çekirdeği kullanmak şaşırtıcı derecede kolaydır: alışık olduğun setup() ve loop() çekirdek 0'da çalışır; çizime setup1() ve loop1() eklersen çekirdek 1 otomatik olarak başlar ve bu iki fonksiyonu yürütür. Çekirdekler arasında veri göndermek için rp2040.fifo nesnesi kullanılır. Arduino'nun resmî "Arduino Mbed OS RP2040" paketinde setup1() desteği yoktur; bu yüzden Kart Yöneticisi'nde doğru paketi seçtiğinden emin ol.

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}
FonksiyonNe yapar?
rp2040.fifo.push(x)32 bitlik değeri öbür çekirdeğe gönderir; kuyruk doluysa yer açılana kadar bekler
rp2040.fifo.push_nb(x)Beklemeden göndermeyi dener; kuyruk doluysa false döndürür
rp2040.fifo.pop()Öbür çekirdekten gelen değeri okur; kuyruk boşsa bekler
rp2040.fifo.pop_nb(&x)Değer varsa x'e yazar ve true döndürür, yoksa hemen false döndürür
rp2040.fifo.available()Okunmayı bekleyen değer sayısını verir
rp2040.idleOtherCore() / resumeOtherCore()Öbür çekirdeği kısa süreliğine durdurur ve yeniden başlatır (ör. flaşa yazarken)
Seri porta, ekran ya da SPI veriyolu gibi tek bir donanımı iki çekirdekten aynı anda kullanma. Örnekteki gibi her donanımı bir çekirdeğe ver, öbür çekirdek ona FIFO ile sayı göndersin. Böylece kilit kullanmadan karışık yazılar ve bozuk ekran görüntüleri yaşamazsın.

PIO nedir? Programlanabilir giriş-çıkış nasıl çalışır?

Çoğu mikrodenetleyicide UART, SPI, I2C ve PWM gibi haberleşme ve sinyal birimleri yongaya sabitlenmiştir; listede olmayan bir protokol gerekirse işlemci pinleri tek tek açıp kapatarak (bit-banging) idare eder. Bu yöntem işlemciyi meşgul eder ve kesmeler araya girince zamanlama bozulur. PIO (Programmable Input/Output) bu sorunu kökten çözer: her durum makinesi, yalnız pin işleri için tasarlanmış dokuz komutluk minik bir işlemcidir. Her komut tam olarak bir saat döngüsünde çalışır ve istenirse sonuna 0–31 döngülük gecikme eklenir. Böylece bir sinyalin her kenarının kaç nanosaniyede geleceğini döngü sayarak kesin olarak bilirsin. Durum makinesinin hızı, sistem saatinin bir bölücüyle (1 ile 65536 arası) bölünmesiyle ayarlanır.

KomutGörevi
set(pins, v)Pinlere ya da X/Y yazmaçlarına 0–31 arası sabit değer yazar
out(hedef, n)Çıkış kaydırma yazmacından (OSR) n biti pinlere ya da X/Y'ye aktarır
in_(kaynak, n)Pinlerden ya da yazmaçlardan n biti giriş kaydırma yazmacına (ISR) alır
pull() / push()TX FIFO'dan OSR'ye sözcük çeker, ISR'deki sözcüğü RX FIFO'ya iter
jmp(koşul, etiket)Koşula göre (X sıfır mı, pin yüksek mi, X-- vb.) atlar
wait(kutup, kaynak, no)Bir pin ya da IRQ bayrağı istenen seviyeye gelene kadar bekler
mov(hedef, kaynak)Yazmaçlar arasında kopyalar, istenirse bitleri ters çevirir
irq(no)İşlemciye ya da başka bir durum makinesine kesme bayrağı gönderir
nop() ve .side(v)Boş komut; side-set ile her komutla birlikte bir pini aynı döngüde değiştirmek

MicroPython'da PIO programları @rp2.asm_pio süslemesiyle yazılan Python fonksiyonlarıdır; fonksiyon çağrılmaz, MicroPython onu derleyip makine koduna çevirir. Sonra rp2.StateMachine(no, program, freq=..., set_base=...) ile bir durum makinesine yüklersin. Pico'da makine numaraları 0–3 PIO0'a, 4–7 PIO1'e aittir. freq değeri saniyedeki komut döngüsü sayısıdır ve sistem saatinin 65536'da birinden düşük olamaz; 125 MHz'de bu yaklaşık 1,9 kHz, 150 MHz'de 2,3 kHz eder. Daha yavaş bir sinyal gerekiyorsa gecikmeleri ya da X/Y sayaçlı döngüleri kullanırsın.

PIO ile kare dalga: ilk durum makinesi programın

İlk örnekte GP15'e bağlı pasif bir buzzer'dan sırayla notalar çalıp sonunda 1 kHz'lik sürekli bir kare dalga bırakacağız. Program yalnız iki komuttan oluşur: pini HIGH yap ve 9 döngü bekle, pini LOW yap ve 9 döngü bekle. Her komut kendisi 1 döngü sürdüğü için bir periyot 20 döngüdür; yani istediğin ton frekansının 20 katını durum makinesine vermen yeterli. Buzzer'ın artı ucunu GP15'e, eksi ucunu GND'ye bağla. Buzzer yerine osiloskop ya da bir LED ile 220 ohm direnç de kullanabilirsin; direnci LED direnci hesaplayıcısıyla seçebilirsin.

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")

Burada formül basittir: ton frekansı = durum makinesi frekansı / döngü sayısı. 1000 Hz için makine 20.000 Hz'de çalışır, bu da alt sınırın oldukça üstündedir. Gecikmeleri kaldırıp yalnız iki set() komutu bırakırsan bir periyot 2 döngüye iner; makineyi 125 MHz'de çalıştırınca pinde 62,5 MHz'lik bir kare dalga elde edersin. Aynı işi donanım PWM'iyle de yapabileceğini unutma: basit bir ton ya da LED parlaklığı için PWM yeterlidir. PIO'nun farkı, sinyalin biçimini tamamen senin belirlemendir; birkaç satırla değişken genişlikli darbeler, belirli sayıda darbe ya da bir girişe tepki veren çıkışlar üretebilirsin.

PIO ile WS2812B (NeoPixel) LED sürmek

WS2812B 24'lü RGB LED halka
24 LED'li WS2812B halkası: her LED'in içinde kendi denetleyicisi vardır ve rengini tek bir veri hattından gelen 24 bitle alır.

WS2812B LED'ler her bit için yaklaşık 1,25 mikrosaniyelik bir darbe bekler; bitin 1 mi 0 mı olduğu, darbenin HIGH kalma süresinden anlaşılır. Bu, PIO için ders kitabı örneğidir. Aşağıdaki program Raspberry Pi'nin resmî MicroPython örneğine dayanır: durum makinesi 8 MHz'de çalışır ve her bit tam 10 döngü sürer, böylece saniyede 800.000 bit gönderilir. autopull sayesinde makine her 24 bitte bir FIFO'dan yeni renk çeker; işlemci yalnız diziyi sm.put() ile FIFO'ya bırakır. LED sayısını kendi şeridine göre değiştir; WS2812B'nin çalışma mantığı ve renk sırası için WS2812B NeoPixel rehberine bakabilirsin.

WS2812B ucuPico piniNot
DIN (veri girişi)GP16 (fiziksel pin 21)Araya 330 ohm direnç koymak sinyali korur
5V / VCCVBUS (pin 40)USB'den 5 V; çok LED'de harici 5 V kaynak kullan
GNDGND (ör. pin 38)Harici kaynağın GND'si de buraya bağlanmalı
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

Programın mantığını satır satır izleyelim. out(x, 1) OSR'den bir biti X yazmacına alır ve bu sırada side-set ile hat LOW tutulur (3 döngü). Ardından hat HIGH yapılır ve X'e bakılır (2 döngü). Bit 1 ise hat 5 döngü daha HIGH kalır; bit 0 ise hat LOW'a çekilip 5 döngü beklenir. Her iki yol da toplam 10 döngüdür, yani 1,25 µs. pull_thresh=24 makineye her sözcükten yalnız 24 bit kullanmasını söyler; bu yüzden Python tarafında renk sola kaydırılır (sm.put(pixels, 8)). Renk sırası RGB değil GRB'dir; kırmızı ve yeşil yer değiştirmişse sorun buradadır. 24 LED tam beyazda 1,4 A'e kadar çekebileceği için örnekte parlaklık %20 ile sınırlandı.

Yalnız LED yakmak istiyorsan MicroPython'un hazır neopixel modülü de işini görür: np = neopixel.NeoPixel(Pin(16), 24), np[0] = (255, 0, 0), np.write(). PIO sürümünü yazmanın avantajı, gönderim sırasında işlemcinin serbest kalması ve aynı tekniği başka protokollere uyarlayabilmendir.

Pico 2 (RP2350) ile neler değişti?

Raspberry Pi Pico 2, aynı boyut ve aynı pin düzeniyle gelen ama içinde RP2350 yongası taşıyan yeni nesildir. İki çekirdek yine vardır, ancak bu kez açılışta iki Arm Cortex-M33 ya da iki Hazard3 RISC-V çekirdeği arasında seçim yapılabilir. Bu yazıdaki kodların hepsi Pico 2'de değişmeden çalışır: MicroPython'da _thread aynı, Arduino-Pico çekirdeğinde setup1()/loop1() ve rp2040.fifo aynı adlarla kullanılır. PIO tarafında üçüncü bir blok eklendiği için durum makinesi sayısı 12'ye çıkar (MicroPython'da 0–11) ve birkaç yeni komut seçeneği gelir.

ÖzellikPico (RP2040)Pico 2 (RP2350)
Çekirdekler2 x Cortex-M0+2 x Cortex-M33 ya da 2 x Hazard3 RISC-V
Saat hızı133 MHz150 MHz
RAM264 KB520 KB
Kart üstü flaş2 MB4 MB
PIO2 blok, 8 durum makinesi3 blok, 12 durum makinesi
Kayan noktaYazılımla (ROM'daki hızlı rutinler)Donanım FPU (tek hassasiyet) + çift hassasiyet yardımcı işlemcisi
GüvenlikYokTrustZone, güvenli açılış, 8 KB OTP
MicroPython dosyasıRPI_PICO (.uf2)RPI_PICO2 (.uf2)

Pratikte Pico 2'nin en hissedilir farkı hızdır: donanım FPU'su sayesinde float ağırlıklı hesaplar (filtreler, sensör füzyonu, ses işleme) birkaç kat hızlanır, iki kat RAM de büyük ekran tamponlarına yer açar. Dikkat etmen gereken tek şey, karta uygun UF2 dosyasını yüklemektir; Pico için olan dosya Pico 2'de çalışmaz. İlk RP2350 sürümlerinde dahili pull-down dirençli girişlerle ilgili bilinen bir donanım hatası (E9) vardı ve A4 revizyonunda giderildi; buton bağlarken harici direnç kullanmak her iki durumda da güvenli yoldur.

Ne zaman iki çekirdek, ne zaman PIO gerekir?

İhtiyacınEn uygun çözümNeden?
LED parlaklığı, servo, basit tonDonanım PWMHazır birim, tek satır kod
Birkaç sensör + ekran + butonTek çekirdek, millis() ya da asyncioEk karmaşıklığa gerek yok
Uzun hesap sürerken akıcı animasyonİki çekirdekHesap çekirdek 1'de ekranı dondurmaz
Wi-Fi yanında kesintisiz ölçümİki çekirdekAğ gecikmeleri ölçüm aralığını bozmaz
WS2812B, ek UART, IR verici, enkoderPIONanosaniye hassasiyeti, işlemci serbest
VGA/DVI görüntü, I2S ses, çok hızlı sinyalPIO + DMAİşlemcinin yetişemeyeceği veri hızı

Kısacası: işler yazılımda çakışıyorsa ikinci çekirdeği, pinlerde mikrosaniyenin altında hassasiyet gerekiyorsa PIO'yu düşün. Hobi projelerinin çoğunda ikisine de gerek kalmaz; ama bir gün WS2812B animasyonu Wi-Fi isteği sırasında takılmaya başlarsa ya da kartın UART'ları yetmezse, çözüm zaten Pico'nun içinde seni bekliyor demektir.

Pico PIO ve iki çekirdek ile proje fikirleri

  • Müziğe tepki veren LED halka: Çekirdek 1 mikrofon örneklerini işler, PIO WS2812B halkasını sürer, çekirdek 0 düğmeleri dinler.
  • Sekiz kanallı darbe üreteci: Her durum makinesi farklı frekansta kare dalga üretir; step motor sürücülerine ya da test düzeneklerine sinyal verir.
  • Kesintisiz veri kaydedici: Çekirdek 1 her 10 ms'de ADC okur, çekirdek 0 verileri Wi-Fi ile gönderir; ağ yavaşlasa da ölçüm kaçmaz.
  • Ek seri portlar: PIO ile üçüncü ve dördüncü UART'ı açıp GPS ve Bluetooth modülünü aynı anda bağla.
  • Döner enkoder sayacı: PIO, enkoder darbelerini hiç kaçırmadan sayar; işlemci yalnız sonucu okur.
  • Nefes alan gece lambası: PWM ile yumuşak geçişleri çekirdek 1'e, ışık sensörünü çekirdek 0'a ver.

Sık yapılan hatalar ve çözümleri

BelirtiNedenÇözüm
Durdurduktan sonra kart yanıt vermiyorÇekirdek 1'deki döngü hâlâ çalışıyorDurdurma bayrağı kullan; gerekirse USB'yi çıkarıp tak
start_new_thread ikinci kez hata veriyorPico'da yalnız bir ek iş parçacığı olabilirÖnceki iş bitmeden yenisini başlatma
Değişkenler arada saçma değer alıyorİki çekirdek aynı veriye kilitsiz yazıyorallocate_lock() ya da FIFO kullan
StateMachine ValueError veriyorfreq, sistem saati / 65536'dan küçükFrekansı artır, yavaşlatmayı gecikmeyle yap
PIOASMError: delay too largeSide-set bitleri gecikme alanını küçülttüGecikmeyi böl ya da nop() ekle
WS2812B'de kırmızı ve yeşil tersRenk sırası GRB(g << 16) | (r << 8) | b biçimini kullan
setup1() hiç çalışmıyorArduino Mbed RP2040 paketi seçiliEarle Philhower'ın Raspberry Pi Pico/RP2040 paketini kur
PIO programını yazarken her komutun döngü sayısını kâğıda yaz ve toplamın istediğin süreye eşit olup olmadığını kontrol et. Elinde osiloskop ya da ucuz bir mantık analizörü varsa, durum makinesi frekansını geçici olarak 2–3 kHz'e düşürüp pini bir LED'le izlemek de hataları gözle görmeni sağlar.

İki çekirdek ve PIO, Pico'yu basit bir LED yakıcıdan ciddi bir gömülü sisteme dönüştüren özelliklerdir; ama hepsi aynı temel üzerine kurulur: pinleri, zamanlamayı ve LED'leri iyi tanımak. BlueGrays uygulamasında Raspberry Pi Pico ile LED yakma, yürüyen LED, karaşimşek, buton ile durum makinesi ve PWM nefes efekti gibi projeleri devre şeması ve MicroPython koduyla adım adım bulabilir, bu yazıdaki teknikleri onların üzerine ekleyebilirsin.

Sıkça sorulan sorular

Raspberry Pi Pico PIO nedir?

PIO (programlanabilir giriş-çıkış), RP2040 ve RP2350 yongalarındaki, işlemciden bağımsız çalışan küçük durum makineleridir. Her biri dokuz komutluk basit bir dilde yazılan programı döngü hassasiyetinde yürütür. WS2812B LED, ek UART, IR gönderme ya da enkoder okuma gibi zamanlaması hassas işleri işlemciyi meşgul etmeden yapar.

Raspberry Pi Pico kaç çekirdekli?

Pico ve Pico W'deki RP2040 yongasında iki adet Cortex-M0+ çekirdeği vardır. Pico 2'deki RP2350'de de iki çekirdek bulunur ve açılışta Cortex-M33 ya da Hazard3 RISC-V çekirdekleri seçilebilir. Program başladığında yalnız çekirdek 0 çalışır, çekirdek 1'i sen başlatırsın.

Pico'da kaç tane PIO durum makinesi var?

RP2040'ta iki PIO bloğu ve her blokta 4 durum makinesi olmak üzere toplam 8 durum makinesi vardır. Her bloğun 32 komutluk program belleği o bloktaki makineler arasında paylaşılır. Pico 2'deki RP2350'de üç PIO bloğu, yani 12 durum makinesi bulunur.

MicroPython'da Pico'nun iki çekirdeği kullanılabilir mi?

Evet. _thread.start_new_thread() ile başlatılan fonksiyon Pico'da ikinci çekirdekte çalışır. Aynı anda yalnız bir ek iş parçacığı açılabilir ve ortak veriler için _thread.allocate_lock() ile kilit kullanmak gerekir. Çok çekirdek desteği MicroPython'da hâlâ deneysel olduğundan ek iş parçacığında ağır bellek işlemlerinden kaçınmak iyi olur.

Arduino IDE'de Pico'nun ikinci çekirdeği nasıl kullanılır?

Earle Philhower'ın Raspberry Pi Pico/RP2040/RP2350 paketini kurduktan sonra çizime setup1() ve loop1() fonksiyonlarını eklemen yeterlidir; çekirdek 1 bunları kendiliğinden çalıştırır. Çekirdekler arasında rp2040.fifo.push() ve rp2040.fifo.pop() ile 32 bitlik değerler gönderilir. Arduino'nun Mbed tabanlı RP2040 paketinde bu özellik yoktur.

WS2812B LED için PIO kullanmak şart mı?

Hayır. MicroPython'un hazır neopixel modülü ve Arduino'daki Adafruit NeoPixel kütüphanesi Pico'da sorunsuz çalışır. PIO ile kendin yazmanın faydası, gönderim sırasında işlemcinin serbest kalması ve aynı tekniği başka zamanlama hassas protokollere uygulayabilmendir.

Tüm projeleri gör
Raspberry Pi Pico Dahili LED Yakma (Pico ve Pico W)
#147Başlangıç

Raspberry Pi Pico Dahili LED Yakma (Pico ve Pico W)

Raspberry Pi Pico serisinin açılış projesi! Pico'nun üzerindeki yeşil dahili LED'i yakıp söndürerek kartın çalıştığını doğruluyoruz — klasik "Hello World" tarzı ilk deney. Harici bileşene ihtiyaç YOK, sadece Pico ve USB kablosu yeterli. Kod MicroPython ile yazıldı (Raspberry Pi Vakfı'nın resmi olarak desteklediği dil). Thonny IDE ile Pico'yu programlarken machine.Pin sınıfı, Pin.OUT mode, led.on()/off() metodları ve time.sleep() fonksiyonlarını kavrarsın. Seri

5.0(1)
10
Raspberry Pi Pico Harici LED Yakma (Breadboard)
#148Başlangıç

Raspberry Pi Pico Harici LED Yakma (Breadboard)

Pico'nun dahili LED'inden sonraki adım: breadboard'a harici LED bağlayıp GP14 pini üzerinden kontrol etmek. Bu proje ile Pico'nun herhangi bir genel amaçlı GPIO pinini output olarak nasıl kullanacağını, LED'in polaritesini (uzun bacak = anot/+, kısa bacak = katot/–) ve neden akım sınırlama direnci (220Ω) şart olduğunu öğreniyorsun. Direnci koymazsan LED'e aşırı akım gider ve yanabilir. Devre çok basit: Pico GP14 → 220Ω direnç → LED (+) → LED (–) → Pico GND. Wokwi'de simüle edebilir, gerçek bread

4.5(2)
7
Raspberry Pi Pico Trafik Lambası (3 LED Sıralı)
#150Başlangıç

Raspberry Pi Pico Trafik Lambası (3 LED Sıralı)

Kırmızı, sarı ve yeşil LED'lerle gerçek trafik ışığı mantığını simüle ediyoruz — Pico serisinin 3 LED'e geçen ilk projesi! Yeşil 4 saniye "geç", sarı 1.5 saniye "hazırlan", kırmızı 3 saniye "dur" ve kırmızı+sarı 1 saniye "kalkış" (Avrupa trafik lambası pattern'i). Bu proje ile fonksiyon tanımlama (def all_off()), durum makinesi mantığı (sequence) ve coklu GPIO senkronizasyonu kavramlarını öğreneceksin. Devre çok basit: GP11 (Yeşil), GP12 (Sarı), GP13 (Kırmızı) pinlerine 3 LE

4.0(1)
12
Raspberry Pi Pico Yürüyen LED / Chasing LED (6 LED)
#153Başlangıç

Raspberry Pi Pico Yürüyen LED / Chasing LED (6 LED)

Pico ile klasik "yürüyen LED" animasyonu — 6 LED versiyonu! 6 LED soldan sağa sırayla yanıp sönüyor, sanki ışık dizi boyunca yürüyor. Bu proje ile Python list yapısı (LED_PINS = [14, 13, 12, 11, 10, 9]), list comprehension ([Pin(p, Pin.OUT) for p in LED_PINS]), for döngüsü ile pin kontrolü ve enumerate() kavramlarını öğreneceksin. GP9-GP14 pinlerine 6 LED, her birinde 220Ω akım sınırlama direnci. Kod çok temiz: 6 LED'i tek tek tanımlamak yerine liste ile toplu tanımlıyoruz

0.0(0)
8
Raspberry Pi Pico Karaşimşek / Knight Rider (6 LED Git-Gel)
#154Başlangıç

Raspberry Pi Pico Karaşimşek / Knight Rider (6 LED Git-Gel)

Pico ile klasik "Karaşimşek" (Knight Rider) LED efekti! 6 LED önce soldan sağa yürür, sonra sağdan sola geri döner, sonsuza kadar. 80'lerin ikonik dizisindeki KITT arabasının ön ızgarası gibi. Bu proje Yürüyen LED projesinin bir üst versiyonu: aynı 6 LED devresi (GP9-GP14), farklı kod pattern'i. Yeni öğrenme: reversed() fonksiyonu ile liste ters çevirme, iki ayrı for döngüsü ile git-gel pattern, ve aynı devrede birden fazla animasyon yaratma.

0.0(0)
13
Raspberry Pi Pico Trafik Lambası 4 Mod Buton Seçici (State Machine)
#158Orta

Raspberry Pi Pico Trafik Lambası 4 Mod Buton Seçici (State Machine)

1 buton ile trafik lambasının 4 farklı modu! Butona her basışta mod değişir: Normal döngü (kırmızı → sarı → yeşil sırayla), Sadece Kırmızı (dur emri), Sarı Flash (dikkat/gece modu), Kapalı (bakım). Öğrenme: state machine (durum makinesi — mode değişkeni hangi davranışın çalışacağını belirler), modulo mod cycling ((mode + 1) % 4 → 0→1→2→3→0 sonsuz döngü), non-blocking timing (LED fazları time.ticks_ms() + time.ticks_diff() ile zamanlanır; ancak kod mod değişimi

5.0(1)
15
Raspberry Pi Pico Buton Debounce Karşılaştırma: IRQ vs Software Polling
#159Orta
Premium

Raspberry Pi Pico Buton Debounce Karşılaştırma: IRQ vs Software Polling

Butona bir kere bastığında kod 3-4 kere saydırdıysa nedeni contact bounce — buton metal kontakları temas anında mikrosaniyelerde defalarca aç-kapa yapar! Bu projede aynı butonu 2 farklı yöntemle sayıp Serial'da yan yana karşılaştırıyoruz: RAW polling (bounce'u yakalar, hatalı sayar) vs IRQ + software debounce (doğru sayar). Öğrenme: contact bounce fiziği (metal titreşim), software debounce (ticks_diff() > 50ms), Pin.irq() interrupt kaydı, IRQ_FALLING trigger tipi,

0.0(0)
12
Raspberry Pi Pico PWM ile LED Fade (Nefes Efekti)
#160Başlangıç

Raspberry Pi Pico PWM ile LED Fade (Nefes Efekti)

LED artık sadece yanıp sönmüyor — nefes alıyor! Raspberry Pi Pico'nun PWM (Pulse Width Modulation — darbe genişlik modülasyonu) özelliğiyle LED'in parlaklığı yavaş yavaş artar, sonra yavaş yavaş azalır. Uyku modundaki MacBook ışığı gibi sakin bir "nefes" efekti. Öğrenme: machine.PWM sınıfı (pini PWM çıkışına çevirme), duty_u16() (0–65535 arası 16-bit parlaklık değeri), PWM frekansı (freq(1000) — saniyede 1000 kez aç-kapa, göz titremeyi görmez), range() ile adım adım döngü

0.0(0)
4