STM32F4 Discovery Maceraları - 4 : Led-Yak Söndür (StdPeriph Kütüphanesi İle)

Başlatan muuzoo, 30 Haziran 2012, 04:31:18

muuzoo

Yazının asıl kaynağı : http://gunluk.muuzoo.gen.tr/2012/06/30/stm32f4-discovery-maceralari-4/

-----------------------------------------------------------------------



Daha önceki yazımızda örnek bir led yakıp söndürme uygulamasına ait kodları vermiştik. Ve kabaca kodlar hakkında bir kaç kelam etmiştik. Hemen bir özet geçelim. Yazdığımız yazılımı yüklediğimiz kartımıza elektrik verildiği anda dahili bir clk kaynağı ile başlangıç komutları işletilir. Bunu bir nevi bilgisayarın açılış rutinine benzetebilirsiniz. Çünkü mikrodenetleyicimiz ilk açılış anında hangi hızla çalışması gerektiği nelerin etkin nelerin devre dışı olduğunu bilmiyor ve açılış bilgilerinin yüklenmesi gerekiyor. Haliyle bu ilk çalıştırma anı için gerekli clk kaynağını dahili bir üreteçten sağlıyor. Daha sonra bizim başlangıç rutinlerimiz işlendiğinde yaptığımız pll ayarlarımız işleme alınıyor ve çalışması gereken hıza ulaşıyor. Bu noktada farklı hızlarda çalışma yeteneğine sahip olan AHB ve APB adlı veri yollarından bahsetmemiz gerekir. Her biri iki adet olan bu veri yollarından AHB1 ve AHB2 168 MHz hızına kadar çıkabiliyor ve yüksek veri trafiğine sahip ethernet, usb, kamera arayüzü v.b. bileşenlerin sorunsuzca haberleşmesini sağlıyor. APB2 veri yolu ise azami 84 MHz hızına ulaşabiliyor. Ve son olarak APB1 ise 42 MHz çalışma frekansına sahip. Bu veri yollarının tam yapısına DM00037051.pdf dosyasının 18. sayfasından ulaşabilirsiniz. Kısaca bu yapıya da değindikten sonra bir önceki yazımızda verdiğimiz led yakıp söndürme uygulaması hakkında konuşmaya devam edelim. Bu kod parçası üzerinde bu kadar durmamın sebebi ise, içerisinde hem STM32F4 kütüphanesine ait fonksiyonları bulundurması hem de "register" seviyesinde atamaların yapılıyor olması. Yani karma bir yapıya sahip olması. Dilerseniz satırları incelemeye başlayalım.
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);


Satırı ile GPIOD  için clk işaretimizi etkinleştiriyoruz. Buradan da anlayacağınız üzere diğer giriş-çıkış birimleri içinde aynı ayarlamayı yapmak mümkün. Bu sayede kullanmadığımız arabirimlerin clk kaynağını devre dışı bırakabiliyoruz. Peki bu komutun bu olduğunu nereden biliyoruz? İlk paragrafta DM00037051.pdf dosyasını incelemeniz gerektiğini belirmiştim. Kullandığımız dahili bileşenlerin çalışması için öncelikle o bileşene ait veri yolunu besleyen saat kaynağının etkinleştirilmesi gerekiyor. GPIOD birimi AHB1 veri yoluna bağlı. Bu veri yoluna ait saat kaynağını yöneten register ise RCC_AHB1 içindeki 3.bit (Bknz: DM00037051 Sayfa:110). Bu bit 1 olduğunda ilgili kaynak etkin, 0 olduğunda ise ilgili kaynak devre dışı oluyor. Bu kaynağı etkinleştirmek için doğrudan olarak bu registere değer atayabiliriz. Ya da kütüphanemizin bize sağladığı yukarıda verilen fonksiyonu kullanabiliriz. Bu noktada bir diğer önemli başvuru kaynağımızdan sözetmemiz gerekiyor. STM32F4 için sağlanan kütüphaneye ait kullanım klavuzu stm32f4xx_dsp_stdperiph_lib_um.chm adlı dosya. Kullandığımız donanım için sağlanan tüm fonksiyonlar ve kullanımları bu dosyada anlatılıyor. Bu kütüphaneyi kullanarak kod yazmak bize daha kolay taşınabilen bir kod olarak fayda sağlayacaktır.
1 - GPIO_InitTypeDef  GPIO_InitStructure; 
2 - ... 
3 - ... 
4 - ... 
5 - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; 
6 - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 
7 - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 
8 - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; 
9 - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 
10-GPIO_Init(GPIOD, &GPIO_InitStructure);


