Künye

Okuduğunuz yazı STM32 Uygulamaları Kategorisi altında 29/12/2013 Tarihinde Tarafından Yazıldı - 1.400 kez okunmuş

Yazı Seçenekleri : | Pdf Kayıt | Arkadaşına Gönder | Yazıyı Paylaş | Geri izleme | Yorum takibi

Etiketler : , , , , , ,

stm32_logo221x295

ST firmasının üretimi olan STM32 son yılların en çok tercih edilen arm cortex m3 işlemcisidir. Özellikle endüstriyel alanda çok tercih ediliyor. Bunun en önemli sebeplerinden biri tabii ki fiyat – performans oranıdır. Fakat onun haricinde güvenilirlik, dayanıklılık, fleksible olmasıyla diğer microcontroller üreticileri tarafından örnek alınan bir konumda olmasıdır.

Benim yazılarımda STM32 programlamaya hem C gücünü hem de embedded programlama kimliğimizi gerçekten kazanmak için, ezberci yapıdan farklı olarak neyi neden dolayı yaptığımızı açıklayıp en basitten başlayarak karmaşığa doğru adım adım programlar yazacağız. Bunun için, doğrudan STM32 ve arm cortex m3 registerlerine bit/byte/16bit/32bit erişebilen, şahsımın hazırladığı bir C kütüphanesi kullanacığız.

Önce neden register’lara tam olarak erişme ihtiyacı duydum veya özel bir register erişimi yapmak istedim onu açıklayayım. Yeni elektronik mühendisi arkadaşlar, embedded programlama konusuna doğrudan C programlama ile giriş yapmışlardır. Benim başlangıcım biraz farklı oldu, işlemciye bildiğiniz makine kodu(binary)’nun bir üst seviyesi olan hex kodlarla yani doğrudan assembly kodlarının karşılığı olan op-code’larla programlamaya başladık. İlk anlamlı programımı paralel taramalı 6 hex digit display üzerinde kendi adımı yazmak için yaklaşık bir ay hergün op-code’larla uğraştığımı hatırlıyorum. Bu durum şimdi bir çok arkadaşa komik geliyordur fakat bundan 15 yıl kadar önce bir işlemciyi silmek için 25-30dk UV ışık altında bekletmek gerekiyordu.

Yüksek okul ve mühendislikten sonra toplamda 5 yıl kadar assembly dili kullandım. Kullandım derken halen piyasada kullanılan 3 tane endüstriyel klimanın programını yazdım. Assembly dili kullanıyorsanız işlemci donanımına tam hakim olmanız gerekir aksi halde tek yanlış için belki bir gün belki bir hafta hata ararsınız.

C ile program yazmaya başladıktan sonra da assembly’den gelen bir alışkanlık sizi bırakmıyor. Bahsettiğim bu alışkanlık detaycılık, yazdığınız programın sürekli asm çıktılarını inceliyorsunuz, işlemcinin tüm asm instruction’larından C içinde faydalanmak istiyorsunuz veya tümüne erişmek istiyorsunuz vs vs. Bu alışkanlık sizi biraz yavaşlatıyor ama ne yaptığınızı ve neden yaptığınızı çok iyi biliyorsunuz veya öğreniyorsunuz. 2008 yılı başlarında STM32′yi sistem içinde toplamda 11 tane can bus üzerinden haberleşmesi gereken büyük bir proje için seçtim, ST’nin library’si kullanmak yerine kendi library’mi oluşturmak istedim, bunun için Türkiye’den faydalanabileceğimde kimse yoktu. Yurtdışı forumlarından ve st library dökümanlarından faydalanarak library’yi oluşturdum. Bu library çok işime yaradı umarım sizinde yarar ve kullanan arkadaşlar feedback yaparlarsa çok memnun olurum. Gerçi şuan library’nin değişik bir versiyonunu oluşturdum kısmet olursa ileride onunlada ilgili projeler yaparız.

Library’de 2 tane asıl header dosyası var, bir tanesinde registerler(uicroarmregs.h), tüm stm32 adreslerine göre tanımlanıyor, bunun için C’nin gücünü kullanıyoruz yani register paketleri için veya bit paketleri için kullanacağımız struct ve union adı verilen C keywords’lerini kullanıyoruz. Bu keyword’lerle her registeri tek tek, her register içeriğini ise bit bit tanimlayarak ilgili stm32 adresine point ediyoruz. (C’de pointer ile ilgili adresi gösteriyoruz). Eğer bu yapıyı incelerseniz sizde STM32′den farklı istediğiniz işlemci için benzer bir yapı oluşturabilirsiniz veya STM32 için benim hazırladığım kütüphane ile st kütüphanesini ortak kullanıp kolayınıza gelen yerde bu kütüphaneyi veya ST library’yi kullanabilirsiniz. Diğer header dosyasında (uicroarmregsdefs.h) gerekli olan bazı define işlemleri yapılmıştır, mesela interruptları kolayca ON/OFF yapmak veya modulleri ON/OFF yapmak, interrupt’lara priority vermek için tanımlamalar vs. Bunları zamanla kullanağımız için çok detayına girmiyorum.

