STM32 – GPIO YAZMA HIZI OPTİMİZASYONU

STM32 ye TFT bağlarken, paralel veri aktarımının SPI a göre çok yavaş kaldığını görerek şaşırmıştım. Halbuki tersi olmalıydı. Üstelik SPI’ı DMA ile kullanmadığımda dahi durum böyle idi. TFT SPI Hızları konusundaki yayınım için burayı tıklayabilirsiniz.

Halbuki benim elimde epeyce paralel arayüzlü TFT var, paralel TFT kullanan kitlerim ve projelerim var. Dahası, TFT nin dışında başka uygulamalar için de paralel veri aktarımı yapıp duruyorum. O halde bu konunun üzerine gitmeye değer diye düşündüm ve epeyi ilerleme kaydettim. Bu yayının konusu “STM32 de GPIO porta nasıl daha hızlı veri aktarılır” olacak.

STM32 DE GPIO PORTA PARALEL VERİ YAZMA YÖNTEMLERİ

Bir porta aynı 8 ya da 16 bit veriyi yazmak için iki yaklaşım var. Bunlardan birisi o portun ODR kayıtçısına (Register) yazmak, diğeri de BSSR kayıtçısını kullanarak istenen bitleri SET ya da RESET etmek. Bu iki yöntem de hız açısından aynı sonucu veriyor.

GPIOC->ODR kayıtçısının üste ve alt 8 bitine iki byte veri yazmak. Üçüncü gruptaki iki satır sadece hızları görmek için, faydalı bir kod değil. Zira alt 8 bite yazıyor ama portu önce okuyup 16 bit olarak geriye yazmadığımızdan diğer 8 biti bozabiliyor.

Yukarıdaki ekran kopyasında GPIOC ye ODR üzerinden iki byte yazım hızlarını ölçmek için kullandığım kodlar görülüyor. iki byte için 0,74 µs yani byte başına 0,37 µs aldığı görünüyor.
Bu işlem yapılırken önce ODR okunuyor, istenen bitler değiştirildikten sonra tekrar yazılıyor. Bu okuma-değiştirme-yazma işlemi nedeniyle epey zaman alan bir yöntem.

Hız kaybı dışında başka sakıncaları da var ama onu bir başka yayında ele aldım. O yayına ulaşmak için burayı tıklayabilirsiniz.

Gelelim diğer yönteme. Burada GPIO pinlerine bu porta ait BSRR kayıtçısını kullanarak yazıyoruz.

GPIOC Portun üst 8 bitine BSSR kayıtçısı kullanılarak iki byte yazmak

Yukarıdaki ekran kopyasında GPIOC ye BSSR üzerinden iki byte yazmak için kullandığım makrolar görülüyor. Bu makrolardaki bir sürü sağa sola kaydırmaya rağmen, ODR üzerinden yazıma göre -az da olsa- daha hızlı olması dikkat çekiyor. Byte başına 0.35 µs alıyor.

Bu ölçüm sonuçlarından hareketle bir Byte’ın yazım süresini ister 0,37µs ister 0.35 µs alarak beklenebilecek veri aktarım sürelerini hesaplamak mümkün.

Ancak, bu hesabı yaptığımda benim programımda byte başına 1.5 µs sürenin kaybolduğunu gördüm. Benim paralel transfer hızımın yavaşlığının nedeni işte bu kayıp.

GPIO YA YAZARKEN KAYBOLAN 1.5 µs NEREDE ?

Herhangi bir geliştirme yapmadan önceki kodlarım şöyle idi :

Verilen sayıdaki byte’ı porta yazan fonksiyonum.
Porta bir byte yazan fonksiyon. Portta veri hazır olduğunda TFT nin WR pini önce bir 0 ardından 1 yapılarak aktarım sağlanıyor.
Bir byte’ı BSSR kayıtçısı üzerinden portun üst 8 bitine yazan makro. Bu makro bir byte veriyi rasgele seçilen 8 GPIO pinine yazabildiği için çok kullanışlı. Bu esnekliğine rağmen hızlı da. Tek sorun, seçilen port ve pinlere göre değişliklikler yapılmasının zorunlu olması.

