AVR WDT Kütüphanesi ve Uygulaması

Bu başlıkta yazdığım bekçi zamanlayıcısı (watchdog timer) kütüphanesini size anlatacağım ve bir uygulama ile bunun nasıl kullanılacağını göstereceğim.

Bekçi zamanlayıcısı (watchdog timer) pek çok mikroişlemci sisteminde yer alan ve sistem için hayati bir görevi üstlenen bir birimdir. Hayati bir görevi üstlenmesine rağmen oldukça basit bir yapıya sahiptir. Bunu çıkış sinyali reset birimine bağlı bir zamanlayıcı gibi düşünebiliriz. Zamanlayıcı taştığında çıkış sinyali alınmakta ve bu sinyal reset birimini tetikleyerek sistem resetlenmektedir. Sistemin resetlenmemesi için belli periyotlarla bu zamanlayıcı tekrar sıfırlanması gereklidir. Böylelikle sistem donduğunda, bir şekilde resetlenemez hale geldiğinde (mesela sonsuz döngü durumları gibi) sistemi bu zamanlayıcı resetleyerek tekrar çalışabilir hale getirmektedir. Bu yönüyle bilgisayarlardaki meşhur mavi ekrana benzemektedir. Yazdıklarımı tekrar etmeme adına “C ile AVR Programlama” derslerinin ilgili başlığını buraya bırakıyorum.

Şimdi kütüphanenin .h dosyasını incelemekle yazıya devam edelim.

İlk öncelikle zamanlayıcı modlarını belirledik. Bu modlar INTERRUPT, RESET ve INTERRUPT_RESET şeklindedir. Yani zamanlayıcı sadece kesmeye gidebilir, sadece reset işlemi yapabilir veya bu ikisini beraber yürütebilir. Daha sonra ise zamanlayıcının taşma eşiğinin kaç milisaniye olacağını tanımlarda belirliyoruz. En kısa süre MS16 ile 16ms, en uzun süre ise MS8000 ile 8 saniyedir. Burada sentaks kurallarından gereği 16MS gibi bir tanım daha anlamlı olsa bile yapılamamaktadır. Buradaki değerleri tek tek datasheetteki tabloya bakıp bit olarak yazdığımı söylemeliyim. Kütüphane yazarken özellikle .h başlık dosyasındaki sayısal değerler datasheetten gelmekte ve başta bunu yazarken biraz zahmet çekmekteyiz. Ama bir kere yazdıktan sonra bir daha datasheete bakma ihtiyacı duymamaktayız. Bu sayede işimiz oldukça kolaylaşmakta. Datasheet okuyabildiğimiz halde yine kütüphane yazıp kullanmamızın sebebi budur.

En son olarak wdt_off(), wdt_prescaler() ve wdt_on() şeklinde kütüphane fonksiyonlarını görmekteyiz. Bu fonksiyonlar adından anlaşılacağı üzere ilki WDT’yi kapatmakta, bir sonraki ön bölücü değerini ayarlamakta ve üçüncüsü ise başlatmaktadır. wdt_reset() ise bir fonksiyon değil makrodur. Bunu makro olarak ayarlamamamızın sebebi C dilinde fonksiyon çağırıldığı zaman bu Assembly diline çevirilirken atlama komutları çalıştırılmaktadır. JMP benzeri bu komutlar mikrodenetleyiciyi birkaç saykıl meşgul etmekte ve kod gecikmeli olarak çalışmaktadır. Ben öneminden dolayı wdt_reset() diye çağırır çağırmaz sıfırlanmasını istiyorum. Bu yüzden bunu makro halinde yazdım, böylelikle kopyala yapıştır şeklinde kod yazılmış olacaktır.

Daha önce SLEEP komutunda gördüğünüz gibi C dilinde WDT’yi sıfırlamak için bir komut, fonksiyon olmadığı için asm(“WDT”) şeklinde bunu kullanmaktayız. Görüldüğü gibi C dilinde kelimenin tam anlamıyla herşey yapılmıyormuş.

