Sayfanın PDF versiyonu: STM32 ile Gömülü Yazılım Geliştirme - 1(.pdf)
STM32 ile Gömülü Yazılım Geliştirme - 1
STM32F10X ile Gömülü Yazılım Geliştirme konusuna hızlı bir giriş yapacağız, gerekli donanımlar ve geliştirme ortamının kurulumu ile başlayıp, basit bir uygulama ile konuyu noktalayacağız.
Neden STM?
Öncelikle temini kolay, deneme kartları ucuz ve konu ile ilgili bilgi alabileceğiniz kaynaklar çeşitli, örnek uygulamalara kolayca erişilebiliyor. Ayrıca, asıl sorunu bilmememe rağmen, STM32’nin ESD ve gerilim dalgalanmalarına karşı diğer firmaların ARM tabanlı mikrodenetleyicilerinden daha dayanıklı olduğunu düşünüyorum.
Diğer firmaların ismini vermeyelim, cevap hakkı doğmasın:)
Gerekli Donanımlar
ARM tabanlı bir mikrodenetleyici ile çalışacaksanız, bir deneme kartı edinmelisiniz. Atmel AVR veya Microchip PIC gibi DIP paketli versiyonları bulunmadığından hazır deneme kartlarından birini tercih etmelisiniz.
STM32F100 mikrodenetleyicisi için deneme kartı aşağıdaki linktedir;
stm32vldiscovery.
Bu deneme kartını yurtiçinden çeşitli firmalardan temin edebilirsiniz. Örneğin; Elektronik Tasarım dan.
Kart üzerinde ST-Link debugger da bulunduğundan ayrıca bir debugger almanıza ve debugger ile mikrodenetleyici arasındaki bağlantıları yapmanıza gerek kalmayacaktır.


Alternatif olarak STM32F103 ile de çalışabilirsiniz. Bunun için deneme kartları aşağıdaki linklerde mevcut.
STM32F103C8T6 Mini 1
STM32F103C8T6 Mini 2
STM32F103C8T6 Mini 3
Fakat bu durumda ST-Link V2 Debugger veya daha uygun fiyatlı olan ST-Link V2 klonu da temin etmek durumundasınız. Daha sonra, ST-Link V2 debugger ile board arasındaki bağlantıları aşağıdaki gibi yapmalısınız.


Geliştirme Ortamının Kurulumu
Kod editoru olarak Eclipse tabanlı CoIDE, derleyici olarak GCC kullanacağız.
(Web sitesine ulaşılamıyor sanıyorum. Buradan 1.7.8 versiyonunu indirebilirsiniz)
Buna alternatif olabilecek, System Workbench ve STM32CubeMX de kullanılabilir fakat bu durumda başlangıçta iyi olarak görünen otomatik kod üretme özelliği, öğrenmeyi zorlaştırmaktadır. Benim tavsiyem CoIDE ile başlamanızdır.
Debugger
Debugger için bilgisayarınıza bir sürücü kurmalısınız. Sürücüyü aşağıdaki adresten indirebilirsiniz;
STLink Driver

Sürücüyü kurduktan sonra STM32VLDiscovery kartınızı veya debugger’ı bilgisayarınıza bağladığınızda, aygıt yöneticisinde “STLink” görünecektir.
Derleyici
Standart GCC derleyicisini (Toolchain diyoruz) kullanacağız. Bu derleyiciyi aşağıdaki adresten edinebilirsiniz;
gnu-toolchain
Kurulum aşamasında standart ayarları kullanırsanız derleyiciniz,

"C:\Program Files (x86)\GNU Tools ARM Embedded\7 2017-q4-major"
klasorüne kurulacaktır. Bu önemli çünkü, bu klasörü IDE mize de bildirmemiz gerekecek.
IDE
CoIDE aşağıdaki adresten indirilebilir;
CoIDE
IDE kurulumunu yaptıktan sonra derleyicimizin nerede olduğunu IDE ye bildirmeliyiz. Bunun için
Project→Select Toolchain Path


Daha sonra açılan pencereden derleyiciyi kurduğumuz klasörü seçmeliyiz.

Artık ilk projemiz için hazırız.
İlk Projemiz
CoIDE ile ilk projemizi oluşturalım. Bunun için,
Project→New Project
ve açılan ekran ile projemizin adını yazıp “Next” diyoruz.

Bu aşamada “hazır bir board” (BOARD) ve “sadece mikrodenetleyici” (Chip) seçeneklerinden birini seçmemiz istenecek. “Chip” i seçiyoruz.

Şimdi bizden kullanacağımız mikrodenetleyiciyi seçmemiz istenecek.

