ATmega328'de ADC 2:Free Running
Geçtiğimiz yazıda ATmega328'in çevre birimlerine kısa bir girizgah yapmıştık. Bugün FreeRunning modu hakkında biraz kod yazalım ve ADC'yi yavaş yavaş kullanmaya başlayalım. Bu mod ile tek kanalda ölçüm yapmaya çalıştığımda bir problem yaşamadım. Fakat birden fazla kanalı okumaya çalıştığım anda işlerin sarpa sardığını gözlemledim. Bunun çözümü diğer yazıda ;)
ADC'yi nasıl çalışmaya hazır hale getirdiğimizden ve nasıl tek kanalda ölçüm alındığından bahsetmek istiyorum. Öncelikle hangi ADC modunu hangi ayarlarda kullanacağımızı belirleyelim. Ölçüme başlamadan önce PRR yazacının PRADC biti mantık 1 seviyesinde ise mantık 0 seviyesine düşürülmelidir. Bu bit etkinken ADC ölçümü yapamazsınız. Güç tasarrufu konusuna geldiğimizde konuyu daha detaylı ele alabiliriz. Aşağıdaki adımların kesin bir sırası olmamakla birlikte bazı yazaçlar ADC açılmadan değiştirilemez.
ADC açılır.
Otomatik Tetikleme etkinleştirilir.
Ön derecelendirici 32 olarak ayarlanır
ADC sola dayalı ölçüm modu seçilir.
Referans voltajı AVCC'ye ayarlanır.
Ölçüm başlatılır.
Şimdi akışımızı programa dökelim ve satır satır açıklamalarımızı yapmaya başlayalım. Deneyler sırasında Microchip firması tarafında geliştirilmiş Xplained Mini kartını kullanacağım. Bu kartın üzerinde dahili bir hata ayıklayıcı bulunduğundan geliştirme sürecinde programcıya yardımcı oluyor.
#include <avr/io.h>
#define F_CPU 16000000
#include <util/delay.h>
uint16_t sonuc;
int main(void)
{
ADCSRA|=(1<<ADEN);//ADC açıldı.
ADCSRA|=(1<<ADATE);//Otomatik tetikleme modu ayarlandı.
ADCSRA|=(1<<ADPS0)|(1<<ADPS1);//Ön derecelendirici 32 olarak ayarlandı.
ADMUX |=(1<<MUX0);//1. ADC kanalı seçildi.
ADMUX |=(1<<REFS0);//Referans voltajı AVCC olarak ayarlandı.
ADMUX|=(1<<ADLAR);//Ölçüm sonuçları sola dayalı olarak seçildi.
ADCSRA|=(1<<ADSC);//Ölçüm başlatıldı.
/* Replace with your application code */
while (1)
{
while(!(ADCSRA&(1<<ADIF)));//Ölçüm bitene kadar bekle.
sonuc=(ADCL>>6)|(ADCH<<2);//ölçüm sonuçlarını hesapla.
}
}
Kodları incelemeye başlamadan önce sizden ricam ATmega328'in veri kitapçığını açmanız ve kodlarla beraber incelemeniz. Yoksa burada gördüklerinizi ezbere yazmanın hiçbir anlamı yok. Unutulmamalıdır ki en güvenilir kaynaklar üçüncü kişilerin hazırladığı belgeler değil üretici firmanın hazırladıklarıdır. Bizim gibi içerik üreticileri sadece konuyu daha iyi anlayabilmeniz için kendi deneyimlerimizi aktarırız. Bir süre sonra bu yazılara pek bakmadan sadece üretici belgelerini okuyarak daha fazla yol katetdiğinizi fark edeceksiniz.
İlk üç satır ile başlayalım. İlk satırımızda giriş çıkışlarımızın dahil edildiği, projeye her zaman eklememiz gereken başlık dosyası bulunuyor. Kullandığımız bütün yazaç ve pin isimleri avr/io.h dosyasında tanımlanmıştır. Üçüncü satırda ise zamana bağlı işlemlerimizi yürütebilmek için util/delay.h başlık dosyasını projeye dahil etmeliyiz. Daha önce değinmediğimiz F_CPU makrosu ise işlemcimizin çalışma hızını belirlediğimiz makrodur. Bu makro util/delay.h içerisinde 1000000 olarak tanımlıdır. ATmega328 fabrikadan ilk çıktığında dahili saat hızı 1MHz olarak ayarlı gelir. Saat ayarları için sizi Mesut Hocamızın YouTube kanalına alalım. Xplained Mini kartında bu ayarlar zaten yapılmış olduğundan F_CPU makrosunu 16MHz'e ayarlamakla yetiniyorum.
F_CPU makrosu _delay_ms() ve _delay_us() fonksiyonları için önemli bir makrodur. Eğer işlemcinin gerçek çalışma hızı ile aralarında fark olursa bekleme fonksiyonları doğru çalışmazlar. Bekleme fonksiyonlarını kullanırken kod optimizasyonunu kapatırsanız kod boyutunuzun inanılmaz boyutlara ulaştığını gözlemleyebilirsiniz. Bundan dolayı zamanla ilgili işlemlerinizi dikkatli yapmanızda yarar var.
Geldik bütün ayarları yaptığımız main fonksiyonuna ;
ADCSRA|=(1<<ADEN);//ADC açıldı.
ADCSRA|=(1<<ADATE);//Otomatik tetikleme modu ayarlandı.
ADCSRA|=(1<<ADPS0)|(1<<ADPS1);//Ön derecelendirici 32 olarak ayarlandı.
ADMUX |=(1<<MUX0);//1. ADC kanalı seçildi.
ADMUX |=(1<<REFS0);//Referans voltajı AVCC olarak ayarlandı.
ADMUX|=(1<<ADLAR);//Ölçüm sonuçları sola dayalı olarak seçildi.
ADCSRA|=(1<<ADSC);//Ölçüm başlatıldı.
İlk Olarak ADC çevre birimizi açarak işe başladık. ADCSRA.ADEN biti ADC'yi etkinleştirmemize yarar. Mantık 1 yazarak ADC'yi açtık. Bazı yazaçlara çevre birimini açmadan ulaşamıyoruz. Bu yüzden ilk adım ADC'yi açmak oldu. İkinci adımda ise ADCSRA.ADATE bitine mantık değerini yükledik. Bu sayede ADC Otamatik Ölçüm modunda başlamış oldu.ADCSRB yazacının ilgili bitleri mantık 0 olduğundan ADC'miz FreeRunning modunda çalışıyor.Detaylar için önceki yazıya bakabilrisiniz.
ADC birimi sinyali ölçmek için sinyalden örnekler alır. ADCSRA yazacının ADPS0 ve ADPS1 bitlerine mantık 1 değeri yükleyerek ADC'mizin çalışma frekansını 16Mhz/32 yaptık. Unutulmamalıdır ki bu ADC'nin çalışma hızı, ADC'nin örnek alma hızı değil. ADPSn bitlerinin değeri ne kadar düşük tutulursa ADC sinyalden o kadar fazla örnek alacaktır.
Önceki yazımızda yazaçların içeriklerini ele almıştık. Orada kanal seçimiyle ilgili MUXn bitlerinin değerinin nasıl olması gerektiği ile ilgili bir tablo vardı. O tablodan yararlanarak kanal seçimi yapabiliriz. ADMUX.MUX0 bitine mantık 1 değerini yüklerseniz PC1'e bağlı olan 1. ADC kanalı kullanılacaktır.
ADC'lerin ölçüm yapmak için referans gerilimine ihtiyacı olduğunu biliyoruz. ADMUX.REFS0 bitine mantık değerini atadığımız zaman ADC AVCC pininin gerilimini referans gerilimi olarak kabul eder. Önemli diğer nokta ise AREF pini ile GND arasına 100nF'lık bir seramik kondansatör bağlanması gerektiği. Bu kondansatörün bağlanması gerektiğini veri kitapçığı söylüyor. Değeri hakkında bilgi vermese de Arduino gibi geliştirme kartlarında bu kondansatörün değeri genellikle 100nF'tır.
ADMUX yazacının ADLAR biti ADC sonuçlarının sola-sağa dayalı kayıt edileceğini seçer. Biz burada ADLAR bitine mantık 1 değerini yükleyerek sola dayalı ölçüm sonuçlarını kullanacağımızı bildirdik. ADCH ve ADCL yazaçlarına bu bitin durumuna göre ölçüm sonuçları sağa veya sola dayalı olarak kayıt edilir.
ADCSRA yazacının ADSC biti ölçümü başlatmamıza yarayan bittir. ADC FreeRunning modundaysa bu bite bir kere mantık 1 değerini yüklememiz yetecektir. ADC sürekli ölçüm yapmaya başlayacaktır. Single Conversation modunda ise her ölçüm sonunda bu bit tekrar tetiklenerek ADC ölçümü başlatılmalıdır.
Son olarak while döngümüzün içerisinde iki satır kod görüyoruz. Bunlardan ilki;
while(!(ADCSRA&(1<<ADIF)));//Ölçüm bitene kadar bekle.
ADCSRA içerisindeki ADIF biti ile ölçümün bitip bitmediğini kontrol edebiliriz. ADIF biti hem FreeRunning hem de Single Conversation modunda kullanılabilir. ADIF biti ölçüm boyunca mantık 0 seviyesinde kalır.FreeRunning modunda da ölçümün bitip bitmediği ADSCRA.ADSC biti kontrol edilerek de yapılabilir.
sonuc=(ADCL>>6)|(ADCH<<2);//ölçüm sonuçlarını hesapla.
Programımızın son satırını anlayabilmek için ADCH ve ADCL yazaçlarının sola dayalı konumlarını inceleyelim.
Burada ADCH ve ADCL yazaçlarının içerisinin ADCSRA.ADLAR bitinin mantık 1 (sola dayalı) olduğundaki durumunu görüyorsunuz. Bu yüzden anlamlı sonuç elde edebilmek için birkaç bit kaydırma işlemi yaptık ve sonucu sonuc isimli değişkene atadık.
Şuan testlerimi Xplained Mini kartında gerçekleştiriyorum. Kart üzerinde hata ayıklama yapabilmek için projemin ayarlarını aşağıdaki şekilde değiştiriyorum.
Debug and Break tuşuna basarak geçerek hata ayıklayıcıyı başlatıyorum.
Açılan pencereden değişkenlerin, işlemci yazaçlarının içerisini inceleyebilirim. Şuan kart üzerinde koşan koda bilgisayar üzerinden müdahale ediyoruz. Kodu adım adım çalıştırabiliriz. Ya da istediğimiz noktalarda programın duraklamasını sağlayabiliriz. Hata ayıklama başlı başına bir yazı konusu olduğundan burada fazla detaya girmeyi uygun görmedim. Kodu çalıştırmak için Run tuşuna basın ardından stop tuşu ile tekrar işlemciyi duraklatın.
Görüldüğü gibi istediğimiz yazaçların içerisini görebiliyoruz. Yaptığımız değişikleri kontrol edecek olursanız yüklediğimiz değerlerin yazaçlara yazıldığını görebilirsiniz. Son olarak watch1 ekranına sonuc yazın işlemciyi çalıştırıp tekrar duraklattığınızda ADC değerinizi sonuc değişkeninde okuyabilirsiniz.
Ben ADC1 pinini kablo ile 3,3 V'a bağladım. Görüldüğü üzere 675 değerini okuyorum. Hemen ufak bir doğrulama yapalım. ADC ile 10 bitlik ölçüm yaptık. Referans gerilimimiz 5 V ise ADC'den okuduğumuz gerilim (5/1024)*675=3,295 V'tur. Gördüğünüz üzere beklediğimiz sonuca ulaştık. ADCH ve ADCL yazaçları okunurken öncelikle ADCL yazacı okunmalıdır. ADCH yazacı önce okunursa, yeni ölçüm sonucu ADCL yazacını içerisine yazılabilir. Bu yüzden sonucun hangi ölçüme ait olduğunu bilemezsiniz. Eğer önce ADCL yazacını okursanız ADCH okunana kadar yazaçlar güncellenmez. Bu sayede ölçüm sonuçlarının doğruluğundan emin olabilirsiniz.
Bu yazıda elimden geldiği kadar FreeRunning modunu anlatmaya çalıştım. Özellikle sayfaya abone olan arkadaşlara çok teşekkür ederim. Hatalı gördüğünüz yerler olursa bildirmekten çekinmeyin.
Free Running Modunda sürekli dönüşüm yapması gerekir. Simülasyon ortamında seçilen adc kanal girişine 3-4 V genlikli kare dalga veya sinüs dalgası uygularsam doğal adc sonucu farklı olması gerekir her seferinde. fakat sürekli aynı dönüşüm sonucunu almam kafamı karıştırdı. Konu hakkında görüşünüzü paylaşabilir misiniz?