STM32 Discovery ile İlk Adımlar – 4

Şimdi tekrar “blinky” projemize dönüyoruz. Bu program STM32 Discovery üzerinde yüklü olarak geliyor. Yaptığı iş temel olarak kart üzerindeki LED’leri belirli aralıklarla yakıp söndürmek.
USER1 butonuna basıldığında LED’lerin yanma sıraları durum değiştiriyor:

1. Durumda sadece LED3 yanıp sönüyor

2. Durumda sadece LED4 yanıp sönüyor

3. durumda hem LED3 hem LED4 yanıp sönüyor.

Bu projede BUTON1, LED3, LED4 kaynakları kullanıldığından bunlara ait giriş çıkış birimlerinin ayarlarının yapılması gerekiyor.

Kart üzerindeki kaynakların bağlantıları şu şekilde:

LED3 -PORTC-9
LED4 -PORTC-8
BUTON1 – PORTA-0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*----------------------------------------------------------------------------
  MAIN function
 *----------------------------------------------------------------------------*/
int main (void) {
 
  LED_Config();
  BTN_Config();                             
 
  SysTick_Config(SystemCoreClock / 100);       /* Setup SysTick Timer  (10ms) */
 
  while(1) {
 
    if (BTN_Pressed()) {
      ledVal += 1;
      if (ledVal > 3) ledVal = 1;
    }
 
    if (ledBlink == 1) {
      ledBlink  = 0;
	  ledOn^= 1;
	  if (ledOn == 1)
       LED_Out (ledVal);                      /* switch the LEDs on          */
      else
        LED_Out (0);                           /* switch the LEDs off         */    
 
	}
  }
 
}

main() fonksiyonu:

Bildiğiniz gibi bütün C programları main() fonksiyonuyla başlar. Burada da en başta LED_Config() ve BTN_Config() fonksiyonları çağırılıyor. Bu fonksiyonlarda LED’ler ve buton için port giriş ayarları yapılıyor. Daha sonra programımız sonsuz döngü içerisine giriyor (super loop). Burada BTN_Pressed() fonkiyonuyla her bir butona basılma durumunda ledVal durum değişkeni 1’er artırılarak, 1,2,3 değerlerinde dolaşıyor. Bu durumları en başta listeledik.

ledBlink değişkeni her 1 olduğunda (System Handler fonksiyonu içerisinde yapılıyor) LED çıkışlarına dönüşümlü olarak 1 veya 0 değeri veriliyor. ledval değişkeninin değerine göre LED’lerin hangilerinin yanacağına LED_out fonksiyonunda karar veriliyor.