Şimdi .c dosyasını inceleyerek yazıya devam edelim.

Burada WDT’nin ön bölücü ayarı, açma ve kapama olarak üç işlevi için üç ayrı fonksiyon tanımladım. Açma ve kapamadan önce ön bölücü wdt_prescaler() fonksiyonu ile yapılan ön bölücü ayarından bahsedeyim.

Bunun için öncelikle hassas bir işlem yaptığım için kesmeleri cli() ile kapattım ve WDR komutunu işlettim. Bu komutu işlettikten sonra WDCE ve WDE komutlarını işletip ve hemen sonrasında ise WDE bitini bir yaparak WDTCSR yazmacının son bitlerine ön bölücü değerini yazdım. Bunu bu kurala uygun ve sırasıyla yapma şartı vardır. Daha önce uyku modu ayarlarında gördüğünüz gibi mikrodenetleyicide bir güvenlik mekanizması var ve rastgele yazmalara karşı kendini korumakta. Bu yazma işlemini yaptıktan sonra sei() ile kesmeleri açıyoruz.

wdt_on() fonksiyonu ile beraber daha öncesinde bölme değerini ayarladığımız zamanlayıcıyı ilgili modda başlatıyoruz. Bu modlar INTERRUPT, RESET ve INTERRUPT_RESET olabilir. İsterseniz hiç reset işlemi ile uğraşmadan belli periyotlarda kesmeye götüren bir zamanlayıcı olarak da kullanabilirsiniz. Böylelikle örneğin ATmega328P’de T0, T1 ve T2 zamanlayıcıları yanında bunu da kullanma imkanınız olur. Üstelik bekleme süresi diğerlerinden daha uzun olabilmekte. WDTCSR yazmacında WDIE biti ile WDT kesmesini etkinleştiriyor, WDE biti ile de WDT’yi etkinleştiriyoruz.

wdt_off() fonksiyonunda ise bunu kapatmak için yine belli kuralları yerine getirmemiz gerekiyor. Öncelikle kesmeleri kapatıp bunun ardından WDRF bitini sıfırlıyoruz. WDE’nin sıfırlanması için öncelikle bu bitin sıfırlanması gerekli. Sonrasında bitlere bit yazıp yazmacın değerini sıfırlıyoruz. Bunu mantığa oturtmak için “güvenlik önlemi” olduğunu hatırlamanız gereklidir.

Şimdi örnek bir uygulama yaparak bunun kullanımını görelim.

Burada D portunun 0 numaralı ayağını çıkış olarak ayarlayıp buna bir LED bağladım. Siz de LED, buzzer ve benzeri bir şey bağlayabilirsiniz. Sonrasında ise sei() ile kesmeleri açtım. Program boyunca kesme kullanacağımız için öncesinde de #include direktifi ile avr/interrupt.h dosyasını programa ekledim. Sonrasında ise wdt_prescaler(MS250) ise WDT ön bölücüsünü 250ms olarak ayarladım. Yani her 250 milisaniyede bir kesmeye gidecek. Sonrasında wdt_on(INTERRUPT) diyerek sadece kesme fonksiyonu ile WDT birimini açtım. Burada kesmeyi kullanmak için kesme rutini oluşturmamız gerekli. Aşağıda ISR(WDT_vect) diyerek bunu oluşturdum. Hangi vektörü kullanacaksanız parantez içine onu yazmanız gerekiyor. Sonranda ise XOR operatörü ile aç/kapa işlemi yaparak programı bitirdim.

Programı çalıştırdığınızda ise 2Hz frekansında bir çıkış elde edeceksiniz. Bu programı belli aralıklarda yapacağınız işlemlerde kullanabilirsiniz. Aynı şekilde eğer WDT’yi resetleme için kullanacaksanız wdt_on() fonksiyonunda INTERRUPT yerine RESET yazarak kullanabilirsiniz. WDT reset işleminin düzgün yapılması gerektiğine, aksi halde yaptığınız programın kullanılamaz hale geleceğine dikkat ediniz.

--

--