STM32F4 Discovery Maceraları – 7 : Buton Okuma ve Timer Kullanımı (StdPeriph)

Başlatan muuzoo, 29 Temmuz 2012, 14:24:29

muuzoo

Bu yazının aslı http://gunluk.muuzoo.gen.tr adresinde yayınlanmıştır.
---------------------------------------------------------------------------------
Buton Okuma

Şimdiye kadar bol bol led yakıp söndürdük fakat baktım ki hiç butonu kullanmamışız. Hemen basit bir kod yazıp hem buton okuyalım hem de GPIO için tanımlanmış fonksiyonlardan kullanmadığımız iki komutu kullanalım istedim. Senaryomuz basit; butona bastıkça ledleri yakıp söndürecek bir uygulama. Her yazımda tekrar ediyorum olacak ama bu kodları yazarken de iki temel kaynağı kullanıyoruz. Biri DM00031020 ve stm32f4xx_dsp_stdperiph_lib_um. Hemen kaynak kodumuzu verelim:

/* Buton Okuma
 * main.c
 *
 *  Created on: 13 May 2012
 *      Author: m2k
 */
#include "stm32f4xx.h"

GPIO_InitTypeDef  GPIOD_InitStructure,GPIOA_InitStructure;

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

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

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

  /* Configure PA0 in input mode */
  GPIOA_InitStructure.GPIO_Pin = GPIO_Pin_0;
  GPIOA_InitStructure.GPIO_Mode = GPIO_Mode_IN;
  GPIOA_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

  GPIO_Init(GPIOD, &GPIOD_InitStructure);
  GPIO_Init(GPIOA, &GPIOA_InitStructure);

  GPIO_ResetBits(GPIOD,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);

  while (1) {
    if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))    {
        while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0));
        Delay(1000000L);
        GPIO_ToggleBits(GPIOD,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
    }
  }
}


Bazı satırlara daha önceki çalışmalardan aşina olmuşsunuzdur. İlk önce ledlerin bağlı olduğu bacaklar için çıkış tanımlamalarımızı yapıyoruz. Daha sonra PA0 pini için giriş tanımlamalarını yapıyoruz. Çünkü STM32F4 kartımız üzerindeki "User Button" olarak geçen mavi düğme PA0 pinine bağlı. Okuma yaptığımız kısıma gelelim.
GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) komutu ile GPIOA portunun 0. bitini okumuş olduk. Ondan sonraki gelen while döngüsü ise bir çeşit koruma. Amaç butonun pek çok kez art arda basılmış gibi algılanmasını engellemeye çalışmak. Ondan sonra gelen gecikme fonksiyonu da aynı amaca hizmet ediyor.
GPIO_ToggleBits(GPIOD,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); Komutu ile ledlerin bağlı olduğu pinlerin değerini 1 ve 0 olarak sırası ile değiştiriyoruz. Zaten komutun yaptığı iş isminden de belli.

Timer Kullanımı

Bir diğer çalışmamız da timer kullanımı ile alakalı. Bu çalışmada basit olduğu için Timer7 ile çalıştık. Kullandığımız mikrodenetleyici çok sayıda timer donanımına sahip. Bunları kısaca listelersek:

       
  • Gelişmiş Özellikleri Olan : TIM1 (16 bit) ve TIM8 (16bit)
  • Genel Amaçlı -1 : TIM2 (16bit), TIM3 (32bit), TIM4 (32bit) ve TIM5(16bit)
  • Genel Amaçlı -2: TIM9′dan TIM14′e kadar. Farklı ayarlanabilir özellikler mevcut.
  • Basit Özellikleri Olan: TIM6 (16bit) ve TIM7 (16bit)
