Raspberry Pi Pico I2C OLED Ekran Kullanımı
Bu yazımızda, Pi Pico I2C OLED ekran modülünü arayüzlüyoruz. Bu yazının sonunda Raspberry Pi Pico’da bir I2C iletişimi gerçekleştirebileceksiniz. Burada Micropython kullanarak bir OLED ekranda bazı dizileri göstereceğiz. OLED ekranının Arabirimi hakkında zaten bazı makalelerimiz ve projelerimiz var. Bir OLED ekranın çalışması hakkında doğru bir fikir edinmek için bu makalelere ve projelere başvurabilirsiniz.
Gerekli Malzemeler
- 1.3″ Ekran I2C OLED Ekran (SSD1306)
- Raspberry Pi Pico
- Breadboard
- Bağlantı Kabloları
Bağlantı Şeması
Raspberry Pi Pico ve OLED ekran modülünün devre bağlantısını aşağıda gösterildiği gibi yapabilirsiniz. OLED Ekran Modülünün SDA pini GPIO16’ya (Pin21) ve SCL pini GPIO17’ye (Pin22) bağlanır. Vcc pini, Pin36 olan Pico Board’un 3.3v Pinine bağlanır. Ekran modülünün GND pini, Pin38 olan Pico kartının GND pinine bağlanır.

OLED Ekran İçin MicroPython Kütüphanesi
İlk başta, kod dosyalarınızı kaydedeceğiniz yeni bir klasör oluşturun. Ardından, MicroPython’a uyumlu buradaki GitHub deposunu indirmeniz gerekir. “T3_Interfacing_An_OLED” klasörüne gidin. Kod dosyalarınızı kaydetmek için “main.py” ve “ssd1306.py” dosyalarını kopyalayıp bu dosyaları oluşturduğunuz klasöre yapıştırmanız gerekmektedir. Yani, şimdi iki dosyamız var. “main.py” dosyası, metin ve resimlerin görüntülenmesi için kod içerir. Ve “ssd1306.py”, OLED ekran modülünün kitaplığıdır. Kodu anlamadan önce, bir görüntüyü OLED modülünde görüntülemek için bir görüntüyü bitmap’e dönüştürmeniz gerekir. “T3_Interfacing_An_OLED” klasöründe başka bir “img_to_bmp.py” dosyası görebilirsiniz. Bu dosyayı çalıştırmak için bir python ortamına ihtiyacınız var.
img_to_bmp.py dosyasının kod açıklaması
Aşağıdaki “PIL” kütüphanesini “pip” komutunu kullanarak Python ortamınıza kurmanız gerekmektedir. input_filename_with_path dosyanın yolunu uzantısız olarak içeriyor. Bizim durumumuzda, resim dosyası ve img_to_bmp.py dosyası aynı klasördedir. Bu yüzden sadece resim dosyasının adından bahsettik. “Image.open(file_in)” hedeflenen resim dosyasını açıyor ve ardından transpose() ve convert() işlevlerini kullanarak bazı görüntü işlemleri gerçekleştiriyoruz. save() işlevi, dönüştürülen görüntüyü “.bmp” uzantılı bir çıktı dosyası olarak kaydetmek için kullanılır. Ardından, “bmp” dosyasını açmak için Image.open(file_out,mode=’r’) ve görüntünün bitmap dizisini okumak için io.BytesIO() işlevi kullanılır. İmg_bytes, çıktıda alacağımız bitmap dizisini depolamak için kullanılır. print(img_bytes) giriş görüntüsünün bayt dizisini yazdırır. “main.py” dosyasında daha fazla referans olması için bu bayt dizisini kopyalamamız gerekir. print(‘Görüntü Çözünürlüğü: ({0},{1})’.format(img.width,img.height)) görüntünün çözünürlüğünü yazdırır. Bu çözünürlüğü “main.py” dosyamızda kullanmamız gerekiyor. Çözünürlüğün büyük olmadığından emin olun. Görüntü çözünürlüğünü azaltmak için herhangi bir çevrimiçi dönüştürücü kullanabilirsiniz.
import io from PIL import Image input_filename_with_path = "rpilogo1" file_in = input_filename_with_path + ".png" file_out = input_filename_with_path+".bmp" img = Image.open(file_in) img = img.transpose(Image.FLIP_LEFT_RIGHT) threshold = 65 func = lambda x : 255 if x > threshold else 0 img = img.convert('L').point(func,mode='1') img.save(file_out) img = Image.open(file_out,mode='r') img_bytes = io.BytesIO() img.save(img_bytes,format='BMP') img_bytes = img_bytes.getvalue() print("Copy this bitmap array:") print('\n') print(img_bytes) print('\n') print('Image Resolution: ({0},{1})'.format(img.width,img.height))