Kullandığınız mikrodenetleyiciyi seçip “Finish” dediğimizde ilk projemiz hazırlanmış olacak. Fakat bu aşamada projemizde sadece main.c dosyası ve içerisinde boş bir main() fonksiyonu bulunmakta.

PC yazılımları için bu durum normal olsa da bizim için pek normal değil. PC üzerinde bir uygulama geliştirdiğinizde main() fonksiyonu işletim sistemi tarafından çağırılır. Dolayısıyla sadece bir main() fonksiyonu işimizi görür.

Fakat bizim bir işletim sistemimiz yok ve main() fonksiyonunu kimse çağırmayacak. Bunu nasıl çözeceğimize birazdan bakacağız.
Derleme
Derleme işlemi, metin dosyaları olarak tutulan kaynak kodların önce .obj ve daha sonra da çalıştırılabilir binary(.elf, .bin, .hex) dosya üretme işlemidir.

Derleyici ayarını yaptığımız için şimdi tek yapmamız gereken
Project→Build
veya doğrudan “F7” tuşuna basmak. Artık ilk projemiz derlenmiş olmalı.

Fakat bir sorun var. Derlenmiş kodun boyutu “0” byte olarak görünüyor.

Bunun sebebi, derleme sırasında kullanılmayan fonksiyonlar binary dosya içerisine eklenmemesidir. Bunu daha sonra yine test ederek açıklarız. Ama önce sorunumuzu çözmeliyiz.

Birinin main() fonksiyonunu çağırması gerekli. Bunun için mikrodenetleyicinin “startup” kodunu eklemeliyiz. Bu kod sistem açıldığında(Reset sonrasında) çalışır ve main() fonksiyonunu çağırır.
View→Repository
ile açılan ekrandan “CMSIS Boot” ve “CMSIS Core” komponentlerini seçtiğimizde ihtiyacımız olan kodlar projemize eklenmiş olacaktır.

Şimdi “Repository” ekranını kapatıp tekrar derlemeyi deneyebiliriz.

Bu kez derlenmiş binary dosyamız 738 byte. Peki, yeni bir fonksiyon daha eklesek dosya boyutu artacak mı?

Dosya boyutu beklediğimiz gibi hala 738 byte. Daha önce soylediğim gibi, kod içerisinde çağırılmayan fonksiyon binary dosyaya eklenmiyor.

Şimdi bu fonksiyonu main() içerisinde çağıralım ve ne olduğunu görelim.

Beklediğimiz gibi binary boyutu 772 byte oldu çünkü bu sefer testFunc() fonksiyonu da binary içerisine eklendi.
Test
Debug→Debug
veya “Ctrl+F5” ile kodu test etmeye başlayabiliriz. Kod otomatik olarak mikrodenetleyiciye yazılacak, program çalıştırılacak ve main() fonksiyonunun ilk satırında mikrodenetleyici durdurulacaktır.

Bu aşamada artık kodu adım adım çalıştırabilir, breakpoint ler kullanarak kodun nerelerinin çalıştığını görebilir, “memory”, “register”, “variable”, “peripheral” pencereleri ile mikrodenetleyici içerisindeki çevresel birim ve bellekleri izleyebilirsiniz.

Bunlar şu an çok gerekli görünmese de yazılım karmaşıklaştıkça tek gerçek dostlarınız olacaktır.

Şimdi bir soruyla devam edelim; main() fonksiyonu çalışmaya başlamadan önce neler gerçekleşiyor?

Bu sorunun cevabı için main() fonksiyonunu kimin çağırdığını bulmalıyız. Sizi uğraştırmadan söyleyelim, “startup_stm32f10x_md_vl.c” dosyası içerisindeki Default_Reset_Handler() fonksiyonu çağırıyor. Bu fonksiyon işlemci ilk çalışmaya başladığında çalışacak şekilde ayarlanmıştır. Bu ayarlamanın nasıl yapıldığı biraz fazla teknik olacağından şimdilik kafanızı karıştırmayayım.

“Default_Reset_Handler()” ne iş yapar?
  1. Yazılımınız içerisinde kullandığınız değişkenlerin ilk değer atamasını yapar.
    • Ilk değeri “0” dan farklı olan değişkenler için atama yapar
    • ilk değeri “0” olan değişkenleri sıfırlar.
  2. SystemInit() fonksiyonunu çağırarak mikrodenetleyicinin saat frekansını istenen değere ayarlar.
  3. main() fonksiyonunu çağırır. Artık kontrol sizde:)