Tüm bu zamanlıyıcılar ile alakalı en geniş bilgiyi DM00031020 belgesinin 13, 14, 15 ve 16. bölümlerinde bulabilirsiniz. Basit olduğu için TIM7′yi kullandık. Aynı kodları TIM6 için de kullanabilirsiniz. Genel olarak bu zamanlıyıcı donanımlarında kullanılabilecek komutların açıklamalarına ve listesine stm32f4xx_dsp_stdperiph_lib_um belgesinden ulaşabilirsiniz. Burda listeyi vermek isterdim fakat gerçekten uzun bir fonksiyon listesi. Sırasıyla neler yaptığımızı açıklayalım.

       
  • İlk olarak kullanacağımız zamanlıyıcı için clk kaynağını etkinleştirmemiz lazım. Bunun için RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE); komutunu kullanıyoruz.
  • Daha sonra her ihtimale karşı zamanlıyıcıyı başlangıç ayarlarına döndürüyorum. Bunun için TIM_DeInit(TIM7); komutunu kullanıyorum.
  • Sıra geldi ön bölücü (prescaler) ayarlarını yapmaya. Bunun için biraz kalem ve kağıda ihtiyacımız olacak. Şimdi göreceğiniz üzere TIM7 APB1′e bağlı gözüküyor. APB1′in çalışma hızı 42 MHz. fakat bu demek değildir ki bu zamanlayıcının saat kaynağı 42Mhz. Burda bir püf noktası var. DM00037051 belgesinin 18. sayfasında bulunan şemanın altında yazan iki önemli bilgi var; "The timers connected to APB2 are clocked from TIMxCLK up to 168 MHz, while the timers connected to APB1 are clocked from TIMxCLK up to 84 MHz." Yani diyor ki, APB2′ye bağlı olan zamanlayıcılar için saat işareti 168Mhz'e kadar olabilir, APB1 içinse 84Mhz olabilir. Burada kafamızda bir "hmmm" baloncuğu ve yüzümüzde şüpheci bir ifade oluşuyor. Ve soruyoruz neye göre kime göre? Aynı zamanda yine aynı belgenin 29 sayfasında bulunan tablo 3′te  tüm timer donanımları için yapılmış olan karşılaştırma tablosunda diyor ki "Max Interface Clock : 42 MHz" ve "Max Timer Clock : 84 MHz" . Araştırmaya devam ediyoruz DM00031020 belgesinin 85. sayfasında bulunan 9 numaralı şemada tüm clk kaynaklarının bağlantıları gösteriliyor ve orada bizi ilgilendiren bir çizim var:

    Ve bu çizimin hemen ardından gelen 86. sayfada şu ifadeler geçiyor :"If the APB prescaler is 1, the timer clock frequencies are set to the same frequency as that of the APB domain to which the timers are connected. Otherwise, they are set to twice (×2) the frequency of the APB domain to which the timers are connected." Yani diyor ki — bu arada bir imam tadı yakalamadım değil, onlar da önce arapçasını söyleyip sonra tercümesini veriyorlar :) — APB ön bölücü değerinin1 ise sorun yok zamanlayıcılar da aynı frekansta çalışır diyor. Yok eğer ön bölücü değerin 1 değilse zamanlayıcılar APB'nin 2 katı hızda çalışır. Vay arkadaş dediğinizi duyar gibiyim. Çalıştıracağımız bir zamanlayıcı çektiğimiz çileye bak diyorsunuz az daha sabır, yüzdük yüzdük kuyruğuna geldik. APB ön bölücü ayarlarını daha ilk yazımda proje oluştururken kullandığımız "system_stm32f4xx.c" dosyasında belirtmiştik. Ne alaka diyenler için ilgili dosyanın 387. satırına bakabilirler. O dosyaya göre APB1 ön bölücü değerimiz 4 (168MHz/4 = 42MHz). İşte tüm bu nedenlerden dolayı TIM7′nin çalışma frekansı 84MHz. Şimdi buna göre hesap yapmamız gerekiyor. Hedeflediğimiz süre 0.5sn. Ön bölücü değerini öyle bir ayarlayacağız ve sayılan değer olarak öyle bir değer seçeceğiz ki bize 0.5sn lik aralıkları ölçebilelim. DM00031020 belgesinin 465. sayfasında bulunan ön bölücü ayarlarına göre : fCK_PSC / (PSC[15:0] + 1). olarak belirleniyor. Biz PSC değeri olarak 41999 dersek, +1 de hesaplamadan eklenip 84MH'i 42.000 değerine bölmüş olacağız bu durumda elde edeceğimiz değer 2000Hz oluyor. Artık çalışma frekansımız 2000Hz dir. Bu durumda biz sayılacak değer olarak 2000′i seçersek zamanlayıcımızda 1sn'lik bir değer elde etmiş olacağız. Bu nereden geldi derseniz ben de şu bağıntıyı veririm t=1/f . Timer içeriğini okuyarak kolayca 0.5sn'lik zamanlamaları elde edebiliriz.
    Yukarıda yazılan tüm bilgiler ışığında TIM_PrescalerConfig(TIM7,41999,TIM_PSCReloadMode_Update); komutu ile ön bölücü değerine 41999 değerini atıyoruz. Bu kadar açıkladık sonra bana gelip neden 41999 seçtik demeyin :)
  • Sıra geldi tekrar yükleme değerine. Bu ayarı yaptığımızda zamanlayıcı içinde bulunan sayıcı belirtilen değere kadar saydığında tekrar baştan, sıfır değerinden saymaya başlar. O yüzden değer olarak 1999 değerini hedef değer olarak belirliyorum. TIM_SetAutoreload(TIM7,1999);
  • Bu kadar işte. Temel seviyede Tim7 için gerekli ayarları tamamladık.
Bundan sonrası ise bildiğimiz led yak söndür kısmı. Şimdi sayıcımız 1sn lik bir süreyi ölçüyor. Sayıcının değerini okuyarak istediğimiz süreyi kestirebiliriz.Tim7 de bulunan sayıcının değerini okumak içinse TIM_GetCounter(TIM7) komutunu kullanıyoruz bu sayede o an içindeki sayıcı değerini öğrenmiş oluyoruz. Dilerseniz aşağıdan tüm kodu inceleyebilirsiniz.

/*TIMER 7 Kullanılarak Yanıp Sönen Led. Yanıp sönme aralığı 0.5sn
 * main.c
 *
 *  Created on: 13 May 2012
 *      Author: m2k
 */
#include "stm32f4xx.h"

GPIO_InitTypeDef  GPIOD_InitStructure;

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

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

  /*TIM7 Settings*/
  TIM_DeInit(TIM7);
  TIM_PrescalerConfig(TIM7,41999,TIM_PSCReloadMode_Update);
  TIM_SetAutoreload(TIM7,1999);


  GPIO_Init(GPIOD, &GPIOD_InitStructure);
  GPIO_ResetBits(GPIOD,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
  TIM_Cmd(TIM7,ENABLE);

  while (1) {
      if(TIM_GetCounter(TIM7)>999){
          GPIO_SetBits(GPIOD,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
      }else
          GPIO_ResetBits(GPIOD,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
  }
}


Son olarak süremizi hemen ölçelim. Aşağıdaki ekran görüntüsünden de görüleceği üzere 0.5sn'lik zamanlamalarımız sağlanmış surumda.
gunluk.muuzoo.gen.tr - Kişisel karalamalarım...