top of page

STM32F1xx Clock, Timers ve Multi Interrupt Uygulaması

Güncelleme tarihi: 7 Ara 2022


Clock: İşlemcinin çalışması için doğru gerilim beslemesine ve clock kaynağına ihtiyaç vardır. Clock sinyali osilatör tarafından üretilir. Pulse üretimi için (periyodik sinyaller için) dijital devrelerde clock sinyali gerekir. İşlemcinin kendisini ve çevre birimlerinin beslenmesi, güç tüketimini optimize etmek için saat ayarlamalarının yapılması gerekir. Sistem ayarı için 3 adet saat kaynağı bulunmaktadır. Bunlar HSI (High Speed Internal) Dahili 8MHz RC osilatör saati, HSE (High Speed External) Harici Kristal osilatör saati, PLL saatidir. Güç tüketimini kontrol etmek için saat kaynakları aktif inaktif edilebilmektedir.


STM32VL Discovery kartın üstünde 8 MHz dahili bir clock vardır onun hemen yanında da takılıp çıkarılabilen 8 Mhz'lik harici clock bulunmaktadır. Genellikle harici osilatör tercih edilir çünkü internal osilatör sıcaklıkltan etkilenebilmektedir. Ve bazı çevrebirimleri yüksek hızda çalışmalarında özel bir harici osilatöre ihtiyaç duyulmaktadır. Ayrıca yüksek hızlı osilatörler gibi düşük hızlı (LSE) osilatörler de harici saat kaynağı olarak kullanılmaktadır. LSI da düşük hızlı internal saat kaynağıdır. Düşük hızlı osilatörler RTC ve IWDT çevrebirimlerini çalıştırmak için kullanılırlar. HSI osilatorun frekansı işlemci çekirdeğinin ve çevrebirimlerin direkt gerkçek frekanslarını oluşturmaz. Reference manualden STM32F100xx’nin clock şeması görülmektedir. Buradaki göre bir saat sinyali işlemciye ve çevrebirimlerine yayılmaktadır.


PLL (Phase Locked Loops) ve prescaler kullanarak ölçeklemeler yapmak hızı arttırıp azaltmak mümkündür.

Örneğin OSC_IN OSC_OUT 4-24 Mhz harici osilatör baglandıgında HighSpeedExternal (HSE OSC) çalışır. LSE OSC (Low Speed External) ise 32.768 kHzdir. İçerdeki dahili olan clock 8 Mhz HSI RC direnç ve kapasitörden oluşturulmuş basit bir osilatör devresidir. (High Speed Internal). Harici kristalle yapılan osilatör daha stabil olur. Oluşturrulan frekans değeri sabit tutulabilir zamanla, sıcaklıkla degişmez. PLLSRC değerine gore external mı internal mı olacağı seçilir. Bir tanesi seçilince PREDIV ile bölünür sonra diğeri de PLLMUL ile çarpılır o da diğer multiplexera gider ve yine seçim olur. Burda PLLCLK HSI ve HSE var SW ile seçilir. Sonunda sysclk değeri olur sonra o da çarpılırak veya bölünerek çevrebrimlere örneğin timerlara veya işlemcinin kendisine gider.


Clock ayarlaması yapılırken,

  • HSE kullanılıyorsa yüksek hızlı osilatör kaynağına göre HSI ve HSE ayarları yapılır.

  • SYSCLK’i daha yüksek bir frekansla beslenmesi gerekiyorsa PLL ayarlamasının yapılması gerekir. (PLL HSI ya da HSE’den gelen çıkış saatini alarak bunları çarpıp frekansı artırır. Yani düşük frekanslı bir osilatörde yüksek frekans elde etmek için bu saat sinyalini PLL’den geçirmek gereklidir. Hız ayarlaması gibi düşünülebilir.)

  • Çalışılacak çevrebirimlerini besleyen clock hatlarının doğru seçilmesi gerekir. (Örneğin B portu için APB2 clock hattı seçilmelidir TIM2,TIM3 ve TIM4 için APB1 clock hattı seçilmektedir.)


Timers:


