20 Şubat 2026 Cuma

Raspberry Pi4 ile Canon MF 4450 için Yazdırma Sunucusu Oluşturmak


Merhaba. Yazının başlığından anlaşılacağı gibi, ağ bağlantısı olmayan Canon MF4450 yazıcımı Raspberry Pi4 ile yerel ağdan nasıl paylaşıma açtığımı anlatacağım. Bunu yapmak zaten oldukça kolay. Ben asıl bunu yaparken hangi yollardan geçtiğimi, hangi zorluklarla karşılaştığımı açıklamak istedim. O yüzden konfigürasyondan önce tasarımı tartışmak istiyorum.

Not: Yazının geri kalanında Raspberry Pi yerine RPi kısaltmasını kullanacağım.


Öncelikli konu platform seçimi. Başlıkta da yazdığım gibi ben RPi4'te çalıştım, daha doğrusu çalışmak zorunda kaldım. Elimde RPi 1B de var ve RPi 1B, bu iş için aslında donanım olarak yeterli. Disk olarak, bu makinada başka birşey çalışmayacaksa 8 GB'lık bir SD kart (4 GB işletim sistemi olmak üzere) ve bir de RPi 1'lerde onboard WiFi olmadığı için bir WiFi dongle gerekiyor. Ben RPi 4'te başka servisler de çalıştırdığım için disk olarak 128 GB'lik bir kart kullanıyorum ama bu da önemli ölçüde boş. Son olarak RPi 4'ün WiFi kartı onboard, bunun için ekstra bir donanım gerekmiyor.

Diğer yandan RPi4'ü sadece bu iş için kullanmak açıkça kaynak israfı, ancak RPi1'le başıma şunlar geldi:


İşletim Sistemi Dağıtımı

Bu iş için ilk önce RPi 1B'i denedim. Bu, 32-bit bir cihaz. Raspberry Pi OS'un (eski Raspbian) 32-bit sürümü resmi sitesinden indirilebiliyor. Bu Debian tabanlı bir dağıtım. Ben bunun lite sürümünü denedim çünkü RPi 1'ler her ne kadar grafik arayüzü çalıştırabilse de çok yavaş. Bunun dışında, Alpine RPi, DietPi ve piCore'u da denedim ancak bunlardan yalnız DietPi'yi kısmen kullanışlı buldum. Debian tabanlı ve RPi OS'un özelleştirilmişi gibi bir izlenim verdi. Ancak DietPi konfigürasyon scripti daha ilk boot'ta, çalışan bir internet bağlantısı istiyor. Bu konuya ileride değineceğim.

RPi4 ise 64-bit. Bunun için RPi OS'un 64-bit sürümünü resmi sitesinden indirdim. Bunun yanında Manjaro ARM, Ubuntu ve Fedora IOT de indirdim ancak ilk ikisini kullanmaya çok zamanım olmadı. Fedora IOT'nin mantığı hem klasik Fedora'dan hem de diğer dağıtımlardan çok farklı. Konfigürasyon için ignition adında cloud-init benzeri ama JSON tabanlı bir script kullanıyor. RPi OS ise başlangıç scriptiyle ayarlanıyor.


Raspberry Pi 1'de WiFi Konfigürasyonu

RPi 1 için bende TP-Link AC600 WiFi dongle vardı ve fark ettim ki, RPi OS'ta ve DietPi'da bunun chipseti (Realtek rtl8821au) için uygun sürücü olmadığından, kart otomatik tanınmıyor. github'da bu chipset için geliştirilen bir sürücü var, fakat indirmek için yine internet gerekiyor. Dolayısıyla RPi'i en başta kabloyla bağlamak zorunda kaldım. DietPi'nin en başta internet olmadan konfigürasyon yapamadığını söylemiştim. Bu durum yumurta/tavuk problemine yol açıyor. Alternatif: Github reposunu indirip SD karta yaz.

Bir de TP-Link'in TL-WM725N modelini (rtl8188eu chipset) sipariş ettim. Bunun sürücüsü RPi OS'ta var ve otomatik tanınıyor. Bu arada github'dan indirdiğim sürücüyü de derledim. gcc, make ve bc paketlerinin yüklü olması gerekiyor ve basit bir install-driver.sh scriptiyle derlenip dkms ile yükleniyor, ancak RPi1'in hızı nedeniyle derleme yaklaşık 4-5 sa sürüyor. Bir de özellikle DietPi gibi küçük olması için optimize edilmiş bir dağıtıma yalnız 1-2 kere kullanacağım ekstra paketler kurmak çok hoşuma gitmedi.


Raspberry Pi 1'de Yazıcı Konfigürasyonu

RPi1'le yaşadığım ikinci sorun (WiFi'dan sonra) Canon MF4450'nin 32-bit ARM için resmi olarak bir sürücüsünün olmaması. Başka model yazıcısı olup RPi1'de sorunsuz çalıştırabilenler bu bölümü atlayabilir.

