STM32 İlk Program (LED Yakma), Temel Giriş ve Çıkış ve TrueStudio Kullanımı

Önceki yazılarımızda STM32 mikrodenetleyici ailesine giriş yapmış fakat daha program yazma sürecine girmemiştik. Önceki yazılar ön bilgi niteliğinde olup sizi STM32 mikrodenetleyicilere hazırlamaya yönelikti. Eğer tüm yazıları okuduysanız güzel bir temel edindiğinizi söyleyebilirim. Ayrıca bu yazılarda kaynaklar hakkında da bilgi vermiştik. Şimdi TrueStudio’da ilk programımızı çalıştıralım ve genel program yapısına bakalım.

STM32 başlangıçta bize de oldukça karışık gelmekte. STM32 programlamak biraz üst seviye olduğu için Arduino bilmenin de bir faydası dokunmayabilir. Çünkü Arduino’da gördüğümüz hemen hiçbir şeyi burada kullanmayacağız. Hatta kütüphaneler Arduino kütüphaneleri gibi basit olmadığından anlamak için biraz tecrübe ve ileri seviye programlama bilgisi gerektiriyor. Eğer AVR öğrendiyseniz sizin için biraz daha kolay olsa da donanım bakımından öğrenmeniz gereken yine pek çok konu olacaktır. Aynı zamanda yazılımsal olarak AVR programlamak STM32’ye göre oldukça kolaydır. Fakat AVR programlamayı bilen birisine Arduino’da olduğu gibi hemen hemen her şey yabancı gelmeyecektir. Çoğu konuyu ve özellikle yazmaçları önceden kavradığımız için sıfırdan başlamak yerine mevcut bilgimizin üzerine ilave etmemiz gereklidir. O yüzden STM32’ye başlamak için C kitaplarını okuyup üzerinde çalışmak ve öncelikle AVR öğrenmek işinizi kolaylaştıracaktır. Bu alanda yeteneğinizi keşfettiğinize inanıyorsanız sizin için STM32 öğrenmek zor olmamalıdır.

Öncelikle TrueStudio’yu açıp yeni bir proje oluşturmamız gerekli. Bunun için seçmemiz gereken özel seçenekler olduğu için öncelikle proje oluşturmayı adım adım anlatacağız. Menüden New / C Project diyerek projemizi oluşturuyoruz. C dilinde program yazacağımız için bunu seçtik.

Sonrasında karşımıza çıkan ekranda Embedded C Project kısmını seçiyoruz ve projemize bir isim veriyoruz. Embedded C seçmemizin sebebi beraberinde kütüphane ve ayar dosyalarıyla beraber gelmesinden ve ARM derleyicisini bulundurmasından dolayıdır.

Bundan sonra karşımıza mikrodenetleyici ya da kart seçeceğimiz ekran geliyor. STM32F3 Discovery kullandığımızdan bu kartı seçiyoruz. Bu kartı seçmemizle bu karta ait ek dosyalar da gelecektir.

Bundan sonra bizi proje sayfası karşılıyor. Burada ana ekran main.c dosyasını bize göstermekte. Burada boş bir sayfa yerine pek çok ayar ve tanımların olduğu bir dosyayı görmekteyiz. Üstelik bunun beraberinde bir sürü kütüphane ve başlık dosyası projemize eklenmiş durumda. AVR programlarken karşımıza çıkan boş bir sayfayı görmemekteyiz. Aslında bize zor gelen de bu karmaşıklığı çözmektir.

Bundan önce ekranın sol kısmında yer alan Project Explorer penceresinden bahsetmemiz gerekir. Atmel Studio, Visual Studio tabanlı olduğu için ve TrueStudio Eclipse tabanlı olduğu için arayüz olarak farklıdır. Project Explorer kısmında projemize ait tüm dosyaları görmemiz mümkündür. Şimdi resmini verelim ve sonrasında açıklamasını yapalım.

Görüldüğü gibi bazı başlıklarda çeşitli dosya türleri yer almakta. Bunlardan “Binaries” olanı adı üstünde makine dilinde ve debug yaparken kullanacağımız program kodunu içermektedir. “Includes” ise kütüphaneleri ve başlık dosyalarını içerir. “Driver” sürücü dosyalarını ve “src” ise bizim “kaynak” dosyalarımızı içerir. Yani burada bizim program yazacağımız ana klasör src klasörüdür. Kaynak klasörüne baktığımızda pek çok dosya görmekteyiz. Bu dosyaların fazlalığı başlangıçta kafamızı karıştıracaktır.