Yukarıdaki resim, img_to_bmp.py’nin çıktısıdır. Bizim durumumuzda, giriş görüntüsünün çözünürlüğünü 32×32 boyutuna dönüştürdük.
Artık görüntünün bayt dizisine sahibiz. Aşağıdaki “main.py” dosyasının koduna bakalım. MicroPython yardımıyla temel donanım işlemlerini gerçekleştirmek için “machine” kütüphanesini kullanıyoruz. Ardından Raspberry Pi Pico kartına yakın zamanda eklediğimiz “ssd1306” kütüphanesini kullanıyoruz. Raspberry pi pico kartının yerleşik zaman özelliğine erişmek için “utime” kitaplığı kullanılır. Bayt dizisi işlemlerini gerçekleştirmek için framebuf,sys kitaplığı kullanılır.
from machine import Pin, I2C from ssd1306 import SSD1306_I2C import utime import framebuf,sys WIDTH = 128 #oled display çözünürlüğü HEIGHT = 64
Aşağıdaki image_byte_arr değişkeni, görüntülenecek görüntünün bayt dizisini (bitmap dizisi) içerir. img_to_bmp.py dosyasının çıktısından kopyaladığınız bayt dizisini yapıştırın. Ardından, img_to_bmp.py dosyasının çıktısına göre görüntünün aynı genişlik ve yüksekliğini belirtin.
image_byte_arr = b'BM\xbe\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00(\x00\x00\x00 \x00\x00\x00 \x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x80\x00\x00\x00\xc4\x0e\x00\x00\xc4\x0e\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xfc?\xff\xff\xfb\xcf\xff\xff\xe7\xf7\xff\xff\xc7\xf1\xff\xffs\xc6\x7f\xfe\xf1\xcf\xbf\xfd\xf7\xef\xbf\xfd\xf7\xf7\xdf\xff\xff\xf7\xdf\xfb\xef\xf3\xdf\xf9\xc7\xf1\x8f\xf6\x13\xeco\xf7|>w\xf7~\x7fw\xf7\xfe\x7fw\xf7\xff\x7fo\xfb\x7f\x7fo\xfc~~\x1f\xfd\xbc=\xff\xff\xe3\xe3\xbf\xfe\xf7\xf7\xbf\xff\x7f\xff\x7f\xff3\xe4\xff\xffx\x1f\x7f\xfe\xfc?\xbf\xfd\xfe?\xff\xfb\xff\x7f\xdf\xfb\xff\x7f\xdf\xfb\xfe\x7f\xef\xfb\xff\xbf\xef\xf9\xfb\xdf\x9f\xff\x0f\xf0\xff' image_width = 32 image_height = 32
Aşağıdaki I2C() işlevi, I2C iletişimini başlatmak için kullanılır. Piko kartında 2 adet I2C kanalı olduğu için bu fonksiyon I2C kanal numarasını alır. Bizim durumumuzda, I2C0 kullandık. I2C1 kullanıyorsanız, ilk parametreyi 0’dan 1’e değiştirin. Sonraki iki parametre, iletişim için SCL ve SDL pinlerini belirtmek için kullanılır. Bizim durumumuzda sırasıyla SCL ve SDL için 17 ve 16 pinlerini kullanıyorum. Ardından, I2C işlevinin bir sonraki parametresi, I2C iletişim frekansına atıfta bulunur. i2c.scan(), hedeflenen cihaz adresini almak için kullanılır.
i2c = I2C(0, scl=Pin(17), sda=Pin(16), freq=200000) print("I2C Adress : "+hex(i2c.scan()[0]).upper()) print("I2C Ayari: "+str(i2c))
SSD1306_I2C() işlevi, OLED Ekranı başlatmak ve onu bir nesne olarak “oled”de saklamak için kullanılır. Bu işlev genellikle cihazın iletişiminin genişliğini, yüksekliğini ve modunu alır. Ardından, ssd1306 kitaplığında “oled” nesnesi kullanılarak çağrılabilecek önceden tanımlanmış bazı işlevlerimiz olur. Oled.fill(0) ekranı temizlemek için kullanılır.
oled = SSD1306_I2C(WIDTH, HEIGHT, i2c)
Aşağıdaki displayText() işlevi, metni OLED’de görüntülemek için kullanılır. Metni, konum, clear_oled ve show_text parametresini işleve iletmeniz gerekir. Konum, varsayılan olarak (0,0) olarak ayarlanmıştır. Ancak bu konum parametresi ile metnin konumunu değiştirebilirsiniz. Metnin konumu, tanımlama grubu biçiminde olmalıdır (örneğin: (x,y)). clear_oled ve show_text varsayılan olarak True olarak ayarlanmıştır, ancak bu değişkenleri False olarak ayarlayarak durumu değiştirebilirsiniz.
def displayText(text, position=(0,0),clear_oled=True,show_text=True): if clear_oled: oled.fill(0) oled.text(text,position[0],position[1]) if show_text: oled.show()
Aşağıdaki displayImage() işlevi, görüntüyü OLED ekranda görüntülemek için kullanılır. Bu işlevin image_byte_array, image_Definition,position,clear_oled ve show_img parametreleri vardır. image_byte_array parametresinin yerine image_byte_arr değişkenini iletmemiz gerekiyor. Ardından, position parametresini kullanarak görüntü konumunu OLED’e aktarabilirsiniz. Yine, konumun demet biçiminde olması gerekir ve varsayılan olarak (0,0) olarak ayarlanmıştır. clear_oled ve show_img parametreleri, sırasıyla False ve True olarak ayarlanır. Gelecekte ihtiyacımıza göre bu durumları değiştirebiliriz. bytearray() işlevi, görüntünün bitmap’ini almak için kullanılır. Ardından, bitmap’in her bir bitini depolamak için framebuf.FrameBuffer() işlevi kullanılır. Çerçeve arabelleğinin birkaç türü vardır. Çerçeve arabelleğinin MONO_HMSB modunu kullanıyoruz. Ardından, görüntünün çerçevesini OLED modülünde görüntülemek için oled.blit() işlevini kullanmamız gerekir.
def displayImage(image_byte_array, image_resolution,position=(0,0),clear_oled=False,show_img=True): img = bytearray(image_byte_array) img = bytearray([img[i] for i in range(len(img)-1,-1,-1)]) frame = framebuf.FrameBuffer(img, image_resolution[0], image_resolution[1], framebuf.MONO_HMSB) if clear_oled: oled.fill(0) print("clear") if show_img: oled.blit(frame, position[0],position[1]) oled.show() print("display")
Aşağıdaki kod, OLED’deki metinleri görüntülemek ve bu metinleri OLED’de kaydırmak için kullanılır. for döngüsü altında displayText() işlevini iki kez çağırdık ve text1 ile text2’yi “(x,0)” ve (WIDTH-x,20) karşılık gelen konumlarıyla dize olarak ilettik. text1 için clear_oled’i False ve show_text’i True olarak ayarlıyoruz ve text2 için clear_oled’i True ve show_text’i True olarak ayarlıyoruz. Her yinelemede OLED üzerindeki metinleri görüntülememize izin verecektir. “x” değişkeninin değeri OLED’in WIDTH’si olan iterasyonun sonuna geldiğinde for döngüsü bozulur.
text1 = "merhaba dünya" text2 = "devreyakan" for x in range(0, WIDTH): displayText(text1,(x,0),clear_oled=False,show_text=True) displayText(text2,(WIDTH-x,20),clear_oled=True, show_text=True) if x == WIDTH: break else: x+=5
Aşağıdaki while döngüsünün altında, Görüntüyü metinle canlandırmak için for döngüsünü kullandık. Resmi görüntülemek için displayImage işlevini kullandık. image_byte_arr, (image_width,image_height), (x,y) ve clear_oled parametrelerini sağladık. Burada “(x,y)”, görüntünün demet biçimindeki konumudur. image_width ve image_height değerleri, görüntünün demet biçimindeki çözünürlüğünü temsil eder. Sonra clear_oled’i False olarak ayarladık ve show_img değişkenini ayarlamadık çünkü varsayılan olarak True olarak ayarlandı. Dolayısıyla, sırasıyla OLED’de resim ve metin görüntülemek için hem displayImage() hem de displayText() işlevini kullanabilirsiniz.
while True: y=0 text = "Interfacing OLED" oled.fill(0) for x in range(0,WIDTH-image_width): displayImage(image_byte_arr,(image_width,image_height),(x,y),clear_oled=False) displayText(text,(x,y+40),clear_oled=False,show_text=True) if x == (WIDTH-image_width)-1: break else: x+=2 oled.fill(0) for x in range(WIDTH-image_width,-1,-1): displayImage(image_byte_arr,(image_width,image_height),(x,y),clear_oled=True) displayText(text,(x,y+40),clear_oled=False,show_text=True) if x == 0: break else: x-=2
Şimdi Thonny IDE’de “main.py” dosyasını ve “ssd1306.py” dosyasını açın. Öncelikle klavyenizden “ctrl+shift+s” tuşlarına basarak “ssd1306.py” dosyasını Pico kartına kaydetmeniz gerekmektedir. Dosyaları kaydetmeden önce Pico kartınızı bilgisayarınıza bağladığınızdan emin olun. Kodu kaydettiğinizde, aşağıdaki resimdeki gibi bir açılır pencere gösterilir. Raspberry Pi Pico’yu seçmeniz ve ardından dosyayı “ssd1306.py” olarak adlandırmanız ve kaydet’i tıklamanız gerekir. Ardından “main.py” dosyası için de aynı işlemi yapın. Bu prosedür, programı Pico çalıştırılacağı zaman çalıştırmanıza olanak tanır.