Clk kaynağımızı etkinleştirdikten sonra sıra geldi kullandığımız giriş-çıkış biriminin ayarlarını yapmaya. İlk olarak 1 numaralı satırda bir "structure" tanımlaması yapıyoruz. Tip olarak ise stm32f4xx_dsp_stdperiph_lib_um.chm dosyasından ayrıntılarına ulaşabileceğimiz "GPIO_InitTypeDef" biçimini kullanıyoruz. 5. satırda kullanacağımız bacakları belirtiyoruz. 6. satırda ise belirttiğimiz bacakların giriş ya da çıkış olacağını tanımlıyoruz. 7. satırda bu bacakların çıkış tipini belirliyoruz. 8. satırı açıklamak biraz zor olacak. Kullandığımız bacakların çalışma frekansına göre uygun tepkiyi verebilmesini sağlamak için hız ayarını yaptığımız yer burası. Kısaca açıklamak gerekirse daha düşük hıza ayarlanmış bir bacağı belirtilen hızın üzerinde bir şekilde çalıştırmaya kalkarsak sinyal geçişlerinde bir takım tutarsızlıkların oluşacağını söyleyebiliriz. 9. satırda ise kullandığımız bacağın "pull-up" ve "pull-down" ayarlarını yaptığımız yer. Son olarak 10. satırda yaptığımız tanımlamları GPIO_Init fonksiyonuna girdi olarak verip GPIOD bileşeninin ayarlarını tamamlamış oluyoruz. Kısa kısa açıkladıktan sonra bu ayarları nasıl olup da yapıyoruz kısmına gelelim. İlk olarak DM00037051.pdf dosyasının 148. sayfasından itibaren başlayan GPIO birimlerine ait register bilgilerini içeren dökümanı inceliyoruz. Daha sonra ise stm32f4xx_dsp_stdperiph_lib_um.chm dosyasında bulunan "Modules-->STM32F4xx_StdPeriph_Driver-->GPIO-->Functions" başlığını inceliyoruz.
Aslında yukarıda verdiğimiz kodların yaptığı aşağı yukarı şuna benziyor :

GPIOD->MODER  = 0x55000000; //12,13,14,15 Nu. pinler çıkış 
GPIOD->OTYPER = 0x00000000; //Çıkış bacakları push-pull 
GPIOD->OSPEEDR= 0xFFFFFFFF; //Çıkış bacak hız bilgisi 100MHz 
GPIOD->PUPDR   = 0x00000000; //No pull-up,pull-down


Bir önceki kodlarda kütüphane yardımıyla yaptığımız ayarları burda doğrudan register değerlerini atayarak yaptık. İlk yazılan kod kütüphane tarafından sağlanan fonksiyonları kullanıyor, ikinci yazdığımız kod ise yine aynı işi yapıyor fakat doğrudan register temelinde atama yaptığımız için taşınabilirlik adına sıkıntılı bir seçim olabilir.

Şimdi de ledleri yakıp söndürdüğümüz kısmı inceleyelim. Aşağıda verilen kod parçasından da göreceğiniz üzere register seviyesinde bir atamadan söz edebiliriz. GPIOD'nin çıkış bilgisini saklayan register içeriklerine doğrudan müdahale ile işlem yapıldığı görülüyor.