1
2
3
#define LED_BLINK_RATE    10             /* rate = LED_BLINK_RATE * systick   */
#define LED_NUM            2             /* Number of user LEDs               */
const unsigned long led_mask[] = { 1UL<<8, 1UL<
1
2
3
4
5
6
7
/*----------------------------------------------------------------------------
  Switch on LEDs
 *----------------------------------------------------------------------------*/
__INLINE static void LED_On (uint32_t num) {
 
  GPIOC->BSRR = led_mask[num];                 /* Turn On  LED                */
}
1
2
3
4
5
6
7
/*----------------------------------------------------------------------------
  Switch off LEDs
 *----------------------------------------------------------------------------*/
__INLINE static void LED_Off (uint32_t num) {
 
  GPIOC->BRR  = led_mask[num];                 /* Turn Off LED                */
}

Daha önceki yazımda portlara ait BRR (bit reset register) kaydedicisi ile o porta ait pinlerin durumlarını değiştirebileceğimizi görmüştük. Burada LED_off fonksiyonuna verilen parametre ile ilgili pine ait çıkış değeri sıfırlanıyor. num değerinin aldığı 0 veya 1 değerine göre LED3 veya LED4 söndürülüyor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*----------------------------------------------------------------------------
  Function that outputs value to LEDs
 *----------------------------------------------------------------------------*/
void LED_Out(uint32_t value) {
  int i;
 
  for (i = 0; i < LED_NUM; i++) {
    if (value & (1<<i)) {
      LED_On (i);
    } else {
      LED_Off(i);
    }
  }
}

Bu fonksiyonla LED’lerin yakılıp söndürülme durumları kontrol ediliyor. Fonksiyona parametre olarak verilen “value” değerine göre 3 durumdan biri gerçekleşiyor:

value == 1 ise LED3 yakılıyor LED4 söndürülüyor
value == 2 ise LED4 yakılıyor LED3 söndürülüyor
value == 3 ise LED3 ve LED4 yakılıyor

Aslında programda yapılmak istenen işe göre çok karmaşık bir yapı kurulduğunu söylemek mümkün. Aynı işi başka şekilde çok daha az kod yazarak da gerçekleştirebilirsiniz.

1
2
3
4
5
6
7
8
9
/*----------------------------------------------------------------------------
  configure Button pins
 *----------------------------------------------------------------------------*/
__INLINE static void BTN_Config(void) {
 
  RCC->APB2ENR |=  1<<2;                       /* Enable GPIOA clock          */
  GPIOA->CRL   &= 0xFFFFFFF0;                  /* Configure the GPIO for BTNs */
  GPIOA->CRL   |= 0x00000008;                   /* Configure the GPIO for BTNs */
}

Burada BUTON1 pinine ait ayarlar yapılıyor. Yine pinin bulunduğu GPIOA portunun saat sinyali etkinleştirilip porta ait CRL kaydedisine butonu giriş olarak ayarlayacak değer atanıyor.

1
2
3
4
5
6
7
8
/*----------------------------------------------------------------------------
  Read Button pins
 *----------------------------------------------------------------------------*/
__INLINE static uint32_t BTN_Get(void) {
 
 return (GPIOA->IDR & 0x0001);
 
}

Bu fonksiyon sadece GPIOA portunun 1. pininin durumunu döndürüyor. Böylelikle butonun basılıp basılmadığını anlıyoruz.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*----------------------------------------------------------------------------
  USER1Pressed
  check if USER1 is pressed (unbounced).
 *----------------------------------------------------------------------------*/
uint32_t BTN_Pressed (void) {
  static uint32_t USER1KeyCount = 0, USER1KeyPressed = 0;
 
  if (USER1KeyPressed) {
    if ((BTN_Get() == 0 )) {                   /* Check if USER1 not pressed  */
      if (USER1KeyCount < UNBOUNCE_CNT) USER1KeyCount++;
      else {
        USER1KeyPressed = 0;
        USER1KeyCount = 0;
      }
    }
  }
  else {
    if (!(BTN_Get() == 0 ))  {                 /* Check if USER1 pressed      */
      if (USER1KeyCount < UNBOUNCE_CNT) USER1KeyCount++;
      else {
        USER1KeyPressed = 1;
        USER1KeyCount = 0;
		return (1);
      }
    }
  }
  return (0);
}

STM32 Discovery üzerinde bulunan mekanik butonlarda “bouncing” denilen bir durum gerçekleşmektedir. Butona basıldığında çok kısa bir süre içerisinde mekanik kontaklar 1 ve 0 değeri arasında gidip gelmektedir. Bu durumda butona tam basıldığını anlamak için kısa bir bekleme süresi konulması gerekiyor. Bu işleme de “debouncing”* deniliyor. BTN_Pressed fonksiyonunda yapılan da işte tam olarak bu. Fonksiyon başında tanımlanan USER1KeyCount ve USER1KeyPressed değişkenleri fonksiyonun iç durumunun fonksiyondan çıkıldığında da saklanmasını sağlıyor. Eğer fonksiyon ilk çağırıldığında henüz butona ilk defa basılmışsa (USER1KeyPressed = 0 ise) butonun durumu tekrar okunuyor ( if ((BTN_Get() == 0 )) ). Bu arada butona basıldığında girişe 0, basılmadığında ise 1 uygulandığını belirteyim. Eğer butona girişinden sıfır değeri okunuyorsa USER1KeyCount değeri bir artırılıyor. Her fonksiyon girişinde aynı işlem tekrarlanarak USER1KeyCount değerinin belirli bir eşik değerine gelmesi sağlanarak (UNBOUNCE_CNT) gecikme oluşturuluyor. Bu süre içerisinde buton mekanik olarak sönümlenmiş oluyor ve kesin olarak butona basılıp basılmadığı anlaşılıyor. “Debouncing” işlemi yapılmadığı takdirde durumlar arası geçişler çok hızlı olacaktır. Fonksiyon sonuç olarak BUTONA BASILDI (1) veya BUTONA BASILMADI (0) değerini döndürüyor.

1
2
3
4
5
6
7
8
9
10
11
12
13
/*----------------------------------------------------------------------------
/*----------------------------------------------------------------------------
  SysTick_Handler
 *----------------------------------------------------------------------------*/
void SysTick_Handler (void) {
  static uint32_t ticks;
 
  if (ticks++ >= LED_BLINK_RATE) {
    ticks    = 0;
    ledBlink = 1;
  }
 
}

SysTick_Handler (System Tick Handler ) fonksiyonu aslında bir “kesme hizmet fonksiyonu”. Yani System Tick kesmesi oluştuğunda bu fonksiyon çağırılıyor. Şimdilik STM32 Kesmelerine çok girmeden basitçe açıklayayım: System Tick kesmesi ile belirli zaman aralıklarında çalışması gereken şeyler için kullanılabilen bir nevi “timer/ zamanlayıcı”. Daha sonra göreceğimiz main() fonksiyonunda System Tick için 10 ms’lik bir periyod belirleniyor. Yani SysTick_Handler fonksiyonu her bir 10 ms’de çağırılıyor. Dolayısıyla bunu zamanla ilgili işlemlerde kullanabiliyoruz.

Burada fonksiyon her çağırıldığında ticks değişkeni 1 artırılıyor. Ticks değişkeni LED_BLINK_RATE değerine ulaştığında (buradaki değeri 10) ticks değeri sıfırlanıp ledBlink değişkenine 1 değeri atanıyor. Bu değer de ana fonksiyon içerisinde led yakıp söndürme işleminde kullanılıyor. LED’lerin yanıp sönme hızını LED_BLINK_RATE sabitiyle kontrol edebiliyoruz.

Basit bir LED yakıp söndüren programın bile bu kadar karmaşık olması gözünüzü korkutmasın. Burada yapılan bütün işlemleri tam olarak anladığınızda C ile gömülü yazılım geliştirmenin temel konularını kavramışsınız demektir. Unutmayın “HERŞEY BİR LED’İ YAKMAKLA BAŞLAR”

*Jack Ganssle’ın “Switch Debouncing” konusuyla ilgili makalesini okumanızı tavsiye ederim: http://www.ganssle.com/debouncing.htm

4 thoughts on “STM32 Discovery ile İlk Adımlar – 4

      • Önceden biraz c# bilgim var fakat c# gömülü sistemler için pekte uygun bir dil değil sanırım.Bende c++ öğrenmeye başlayacağım ve c++’ın gömülü sistemlerdeki yerini öğrenmek istiyorum.Çünkü yapmak istediğim şey Gömülü oramda görüntü işleme, sinyal işleme gibi dallara yönelmek.Biraz araştırdım ve şu linke ulaştım:
        http://www.linkedin.com/grp/post/3396307-5841888217082658817

        Mehmet Fide’nin yazısını okurken son satırda
        -“Ben inaniyorum, simdi degil ama zamanla, derleyicilerinde yayginlasmasi ve desteklerinin artmasiyla C++, C nin tamamen yerini alacaktir.”

        dediğini gördüm.
        1) c++’ın c’nin yerini alması ne kadar sürer?
        2)Benim c++ öğrenmem gömülü sistemlerde tekrar c’ye yönelmeme neden olur mu?
        3)Örnek veriyorum stm32f4 discovery kitini alıp c++ ile geliştirme yapabilir miyim?
        yada Raspberry pi mı almalıyım?

        • 1) Bu süreyi tam olarak tahmin etmek zor. Tam olarak yerini almayacak belki ama C++’ın kullanımı artacaktır.
          2) Bence ikisini de öğren.
          3) C++ derleyicileri ile çalışabilirsin Çok kullanmadığım için ayrıntı veremiyorum.

Bir Cevap Yazın

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