ATMEL PROGRAMLAMA 21 - Timer/Counter0 Normal Mode & Overflow Interrupt



Örneğin 1ms'lik  bir zamanlama istiyoruz. Timer\Counter0  donanım birimini kullanarak Normal Mod ile nasıl yapacağız. ? ( Mikrodenetleyicimiz  8Mhz lik dahili RC osilatör ile çalışacak.)

Normal mode ile iki şekilde bu süreyi ayarlayabiliriz.

 1- TCNT0 kaydedicisi değerini sürekli olarak kontrol edip istenilen değere geldiğinde (1ms) TCNT0 değeri sıfırlanır.

2- Timer0  Kesmesi (Timer0 Interrupt)  ayarlanır. TCNT0 maximum (255=0xFF) değerine ulaşınca kesme oluşur ve TCNT0 değeri otomatik olarak sıfırlanır. 

1.YÖNTEM

TCNT0 kaydedicisi değerini sürekli olarak kontrol edip istenilen değere geldiğinde TCNT0 değeri sıfırlanır.

Timer\Counter  donanım birimi zamanlama işlemini sayarak yapar.  Timer\counter0 biriminin sayma değeri 8 bitlik TCNT0 kaydedicisinde tutulur. TCNT0 içeriği otomatik olarak belirli aralıklarla bir artırılır. 8 bit olduğu için maximum 255 (0xFF) değerini alır. 

Sayma aralığı süresi  ve  TCNT0 kaydedicisinin değerinin (Sayma miktarı) çarpımı bize o ana kadar geçen süreyi verir. Bu şekilde  Timer\counter0  donanım birimi kullanılarak  belirli süreler ayarlanabilir.

Sayma aralığı süresini ve TCNT0 değerini öyle bir ayarlamalıyızki çarpımı 1ms(1000 mikrosaniye)  olsun.

Sayma aralığı süremizi mikrodenetleyicimizin çalışma frekansı belirler. Mikrodenetleyicimiz 8Mhz de çalıştığına göre 

Sayma aralık süresi = 1/ 8Mhz=0,125 mikrosaniye olur.  

Yani Her 0,125 mikrosaniyede bir  TCNT0 kaydedicisinin değeri bir artar. 

1ms için = 1ms/0,125 mikrosaniye = 8000 sayma işlemi yapılması gerekir. 

Ancak biz biliyoruz ki TCNT0 8 bitlik bir kaydedicidir ve maximum 255'e kadar sayar. İşte tam burada PRESCALER değeri işin içine girip bizim sayma aralık süremizi ayarlar. prescaler mikrodeneleyici çalışma frekansı bazı değerlere böler, böylece sayma aralık süremiz artar ve saydırmamız gereken miktar azalır.

Prescaler değerleri her Timer\Counter birimi için önceden belirlenmiş değerlerdir. Bu değerler Timer\Counter0 birimi için 1, 8,64,256 ve 1024'tür.

Aşağıdaki tabloda 8Mhz frekansı ve  Timer\Counter0 prescaler değerlerine göre 1 ms için saydırma miktarları gösterilmiştir


tablodan da görüleceği üzere 8000 ve 1000 sayma miktarları Timer\Counter0 sayma sınırının dışındadır. Bu nedenle prescaler 1ve 8 değerlerini kullanamayız.

31,25 ve 7,8 değerleri ise tam sayı değiller, yuvarlayıp kullansak hatalı süre elde etmiş oluruz bu nedenle bunları da kullanmamak daha iyi olur. Eğer zamanlamanın çok hassas olması istenmiyorsa ve diğer prescaler değerleri işimizi görmüyorsa kullanabiliriz.

Tabloda en mantıklı değer 125 değeridir. prescaler 64 olarak ayarlamalıyız. bu şekilde her 8 mikro saniyede bir TCNT0 değeri bir artırılır ve 125 sayma sonunda 1ms (8x125 =1ms) elde edilmiş olur.

Dikkat edilmesi gereken nokta 125 TCNT0 kaydedicisinin alacağı değer değildir, toplam sayma miktarıdır. Eğer TCNT0 kaydedicisinin değerini 0 dan başlatıp 125 sayma yaparsak  sınır değerimiz 124 olur (125-1). 

TCNT0 kaydedicisinin değeri başlangıçta 0'a ayarlanır , ana program içinde 124 olup olmadığı kontrol edilir. 124 olmuşsa yani 125 sayma yapılmışsa , 1ms süre dolmuş demektir. Tekrar için TCNT0 içeriği sıfırlanır ve bu şekilde 1 ms'lik  süreler elde  edilir.

Programda öncelikle Timer\counter0 normal moda alınmalı bunun için :

  WGM02-WGM01-WGM00 bitleri düzenlenir.  Aşağıdaki tabloda bu bitler ve bunlara karşılık çalışma modları gösterilmiştir. 

 
WGM02=0 ,  WGM01=0 ve  WGM00=0 yapılmalı  bunun için

TCCR0B &=~(1<<WGM02);
TCCR0A &=~(1<<WGM01);
TCCR0A &=~(1<<WGM00);