/* Set PD12 Green */ 
GPIOD->BSRRL = GPIO_Pin_12; 
/* Reset PD13 Orange, PD14 Red, PD15 Blue */ 
GPIOD->BSRRH = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;


Pekala aynı işi bir de kütüphanenin bize sağladığı fonksiyonlar ile yapalım. Burda da karşımıza birden fazla seçenek çıkıyor. İlk olarak bit işlemleri kullanarak yazalım.

/* Set PD12 Green */ 
GPIO_SetBits(GPIOD,GPIO_Pin_12); 
/* Reset PD13 Orange, PD14 Red, PD15 Blue */ 
GPIO_ResetBits(GPIOD,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);


Görüldüğü gibi doğrudan register içeriklerini değiştirmek yerine okunabilirliği daha fazla olan ve ne iş yaptığı açık olan "set" ve "reset" fonksiyonlarını kullandık. Peki bu işlemleri bir kerede yapmak istersek?

GPIO_Write(GPIOD,0x1000); 
Delay(10000000L); 

GPIO_Write(GPIOD,0x2000); 
Delay(10000000L); 

GPIO_Write(GPIOD,0x4000); 
Delay(10000000L); 

GPIO_Write(GPIOD,0x8000); 
Delay(10000000L);


Görüldüğü gibi set-reset işlemleri yerine doğrudan GPIOD çıkışına gerekli değerleri yazdırarak da işlemizi tamamlayabiliyoruz. Geçen yazıda verdiğim led yak söndür uygulamasının son hali ise şöyle:

#include "stm32f4xx.h"

GPIO_InitTypeDef  GPIO_InitStructure;

void Delay(__IO uint32_t nCount) {
  while(nCount--) {
  }
}

int main(void) {
  /* GPIOD Periph clock enable */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

  /* Configure PD12, 13, 14 and PD15 in output pushpull mode */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOD, &GPIO_InitStructure);

  while (1) {

    GPIO_Write(GPIOD,0x1000);
    Delay(10000000L);

    GPIO_Write(GPIOD,0x2000);
    Delay(10000000L);

    GPIO_Write(GPIOD,0x4000);
    Delay(10000000L);

    GPIO_Write(GPIOD,0x8000);
    Delay(10000000L);
  }
}
DEVAM EDECEK...
gunluk.muuzoo.gen.tr - Kişisel karalamalarım...

pisayisi

Forumda ilk kez firmware li ve firmware siz kod nasıl yazılır mevzuuna giriş yapıldı, umarım bundan cesaret alanlar kütüpaneleri kullanarak kolayca kod yazmanın  ve arm işlemcilerin öcü olmadığının farkına varacak.

Teşekkürler ,eline sağlık...
Murat

Tagli

ARM öğrenmeye yeni başlayan birisi olarak kafama takılan birkaç soru var:

Öncelikle, ST Peripheral Library mi kullanmak yoksa doğrudan register'lara yazmak mı daha doğru? ST'nin kütüphaneleri diğer üreticilerin Cortex M kütüphaneleri ile ne kadar uyumlu? Veya register'ların benzerliği ne seviyede?

İnternette biraz araştırınca, bazı kişilerin Keil kurulumu ile gelen kütüphanelerin eskiliğinden yakındığını gördüm. Kütüphaneyi ST'nin sitesinden çekmek mümkün. Keil'e bir klasör olarak çekmiş olduğum bu kütüphanedeki dosyaları kullanmasını nasıl söyleyebilirim? Ayrıca yine bazı kişilerin ST kütüphanesindeki gerekli dosyaları proje klasörüne kopyaladıklarını, bu şekilde olası kütüphane güncellemelerinin eski program ile uyumsuz olabilme riskini ortadan kaldırmayı amaçladıklarını öğrendim. Bu doğru bir yaklaşım mıdır?
Gökçe Tağlıoğlu