Şimdi bu struct ve union yapılarla stm32 ve arm cortex m3 registerlerine bit / byte / 16bit / 32bit nasıl erişiyoruz ve nasıl program yazdığımızı adım adım göstermeye çalışacağım. Bunun için modülleri tek tek kullanacağız sonrasında modülleri karışık kullanacağız.

Aşağıda bit bazında erişim kısmının kullanımından örnekler var. Bu bitler veya register adları stm32 referans manualindeki isimlerle birebir uyumludur. Dolayısıyla elinizde referans manual dokümanı varsa aynı monitörde ekranı dikey olarak 2′ye bölüp bir tarafta pdf dosyası diğer tarafta editör programınızla kolayca programlama yapabilirsiniz. Ama aslında çift monitör olursa işiniz çok kolaylaşır. Bu programcılığın güzel tarafı st library gibi uzun uzun isimler içermemesi, dezavantajı ise stm32 hardware modüllerininin ilk kullanımları hazırlamak biraz zaman alıyor olması ama bu dersler sayesinde ben size bir çoğunu hazır ve denenmiş olarak vereceğim. Ayrıca akıllı bir editör kullanırsanız, autocomplete özelliği olan böylece register isimlerini veya bit isimlerini hatırlamanız gerekmez. Aşağıda böyle bir editörde yazım örneklerini göreceksiniz, ben şahsen göz sağlığı için black background temalar seçiyorum siz nasıl rahat ediyorsanız öyle kullanın.
Akıllı editörün, tanımlı struct’ları pointer olarak görmesi için aşağıda vereceğim zip dosyası içindeki pointerforeditor.h’i editörün proje dosyasına ekleyin ama keil projesine eklemeyin, çünkü editörü aldatıp, aslında define pointer’i, variable pointer olarak görmesini sağlıyoruz bu durumda akıllı editörün tüm özelliklerinden faydalanmış oluruz.

struct

//========================================================
// Bit Bazında Erişim

RccRegs->apb2enr.bits.ADC1EN=1; // ADC1 modulü clk enable

RccRegs->apb2enr.bits.AFIOEN=1; // Pin Alternate fonksiyon clk enable

GpiobRegs->odr.bits.ODR10=1; // GpioB10 bit 1

Spi1Regs->cr1.bits.SPE=1; // SPI1 modulü ON

Adc1Regs->cr2.bits.ADON=1; // ADC on

while(Usart3Regs->sr.bits.RXNE==0);
//========================================================

Aşağıda 16bitlik ve 32bitlik işlemler için örnekler var. Dikkat edilmesi gereken nokta, eğer bir register 16bitlik ise onun low halfword’une(32bitin düşük 16 bitine) veriyi yazmak zorundayız.

//========================================================
//16bit ve 32bit erişim
ScbRegs->vtor.b32=0x08000000; // flash başlangıç adresi
ScbRegs->aircr.b32=(0x05FA0000)|((u32)0x500); // 4 grup priority, 4 subpriority
FlashRegs->acr.b32&=0x00000038; // 16bitlik registerlerin low halfword'une veri yazılmak zorunda
// aşağıdaki registerler 16bitliktir bu nedenle low HW'ye yazılmıştır datalar
RccRegs->apb2enr.b16.low=0x007d;
GpioaRegs->odr.b16.low=0xFFFF; // gpioa'daki tümledler on
GpioaRegs->odr.b16.low=0; // tümü off
//========================================================

ÖRNEK PROJE :
STM32 Systick ile bir örnek proje yapalım böylece yapının kullanımını pratik olarak uygulamış olacağız. Bu programla hem register ile programlama mantığını hemde System Tick timer’ın nasıl kullanıldığını görmüş olacağız.
Programda PD8′e bağlı led 200ms’lik periyodda 100ms on, 100ms off şeklinde çalışıyor.

//========================================================
// Main.c
//========================================================
#include "uicroarmregs.h"
#include "uicroarmregsdefs.h"
#include "main.h"

volatile static u8  k_us=0;    // us için katsayı
volatile static u16 k_ms=0;    // ms için katsayı

int main(void){
RccInit();                 // 56Mhz'de osc init
NvicInit();                //
SystickInit(56);           // systick 56Mhz'de 1us ve 1ms
GpioInit();

   while(1){
      GpiodRegs->odr.bits.ODR8=1;
      DlyMs(100);
      GpiodRegs->odr.bits.ODR8=0;
      DlyMs(100);
   }
}

