Hakan Kaya

ARM Uygulamaları – STM32F411 LED Yakma (Registerlarla)

Kütüphaneler kullanmadan önce işin background’ı incelemeyi kendim ve arkadaşlarım için yararlı olabileceğini düşündüm. Kod yazmaktan ve iki jumper kablosunu birleştirmekten daha fazlası olduğu görmek lazım. Kavramları olduğunca basitleştirip somutlaştırabileceğimiz nesneler ile bağlantı kuracağım. 


Başlıyoruz…

STM32F4 kitin üzerinde 4 adet kullanıcı tarafından kullanılabilinen LED’ler mevcut. Bugünlük işimiz bunları yakıp söndüreceğiz.

Peki bu LED’ler nereye bağlı? STM32F411 User Manual PDF‘inden bakalım. 

Sayfa 15’te LED’lere dair bilgiler var.

 

Bak sen, turuncu, yeşil, kırmızı ve mavi renkli 4 ledimiz sırasıyla D13, D12, D14, D15 pinlere bağlıymış. D portuna bağlı olduğunu öğrendik. 🙂

 

Sayfa 33’teki şematik çizime bakınca da ledlerin devre çizimlerini görebilirsiniz.

Ancak bir sıkıntı var gibi 4 led neden farklı değerlerde dirençlerle kullanılmış? Sanırım, farklı renkteki LED’ler farklı dirence sahip olmalı ki, direnç değerleri farklı olan yardımcı dirençlerle kullanılmış. Aynı değerdeki dirençlerle kullanılsaydı biri daha diğerlerinden daha kısa ömürlü olurdu.

Şimdi neyi nasıl yapabileceğimizi öğrenebilmek için ST firmasının STm32f411 için hazırladığı Reference Manual’a (Referans El Kitabı) bakmamız gerekecektir.

Genel olarak kitap hazırlanırken tek tek tüm register’ların adresleri verilmemiş, ancak başlangıç adresleri verilip ve istediğimiz herhangi bir register’ın başlangıç adresine olan uzaklığı (offset) verilmiştir.

GPIO portların clock buslarının (RCC) kullanıcı tarafından ayarlanması gerekmektedir.

Başlangıç adreslerini öğrenmemiz için Sayfa 37 Memory Map‘e baktığımızda gerekli adreslerin verildiğini görebiliriz.

Neyi biliyoruz şuan?

RCC başlangıç adresi(bilgilerin saklandığı çekmece numarası): 0x40023800

GPIOD başlangıç adresi(bilgilerin saklandığı çekmece numarası): 0x40020c00

bunları not edelim…

 

Reset and Clock  Control (RCC) diagramına baktığımızda D Portu için açılması gereken AHB1 bus matrix’i tarafından kontrol edildiğini görebilirsiniz. (DMA: Direct Memory access)

 

 

 

Öyleyse AHB1’i aktif etmemiz gerekecek. Bunun için RCC_AHB1ENR registeri sayfa 114’te aşağıdaki gibi verilmiş. Şimdi biz bu adresin içerisine veri yazacağız “Bak ben D portunu kullanacağım o portun AHB1 bus’ını aç” şeklinde…

32-bitlik(basamaklı) bir veri sakladığını görebiliyoruz sanırım. Offset 0x30 imiş, yani RCC adresinden 0x30 hex kadar uzakta. Öyleyse bizim RCC_AHB1ENR registerımızın adresi: 0x40023800 + 0x30 = 0x40023830 olur. Bu toplamayı sağa yaslanmış şekilde 16 tabanında yapıyoruz. Herhangi bir basamakta 16’dan büyük çıkması halinde 16’ya olan kalanını yazıp elde 1 var gibi düşünüp ilkokul yıllarında abaküs ile saymaya geri dönebiliriz zaman zaman.

Çoğu bit’lerinde Reserved yazıyor, onların çekmecelerde saklanan gizli bilgiler olduğunu düşünebilirsiniz. RW yazılanlar ise Read/Write(okuma-yazma) yapılabildiğini gösteriyor. Reset Value (başlangıç değeri) = 0x00000000 imiş. Kullanmadıklarımızı ve Reserved yazan yerlere 0 yazmalıyız demek ki.

Hemen tablonun altında GPIO D portu Clock bus’ını açmamız için 3. bite 1 yapmamız gerektiği yazıyor. Diğerleriyle şimdilik işimiz olmadığına göre yazacağımız veri binary olarak 0b0000000000000000000000000000100 olmalı.

Ancak hexadecimal olarak yazmak daha yakışıklı olacak gibi. Hex olarak: 0x00000008 yazmalıyız. Bence 31 tane 0 yazmak iyidir. 😀

 

 


Şimdi D portu GPIO (General Purpose ınput Output) registerlarına geçebiliriz. Sayfa 153 GPIO port mode register (GPIOx_MODER) registerine göz atalım. Bu registerda pin modu tanımlanır.

