PIC Programlama – 8 – Zamanlayıcılar (Timers)

Merhabalar, geçen yazımızda kesmeler üzerine oldukça marjinal bazı çalışmalar yapmıştık. Şu ana kadar PIC mikrokontrolörün giriş çıkış ünitelerini, kesmeleri de içerecek şekilde evire çevire kullanabilecek bilgi birikimini edindik. Tabi bu bahsettiğimiz konu, işin bahanesiydi. Asıl amacımız, tüm mikrokontrolörlerde kullanabileceğimiz bir modelleme ve geliştirme altyapısını tahsis etmekti. Buna istinaden yaptığımız en orijinal iş, Microchip firmasının sunduğu kütüphanelerden performans, estetik, yazılım kalitesi, taşınabilirlik ve geliştirilebilirlik bakımından çok daha iyi bir kütüphane oluşturmak oldu. Mikrokontrolör’ün veri kağıdını nasıl modelleyeceğimizi gördüğümüzden aynı işlemleri herhangi bir başka platform için yapmak sorun olmayacaktır.

Dış dünyaya mikrokontrolörün I/O pinleri üzerinden müdahale edebildiğimizden bir sonraki adıma geçmenin sırası geldi. Bir sonraki adım şüphesiz ki periyodik işlerle nasıl başa çıkabileceğimizi bulmak olacak. Güncel durumda yazdığımız kodlar ömürleri boyunca aynı işi aralıksız olarak yaptılar. Oysa gerçek hayattaki pek çok uygulamada gerçekleşmesi gereken zamanda ve periyodik gerçekleştirilmesi gereken olaylar görürüz. Periyodik yapılan işlerden ilk akla gelen örneklemedir. Nyquist amcamızın da yıllar önce ortaya koyduğu gibi, bir işareti doğru zamanlama ile örneklemezsek o işaretin taşıdığı bilgiyi kaybetmiş olabiliyoruz.  Biraz daha net şekilde açıklamak gerekirse:

Sosyal Mesaj
Nyquist örnekleme frekansı, bir işaretin örneklenmesi sürecinde tanımlanan bir alt limittir ve bu limit işaretin içerdiği maksimum frekans bileşeninin iki katıdır. Eğer işaret maksimum frekans bileşeninin iki katından  – ki bu frekans Nyquist frekansı oluyor – daha az bir frekansla örneklenirse o işaret, ölçümlerden düzgünce tekrar üretilemez çünkü örnekleme esnasında bilgi kaybı yaşanmış olur.

Sonuç olarak, mikrokontrolörde zamanı ölçmenin bir yolunun olması gerekiyor. İşte bu noktada devreye Timer (Zamanlayıcı) modülleri giriyor. Ancak zamanlayıcılara geçmeden önce, zamanlama sorununa sunduğumuz en basit çözümü hatırlayalım. BURADAKİ yazımızda, DummyDelayMs fonksiyonun kurnazca tanımlayarak milisaniye mertebesinde bir zamanlama sağlamıştık. Hemen implementasyonu hatırlayalım.

Gördüğünüz gibi bu kodda, mikrokontrolöre boş beleş sayı saydırıp, geçen zamandan faydalandık. Tersten giderek, 1ms beklemek için gerekli olan sayma sayısını hesaplayıp, mikrokontrolörün o kadar boş beleş (dummy) işlem yapmasını sağladık. Bu da adeta bir araçta vitesi boşa atıp gaza sonuna kadar basmaya benziyor. Bir yere gitmiyoruz ama cayır cayır benzin yakıyoruz 🙂 İşte bunu yapmamak için Timer modülü var.

Bizim bu zamana kadar çalıştığımız PIC16F84A’da ne yazık ki tek bir zamanlayıcı modülü var. Ancak diğer modellerde daha fazla sayıda zamanlayıcı modülü mevcut. Unutmamak gerekir ki zamanlayıcılar gömülü sistemlerde “elzem” olduğundan, hemen her mikrokontrolörde zamanlayıcı bulunur. Şimdi hemen bir tablo ile hangi PIC’te hangi zamanlayıcı var görelim.

