STM32 ile ARM Programlama – Yazmaç Kullanarak Giriş/Çıkış ve LED Yakma

Önceki makalemizde temel giriş ve çıkış yazmaçlarını anlatmıştık. Şimdi bu yazmaçların bazılarını kısaca anlatalım ve bir programlama örneği verelim. Yazmaçların ne olduğunu, nasıl çalıştığını ve nasıl programlandığını C ile AVR Programlama derslerimizde okuyacağınız gibi kendi yazdığım ücretsiz kitap olan C ile AVR Programlama ve Gömülü Sistemlere Giriş elektronik kitabında da görebilirsiniz. Yazmaçların yapısı ve C dilindeki bitwise operatörler ile programlanması ayrıntılı olarak anlatılmıştır. Kitaba buradan erişebilirsiniz.

HAL kütüphanesi kullanırken temel giriş ve çıkış özelliğine dair 6 noktayı muhakkak kullandığımızı görebilirsiniz. Bunlardan dördü tanımlama ve ayar sürecinde ikisi ise uygulamada kullanılmaktadır. Ayarlamak için giriş ve çıkış modu, çıkış tipi, hız oranı ve pull-up ya da pull-down durumu olarak dört ayrı ayar yapılır. Uygulamada ise giriş (input)  ve çıkış (output) olmak üzere iki ayrı özellik kullanılır. Bu çıkışın ve girişin değeri veri seviyesinde olduğu için bunları veri (data) olarak değerlendiririz.

Bu durumda HAL kütüphanesinde olduğu gibi yapmamız gereken belli başlı adımlar vardır. Bunları madde madde sıralayalım.

  • Giriş/Çıkış modu belirlenir. Giriş mi çıkış mı olacağı kararlaştırılır.
  • Çıkış tipi Belirlenir
  • Hız oranı belirlenir.
  • Pull-up/down modu belirlenir.
  • Uygulamada giriş ve çıkış veri okuma ve yazması yapılır.

Kısacası öncelikle bir ön ayar (konfigürasyon) sonrasında ise veri okuma ve yazma yaparız. Aynı AVR veya Arduino’da olduğu gibi öncelikle başta ayar ve tanımlama yapmamız gereklidir. Veri okuma ve yazma işini sonrasında nerede yaptığımız önemli değildir. Programın akışına göre ihtiyaç duyduğumuz noktada yapsak da başta ayarlama yapmak şarttır.

Demek ki HAL kütüphanesinde yaptığımız işin aynısını yapmaktan başka bir çaremiz yoktur. HAL kütüphanesinde ne yazmaçla, ne bitle ne de sayılarla uğraşmadan neredeyse konuşma dilinde kod yazıyorduk. Şimdi bütün bu zahmete girerek aynı kodu yazacağız. Bunun sebebi geleceğe yatırım yapmak istememizdendir. Eğer ilerlemek istiyorsak bir gün muhakkak karşımıza yazmaç ve bitler gelecektir. Bu noktada HAL kütüphanesi bana yeter diyecek adam genellikle başta Arduino bana yeter diyerek zaten hiç ilerleme kaydedemediği için bu noktaya gelenler arasında bu konuda pek tartışma olacağını sanmıyorum. HAL kütüphanesi bana  yeter derseniz bir noktadan sonra durur ve hiç ilerleme kaydedemezsiniz. HAL kütüphanesinin de bir sınırı vardır.

Yazmaç tabanlı alt seviye bir programlama yapıyorsak madde madde belirttiğimiz işlere yönelik yazmaçları tespit ederek işe başlarız. Bunu da referans kılavuzunu kullanarak yaparız. Şimdi yaptığımız iş için bize lazım olan yazmaçların adlarını tespit edelim. Bunun için RM0316 adlı referans kılavuzunda aradığımız özelliğin başlığını buluyoruz ve ardından yazmaç alt başlığına bakıyoruz.

Sayfa bu şekilde karşımıza çıkıyor. 1141 sayfalık kılavuzu baştan sonra okumamıza gerek yok. Şu an önemli olan yapacağımız işe yönelik bilgiyi elde etmemizdir. Şimdi kullanacağımız yazmaçların adlarını tespit edelim. Yukarıda belirttiğimiz işlemlere karşılık gelen yazmaçlar şu şekildedir.

  • MODER : Giriş/Çıkış modu belirlenir. Giriş mi çıkış mı olacağı kararlaştırılır.
  • OTYPER : Çıkış tipi Belirlenir
  • OSPEEDR : Hız oranı belirlenir.
  • PUPDR : Pull-up/down modu belirlenir.
  • ODR / IDR Uygulamada giriş ve çıkış veri okuma ve yazması yapılır.

