Stm32 ve Mbed ile Nextion Ekran Kullanımı
Merhabalar..
Bu konuda Nextion ekran kullanarak yaptığımız Arduino uygulamasını STM32 serisi kartlar kullanarak yapacağız. Bu konuda yine önceki örneklerde kullandığım gibi Mbed kullanacağım. Muhtemelen Mbed Stduio (merak edenler bağlantıdan bakabilir) kullanıma açılana ve stabil hale gelene kadar Mbed ile anlatacağım son konu olacak. Bundan sonra fırsat buldukça STM32CubeMx ile bazı uygulamalar yapmaya çalışacağım.
Bu konuda Arduino’ daki konudan farklı olarak kütüphane kullanmayacağız. Her veriyi byte byte kendimiz işleyeceğiz. Bu şekilde yapmanın avantajlarını ve dezavantajlarını birlikte göreceğiz. Arduino’ daki konuyu incelemek isteyenler bağlantıdan ulaşabilirler. Nextion tasarım ide’ sinin kullanımına ve tasarımın nasıl yapılacağına bağlantıdaki konuda detaylı olarak anlattığım için tekrar değinmeyeceğim. Bu kısımda daha çok yazılım ağırlıklı olacaktır. Ben projede Nucleo-F767Zi model numaralı bir kart kullanacağım. Üzerinde Uart hattı olan ve Mbed destekleyen herhangi bir STM32 serisi kart ile bu uygulamayı yapabilirsiniz. Elbet de bağlantı şemasını uygun şekilde değiştirmelisiniz.
Önceki uygulamaya ek olarak birde slider widget’ i ekledim. Bu widget’ i ikinci sayfaya ekledim. Özellikle buraya ekledim. Çünkü checkbox ile kullanımı özel bir durum gerektirmekte. Yeri geldiğinde bundan bahsedeceğim. Öncelikle yapacağımız bağlantı şemasına bakalım. Bağlantı şemasında F401 kullandım. Çünkü F767′ in fritzing çizimi bulunmamakta.
Çizimdeki FTDI dönüştürücü göstermeliktir. Siz onun yerine Nextion erkan bağlıyacaksınız (Rx ve Tx pinleri resimdeki gibi olacak). Fritzing’ de Nextion ekran bulamadığım için FTDI dönüştürücü koydum.
Bağlantıları siz kendi kartınıza göre değiştirebilirsiniz. Dikkat etmeniz gereken Nextion ekranı UART pinlerine, led’ lerden birini de PWM pinine bağlamalısınız. Diğer led’ leri herhangi bir dijital pine bağlıyabilir yada kartın üzerindeki onboard led’ leri kullanabilirsiniz. PWM pinine bağlayarak kullanacağımız ledin paralıklığını Nextion ekrandaki slider ile kontrol edeceğiz. Slider’ i ikinci ekrana yerleştirdikten sonra “touch release” eventini aktif etmeyi unutmayın.
Led’ lerden ikisi toggle işlemi için, bir tanesi checkbox için, 3. sü ise Nextion ekran ile kurulan iletişimin kontrolü için kullanacağız. Şimdi sıra geldi yazılım kısmına. Yazılımda kütüphane kullanmadan doğrudan Nextion haberleşme protokolü ile işlemlerimizi gerçekleştireceğiz. Itead tarafından hazırlanmış instruction sete bağlantıdan ulaşabilirsiniz. Dökümanın başında belirtildiği gibi bütün komutlar 3 adet 0xFF byte’ ı ile sonlanmalıdır. Yani siz listeden herhangi bir komut göndermek istediğiniz zaman sonuna mutlaka 0xFF 0xFF 0xFF eklemelisiniz. Öncelikle kullanacağımız sınıfların tanımlarını yapalım.
Serial pc(USBTX, USBRX); //debug values from pc Serial nex(D1, D0); //nextion communicate pins AnalogIn aIn1(A0), aIn2(A1); //analog input pins Ticker sendValTicker; //ticker need for update values on Nextion screen Timer t1; //timer need for measure pass time in serial read interrupt
Serial ve analog sınıflarının neden kullanıldığı rahat anlaşılabiliyor. Burda ek olarak Ticker ve timer kullanımına değinmek istiyorum. Ticker mbed’ de belirli sürelerde işlem yaptırmak istediğiniz kullanabileceğiniz bir sınıftır. Belirlediğiniz süre dolduğu zaman ana fonksiyon(main) dışında bir callback içerisinde yazdığınız işlemleri gerçekleştirir ve ana fonksiyona geri döner. Ancak bu kesinlikle paralel bir işlem değildir. Senkron bir olaydır. İşlemler yine sıra ile gerçekleşir. Bu yüzden callback içerisinde uzun sürecek işlemler yapılması çok tavsiye edilmez. Ticker’ ı biz 500ms’ de bir Nextion ekran üzerindeki değerleri güncellemek için kullanacağız. Timer sınıfı ise süre ölçmek için oluşturulmul bir sınıf. Yapacağınız işlem için geçen süreyi saniye, milisaniye veya mikrosaniye cinsinden timer ile ölçebilirsiniz. Biz timer’ ı Nexiton’ dan veri okurken veri kaçırılması durumunda timeout yapabilmek için kullanacağız.
Main fonksiyon içerisinde ayarlamalarımızı yapalım. Ana fonksiyonun başlangıcında uart ayarlamalarımızı yapıp, timer’ ı ve ticker’ ı ayarlamalıyız.
pc.baud(115200); //pc uart comm baud rate nex.baud(9600); //nextion uart comm baud rate t1.start(); //start timer pageID = 0; //set first page id //callback for nextion uart receive interrupt //when data comes from uart this callback function will call nex.attach(&nexReceiveCallback, Serial::RxIrq); //set ticker callback update time to 0.5 ms sendValTicker.attach(&sendValCallback, 0.5);
Bu kısımda ek olarak pageID değişkeni mevcut. Bu önemli bir değişken olduğu için buraya koydum. Bu değişken Nextion ekran üzerindeki widget’ larda güncelleme yaparken hangi sayfada olduğumuzu tutan değişkendir. Daha açık anlatacak olursam, siz ikinci sayfaya geçince nextion ekrana hala ilk sayfadaki bir widget’ ı değişmek için komut göndermeye çalışırsanız ekran size sürekli olarak hata komutu gönderir. Bu çok istenmeyen bir durumdur. Bunun önüne geçmenin en kolay yolu budur. Şimdi ticker sınıfımızın callback fonksiyonuna bakabiliriz.
void sendValCallback() { //if uart receive callback is using uart to read data return to main function if(isUARTBusy) return; //check which nextion page is showing now switch(pageID) { //if page0 showiing send t0 and t1 update value case 0: nex.printf("t0.txt=\"%d\"%c%c%c", analogVal1, 0xFF, 0xFF, 0xFF); nex.printf("t1.txt=\"%d\"%c%c%c", analogVal1, 0xFF, 0xFF, 0xFF); break; case 1: //if page 1 showing sed t0 and j0 update value nex.printf("t0.txt=\"%d\"%c%c%c", analogVal1, 0xFF, 0xFF, 0xFF); nex.printf("j0.val=%d%c%c%c", analogVal1, 0xFF, 0xFF, 0xFF); break; } }
Üstteki yazılımda yine dikkat edilmesi gereken bir nokta mevcut. Gördüğünüz gibi callback fonksiyonun başında UART kullanılıyormu diye kontrol ettim. UART aslında asenkron fullduplex bir haberleşme protokolüdür. (Yani veri okurken aynı zamanda veri gönderebilir) Ancak burada nextion’ a veri gönderirken widget’ lardan gelen eventleri okumaya çalışmak bazı eventlerin yakalanmamasına sebebiyet verebiliyor. Daha açık hali, siz Nextion ekran’ da bir text’ i sürekli güncellerken butona basıldı mı diye kontrol etmek isterseniz bazen algılama sorunları olabiliyor. Nextion içerisindeki yazılımdan kaynaklı olduğunu düşündüğüm bu sorun çok can sıkıcı olabiliyor. Bunun dışında printf’ in kullanımına dikkat etmelisiniz. Komutun sonunda gönermemiz gerken 3 adet 0xFF komutunu karakter olarak göndermeliyiriz. Aksi takdir NExtion yanlış parselleme yapacak ve size sürekli hata komutu gönderecektir. Şimdi sıra geldi uart interrupt’ ına. Uart Interrupt’ ında gelen data yı okuduğumuz kısmı ele alacak olursak,
while(buffIndex != 8) { if(nex.readable()) { nexBuffer[buffIndex] = nex.getc(); if(nexBuffer[buffIndex] == 0xFF) EOCcnt++; buffIndex++; } //if 0xFF comes 3 times break if(EOCcnt == 3) break; //if wait time exceeds 50ms break if(t1.read_ms() > 50) break; }
Bu kısımda nelere dikkat etmeliyiz. Öncelikle dizimiz “buffIndex” değişkeni 8 olana kadar dönüyor. Sebebi ise yine Nextion instruction set’ ten kaynaklı. Yukarıda bağlantısını veridğim dökümanda 27. sayfaya bakarsanız Nextion’ un size gönderebileceği verileri ve uzunluklarını görebilirsiniz. Dökümanda en uzun data 9 byte uzunlukta ancak onu kullanmadığım için ben 8 olarak tanımladım. Hangilerini kullandım, “0x65” ile başlayan “touch event return data” ve “0x71” ile başlayan “numeric variable data returns”. 0x65 ile başlayan veri 7 byte, 0x71 ile başyana veri ise 8 byte uzunlukta. Verinin bir tanesi 7 byte uzunlukta olduğu için 3 adet 0xFF gelmiş mi kontolü de yapmam gerekiyor. Aksi taktirde döngü her zaman 8 byte veri okumaya çalışır. Peki bunlar yeterli mi? Elbette hayır. Burada bir döngü oluşturduk ve bu döngü 8 btye veri okuyana yada 3 adet 0xFF gelene kadar dönsün istiyoruz. Ancak arada kaçan byte’ lar olabilir. Bu da yazılımımızın o döngü içerisinde sonsuza dek kalmasına sebep olur. Bunu önlemek için bir timeout oluşturmalıyız. Bunuda timer ile sağlıyoruz. Daha önce de bundan bahsetmiştik.
Burada kullandığımız gibi interrupt içerisindeki döngülü bir yapı olması aslında çok tavsiye edilen bir kullanım şekli değilr. Bizim okumak istediğimiz veri çok kısa olduğu için sorun oluşturmamakta ancak ESP8266 gibi cihazlar ile işlem yaparken bu döngü asenkron olarak kullanılmalı ve gelen veri büyük bir tampon tutulmalıdır. Nasıl yapılacağına bağlantıdaki konuyu inceleyerek görebilirsiniz. Bu proje için böyle bir çözüm kullanmayı tercih ettim. Şimdi sıra geldi gelen veriye göre işlem yapmaya. Öncelikle gelen veri 0x65 ile başlıyor yani bir “touch event” ise ne yapmalıyız ona bakalım.
//check first byte and last 3 bytes //if first byte equal to 65 and data length equal 7 if(nexBuffer[0] == 0x65 && nexBuffer[4] == 0xFF && nexBuffer[5] == 0xFF && nexBuffer[6] == 0xFF) { isParsed = true; //second byte carries widget ID number widgetID = nexBuffer[2]; //third byte carries touch or touch release event readVal = nexBuffer[3]; //debug values from pc serial terminal pc.printf("%d %d %d\n\r", pageID, widgetID, readVal); //check page change button pushed if(widgetID == 6 && readVal == 1 && pageID == 0) { pageID = 1; }else if(widgetID == 2 && readVal == 1 && pageID == 1) { pageID = 0; } }
Üstteki yazılımın son kısmında hangi sayfa değiştirme işlemlerini de algıladık. Bunu burada yapmamızın sebebi main’ e geri döndüğümüzde eğer sayfa değişmişse ticker ile yeni veri gönderme işlemini yeni sayafaya göre yapmamız gerekiyor olması. Yani o işlemi main içerisinde yapsak. Interrupt çıktığımız anda sayfa değişikliğini algılama yazılımına sıra gelmeden ticker interrupt’ ı çalışabilir ve eski sayfa için veri gönderebilir. Bu da hataya sebebiyet verecektir. Peki hangi sayfa değişmesini nasıl anlıyoruz. Örneğin ilk sayfadan ikinci sayfaya geçmek için butona basmışsak o butonun bilgileri seri porttan gelecektir. Bizde seri porttan veriyi alınca gelen veri butondan mı geldi diye kontrol edersek sayfa değişimini algılamış oluruz.
Yazılımdan da anlaşılacağı üzere ilk byte’ ı ve son 3 byte formata uygun mu diye kontrol ettik. Eğer uygunsa veri dizisi içerisinden ihtyiacımız olan verileri alabiliriz demektir. Peki ya ihtiyacımız olan veriler neler? “touch event” i gönderen widget’ ın ID’ si ve gelen event dokunma mı yoksa bırak ma eventi mi olduğu bilgisi. Bunları alıp birer değişkende tutuyoruz. İkinci olarak de verimiz “get” eventine gelen bir cevap mı diye kontrol etmemiz lazım. Bunun için de şu şekilde bir kontrol gerçekleştirebiliriz.
//if first byte equal 0x71 and data length equal 8 else if(nexBuffer[0] == 0x71 && nexBuffer[5] == 0xFF && nexBuffer[6] == 0xFF && nexBuffer[7] == 0xFF) { //if checkbox pressed if(getCheckBox) { //second byte carries widget value sliderVal = nexBuffer[1]; ledPwm = (float)(sliderVal / 100.0); pc.printf("slider %d\n\r", sliderVal); getCheckBox = false; //set checkbox flag false } //if slider pressed else if(getSlider) { //second byte carries widget value checkBoxVal = nexBuffer[1]; pc.printf("checkbox %d\n\r", checkBoxVal); getSlider = false; //set slider flag false } }
Bu kısımda ise ilk byte 0x71 ve son 3 byte 0xFF mi diye kontrol ediyoruz. Eğer bu şekilde ise bu bir get eventi cevabıdır. Yani slider’ ın yada checkbox widget’ inin cevabı dönmüş demektir. Ancak burda neyin cevabı geldiği veri paketinde yazmıyor. Bunu anlamamızın tek yolu get eventi gönderdiğimizde gönderdiğimiz eleman için yazılımda bir bayrak set’ lemek. Böylece cevap geldiği zaman bayrakları tutan değişlenlere bakıp hangi widget eventi geldiğini anlayabiliriz. Şimdi sıra geldi döngü kısmına.
Ana döngüde potansiyometre değerlerini okuyup, seri porttan gelen veride yaptığımız veri ayırma işlemine göre mikrodenetleyicide çıkışlar üretebiliriz. Ben çıkış olarak basit olması için led kullandım.
while(1) { // put your main code here, to run repeatedly: analogVal1 = aIn1.read() * 100; analogVal2 = aIn2.read() * 100; led4 = checkBoxVal; if(isParsed) { //button1 if(pageID == 0 && widgetID == 2 && readVal == 0) { led1 = !led1; } //butonn2 else if(pageID == 0 && widgetID == 3 && readVal == 0) { led3 = !led3; } //slider else if(pageID == 1 && widgetID == 7 && readVal == 0) { while(isUARTBusy); nex.printf("get h0.val%c%c%c", 0xFF, 0xFF, 0xFF); getCheckBox = true; } //checkbox else if(pageID == 1 && widgetID == 4 && readVal == 1) { while(isUARTBusy); nex.printf("get bt0.val%c%c%c", 0xFF, 0xFF, 0xFF); getSlider = true; } isParsed = false; } }
Ana döngüde dikkat edilmesi gereken yer işlem yapmadan önce bir bayrak değişken kullandım. Bu işlemin sadece bir keren yapılmasını sağlamak için gerekli. Eğer o bayrak orda olmazsa her döngü dönmesinde veri dizisinin içerisi boşaltılmamışsa aynı işlemleri yapıp durur. Eğer bayrak setlenmiş yani veri güncellenmişse, dizideki verileri kontrol ederek gelen “touch event” in hangi widget’ dan geldiğini seçiyoruz ve gerekli işlemi yaptırıyoruz.
STM32 ile Nextion kullanımı hakkında anlatacaklarım genel olarak bu kadar. Biraz uzun bir konu oldu. Ancak en çok içeriği tek bir konuda açık bir şekilde toplamak istedim. Yazılımın tamamına bağlantıdan ulaşabilirsiniz. Nextion Dosyasının güncellenmiş hali bağlantıda mevcuttur.
İyi çalışmalar dilerim…
Related Posts
-
PySide2 Arayüz ile UART Kullanarak STM32 Haberleşme
Yorum yapılmamış | Nis 30, 2020 -
STM32 ve ENC28J60 ile TCP Server Uygulaması (MBED)
Yorum yapılmamış | Haz 3, 2020 -
STM32F4 ile İlk Uygulamalar
1 Yorum | Ağu 24, 2015 -
STM32 ile Mbed OS Kullanarak I2C LCD Ekran ve Ultrasonic Sensör
Yorum yapılmamış | Ağu 9, 2018
About The Author
Oguzhan
Karadeniz Teknik Üniversitesi Elektrik-Elektronik Mühendisliği Elektronik ve Haberleşme dalı mezunuyum. Nesnelerin interneti, görüntü işleme teknikleri, robotik sistemler ve derin öğrenme alanları üzerinde çalışmaktayım.
Merhaba Hocam, nextion ekran olarak elimde var olan Nextion NX4832T035 3.5 Inch 480×320 HMI TFT LCD Touch Display Module Resistive Touch Screen kullanmayı düşünüyorum. Bu Arduino için uyumlu. Umarım stm32 de sıkıntı olmaz.
Merhaba, bahsettiğiniz Nextion ekran modülü mikrodenetleyici ile UART kullanarak haberleştiği için STM32 ile de rahatlıkla kullanabilirsiniz. iyi çalışmalar dilerim…