Timerlar, belli bir değere kadar ileri veya belli değerden geri doğru sayan bir sayaç olarak tanımlanır. Sayaçlar görevlerini işlemcinin clock hızına göre yaparlar buna ek olarak dışardan interruptlarla da ayarlanabilmektedir. (0’dan belli değere kadar sayarlar. Örneğin 16 bitlik bir zamanlayıcı 65535’e geldiğinde taşma olur.) Farklı modlarda çalışabilirler. Input capture modeda harici bir olayın frekanslarını ölçmek için kullanılabilirler. Output compare modeda çıkış dalgasını kontrol etmek için veya geçen süreyi belirtmek için kullanılabilirler.

PWM modeda PWM sinyalleri üretmek için kullanılabilirler.


Basic Timers:


STM32F1xx’de TIM6 ve TIM7 temel zamanlayıcıdır. 16 bit kullanırlar. Giriş çıkış pinleri yoktur. DAC çevrebirimini beslemek için de kullanılırlar. Diğer zamanlayıcılar için master olarak da kullanılabilir.


General Purpose Timers:


TIM2, TIM3, TIM4, TIM5, TIM12, TIM13, TIM15, TIM16, TIM17 genel amaçlı zamanlayıcılardır. 16-32 bitliktirler. Giriş çıkış kanalı sağlarlar. Çıkış karşılaştırması, tek darbe modu, giriş yakalama gibi kullanabilirler. Ek olarak sensör arayüzü encoder, sensor vb. için uygulamalarda kullanılabilirler.


Advanced Timers:


Genel amaçlı zamanlayıcılara ek olarak motor kontrolü be dijital güç dönüştürme uygulamalarında hassas işlemler için kullanılır. TIM1 advanced timerstır.


Uygulamada da kullanılan bazı timers terimleri:


  • Prescaler: Osilator saatini değişken faktörlere böler. Örneğin 16 bit çözünürlükte bir işlemci için 1 ile 65536 arasında değişen faktör ölçeklemeleri vardır.

  • Counter Mode: Zamanlayıcının sayım yönünü belirler TIM_COUNTER_UP 0’dan yukarı sayar. Overflow olduğunda tekrar sıfırlanır. Sadece temel zamanlayıcılar için tanımlıdırlar. TIM_COUNTERMODE_DOWN belirlenen değerden 0’a aşağı doğru sayar. 0 olduğunda tekrar başladığı bit değerine çıkar. TIM_COUNTERMODE_CENTERALIGNED 0’dan başlar bit değerine kadar sayar ve bit değerine gelince de geriye doğru 0’a kadar sayar.

  • Periyot: Auto Reload register (ARR) olarak da adlandırılır. Sayacın maksimum değerini ayarlar. 16 bitlik zamanlayıcılar 0x01 ile 0xFFFF arasında 32 bitlikler 0x01 ile 0xFFFF FFFF arasında sayabilir .

  • Clock Division:Filtreler ile saatin bölünmesi ayarlanabilir. Microişlemcinin kendi clock değerinden daha küçük clock değerleri isteniyorsa çevrebirimler için clock division ayarı yapılması gerekir. TIM_CLOCKDIVISION_DIV1,TIM_CLOCKDIVISION_DIV2, TIM_CLOCKDIVISION_DIV4 gibi bölünmeleri vardır.

  • Repetition Counter:Her zamanlayıcının taşma durumunu kontrol eden bir kayıt vardır. Zamanlayıcının çalıştığı süre içinde kaç kez taştığını bildirir (Kaç kez çalıştığını gösterir.) Yalnızca advanced timersta bulunur.

**Timerlarla ilgili çok daha detaylı bilgiler kartın reference manuelinde bulunmaktadır.

Sayaçlarda timer update event hesaplaması:


Update event = TIMERClock/((Prescaler+1)*(Periyot+1)*(Repetition Counter+1)) şeklindedir. Repetition conter kısmı advanced timer kullanılacaksa yani TIM1'deyken dahil edilir. Update event Hz yani frekans cinsindendir. 1/Frekans yapılınca saniye elde edilir.


Örneğin bir ledin 1 saniyede bir yanıp sönmesini isteniyorsa ve TIM2 kullanıldıysa Timer Clock'u 24Mhz olan saat hattında 24000000/((Prescaler+1)*(Periyot+1)) değerinden

prescaler 5999 ve periyot 3999 ayarlanabilir.



