Proje 3. Sistem Darbe Zamanlayıcısı (SysTick Timer) Kullanarak Ledleri Yakıp Söndürmek.
Amaç :
Bu uygulama ile sistem onay zamanlayıcısının(SysTick Timer) nasıl kurulacağını ve 500ms'lik gecikme süresinin nasıl oluşturulup kullanılacağını öğreneceğiz.İşlem Basamakları
- SysTick Timer (Sistem Onay Zamanlayıcısı) nedir nasıl kullanılır öğreneceğiz,
- STM32F429I Discovery kartının sistem saati 180Mhz olarak nasıl ayarlanır inceleyeceğiz,
- SysTick Timer (Sistem Onay Zamanlayıcısı) Kesmesi nasıl kullanılır detaylıca öğreneceğiz,
- SysTick Timer (Sistem Onay Zamanlayıcısı) /Counter (Sayıcı), serbest çalışma modunda periyodik kesmeler oluşturmak üzere başllatacağız
- SysTick Timer (Sistem Onay Zamanlayıcısı) kemesi her 1ms'de bir tetiklenmesini sağlayacak ve Delay fonksiyonumuzu oluşturacağız.
- Bir gecikme fonksiyonu, SysTick Timer (Sistem Onay Zamanlayıcısı) 'na bağlı olarak sayım sonu eylemini oluşturacak ve ledlerimizi her 500ms de bir yakıp söndüreceğiz.
SysTick Timer (Sistem Darbe Zamanlayıcısı) :
SysTick basit bir zamanlayıcıdır. Diğer zamanlayıcıların input capture / output compare gibi özellikleri olduğu için, bu zamanlayıcıları sıradan zamanlama için kullanmak, işlemci kaynaklaını boşa harcamakmak anlamına gelmektedir. Yani mümkünse bunun yerine SysTick zamanlayıcısını kullanmalıyız.SysTick zamanlayıcısı ARM mimarisinde çekirdeğin temel zamanlayıcı fonksiyonu olduğundan cmsis altında core_cm4 kütüphanesinde bulunur.
Kütüphaneyi inceleyecek olursak ;
/* ################## SysTick function ################## */
/** \ingroup CMSIS_Core_FunctionInterface
\defgroup CMSIS_Core_SysTickFunctions SysTick Functions
\brief Functions that configure the System.
@{
*/
#if (__Vendor_SysTickConfig == 0)
/** \brief System Tick Configuration
The function initializes the System Timer and its interrupt, and starts the System Tick Timer.
Counter is in free running mode to generate periodic interrupts.
\param [in] ticks Number of ticks between two interrupts.
\return 0 Function succeeded.
\return 1 Function failed.
\note When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
must contain a vendor-specific implementation of this function.
*/
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = ticks - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
#endif
/*@} end of CMSIS_Core_SysTickFunctions */
Yukarıdaki fonksiyonda kırmızı renkle yazılmış olan kısmı systick zamanlayıcısını kurmak için fonksiyonu çağırmak amaçlı kullanacağız.
SysTick_Config(uint32_t ticks)
Timer 'ın ne olduğunu hatırlayacak olursak ; Timer donanımın geçen süreyi ölçümlemek kullandığı bir yapı. Genellikle belirlenen sürenin sonuna gelindiğinde bir kesme oluşturarak sürenin sona erdiğini kullanıcıya bildirmek amaçlı kullanılır.
STM32F429 Discovery kitimizin sistem saati 180Mhz olduğuna göre systick ön tanımlı olarak saniyede 180.000.000 darbe üretmektedir,buna bağlı olarak fonksiyonumuzda uint32_t ticks değişkenine atayacağımız değer zamanlayıcının ölçümleyeceği değer olacağından 1ms lik zaman palsi üretmek için 1000 olarak atanmalıdır.
Tabi sistem saatimizin ön tanımlı olarak 180Mhz olarak tanımlanmış olduğunu farzederek bu değeri veriyorum.
Ki kendi kartımda yaptığım ilk denemlerde maalesef SysTick_Config(SystemCoreClock/1000); olarak tanımlamamı yaptığımda ledlerin togle süresinin neredeyse 1sn yi geçtiğini gözlemleyince ciddi bir kafa karışıklığı yaşamıştım.
Şimdi size aksi bir durumla karşılaştığınızda sistem saatini nasıl 180Mhz olarak ayarlayacağınızı anlatacağım (eğer bu konuda bir sorun yaşamadıysanız bu bölümü okumanıza gerek yok yazının ilerleyen bölümlerinden devam edebilirisiniz.)
Pek çok forum alanında "stm32f4xx.h" kütüphanesinde bazı değişiklikler yapılması gerektiğinden bahsetmiş.
Yönerge aynen şöyle...
İlk olarak "stm32f4xx.h" kütüphanesini açıp aşağıda gördüğünüz satırı buluyoruz,
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
Bu kodda HSE_VALUE karşısında bulunan 25000000 değerinin yerine (ki bu harici osilatörün değeridir.), kendi bordumuzdaki kristal osilatörün değeri olan 8Mhz karşılığı olan "8000000" yazıp keydediyoruz. Yeni kod aşağıdaki gibi değiştirilmiş olmalı.
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
Bu işlemden sonra "system_stm32f4xx.c" kütüphanesini açalım ve aşağıdaki satırı bulalım.
#if defined (STM32F427_437xx) || defined (STM32F429_439xx)
#define PLL_N 360 //Set this to 336 for 168MHz clock
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P 2
#endif /* STM32F427_437x || STM32F429_439xx */
ve
#if defined (STM32F427_437xx) || defined (STM32F429_439xx)
#define PLL_M 8
#define PLL_N 360 //Set this to 336 for 168MHz clock
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P 2
#endif /* STM32F427_437x || STM32F429_439xx */
Şeklinde değiştirip kaydettiğimizde bu ayarlar STM32F429I Discovery kitimizin saat frekansını 180MHz e ayarlanmış olacak.
Son olarak aynı kütüphanede aşağıdaki satırı kontrol edeceğiz eğer görüldüğü gibi "168000000" tanımlanmış ise bunu :
uint32_t SystemCoreClock = 168000000;
yerine
uint32_t SystemCoreClock = 180000000;
yazıp kaydediyoruz. Ve işlem tamam. Artık kitimiz 180Mhz olarak çalışmaya hazır.
Tabi bu tanımlamalardaki kontrol ve düzeltmeler yapıldıktan sonra kodumuza eklememiz gereken satırlar var :
int main(void) {
Tanımlamasından hemen sonra uygun göreceğiniz bir yere aşağıdaki satırları yazalım.
//HSE clock aktif ediliyor
RCC_HSEConfig(RCC_HSE_ON);
//Saatin stabil hale gelmesi bekleniyor.
while (!RCC_WaitForHSEStartUp());
//Sistem ayarlarını güncelliyoruz
SystemInit();
Bu kodlar ile sistem saatimizi 180Mhz'e sabitliyor ve sistem saatini çalıştırıyoruz.
Şimdi dönelim kodumuza...
systick artık 1ms lik darbeler üretebildiğine göre ledlerimizi 500ms lik periyodlar ile yakıp söndürebiliriz.
Tabi bunun için öncelikle Delay fonksiyonumuzu ve her 1ms sürenin sonunda oluşacak kesme (interrupt) yi kontrol edecek ve 500ms'lik süreyi denetleyecek fonksiyonlarımızı yazmamız gerekiyor.
İlk fonksiyonumuz 1ms de bir oluşacak kesme fonksiyonumuz ki bu fonksiyon Systick_Handler olarak anılmaktadır.
Systick_Handler fonksiyonumuzda belirleyeceğimiz TimingDelay değişkenine atayacağımız 500ms lik süre tanımımızın her 1ms de bir bir azaltıp 500ms lik süreyi tutacağız.
kodumuz şöyle olacak ;
void SysTick_Handler(void)
{
if (TimingDelay != 0)
{
TimingDelay --;
}
}
Burada TimingDelay Delay fonksiyonumuzla tanımladığımız geciktirme süresi olarak kullanılmaktadır. ifade de TimingDelay değişkeninin değeri sıfırdan farklı olduğu sürece if rutinimiz her 1ms de bir kesme oluştuğunda çalışacak ve TimingDelay değişkeninin değierini 1 azaltacak.
TimingDelay değişkenini ise Delay fonksiyonu ile belirliyoruz ki o kodumuzda şöyle olacak.
void Delay(__IO uint32_t time)
{
TimingDelay = time;
while(TimingDelay != 0);
}
Delay fonksiyonumuzu kodumuzun içinden Delay(500); kodu ile çağırıyoruz ve time değişkenimize 500 değerini atıyoruz. Fonksiyonumuzun içinde ise kesmemizde kullanmakta olduğumuzn TimingDelay değişkenimize time ile belirlediğimiz değeri atıyor ve TimingDelay'in değeri 0 olana kadar beklemeye başlıyoruz. Her 1ms de oluşan kesme ile TimingDelay değişkenimizin değeri bir azalıyor taki değeri 0'a eşit olana kadar.
Eşitlik 0 olduğunda Delay fonksiyonumuz sonlanıyor ve kodumuzda 500ms tanımını yaptığımız Delay(500); kodumuzun alt satırından itibaren main yürütülmeye devam ediliyor.
İşte bizde Delay fonksiyonumuzla belirlediğimiz bekleme süresini bu şekilde oluşturuyor ve sonraki satırda ledlerimizi togle ediyoruz.
while (1)
{
Delay(500);
GPIO_ToggleBits(GPIOG, GPIO_Pin_13 | GPIO_Pin_14); //Ledlerimizin durumunu değiştirerek birinn yanmasını sağlarken diğerini söndürmüş oluyoruz
}
return 0;
Şimdi kodumuzun tamamını görelim.
/**
* Project template
*
* @author Zeynel Abidin Delikan
* @email zdelikan@gmail.com
* @website http://netmicrohouse.blogspot.com.tr/
* @version v1.4
* @gcc v4.9
* @ide CooCox CoIDE v2.0.2
*/
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "defines.h"
#include "core_cm4.h" // SysTick Registerlarının bulunduğu kütüphane
void Delay(__IO uint32_t time);
__IO uint32_t TimingDelay;
int main(void) {
//HSE clock aktif ediliyor
RCC_HSEConfig(RCC_HSE_ON);
//Saatin stabil hale gelmesi bekleniyor.
while (!RCC_WaitForHSEStartUp());
//Sistem ayarlarını güncelliyoruz
SystemInit();
// Artık Systick zamanlayıcımız sistem saatimizin 180Mhz e kurulmasıyla
// tam olarak 1ms'lik puls üretebilir.
// Disco bordumuzun kırmızı ve yeşil ledlerimizi kullanmak için AHB1 i hazırlıyoruz
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);
// GPIO port tiplerini kullanabilmek için GPIO_InitDef değişkenimizi tanımlıyoruz
GPIO_InitTypeDef GPIO_InitDef;
// İlk olarak GPIOG portunun pinlerini düzenliyoruz
// Pinlerimizi Belirliyoruz
GPIO_InitDef.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
// Pinlerimizi çıkış tipini Push Pull olarak belirliyoruz
GPIO_InitDef.GPIO_OType = GPIO_OType_PP;
// Pinlerimizi çıkış olarak tanımlıyoruz.
GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;
// Pinlerimizin çekme direncini ayarlıyoruz.
GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
// Pinlerimizin çalışma frekanslarını ayarlıyoruz
GPIO_InitDef.GPIO_Speed = GPIO_Speed_2MHz;
//Şimdi portlarımızı kuralım
GPIO_Init(GPIOG, &GPIO_InitDef);
// Şimdi ledlerimizin ilk değerlerini tanımlıyoruz.
// Kırmızı Led "Düşük", yeşilled ise "Yüksek" durumda olacak
GPIO_SetBits(GPIOG, GPIO_Pin_14);
GPIO_ResetBits(GPIOG, GPIO_Pin_13);
//Son Olarak Systick Timer'ı 1ms'ye ayarlayalım. (sistem saatimiz ön tanımlı olarak 180Mhz dir)
SysTick_Config(SystemCoreClock/1000);
while (1)
{
Delay(500); // Delay Fonksiyonumuzu çağırarak 500ms lik bekleme süresi oluşturuyoruz
GPIO_ToggleBits(GPIOG, GPIO_Pin_13 | GPIO_Pin_14);
}
return 0;
}
void SysTick_Handler(void) // Her 1ms'de bir oluşacak kesmeyi yakaladığımız fonksiyonumuz
{
if (TimingDelay != 0) // TimingDelay'in değeri sıfırdan farklı ise çalıştır.
{
TimingDelay --; //TimingDelay'in değeri bir azaltır.
}
}
void Delay(__IO uint32_t time) // Delay Fonksiyonumuz
{
TimingDelay = time; //Kesme fonksiyonunda kullanacağımız değişkenin değerini ayarlıyoruz
while(TimingDelay != 0); //TimingDelay değişkeninin değeri sıfırdan farklı olduğu sürece döngüde kal.
}
Şimdi kodumuzu derleyip MCU muza yükleyip sonucu görelim.
Sonuç olarak STM32F429 Disco bordumuz üzerindeki kullanıcı ledlerimiz, gecikme fonksiyonumuzda tanımlanmış zamanlamaya bağlı olarak yanıp söner.
Hiç yorum yok:
Yorum Gönder