Yazıcının sürücüsünü Canon'un resmi sitesinden indirdim. Bu sayfaya göre Linux ARM destekleniyor. Dosyada (linux-UFRII-drv-v620-m17n-20.tar.gz) ARM64, x86 ve x84 gibi platformlar için .deb ve .rpm dosyaları var ama ARM32 yok. O nedenle Sources dizinindeki kaynak kodları derlemeye karar verdim. Ama bu yine, sadece derlerken kullanacağım bir yığın paket kurmak demek. Üstelik sonuç olumsuz (gidiş yolunu merak etmeyenler burayı atlayabilir). Sürücüyü derlemek için autoconf ve automake başta olmak üzere toplam 11 tane paket kurmak gerekiyor.

apt install autoconf automake libcups2-dev libglib2.0-dev libltdl-dev libtool-bin libglade2-0 libglade2-dev libgtk-3-dev libxml2 libxml2-dev

Bunları kurduktan sonra sırayla cnrdrvcups-common-6.20 ve cnrdrvcups-lb-6.20 dizinlerindeki allgen.sh scriptini çalıştırıp kodu derledim ve make install ile dosyaları kopyaladım. Bir de DietPi'de cnrdrvcups-lb-6.20'yi derlerken bir gösterici dönüştürme hatası alınıyor. O nedenle CFLAGS değişkenini -fpermissive olarak ayarlamak gerekiyor. Ben bunun için allgen.sh içindeki case yapısına aşağıdaki case'i ekledim:

    "armv6l")
        _machine_type="MACHINETYPE=armv6"
        _cflags="CFLAGS=-fpermissive"
    ;;

Bunları yaptıktan sonra, ne derlerken ne de CUPS'tan yazıcıyla ilgili bir hata aldım, ama lokalden gönderdiğim yazdırma istekleri dahi yazıcıya ulaşmadı. Sorunu henüz çözemedim ama denemediğim bir kaç şey var, çözebilirsem bu ayrı bir makale konusu olabilir. Özetle hem WiFi sorunu -çözülmüş bile olsa- hem de yazıcı sorunu nedeniyle RPi 1B ile zaman kaybetmeyip çalışır haldeki RPi 4'e yoğunlaşmaya karar verdim.


Kurulum Adımları

Yazının bu bölümünde "Raspberry Pi Print Server" makalesini referans aldım. RPi 4 kurulumunu uzun uzun anlatmayacağım. İmaj dosyasını SD karta aktarıp boot ettikten sonra, bir sihirbaz kullanıcı adı ve WiFi gibi ayrıntıları sorup sistemi hazırlıyor. Eğer SSH açık değilse

sudo systemctl enable --now ssh

komutuyla açılabilir. Ardından makalede sözü edilen paketleri yükledim:

sudo apt install cups printer-driver-gutenprint hplip samba cups-bsd

Açıkçası hplip ve printer-driver-gutenprint pek işime yaramadı. Yazıya göre Canon dahil birçok yazıcı sürücüsü içeriyormuş ama görünüşe göre MF4450 modeli yok. cups-bsd ise Canon sürücüsünün bağımlı olduğu bir paket.

CUPS bir web arayüzü üzerinden çalışıyor ve burada ayar yapabilmek için kullanıcının lpadmin grubunda veya root olması gerek. Aşağıdaki komutla kullanıcı kendini bu gruba ekleyip CUPS'a dışarıdan erişim izni veriyor:

sudo usermod -a -G lpadmin $USER
cupsctl --remote-any
sudo systemctl restart cups

Bu adımdan sonra ağdaki bir bilgisayardan http://<makinaIP>:631/ adresinden erişilebiliyor olmalı. Erişim yoksa host firewall çalışıyor olabilir. Bu durumda sudo ufw disable ile firewall'u kapatarak veya söz konusu port açılarak erişilebilir. Hala erişilmiyorsa o portta çalışan bir process olup olmadığı ve CUPS'ın çalışıp çalışmadığı kontrol edilmelidir.

Buraya kadar herşey tamamsa küçük bir parantez açıp yazıcı sürücüsünü kurmalıyım.


Raspberry Pi 4'de Yazıcı Konfigürasyonu

RPi 4'te yazıcıyı tanıtmak için, RPi 1 için kullandığım sürücü paketini kullandım. Yazıcıyı bağladıktan sonra ARM64/Debian dizinindeki .deb dosyayı aşağıdaki komutla kurdum.

sudo dpkg -i cnrdrvcups-ufr2-uk_6.20-1.20_arm64.deb

Burada dikkat edilmesi gereken bir ayrıntı var: Eğer bir pencere yöneticisi kuruluysa, kurulum sırasında masaüstünde yandaki gibi bir pencere açılıyor. Siz SSH üzerinden kuruyorsanız, kurulum durdu gibi geliyor. Halbuki program GUI üzerinden girdi bekliyor. Bu nedenle kurulum sırasında monitörü de bağlamak gerekli. Bağlı yazıcı otomatik tanınıyor. Sonraki adımda yazıcının modeli seçilerek kurulum tamamlanıyor.



RPi 1'de saatlerce ve farklı şekillerde derlediğim halde varamadığım noktaya, RPi 4'le ortalama on dakika gibi bir sürede geldim. Şimdi tekrar servis konfigürasyonuna geri dönüyorum.


