STM32 ile Mbed OS Kullanarak I2C LCD Ekran ve Ultrasonic Sensör
|Merhabalar,
Bu konuda STM32 serisi kartlar ile mbed OS kullanarak basit bir uygulama yapacağız. Bu uygulamada STM32 kartımız bağlayacağımız bir Ultrasonik sensör ile ölçtüğümüz mesafeyi I2c Lcd ekrana aktaracağız. Bu işlemi biraz daha Arduino’ dan farklı olsun bir Mbed OS’ nun kendi thread yapısını kullanarak iki ayrı thread’ de gerçekleştireceğiz. Öncelikle burada bahsettiğim thread’ lerin ne olduğuna bakalım. Türkçe karşılıkları iş parçacığı olan thread’ ler basit olarak yapılacak işi yada işi zamanlama yöntemiyle azar azar yapılmasını sağlarlar.
Yapacağımız örnekten açıklayacak olursam. Ultrasonik sensörden mesafe okuma işlemi uzun mesafelerde (3 – 5 m gibi) 3 sn gibi süreler tutabiliyor. LCD ekran ile ultrasonik sensörden okuma işlemlerini sıralı olarak yapmam durumunda ekranın yenilenmesi, sensörden gelen verinin okunmasından sonra yani 3 sn sonra olacaktır. Ancak siz bunu thread’ ler ile bölerseniz iki işlem birbirinden bağımsız olarak gerçekleştirilirler. Thread’ ler içerinde yapacağınız gecikmeli işlemler birbirini etkilemezler. Bİr thread beklerken diğerleri o süre çalışmaya devam ederler. Burada dikkat etmeniz gereken en önemli şey fazla sayıda thread’ i çok az zamanlı oluşturmamanız. Bu tür durumlarda sistemin stabil çalışması için bazı ince hesaplara ihtiyaç duyabilirsiniz.
Biz örneğimize geri dönelim. Daha önceki konularda ultrasonik sensörün ve I2C LCD ekranın kullanımını Arduino ile anlatmıştık. O yüzden onların detaylarına fazla değinmeyeceğim. Bu örnekte Mbed kullanmak için Vscode’ nin platformio eklentisini kullanacağım. Bunu seçmemin sebebi basit uygulamar için Platformio kolay ve hızlı bir çözüm olması. Mbed cli yöntemi biraz daha karmaşık (çok yada büyük kütüphaneli) projeler için ideal çözüm. Daha önce Platformio’ nun nasıl kullanıldığı ile ilgili konu paylaşmıştım. Bağlantıdan ulaşabilirisiniz. Öncelikle gerekli bağlantılarımızı yapalım.
Uygulamayı nucleo-f767 kartı ile yapacağım. Ancak fritzing şemasında 64 pin serisi kart bulabildiğimden onu koydum. Aradaki değişiklikleri yeri geldikçe belirtmeye çalışacağım. Mbed kullanacağımız için ortak donanımların kullanımı iki kart için de hemen hemen aynı olacağından yazılımda çok farklılık bulunmamakta. Bağlantı şemamız herhangi bir lojik seviye dönüştürücü işlem kullanmadık. Bunun sebebi Nucleo serisi kartların hemen hemen bütün GPIO pinlerinin 5V tolerans lı olması. Bu sayede giriş den uygulanan 5V ile mikrodenetleyici zarar görmemekte.
Yazılıma gelecek olursak. Basitçe bir ultrasonic sensörün çalışmasına değinelim. Ultrasonic sensörler yaydığı ses dalgasının bir cisimden yansıyıp geri gelme süresini ölçme prensibine göre çalışırlar. Burada kullanacağımız HC-SR04 serisi ultrasonic sensörde TRIG pininden uygulanan 10 – 15 us arası tetik sinyali ile ürettiği 38 kHz’ lik ses işaretinin geri gelme süresini ECHO pininden geri iletir. Sensör tarafından ölçülen mesafe ECHO pinin lojik 1 olma süresi olarak iletilecektir. Yani buradaki süre hesabının çok hızlı ve doğru yapılması gerekir. Bu işlem için Mbed OS içerisindeki timer’ ları kullanacağız. Oldukça kullanışlı hale getirilen timer’ lar Arduino’ ya daki millis yada micros fonksiyonları kadar kolay kullanılır hale gelmişler. Bunun için tanımlayacağımız bir bir interrupt ile echo pini için yükselen kenarı belirleyeceğiz ve timer ile o andan itibaren saymaya başlayacağız. Kullanacağımız LCD I2C lcd olduğu için kullanacağımız kütüphane de bağlantıdaki kütüpahane olacaktır.
Echo pini interrupt, trig ise dijital çıkış olarak tanımlayalım.
InterruptIn echo(D5); //echo pin defines as a interuupt in DigitalOut trig(D6); //trig pin defines as a digital out
Interrupt geldiği zaman ise aşağıdaki gibi bir yapı ile timer’ ı başlatabilirsiniz.
void ultasonic_rise() { //on interrupt triggered echoStat = true; } int main() { echo.rise(&ultasonic_rise); //interrupt callback function triggers when rising edge return 0; }
Daha sonra kullanacağımız timer’ ları tanımlayalım.
Timer t1, t2;
Gördüğünüz gibi timer oluşturmak bu kadar basit. Bunun yanı sıra Mbed OS’ de ticker isimli asenkron ve callback li zamanlayıcılar mevcuttur. Bu sınıflara bağlantıdan ulaşabilirsiniz. Şimdi sıra geldi thread’ leri oluşturmaya. İki adet thread oluşturacağız. Bunlardan bir tanesi lcd’ yi güncelliyecek diğeri ise ultrasonic sensörden okuma yapacak.
Thread u_thread, l_thread;
Bu thread’ ların callback’ lerini main fonksiyonu içerisinde aşağıdaki gibi tanımlayabilirsiniz.
//ultrasonic sensor thread void ultasonic_thread() { while(1) { //on thread wake up Thread::wait(1000); //sleep 1000ms } } //lcd update thread void lcd_thread() { while(1) { //on thread wake up Thread::wait(500); //sleep 500 ms } } int main() { u_thread.start(&ultasonic_thread); l_thread.start(&lcd_thread); }
Bu kısımda dikkat etmeniz gereken thread’ ler içerisindeki bekleme fonksiyonları. Bu fonksiyonları kullanmak zorundasınız. Aksi taktirde diğer thread’ deki işlemler sağlıklı çalışmayabilir. En az 1ms gecikme vermenizi şiddetle tavsiye ederim. Şimdi LCD’ nin tanımını yapalım.
#include <mbed.h> #include "TextLCD.h" I2C i2c_lcd(D14, D15); //I2C pins SDA, SCL TextLCD_I2C lcd(&i2c_lcd, 0x4E, TextLCD::LCD16x2); //I2C LCD library constructor
Lcd tanımlarını yaptıktan sonra ilk ayarlamaları yapalım. Bu ilk ayarlamalar arka ışığı aktif etme, kürsörü devre dışı bırakma gibi ayarlar. Aşağıdaki gibi yapabilirsiniz.
int main() { lcd.cls(); lcd.setBacklight(TextLCD_Base::LightOn); lcd.setCursor(TextLCD::CurOff_BlkOff); }
Ayarlama işlemleri bittikten sonra sıra geldi ana döngümüze. Thread’ li yapı kullandığımız için iki ayrı ana döngümüz olacak. İlki ultrasonik sensörün hesaplandığı ana döngümüz. Her hesaplamadan sonra 1 sn bekleyecek şekilde ayarlayacağız. Böylece saniyede 1 ölçüm yapmış alacağız. Ancak ekranı saniyede iki sefer güncelleyeceğiz. Her thread de ayrı ayrı çalışması için iki adet te led oluşturduk ki thread’ lerin çalışmalarını dışardan rahatlıkla gözlemleyebilelim.
//ultrasonic sensor thread void ultasonic_thread() { t1.start(); //timer 1 starts counting while(true) { led2 = !led2; //change led status to see thread working t1.reset(); //set timer 1 counter to zero trig = 1; while(t1.read_us() < 10); //wait until timer 1 reach 10 us //wait_us(10); // not used wait_us, because that function can break threads trig = 0; //this loop used for make timeout //wait three seconds until echo pin rises //if passed time reach 3 seconds do not wait anymore t2.start(); while(t2.read_ms() < 3000) { if(echoStat == true) break; //if echo pin rises echoStat become true and breaks loop } //if echo stat true calculate passed time until echo pin falls if(echoStat) { t2.reset(); while(echo.read() == true); t2.stop(); } //if echo stat true calculate distance if(echoStat) { long duration = t2.read_us(); u_distance = duration / 58.0; pc.printf("Value %ld %.2f\n\r", duration, u_distance); //debug values on serial port echoStat = false; }else{ u_distance = 0; } t2.reset(); Thread::wait(1000); } }
Ultrasonik sensör için gerekli hesaplamaları yaptık. Bu aşamadan sonra sıra geldi bunu ekranda görmeye. Ekran için kullandığımız thread fonksiyonu içerisini aşağıdaki gibi oluşturabiliriz.
//lcd update thread void lcd_thread() { while(true) { lcd.cls(); //clear lcd lcd.setAddress(0, 0); lcd.printf("Oguzhan BASER"); lcd.setAddress(0, 1); //set cursor to second row lcd.printf("Distance %3.2f", u_distance); //print distance on second row led1 = !led1; Thread::wait(500); } }
Yazılım kısmımızda bu kadardı. Nucleo Serisi kartlarda mbed kullanarak ultrasonik sensör ve I2C Lcd çalıştırma örneğimizde bu kadardı. Yazılımın tamamına bağlantıdan ulaşabilirsiniz.
Yazılımın tamamında led pinleri
//led pins on nucleo-f767zi DigitalOut led1(PB_7); DigitalOut led2(PB_0);
şeklinde tanımlanmış. Siz bunları şematikteki D2 ve D3 pinlerine bağlı olması için aşağıdaki gibi değiştirebilirsiniz.
//led pins on nucleo-f401re DigitalOut led1(D2); DigitalOut led2(D3);
İyi çalışmalar dilerim…