yamak

      Hocam ilk olarak hazır kütüphane kullanıp kullanmamak duruma göre değişir. Örneğin yetişmesi gereken bir proje var bu durumda kütüphaneler  kullanarak  proje hızlı bir şekilde bitirilebilir. Örneğin stellarisware 'de harika kütüphaneler mevcut sani nesne yönelimli bir dil ile masaüstü uygulaması yazıyomuş gibi program yazıyoruz. Fakat işlemciyi iyi bir şekilde öğrenmek gerekli durumlar kendi kütüphanemi de yazayım diyorsanız registerlarla uğraşmak gerekir.
      2. sorunuzun cevabı da bir firmanın kütüphaneli diğer firmanın MCU ile uyumlu olmaz. Çünkü iki işlemcinin registerları bir birinden farklı oluyo. Mesela stm'in registerları ile LPC nin registerları arasında neredeyse hiçbir menzerlik yok.
      3. sorunuzun cevabı da: Flash->Configure Flash Tools->C/C++ sekmesinden kütüphaneleriniz bulunduğu klasörün yolunu belirtebiliyosunuz.
      4. sorunuza gelince: Aslında söyledikleri doğru. Fakat kütüphanenin hiçbir zaman değiştirmeyecekseniz ya da kütüphanenin içindeki fonksiyonları değiştirmek değil de yeni fonksiyonların eklenmesi gibi değişiklik yaparsanız klasörün yolunu belirtmek daha pratik bir çözüm olur.


muuzoo

Alıntı yapılan: Tagli - 30 Haziran 2012, 14:14:49
ARM öğrenmeye yeni başlayan birisi olarak kafama takılan birkaç soru var:

Öncelikle, ST Peripheral Library mi kullanmak yoksa doğrudan register'lara yazmak mı daha doğru? ST'nin kütüphaneleri diğer üreticilerin Cortex M kütüphaneleri ile ne kadar uyumlu? Veya register'ların benzerliği ne seviyede?

İnternette biraz araştırınca, bazı kişilerin Keil kurulumu ile gelen kütüphanelerin eskiliğinden yakındığını gördüm. Kütüphaneyi ST'nin sitesinden çekmek mümkün. Keil'e bir klasör olarak çekmiş olduğum bu kütüphanedeki dosyaları kullanmasını nasıl söyleyebilirim? Ayrıca yine bazı kişilerin ST kütüphanesindeki gerekli dosyaları proje klasörüne kopyaladıklarını, bu şekilde olası kütüphane güncellemelerinin eski program ile uyumsuz olabilme riskini ortadan kaldırmayı amaçladıklarını öğrendim. Bu doğru bir yaklaşım mıdır?

Benim şahsi görüşüm kütüphane kullanmaktan yana ama bu demek değildir ki "register" önemsiz. İlgili donanım kullanılmadan önce kullanım klavuzundan o donanıma ait register bilgileri incelenmeli, nerede neyin olduğu bilinmeli ki yazdığımız kodun aslında arka tarafta nereleri etkilediğini bilelim. Bu öngörüye sahip olduktan sonra da kütüphane kullanarak yola devam edilmeli ki başka bir mcu ya kodlarımızı taşırken çok fazla zorlukla karşılaşmayalım. Aynı üreticinin farklı mcu ları arasında geçiş nispeten kolay olacakken, başka bir üreticinin mcu'larına geçiş daha sancılı olacaktır hele ki bir de register seviyesinde çalıştıysak.

ARM firması CMSIS (Cortex Microcontroller Software Interface Standard) adıyla bir kütüphane paylaşıyor ve çoğu üretici kendi sağladıkları kütüphaneleri bununla uyumlu hale getirdi. Bizim kullandığımız STM32 kütüphaneleri de CMSIS v2.1 ile uyumlu. Bu kütüphaneler yalnızca cortex serileri için bir uyum sağlıyor.

gunluk.muuzoo.gen.tr - Kişisel karalamalarım...