En başta yüklediğim samba, yazıcıyı Windows makinalara paylaştırmak için gerekli. Benim cups.conf'ta bir değişiklik yapmam gerekmedi. Herhangi bir dizin paylaşmayacağım için, smb.conf'ta [homes] bölümünü kapattım (comment out). [printers] ve [print$] bölümlerinde, guest ok = yes olarak ayarladım. testparm -s ile hata var mı diye kontrol edip, systemctl reload smbd komutuyla yeni ayarları yükledim.

DietPi'de bu adımlar biraz farklı. Birincisi CUPS ve samba'nın dietpi-software programıyla yüklenmesi tavsiye ediliyor. İkincisi DietPi'nin özelleştirilmiş bir smb.conf'u var. Default [printers] ve [print$] bölümlerini smb.conf.example dosyasından kopyalamak gerekiyor.

CUPS ile sürücü birbirinden bağımsız. Sürücü yüklü olmasa bile CUPS, yazıcıyı bulup web arayüzünde gösteriyor ve sorunsuz şekilde ekleniyor. Bunun için ilk iş web arayüzünde üst frame'den Administration'ı seçtim. Bu adımda HTTP'den HTTPS'e yönlendirme yapıldığı için tarayıcıda sertifika uyarısı çıkıyor. Bunu geçip lpadmin'e eklediğim kullanıcıyla login oldum. "Find New Printers"a basınca yazıcıyı otomatik buldu. "Add This Printer"a basınca hangi adla ekleyeceğimi soran bir sayfa çıktı. Burada tek önemli konu "Share This Printer"ı seçmek. Sürücü yüklenmişse sonraki sayfada Canon'un model listesi çıkıyor. Bu adım sürücüyü yüklemeden, bağlı yazıcının PPD dosyasını yükleyerek geçilebilir. Canon MF4450 yazıcı için uygun dosya CNRCUPSMF4400ZK.ppd. Bunu, sürücü paketindeki herhangi bir .deb veya .rpm dosyadan makinama açabilirim. Fedora'da bu

rpm2cpio cnrdrvcups-ufr2-uk-6.20-1.20.aarch64.rpm | cpio -idmv

komutuyla yapılabilir. PPD dosyalar yazıcının özelliklerini içeren tanım dosyalarıdır. Platform ve dağıtımdan bağımsızdırlar. Sürücü seçme sayfası aşağıdaki görseldekine çok benzer, fakat ben yazıcıyı eklemiş bulunduğum için "Modify Printer" sayfasının görselini ekledim.


Yazıcıyı ekledikten sonra artık üst frame'in sağında Printers altında listeleniyor. (bu aşamada yazıcıyı silip farklı bir adla tekrar eklemem gerekti, bu nedenle bundan sonraki ekran görüntülerinde yazıcı adı farklı oldu).


Bu yazıcıya tıkladığımda yazıcı ve kuyruk ayarlarının yönetilebildiği sayfa açılıyor (yanda). Burada doğru PPD dosyayı yükleyip yüklemediğime bakacağım. Aynı PPD dosyanın Avrupa ve Amerika sürümünde varsayılan kağıt büyüklükleri farklı (A4 ve Letter). Yanlışlıkla Amerika sürümünü yükleyince yazdırma işi yazıcıya Letter olarak gidiyor ve yazdırmada bir uyarı çıkıyor. Ancak bu ekran görüntüsünden de görüleceği üzere burada doğru ayarlar seçili. Eğer bu ayarlarda Letter seçiliyse, 'Administration' menüsünden 'Set Default Options'ı seçip

çıkan sayfadan kağıt büyüklüğü A4 olarak değiştirilmelidir.


Son olarak ben bu arayüzde Maintenance -> Print Test Page ile bir test çıktısı aldım.


Eğer bu çıktı sorunsuz geliyorsa yazıcı kurulmuştur. İlk denememde sürücüleri kurmadan sadece PPD dosyayı girdiğimde, bu adımda çıktı gelmiyordu ve 'Show All Jobs' butonuna bastığımda rastertoufr2 ile ilgili bir hata veriyordu. Bu hata çıkıyorsa sürücü yüklen(e)memiştir. Normalde bu dosyanın /usr/lib/cups/filter/rastertoufr2 yolunda olması gerekir. Bu adımda artık sürücüyü yüklemekten kaçış yok. Yalnız bu dosyanın çağırılması gerektiği PPD dosyada belirtiliyor. Belki PPD dosyada bu satır değiştirilirerek birşeyler yapılabilir.

Ve son olarak bir ayar daha kaldı: Debian, uzun süre kablosuz ağ kartında bir aktivite görmediği zaman kartı kapatıyor. Bunu engellemek için

nmcli connection mod <SSID> 802-11-wireless.powersave 2

komutunu girmek gerek. Böylece yazıcı yerel ağa açılmış oldu.


Yazıcıyı Windows ve Linux İstemcilere Tanıtmak