Şimdilik diğer dosyalara dikkat etmemize gerek yoktur. Bu dosyalarda gerekli ön ayarlar zaten biz kartımızı seçerken yapılmıştır. Eğer kartı aynı şekilde kullanacaksak dokunmamıza gerek yoktur. Şimdi main.c programını açalım ve yazılım kısmına geçelim. Main dosyası oldukça uzun olduğu için parça parça incelemeye başlayacağız. Sonrasında ise açıklamaları kaldırıp sade bir görünüm elde edeceğiz.

Kodun başında gördüğümüz açıklama bloku aslında bir lisans dosyasıdır. Fark edeceğiniz üzere çeşitli kütüphanelerin ve kaynak kodlarının başında böyle lisans dosyaları bulunmaktadır. Aynı bir bilgisayar yazılımı yüklerken başta okuduğumuz ve onayladığımız lisans dosyası gibi buradaki şartları da kodu kullanırken okumuş ve onaylamış sayılırız. Bu programın açıklamasında GPIO ayaklarının STM32F3xx HAL API ile kullanımından bahsedilmektedir. Yani ana program aslında bir örnek kod ile karşımıza çıkmaktadır.

main.c olduğu gibi bir de main.h dosyası bulunmaktadır. Burada ana programımıza main.h dosyası eklenmiştir. Bu dosyada ne olduğuna bir bakalım.

Project Explorer’den baktığımız üzere bu dosyanın bomboş olduğunu görüyoruz. Sadece programımıza stm32fxx_hal.h adında HAL kütüphanesi başlık dosyası eklenmiş ve geri kalanı boş bırakılmış. Buraya biz tanımları, sabitleri, makroları ve fonksiyonları yazabiliriz. Şimdilik boş olan fakat programlarken ihtiyacımız olacak bir dosyayı görmekteyiz.

Bu aslında HAL kütüphanesine ait bir yapı tanımlamasıdır. Hal kütüphanesi Arduino kütüphaneleri gibi basit bir yapıda olmayıp C diinde olsa bile karmaşık veri yapılarına sahiptir. Bu karmaşık veri yapıları yüzünden Arduino veya AVR kütüphanesi gibi birkaç sabit ve fonksiyonla kullanacağımız bir kütüphane basitliğinde değildir. Bizim için zor olan noktalardan biri de bu HAL kütüphanesinin karmaşık veri yapılarını öğrenmek olacaktır. Burada GPIO_InitStruct adında bir yapı tanımlaması yaptık. Bu tip tanımlaması şeklindeki yapının içeriğinin ne olduğunu CTRL + Sol Tık ile üzerine tıklayarak görebiliriz. TrueStudio kullanırken başta unutmamamız gereken en önemli noktalardan biri bilmediğimiz bir yapı ya da fonksiyonun CTRL + Sol Tık ile kaynak kodunu ve beraberinde kod açıklamarını görmemizdir.

GPIO_InitTypeDef yapısının içeriğine bakmak için CTRL + Sol Tık uygulamasını GPIO_InitTypeDef yazısının üzerine yapıyoruz. Karşımıza yapının kaynak kodu çıkıyor.

Görüldüğü gibi bu yapı belli değerleri almakta ve bu değerler de enum yapısı ile sembolleştirilebilmektedir. Yine bu enum yapılarını HAL kütüphanesinin giriş ve çıkış kaynak kodu olan stm32f3xx_hal_gpio.h başlık dosyasından görebilirsiniz. Biz bu noktada şimdilik pek meşgul olmayalım ve main.c dosyası üzerinde ilerleyelim.

Burada SystemClock_Config adında bir fonksiyon prototipinin olduğunu görüyoruz. Bu fonksiyon kartımızın saat değerine ait ayarları yapacağından dokunmamakta fayda vardır. Error_handler de aynı zamanda bizim yazacağımız programı ilgilendirmeyen fakat sistemi ilgilendiren bir fonksiyon olduğu için buna da dokunmadan devam ediyoruz. Dikkatinizi çeken bir başka nokta da bazı açıklama satırlarıdır. Kullanıcı için hazır tanımlanan açıklama satırları aynı bizim yaptığımız gibi programı sanal olarak parçalara bölmektedir. Bunları kalabalık olmasın diye silebiliriz.

Şimdi ana program yapımıza bakalım. Burada da bazı açıklamaları görmekteyiz. Sizin için inceleyelim.

Ana program yine bildiğimiz gibi başlıyor ve açıklamalarda hazır program hakkında bilgiler yer almakta. Ayrıca hal kütüphanesinin başlatılmasına dair bilgiler yer almakta. Şimdi hal kütüphanesinin nasıl başlatıldığını görelim.

