3.3- MicroPython’da I2C LCD Modülü Kullanımı
Karakter LCD’yi sürücüsü sayesinde normalde olması gerekenden daha az ayakla ve daha kolay bir şekilde kullanma imkanına sahiptik. Bunu 4-bit modunda kullandığımızda ise 4 ayaktan tasarruf etmekteydik. Yine de günümüzdeki modüllere kıyasla karakter LCD fazlaca ayak meşgul etmektedir. Özellikle devre tahtası bağlantılarında bu yüzden zorluk yaşamanız muhtemeldir. Bunu daha da pratik hale getirmek için karakter LCD’yi I2C protokolüne uyumlu hale getirip haberleşmeyi I2C üzerinden yaparak çok daha az ayak kullanarak kontrol edebiliriz.
Daha önce belirttiğimiz gibi karakter LCD paralel iletişimi destekliyordu. Biz bunu seri iletişimde kullanmamız için arada bir dönüştürücü entegreye ihtiyacımız olacaktır. Yarı iletken üreticileri pek çok protokoller arası iletişimde kullanılmak üzere entegre üretmektedir. Bizim ihtiyacımız ise I2C ve paralel iletişim arası bir köprü görevi görecek bir entegredir. Buna kısaca “Port çoklayıcı” da adı verilmektedir.
PCF8574 Philip firmasının (günümüzde NXP) üretmiş olduğu I2C I/O hattı çoklayıcısı bir entegredir. Bu entegrenin datasheetini aşağıdan inceleyebilirsiniz.
https://www.aurel32.net/elec/pcf8574.pdf
Bizim karakter LCD’yi kullanmak için toplam en az 6 ayağa ihtiyacımız vardır. Bu entegre ise 8 adet dijital giriş ve çıkışa sahiptir. Bu entegreyi kullanan modüller genel amaçlı giriş ve çıkışa yönelik olsa da karakter LCD için geliştirilmiş modüller doğrudan karakter LCD’nin arkasına takılabilir haldedir. Bu modüllerin arkasında kontrast ayarı, arka aydınlatma için direnç lehimleyebileceğimiz bir header ve arka aydınlatmayı kontrol eden bir transistör bulunmaktadır.
Uygun fiyatlara bulacağınız bu modülü isterseniz 20x4 gibi diğer boyuttaki karakter LCD ekranlarla da kullanabilirsiniz. Uygulamalarda bu modül sıkça tercih edilse de bazı kütüphane ve framework arası uyumsuzluklardan dolayı pek çok zaman kullanıcılar bunu kullanırken sorun yaşamaktadır. Burada hazırladığım I2C LCD kütüphanesinin bu noktada çok sorunlu olmamasını beklemekteyim. Olabildiğince basit yazmaya çalıştığım kütüphane önceki LCD kütüphanesinin üzerine I2C fonksiyonlarının eklenmesiyle meydana getirilmiştir.
Bu kütüphaneyi yazmak için öncelikle kullanmamız gereken modülün datasheetindeki bilgilere müracaat etmemiz gerekir. Modül kullanım açısından I2C aygıtlar içerisinde en basitlerinden biri olsa gerektir. Kısaca anlatmak gerekirse bizim modüle yazdığımız veriler çıkış olarak karşımıza çıkmaktadır. Yani adres + veri şeklinde aynı bir yazmaca değer yazar gibi değeri yazmamız gereklidir.
Burada dikkat etmemiz gereken nokta bit bazlı işlemleri doğrudan modül üzerinde yapamayışımızdır. Modüldeki bir biti sıfır veya bir yapmak istediğimizde önce değeri okumalı ve sonrasında güncelleyip tekrar yazdırmalıyız. Bu durum zaman kaybettirici olduğundan yazdırılacak değeri mikrodenetleyici ortamında takip edip bunun üzerinde düzenlemeler yaptıktan sonra son halini göndermek, maskeleme işlemi gerekiyorsa bunu burada yapmak daha etkili olacaktır.
LCD çalışmaya başladığı zaman 8-bit modda çalışmaya başlayacaktır. Bunu 4-bit moda çevirmek için başlangıçta sadece 4 bitlik verileri yazdırmamız gerekecektir. Bizim yaz() metodu bir veriyi 2 adet 4 bitlik (Nibble) parçalara bölerek yazdırmaktadır. Kütüphaneye eklenen init_yaz() metodu ile başlangıçta gerekli olan 4-bitlik veriler yazdırılmıştır.
Şimdi devreyi kuralım ve örnek bir program deneyelim.
Burada LCD sınıfı oluştururken I2C modülün adresini, ESP32’de bağladığımız SDA ayağını ve SCL ayağını belirtmemiz gereklidir. Pek çok I2C LCD modülün adresi 0x3F olup bazılarının ise 0x27’dir. Eğer modülün üzerindeki entegre “AT” ile bitiyorsa adres 0x3F olacaktır. I2C modüllerin başlıca çalışmama sebeplerinden biri de doğru adresin belirtilmemesidir.
Nesne tabanlı kütüphane oluşturmanın önemi burada karşımıza çıkmaktadır. Biz istediğimiz taktirde modül üzerinde bulunan A0, A1, A2 noktalarını kısa devre yaparak bu modüllerin adreslerini değiştirebilir ve bu sayede pek çok modül bağlayabiliriz. Böylelikle tek bir mikrodenetleyici sadece 2 hat üzerinden 8’e kadar (A0, A1, A2 toplamda 3 bit etmekte.) ekranı kullanabilir.
Modüller farklı adreslere ayarlanabildiğinden pek çok modülü aynı hatta kullanabilsek de bir de bunun yazılım tarafı vardır. Normalde nesne oluşturmadan bunu yapmak istediğimizde her modülün adresini ayrı ayrı belirtip her yazdırma işleminde bu adresleri de argüman olarak aktararak bunu yapabilirdik. Örneğin lcd.puts() fonksiyonu iki modülle şu şekilde olabilirdi.
lcd.puts(0x3F, “Merhaba”)
lcd.puts(0x27, “Merhaba”)
Fonksiyonu adresleri alıp ona göre işlem yapacak şekilde yapsak da fonksiyon aldığı argümanı diğer fonksiyonlara da aktarmak zorunda kalacaktır. Fark ettiyseniz pek çok fonksiyon diğer fonksiyonları çağırmaktadır. Fonksiyonlar arasında bu değer aktarımı yerine genelde global bir değişken kullanabilsek de bu sefer de hangi global değişkenin kullanılacağını belirlememiz zahmetli olacaktı.
C dilinde bu tarz işleri yaparken struct yapılarından sıkça yararlanılır. Tek bir fonksiyon farklı struct yapılarının adresini argüman alarak doğrudan bu struct yapılarına erişir ve bu yapıların içerisinde yer alan değerlere göre işlem yapar. Yani nesne tabanlı sayılmayan programlama dillerinde bile mümkün mertebe nesne tabanlı programlamaya benzer işler yapılmaktadır.
Biz ise burada çok daha kolay ve özellikli bir şekilde yapmamız gereken işi gerçekleştirmekteyiz. Her bir ekran için tanımladığımız ayrı nesneler kendi içerisinde metotlara ve değerlere sahip olduğundan her birini bağımsız bir şekilde ele alabiliriz.
Kütüphane yazarken göz önünde bulundurmamız gereken noktaları madde madde şu şekilde açıklayabiliriz
· Her nesnenin ortak olarak kullanacağı değişkenler yani sabit değerler class anahtar kelimesinden önce global olarak belirtilmelidir. Yukarıdaki kütüphanede MASK_RS, MASK_E gibi değerler sabit olarak kullanılmakta olduğundan en üst kısımda belirtilmiştir.
· Her nesnenin kullandığı ama başlangıçta değiştirmemize gerek olmaya n değişkenler class anahtar kelimesinden sonra belirtilir. Burada LCD_SATIR değişkeni böyle belirtilmiştir. Bu değişken nesneye ait olduğu için self.LCD_SATIR diyerek erişim operatörü (.) kullandıktan sonra metotlar içerisinde kullanılmıştır.
· Her nesnede değişiklik gösteren değerler __init__ yapıcı metodu içerisinde argüman olarak alındıktan sonra erişim operatörü (.) ile nesnenin değişkenleri tanımlanır ve bu değerler bu değişkenlere aktarılır. Burada self mevcut nesneyi işaret etmektedir.
· __init__ metodu içerisinde nesnenin ilk oluşturulduğunda çalıştırılması gereken metotlar belirlenir. Burada LCD’nin ilk ayar komutları bu şekilde çalıştırılmıştır.
· Nesnenin metotları class içerisinde fonksiyon olarak tanımlanır. Nesne fonksiyonlarına metot adı verilmektedir.
· Her bir metot ilk argüman olarak self değerini, yani nesnenin kendisini almaktadır. Bunu metotları programda kullanırken belirtmemize gerek yoktur.
· Nesnenin metotlarına ve fonksiyonlarına sınıf içerisinden erişim sağlanırken her zaman erişim operatörü ile beraber self kullanılmaktadır.
· Birbirinden farklı ama ortak noktaları olan nesneler için bir ana sınıf kullanılabilir. Burada kullandığımız nesnelerin hepsi aynı olduğu (I2C LCD) için buna gerek görülmemiştir.
· Modülleri kullanırken datasheetlerinde belirtilen komutlara ve talimatlara harfiyen uymak gereklidir.