Yazıcıyı ağ yazıcısı olarak yerel ağdaki bilgisayarlara tanıtmak gayet kolay. Üstelik itiraf edeyim Windows'ta Fedora'dan daha kolay. Muhtemelen diğer Linux dağıtımlarında da benzer şekildedir ama elimde Fedora olduğundan ben buna yoğunlaşacağım.


Fedora'da yazıcı eklemek için System Settings -> Printers'ı açtım, yukarıdaki '+ Add'e bastım ve ağ yazıcısı kısa sürede bulundu. Burada (aşağıda) 'Select Recommended Driver'a bastığım zaman IPP Everywhere seçiliyor.


Bunu seçerek devam ettim ve sonraki adımda yazıcının nasıl ekleneceğine dair bir ayar ekranı geldi. Burada hiçbir şeyi değiştirmeden devam ettim. Queue Name'in yerel makinaya özgü ve eşsiz (unique) bir ad olması gerek. Description ve Location alanları isteğe göre değiştirilebilir.

Benim sistemimde başka bir yazıcı bulunmadığı için 'Default printer' seçeneğinin bir önemi yok. Bunlar tamamsa 'Add'e basılarak yazıcı eklenir ve yazıcı listesinde bu yazıcının listeleniyor olması gerekir.


Yazıcının üzerine tıklayınca gelen formda 'Print Test Page'e basarak yazıcı test edilebilir.

Windows'ta (v10 ve v11) ayarlardan yazıcıları seçince veya başlat menüsünde Printers yazınca gelen formda, 'Add a printer or scanner' yanındaki 'Add device' butonuna basınca yazıcı otomatik listelendi. Listelenen yazıcının yanındaki 'Add device' butonuna basıldığı zaman, sisteme otomatik ekleniyor ancak bu adım 1-2 dakika kadar sürebiliyor. Muhtemelen bu sırada sürücüler internetten indiriliyor.


22 Ocak 2026 Perşembe

MAX7219 Kullanarak Raspberry Pico Üzerinde Performans Karşılaştırması: C vs Micropython


Merhaba. Bu yazıda Raspberry Pi Pico mikrodenetleyicisiyle yaptığım bir kaç deneyden ve elde ettiğim ilginç sonuçlardan söz ettim. Uzunca bir yazı oldu, o yüzden sonuçları ortalarda ele aldım. Kodları nasıl çalıştırdığımı ek okuma olarak sona bıraktım.


Raspberry Pi Pico Nedir?

Raspberry Pi Pico (kısaca Pico), Raspberry Pi Vakfı'nın (Raspberry Pi Foundation) Arduino'ya her ne kadar doğrudan cevabı olmasa da, çok güçlü bir alternatif mikrodenetleyici. Arduino, giriş seviyesi hobi kullanıcıları hedef alıyor. Pico bence biraz daha teknik bilgi gerektiriyor. Ben bu yazıda Pico üzerinde farklı programlama yaklaşımlarını gösterip bunların hızını karşılaştırdım. Elimde önceki yazıdan hazır kod var: SPI ve MAX7219 ile LED sürme, bu yazıda da bu konudan devam ettim. Ama daha önce bunu yalnız Arduino üzerinde uygulamıştım, şimdiyse yalnız Pico üzerinde.

Arduino 8-bitlik ATmega328P (16 MHz) mikrodenetleyicisine sahip. Pico'daysa 32-bitlik çift çekirdekli ARM Cortex mikrodenetleyici var ve dökümante edilen maksimum çalışma hızı 133 MHz. İşlemci, varsayılan 125 Mhz ile çalışmaya başlıyor. Saat hızı sonradan programla değiştirilebiliyor. Örn. pille beslenen cihazlarda pili idareli kullanmak için saat hızı yavaşlatılabiliyor. Sonradan dökümante edildi mi bilmiyorum ama sahada Pico'nun 200 MHz ile çalıştırıldığını okudum. Ben kendi kodumu paralelleştirmeye uğraşmadığımdan çift çekirdeği dikkate almıyorum. Pico'da RAM ve flash bellek miktarı Arduino'ya göre daha fazla ama benim uygulamam CPU'yu yoğun olarak kullandığından bunları da burada karşılaştırmayacağım.

Pico'ya yeni başlayanlar için "Get Started with MicroPython on Raspberry Pi Pico" adlı açık ekitap var. Son baktığımda bu sayfadaki bağlantı düzgün çalışmıyordu ama kitabın adını Google'da aratınca .pdf sürümü çıkıyor.

Pico için kod geliştirirken kullanılabilecek çeşitli alternatifler var. İlki Arduino IDE ve bunun güzel yanı Arduino'da çalışan C kodu Pico'da da aynı şekilde çalışıyor. pinMode() veya digitalWrite() gibi komutlar tamamen uyumlu. Eğer çok Arduino-native bir kod değilse Pico'da da çalışacaktır.

İkinci alternatif Micropython. Pico'yu (micro)python'la da programlamak mümkün. Örneğin aşağıdaki satır onüçüncü GPIO pinini çıkış olarak ayarlayıp set ediyor:

spi_CS = machine.Pin(13, machine.Pin.OUT, 1);