Bu fonksiyon ile hal kütüphanesi başlatılıyor ve kullanıma hazır hale geliyor. Bunun ayrıntılı yapısını yine CTRL + Sol Tık ile görmeniz mümkündür.

Burada ise SystemClock_Config fonksiyonunun çalıştırıldığını görmekteyiz. Bu fonksiyon sistemin saat ayarlarını yapmaktadır. Saat ayarları AVR’de olduğu gibi basit değildir ve anlatılması için ayrı bir konu gereklidir.

GPIO kullanmak için ilgili GPIO biriminin saatini açmamız gereklidir. Sistemimiz AVR’de olduğu gibi otomatik sistem saatinden beslenen bir yapıdan çok her yapının beslendiği saat  bulunup bunlar güç tüketimi açısından kapalı haldedir. O yüzden ADC, SPI, USART, GPIO gibi birimlerin öncelikle saatini etkinleştirmemiz gereklidir. Saat sinyali gidince o birimler çalışmaya başlayacaktır. Bu fonksiyonun aslında bir makro olduğunu ve şu komutu çalıştırdığını görmekteyiz.

RCC -> AHBENR |= 0x00200000;  yazmakla aynı anlama geldiğini görüyoruz çünkü RCC_AHBENR_GPIOEEN adı 0x00200000 olarak tanımlanmıştır. Onu da şu satırdan görebiliriz.

HAL kütüphanesinin donanım soyutlama katmanı olarak adlandırılmasının sebebini buradan anlamış olmanız lazımdır. Görüldüğü gibi yazmaç, bit ve değerlerle uğraşmak yerine fonksiyonlarla, yapılarla ve sabit değerlerle uğraşıyoruz. Donanıma ait her bir konu her bir bit ve özellik bir fonksiyon, tanım ya da sabit olarak tanımlanmıştır. 0x00200000 değerinin ikilik olarak “0000 0000 0010 0000 0000 0000 0000 0000” değerine geldiğini söylememiz gerekir. 8-bit AVR denetleyicilerde çalışırken bu on altılık ve ikilik sayılar pek sorun olmuyordu. Çünkü 8-bit oldukça küçük bir veri yapısıydı ve rahatça işlem yapabiliyorduk. Burada ise 32 bitten bahsetmekteyiz ve bit bazlı işlemler oldukça kafa karıştırıcı olabilmekte.

Burada temel giriş ve çıkış ayaklarının tanımlarının yapıldığını görmekteyiz. AVR’de DDRx yazmacına attığımız değerle bu kolayca belirlenirken burada iş oldukça karmaşık bir hal almış durumda. Kütüphane değil de yazmaç tabanlı işlem yapsak da AVR’de olduğu gibi basit değil. Burada yukarıda verdiğimiz InitStruct yapısına ait Pin, Mode, Pull ve Speed değerlerine atama yapıldığını görmekteyiz. GPIO_PIN_15 dediğimizde yazmacın 15 numaralı bitine denk gelen ayak anlaşılmalıdır. Aynı AVR’de PB5 dediğimizde 5 numaralı bite denk gelmesi gibi burada da belli bir sabite binary değeri tanımlanmıştır. Şimdi GPIO_PIN_15’in aslında ne olduğuna bakalım.

0x8000 değerini ikilik değere çevirdiğimizde (0000 0000 1000 0000 0000 0000) 15 numaralı bitin yani 16. bitin “1” olduğunu görüyoruz.  Bunu da yine stm32fxxx_hal_gpio.h dosyası içerisinde görmemiz mümkündür. Pin, Mode, Pull ve Speed değerlerinin uint32_t  tipinde birer değişken olduğunu unutmayalım. Yani bu değişkenlere attığımız değerler sonrasında ilgili PORT yazmaçlarına atılacaktır. RM0316 numaralı referans kılavuzunu üreticinin sayfasından indirip bu yazmaçlara siz de bakabilirsiniz. Şimdi yazmaçları yazımıza dahil etmek istemiyoruz. Şimdi diğer değer atamalarına bakalım ve GPIO yapısını tam şekliyle anlayalım.

Burada giriş ve çıkış yapısının çıkış olacağı ve push-pull şeklinde çıkış alınacağı anlatılmış. Open drain ve push-pull olarak iki farklı çıkış tipi elimizde vardır. Bunları sonra anlatacağız. Diğer GPIO modlarını ise stm32f3xx_hal_gpio.h dosyasında açıklamaları ile beraber görmekteyiz.