komutları yazılır.


Normal ayarlandıktan sonra sıra geldi prescaler değerini ve Timer\Counter0 birimi için saat sinyali kaynağını seçmeye. TCCR0B kaydedicisinin CS02,CS01 ve CS00 bitlerini ayarlarsak istediğimizi yapmış oluruz. prescaler değerimiz 64 olmalı

 
Tabloya göre  CS02=0, CS01=1 ve CS00=1 olmalıdır.

 TCCR0B &=~(1<<CS02);
 TCCR0B |=(1<<CS01);
  TCCR0B =(1<<CS00);
 
Programın tamamı

#include <avr/io.h>
#define F_CPU 8000000ul
#include <util/delay.h>
void Timer0_ayar()
      {
         
// normal mod ayarlanıyor
           TCCR0B &=~(1<<WGM02);
           TCCR0A &=~(1<<WGM01);
           TCCR0A &=~(1<<WGM00);
        //prescaler değeri 64 
           TCCR0B &=~(1<<CS02);
           TCCR0B |=(1<<CS01);
           TCCR0B =(1<<CS00);
      
      }

 int main(void)
     {
       
        Timer0_ayar();
  
        for(;;)
            {
                if(TCNT0 ==124)
                      {
                         TCNT0=0;
                       }
            } 
  }

bu şekilde 1ms zaman ayarı yapan bir programımız oldu. bu pek kullanışlı bir yöntem değildir. sadece  normal mode ve TCNT0 sayma mantığını anlatmak amacıyla yapılmış bir örnektir.  1ms pratikte  bu şekilde yalın olarak pek işimize yaramaz. Biz bu 1ms'yi kullanarak 1000ms yani1sn'lik zamanlama yapmak istersek ve her 1sn de bir bir ledin durum değiştirmesini istersek ne yapacağız.

Yapmamız gereken her 1ms de bir bir sayacın  değerini artırmaktır. Böylece  sayac 1000 defa  saydığında fark edip ledin konum değişmesini sağlayabiliriz.


Programımızda şu eklemelri yapmalıyız.

#include <avr/io.h>
#define F_CPU 8000000ul
#include <util/delay.h>

#define led  PORTB0
uint16_t  sayac=0; 
void Timer0_ayar()
      {
         
// normal mod ayarlanıyor
           TCCR0B &=~(1<<WGM02);
           TCCR0A &=~(1<<WGM01);
           TCCR0A &=~(1<<WGM00);
        //prescaler değeri 64 
           TCCR0B &=~(1<<CS02);
           TCCR0B |=(1<<CS01);
           TCCR0B =(1<<CS00);
      
      }

 int main(void)
     {
       DDRB|=(1<<led);
       Timer0_ayar();
  
        for(;;)
            {
                if(TCNT0 ==124)
                      {
                        sayac++;
                           if(sayac==1000)
                              {
                                 PORTB^=(1<<led);
                                 sayac=0; 
                               }
                         TCNT0=0;
                       }
            } 
  }

 2.YÖNTEM (Timer/Counter0 Overflow Interrupt)

Timer0  Kesmesi (Timer0 Interrupt)  ayarlanır. TCNT0 maximum (255=0xFF) değerine ulaşınca kesme oluşur ve TCNT0 değeri otomatik olarak sıfırlanır. 

Timer\Counter0 overflow kesmesinin aktif olması için 

  1 - TIMSK0  (Timer/Counter Interrupt Mask Register) içersindeki TOIE0 (Timer/Counter0 Overflow Interrupt Enable) biti 1 yapılmalı
    2- SREG kaydedicisinin 7. biti I-bit 1 yapılmalı veya sei() ; 
    
Programımızda şu eklemelri yapmalıyız.

#include <avr/io.h>
#define F_CPU 8000000ul
#include <util/delay.h>
#include <avr/interrupt.h>
 
#define led  PORTB0
uint16_t  sayac=0; 
ISR(TIMER0_OVF_vect)
{
    sayac++;
    if(sayac==1000)
    {
        PORTB^=(1<<led);
        sayac=0;
    }
    TCNT0=131;
}


void timer0_ayar()
      {
           //aktif olan tüm kesmeler durduruluyor   
           cli();
                     
         
// normal mod ayarlanıyor
           TCCR0B &=~(1<<WGM02);
           TCCR0A &=~(1<<WGM01);
           TCCR0A &=~(1<<WGM00);
          //prescaler değeri 64 
           TCCR0B &=~(1<<CS02);
           TCCR0B |=(1<<CS01);
           TCCR0B =(1<<CS00);

           //  Timer/Counter0 Overflow Interrupt aktif ediliyor
           TIMSK |=(1<<TOIE0 )  
          
           TCNT0=131          // 255-125=130, 130+1=131
     
      }

 int main(void)
     {
       DDRB|=(1<<led);
       timer0_ayar();
   
       // bütün kesmeler aktif ediliyor.
        sei();            
        for(;;) ;
    }


Yorumlar