Zamanlayıcı PIC16F8X PIC16F62X PIC16F877
Timer0 + + +
Timer1 + +
Timer2 + +

Bu arada hemen araya sıkıştırayım; zamanlayıcıları da bitirince artık daha gelişmiş PIC modelleri üzerinden devam etmemizde bir sakınca kalmayacak. O nedenle yavaştan diğerlerine de geçebiliriz. Şimdi Timer0’ın hakkından gelelim 😉

TIMER 0

Timer0 kardeşimiz, 8-bit’lik bir zamanlayıcı/sayıcı olarak tasarlanmış olup, doğal olarak 0’dan 255’e kadar sayabilmektedir. Yine bu efendi kardeşimizin sayacını okumak da sayacına yazmak da mümkündür; bu da bize zamanlayı istediğimiz değerden başlatma ve güncel değeri okuma imkanları sunar.

Zamanlayıcı, esasında; herhangi bir saat işaretini (clock pulse) alıp o işareti çeşitli ön işlemlerle örnekleyerek sayan bir devreciktir. Buna istinaden söz konusu saat işareti dışarıdaki bir osilatörden (Ring osilatör) ya da iç osilatörden (RC osilatör) alınabilmektedir.

Bu zamanlayıcılar 255’e kadar sayabildiğinden 255’ten sonraki sayımda taşma olur yani değer tekrar 0’a döner. Bu anda kesme kurmak mümkündür. Bu da zamanlayıcı kesmesi olarak adlandırılmaktadır.

Timer0 ile alakası olan kütükler, mikrodoentleyicinin veri kağıdında açıkça belirtilmiştir. Buna göre OPTION_REG, INTCON_REG, TMR0_REG, TMR0_REG ve inanmazsınız TRISA/PORTA kütükleri Timer0 ile ilintili imiş. Bunların her birini didik didik ederek zamanlayıcıların suyunu çıkaracağız. Tabi ki yapacağımız ilk iş, henüz modellemediğimiz kütükleri modelleyerek kütüphanemize eklemek olacak.

Tüm bunlara ek olarak, kütüphanemizi yine adım adım optimize etmeye devam edeceğiz. Burada yine tasarım trade-off”larını tartışacağız ve yeni eksikleri hep birlikte tespit edeceğiz 🙂 Öyleyse işe koyulalım ve TIMER0 ile ilk periyodik fonksiyonumuza hayat verelim!

Malumunuz her yazıda yeni kesme değerlendirme rutinleri, yeni veri yapıları ekliyoruz. Bunları her uygulamada kullanmayacağımızdan, bu uygulamalarda biraz bellek israfı yapmış oluyorduk. Şimdi onları bir config dosyası üzerinden seçimli hale getirmenin tam sırası. Yeni config dosyamız “pic16lib_conf.h” aşağıdaki gibi.

Görmüş olduğunuz PIC16FLIB_USE* etiketlerinden ikisi commentlenmiş. Eğer uygulamanızda RB0/PORTB kesmesini kullanacaksanız bunları da açabilirsiniz. Biz bu uygulamamızda yalnızca TIMER0 kesmelerini kullanacağımızdan seçimi o şekilde yaptık.

Hemen güncellediğimiz kütüphane dosyalarımızı görelim.

Timer0 adres ve kütük tanımlamalarının yapılmış olduğuna dikkatinizi çekerim 🙂 Aynı zamanda, config dosyasında kullandığımız etiketler de, kullanılmayan kesmelerin fonksiyon prototiplerini derleme zamanında kapatmak için yerli yerinde. Öyleyse hemen .c dosyamızı görelim.

Görüldüğü üzere, alışılageldik şekilde timer0 kesme rutinlerini de ekledik. Ek olarak yine config etiketlerimiz de fonksiyonları çevrelemiş durumda 🙂 Artık kütüphanemizi kullanan kimselere çok daha geniş optimizasyon imkanı sunuyoruz! Her durumda yine Microchip liblerinden çok daha yüksek performanslı bir konumda kütüphanemiz 🙂 0 byte kullanarak tüm register erişimlerini yapmak mümkün. Konfor arayan yazılımcılar için de, kütük erişimlerini çok kolaylaştıran veri yapılarını implement ettik. İsteyen istediği yönde optimizasyon yapabiliyor. Bu da kütüphanemizin esnekliğini ciddi oranda artırmış durumda. Şimdi uygulama koduna geçelim.