Micropython'da SPI protokolü için kullanılacak pinlerle bir SPI nesnesi oluşturup bunun write() metodunu çağırmak yeterli. Micropython basit, ama biraz yavaş. Pico için yazılmış 660 KB boyutunda bir Micropython yorumlayıcısı var. Pico'yu boot select modunda çalıştırıp bu dosyayı kopyalayınca (boot select modunda Pico bilgisayarda bir USB flash bellek gibi görünüyor) seri arabirimi (RS232) USB üzerinden emüle ederek bilgisayarla haberleşiyor. Bu yolla python çalıştırmak mümkün.

Üçüncü alternatif C/C++. Pico'nun geniş bir C desteği ve kütüphaneleri var. Platforma özgü çok fazla fonksiyon olması (get_absolute_time(), cyw43_arch_gpio_put(), stdio_init_all() vb.) en başta benim için kafa karıştırıcı oldu. Ancak her konu için Raspberry'nin resmi github hesabında sayısız örnek var. C için en iyi kaynak Getting started with Raspberry Pi Pico-series adlı kitap. Pico SDK'nın kullanım kılavuzu da oldukça detaylı ama bu okunacak bir kitap değil, 745 sayfa. SDK'yı ve örnekleri derlerken aşağıdaki videodan yararlandım.


Elbette Assembly de bir seçenek ama ben ne Arduino'da ne de Pico'da assembly yazmadım. C kodları içinden assembly çağırmak da bir olasılık.


Nasıl Test Ettim?

Bütün testler önceki yazıda kütüphane kullanmadan yazdığım sayaç kodunu temel alıyor. Elbette Micropython için algoritmayı python'a çevirdim. Sayaç kodu Arduino IDE'den çalıştırılınca hem Arduino'da hem de Pico'da sorunsuz ve aynı şekilde çalışıyor. İlginç şekilde bu kodun kütüphane kullanan sürümü Pico'da çalışmadı. IDE, farklı platformlar için header dosyalarını muhtemelen farklı dizinlerde arıyor. Kodun kütüphanesiz sürümü sorunsuz çalıştığından bunu önemsemedim.

Aslında kafamdaki plan a dizisinin (satır 67) ilk elemanında gerçekleşen her taşmada bir pini set/reset yapıp frekansmetreyle taşmaların periyodunu bulmaktı. Bu plan frekansmetremin düzgün çalışmaması nedeniyle en başta bozuldu. Pini osiloskopa bağlayıp test ettim, bu sefer de örnekleme hızından dolayı bazı atımları kaçırdı. Set ile reset arasına ufak bir gecikme eklediğimde sorunsuz ölçüm yapabildim. Yukarıdaki görselde her bir atımın çıkan ve inen kenarı arasında 10 ms gecikme görülüyor (ölçek sağ üstte). Burada atımların frekansı 2.182 Hz. Bu periyoda çevrildiğinde 458 ms ediyor. Bunun 10ms'si gecikme olduğu için her 448 ms.'de sayaç taşıyor.

Önceki yazıda onuncu LED'in yaklaşık bir saniye yanık kaldığını gözlemlemiş ve hesabı buna istinaden yapmıştım. Sekizinci LED'in periyodu 448 ms. ise dokuzuncu LED'in periyodu 892 ms. olur, dolayısıyla onuncu LED 892 ms boyunca yanık kalır. Gözleme dayalı kaba bir tahmin için fena değil.

Ancak süreye dayalı bir ölçüm yaparken araya gecikme eklemek mantıklı olmadığı için, paralelde a dizisinin ikinci elemanındaki iki taşma arasında geçen süreyi seri porttan yazdırdım. Bu süre ortalama 114.486 saniye. Birinci dizi elemanı 892 milisaniye aralıklarla 256 kere taşarsa (overflow) 114.176 saniye yapıyor. Hesapla deney arasında 310 ms'lik bir fark var. Bu da Arduino'nun osilatörünün yeterince hassas olmadığı için ölçümleri yaptığım günler arasında odadaki sıcaklık farkından kaynaklanıyor olabilir, veya seri port iletişiminin getirdiği yük (overhead) olabilir, ki bu daha olası. Arduino Serial.println()'yi asenkron olarak yapamıyor, üstelik seçtiğim baud hızı da yavaş. Diğer yandan ‰2.7 kabul edilebilir bir hata. Kısacası süreleri seri porttan okumaya karar verdim.

Seri portun başlatılması için sekizinci satırdan #ifdef'i kaldırdım. Yeni kod github'dan ulaşılabilir. Süre ölçümü için millis() fonksiyonunu kullandım (satır 69). Eski koda ek olarak sekseninci satırda bir if bloğu var. Burada StartTime1'in değeri güncel tick count'tan çıkarılıp aradan geçen zaman seri porta yazılıyor.

Bu arada Arduino IDE'de ilginç bir durum var. Nedense LED Matrix'e yazılan sayının paritesine bağlı olarak döngü süresi değişiyor. Üstelik değişim Arduino'da pozitif, Pico'da negatif yönlü. Aşağıdaki grafik Arduino'nun sürelerini gösteriyor.

