STM32 ve STONE LCD ile Grafik Çizdirme (MBED)
|Merhabalar,
Bu konuda STONE LCD’ lerin grafik çizdirme özelliklerini içeren bir uygulama gerçekleştireceğiz. Öncelikle grafik çizdirme işlemini yapmaya çalışırken edindiğim bazı tecrübeleri paylaşacağım. Daha önceki uygulamalarda da yaptığımız gibi seri porttan byte veri göndererek çizdirme işlemini gerçekleştireceğiz. Çizdirilmesini istediğimiz her nokta 16 bit yani 2 byte olarak gönderilmesi gerekmekte. Böylece ekranda gösterebileceğimiz en yüksek tam sayı değeri 65536 olmaktadır. Ancak bu değeri doğrudan yazdırmaya çalışırsak ekranın dikey ekseninin dışına çıkacaktır. Bu yüzden de göndereceğimiz sayı bu kadar büyükse ya bölerek göndermeli yada ekrandaki “Real Time Curve” aracının ayarları ile skala eklemeliyiz ki nasıl yapıldığını ilerde göstereceğim. Ekranın bence en büyük artısı verilerin byte olarak gönderilmesi ve bir veri paketinde birden fazla nokta gönderilebilmesi. Bu sayede çizdirme işlemi Nextion ekranlara göre çok daha hızlı gerçekleşmekte. Ayrıca sürekli olarak sabit sayıda nokta çizdirme ihtiyacımız olan uygulamalarda (örneğin FFT verisi çidirmek) işlemlerin gerçekleşmesini daha rahat hale getirmekte. Bu sebeple çizim işlemleri için kullanışlı bir ekran olduğunu düşünüyorum. Keşke nokta silme yada çizgi kalınlığı değiştirme gibi ayarları da seri port ekranından yapabilseydik çok daha güzel olabilirdi. Bence en büyük eksisi ise bir süre sonra grafiğin sıfırlanıyor olması. Yani çok fazla nokta arka arkaya gönderince bütün noktaları silip tekrar sıfırdan çizmeye başlıyor ki bu da bazı durumlarda istenmeyebilir. Şimdi yapacağımız uygulamaya dönecek olursak.
Yapacağımız Uygulamada STM32′ de iki ADC kanalını kullanarak analog değer okuyup, okuduğumuz değerleri STONE LCD üzerinde çizdireceğiz. Uygulama detaylarına geçmeden grafiği nasıl çizdireceğimize bir bakalım. STONE LCD editöründe grafik çizdirme aracı “Real Time Curve” olarak isimlendirilmiş. Ekran üzerinde grafik aracını kullanmak için bazı ayarlar mevcut. Bu ayarların hepsine tek tek bakalım. Bu aracın editör ekranındaki yeri aşağıdaki resimde görünmekte.
Bu aracı seçtikten sonra ekran tasarımı üzerinde grafik olarak çalışmasını istediğimiz yeri belirliyoruz. Böylece seri porttan gönderdiğimiz verileri belirlediğimiz bu alan içerisinde çizilecektir. Şimdi bu aracın çizdirme işlemine etki eden bazı ayarlarına bakalım.
Yukarıdaki resimde görülen ekran format özellikleri, yukarıdan aşağıya doğru açıklamaları aşağıdaki gibidir. Özelliklerin üzerine tıklayarak da ilgili açıklamaları görebilirsiniz.
- Y_Central: Çizim için ayrılan alanın merkezi. Basitçe (Ymax – Ymin) / 2 şeklinde hesaplanabilir
- VD_Central: Gönderilecek değer skalasının orta değeridir. (Vmax – Vmin) / 2
- Vertical axis magnification (N): STONE LCD dökümantasyon sayfasında bu parametre “N” ile ifade edilmekte ve gönderilen değer ile çizdirilen değer arasında bir çarpma işlemi yapar. Bu değeri rasgele belirlememelisiniz. Basitçe şu şekilde hesaplanarak yerine yazılmalıdır: N = (Ymax-Ymin)*256/(Vmax-Vmin)
- Data source channel: Kullanılan grafik için kanal numarası. En az 0 en fazla 7 değeri kullanılabilir. En yüksek ve düşük değerlerden de anlaşılacağı üzere en fazla 8 adet farklı çizim gerçekleştirilebilir. İkinci kanalı kullanarak farklı değerlerde grafik çizdirmek için ikinci bir grafik kullanılmalıdır.
- Horizontal Invertal: Ekrana yerleştirilen iki nokta arasında koyulan boşluk. İhtiyacınıza göre belirleyebilirsiniz.
Yapacağım uygulamada Vmax değerini 500, Vmin değerini ise 0 olarak belirledim. Bu Vmax ve Vmin değerlerine göre hesaplama yaparsanız üstteki resimde yazılı değerleri elde edebilirsiniz. Bu değerleri doğru şekilde ayarlamışsanız Vmax ve Vmin arasında veri göndererek grafik çizdirirken grafiğin tamamını kullanmış olursunuz. İki kanal veri çizdirmek için ikinci bir grafiği ihtiyacımız olacak. Bunu ayarlarını yaptığımız ilk grafiği kopyalayıp olduğu yere yapıştırarak gerçekleştirebiliriz. Yapıştırma işleminden sonra “Data Source Channel” parametresini 1 olarak değiştirmelisiniz. Böylece iki grafiğin kanalları farklı olacaktır. Ekrana göndereceğimiz veri gönderirken bu kanalı kullanarak gönderme işlemini gerçekleştireceğiz. Ekrana gönderilecek veri formatı aşağıdaki şekildedir.
0xA5 0x5A <DataLen> 0x84 <Channel> <Data1H> <Data1L>
- DataLen: Veri paketinin kalan kısmındaki byte sayısı
- Channel: Veriyi göndermek istediğimiz grafik
- Data1H ve Data1L: Gönderilecek 16 bit’ lik verinin MSB ve LSB’si
Kanal numarası belirlenirken veri gönderilmek istenilen kanallar 8 bit olarak temsil edilirler. Yani biz grafiğin 1. ve 3. kanallarına veri göndermek istersek kanal değerimiz şu şekilde olmalı:
0 0 0 0 0 1 0 1 = 0x05
Grafikte çizilmek üzere göndereceğimiz veriler daha önce de belirttiğim gibi 16 bit olmalı. Bu sebeple seri porttan gönderirken 2 byte’ e bölerek önce MSB sonra LSB gönderilmelidir. 1 kanal için çok sayıda veri gönderilecekse veriler arka arakaya dizilerek gönderilebilirler. Ancak 2 kanal için çok sayıda veri gönderilecekse veriler harmanlanarak gönderilmeli. Bu harmanlama tabirine şu şekilde örnek vereyim. 1. kanalda 1 2 3 verilerini 2. kanalda ise 5 6 7 verilerini çizdirmek isteyelim. Göndermemiz gereken verilerin sırası aşağıdaki gibi olmalıdır:
0x00 0x01 0x00 0x05 0x00 0x02 0x00 0x06 0x00 0x03 0x00 0x07
Verilerin yerleşimini yazılım kısmında daha iyi anlayabilirsiniz. Şimdi gelelim yazılıma. Yazılım kısmında Mbed OS 6 ile kullanılabilecek iki fonksiyon oluşturdum. Bunlardan bir tanesi tek grafiğe veri göndermek için diğeri ise iki grafiğe aynı anda veri göndermek için. Öncelikle tek grafik çizimi için hazırladığım fonksiyona bakalım.
// add point to one chart void add_point(uint8_t pCh, uint8_t pLen, const uint16_t *p1Value){ uint8_t mCnt = 0; lcd_buffer[mCnt++] = 0xA5; // buffer header 1 lcd_buffer[mCnt++] = 0x5A; // buffer header 2 lcd_buffer[mCnt++] = (pLen * 2) + 2; // data length lcd_buffer[mCnt++] = 0x84; // chart data header lcd_buffer[mCnt++] = pCh; // chart channel number // chart value for (int i = 0; i < pLen; i++) { lcd_buffer[mCnt++] = (p1Value[i] & 0xFF00) >> 8; // 16 bit data MSB lcd_buffer[mCnt++] = p1Value[i] & 0x00FF; // 16 bit data LSB } serial_port.write(lcd_buffer, mCnt); // send data buffer to LCD clear_buffer(); // clear data buffer }
Fonksiyonu kullanarak LCD’ de grafiğe göndermek istediğimizi verileri uygun formata yerleştiriyoruz. Öncelikle LCD header’ ları daha sonra uzunluk, grafik çizidirme komutu, çizdirilmek istenilen grafik kanalı ve son olarak da çizdrimek istediğimiz veriyi gönderiyoruz. Veri uzunluğu değişkenini 2 ile çarpıp 2 ekledik. 2 ile çarpmamızın sebebi çizdirmek istediğimiz veriler 16 bit yani 2 byte. Bu sebeple 4 veri çizdirmek istersek 8 byte göndermeliyiz. 2 eklememizin sebebi ise kanal numarası ve komut’ u da gönderdiğimiz için veri paketine +2 byte olarak eklenmekteler.
İki çizdirme fonksiyonunda buna ek olarak çok bir şey yapmamıza gerek yok. Sadece parametre olarak ikinci değeri eklemeli ve for döngüsüne diğer veri için ekleme yapmalıyız. Ancak burada dikkat edilmesi gereken bir nokta var. Daha önce de söylediğim gibi aynı anda 2 veri çizdirmek istersek verileri harmanlayarak göndermeliyiz. Bu sebeple aynı anda iki veri çizdireceksek bunların ikisinin de uzunluğu aynı olmalıdır.
// add point to two charts same time void add_point_multi(uint8_t pLen, const uint16_t *p1Value, const uint16_t *p2Value) { uint8_t mCnt = 0; lcd_buffer[mCnt++] = 0xA5; // buffer header 1 lcd_buffer[mCnt++] = 0x5A; // buffer header 2 lcd_buffer[mCnt++] = (pLen * 2 * 2) + 2; // data length lcd_buffer[mCnt++] = 0x84; // chart data header lcd_buffer[mCnt++] = 0x03; // chart channel number for (int i = 0; i < pLen; i++) { lcd_buffer[mCnt++] = (p1Value[i] & 0xFF00) >> 8; // 16 bit first data MSB lcd_buffer[mCnt++] = p1Value[i] & 0x00FF; // 16 bit first data LSB lcd_buffer[mCnt++] = (p2Value[i] & 0xFF00) >> 8; // 16 bit second data MSB lcd_buffer[mCnt++] = p2Value[i] & 0x00FF; // 16 bit second data LSB } serial_port.write(lcd_buffer, mCnt); // send data buffer to LCD clear_buffer(); // clear data buffer }
İki grafiği aynı anda çizdiren fonksiyonu da oluşturduğumuza göre sıra ana döngüde oluşturduğumuz yazılma geldi. Burada basitçe ADC’ den okuma yapıp, okuduğumuz değeri skale ettikten sonra fonksiyona gönderiyoruz. Skala etme işleminde 500 kullanmamın sebebi grafiği ayarlarken de bu değeri seçmiş olmam. Böylece grafiğin tamamını kullanmış olacağız.
// if elapset time bigger than 10ms save adc values to arrays if(t1.elapsed_time() > 10ms) { //read adc values float nAdcVal1 = pot1.read(); float nAdcVal2 = pot2.read(); // multiply adc value to scale data adcVal1[cnt] = nAdcVal1 * 500; adcVal2[cnt] = nAdcVal2 * 500; cnt++; // reset timer t1.reset(); } // if adc read count equals to buffer limit draw chart if(cnt == readCnt) { // add_point(1, readCnt, adcVal1); // uncomment if you want to draw one chart add_point_multi(readCnt, adcVal1, adcVal2); // uncomment if you want to draw two charts same time mLED = !mLED; // toggle status LED cnt = 0; // reset counter }
Ekran için yapacağımız bağlantılar önceki konuda verildiği gibidir. Bu sebeple tekrardan çizim vermeyeceğim. Referans olarak buradaki bağlantı şemasını kullanabilirsiniz. Yazılım tamamına buradan ulaşabilirsiniz. Normalde tasarıma butonlar da eklemeyi düşünüyordum. Lakin konu çok uzayacaktı. Bu sebeple butonlu grafik örneğini ilerideki konulara saklamaya karar verdim.
Bu konuda kullandığım ekranın üreticisi olan Stoneitech şirketine, ekranı inceleyebilmem için gönderdiği için teşekkürler.