İşte bu kodlar ile GPIO B nin 8..15 pinlerine 7200 Byte yazmak, buffer hazırlık süresi düşüldükten sonra, 13.5 ms sürüyordu. Halbuki ideal durumda bu sürenin 2.66 ms olması gerekir. (7200 byte*0,37 µs) Yani byte başına 1.5 µs kayıp var.

O halde yukarıdaki fonksiyonlarda ciddi sızıntılar var demektir.

GPIO YAZMA İŞLEMLERİNİN OPTİMİZASYONU

İki adımlık bir optimizasyon ile istediğim sonuca ulaştım.

İlk adımda, diziyi aktaran fonksiyon içindeki alt fonksiyon çağrısını kaldırıp BSSR a yazma makrosunu içeri aldım. Bu bana 2.4 ms fayda sağladı.

Bu yeterli bir iyileştirme değil. Bir de şu WR_CLR ve WR_SET komutlarına bakalım. Bu makrolar HAL_GPIO_WRITE_Pin() fonksiyonunu kullanıyorlar. Ben doğrudan BSSR kayıtçıları üzerinden bu işi kendim yapayım:

Evet, asıl faydayı burada gördük. HAL fonksiyonlarını kullanmak yerine doğrudan BSSR GPIO bit SET RESET işlemleri yapınca 7.2 ms birden hızlandık.

SONUÇ – MUTLU SON

Sonuçta ulaştığımız durum aşağıdaki Tablo 1 de görülüyor.

7200 Byte lık paketin GPIO porta yazım süresi 17 ms den 9 ms ye düştü. Bu hız TFT kontrol sinyalleri ile birlikte ~1 MByte/s ye karşı düşüyor. Optimizasyon öncesine göre iki kat hız kazanmış olduk. İdeal durum olan 6,16 ms ye (2.66+3.50ms overhead) ulaşamadık ama kaçınılmaz olarak kullanmak zorunda olduğumuz TFT kontrol sinyalleri var.

Artık 8 Bit paralel veri aktarımı SPI DMA ya göre yavaş, ama DMA kullanmayan SPI a göre de hızlı. 7200 Byte aktarımı için karşılaştırırsak :

SPI : 11.5 ms
Paralel 8 bit : 9.0 ms
SPI + DMA : 5.2 ms

Paralel aktarımın gene de yeterince hızlı olmamasının nedeni, porta konulan her byte’dan sonra bir WR SET/RESET sinyal çiftinin gerekmesi. Bu, byte başına fazladan 0,4 µs kayba yol açıyor. Halbuki SPI böyle bir senkronizasyona gerek duymuyor, SPI CLK sinyalinin senkronizasyonu sayesinde byte’ları ard arda gönderebiliyor. Üstüne bir de DMA kullanılınca -GPIO için DMA kullanamıyoruz- SPI süper hızlı hale geliyor. SPI hızı STM32F103 de 18MBit/s ye kadar çıkabiliyor. (2 MByte/s).

Not : Bellekten GPIO'ya DMA yöntemi ile veri transferi çetrefil bir konu. Bunu bellekten belleğe imiş gibi tanımlayıp hedef adresi ODR olarak vermek mümkün, ama GPIO yu dışarıdan okuyacak alet ile bir handshaking mekanizması gerekiyor.
Bazı mikrdenetleyicilerdeki DMCI

ÇIKARTILABİLECEK DERSLER

Burada fonksiyon çağrılarının ve HAL fonksiyonlarının ciddi hız kayıplarına yol açabileceğini görüyoruz. Eğer, bir kod bloku çok fazla yerde kullanılmıyorsa bunu ayrı bir fonksiyon içine koymayıp kullanım yerinde tekrarlamak hız kazandırıyor. Bunun da bir bedeli var elbette; programın okunabilirliği bozulup, daha fazla bellek kullanımına yol açıyoruz, programın bakımını da güçleştiriyoruz. Ama hızın kritik olduğu uygulamalarda bu mahzurlar kaçınılmaz olarak sineye çekilmek zorunda.

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

Leave a Reply

Your email address will not be published. Required fields are marked *