0.96″ boyutunda 128×64 ve 128×32 piksel OLED ekranlar birkaç dolarlık fiyatlarla bulunabiliyor. Bu ekranlar SSD1306 ya da SH1106 kontrol yongaları ile I2C ve SPI arayüzlü olarak satılıyor. TFT ve Monokrom LCD lere alternatif olarak kullanılabilen bu ekranların kullanımı için internette bol miktarda örnek kod ve sürücü var.
Ben bu yayında, ssd1306 OLED ekranları STM8 gibi program ve RAM belleği çok kısıtlı olan mikro denetleyicilerle kullanmak için geliştirdiğim bir algoritmayı anlatıyorum. Bu sürücü, 128×32 çözünürlüklü OLED ekranlara yönelik olarak önemli bir iyileştirmeyi daha içeriyor.
ÇALIŞMANIN AMACI
Bu ekranlar yıllardır piyasada, STM32 ve benzeri mikro denetletiyicilerle kullanılışını anlatan bir sürü yayın, örnek program ve sürücü kodları bulunabiliyor. Bu örneklerin çoğu sadece albenili fontlarla yazılar yazmakla kalmıyor, grafik sembolleri ve animasyonları görüntülemeyi de beceriyor.
Bu hazır çözümleri ben de zaman zaman STM32F1, F2, F3 ve F4 serisi mikro denetleyiciler ile kullandım. Fakat iki nedenden dolayı kendi sürücü kodlarımı geliştirme gereksinimi duydum:
OLED SÜRÜCÜLERİN RAM VE FLASH BELLEK GEREKSİNİMİ
İnternetten bulup kullandığım sürücü kodların hepsi -en azından benim karşıma çıkanlar- 128×64 (1024 B) ya da 128×32 (512 B) RAM alanı kullanan tam ekran tampon diziler kullanıyorlar. Bu sayede animasyon ve grafik simgeleri rahatlıkla ekrana gönderebiliyorlar.
Fakat STM8F003 gibi sadece 1KB RAM a sahip mikro denetleyicilerde bu kadar RAM alanı yok, olsa bile sadece ekrana bu kadar RAM tahsis etme lüksümüz yok.
Bellek kısıtlamalarından birisi de Font dosyalarını kaydedeceğimiz flash bellek için karşımıza çıkıyor. 8 KB lık flash alanımızın yarısından fazlasını font dosyalarına bırakırsak, geriye programın kendisi için pek birşey kalmıyor. Bu nedenle çeşit çeşit fontlar kullanmak yerine sadece tek boyut ve tipte font ile yetinmek zorundayız.
OLED 128×64 İLE 128×32 SÜRÜCÜLERİ FARKLI OLMAK ZORUNDA
Hazırda bulduğum OLED 128×64 SSD1306 sürücüleri 128×32 olan ekranlarla da kullanılabiliyor -gibi. Ama aslında, 32 satırlık olan ekranlarda SSD1306 bu işi her iki satırdan birisini atlayıp, resmi alttan üstten bastırarak yapıyor. Harfler, resimler basık ve yayvan olarak görüntüleniyor.
ÖNEMLİ BİR GÜNCELLEME : Dikkatli bir ziyaretçim, Ömer Faruk Erkalan Bey'in uyarısı ile SSD1306 denetleyicisinin 128x64 ya da 128x32 boyutları ile çalıştırılacak şekilde ayarlanabildiğini öğrendim. 0xDA komutundan sonra 0x12 yerine 0x02 gönderilirse 128x32 ye uygun şekilde çalışıyor. Bu durumda benim kodlarımdaki rearrange... fonksiyonuna da gerek kalmıyor. Program boyutları daha da küçülüyor. Bu durumda 128x32 sayfa adreslemesinde de değişiklik yapılması gerekiyor. Bunu ESP8266 projelerimde uyguladım ama STM32 için revizyonu şu anda yapamıyorum. STM32 uygulamasını yaptığımda yayınlarım. Ömer Bey'e teşekkür ediyorum.
Bu deforme olmuş fontlardan kurtulmak için 128×32 ekranları 64 satırlık olanlardan farklı bir şekilde sürmek gerekiyor.
SSD1306 OLED 128×32 PİKSEL BELLEK YAPISI
İşe başlarken önce SSD1306 denetleyicisinin data sheet’ine bakmak zorundayız. Ekrana pikselleri nasıl göndereceğimizi anlamamız gerekiyor.
OLED ekranlarımız tek renkli olduğundan her bir piksel için ‘1’ ya da ‘0’ göndermemiz yeterli. İyi ki de böyleymiş, yoksa her bir piksel için 16 ila 24, hatta 32 bit göndermemiz gerekecekti.
Aşağıdaki üç çizimi Solomon Systech Limited 2008 www.solomon-systech.com dan aldım.
SSD1306 ekran piksellerini RAM Belleğinde her biri 8 satır, 128 kolondan oluşan sayfalar (page) halinde saklıyor. 8 satırdan oluşan her bir kolonu 1 byte veri göndererek dolduruyoruz. Böylece seçtiğimiz bir sayfayı ard arda gönderdiğimiz byte’lar ile doldurabiliyoruz.
Sayfanın başladığı ve bittiği kolonları verdiğimiz komutlarla belirleyebildiğimiz için istediğimiz yerde başlayan, istediğimiz genişlikte sayfalar tanımlayıp çalışabiliyoruz.
Başka sürücüler ekranın tamamını ele alarak çalışırken, ben font genişliğinde -yani 8 kolonluk- sayfalar ile çalışmayı tercih ettim. Böylece sadece tek fonta yeterli 32 byte’lık bir tampon dizi işimi görmeye yetti. (16×8 lik font için neden 16 değil de 32 olduğunu ileride açıklayacağım.)
Yukarıdaki çizimde yeşil yazıları gözardı edebilirsiniz. Onlar ekranı sağdan sola ve yukarıdan aşağı çevirerdiğinizde (remapping) kullanacağınız veriler, biz ekran yönü için varsayılan ayarlarla çalışacağız.
Yukarıdaki çizimin tek bir sayfasını ele alıp yakından bakarsak aşağıdaki durum ortaya çıkıyor. Burada kolon yerine “SEG”, satır yerine de “COM” kısaltmalarını kullanmışlar.
Bu belleğe, dolayısıyla ekrana bir şeyler yazmak için, düşey konum ve yüksekliği belirlemek gerekiyor. Bunun için bir ya da daha fazla sayıda sayfa ile bu sayfalarının başlangıç ve bitiş kolonlarını belirleyerek özel komutlar ile SSD1306 ya gönderiyoruz. Ondan sonra da “data” olarak sırasıyla soldan sağa doğru 8 er bitlik verilerimizi gönderiyoruz. Gönderdiğimiz byte’lar ile sayfanın son kolonuna gelindiğinde bir sonraki sayfanın ilk kolonuna geçilip oradan devam ediliyor, aşağıdaki gibi :
FONT DOSYASINDAN ALINAN BİR KARAKTERİN SSD1306 YA GÖNDERİLMESİ
Fontlarımızın her birisi 16 byte’lık gruplar halinde font.c dosyasında yer alıyor. Bu dosyada diğer sürücülerde değişik boyut ve stillerde fontlar var. Bu çalışmada ise yer tasarrufu için sadece 16×8 fontları bıraktım, diğerlerini sildim.
Bu dosyaya bakıldığında her satırda 16 byte ile 1 fontun yer aldığını görebiliyoruz. Bu byte’lar ikişer ikişer o fontun 16 piksel yüksekliğindeki kolonlarını veriyor.
Bu çalışmadaki açıklamaları ‘1’ ve ‘0’ karakterleri üzerinden yapacağım. Bu karakterlere ilişkin satırları aşağıdaki çizimlere aktaralım:
Burada görüldüğü gibi ekrana ’10’ yazmak için önce ‘1’ in üst yarısını ilk sayfanın 1..8 kolonları, sonra alt yarısını ikinci sayfanın 1..8 inci kolonları için göndermemiz gerekiyor. Sonra başlangıç ve bitiş kolonlarını 8 er ilerleterek aynı şeyi ‘0’ karakteri için yapıyoruz.
Sonuç olarak ’10’ yazımız, 128×32 ekrana aşağıdaki gibi yerleşmiş oluyor.
SSD1306 NIN 128×32 EKRANLARA UYGULAMASI
Buraya kadar anlattıklarım 128×64 ekranlar için geçerli.
128×32 ekranlarda ise SSD1306 nin tek nolu satırları atlanarak ekrana sadece 0,2,4,6 nolu satırları 0,1,2,3 nolu satırlar olarak yazılıyor. Yani biz 8 satır içeren bir byte gönderiyoruz, OLED bunu 4 satır olarak gösteriyor.
SSD1306 kullanan 128×32 ekranlarda izlenen bu tuhaf kestirme yolu internet üzerindeki kaynakların hiçbirinde göremedim. Epey bir deneme yanılma, ekrandaki harfleri büyüteç altında inceleme sonucunda ulaştığım bir bilgi oldu. Birileri bir yerlerde değindi ise de ben bulamadım.
Bu durumda bizim de SSD1306 yı aldatarak, 4 bitin arasında boş bitler katarak 8 bit haline getirerek göndermemiz gerekiyor. Geliştirdiğim sürücü ile yaptığım da bu. Bu durumda bir karakteri ssd1306 ya 32 Byte halinde gönderiyoruz. SSD1306 birer satır atlayarak ekrana aktarılınca 16×8 olarak sonuçlanıyor.
KODLAR
Yukarıdaki açıklamalardan sonra algoritmayı kodlamak kolay olmalı. Sadece kritik önemdeki fonksiyonu vermekle yetiniyorum. Bu fonksiyonun ağırlıklı olarak yaptığı iş, font dosyasından okuduğu font-piksel verisinin, satır sayısını araya boş satırlar sokarak 2 katına çıkartılarak ssd1306 ya gönderilmesinden ibaret.
void ssd1306_showChar(uint8_t harf, uint8_t xPos, uint8_t satir) { uint8_t startC, endC, startP, endP; uint8_t i,j, fontByte1,fontByte2,temp, fontIndex; uint8_t expData[2];fontIndex = harf-32;
/* Bu karaktere ait 8 kolonu ele alalım. */for(i=0;i<8;i++){
/* 8 satırı aralara boş bitler sokarak 16 satır haline getireceğiz, bu iki byte'ı koyacağımız dizi expData */expData[0] = 0x00;
expData[1] = 0x00;
/* karakterimizin 16 bitlik her bir kolonunu iki byte'a okuyalım */fontByte1 = c_chFont1608[fontIndex][2*i];
fontByte2 = c_chFont1608[fontIndex][2*i+1];
expData[0] = 0x00;
expData[1] = 0x00;
/* ilk 4 biti 8 bite genişleterek expdata[0] a koyalım */for(j=0;j<4;j++){
temp = fontByte1;
expData[0] |= ((temp&0x80)>>(6-2*j));
fontByte1 = fontByte1<<1;}
/* ikinci 4 biti 8 bite genişleterek expdata[1] e koyalım */for(j=0;j<4;j++){
temp = fontByte1;
expData[1] |= ((temp&(0x80))>>(6-2*j));
fontByte1 = fontByte1<<1;}
/* ilk 8 bit ilk sayfada, ikinci 8 bit bunun 8 ilerisinde, yani bir sonraki sayfada */displayBuffer[i] = expData[0];
displayBuffer[i+8] = expData[1];
expData[0] = 0x00;
expData[1] = 0x00;
/* Font kolonunun ikinci byte'ına da yukarıdaki işlemler uygulanacak */for(j=0;j<4;j++){
expData[0] |= ((fontByte2&0x80)>>(6-2*j));
fontByte2 = fontByte2<<1;}
for(j=0;j<4;j++){
expData[1] |= ((fontByte2&(0x80))>>(6-2*j));
fontByte2 = fontByte2<<1;}
/* Font kolonunun alt yarısı da üst yarıyı izleyen iki sayfada */displayBuffer[i+16] = expData[0];
displayBuffer[i+24] = expData[1];
} /* Font'un 16 bitlik kolonlarını displayBuffer'a 32 şer bit olarak yerleştirdik. 16x8 karakter 32 satırx8 kolon oldu. */ startP = satir*4; // Başlangıç sayfası endP = startP + 3; // Bitiş sayfası startC = 0x00 + xPos*8; // Başlangıç kolonu endC = startC + 7; // Son kolon /* Fontun yerleşeceği pencereyi belirledik, şimdi çizelim. displayBuffer global olarak tanımlandığından parametre olarak aktarmıyoruz */ ssd1306_fill_window(startP, endP, startC, endC); }
SONUÇ
Bu çalışma ile STM8 ile OLED ekran kullanabilir duruma geldim. STM8 ile olabildiğince küçük boyutlu cihazlar yapmayı hedefliyorum, ama TFT ve 2×16 ekranların en küçükleri bile hatırı sayılır büyüklükte ve ağırlıkta. RAM ve flash belleklerde çok küçük bir yer kaplayan bu sürücü ile 128×32 ekranlarda şık görünümlü harfler ile yazabiliyorum. 16×8 fontlara bağlı kalmak bir kısıtlama olabilir ama 2×16 LCD kullansam da öyle olmayacak mıydı?
Program kodlarının tamamını vermedim. İlgi çekmediği durumlarda boş yere uğraşmış oluyorum. Sorusu olan, fazladan bilgiye ihtiyacı olanlar olursa yorum bırakarak bana ulaşabilirler.
merhaba, yazdığınız kütüphaneleri paylaştığınız bir github hesabınız var mı?
Ömer Faruk Bey merhaba,
Bir GitHub hesabım var ama bir kaç deneme dışında kod yayınlamadım. Meraklılara mail ile yardımcı oluyorum.Burada “meraklı” kelimesinin altını çizmek isterim, ödevini bana yaptırmak isteyen öğrenci arkadaşlardan kaçınmaya çalışıyorum. 🙂
Selamlarımla,
teşekkürler hocam 🙂
blogunuz ve datasheet dökümanlarından yararlanarak kütüphanemi yazıyorum. içeriklerinizin devamını merakla takip ediyorum.
Hocam tekrar merhaba, vakit ayırıp değerli bilgiler verdiğiniz için teşekkürler.
Bir ekleme daha yaparsak, sürücü entegreye ekranın 32 satır ya da 64 satır olduğunu söyleyebiliyoruz. Konfigürasyon ayarları yapılırken “Set COM Pins Hardware Configuration” (0xDA) komutunun arkasından 0x12 komutu gönderilirse sürücü 64 satır olarak çalışıyor. 0x12 komutu yerine 0x02 komutu gönderilirse sürücü 32 satır olarak çalışıyor.
İngilizce kaynakların bile kısıtlı olduğu konularda Türkçe kaynaklar paylaşmanız gerçekten çok kıymetli. Çalışmalarınızı ilgiyle takip ediyoruz, kolay gelsin.
Çok teşekkürler Ömer Faruk Bey, bir şey daha öğrenmiş oldum. İlk fırsatta deneyeceğim.
Selamlarımla,
Ömer Faruk Bey merhaba,
Önerdiğiniz komutu ancak deneyebildim, bu display’e işim düşen bir başka projede, bir başka işlemci (ESP) ile çalışırken.
Beklendiği gibi çalışıyor. STM8 e dönüp oradaki kodları revize etme işini şimdilik sonraya bıraktım. Yayono da o zaman güncellerim.
Tekrar teşekkürler.
Merhabalar
Ben STM8S103 kullanıyorum sprintf veya sscanf fonksiyonlarını kullandığımda memory fazlası ile dolduğunu gösterip kullanamıyorum.Benim istediği integer değeri ascii formatına dönüştürmek.Derleyici olarak IAR kullanıyorum.
Bana bu konuda yardımcı olurmusunuz?
Osman Bey maalesef STM8 hazır kütüphanelerin çoğunu kullanamayacağınız kadar küçük bir işlemci.
String kütüphanesini kullanamayacağınız için bunun fonksiyonlarını kendiniz yazmak zorundasınız.
Aslında kodlamayı bu gibi temel algoritmaları kendiniz geliştirerek öğreneceksiniz.
Size iyilik mi yapmış oluyorum bilemiyorum, ama ben vermezsem de nasıl olsa internetten bir
yerlerden bulacaksınız. Kodun üzerindeki açıklamaları okuyarak her bir satırında
ne yapıldığını kavrayarak çalışmanızı rica ediyorum.
*** Kodu mail ile göndereceğim. Her nedense buraya koyamadım.
Bunu vermekle birlikte, öğrenci olduğunuzu düşünerek ve bir büyüğünüz olarak hazır çözümler bularak çalışmanıza şiddetle karşıyım. Örneğin, bana iş başvurusu yapıyor olsanız bu kodu kendisi yazamayan bir mühendis olarak karşıma gelirseniz sizi işe almam. Daha çetrefil konularda önce kendiniz çalışarak sonra yardım isteyeceğiniz durumlarda görüşmek umuduyla beklerim.
Selamlarımla.
Merhaba Selçuk bey
Teşekkür ederim . Evet haklısınız bundan sonra sizin tavsiyenize uyacağım.
Saygılarımla