Örneğin, ortadaki pik 127'de ortaya çıkıyor, 128'de birden süre aşağıya düşüyor. O pikle sıfır noktasının tam ortasında daha küçük bir sıçrama 63 ile 64 arasında, ve benzeri bir sıçrama 191 ile 192 arasında var. En büyük sıçramaysa 255 ile 256 arasında, sağda. Diğer küçük sıçramaların 2n deseninde olduğu görülebilir. Nedeni konusunda bir fikrim yok. Pico'nun grafiğine bakarsak:

Bu grafik sanki öncekinin simetriği gibi, sayıdaki birlerin adedi arttıkça süre azalıyor. İlk grafiktekine benzer sıçramalar ters yönde 64, 128, 192 ve 256 civarında görülebilir. Neden Pico'da tersi davranış gösterdiği konusunda yine bir fikrim yok.

Python kodunu, C'den birebir çevirdiğimi yazmıştım. spiwrite() MAX7219'a iki byte'lık komutlar gönderiyor. SPI için kullanacağım pinleri initialize ettikten sonra, bu pinlerle SPI metodununu çağırarak bir nesne oluşturdum (satır 19). Ardından Arduino'dakiyle aynı şekilde MAX7219'u initialize ettim. Sonsuz döngüye girmeden önce süreyi StartTime1'e yazdım ve Z[1]'deki her taşmada bilgisayara print() ile süreyi ilettim.

C kodunu büyük ölçüde pico-examples/spi/max7219_32x8_spi/max7219_32x8_spi.c kodundan sadeleştirerek aldım. Örnek kod, SPI pinleri için "hardware/spi.h"daki varsayılan tanımları (PICO_DEFAULT_SPI_SCK_PIN, PICO_DEFAULT_SPI_CSN_PIN vb.) kullanıyor. Ben kendi tanımladığım pinleri kullandım (satır 12, 13, 14). MAX7219 komut tanımlamalarını, cs_select() ve write_register() fonksiyonlarını orjinal koddan aldım. Bu kodda StartTime1 (satır 48) değişkenine süre get_absolute_time() ile main()'in başında yazılıyor. Bunun aslında sonsuz döngüden hemen önce olması gerekirdi ancak sonuçlarda ciddi bir fark yaratmıyor. Burada dikkat edilmesi gereken bir konu, Pico'da denetleyicinin içinde iki tane SPI çevre donanımı (peripheral) var. Bu da demek oluyor ki (bit-banging ve Programmable IO gibi ileri konulara girmeden) aynı anda iki SPI bus bağlanabilir. Bu SPI donanımları birden fazla GPIO pinine bağlı. Demek istediğim Pico'nun pinout şemasından daha iyi görülebilir:


SPI0 devresini yalnız SPI0 etiketli pinlerle kullanabilirim. Örn. GP0, GP1, GP2 ve GP3 (Pin 1, 2, 4, 5). Eğer bir SPI cihazı SPI1'e bağlayacaksam, bunu da benim kodumda olduğu gibi GP10, GP11, GP12 ve GP13 (Pin 14, 15, 16, 17) üzerinden bağlayabilirim. Ve SPI'ı initialize ederken hangi donanımı kullandığımı bildirmem gerekir. Orjinal kodda spi_default, spi0. Ben spi1'i kullandığımdan elli dördüncü satırda spi_init()'i spi1 ile çağırmam gerekti. Sırf SPI için bile bunca detay bulunması, C'nin donanıma yakın olmasının bir sonucu ama bu durum hız olarak kullanıcıya geri dönüyor.

MAX7219 initialization kısmı kodun 64 ve 70. satırları arasında. Ondan sonraki blok zaten Arduino'da da çalışan kod bloğu. Tek fark, sayının değeri SPI'a write_register() fonksiyonu üzerinden yazılıyor.


Test Sonuçları

Kodları nasıl çalıştırdığıma, yazının asıl konusu olmadığı için, sonuçlardan sonra ayrıca değineceğim.

İlk olarak Arduino'dan elde ettiğim sonuçları, verim ve saat hızına temel olması için ele alacağım. Arduino'da gerçekleşen 282 taşma ortalama 114.486 sn sürmüş. Yani 216'ya kadar saymanın süresi ve diğer bir ifadeyle on yedinci LED'in periyodu. 64 tane LED olduğuna göre daha 64 - 17 = 47 bit var. Bunların tamamının yanması için gereken süre 114.486 sn * 247 = 1.611 * 1016 saniye = 5.106 * 108 yıl (511 milyon). Önceki yazıda göz kararı hesapladığım 5.709 * 108 yıldı.

Bu, satranç tahtasındaki buğday taneleri problemine benziyor. Hikayeye göre satrancın mucidi, icadını Hint hükümdarına sunar. Hükümdar bunu çok beğenir ve klasik olarak "dile benden ne dilersen" der. Mucit, tahtanın birinci karesine bir buğday tanesi ve takip eden karelere öncekinin iki katı olmak üzere tahtaya koyulacak miktarda buğday ister. Hükümdar başta basit bir istek gibi algılasa da, mucidin istediği buğday tam olarak