Kodun Tamamı
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
import utime
WIDTH = 128
HEIGHT = 64
i2c = I2C(0, scl=Pin(17), sda=Pin(16), freq=200000)
print("I2C Address : "+hex(i2c.scan()[0]).upper())
print("I2C Configuration: "+str(i2c))
oled = SSD1306_I2C(WIDTH, HEIGHT, i2c)
oled.fill(0)
oled.text("Merhaba",5,8)
oled.text("Dunya",15,8)
oled.text("devreyakan",8,25)
oled.show()
utime.sleep(1)
counter = 0
while True:
oled.fill(0)
utime.sleep(0.2)
oled.text("Sayac:",5,8)
oled.text(str(counter),15,8)
oled.show()
utime.sleep(2)
counter+=1
SSD1306:
from micropython import const
import framebuf
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xA4)
SET_NORM_INV = const(0xA6)
SET_DISP = const(0xAE)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xA0)
SET_MUX_RATIO = const(0xA8)
SET_COM_OUT_DIR = const(0xC0)
SET_DISP_OFFSET = const(0xD3)
SET_COM_PIN_CFG = const(0xDA)
SET_DISP_CLK_DIV = const(0xD5)
SET_PRECHARGE = const(0xD9)
SET_VCOM_DESEL = const(0xDB)
SET_CHARGE_PUMP = const(0x8D)
class SSD1306(framebuf.FrameBuffer):
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = self.height // 8
self.buffer = bytearray(self.pages * self.width)
super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
self.init_display()
def init_display(self):
for cmd in (
SET_DISP | 0x00, # off
SET_MEM_ADDR,
0x00, # horizontal
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO,
self.height - 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_DISP_OFFSET,
0x00,
SET_COM_PIN_CFG,
0x02 if self.width > 2 * self.height else 0x12,
SET_DISP_CLK_DIV,
0x80,
SET_PRECHARGE,
0x22 if self.external_vcc else 0xF1,
SET_VCOM_DESEL,
0x30, # 0.83*Vcc
SET_CONTRAST,
0xFF,
SET_ENTIRE_ON,
SET_NORM_INV,
SET_CHARGE_PUMP,
0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01,
): # on
self.write_cmd(cmd)
self.fill(0)
self.show()
def poweroff(self):
self.write_cmd(SET_DISP | 0x00)
def poweron(self):
self.write_cmd(SET_DISP | 0x01)
def contrast(self, contrast):
self.write_cmd(SET_CONTRAST)
self.write_cmd(contrast)
def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))
def show(self):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_data(self.buffer)
class SSD1306_I2C(SSD1306):
def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
self.write_list = [b"\x40", None] # Co=0, D/C#=1
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
self.i2c.writeto(self.addr, self.temp)
def write_data(self, buf):
self.write_list[1] = buf
self.i2c.writevto(self.addr, self.write_list)
class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
import time
self.res(1)
time.sleep_ms(1)
self.res(0)
time.sleep_ms(10)
self.res(1)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(0)
self.cs(0)
self.spi.write(bytearray([cmd]))
self.cs(1)
def write_data(self, buf):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(1)
self.cs(0)
self.spi.write(buf)
self.cs(1)
Yorum yapma özelliği, forum tarafından gelen istek sebebiyle kapatılmıştır. Lütfen tartışmalar ve sorularınız için topluluk forumumuza katılın.