STM32 mikrodenetleyicisinin GPIO çıkışlarına bir şeyler göndermek istediğimizde başvuracağımız iki farklı yöntem var.
ODR ya da BSRR kayıtçılarının seçtiğimiz bitlerine “1” ya da “0” yazarak bunlara karşı düşen GPIO pinlerini “1” ya da “0” konumlarına getirebiliyoruz.
Aynı işi yapmak üzere neden iki farklı kayıtçı kullanıldığı ilk bakışta çok belirgin değil. STM32 dokümantasyonundaki açıklamalar da tekrar açıklama gerektiriyor.
Hatta işi biraz daha kurcalarsak, aynı işi yapmak üzere kullanılabilecek iki kayıtçı daha, BRR ve BSR kayıtçılarının da olduğunu görüyoruz. Ben konuyu burada kendi cümlelerimle açıklamak istiyorum.
ODR ve BSRR NEDİR NE İŞE YARAR
İlk olarak ODR ve BSRR kayıtçılarının ne olduğunu ne işe yaradığını kısaca açıklayayım.
ODR kayıtçısı, yani açık haliyle Output Data Register, GPIO Port a koymak istediğiniz 16 biti doğrudan yazabileceğiniz bir kayıtçı. Buraya ne yazarsanız, “1” ler “1” olarak, “0” lar da “0” olarak portun çıkış pinlerinde belirir. ODR kayıtçısı 32 bitlik, ama üst 16 biti rezerv durumda, şu anda kullanılmıyor.
BSRR, açık haliyle Bit Set Reset Register ise 32 bitlik. Bunun alt 16 bitine yazdığınız “1” ler, karşı düştükleri çıkış pinlerini “1” yapar, “0” yazdıklarınız ise çıkış pininde bir değişikliğe yol açmaz. Bir başka deyişle “0” yazılan bite karşı düşen pin yazım öncesinde “1” idiyse “1” olarak, “0” idiyse “0” olarak kalır.
BSRR ın üst 16 biti de yukarıda alt 16 bit için söylediğimin tersini yapıyor. Yani “1” yazılan bitlere karşı düşen pinler “0” oluyor, “0” yazdığımız bitler ise bir değişikliğe yol açmıyor.
Eğer BSRR da, aynı GPIO pinine karşı düşen bitler hem üst 16 hem de alt 16 bitlik grupta “1” ise, çıkış “1” olarak konumlandırılıyor, alt 16 bitin önceliği var.
ST firması BSRR üzerinden yapılan işlemlere “Atomic” işlem adını vermiş. Çünkü bitler üzerinde tek tek işlem yapılabiliyor.
Burada bir ayrıntıya daha dikkat çekmek isterim : BSRR kayıtçısı ile yapılan işlemler tek atımlık. Yani BSRR a her bir şeyler yazışınızda seçmiş olduğunuz GPIO pinlerini set ya da reset eder ardından da kendini sıfırlar. Oraya yazmış olduğunuz kod işlem sonrasında 0x0000 olarak silinmiş olur.
GPIO ya ODR ÜZERİNDEN YAZMAK
Bir çıkışa ODR üzerinden istenen 16 biti yazmak direkt ve basit bir işlem olarak görülebilir.
O zaman neden BSRR kullanalım ki, diyebiliriz. Ama uygulamalarımızda bir portun 16 bitine birden veri yazdığımız durumlar çok nadir. Genelde portun pinlerini farklı farklı amaçlarla kullanıyoruz. Bir pin ya da bir grup pine bir şeyler yazarken diğerlerine dokunmak istemeyiz.
İşte bu nedenle ODR ın bazı bitlerine bir şeyler yazacak isek, önce bu kayıtçıyı 16 biti ile okuyup, istediğimiz bitlerini değiştirdikten sonra geriye yazmamız gerekir. Bu da oku-değiştir-tekrar yaz şeklinde verimsiz bir işlem olur.
Aslında bunun tek sakıncası CPU yu fazla meşgul etmesi değil. Eğer portun bazı pinlerini herhangi bir kesinti rutini içinde değiştiriyorsak iş daha çetrefil hale geliyor. Zira biz portun bazı pinlerine yazmak üzere ODR’ı henüz okumuş iken bir kesinti oluşursa, kesinti rutini devreye girecek ve kendine ait bitleri değiştirecektir.
Halbuki biz ODR’ı kesinti gelmeden önceki haliyle okumuş ve saklamıştık. Kesinti rutini işini bitirip kontrolu tekrar ana akışa bıraktığında biz kaldığımız yerden devam edip, portu kesinti öncesindeki haliyle ele alarak değiştirir ve ODR a geri yazarız. Yani kesinti rutininin yaptığı değişikliği silmiş oluruz. Bu nedenle böyle bir duruma engel olmak için portta değişiklik yapmaya girişmeden önce kesinti’lerin yetkisizleştirilmesi gerekir.
BSRR KULLANMANIN AVANTAJI
İşte BSRR kullanımı, portun sadece istenen bitlerine dokunup geri kalan bitleri rahatsız etmeyerek bu sıkıntıları aşmamıza yardımcı oluyor.
KOD ÖRNEKLERİ İLE ODR ve BSRR KULLANIMI
Şimdi iki yöntemi karşılaştırmak üzere, CNC Step motorlarını sürmede kullandığım iki kod örneğini vereyim:
GPIO’ya ODR kullanarak yazmak :
/* Set edilen bitler */
motor->GPIOx->ODR |= (motor->portMask) & (*(*motor->lookUp + motor->phase));
/* Reset edilen bitler */
motor->GPIOx->ODR &=(~(motor->portMask))|(*(*motor->lookUp + motor->phase));
Bu kod biraz karmaşık oldu, sadeleştirerek yazayım :
/* Set edilen bitler */
GPIOC->ODR |= Mask &data16bit;
/* Reset edilen bitler */
GPIOC->ODR &= ~Mask | data16bit;
/* Buradaki “Mask” sadece değiştireceğimiz bitleri “1” olan 16 bitlik bir maske. Örneğin değiştirilecek olan 4 adet bit, portun 8,9,10 ve 11. bitleri ise maskemiz 0x0F00 olur*/
GPIO’ya BSRR kullanarak yazmak :
/* Reset edilen bitler */
motor->GPIOx->BSRR=((motor->portMask) & (~(*(*motor->lookUp+motor->phase))))<< 16;
/* Set edilen bitler */
motor->GPIOx->BSRR = (motor->portMask) & (*(*motor->lookUp + motor->phase));
/* Yine sadeleştirilmiş haliyle */
/*Reset edilen bitler*/
GPIOC->BSRR = (Mask & (~data16bit))<<16;
/*Set edilen bitler*/
GPIOC->BSRR= Mask&data16Bit;
/* Buradaki “Mask” sadece değiştireceğimiz bitleri “1” olan 16 bitlik bir maske. Örneğin değiştirilecek olan 4 adet bit, portun 8,9,10 ve 11. bitleri ise maskemiz 0x0F00 olur*/
Yukarıdan görüleceği gibi porta BSRR üzerinden birşeyler yazıldığında, portun işlem öncesinde okunması gerekmiyor, doğrudan yazılıyor.
ODR ve BSRR YÖNTEMLERİ ARASINDAKİ HIZ FARKI
Yukarıda verdiğim örnekler üzerinden APB2 çevre aygıt saati 72 MHz iken :
ODR üzerinden yazmak : 1.06 µs
BSSR üzerinden yazmak : 0.76 µs
Burada, örnek kodlardan da görüleceği gibi, her iki durumda da ikişer yazma işlemi yapıldığını hatırlatayım. Yazılacak 16 bit ya da 32 biti önceden hazırlarsak bu iş tek adımda da yapılabilir.
Daha önce de belirttiğim gibi, BSRR kullanmanın hatırı sayılır bir hız avantajı sağladığı görülüyor ama BSRR kullanımındaki asıl kaygımız kesinti rutinleri ile çatışma riski.
BSR ve BRR a NEDEN İHTİYAÇ VAR ?
Yukarıdaki örneklerde BSRR ın”0″ yapılacak bitleri seçen üst 16 biti ile, “1” yapılacak bitleri seçen alt 16 bitine ayrı ayrı veri yazdığımızı görüyoruz.
Üst 16 bite yazarken ayrıca <<16 şeklinde bir sola kaydırma işlemi de var.
BRR kayıtçısı BSRR ın üst 16 bitinin işini yapıyor, BSR kayıtçısı da alt 16 bitin işini yapıyor.
Yani bu kayıtçıları kullanarak işimizi daha basit ve hızlı halledebiliriz. Tercih bize kalmış.
GPIO ya HAL FONKSİYONU İLE YAZMAK
HAL GPIO fonksiyonları arasında her seferinde tek bir bite yazan :
HAL_GPIO_WritePin(GPIOx, GPIO_Pin, PinState);
Bu fonksiyon kendi içinde BSRR kullanıyor, yazılacak data tek bitten ibaret ise kullanılabilir. Ama hızdan yana kaygımız var ise, doğrudan BSRR a yazmak, tek bit için bile olsa daha uygun oluyor.
KODUN TAŞINABİLİRLİĞİ HAKKINDA BİR NOT
GPIO Portları Cortex M çekirdeklerinin içinde yer almıyor, çünkü bunlar STM32 nin çevre aygıtlarından.
O nedenle BSRR, BRR, BSR ve ODR kayıtçıları diğer mikro denetleyicilerde olmayabilir, ya da farklı olabilir.
Bu yayının sonu – Selçuk Özbayraktar Temmuz 2020
Yazı faydalı ve açıklayıcı olmuş. Elinize sağlık, çok teşekkürler.