Burada her sabitin bir hex değerine tanımlandığını görebilirsiniz. Ayrıca kaynak kodunun aynı zamanda bir kütüphane referansı niteliğini taşıdığını söylememiz mümkündür. Burada kullanabileceğiniz bütün GPIO yani temel giriş ve çıkış modları görülmektedir.

AVR’de sadece dahili pull-up işlevinden söz etmemiz mümkündü. Burada ise pull-up, pull-down ve no-pull olarak üç adet pull işlemimiz bulunuyor. Hazır kodda pull-up olarak tanımlandığı için buna dokunmayacağız. Pull değerine üç farklı değer atamamız mümkündür çünkü kaynak kodunda böyle belirtilmektedir.

En son olarak hız ayarımızın bulunduğu Speed değişkenine değer atayacağız.

Biz temel giriş ve çıkış birimlerinin hızını ayarlayabiliriz. AVR’de olduğu gibi işlemcinin saat hızına bağlı olmayıp farklı bir saat hızında çalıştıkları yani aslında işlemcinin gelişmiş bir saat donanımı olduğu için bu temel giriş ve çıkış birimlerinin hızını değiştirmemiz işlemci hızını değiştirmeyecektir. Hızını artırıp azaltmamız ise güç tüketimine bağlıdır bu yüzden HIGH yapıyoruz. Diğer modları ise kaynak dosyasında şöyle görmemiz mümkündür.

Böylelikle GPIO_InitTypeDef yapısına ait tip tanımlamasına göre GPIO_InitStruct olarak tanımladığımız yapı değişkenine ait tüm değerleri ve parametreleri görmüş olduk. Şimdi bu değişkene ait konfigürasyon değerlerini mikrodenetleyicinin yazmaçlarına yüklememiz gerekli. Bunun için ise HAL_GPIO_Init fonksiyonunu kullanmaktayız.

Burada GPIOE argümanı port adı olup GPIOx yani x yerine herhangi bir harf değeri alabilir. Örneğin A portu ise GPIOA değerini kullanırız. STM32f303xc.h başlık dosyasında ise kullanabileceğimiz GPIOx değişkenleri yazılıdır.

Bu başlık dosyası bizim kullandığımız mikrodenetleyicinin donanım özelliklerine ait tanımlamaları içerir. Aynı AVR’de PORTx, DDRx, PINx tanımlamalarının olduğu mikrodenetleyici başlık dosyası gibidir. Şimdi while(1) içindeki programı silelim ve kendi programımızı yazalım. Biz sadece bir adet ledi yakıp söndüren bir program yazacağız.

Burada hazır olarak gördüğümüz iki fonksiyon yer almaktadır. Bunlardan biri HAL_GPIO_TogglePin fonksiyonu öteki ise HAL_Delay fonksiyoınudur. Program tekrarlayan biçimde şu iki fonksiyonu kullanmaktadır.

Bu iki fonksiyonun işlevini adlarından da öğrenebiliriz. Bir tanesi yakıp söndürme (toggle) özelliği öteki ise delay (bekletme) özelliğine sahip. Aldıkları parametreleri öğrendikten sonra istediğimiz gibi kullanma imkanımız olur. Önceden nasıl ayak tanımlaması yapacağını öğrendik şimdi ise sadece tanımladığımız ayakları bu fonksiyonlarla doğru bir şekilde kullanacağız.

HAL_GPIO_TogglePin fonksiyonunun parametrelerinin şu şekilde olduğunu görüyoruz.

Yani bir adet GPIOx argümanını başta almakta ve sonrasında bizim kullandığımız GPIO_Pin değerini uint16_t yani işaretsiz 16 bitlik tam sayı olarak almaktadır. Bu durumda buraya ikilik ya da onaltılık olarak da değer yazabilsek de önceden tanımlanmış GPIO_PIN_x ( x = 0-15) değerini yazabiliriz. Bu tanımlamayı yukarıda bahsetmiştik.

GPIOx dediğimiz ise yine önceden bahsettiğimiz tanımlanan GPIO tiplerinden biri olabilir. Burada A-F arası harfler olduğunu görüyoruz. Bu durumda fonksiyonun örnek söz dizimi şöyle olabilir.