Bu uygulamada üç farklı timer ve interrupt kullanılarak ledler toggle işlemiyle yakılmıştır. Birinci ve üçüncü led 0.25 saniye aralıklarla yanıp sönmektedir ikinci led ise 1 saniye aralıklarla yanıp sönmektedir. Prescaler ve periyot değerleri farklıdır. Timerlar up counter modda kullanılmıştır. Interrupt gerçekleştiğinde priority sırasına göre fonksiyonlar çalışmaktadır. TIM2 birinci ledi,TIM3 ikinci ledi, TIM4 ise üçüncü ledi yakmaktadır.


Malzemeler:

  • Üç adet ded

  • Jumper Kablolar

  • STM32VL Discovery







Devre Şeması ve Kod:





#include "stm32f10x.h"                  // Device header
#include "stm32f10x_tim.h"              // Keil::Device:StdPeriph Drivers:TIM


void gpioConfig(); // giris cikispinlerinin ayarlanmasi fonksiyonu 
void timerConfig(); //tim2 tim3 ve tim4'un ayarlanmasi 
void nvicConfig(); // kesme gerceklestiginde calisacak fonksiyonlarin ayarlanmasi 

void toggleLED(uint16_t pin);  // ledlerin yanmasi ile ilgili fonksiyon



int main (){
	
	  gpioConfig();
    timerConfig();
    nvicConfig();
    


while(1){


}



}

void gpioConfig(){
	
	
 GPIO_InitTypeDef GPIOInitStructure;
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // ledler icin 3 pinin bagli oldugu B portunun clock hatti aktif edildi
 
 GPIOInitStructure.GPIO_Mode=GPIO_Mode_Out_PP; // pinler cikis olarak push pull ayarlandi
 GPIOInitStructure.GPIO_Pin= GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;	// 0. 1. ve 2. pinler ledlere baglandi
 GPIOInitStructure.GPIO_Speed= GPIO_Speed_50MHz; // hiz 50 Mhz 

 GPIO_Init(GPIOB,&GPIOInitStructure); // portun konfigürasyonu yapildigi haber veriliyor
 	

}



void timerConfig(){ 
	
	TIM_TimeBaseInitTypeDef TimerInitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); // timer2'nin clock hatti APB1 aktif edildi, timer 2 ile Pin'0a bagli led kontrol ediliyor.
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); // timer3'ün clock hatti APB1 aktif edildi , timer3 ile Pin1'e bagli led kontrol ediliyor.
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); // timer4'ün clock hatti APB1 aktif edildi, timer4 ile Pin2'ye bagli led kontol ediliyor.
	
	

	
	TimerInitStructure.TIM_ClockDivision=1; 
	TimerInitStructure.TIM_CounterMode=TIM_CounterMode_Up; // timerin modu yukari dogru sayma counter up secildi
	TimerInitStructure.TIM_Period=5999; // Update event formülünden periyot ve prescalera deger atandi, 0.25 saniyede bir interrupta girecek, yanip sonecek
	TimerInitStructure.TIM_Prescaler=999;
	TimerInitStructure.TIM_RepetitionCounter=0; // TIM2 TIM3 TIM4 general purpose timerlardir, repetition counter advanced counter için aktif edilir
	
  TIM_TimeBaseInit(TIM2,&TimerInitStructure); // TIM2 icin konfigürasyon yapildi komutlari
	TIM_Cmd(TIM2,ENABLE);


	TimerInitStructure.TIM_ClockDivision=1;
	TimerInitStructure.TIM_CounterMode=TIM_CounterMode_Up; // timerin modu yukari dogru sayma counter up secildi
	TimerInitStructure.TIM_Period=5999; // Update event formülünden periyot ve prescalera deger atandi, 1 saniyede bir interrupta girecek, yanip sonecek
	TimerInitStructure.TIM_Prescaler=3999;
	TimerInitStructure.TIM_RepetitionCounter=0;
	
  TIM_TimeBaseInit(TIM3,&TimerInitStructure); // TIM3 icin konfigürasyon yapildi komutlari
	TIM_Cmd(TIM3,ENABLE);
	
	TimerInitStructure.TIM_ClockDivision=1;
	TimerInitStructure.TIM_CounterMode=TIM_CounterMode_Up; // timerin modu yukari dogru sayma counter up secildi
	TimerInitStructure.TIM_Period=5999; // Update event formülünden periyot ve prescalera deger atandi, 0.25 saniyede bir interrupta girecek, yanip sonecek
	TimerInitStructure.TIM_Prescaler=999;
	TimerInitStructure.TIM_RepetitionCounter=0;
	
  TIM_TimeBaseInit(TIM4,&TimerInitStructure); // TIM4 icin konfigürasyon yapildi komutlari
	TIM_Cmd(TIM4,ENABLE);
	
	
	  

}