Yazmaçların adlarını tespit ettik fakat adlarını bilmekle nasıl kullanılacaklarını öğrenme şansımız yoktur. Yazmaçların adlarından ya da önceki başlıklardan ne işe yaradığını öğrensek de bunların nasıl kullanılacağı konusunda hiçbir fikrimiz yok. O yüzden tablodan ve açıklamalardan her yazmacın her bitinin ne işe yaradığını öğrenmemiz gereklidir. Bu aşamada AVR derslerini takip ettiyseniz size oldukça kolay ve anlaşılır gelecektir. Çünkü diğer içeriklerden farklı olarak aslında nasıl datasheet okunacağını da size öğretmiş olduk. Şimdi kullanacağımız yazmaçların yapısına ve bitlerine sırayla bakalım.

Yazmaçları incelerken makalenin sonunda STM32F3 Discovery kartıyla yanıp sönen led (blink) uygulaması yapacağımıza da belirtmemiz gerekir. O yüzden yaptığımız uygulamaya yönelik açıklamalarda bulunacağız.

GPIO Port Modu Yazmacı (GPIOx_MODER) (x =A..H)

Burada gördüğünüz MODERx adlı her ikilik bit bölümü bir adet ayağa denk gelmektedir. Yani buradan temel giriş ve çıkış portlarının 16 bit olduğunu görebilirsiniz. Ayaklar 0-16 arası numaralandırılırken portlar AVR’de olduğu gibi harfle ve A..H arası adlandırılır. Bu yazmaç aynı AVR’deki DDRx yazmacı gibi her bir ayağın giriş mi çıkış mı olacağını belirler. MODER15 ile belirtilen ayak 15 numaralı ayak olmaktadır. Her bir ayağa iki bitlik veri yazarız ve toplamda 16 ayak için 32 bit veri yazılmış olur. Bunu tabi ki bir (1) ve sıfır (0) ile yazmamız lazımdır. Bu durumda 32 adet bir ve sıfırı yan yana yazacağımız gibi on altılık sekiz adet rakama çevirip bunları yan yana yazabiliriz. Bir ve sıfır şeklinde yazmak oldukça amatörce olduğu için on altılık dönüşümü yapıp yazmanız gereklidir. İkilik ve on altılık dönüşüm tablosunu bir kenarda bulundurmanızda fayda var. Sonrasında ezbere dönüşümü yapabilirsiniz. İki sayı sistemi de ikinin katları olduğu için birbiri arasında dönüşüm yapmak tabloyu ezberlemek kadar kolaydır.

Her bir ikilik bitin dört farklı özelliğinin olduğunu görüyoruz. Yani her bir ayak için burada dört ayrı mod tanımlayabiliriz.

MODERy [1:0] Port x Ön ayar bitleri. 
Bu bitler yazılım (programcı) tarafından giriş ve çıkış modunu belirlemek için kullanılır.
00 : Giriş Modu (Reset (0) durumunda)
01 : Genel maksatlı çıkış modu 
10 : Alternatif fonksiyon modu
11 : Analog Modu 

Led yakma uygulaması için genel maksatlı çıkış moduna almamız gereklidir. O yüzden kullanacağımız ayaklara denk gelen bitleri 01 yapmamız gerekir. Bunu bir kenara not alıyoruz.

GPIO Port Çıkış Tipi Yazmacı (GPIOx_OTYPER) (x = A..H)

Bu yazmacın 16 bitlik yazmaç olduğunu görüyoruz. Aslında 32 bit olsa da 16 bitlik üst kısmı rezerve edilmiş durumdadır. O yüzden pratikte 16 bit olarak görev yapmaktadır.   Yani her ayak için bir bir tanımlanmış ve numaralandırılmıştır. 0 ve 16 arası bitlerin her numarası bir ayak numarasına karşılık gelmektedir. Mesela PE3 adlı ayağa OT3 adlı bit karşılık gelmektedir. Bu yazmaca ait bitlerin ne iş yaptığını da bu tablonun altındaki yazıdan görmekteyiz.

Bits 31:16  Rezerve, sıfır konumunda kalmalıdır.
Bits 15:0 OTy: Port x konfigürasyon bitleri (y = 0..15)
Çıkış tipini yazılım ile belirleyen bitlerdir.
0: Çıkış push-pull (standart olarak sıfır durumundadır (reset) )
1: Çıkış open-drain

Led yakmak için Push-pull (standart) çıkış elde edeceğimiz için kullanacağımız ayaklara denk gelen bitleri sıfır yapmamız gereklidir. Bunu da bir kenara not alıyoruz.

GPIO PORT Çıkış Hızı Yazmacı (GPIOx_OSPEEDR)
(x = A..H)

