STM32 – TFT SPI HIZLARI

TFT ekranları kullanmaya ilk başladığımda internetten bulduğum örnek kodları kullanmıştım. Karşıma çıkan tüm örnekler ekrana text yazmak için aynı algoritmayı kullanıyorlardı.

Bu algoritmada bir satırdaki her bir karakter sıra ile alınarak ekrana çiziliyor, sonra yanındaki karaktere geçilerek devam ediliyordu. Bu yöntem ile bir süre çalıştıktan sonra, görüntüleme hızının pek çok uygulamam için yetersiz kaldığını gördüm. Örneğin, bir encoderi okurken ekranı güncellemeye yetişemiyordum. CNC programımda da koordinat değerlerini motor adım süreleri içinde ekrana yazmak mümkün olmuyordu. Ekran güncellemek için diğer işlemleri bekletmek söz konusu değil elbette.

Bu sıkıntılar karşısında ekrana daha hızlı veri göndermenin yollarını araştırarak algoritmamı geliştirdim. Esas olarak iki konuda yaklaşımımı değiştirdim.

İLK ADIM : DAHA BÜYÜK VERİ PAKETLERİ

Bunlardan birincisi, ekrana veriyi harf harf küçük paketler halinde değil, satırın tamamını bitmap olarak içeren olabildiğince büyük bir paket halinde göndermek oldu. Sadece bu değişiklik 50 karakterlik bir satırın yazım süresini 36 ms den 23 ms ye düşürdü.

49 karakterlik bir satırı SPI üzerinden TFT ye gönderme hızında kaydettiğim gelişme. 36ms den 9.6 ms ye indik.

Ancak bu süre dahi pek çok uygulama için uzun kalıyordu. Ekranda 12 piksellik fontlar ile güncellenmesi gereken 10 karakterlik bir satır için gereken süre hala 4.9ms idi. CPU yu bu kadar uzun bir süre motor ya da encoder kontrolundan uzak tutmak mümkün değil.

DMA NIN GETİRDİĞİ KAZANÇ

Geldik ikinci geliştirme alanına. Bu durumda da DMA – Direct Memory Access yöntemi imdadımıza yetişiyor. SPI iletişimini DMA ile kotardığımızda aşağıda Table 1 de verdiğim değerlere ulaşabiliyorum. 12 piksellik fontlarla 10 harflik bir satirin güncellenmesi, CPU nun sadece 2.5 milisaniyesini alıyor.

DMA yöntemi, 49 karakterlik bir satırın görüntülenmesi için gereken CPU zamanını 22.8 ms den 9.6 ms ye indiriyor. Başlangıçta ekrana harf harf yazıldığında 36ms alan bir satır, artık 9.6 ms de yazılabiliyor. Güzel bir gelişme.

Burada şunu belirtmekte yarar var: DMA yöntemini kulladığımızda CPU, bir buffere doldurduğu veri paketini SPI a aktarılmak üzere DMA ya emanet edip işine dönüyor. Bu tabloda verdiğim süreler, CPU nun bu işle meşgul olma süresi. Verinin tamamının arka planda TFT ye aktarılması bu sürenin dışında. Yani, CPU hiç aralıksız ard arda satırlar göndermeye kalkarsa biraz beklemek zorunda kalabilir. Yine bir başka deyişle, DMA veri aktarımını hızlandırmıyor, sadece bu yükü CPU nun üzerinden alıyor.

Neyse ki, aşağılarda da tekrar değineceğim gibi, CPU nun buffer hazırlık süresi, bunun TFT ye aktarım süresinin yanında çok uzun kalıyor. Bufferi yeniden doldurup SPI a verdiğimizde önceki transfer çoktan tamamlanmış oluyor.

Benim uygulamalarımda ard arda soluksuz satır yazmak söz konusu değil, zaten CPU nun uğraşacak başka işleri olduğundan DMA kullanıyorum.