Bu kadar buğday tanesi günümüz koşullarındaki yıllık buğday üretiminin 1600 katına denk. Bu arada hikayenin çeşitli varyasyonları var, ben lise matematik hocamdan duyduğumu aklımda kaldığı kadarıyla aktardım. Ana fikir üstel sayıların akıl almaz şekilde büyüyebileceği. İlk LED'in periyodu 1.75 ms (aslında mikroişlemci için uzun bir süre) kısa gibi gelse de bunun 264 katı inanılmaz büyük.

Konuya geri döneyim. Aynı kodun Arduino IDE içinden Pico'da çalışması ortalama 14.658 saniye sürüyor. Pico, Arduino'dan 7.8125 kat hızlı (125 MHz / 16 MHz). 14.658 sn * 7.8125 = 114.516 sn ediyor. Saat hızına göre normalleştirildiğinde Arduino ile aralarında sadece 30 ms fark var. Demek ki verim olarak ikisi de neredeyse aynı. Tüm LED'lerin yanması için 14.658 sn * 247 = 2.063 * 1015 sn = 6.537 * 107 (65.3 milyon) yıl gerekiyor.

Aynı algoritma Micropython'la hepsinden yavaş çalışıyor. Bir çevrim ortalama 128.641 saniye. Arduino IDE'deki koddan 8.78 kat daha yavaş. Bu, Micropython'ın mikrodenetleyici gibi düşük seviye bir platformda çok verimsiz olduğunu gösteriyor. Ben bu sonuçları SPI hızını 1 MHz olarak ayarladıktan sonra aldım. İlk denemede yanlışlıkla hızı 400 KHz ayarladığımda aynı döngü ortalama 138.291 saniye sürmüştü, ve ertesi gün 142.800 saniye. Baud'un genel hıza etkisi var ama yavaşlık kesinlikle program kaynaklı. Aksi halde baud'u 2.5 katına çıkarmak %7'den fazla etkili olurdu. Bu sonuçların ne zaman 264'e varacağını hesaplama zahmetine girmeye gerek yok.

Benim açımdan en heyecan verici olan C'deki sürelerdi. C'de 65 536'ya kadar 1.656 saniyede sayabiliyor. Yani, Arduino IDE'den bile 8.85 kat hızlı çalışan bir kod üretiliyor. Bu arada çıktı için UART veyea USB seri port kullanmak arasında süre farkı yok. 1.656 sn * 247 = 2.331 * 1014 sn = 7.385 * 106 (7.385 milyon) yıl. Bu da Otostopçunun Galaksi Rehberi'ne göre yaşamın, evrenin ve her şeyin cevabı olarak 42'yi hesaplamak için süper bilgisayarın ihtiyaç duyduğu süreden sadece biraz az.


Ek: Raspberry Pico'da Kod Çalıştırmak

Yazıda bahsettiğim üç farklı yaklaşımın (Arduino IDE, Micropython, C) ortak noktası Pico'nun Bootsel butonu. Bu butona basılı tutarken Pico'yu USB'ye bağlayınca Bootloader modunda açılıyor ve bilgisayarda 128 MB'lik bir USB flash bellek gibi görünüyor. Bu diske Raspberry Pico için derlenmiş .uf2 uzantılı bir dosya kopyalayınca, Pico otomatik olarak bunu tanıyıp çalıştırıyor. UF2, Pico'ya özgü çalıştırılabilir dosya formatı. Çapraz derleyici çıktı olarak .uf2 dosyası üretiyor. Bu dosya bizim yazdığımız C kodu veya Micropython yorumlayıcısı olabilir.

Arduino IDE'de çalışmak için, Bootsel'e basılı tutarken Pico USB'ye bağlanmalı. Sonra IDE'de yukarıdaki drop-down menüden "Select other board and port..."u seçip, çıkan pencerede Boards altında Pico diye aratınca çıkanlardan "Deprecated" olmayan board'u seçmek gerekiyor. Pico bağlandığında /dev/ttyACM0 aygıtını oluşturuyor, port olarak bu seçilmeli. Bunu yaptıktan sonra IDE, Pico çapraz derleyicisini ve kütüphanelerini indireyim mi diye soruyor. Buna evet diyip kurulumu tamamladıktan sonra, Pico Blink'le test edilebilir (File -> Examples -> 01. Basics -> Blink). Bu çalışıyorsa Pico diğer sketch'leri çalıştırmaya hazır demektir.

Micropython ile çalışmak için ilkin Micropython yorumlayıcısına ihtiyaç var. Bu daha önce belirttiğim gibi .uf2 uzantılı bir dosya. Dosyanın bağlantısı Raspberry'nin resmi sayfasında var. Pico bootloader modundayken çıkan .htm dosyanın bu yorumlayıcının bulunduğu sayfaya yönlendirdiği ve .txt dosyada da model bilgisinin yer aldığı yazıyor ama bende düzgün çalışmadı. Doğru .uf2 dosyasını Pico'ya kopyalayınca disk ortadan kayboluyor, yanlış .uf2 dosyasını kopyalayınca hiç birşey olmuyor.