Önceden söylediğimiz gibi STM32 mikrodenetleyiciler gelişmiş bir saat yapısına sahip olduğu için her bir birimin işlemci saatinden bağımsız ayrı saatleri olabiliyordu. AVR mikrodenetleyicilerde giriş ve çıkış birimi işlemcinin saatini referans alsa da burada giriş ve çıkış biriminin saat sinyali ayrı olduğu için hız modlarını seçme şansımız vardır. Bu modlar giriş ve çıkışın ne kadar hızlı olacağını belirler. Her zaman hızlı olmasını istesek de güç tüketimi söz konusu olduğunda yavaşlatma ihtiyacı duyabiliriz.

Bits 2y+1:2y OSPEEDRy[1:0]: Port x konfigürasyon bitleri (y = 0..15)
Bu bitler yazılım tarafından giriş ve çıkış hızını ayarlamakta kullanılır.
x0: Düşük Hız
01: Orta Hız
11: Yüksek Hız

LED yakma uygulamamızda bu değerlerin bir önemi olmasa da biz standart olarak yüksek hızda kullanmayı alışmak adına yüksek hız tercihini yapacağız. Bunun için her bir ayağa denk gelen bite 11 değerini yazmamız gerekecek.

GPIO port pull-up/pull-down yazmacı (GPIOx_PUPDR)
(x = A..H)

Bu yazmaç dahili pull-up ve pull-down dirençlerini etkinleştirmeye veya devre dışı bırakmaya yarayan yazmaçtır. AVR programlarken bunu dolaylı olarak PORT yazmacıyla yapmaktaydık. Burada ise bu özellik için ayrı bir yazmaç belirlenmiştir. Ayrıca pull-up özelliğinin yanı sıra pull-down özelliği de gelmiştir. Bu bitlerin ise ne yaptığını yine kılavuzdan öğrenmekteyiz.

Bits 2y+1:2y PUPDRy[1:0]: Port x konfigürasyon bitleri (y = 0..15)
Bu bitler pull-up ve pull-down özelliğini ayarlamak için kullanılır.
00: Pull-up/down devre dışı
01: Pull-up
10: Pull-down
11: Rezerve

Biz led yakma uygulamasında pull-up veya pull-down özelliklerini kullanmayacağımız için her bir ayağa denk gelen bitleri 00 yapmalıyız.

GPIO Port Çıkış Veri Yazmacı (GPIOx_ODR) (x = A..H)

Bu yazmaç ise led yakmak için kullanacağımız son yazmaçtır. Bit bazlı işlem için farklı yazmaçlar olsa da şimdilik işi en basit yönüyle ele alalım. Bu yazmacın 16 bitlik olduğunu görmekteyiz. Yani her bir ayağa karşılık bir bit gelmektedir. Örneğin PE15 ayağını bir (1) yapmak için GPIOE’nin ODR yazmacının ODR15 ayağını bir (1) yapmamız gerekir. Her bir biti bir yaparsak ayaklar bir olur sıfır yaparsak da ayaklar sıfırlanır. Bu aynı AVR’deki PORT yazmacı gibidir. Yine kılavuzdan bu yazmacın bu işe yaradığını öğrenmekteyiz.

 

Şimdi yazmaçları ve bitleri kullanarak STM32F3 Discovery kartı üzerinde bulunan ledleri yakalım. Fakat öncesinde bu ledlerin hangi ayağa ve GPIO portuna bağlı olduğunu bulmamız gereklidir. Bunu da kullanma kılavuzundaki devre şemasından görebiliriz.

Devre şemasında PE8 ve PE15 arasındaki ayaklara ledlerin bağlı olduğunu görüyoruz. Bizim kullanacağımız GPIO birimi E olmalı ve kullanacağımız bitler yani ayaklar 15, 14, 13, 12, 11, 10, 9 ve 8 numaralı bitler olmalı. Bunu da bir kenara kaydederek programımızı yazmaya başlayalım. Öncelikle boş bir proje açmamız gerekli. STM32F3 Discovery için hazırladığım örnek boş projeyi aşağıdaki bağlantıdan indirebilirsiniz.

https://drive.google.com/file/d/1ZUyHU2XKVc4qUjjrUGn58f9wLYF-Qs_j/view?usp=sharing

Şimdi ana programımızı buraya yazalım ve sonrasında her zaman olduğu gibi komutları satır satır açıklayalım.

Eğer önceden yazmaçları öğrenmemiş ve ayarlardan bihaber olsaydınız bu program sizin için tamamen anlamsız olacaktı. Burada ODR ne demek, MODER ne işe yarıyor artık bildiğiniz için anlamanız da oldukça kolaydır.