36 MHz CPU hızı için TFT de görüntüleme hızları

TFT yi SPI arayüz üzerinden DMA kullanarak sürmek için kullandığım kodları bir başka yayınımda anlattığım için burada tekrarlamıyorum. O yayına ulaşmak için burayı tıklayınız.

CPU SAAT HIZININ ETKİSİ

Yukarıdaki açıklamalarda verdiğim rakamlar STM32F103 ü 36 MHz ile çalıştırdığım duruma ait. Harici kristal kullanmadığım için CPU saatini en fazla 64 MHz e yükseltebiliyorum.

Maksimum SPI baud rate 18 Mbit/s olduğundan, 64 MHz için SPI Baudrate prescaler’i 4 olarak seçmek durumundayız, yani 36Mhz de 18 Mbit/s olan hız 64 MHz de 16 Mbit/s ye düşüyor. Öte yandan buffer hazırlama süresi yarı yarıya azalıyor, CPU saat hızını arttırmanın faydasını burada görüyoruz.

SPI ve 8 bit paralel girişli TFT lere yazım süreleri

Tablo 1 de DMA kullanılmaması durumunda “blocking node” SPI ile çalışıldığındaki yazım süreleri de görülüyor. Burada DMA yönteminin ne kadar büyük bir fayda sağladığı görülebiliyor. Hız iki kat artıyor.

AÇIKLAYAMADIĞIM KONU : PARALEL SÜRÜM HIZI SPI A GÖRE ÇOK DÜŞÜK

Burada verdiğim tüm rakamlar osiloskop ile yaptığım ölçümlere dayanıyor. İşin ilginci DMA kullanmadığım SPI hızları dahi 8 bit paralel sürüm tekniğine göre çok düşük.

Bunun net açıklamasını şu an veremiyorum. Fakat bu gecikmenin 8bit veriyi GPIO porta koyarken izlediğim yoldan kaynaklandığını düşünüyorum. Kullandığım yöntemi aşağıdaki paragraflarda vereceğim.

Bu durumu tam olarak açıklığa kavuşturmak üzere özel bir çalışma yapacağım.

Not : Paralel transfer hızının neden düşük kaldığını incelediğim çalışmamı yaptım, ve hızı iki katına çıkardım. Bulgularımı ve önerilerimi bir başka yayında anlattım. O yayına ulaşmak için burayı tıklayınız.

ASLINDA TFT YE VERİ TRANSFERİ ÇOK DAHA HIZLI

Bu sürelerin çok büyük bir bölümü, TFT ye veri gönderme süresi değil, gönderme için kullanacağımız buffer’a piksel verilerinin yüklenmesi için harcanan süre. “Overhead” olarak adlandırdığım bu süreleri de hem 64MHz hem e 72 MHz için Tablo 1 e koydum. Görüleceği gibi, toplam sürenin önemli bir bölümünü buffer’i doldurmak için kullanıyoruz.

Buna göre buffer hazırlama süreleri ve SPI üzerinden aktarım süreleri aşağıdaki gibi oluyor (64Mhz CPU hızı için) :

49 karakter 12×6 Px Font : 7056 Byte – 4,35 ms, SPI DMA aktarımı: 0,85ms
10 karakter 12×6 Px Font : 1440 Byte – 1,07 ms, SPI DMA aktarım ı: 0,33ms
10 karakter 16×8 Px Font : 2560 Byte – 1,65 ms , SPI DMA aktarımı: 0,47ms

Yukarıdaki SPI aktarım süreleri CPU ya görünen süreler.

Gerçek SPI veri aktarım süreleri

Aslında veri aktarımı arka planda Tablo 1 de verilenlerden daha uzun sürüyor. 16 Mbit/s SPI hızı ile gerçek veri aktarım süreleri :

49 karakter 12×6 Px Font : 7056 Byte – 3,53 ms
10 karakter 12×6 Px Font : 1440 Byte – 0,72 ms
10 karakter 16×8 Px Font : 2560 Byte – 1,28 ms