Bu yazılımda, B portlarını 1 milisaniyede bir eviren bir yazılım oluşturduk. Peki ama bunu nasıl yaptık? Hemen hesap kitaba değinelim. PIC mikrokontrolörümüze dışarıdan bir kristal osilatör bağlı ve frekansı 4MHz.

Aman Diyelim
Proteus simülasyonu yapmadan önce, PIC mikrokontrolöre çift tıklayıp FOSC değerini 4Mhz yaparsak, buradaki hesaplarla uyumlu çıktılar alabiliriz. Aksi durumda varsayılan değer olan 1Mhz geçerli olacaktır ve bu durumda istenen çıktılar elde edilemeyecektir.

PIC16F84A’da timer modülüne gelen saat işareti 4’e bölünerek gelir (datasheet’te yazmakta). Dolayısıyla modülün girişi 1MHz yani saniyede 1 milyon saat darbesi geliyor. Timer modülünün giriş kısmında bir frekans ölçeklendirici (prescaler) bulunmakta. Biz ölçeklendirme oranını 8’e bölecek şekilde seçtik. Bunu, aşağıdaki satır ile yaptık.

Buna göre, 1MHz’lik işaret frekansını 8’e böldük ve prescaler çıkışındaki frekans 125Khz oldu. Bu da 8 micro saniyede bir saat darbesi demek. Eğer bu saat darbelerinden 125 tane sayılınca kesme olacak şekilde bir konfigurasyon yaparsak, 8*125 us = 1ms’lik bir kesme rutini elde etmiş olacağız.  Bu sebeple aşağıdaki tanımla 125 değeri tanımlandı.

Bu değer ise timer0 kütüğüne aşağıdaki şekilde atandı.

Burada ibretlik bir macromuz var. Bu macro TIMER0_ASSIGN_CNT. Peki neden buna ihtiyaç duyduk? Malumunuz bizim timer0 modülü yukarı doğru sayıyor ve 255(0xFF)’de taşıyor. Taşmadan önce 125 tur sayabilmesi için aslında 255-125 =130 değerinden başlamalı Timer0. Bu durumda timer 130’dan başlatılacak ve tam 125 tur sonra (1ms yapıyor) kesme gelmiş olacak. İşte bu çıkarma hesabını yapan macromuz TIMER0_ASSIGN_CNT. Kütüphanemizdeki tanımı da aşağıdaki gibi idi.

Bu sayede 1ms’lik zamanlama sağlamış oluyor. Ancak sürekli olarak 1ms’lik zamanlamayı sağlamak için TIMER0 taştığında, yani kesme oluştuğunda Timer0’ı tekrar 130 değerinden başlatmak gerekiyor. Bunun için, milisaniye callback fonskiyonumuzda aşağıdaki gibi bir tekar yükleme yaptık.

Bu sayede her şey çalışır durumda ve enerji efektif şekilde periyodik işlerimizi yapabilir hale geldik 🙂 Hemen proteus isis üzerindeki simülasyon çıktımızı da ekleyelim.

pic_timer0_0

 

Osiloskoptaki zaman ölçümünün 1.06 mS oluşuna dikkat ediniz. 1ms güzel de, bu 0.06 mS nereden geldi? Buradan da çok güzel ibretlere değineceğiz dostlar, ancak bu sorunun yanıtı ve dahasını bir sonraki yazıya bırakıyoruz 🙂

Zamanlayıcılar konusunda ne kadar çok tekrar yaparsanız, farklı uygulamalar yazarsanız o kadar iyi olur. Örneğin karaşimşek uygulamasını zamanlayıcı kullanarak yazmakta faydalar var.

Şimdi devam!

Yazıları beğendiyseniz, faydalanabilecek tanıdıklarınızla paylaşmayı unutmayınız.

Önceki Sayfa   Sonraki Sayfa