void nvicConfig(){
 
	NVIC_InitTypeDef NVICInitStructure;
	 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 3 adet kesme kullanilacak priority grup olarak 2 seçildi 
	
	NVICInitStructure.NVIC_IRQChannel= TIM2_IRQn; // TIM2 için calisacak fonksiyon channeli
	NVICInitStructure.NVIC_IRQChannelCmd=ENABLE; // fonksiyona gidilecegi aktif edildi
	NVICInitStructure.NVIC_IRQChannelPreemptionPriority=0; // öncelik siralamasinda en üstte 
	NVICInitStructure.NVIC_IRQChannelSubPriority=0; // ayni öncelige sahip kesme yok o yüzden sub priority=0
	
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); // TIM2'nin interrupt konfigurasyonu yapildi
	NVIC_Init(&NVICInitStructure); // kesme ayarlari yapildigi bildiriliyor
	
	NVICInitStructure.NVIC_IRQChannel=TIM3_IRQn; // TIM3 için calisacak fonksiyon channeli
  NVICInitStructure.NVIC_IRQChannelCmd=ENABLE; // fonksiyona gidilecegi aktif edildi
  NVICInitStructure.NVIC_IRQChannelPreemptionPriority=1; // öncelik siralamasinda ikinci
  NVICInitStructure.NVIC_IRQChannelSubPriority=0;	
	
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); // TIM3'ün interrupt konfigurasyonu yapildi
	NVIC_Init(&NVICInitStructure);
	
	
	NVICInitStructure.NVIC_IRQChannel=TIM4_IRQn; // TIM4 için calisacak fonksiyon channeli
  NVICInitStructure.NVIC_IRQChannelCmd=ENABLE; // fonksiyona gidilecegi aktif edildi
  NVICInitStructure.NVIC_IRQChannelPreemptionPriority=2; // öncelik siralamasinda üçüncü
  NVICInitStructure.NVIC_IRQChannelSubPriority=0;	
	
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //TIM4'ün interrupt konfigurasyonu yapildi
	NVIC_Init(&NVICInitStructure);
	

}




void TIM2_IRQHandler(){ // kesme oldugunda TIM2'de gerceklesecek islem, pin_0'daki led yanip sonecek
  
	toggleLED(GPIO_Pin_0);
	
	TIM_ClearITPendingBit (TIM2,TIM_IT_Update); // islem  sonunda pending bitin temizlenmesi gerekiyor 
	


}
void TIM3_IRQHandler(){ // kesme oldugunda TIM3'de gerceklesecek islem, pin_1'deki led yanip sonecek
	  
	
	toggleLED(GPIO_Pin_1);

	
  TIM_ClearITPendingBit (TIM3,TIM_IT_Update); // islem sonunda pending bitin temizlenmesi gerekiyor 

}


void TIM4_IRQHandler(){  // kesme oldugunda TIM4'de gerceklesecek islem, pin_2'deki led yanip sonecek
  
	toggleLED(GPIO_Pin_2);
	
	TIM_ClearITPendingBit (TIM4,TIM_IT_Update); // islem sonunda pending bitin temizlenmesi gerekiyor 
	

}


void toggleLED(uint16_t pin){ 

uint16_t readLED=GPIO_ReadInputDataBit(GPIOB, pin);
	
	if(readLED==(uint16_t)Bit_SET){ // eger pinlerdeki okunan deger 1 ise 0 yapilsin. yani yaniyorsa söndürülsün 
	  
		 GPIO_ResetBits(GPIOB, pin); 
	
	}
	
	else {
	  
	  GPIO_SetBits (GPIOB, pin); // sönüyorsa yandirilsin
		
	}
	


}































Son Paylaşımlar

banner300900.gif
banner300900.gif
bottom of page