3.4- MicroPython’da MAX7219 ile 7 Segman Gösterge Uygulaması
Kitabın ikinci bölümünde 7-segman gösterge ile bir uygulama yapmıştık. Uygulamada hatırlayacağınız üzere 7-segman göstergenin her bir LED’ini tek tek GPIO ayaklarıyla biz kontrol etmekteydik. Bunun dışında her bir sayıyı temsil için her bir ayağı uygun şekilde kodlamamız gerekliydi. 7-segman göstergeleri doğrudan sürmek biraz zahmetli olduğu için bu yöntem yerine uzun yıllardır sürücü entegreler kullanılmaktadır. Eski sürücüler daha çok paralel olarak karakterin değerini ikilik olarak (BCD) alıp bunu göstergede göstermekteydi. Daha gelişmiş sürücüler ise seri iletişim protokolünü destekler ve fazlaca özelliği içinde barındırır. MAX7219 entegresi Maxim firmasının üretmiş olduğu gelişmiş sürücülerden biridir.
MAX7219’u MicroPython ile kullanmak için öncelikle önceki uygulamada olduğu gibi bir sürücü yazmamız gereklidir. Sürücüyü yazmadan önce kullanacağımız donanımın özelliklerini öğrenmek için datasheetini incelememiz gerekir.
https://www.analog.com/media/en/technical-documentation/data-sheets/MAX7219-MAX7221.pdf
Pek çok yaygın kullanılan modül için GitHub’da MicroPython kütüphaneleri yer alsa da bir geliştirici olarak kendi kütüphanemizi yazabilmeli, hazır kütüphane kullansak bile gerektiğinde düzenlemeleri yapabilecek kadar kütüphane yazmayı bilmemiz gereklidir.
Datasheeti açtığımızda ilk sayfada modül ile ilgili özet bilgiler yer almaktadır. Bu modül içerisinde bir yazmaç listesi bulundurmakta ve adres-veri şeklindeki veri formatına göre kullanılmaktadır. Yani öncelikle yazmak istediğimiz değerin adresini belirteceğiz ve sonrasında ise değeri yazacağız. Pek çok modül bu şekilde kullanılmaktadır. Veri yazacağımız yazmaçların listesini ve hangi yazmaçlar olduğunu “Register Address Map” tablosunda görebilmekteyiz.
Buradaki yazmaçların işlevleri datasheette ayrı başlıklar altında açıklanmıştır. Bunları kısaca şöyle özetleyebiliriz.
· No-Op: Bu komut aynı hat üzerinde birden fazla MAX7219 modülü kullanmak istediğimizde kullanılır. Örneğin ilk modülde yer alan DOUT ayağı ikinci modülün DIN ayağına bağlanır ve ikinci modüle veri göndermek istediğimizde önce No-Op komutu yazdırılır ve sonrasında veri yazdırılır. Bu şekilde pek çok modülü sıra ile bağlamak mümkündür.
· Digit 0–7: MAX7219 toplamda 8 haneye kadar sayı gösterebilir. Digit 0 kullandığımız modülde en sağdaki haneye karşılık gelirken Digit 7 en soldaki haneye karşılık gelmektedir.
· Decode Mode: Decode mode karakterleri kodlamak için kullanılmaktadır. Kodlama devre dışı bırakılırsa her bir segmanı kendimiz kontrol edebiliriz. Kodlama etkin iken BCD değerine göre sürücü hangi segmanların yakılıp söndürüleceğini belirlemektedir. Eğer dotmatrix göstergelerle çalışacaksanız bu modun devre dışı bırakılması gereklidir.
· Intensity: Göstergelerdeki LED’lerin parlaklığını belirler. 0–32 arası değer alabilir.
· Scan Limit: Kaç hanenin gösterileceğini belirler.
· Shutdown: Modülü açıp kapamak için kullanılır.
· Display Test: Görüntü test moduna giriş yapar.
Datasheette bizim için en önemli olan kısmı yukarıda gördük. Belgeyi baştan aşağıya incelediğimizde ise datasheette şu bilgilerin yer aldığını görürüz.
· Özet bilgi
· Elektrik karakteristikleri (Besleme gerilimi, akım değerleri, azami değerler, tarama frekansı vd.)
· Çalışma karakteristikleri (Slew rate, gerilim/akım ve gerilim/frekans grafikleri)
· Ayak açıklamaları
· İşlevsel diyagram (Entegrenin şemasının kabaca gösterimi)
· Seri veri formatı ve zamanlama
· Yazmaç açıklamaları
· Örnek devreler
· Paket ve sipariş bilgileri
Datasheetteki bilgiler bu modülü kullanacak firmaların farklı departmanları için farklı bilgileri bir arada vermektedir. Örneğin bir tarafta sipariş kodu, öteki kısımda donanım tasarımı için teknik çizimler, bir başka kısımda ise geliştiriciler için bilgiler yer almaktadır. İşin aslında datasheet sadece “Nasıl programlayacağız?” sorusuna cevap vermemektedir.
Burada fark ettiğiniz üzere programlamak için kullanılan seri iletişim protokolü ve yazmaçlardan bahsedilmiş ama programlama kısmında bir ipucu verilmemiştir. Bazı firmalar datasheetlerinde akış diyagramı (flow chart) olarak bunu vermekle beraber bazı firmalar yazı ile kısaca tarif etmektedir. Bazı firmalar ise bazı donanımlar için örnek kod veya kütüphane verebilmektedir. Bunların hepsi istisna olarak sayılıp datasheetlerde bu tarz programlama ile ilgili yardımcı bilgileri beklememek gereklidir.
Üretici firmalar sizin kullanacağınız onlarca farklı donanım olduğundan bunu tahmin etmeleri zor olduğu gibi sizin bir geliştirici olup buradaki yer alan bilgileri okuyarak kendi programınızı ve kütüphanenizi yazabileceğinizi beklerler. Yani onların size geliştiriciliği öğretme gibi bir zorunlulukları yoktur, zaten bilen bir geliştiriciye ihtiyacı olan bilgiyi vermekteler. Hatta bazen bilen biri için bile çözmesi zorlayıcı şekilde anlatıma sahip olan datasheetler bulunmaktadır.
Datasheet okumanın ve uygulama geliştirmenin birkaç püf noktasından bahsedebiliriz,
· Öncelikle büyük resmi görmek gereklidir. Bunun için örnekte olduğu gibi “Functional Diagram” ve yazmaç açıklamalarını iyi okuyup önemli görülen kısımları işaretlemek veya not almak gerekli.
· Datasheeti baştan sona okuyup sonrasında kendiniz için gerekli kısımları tespit etmelisiniz.
· Bazen önemli ayrıntılar bir cümle arasında sarf edilmiş olabilir. Bunları kaçırmamaya çalışın.
· Doğrudan programlamaya geçmek yerine öncelikle ne yapacağınız noktasında fikir sahibi olun, karanlık nokta bırakmamaya çalışın.
· Teorik bilgi eksikliğiniz varsa bilmediğiniz konularda bunu gözden geçirmeyi unutmayın. Bazen firmaların kendi geliştirdiği ve adlandırdığı özellikler olabilir, bunların karşılıklarını bulmayı hedefleyin. Açıklamalardaki ipuçlarını kaçırmayın.
· İlk olarak kabaca akış diyagramı oluşturun ve sonrasında yalancı kod (pseudocode) ile datasheet okurken program örneklerinizi bir kâğıda aktarın.
· Yazmaçlara yazılması gereken değerleri, bit adreslerini belirleyin ve ilk kütüphanenizi mümkün mertebe alt seviye işlemler kullanarak geliştirin. Sonrasında bu alt seviye fonksiyonları kullanarak üst seviye işlemleri gerçekleştirin.
Kısacası bir donanıma sıfırdan kod yazmak özen ve dikkat gerektiren bir iştir. Kütüphane referanslarına bakar gibi rastgele bir datasheet açıp okumadan birkaç noktaya bakarak hızlıca program yazmaya çalışmak çoğu zaman hüsran ile sonuçlanmaktadır. Bazen 1–2 satır kod yazmak için 30–40 sayfa datasheet okuduğumuz zamanlar olmaktadır. Burada C yerine Python kullanmamız kütüphane yazma kısmında ve mikrodenetleyici fonksiyonlarında işimizi kolaylaştırsa da MicroPython’ın sınırları dışında kalan noktada (örneğin MAX7219’u kullanmak) yine aynı yöntemleri uygulamamız gereklidir.
Şimdi açıklama kısmından MAX7219’u nasıl programlayacağımız noktasında ipuçlarına bakalım.
7. sayfada “Initial Power-Up” başlığı altında MAX7219’a güç verildikten sonra bütün kontrol yazmaçlarının sıfırlandığını, ekranın silindiğini ve MAX7219’un kapanma (shutdown) moduna girdiğini yazmaktadır.
Bu durumda “Shutdown” yazmacının tablosuna bakarak sürücüyü açmak için 0x0C adresindeki yazmacı 0x01 yapmamız gerektiğini anlıyoruz.
Sonrasında diğer ayarlara bakalım. Örneğin “Scan Limit” bizim kaç karakter kullanacağımız belirlemekteydi. Biz 8 haneli bir gösterge kullanacağımız için bunu 8’e ayarlamamız gereklidir. 0x0B adresindeki Scan-Limit yazmacını tabloya bakarak (sf. 9) 0x07 değerine ayarlamamız gerektiğini görüyoruz.
Bir diğer önemli konu ise “Decode mode” adı kod çözme modudur. Yazacağımız bilgilerin doğru gösterilmesi adına bunu doğru ayarlamamız gereklidir. Biz verileri BCD değerinde yazdırıp sürücünün hangi değerde hangi segmanı yakacağını karar vermesini istiyoruz. Bu durumda “Code B decode for digits 7–0” seçeneğini (sf. 7) seçmemiz gereklidir. Yani 0x09 adresindeki yazmacın değerini 0xFF yapmamız gerekli.
Son olarak “Intensity” değerini belirlememiz gerekli. Bunu sınıf içinde bir metot olarak tanımlayıp parlaklığı kullanıcının istediği gibi ayarlamasını sağlayabiliriz. Başlangıçta ortalama ir değer olarak 0x0A adresine 0x07 değerini yazacağız. Görev döngüsü 0–32 arasında olsa da bunun çözünürlüğü 4 bittir (0–16).
“Display Test” modu ise ekranda arızalı bir parça olup olmadığını görmemiz için faydalı olacaktır. Bu mod ekrandaki bütün parçaları yakmaktadır. Bunu 0x0F adresindeki yazmaca 0x01 değerini yazarak kullanabiliriz. Test modundan çıkmak için aynı yazmaca 0x00 değeri yazılmalıdır.
Son olarak “Digit” adresleri ise 0 en sağda olmak üzere göstergede yer alan haneleri temsil etmektedir. Biz BCD decode olarak belirlediğimiz için bunlara sayılara karşılık gelen BCD değerlerini (Örneğin 1 yazdırmak için 0x01) yazdıracağız. İstediğimiz zaman 8 parçayı (nokta dahil) 8-bit olarak istediğimiz gibi değiştirebilirdik. Bu bizim amacımız dışında olduğu için uygulamada tercih edilmemiştir.
Datasheetten elde ettiğimiz bilgiye göre artık nasıl programlayacağımız noktasında kafamızda karanlık bir nokta kalmadı. MAX7219’a SPI üzerinden veri yazmak için şu adımları takip etmemiz gerekir.
· CS ayağını LOW yap.
· Adres verisini yaz
· Veriyi yaz
· CS ayağını HIGH yap.
Bu bizim en alt seviye fonksiyonumuz olacak. Sonrasında ise önceki kütüphanede olduğu gibi ilk ayarları yapmamız gereken bir init() metodu yazmamız gerekli. Burada yapmamız gerekenler şu şekilde olabilir.
· 0x09 adresindeki değeri 0xFF yap. (Kod çözümleme bütün haneler için etkin.)
· 0x0B adresindeki değeri 0x07 yap. (Bütün haneler kullanılsın.)
· 0x0A adresindeki değeri 0x07 yap. (Parlaklık orta olsun.)
· 0x0C adresindeki değeri 0x01 yap. (Kapanma modundan çıksın ve gösterge çalışmaya başlasın.)
Bundan sonra bizim sayı değerini yazdırmamız için Python’da int tipindeki değeri BCD formata dönüştürüp sırayla hanelere yazdırmamız gereklidir. Eğer bu konuda bilgimiz yoksa öncelikle BCD’nin ne olduğuna dair bir araştırma yapmalıyız.
Dijital entegreleri doğru anlayıp kullanabilmek için dijital elektronik konularını ve terimlerini iyi bilmek gereklidir.
Uygulamada kullanacağımız modül MAX7219 ve gerekli 7-segman göstergeyi üzerinde bulunduran hazır bir modüldür. Bu yüzden devremizi kolayca kurabiliriz. Elektronik malzeme satan sitelerde MAX7219 diye arama yaptığınızda pek çok yerde karşınıza çıkacaktır. Uygulamada kullanacağımız devrenin şeması şu şekildedir.
Ülkemiz pazarında elektronik malzeme temininde belki aradığınız malzemelerin çoğunu bulacağınız birkaç mağaza mevcut olsa da genellikle aradığımız ürünleri bulmak için pek çok farklı mağazayı gezmemiz gerekebilir. Özellikle ar-ge işlerinde daha spesifik malzemelere ihtiyaç duyduğumuzda bunları Türkiye pazarında bulmamız pek mümkün olmamaktadır. Bu durumda alternatif olarak güvenilir olmasa da Çin sitelerini tercih edebiliriz. Genelde de bize gerekli olan malzemeleri Avrupalı tedarikçilerden sipariş vermemiz veya buralardan sipariş üzerine tedarik eden yerli firmalarla çalışmamız gerekebilir.
Şimdi yazdığımız kütüphaneyi inceleyelim.
__init__() metodu içerisinde bizim kullanacağımız ayakları belirlememiz gerekti. Biz SPI birimini kullanacağımız için MOSI, MISO, SCK ve CS ayaklarını argüman olarak aldık. Sonrasında bir SPI nesnesi oluşturduktan sonra SPI’ı kullanıma hazır hale getirdik. Bundan sonra ise ilk ayar değerlerini yazdırmamız gereklidir. Bunun için bütün fonksiyonların kullanabileceği __yaz() adında özel bir metot oluşturduk. Buna programcının erişmemesi istenmeyen değişikliklerin önüne geçecektir. Python dilinde “private” metotlar isimlerinin başında çift alt tire (__) ile belirtilmektedir. __yaz() metodunda yaptığımız CS ayağını LOW’a çektikten sonra adres ve veriyi yazmak ve sonrasında CS ayağını HIGH’a çıkarmaktır. Bu sayede tek bir yazma işlemi gerçekleşmiş olur. __init__() metodunda __yaz() metodunu kullanarak yaptığımız yazdırma işlemelerinin değer sabitleri olduğunu görebilirsiniz. Bu değer sabitleri kütüphanemizi kullanan programcı için çok bir anlam ifade etmeyecektir. Çünkü bu değerleri biz datasheet okuyarak elde ettik. Doğru bir kütüphane yazdığımızın işareti bu kütüphaneyi kullanacak programcının datasheet okumaya gerek duymamasıdır.
Dikkat ederseniz biz datasheet okurken MAX7219’un iç devre yapısı hakkında bilgi edinmemize ve buna müdahale etmeye gerek duymadık. Entegreyi tasarlayanlar seri iletişim protokolü ile yazmaçların değerini değiştirerek daha derine inmeden kullanmamıza imkân tanıdı. Devrenin çalışmasında bizim için gerekli olmayan ayrıntıları öğrenmek zorunda kalmadık. Biz devrenin içindeki kombinasyonel mantığı iç devreyi bilmeden yazmaçların bitlerine müdahale ederek kontrol edebiliyoruz. Burada bir soyutlama olduğunu görebilirsiniz. Biz de burada yazmaçlar ve metotlar arası bir köprü kurarak bir soyutlama işlemi yapmaktayız.
Sürücü entegreyi kontrol etmenin dışında yazılım tarafında kullandığımız platforma uyumlu hale getirmek adına int_yaz() metodu geliştirilmiştir. Bu metot Python’daki int tipindeki değeri BCD formatına çevirip kullanılan donanımın kısıtlarına uygun şekilde sınırlandırıp yazdırmaktadır. Bizim burada asıl amacımız sadece entegreyi kontrol etmek veya alt seviyede kullanmak değil bir int değerini doğru bir şekilde yazdırmaktır. Bunun için son basamağın BCD kodunu mod 10 işlemiyle elde ettikten sonra bir basamak sağa kaydırmak için 10’a bölmekteyiz. Bu sayede sırayla BCD kodları modüle gönderilecektir.
GitHub’da arama yaptığınızda zaten önceden birinin MicroPython için MAX7219 modülü yazdığını görebilirsiniz. Bizim de asıl amacımız başka programcılara kütüphane yazmak değil kendi uygulamamızı geliştirmek olduğu için zaman kazanma adına hazır kütüphane kullanmayı tercih edebiliriz. Fakat gömülü sistemlerde mevcut kütüphanelerin çoğunun bir geliştirici tarafından geliştirildiğini, donanım farklılıklarının sorunlara yol açtığını, frameworklere gelen güncellemelerle kullanılmaz hale geldiğini ve çoğu zaman çok deneyimli olmayanlar tarafından geliştirildiği için eksikliklere sahip olduğunu söyleyebiliriz. Bu alan biraz spesifik ve sınırlı olduğu için açık kaynak olarak yeterli içeriği bulamadığımızı söyleyebiliriz.
Bu durumda “Kütüphane yazamamak” sizi geliştirme yaparken elinizi bağlayacak en önemli unsurlardan biri olarak karşınıza çıkacaktır.
Şimdi deneme amacıyla örnek programı çalıştıralım.
Programı çalıştırdığımızda sayaç her yarım saniyede bir artacaktır. Bütün metotların doğru çalışıp çalışmadığını kontrol etmekte yarar vardır. Kütüphane yazarken test amacıyla bir örnek kod bırakmanız faydalı olacaktır.