STM32F4’te External (Harici) Interrupt ile User Button Uygulaması ve Interrupt’ta Priority (Öncelik) Kavramı

User Button ile LED Toggle uygulamasının Interrupt kullanılmadan yapmıştık. Interrupt’sız Input’ların main fonksiyonu içerisinde kullanılması için programın yürütmesinde büyük sıkıntılar oluştuğunu açıklamıştım. Çünkü int main(void)‘in içindeki while(1){…} sonsuz döngü blokunun içinde while döngüsü kullanmamız gerekiyor veya flag kullanarak butona bastığımızda flag’ı 1’e eşitleyerek butona basıldığını anlayabilir ve de işlemimizi yaptıktan sonra flag‘ı tekrar 0 yaparak programın döngüde kalmasını engelleyebiliyor olsak da işlemcimiz butona bastığımız andan çok daha hızlı çalıştığı için bu çeşit bir metotta da sıkıntı yaşayabilirdik. En sorunsuz uygulama ise butona basıldığında ana programımızı çok kısa bir süreliğine Interrupt ile keserek, External Interrupt Handler ile butona basıldığında yapılması gerekenleri yapabiliriz. Bu şekilde programımız kaldığı yerden sorunsuz olarak çalışmaya devam etmiş olur.

External Interrupt kullanabilmek için ST’nin bize sunmuş olduğu Standard Peripheral Library‘deki misc.c ve stm32f4xx_exti.c dosyalarını projemize dahil etmemiz gerekiyor. Project sekmesi aşağıdaki gibi görünmelidir.

project for exti

Bu uygulamada butona her basıldığında LED bir yanıp bir sönecek yani Toggle olacak. Ayrıca main.c dosyası dışında stm32f4xx_it.c ve stm32f4xx_it.h dosyasını da kullanacağız. Çünkü Interrupt Handler’lar bütün örnek kodlarda bu dosyaların içinde yazılmış. main.c’ye de yazabiliriz fakat farklı bir dosyaya yazmak main.c dosyasının gereksiz yere kalabalıklaşmasını engeller. stm32f4xx_it.c EXTI Interrupt Handler’ının Definition’ını (tanımlamasını) yapacağız. stm32f4xx_it.h dosyasında bu fonksiyonun Prototype’ını eklememiz gerekiyor.

Yukarıdaki kod main.c,

  • LED_D12_Config()  D12’deki LED’in GPIO ayarlaması için yazdığım fonksiyon…
  • UserButton_EXTI0_Config()  User Button’un EXTI0’a bağlanması ve hangi PIN’e EXTI0’ın bağlanması için yazdığım fonksiyon… User Button, A0 PIN’ine bağlı olduğu için EXTI0’ı da A0 PIN’ine bağladım.
  • EXTI0_IRQn_NVIC_Config()  EXTI0 için Interrup Handler fonksiyon ismini, bu Interrupt’ın Preemption Priority (Ön Öncelik) ve Sub Priority (Alt Öncelik) sıralamasını ayarları için yazdığım fonksiyon… Default olarak Priority’ler NVIC_PriorityGroup_2‘ye ayarlanmıştır. 2 bit Preemption Priority, 2 bit de Sub Priority olarak… Yani 00, 01, 10, 11 olmak üzere ikisi için de 4 seviyedir.  0’a yaklaştıkça Interrupt’ın Priority’si (önceliği) artar. Aynı anda Interrupt oluşursa önceliği düşük olan Interrupt durdurulur ve önceliği yüksek olan Interrupt gerçekleştirilir. Preemption Priority’si sayısal olarak 0’a yakın olan Interrupt diğerine göre daha önceliklidir. Eğer Preemption Priority’ler eşitse, Sub Priority’si 0’a daha yakın olan diğerinden daha önceliklidir. Eğer hem Preemption Priority hem de Sub Priority’leri eşit ise bu sefer iki Interrupt arasında zaman paylaşımı yapılır. Bir süre biri çalışır ve durur, bir süre diğeri çalışır ve durur. Eğer Interrupt’lardan birinin komutları biterse, diğeri devam eder. Görüldüğü gibi Priority (öncelik) olayı mikroişlemciler için çok önemlidir ve ayarlaması dikkatle yapılmalıdır. Örneğin, USART haberleşmesi yapan bir uygulamada User Button Interrupt’ının önceliği USART Recieve Interrupt’ından daha öncelikli olursa, butona bastığımız anda eğer USART’tan veri gelirse USART verisini alamayız. Çünkü o sırada EXTI Handler yürütülmekte olur. Bu sebeple öncelik sıralamasını kendi programımızın ihtiyaçlarını iyi analiz ettikten sonra belirlememiz çok önemlidir.
  • NVIC_PriorityGroup ile ilgili detaylı bilgi misc.c dosyasında bulunmaktadır. Eğer öncelik grubunu değiştirmek istiyorsanız, aşağıdaki fonksiyonu istediğiniz grup ile main içinde çağırmanız gerekir.

