Raspberry Pi Pico ile STONE LCD Kullanımı
|Merhabalar,
Bu konuda Raspberry Pi Pico ile STONE LCD kullanımına değineceğiz. Daha önceki konularda da verildiği üzere STONE LCD, bir insan makine arayüz birimi cihazıdır. Bu ekranlar donanıma yük oluşturmadan donanım ile haberleşerek donanım üzerinde görsel arayüz oluşturmanızı sağlarlar. Bu konuda Pico ve MicroPython kullanılarak bu ekranlar ile nasıl haberleşebileceğimizi göstereceğiz. Önceki konuları bağlantıdan inceleyebilirsiniz. Önceki konularda ekran ayarlarının nasıl yapıldığını detaylıca anlattığım için bu konuda sadece Pico içerisinde yazacağımız yazıya yer vereceğiz. Konuya başlamadan önce bir kaç ufak noktaya değinmeliyim. Öncelikle STONE ekranların üretici firması olan Stoneitech firması, artık bu ekranları üretmiyor ve geliştirmiyor. Bu sebeple bu konu STONE LCD’ ler ile ilgili son konum olacak. İkinci mesele elinizdeki ekranlar için yazılım nereden yükleyebileceğiniz. Elinizdeki ekranlar için yazılımı bağlantıdaki sayfadan indirebilirsiniz. Ancak yazılımı indirdiğinizde size bir yazılımın süresi dolduğuna dair bir uyarı verecek. Bu uyarıyı geçmek için yine bağlantıdaki sayfada bulunana “UpdateValidityPeriod” isimli dosyayı indirip, yazılımın bulunduğu klasörde çalıştırmalısınız. Böylece indirdiğiniz yazılımın kullanım süresi uzayacaktır. Firmanın ürettiği yeni ekranlara bağlantıdaki sayfadan ulaşabilirsiniz.
Bu konuda yapacağımız uygulama Arduino ile kullandığımız tasarımın aynısını içerecektir. Bu sebeple bağlantı şemamızda o konudaki ile benzer olacaktır. Bu uygulamada kullanacağımız bağlantı şeması aşağıda verilmiştir. Bağlantı şemasında ekranda kullanılmak üzere 3 LED ve değerini ekrandaki gösterge üzerinde göstereceğimiz bir potansiyometre kullanılmıştır. Kullanılan transistörler RS232 ile UART dönüştürme işlemi için kullanılmıştır.
Bağlantı şemasını tamamladıktan sonra yazılım kısmına gelelim. Ekran tasarımımızda 3 ekran mevcut. İlk ekranda iki adet buton var. İkinci ekranda bir switch buton ve bir label, üçüncü ekranda ise bir gauge gösterge var. Yapacağımız yazılımı da bu ekran tasarımına göre yapacağız. Aslında hazırlanacak yazılım daha öncesinde Arduino’ da kullanılan yazılımın Python’ a çevrilmiş hali olacaktır. Daha önceden de kullandığımız ekran tasarımımız aşağıdaki gibidir.
Yazılım kısmında öncelikle kullanılan fonksiyonlardan başlayalım. STONE LCD’ den gelen veriyi ayırmak için iki adet fonksiyon kullanılmıştır. Daha önceki konularda da bahsettiğimiz üzere STONE LCD’ de butona bastığınız zaman gelen verinin formatı aşağıda verilmiştir.
[ Header1, Header2, Paket uzunluğu, Veri Tipi, Adres 1, Adres 2, Veri Uzunluğu, Veri1, Veri2 ]
- Header1: Screen Parameter ekranında yazan Seri Haberleşme 16 Bit Header’ ın ilk kısmı
- Header2: Screen Parameter ekranında yazan Seri Haberleşme 16 Bit Header’ ın ikinci kısmı
- Paket Uzunluğu: Gönderdiğiniz yada aldığınız verinin uzunluğu. Eğer veri gönderiyorsanız, gönderdiğiniz veri uzunluğunu sayıp bu değere yazmalısınız.
- Veri Tipi: Ekran kullanım kılavuzunda yazan komutun ne işe yaradığını gösteren kısım. Komutun yaptığı işe göre 0x80 ile 0x83 arasında değerler alabilir.
- Adres 1: Ekran üzerindeki widget’ ların adreslerinin 16 bitlik olduğunu söylemiştik. Bu adres değerinin ilk 8 bitlik kısmı
- Adres 2: Widget’ ın 8 bitlik ikinci kısmı
- Veri Uzunluğu: Bu kısımdan sonra gelen verinin 16 bitlik paket olarak uzunluğu
- Veri 1: Gönderilen yada alınan 16 bitlik olarak iletilmekte. Bu 16 bitlik verinin ilk kısmı
- Veri 2: 16 bitlik verinin ikinci kısmı
Öncelikle seri porttan gelen veriler doğru şekilde gelmiş mi kontrol edelim. Bu işlem için aşağıdaki fonksiyonu kullanabiliriz. Fonksiyon bir döngü içerisinde seri porttan gelen veriler içerisinde dönerken, header’ ları kontrol ediyor. Daha sonrasında paket uzunluğunu alıp, olamsı gereken paket uzunluğu ile seri porttan alınan verinin uzunluğunu kıyaslıyoruz. Eğer küçükse verinin tamamı alınmamış demektir. Bu sebeple fonksiyondan -1 değeri ile dönüyoruz. Eğer her şey olması gerektiği gibi ise buffer içerisinde veriyi alıp fonksiyonu 1 değeri ile döndürüyoruz.
# try parse received buffer
# if succesfully parse received buffer returns 1 else returns -1
def parseSTONEData(pBuffer, pData):
for i in range(len(pBuffer)):
# check first and second byte of received buffer
if((pBuffer[i] == 0xA5) and (pBuffer[i + 1] == 0x5A)):
# get data len from third index from buffer
dLen = pBuffer[i + 2]
# check buffer len smaller then data len
# if yes return -1
if dLen + 2 > len(pBuffer): return -1
# get data from buffer
for j in range(dLen):
pData.append(pBuffer[i + 3 + j])
return 1
Bu fonsiyonun 1 değeri döndürmesi halinde alınan veri paketi içerisindeki byte’ ları doğru şekilde birleştirecek bir fonksiyon daha kullanıyoruz. Bu fonksiyon da aşağıdaki gibidir. Fonksiyon sadece veri tipi 0x83 ile başlayan komutlar için ayrım yapabilmektedir. Diğerleri için isterseniz STONE LCD’ nin komut setini okuyarak ekleme yapabilirsiniz. 0x83 ile başlayan veri tipinde iki byte adres sonrasında veri uzunluğu ve ikişer byte olacak şekilde widget’ dan gelen veriler yer alıyor.
# parse data from received packet
def parseData(pData):
cmdType = pData[0]
# check data type is 0x83
# if yes, get value and adress from packet
if cmdType == 0x83:
adr = (pData[1] << 8) | pData[2]
dLen = pData[3]
dArr = []
for i in range(dLen):
tt = (pData[4 + i * 2] << 8) | pData[i * 2 + 5]
dArr.append(tt)
return [adr, dArr]
else: return None
Kullandığımız fonksiyonları verdiğimize göre şimdi ana döngüyü geldi sıra. Ana döngü içerisinde öncelikle Uart buffer’ ı içerisinde veri varmı kontrol edip eğer varsa ayırma işlemine başlıyoruz. Gelen veriler toplama operatörü ile bir byte buffer’ ına atanıyor. Tek tek byte okuma işlemi yapılırken hızlı ve kolay kullanılabilir bir yöntem olduğu için bunu tercih ettim. Farklı şekillerde bu veriler alınabilir.
# if data available in uart buffer, read it and append recBuffer
while(uart1.any() > 0):
recBuffer += uart1.read(1)
time.sleep(0.01)
Verileri okuyup bir buffer içerisine yerleştirdik. Daha sonrasında, üstte açıklamalarını yaptığımız buffer içerisindeki veriyi ayırma yapan fonksiyonları çağıracağız. Veri ayırma işleminin başarılı olması halinde gelen verileri tuttuğumuz buffer’ ı boşaltmayı unutmamalıyız. Yoksa hazırladığımız yazılım her seferinde aynı veriyi almış gibi işlem yapmaya devam edecektir.
# if data available in recBuffer try to parse
if(len(recBuffer) > 0):
# print([a for a in aa])
recData = []
# if packet suffesfully parsed, function retuns 1
if(parseSTONEData(recBuffer, recData) == 1):
# if received packet succesfully parsed, read data from packet
[adr, data] = parseData(recData)
print(hex(adr), data)
# toggle LED's according to parse data address and data
if adr == 0x16a1:
print("Led1 Toggle")
led1.toggle()
elif adr == 0x16a2:
print("Led2 Toggle")
led2.toggle()
elif adr == 0x1992:
led3.value(data[0])
if(data[0] == 1): uart1.write(arrOn) # send "LED Yandı" text to screen
elif(data[0] == 0): uart1.write(arrOff) # send "LED Sondu" text to screen
# clear recBuffer after parsing process
recBuffer = b''
Son olarak da ekrandaki göstergeye belirli zaman aralıklarında veri göndereceğiz. Bu işlemi geciktirme fonksiyonları (delay) kullanarak yapmak doğru değildir. Çünkü yazılıma gecikme verirsek o sırada ekrandan gelen verileri alamayabiliriz. Ayrıca araştırdığım kadarı ile Pico üzerinde MicroPython kullanırken seri port kesmesi kullanılamamaktadır. Bu sebeple ana döngü içerisinde hiçbir yerde gecikme kullanmamalıyız. Belirli zaman aralıklarında yapacağımz işimler için timer ya da Arduino’ da kullandığımız millis yapısı gibi yapılar kullanmalıyız. Ben millis kullanmayı tercih ettim. Arudino’ daki millis benzeri işlem yapması için aşağıdaki gibi bir fonksiyon tanımladır.
# returns system time when called
# this function used for making async wait operations
def millis():
return round(time.time() * 1000)
Artık hazırladığımız bu fonksiyon her çağırdığımızda çalışma zamanını bize değer olarka döndürecek. Aslında Pico üzerinde RTC pilli takılı olmadığı için zaman ifadesi gün ya da saat olarak her zaman doğru olmayabilir. Ancak bizim amacımız çalışırken geçen zamanı ölçmek olduğu için bu her zaman doğru olacaktır. Millis fonksiyonunu da tanımladığımıza göre artık Arduino’ da yaptığımız gibi belirli zaman aralıklarında veriyi LCD’ ye gönderebiliriz.
# send gauge data in every 100 miliseconds
if(millis() - lastTime > 100):
adc_val = adc.read_u16() # read adc value
aVal = int(adc_val * 380 / 65535) # scale adc value range to 0 - 380
arr[7] = aVal & 0xFF # split Low byte first
arr[6] = (aVal & 0xFF00) >> 8 # split high byte
uart1.write(arr) # send data to lcd
lastTime = millis()
Bu konunun sonuna geldik. Yazılımın tamamına bağlantıdan ulaşabilirsiniz.
İyi çalışmalar dilerim…