__GPIOE_CLK_ENABLE();   GPIOE biriminin çalışması için muhakkak bu fonksiyonu kullanmanız gereklidir. Bu saat ile alakalı olduğu için HAL kütüphanesini burada kullandık. Hem HAL kütüphanesini hem de yazmaçları kullanarak hem kolay hem de yeri geldiğinde güçlü bir program yazabilirsiniz.

GPIOE -> MODER |= 0x55550000;  Burada 15-8 arası ayakları kullanacağımız için ilk 16 bitle bir işimiz vardır. Diğer bitlere dokunmak istemediğimiz için maskeleme operatörünü kullandık. Bunları AVR derslerinde anlattığım için tekrar tekrar anlatmak istemiyorum. O yüzden bu operatörlerin nasıl kullanıldığını o derslerde görebilirsiniz. Biz çıkış tanımlamak istediğimiz için 01 yazmak durumundaydık. O halde 15-8 arası sürekli 01 yazmamız gerekecektir. Bu da 16 bit boyunca 0101010101… diye gidecektir. Bunu bir ve sıfır yerine yazmak yerine hiç olmazsa çevirici program vasıtasıyla çevirip yazmamız daha iyi olacaktır. Burada da 16 bitlik ilk kısmı 0x5555 yazdık ve devamını sıfır ile tamamladık.

GPIOE -> OTYPER = 0x00000000;  Kullanacağımız ayaklar push-pull özelliğinde olacağı için hepsini sıfır yaptık. Burada birimin yazmacına değer atamak için ok operatörünün kullanıldığına dikkat ediniz.

GPIOE -> OSPEEDR = 0xFFFFFFFF;  En yüksek hızı tanımlamak için bütün bitleri 1 yaptık. Bu durumda 32 bit için on altılık tabanda 8 adet F yazmak gerekli. Her dört bit sırayla bir rakama denk gelmektedir. Ayrıca bu ayarlamanın program döngüsünde gerçekleşmediğine dikkat ediniz. main fonksiyonunun döngü dışı kısmına yazmaktayız. İlk olarak bu kodlar yürütülecek ve bir defalığına yürütülecektir.

GPIOE -> PUPDR = 0x00000000;  Pull-up veya pull-down ile işimiz olmadığı için tüm bitleri sıfır yapıyoruz. Neden sıfır yaptığımızı yazmaçları açıklarken anlattık.

GPIOE -> ODR = 0xFF00;  Bu komut ile ODR yazmacının ilk 8 biti bir yapılır. 16 bitlik bir yazmaç olduğu için 4 basamak kullanmamız yeterli olmaktadır. Bu komut ile kart üzerindeki bütün ledler yanacaktır.

GPIOE -> ODR = 0x0000;  Bekleme komutunun ardından çıkış veri yazmacının tüm bitlerini sıfır yapıyoruz. Böylelikle karta bağlı olan ledler sönecektir. Sonrasında beklemenin ardından tekrar yakarak programımız sonsuz döngüde devam ediyor.

Programın çalışma videosunu aşağıdan izleyebilirsiniz.

 

Bizi Facebook grubumuzda takip etmeyi unutmayın. Bilgili ve öğrenmeye hevesli bir topluluk oluşturmak istiyoruz.

https://www.facebook.com/groups/1233336523490761/

UYARI!!

 

Gökhan Dökmetaş

"Arduino Eğitim Kitabı" ve "Arduino ve Raspberry PI ile Nesnelerin İnterneti" kitaplarının yazarı. Başkent Teknoloji ve Dedektör Merkezi'nde Ar-ge Sorumlusu. Araştırmacı-Yazar.

You may also like...

2 Responses

  1. Umut dedi ki:

    Merhaba eline sağlık güzel blog yazıyorsun. Devamını sağlarsan başarılı uzun vadede başarılır olacağını inanıyorum. Bir Yazılım Mühendisi olarak elektronikle geçen yıl bir firmada tanıştım. Embedded Systems benim daha çok ilgilendiğim kısım. STM32 Cube ü mac sürümünü bulamadığım için inceleme fırsatım olmadı.

  2. Muhammed dedi ki:

    Selamun Aleykum. Stm32f4 de timer i ne yaptıysam 400khz e ayarlayamadim. Hal kütüphanesi kullaniyorum. Prescaler ayarlarken bir yerden sonra değişmiyor değer. Timer 2 kullaniyorum bu arada. Advanced işler için tim1 ve tim8 kullaniliyor galiba. Ama onlarida ayarlayamadim. Lutfen timer kullanimi ile alakali ileri seviye bir video ve kod paylasimi yapar misiniz?

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.