void GpioInit(void){
   RccRegs->apb2enr.b16.low=0x007d;      // default olarak hepsini açtim, A,B,C,D,E ve AF enable
   AfioRegs->mapr.bits.SWJ_CFG=4;                // jtag portlari default tanimlidir, IO yapalim

// PortD8 output, slewrate 2Mhz'de ve Pushpull
   GpiodRegs->crh.bits.MODE8=mode_out_2mhz;
   GpiodRegs->crh.bits.CNF8=cnf_out_pushpull;
}

void
SystickInit(u8 sysfreq){
   SystickRegs->ctrl.b32=(u32)0x0;          //int disable, clock kaynağı core clock, systick disable
   k_us=sysfreq/8;
   k_ms=(u16)k_us*1000;
}

void
DlyUs(u16 us){
   SystickRegs->load.b32=(u32)us*k_us;      // reload reg. us*1us için değeri yükle
   SystickRegs->val.b32=0;
   SystickRegs->ctrl.bits.ENABLE=1;
   while(!SystickRegs->ctrl.bits.COUNTFLAG);//0 iken bekle
   SystickRegs->ctrl.b32=0;
   SystickRegs->val.b32=0;
}

void
DlyMs(u16 ms){
   SystickRegs->load.b32=(u32)ms*k_ms;      // reload reg.
   SystickRegs->val.b32=0;
   SystickRegs->ctrl.bits.ENABLE=1;         // systick enable
   while(!SystickRegs->ctrl.bits.COUNTFLAG);// bekle
   SystickRegs->ctrl.b32=0;                 // coreclock seçili
   SystickRegs->val.b32=0;
}

/*
Rcc init fonksiyonda stm32 osc initial yapiliyor
*/

void RccInit(void){
   RccRegs->cr.bits.HSION=1;            // dahili osc enable
   while(!RccRegs->cr.bits.HSIRDY);        // hazir oluncaya kadar bekle
   RccRegs->cfgr.b32&=(u32)0xF8FF0000;     //Reset SW[1:0], HPRE[3:0], PPRE1[2:0], PPRE2[2:0], ADCPRE[1:0] and MCO[2:0]
   RccRegs->cr.b32&=(u32)0xFEF6FFFF;
// HSEBYP bit clear
   RccRegs->cr.b32&=0xFFFBFFFF;            // harici osc bypass bit clear
// PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE bits clear
   RccRegs->cfgr.b32&=(u32)0xFF80FFFF;
   RccRegs->cir.b32=0;

   RccRegs->cr.bits.HSERDY=0;
   RccRegs->cr.bits.HSEON=1;            // harici 8mhz osc enable
   while(!RccRegs->cr.bits.HSERDY);     // stable kalincaya kadar bekle

   RccRegs->cfgr.bits.HPRE=0;           // prescaler off

   RccRegs->cfgr.bits.PPRE2=0;
   RccRegs->cfgr.bits.PPRE1=4;          // islemciyi 56Mhz'de calistiracagim
// adc 56/8=7Mhz'de çalışsın , acelesi yok
   RccRegs->cfgr.bits.ADCPRE=3;
   FlashRegs->acr.b32&=((u32)0x00000038);
   FlashRegs->acr.bits.LATENCY=2;
   FlashRegs->acr.bits.PRFTBE=1;        // Prefetch buffer enable, flash'a yazmak için gerekli
// Pllclk=8mhz*7mhz=56mhz
   RccRegs->cfgr.bits.PLLXTPRE=0;       // xtal'i bölme
   RccRegs->cfgr.bits.PLLSRC=1;         // PLL'e HSE'yi bağla
   RccRegs->cfgr.bits.PLLMUL=5;
// pll nable
   RccRegs->cr.bits.PLLON=1;
   while(!RccRegs->cr.bits.PLLRDY);
   RccRegs->cfgr.bits.SW=2;
   while(RccRegs->cfgr.bits.SWS!=2);
}

void NvicInit(void){
#ifdef VECT_TAB_RAM
   ScbRegs->vtor.b32=0x20000000;        // statik ram başlangıç adresi
#else
   ScbRegs->vtor.b32=0x08000000;        // flash başlangıç adresi
#endif
   ScbRegs->aircr.b32=((u32)0x05FA0000)|((u32)0x500);   // 4 grup priority, 4 subpriority
}
//========================================================

 

//========================================================
// Main.h
//========================================================
#ifndef __MAIN_H
#define __MAIN_H

void RccInit(void);
void SystickInit(u8 sysfreq);
void DlyUs(u16 us);
void DlyMs(u16 ms);
void GpioInit(void);
void NvicInit(void);

#endif
//========================================================

Source Code

Pass:www.uicroarm.com

Video Linki: Ekliyeceğim tüm videolar HD kalitesinde olacaktır. Youtube’da isterseniz yüksek çözünürlükte inceleyebilirsiniz.