LED Uygulaması
Şimdi basit bir LED uygulaması yapalım. Bu bizim için PC yazılımı derslerindeki “Hello World” uygulamasına eşdeğerdir.

Öncelikle LED in hangi pine bağlı olduğunu bilmeliyiz. STM32VLDiscovery üzerinde PC8 ve PC9 pinleri halihazırda LED lere bağlanmıştır.

STM32F103C8T6 Mini Geliştirme Kartı kullanıyorsanız PC13 ü kullanabilirsiniz.
Pin Konfigurasyonu
Herhangi bir çevresel birimi kullanmadan önce, o çevresel birimin saat sinyali açılmalıdır. Aksi halde çevresel birim kapalı durumda olacak ve kontrol edemeyeceksiniz.

PC8, PC9 veya PC13 GPIOC çevresel birimine bağlı olduğundan GPIOC saat sinyalini açmalıyız.

RCC->APB2ENR |= 0x00000010;

GPIO pinleri reset sonrasında otomatik olarak “Giriş” modundadır. Dolayısıyla kullanacağımız pinleri “Çıkış” olarak ayarlamamız gerekiyor.

//Set PC8 and PC9 as outputs
GPIOC->CRH &= 0xFFFFFF00;
GPIOC->CRH |= 0x00000033;


veya STM32F103C8T6 Mini Geliştirme Kartı için

//Set PC13 as output
GPIOC->CRH &= 0xFF0FFFFF;
GPIOC->CRH |= 0x00300000;
LED Yakma/Söndürme
Çıkış pin(ler)’i konfigure edildikten sonra pin(ler)’i Set (Lojik 1) veya Reset (Lojik 0) yaparak bu pine bağlı olan LED’i yakabilir ve söndürebiliriz.

GPIOC->ODR |= 0x00000300; //Set Leds
GPIOC->ODR &= 0xFFFFFCFF; //Reset Leds


veya STM32F103C8T6 Mini Geliştirme Kartı için

GPIOC->ODR |= 0x00002000; //Set Led
GPIOC->ODR &= 0xFFFFDFFF; //Reset Led


Şimdiye kadar kodumuz bu hale geldi;
#include "stm32f10x.h"

int main(void)
{
    RCC->APB2ENR |= 0x00000010; //Enable GPIOC clock

    //Set PC8 and PC9 as outputs
    GPIOC->CRH &= 0xFFFFFF00;
    GPIOC->CRH |= 0x00000033;
    while(1)
    {
        GPIOC->ODR |= 0x00000300; //Set Leds
        GPIOC->ODR &= 0xFFFFFCFF; //Reset Leds
    }
}
Sleep
Ledleri yakıp söndürebiliyoruz ama gözle görülebilmesi çok zor. Çünkü LED(ler) çok hızlı aralıklarla yanıp sönüyor. Ekleyeceğimiz bir Sleep fonksiyonu ile sorunu çözüyoruz;
#include "stm32f10x.h"

void sleep(int time){
    volatile int i,j;
    for(i=0;i< time;i++){
        for(j=0;j<1000;j++);
    }
}

int main(void)
{
    RCC->APB2ENR |= 0x00000010; //Enable GPIOC clock

    //Set PC8 and PC9 as outputs
    GPIOC->CRH &= 0xFFFFFF00;
    GPIOC->CRH |= 0x00000033;

    while(1)
    {
        GPIOC->ODR |= 0x00000300; //Set Leds
        sleep(1000);
        GPIOC->ODR &= 0xFFFFFCFF; //Reset Leds
        sleep(1000);
    }
}
Şimdi ne yapabiliriz?
Şimdiye kadar Geliştirme ortamını kurduk ve ilk uygulamamızı yaparak Gömülü Yazılım Geliştirme konusuna giriş yaptık. Bundan sonra yapacaklarınız, ihtiyaclarınız ve hayal gücünüze bağlı. Hepsinden önce,

RCC->APB2ENR |= 0x00000010; //Enable GPIOC clock

yukarıdaki kod satırı gibi satırları nasıl hazırladık? Nereden biliyorduk GPIOC saat sinyalini açmak için böyle yapmamız gerektiğini? Bunların hepsinin cevabı üreticinin web sitesindeki

RM0041: STM32F100xx advanced ARM®-based 32-bit MCUs dökümanındadır. Geliştirme sürecinde en çok okuyacağınız döküman olacaktır. Döküman biraz uzun ama bu detaylı olduğundan. Dolayısıyla okumaktan vazgeçmeyin. Karşılaştığınız sorunların çözümleri çok büyük olasılıkla bu döküman içerisinde bir yerlerde:)