32 bitlik veri sakladığını görüyoruz ancak her 2 bit bir pini kontrol ediyor. Bizim kontrol etmek istediğimiz pinler 15,14,13,12 olduğunu unutmayalım. Offset 0x00 imiş, yani D portunun başlangıç adresine olan uzaklığı 0. O halde GPIOD_MODER registerı adresimiz: 0x40020C00 ‘dır.

Ekstra: 1 bite sadece 2 adet veri saklayabiliriz ama 2 bite 4 adet veri saklayabiliriz. Solda görüyoruz ki 4 farklı kullanımı var. Bu da demek ki 4 veri saklayabilmemiz için 2 bit 2 bit şeklinde yapılmış.

Her neyse biz LED’lere enerji vereceğimiz için çıkış yapıyoruz ve yazacağımız veri de 01 olmalı.

4 pine 01 01 01 01 şeklinde veri yazıp diğerlerini 0 yaparsak. Elde edeceğimiz hex: 0x55000000 olur.

 

 

 

 

 

 

 


Sayfa 154 GPIOD_OTYPER register ile çıkış tipini ayarlamamız için 15,14,13,12. bitlerine 0 verisi yazmamız gerek. GPIOD_OTYPER adresi: 0x40020c04 ve yazacağımız veri ise 0x00000000 olmalı.

 


Şimdi pinlerin çıkış hızları ayarlanması gerek. Bunun için GPIO port output speed register‘ına bakalım.

 

Bunu frekansı ayarlayarak yapar. “00” 2 MHz ve altı, “01” 25 MHz ve altı, “10” 50 MHz ve altı, “11” 100 MHz ve altı olduğunu gösterir. Bizim için şuanlık yüksek hız pek de önemli değil. GPIOD_OSPEEDR adresi: 0x40020c08 ve yazacağımız veri ise 0x00000000


Input modundaki pinlerin dahili pull up/pull down dirençlerinin açılıp açılmadığını kontrol eden bir adet register’ımız bulunmakta. Sayfa 156’da buna ait gerekli açıklamaları da bulabilirsiniz.

Biz herhangi bir input girişiyle şuanlık işlem yapmadığımız için dahili dirençlere gerek yoktur. Bu nedenle bu adrese 0’ları yazacağız.

GPIOD_PUPDR adresimiz: 0x40020C0C ve yazacağımız veri 0x0 (yada 0x00000000) olacaktır.


Son olarak çıkışlara lojik seviyelere yükleyebileceğimiz register’a geldik. Bu register’ın herhangi bir bit’ini 1 yaparsak lojik-1, herhangi bir bit’ini 0 yaparsak lojik-0 çıkışı elde edeceğiz. Açma kapamayı yapan register gibi düşünebilirsiniz.

Bu registerin D portu başlangıç adresine (base’e) olan uzaklığı 0x14 olduğuna göre GPIOD_ODR adresimiz 0x40020C14 olacaktır. 1-0 yaptıkça ledlerimiz yanım sönecektir.

 

Şimdi tüm bu bilgiler ışığında programımızı yazalım. (Bknz: IAR’da yeni proje nasıl açılır? İlk ayarlar nasıl yapılır?)

 

/*
** Led Blinking - Stm32f411
** @author: Hakan Kaya
** @date : 2.11.2016
*/


// Adres Tanımlamaları
#define RCC_AHBENR *((unsigned int*)0x40023830U)  
#define GPIOD_BASE *((unsigned int*)0x40020C00U)

#define GPIOD_MODER *((unsigned int*)0x40020C00U)
#define GPIOD_OTYPER *((unsigned int*)0x40020C04U)
#define GPIOD_OSPEEDR *((unsigned int*)0x40020C08U)
#define GPIOD_PUPDR *((unsigned int*)0x40020C0CU)
#define GPIOD_ODR *((unsigned int*)0x40020C14U)

// Gecikme için zaman fonksiyonu
void delayzz(unsigned long zaman){  // İsaretsiz arguman girisi
 while(zaman > 0){                   // Koşullu döngü
 zaman--;                             // Her defasında bir azalt
 }
}

int main(void)
{
 RCC_AHBENR = 0x00000008;  // AHB1 Enable Register D portu için aktif
 
 GPIOD_MODER = 0x55000000;  // Pin modu tanimlamasi
 
 
 GPIOD_OTYPER = 0x0U;  // Cikis tipi tanımlamasi (push-pull/reset state)
 
 GPIOD_OSPEEDR = 0x0U;  // Cikis hizi tanımlaması (Low speed)
 
 GPIOD_PUPDR = 0x0U;   // Non pull-up-pull-down
 
 while(1){              // Sonsuz döngü
 GPIOD_ODR = 0xF000U;   // Lojik-1
 delayzz(2000000);   // Bekle
 GPIOD_ODR = 0x0000U;  // Lojik-0
 delayzz(2000000);  // Bekle
 }
 return 0;  
}


 

Video:

 

 

 

Exit mobile version