TFT Hız ölçümü için izlediğim yol :

Kullandığım ölçüm yöntemi şöyle : Satır yazdırma komutunun hemen öncesinde ve sonrasında PB2 pinini evirerek osiloskopta toplam süreyi ölçüyorum.
Bundan sonra tft_basic.c içinde SPI transfer komutunu kapatıp sadece buffer doldurma kısmını bırakarak aynı süreyi tekrar ölçüyorum. Böylece SPI transferi hariç "overhead" dediğim hazırlık süresini ölçüyorum. 
İkisi arasındaki farkı da "CPU ya görünen SPI süresi" olarak değerlendiriyorum.
Main.c içinde hız ölçümleri için kullandığım çevrim
TFT SPI veri transfer süresini ölçebilmek için tft_basic.c içindeki bu TFT_writeMultipeData() satırını kapatıp açıyorum.

SPI DMA HIZI İLE 8 BİT PARALEL SÜRÜM HIZI KARŞILAŞTIRMASI

TFT ekranların 8 bit paralel girişli olanları olduğunu söylemiştim. Bu iki tip ekranın hız karşılaştırmasını da Tablo 1 e koydum. Paralel girişli ekranı süren CPU 72 MHz olmasına rağmen SPI kite göre 1/3 oranında yavaş kaldığı görülüyor. Aslında 8 biti paralel aktarmanın, SPI ile seri aktarmaktan çok daha hızlı olması beklenirdi.

Bu farkı sadece DMA ile açıklayamıyorum. Paralel aktarmadaki bir başka ek işlem de TFT ye bağlı CPU portuna verinin yazılması, bu da hatırı sayılır zaman alıyor. Aşağıdaki gibi bir makro kullanıyoruz:

Bir de şu seçenek var :

GPIOB->ODR=((data<<8)|(GPIOB->ODR&0x00FF));

Ama bu da yukarıdaki makro ile aynı süreyi veriyor. Bu nedenle daha fazla esneklik sağlayan makroyu kullanmayı yeğliyorum. Aslında bu ikisinin aynı süreyi almalarını da açıklayabilmiş değilim, nedenini anlamak için assembly kodlara bakarak derleyicinin her iki durumda ne yaptığını görmek gerekir. Belki günün birinde buna da bakarım.

SON SÖZ

Bu çalışmaya başlarken SPI üzerinden 36ms de yazmakta olduğum 50 karakterlik bir satırı artık 5.2ms de yazdırır duruma gelmiş oldum. Çoğunlukla ekrana 8-10 karakterden ibaret bir şeyler yazdırdığımdan, uygulamalarım açısından çok tatminkar sürelere ulaştım. 1-3 ms lik yazım hızları step motor adım aralarına dahi sığabilecek süreler.

Paralel aktarım hızının düşüklüğüne dair bir açıklamayı havada bıraktığımın farkındayım. Bu konu üzerinde çalışarak ayrı bir yayında ele almak daha iyi olacak.

Not : O çalışmayı yaptım, şimdi paralel aktarım yukarılarda verdiğime göre iki kat daha hızlı. O yayına ulaşmak için burayı tıklayınız.

Sizi yönlendirdiğim yayında kodlarını verdiğim TFT_ShowString() fonksiyonu yukarıda sözünü ettiğim eski, yani satırı karakter karakter yazan yavaş algoritmayı kullanıyor.

Burada sözünü ettiğim hızlı algoritma için TFT_ShowStringFast() adını verdiğim yeni bir fonksiyon geliştirdim. Ancak buna biraz daha çeki düzen verip optimize etmem gerekiyor. Kodları ileride yayınlarım. Bu biraz da ilgilenen olup olmadığına bağlı bir şey.

Bu yayının sonu – Selçuk Özbayraktar Haziran 2020