5 thoughts on “PIC Programlama – 8 – Zamanlayıcılar (Timers)

  1. Merhaba Özen Bey. Yazı serinizi yeni gördüm. Hızınıza hayran kaldım gerçekten. Emeğinize sağlık. Benim sorum STM olacak. Acaba ne zaman tanışacağız kendileri ile ?

  2. Merhaba Özen Bey. Yazı serinizi yeni gördüm. Hızınıza hayran kaldım gerçekten. Emeğinize sağlık. Benim sorum STM olacak. Acaba ne zaman tanışacağız kendileri ile ?

  3. Merhaba İlhan Bey, olumlu yorumunuz için çok teşekkür ederim. STM açıkçası bundan sonraki yazıdan (Atmel/Arduino) bir sonraki yazı olarak planlanmıştı. Dolayısıyla en iyi ihtimalle önümüzdeki yılın başında başlayabileceğimi düşünüyorum STM yazısına. Bununla birlikte, farklı bir önceliklendirme de yapmak mümkün diye düşünüyorum. STM ile ilgili, konuya ve çalışma ortamına giriş bilgilerine ihtiyacınız var ise, onunla ilgili bir yazıyı çok yakın zamanda yazabilirim. Bu sayede STM32 ile nasıl çalışılacağını aktarmam mümkün. ST firması çok güzel kütüphaneler ile ürününü destekliyor. Bu sebeple, çalışabilmek için çok da fazla bilgiye ihtiyaç olmuyor.

    STM32/LPC17 yazı dizisinde yine giriş bilgilerini verdikten sonra, ileri seviye Arm Cortex programlama, software abstraction, embedded design patterns, nested interrupt control gibi konulara değinmeyi düşünüyorum, bu sebeple uzun bir yol söz konusu. Ancak, giriş amaçlı bir dökümanı hızlıca hazırlamam mümkün.

    Eğer daha fazla bilgiye, acilen ihtiyaç duyuyorsanız ve merak ettiğiniz noktalar var ise mail yoluyla cevaplamaya çalışabilirim. Her durumda, çalışma isteğinize bir karşılık oluşturmak için elimden geleni yaparım.

    Farklı bir öneriniz var ise, onu da mutlulukla değerlendiririm.

  4. Merhaba İlhan Bey, olumlu yorumunuz için çok teşekkür ederim. STM açıkçası bundan sonraki yazıdan (Atmel/Arduino) bir sonraki yazı olarak planlanmıştı. Dolayısıyla en iyi ihtimalle önümüzdeki yılın başında başlayabileceğimi düşünüyorum STM yazısına. Bununla birlikte, farklı bir önceliklendirme de yapmak mümkün diye düşünüyorum. STM ile ilgili, konuya ve çalışma ortamına giriş bilgilerine ihtiyacınız var ise, onunla ilgili bir yazıyı çok yakın zamanda yazabilirim. Bu sayede STM32 ile nasıl çalışılacağını aktarmam mümkün. ST firması çok güzel kütüphaneler ile ürününü destekliyor. Bu sebeple, çalışabilmek için çok da fazla bilgiye ihtiyaç olmuyor.

    STM32/LPC17 yazı dizisinde yine giriş bilgilerini verdikten sonra, ileri seviye Arm Cortex programlama, software abstraction, embedded design patterns, nested interrupt control gibi konulara değinmeyi düşünüyorum, bu sebeple uzun bir yol söz konusu. Ancak, giriş amaçlı bir dökümanı hızlıca hazırlamam mümkün.

    Eğer daha fazla bilgiye, acilen ihtiyaç duyuyorsanız ve merak ettiğiniz noktalar var ise mail yoluyla cevaplamaya çalışabilirim. Her durumda, çalışma isteğinize bir karşılık oluşturmak için elimden geleni yaparım.

    Farklı bir öneriniz var ise, onu da mutlulukla değerlendiririm.

  5. Murat Yiğit

    Hacı abi, 0.06ms nerden geliyo?
    Set_timer(6)
    prescaler 1/4
    veya
    set timer0(131)
    prescaler 1/8
    yapsam da similasyonda 1 ms den fazla bi değer alıyorum. Bi sonraki yazı ne zaman 😀
    şimdiden teşekkürler

Leave a Reply