İkinci olarak bir python IDE gerekli. Genellikle, Linux ve Windows'ta Thonny IDE öneriliyor. Bu yazıyı hazırlarken paket yöneticisinin resmi reposunda 4.1.6 sürümü vardı ancak Thonny'i Linux'ta çalıştırmak çok basit değil. Ben önce paket yöneticisiyle kurdum, fakat aşağıdaki hatayı aldım:

RuntimeError: There is no current event loop in thread 'MainThread'.


Bu bir izin (permission) kaynaklı değil, çünkü daha sonra github'dan .tar.gz dosyasını indirip açtım ve çalıştırdığımda hiç bir hata almadım ve daha da garibi paket yöneticisiyle kurduğum thonny'i tekrar çalıştırdığımda aynı hatayı almama rağmen sorunsuz çalıştı. Bu arada .tar.gz'den çıkan dosyayla paket yöneticisinden gelen dosya binary olarak farklı. Muhtemelen derleme parametreleriyle ilgi bir durum veya paket yöneticisi bazı config dosyalarını düzgün oluşturamıyor.

Eğer Pico, Micropython yorumlayıcısını çalıştırmaya başlamışsa, thonny'nin sağ alt köşesinden doğru board'u ve portu seçince ekrana gelen python prompt'u seri port üzerinden Pico'da çalışan python'dan geliyor. Yani buraya bir komut yazarak doğrudan Pico üzerinde çalıştırılabilir. Komutun çıktısı yine USB üzerinden thonny'e gelecektir. Her ne kadar bir ekran bağlı olmasa da Pico'daki stdout seri port olduğu için, print komutu seri porta yazıyor ve bu thonny'le görülebiliyor. Yukarıda "doğru portu board'u seçince" yazdım, çünkü thonny aynı zamanda bilgisayardaki python yorumlayıcısını da kullanabiliyor. Eğer bu seçiliyse aynı çıktı yerel python'dan gelecektir. Üst tarafta python betiği yazılıp çalıştırılabiliyor ve kaydedilebiliyor. Bir dosya açarken veya kaydederken, thonny kullanıcıya bilgisayarı mı yoksa Pico'yu mu kullanacağını soruyor. Yani board'a dosya da kaydedilebiliyor. Üstelik eğer dosya main.py olarak adlandırılırsa, board'a elektrik verilir verilmez bu dosya Micropython tarafından otomatik çalıştırılıyor. Yazılan betik IDE'deki yeşil play tuşu ile seçilen platformda çalıştırılıp kırmızı stop tuşu ile durdurulabiliyor. thonny herhangi bir örnek içermediğinden basit bir blink kodunu aşağıya ekledim:

from machine import Pin
import time

led = Pin(25, Pin.OUT)

while True:
    led.on()
    time.sleep(0.5)
    led.off()
    time.sleep(0.5)

Bu kadar thonny anlattıktan sonra şunu da ekleyeyim ki, aslında Pico'da python kodu çalıştırmak için thonny'e gerek yok. İletişimin RS232 protokolüyle gerçekleştiğini yazmıştım. Dolayısıyla minicom'da (veya Windows'ta hala var mı bilmiyorum ama Hyperterminal veya putty'de) sudo minicom -D /dev/ttyACM0 -b 115200 komutuyla bir oturum açıp, Pico üzerindeki python yorumlayıcısına ulaşmak mümkün. Yanılmıyorsam kodları kaydetmek ve kayıtlı dosyaları çalıştırmak için de klavye kısayolları mevcut ancak tüm bunları akılda tutmamak için thonny ile çalışmayı tercih ediyorum. thonny için oldukça detaylı bir videoyu buraya bırakayım:



Son olarak C/C++ kodlarını çalıştırmak aslında hepsinden daha kolay. Çapraz derleyicinin çıktısı olan .uf2 dosyayı Pico'ya atınca çalışıyor. Zor kısım SDK'nın ön koşullarını yükleyip derlemek. Ben bunun için

  1. arm-none-eabi-gcc-cs.x86_64
  2. arm-none-eabi-gcc-cs-c++.x86_64
  3. arm-none-eabi-newlib.noarch
  4. arm-image-installer.noarch

paketlerini yükledim. Ancak ben yıllardır kendi makinamda türlü çeşit paket derlediğim için zaten bir sürü devel paketi yüklü. Dolayısıyla yukarıdaki dört paket minimal gereklilik. Örn. cmake de gerekli ama bu benim bilgisayarımda zaten vardı.

Pico SDK'yı ve Pico Examples'ı github'dan klonladım. PICO_SDK_PATH çevre değişkenine SDK'yı indirdiğim yolu girip pico-examples dizininde cmake -S . && make -j4 dedim. Küçük küçük bir sürü örnek olduğu için paralel make derlemeyi çok hızlandırıyor. Bu arada bir build/ dizini oluşturup derlenen dosyaların oraya yazılması tavsiye ediliyor. Ben biraz üstünkörü derledim. Derleme bittikten sonra her örneğe karşılık bir .uf2 dosyası üretiliyor. Bunu denemek için blink/blink.uf2 Pico'ya kopyalanabilir.