Bu fonksiyonun nasıl çalıştığını ise yine kaynak kodundan görmekteyiz. Aslında bu fonksiyon AVR’de kullandığımız PORT yazmaçlarına değer atamaktan veya Arduino’daki digitalWrite fonksiyonundan farklı bir iş yapmamakta. Tabi ki Arduino gibi ucuz bir kütüphanede ve AVR gibi bir sistemde bu giriş ve çıkış işlemi oldukça performanssız gerçekleşse de HAL kütüphanesinde bunu pek dert etmek gerekmez. Şekilde görüldüğü gibi tek bir komutla alınan değerlere göre ODR yazmacına (Output Data Register) değerler yazılmaktadır.

HAL_GPIO_TogglePin  fonksiyonunu yeteri kadar anladıysak şimdi de HAL_Delay fonksiyonuna geçelim. Bu iki fonksiyonu anlamadan ezbere kod yazarak öğrendiğimizi iddia edemeyiz. Eğer ezbere kod yazarak bir şeyler yapmak istiyorsak STM32 ile hiç zahmete girmemize gerek yok. Arduino hazır kodu da ezbere kodu da kopyala yapıştır yöntemini de zirveye çıkarmış durumdadır.

Şimdi HAL_Delay fonksiyonuna bakalım.

Görüldüğü gibi Arduino’daki delay fonksiyonuna benzer bir şekilde fonksiyon ilk başladığında alınan süreyi geçen süreyle kıyaslayıp programı sonsuz döngüye sokarak meşgul etmektedir. Aldığı argüman uint32_t büyüklüğünde olup mili saniye değeridir. Bu delay fonksiyonu SysTick adına donanımsal bir zamanlayıcıyı referans almaktadır. HAL/LL Referans kılavuzunda ise bu fonksiyondan bahsedilmektedir. Sadece kaynak dosyalarını inceleyerek öğrenmek zorunda değiliz.

Burada fonksiyonun ne işe yaradığı, adı, geri döndürdüğü değerler ve aldığı parametreler daha iyi açıklandığı için kütüphane referans kıavuzunu okumayı ihmal etmemek gereklidir.

Şimdi iki fonksiyonu da öğrenmiş olduk. HAL_GPIO_TogglePin bir ayağı açıp ya da kapatıyor. HAL_Delay() ise mili saniye bazında bekleme yapıyor. Şimdi tek bir ledi yakıp söndürecek kodumuzu yazalım.

Kodumuz bu kadar sade ve basit bir hale geldi. E portunun 15 numaralı ayağı 500 mili saniye aralıklarla yanıp sönecek. Fakat öncesinde derleme işlemini yapmamız gereklidir. Bunun için resimdeki “Build” düğmesine tıklıyoruz.

Eğer bir hata olmadıysa alt taraftaki konsol ekranından derlemenin başarı ile gerçekleştiğine dair bir mesaj almamız gereklidir.

Artık derleme başarı ile gerçekleştiyse böcek ile simgelenen debug düğmesine tıklayarak hata ayıklama moduna geçiyoruz. Böylelikle yazdığımız programı deneme imkanına sahip oluruz ve aynı zamanda hata ayıklama özelliklerini kullanabiliriz.

Debug ekranında pek çok özellik olsa da şimdilik ilgileneceğimiz tek bir düğme var. Debug moduna girince STM32F3 Discovery kartınaki programlayıcı ledi yeşil ve kırmızı olarak yanıp sönmeye başlayacaktır. Şu düğmeye tıklayarak programı başlatalım.

Debug işlemi başlayınca kartımızda program çalışacak ve led yanıp sönmeye başlayacaktır. Aşağıdaki videodan göreceğiniz üzere STM32 ile ilk programımızı yapmış olduk.  Bu ilk program bazı Youtube derslerinde olduğu gibi ezbere kod yazıp çalıştırmaktan öte size nasıl kod yazılacağını, fonksiyonların nasıl kullanılacağını ve nasıl programlama yapılacağını öğretti. Bunu “şu kodu yazıyoruz sonra şuraya tıklıyoruz” diyerek çok daha kolay bir şekilde anlatabilirdik fakat sizin bir kazanımınız olmazdı. O yüzden bu yazıda anlatılanların hepsini anlamaya çalışmanız gereklidir.

 

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. Mustys dedi ki:

    Tebrikler.sozunuz meselenin ozune dokunmakta.takipteyiz…

  2. Mehmet Osman BURSALIOĞLU dedi ki:

    62 yaşında Mekatronik Yüksek Lisans öğrencisiyim, arayıp da bulamadığım bilgiyi sizin sayfanızda buldum, çok teşekkür ederim, emeğinize bereket, aklınız ve iyi niyetinizle çok yaşayın. Selam ve sevgilerimle…

Bir cevap yazın

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