Yukardaki fonksiyonlar içerisinde her satır için açıklaması üst kısmında // yorum içerisinde yazılmıştır.

main.c’de çağırıken de aşağıdaki gibi kullanılmalıdır. Örneğin, NVIC_PriorityGroup_3 olarak ayarlamak istiyorsanız:

 

Yukarıdaki kod stm32f4xx_it.c,

  • EXTI0_IRQHandler(void) { … }  EXTIO için NVIC ile ürettiğimiz EXTI0_IRQ Interrupt Handler isminin yanına Handler ekleyerek kullanıyoruz. Diğer Interrupt’lar için de aynı durum geçerlidir. Bu fonksiyonun içerisine yazdığımız kod her butona basıldığında oluşan Interrupt ile gerçekleştirilir.
  • if(EXTI_GetITStatus(EXTI_Line0) != RESET) komutuyla Interrupt’ın durumu kontrol edilir. Eğer Interrupt oluşmuşsa bu if blokunun içine girer ve içindeki komutları yürütür. Bu bir kontrol blokudur. İşlemciden doğabilecek çalışma problemlerine karşı durum kontrolü yapar. Yerli yersiz yürütülmesi istenilen komutların işlenmesi engellenmiş olur.
  • Yapılmasını istediğimiz komuttan sonra EXTI_ClearITPendingBit(EXTI_Line0) komutuyla EXTI0 Interrupt Pending Bit’ini temizleriz. Bunun sebebi tekrar Interrupt oluştuğunda bir önceki Interrupt’ın tamamladığını göstermek ve tekrar Interrupt oluşturulabilmesini sağlamaktır. Eğer bu biti temizlemezsek tekrar Interrupt oluşmaz.

Yukarıdaki kod stm32f4xx_it.h,

  • void EXTI0_IRQHandler(void);  EXTI0 Interrupt Handler için yazdığımız Prototype’tır. Header dosyalarında Source dosyalarında yazdığımız fonksiyonların Prototype’larını yazmamız gerekir.

Sonuç olarak eğer projelerimizin daha profesyonel olmasını ve hatasız çalışmasını istiyorsak while(1){…} doldurmaktansa, Interrupt kullanmayı tavsiye ediyorum. Benim 5 Eksenli Robot Kol projemde 5000 küsür satır kod var. Fakat while(1){…} blokunun içinde bir satır dahi kod yok. 🙂

Interrupt’tan daha iyi bir yöntem varsa o da Real Time Operating System, RTOS kullanmaktır. O kısma da ilerideki zamanlarda geleceğiz inşallah. Herkese iyi çalışmalar.

2 thoughts on “STM32F4’te External (Harici) Interrupt ile User Button Uygulaması ve Interrupt’ta Priority (Öncelik) Kavramı

Leave a Comment

%d bloggers like this: