C ile AVR Programlama -34- Frekans Sayıcı Programı İncelemesi

İnternette Arduino için yazılmış ama çoğunlukla AVR kodlarından oluşan benim de kullandığım bir frekans sayıcı programı vardı. Bu programı beğendiğim için ne kadar Arduino uyumlu olsa da AVR üzerinden anlatarak derslere ilave edeceğim. Bu program Arduino UNO’yu yani Atmega328P mikrodenetleyicisinin zamanlayıcı ünitelerini kullanarak D5 ayağından yani PD5 / T1 ayağından giriş alır. Belli bir zaman periyodunu kullanarak o ayağa gelen sinyalleri ölçerek frekansı bildirir. Uzun zaman periyotu daha fazla hassasiyet verecektir.  Şimdi programı verelim ve sonrasında kodları tek tek açıklayalım.

Öncelikle loop() fonksiyonundan kodları incelemeye başlayalım.

Burada TCCR0 yazmaçlarında olan ilk değer oldTCCR0A ve oldTCCR0B değişkenlerine aktarılarak saklanır.

Burada TCCR0A ve TCCR0B yazmaçları sıfırlanarak zamanlayıcı durdurulur.

Frekans ölçme fonksiyonu 500 argümanı ile birlikte çalıştırılır. Bunun anlamı frekans ölçme fonksiyonu 500 milisaniye boyunca gelen frekansı ölçecek demektir. Şimdi startCounting() fonksiyonuna geçelim ve ondan sonra tekrar ana program döngüsüne dönelim.

Burada startCounting fonksiyonunun aldığı argüman “ms” adıyla adlandırılır. “ms” değişkeninin kullanıldığı yer bizim milisaniye değerimiz olan 500 olacak. Önceden dört adet genel değişken program başında tanımlanmıştı. Şimdi bunlara fonksiyonun başlangıcında ilk değerlerin verildiğini görüyoruz. CounterReady yani sayacın hazır olduğu false durumuna getirilmiştir. Böylelikle sayacın meşgul olduğu belirtilir. timerPeriod değişkenine ise bizim argüman olarak aldığımız ms değişkeninin değeri aktarılmıştır. 500 değeri şimdi de timerPeriod değişkenine geçmiştir. timerTicks değişkeni ise zamanlayıcının kaç defa attığını yani kesmeye geçtiğini sayma verisini tutan değişkendir. overflowCount ise kaç defa taşma gerçekleştiğinin sayım verisini tutan değişken olarak belirtilmiştir. Bu değişkenlerin eski değerleri yeni sayımda önemli olmadığı için hepsi sıfırlanmıştır.

TC1 ve TC2 zamanlayıcılarının kontrol yazmaçları sıfırlanmıştır. Böylelikle zamanlayıcıların tüm özellikleri sıfırlanıp devre dışı bırakılmıştır.

TIMSK1 yazmacının TOIE1 biti bir (1) yapılmıştır. Buradaki bit() fonksiyonu AVR’deki BV_() makrosu ile aynı işlevi görmektedir. TOIE0 bitine bir (1) yazıldığında Zamanlayıcı/Sayıcı Taşma Kesmesi etkin hale gelir.

TCCR2A yani TC2 zamanlayıcısının A isimli kontrol yazmacındaki WGM21 biti bir (1) konumuna getirilir. Böylelikle zamanlayıcı CTC modunda çalışmış olur. Bunun için karşılaştırma değeri olan OCR2A yazmacına ise 124 değeri atanmıştır.

TC2 sayıcısının TIMSK2 yazmacındaki OCIE2A biti bir (1) yapılmıştır. OCIE0A bitine bir (1) yazıldığına Zamanlayıcı/Sayıcı karşılaştırma eşleşmesi kesmesi etkin hale gelir. Böylelikle yukarıda OCR2A yazmacına atanan 124 değeri ile eşleşme sağlanırsa kesme yürütülür.

Her iki zamanlayıcının sayaç değerlerini sayaç değeri yazmacına sıfır (0) atayarak sıfırlıyoruz.

GTCCR yazmacının PSRASY biti bir yapıldığında ön derecelendirici sıfırlanır.

Buraya kadar zamanlayıcıları ayarladık, sıfırladık ve bazı değişkenlere değer atadık. Zamanlayıcıları ise şimdi bu kodlarla başlatıyoruz ve ardından kesme fonksiyonları ile diğer işlemleri yapıyoruz. Öncelikle TCCR2B yazmacının CS20 ve CS22 bitleri bir (1) yapılır. Böylelikle TC2 zamanlayıcısının ön derecelendiricisi 128’e ayarlanmış olur. Şimdi ise 16 bitlik TC1 zamanlayıcısının giriş ayağından frekans ölçmemiz gerekli. Bunun için TCCR1B yazmacının CS10, CS11 ve CS12 bitleri bir (1) yapılır. Böylelikle zamanlayıcının sayması için gereken saat frekansı T1 ayağından (PD5) verilmiş olur. Verilen saat frekansına göre yükselen kenarda sayım yapılır. Asıl frekans ölçüm verisinin depolandığı yer T1 zamanlayıcısının sayaç yazmacıdır. Bizim fonksiyonumuz bundan ibaret olsa da TC1 zamanlayıcısının dolup boşalan sayacı bizim için bir anlam ifade etmez. O veriyi işleyip yorumlamak için diğer komutlara ihtiyacımız vardır. Bunu da kesmeler ile yapıyoruz. Şimdi kesme fonksiyonlarına göz atalım.

TIMSK1 = bit (TOIE1); komutuyla TC1 zamanlayıcısını taşma sağlandığında kesmeye götürmesini söylemiştik. Şimdi ise TIMER1_OVF_vect bu kesmede ne yapılacağını belirlememiz için burada kullanılmıştır. Her taşma gerçekleştiğinde taşma sayıcı değişkenin değeri bir artırılacaktır. Bu taşma verisi kullanılmak üzere şimdilik saklanacaktır.

Frekans değerini dışarıdan alıp depolayan TC1 yazmacının yanında süre hesabını yapan TC2 yazmacı vardı. Bu yazmaca OCR2A = 124; komutu ile önceden karşılaştırma değerini atayıp bu karşılaştırma gerçekleştiğinde ise kesmeye götürmesini söylemiştik. Şimdi bu kesme fonksiyonunda neler yapılacağı ise burada belirtilmiştir. Öncelikle teknik veri sayfasında belirtildiği üzere kesmeden sonra ilk öncelikle sayaç değerini okumamız gerekiyordu. Bunun için unsigned int cinsinde timer1CounterValue değişkeni tanımlanmıştır. Ardından sayaç verisini içinde barındıran TCNT1 yazmacı bu değişkene aktarılmıştır. TCNT1 yazmacı önceden de bahsettiğimiz üzere TC1 zamanlayıcısının sayaç yazmacıydı. Bunun ardından overflowcopy adında bir değişken tanımlanmış ve taşma sayımını yapan değişken olan overflowCount’un değeri buna aktarılmıştır.

TC2 karşılaştırma eşleşme kesmesi her bir (1) mili saniyede bir yürütüldüğü için bizim timerPeriod olarak tanımladığımız ve kaç mili saniyelik bir ölçüm gerçekleştirileceğini belirlediğimiz değere ulaşılmadıkça bu ölçüm yapılmayacaktır. Öncelikle timerTicks yazmacı her bir mili saniyede yürütülen kesmeden dolayı bir artırılır ve timerPeriod değişkeni ile karşılaştırılır. ++ operatörünün başta olması işlem önceliği için önemlidir. return; komutu ile kesme fonksiyonundan çıkılır ve diğer kodlar işletilmez.

Kesme fonksiyonuna girildiği zaman mevcut taşma durumu kaçırılırsa TIFR1 yazmacındaki taşma bayrak biti olan TOV1 denetlenir overflowCopy değişkeni bir artırılır. Normalde TC1 yazmacının özel taşma kesmesi fonksiyonu ile bu taşma verisi artırılıyordu.

Şimdi bütün sayaçlar ve kesmeleri ölçümün hesaplanması üzere devre dışı bırakılır.

Şimdi toplam sayılan frekansı hesaplamak için kaç kere taşma yapıldıysa bu değerin bitleri 16 kere sola kaydırılır. Bu işlem mevcut taşma değerini 65536 değeri ile çarpmaya eşdeğerdir. Ardından timerCounts değişkenine bu değer aktarılır.  Sonrasında ise sayacın işlemi bitirdiği ve hazır olduğu anlamında counterReady değeri “true” yapılır.

 

Frekans ölçme ile ilgili tüm fonksiyonlar ve kesme fonksiyonlarını anlatmayı bitirdik. Şimdi ana programa dönelim ve frekansın nasıl hesaplandığına bakalım.

Frekans değeri timerCounts değişkeninin 1000 ile çarpımının timerPeriod değişkeninin değerine bölünmesiyle elde edilir. 1000 ile çarpılmasının sebebi her 1 mili saniyede bir TC2 eşleşme kesmesinin yürütülmesidir.

Bu kodlarla ise TCCR0A ve TCCR0B değerlerinin ilk halleri tekrar bu yazmaçlara yüklenir ve sayma işlemi yeniden başlar. Görmezden geldiğimiz Arduino kodları bizim işimize yaramasa da buraya kadar incelediğimiz kodlar Arduino kodu olmayıp AVR programlamada geçerli kodlardır. O yüzden AVR programcıları için faydalı olacağına inandığım bu programı satır satır inceledim.

Kaynaklar:

Timers and counters, Nick Gammon, http://www.gammon.com.au/timers, Erişim Tarihi: 30.08.2018

Kapak Resmi :https://d3s11pzv7w3h1q.cloudfront.net/wp-content/uploads/IC-ATMEGA8A-PU-2.jpg

C ile AVR Programlama -27- TC0 Zamanlayıcı Yazmaçları, Gökhan Dökmetaş, http://www.lojikprob.com/avr/c-ile-avr-programlama-27-tc0-zamanlayici-yazmaclari/, Erişim Tarihi : 30.08.2018

C ile AVR Programlama -26- TC0 Zamanlayıcısı, Gökhan Dökmetaş, http://www.lojikprob.com/avr/c-ile-avr-programlama-26-tc0-zamanlayicisi/, Erişim Tarihi: 30.08.2018

UYARI!!

Gökhan Dökmetaş

"Arduino Eğitim Kitabı" ve "Arduino ve Raspberry PI ile Nesnelerin İnterneti" kitaplarının yazarı. Başkent Teknoloji ve Dedektör Merkezi'nde Ar-ge Sorumlusu. Araştırmacı-Yazar.

You may also like...

11 Responses

  1. cemal dedi ki:

    // Eğer taşma kaçırılırsa
    if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 256)
    overflowCopy++;
    Hocam burayı anlayamadım.
    *Taşma neden kaçırılsın?
    * Kaçırılma bu kodlar ile nasıl telafi ediliyor?

    • Orada kesme fonksiyonuna giderken mevcut bir taşma süreci gerçeklemiş olabiliyor. Eğer o taşma hesaba katılmazsa frekans yanlış sayılır. Buna önlem için bu dahil edilmiş.

  2. cemal dedi ki:

    anladım çok sağolun. yani bir taşma eksik hesaplasaydık 65565 darbe eksik saymış olacaktık, bu yüzden sonucu baya etkilemiş olacaktık. doğru mu anlıyorum 🙂 ?

  3. cemal dedi ki:

    sağolun. Birşey daha sormak istiyorum. en başta tanımlar yapılırken timerCounts ve counterReady volatile tanımlanmasaydı veya sayaç rutinleri volatile tanımlansaydı ne değişirdi? neden farklı olarak tanımlanmışlar?
    Bu arada çok soru sordum kusura bakmayın. Ama sordukça daha da oturuyor kafamda. bir iki soru daha gelebilir 🙂

  4. cemal dedi ki:

    birde atmel studio7 de boolean veri tipi tanımlayamıyorum. derleyince hata veriyor? anlamadım

  5. cemal dedi ki:

    hocam;
    if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 256)
    burda , timer1CounterValue < 256' yı neden sorguluyoruz?

  6. cemal dedi ki:

    hocam;
    Her satırını anlayarak yazmaya çalışıyorum. O yüzden aklıma yeni sorular geliyor. Umarım sorun olmuyordur:) Tam anlayınca kendimizde kod üretir hale geliyoruz. yoksa kopyala/yapıştır dan öteye geçilmiyor.

    Bu nedenle bir sorum daha olacak müsadenizle;
    TIMSK1 = 0; // TC1 kesmesini devre dışı bırak
    TIMSK2 = 0; // TC2 kesmesini devre dışı bırak
    Kesme vektör alt programındayken kesmeleri kapatıyoruz. Bu bir sorun oluşturmaz mı?

  7. cemal dedi ki:

    anladım hocam sağolun.
    Timer 0’ı hiç kullanmadık. Neden onu sıfırlayıp durduruyoruz.

    (bu arada bool tanımlayabilmek için stdbool.h kütüphanesini include etmek gerekmiş. öğrendim.)

    • Bu program arduino programı olduğu için Arduino millis() delay() gibi fonksiyonlarda TC0 zamanlayıcısını kullanır ve kesmeye götürür. Normalde TC0 zamanlayıcısı kullanmak istersen kullanamazsın bu yüzden arka planda çalışır

Bir cevap yazın

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