AVR Analog Karşılaştırıcı ile ADC Uygulaması

Bu makalede yaptığım ilginç bir uygulamayı anlatacağım. Mikrodenetleyicinin diğer birimlerinden faydalanarak ve harici birkaç devre elemanı ile kendi ADC birimimizi yapacağız. Üstelik bu ADC birimi bildiğimiz klasik ADC’lerden biraz daha farklı olacak.

Bu uygulamayı yapma fikri üreticinin sağladığı uygulama notlarını incelerken AVR400 kodlu uygulama notunu görünce geldi. O uygulama notunda Assembly dilinde örnek kodlar da mevcuttur.

Bu kodlara hiç bakmayıp baştan uygulamayı kendim C ile yapayım dedim. Onun için öncelikle şöyle bir devre kurdum.

Burada donanımsal olarak analog giriş karşılaştırıcıya bağlı olsa da arka tarafta da yazılımsal olarak bir zamanlayıcı çalışmakta ve bu karşılaştırma değerleri arasında geçen süreyi bize analog okuma değeri olarak vermektedir. Çalışma prensibi olarak sayaç tipi ADC’ye benzemekte.

Yukarıda sayaç tipi bir ADC’nin şemasını görmekteyiz. Görüldüğü gibi Vin kısmı aynı bizim analog karşılaştırıcı birimi gibi çalışmakta. Bunun yanında CTR adı verilen bir sayaç mevcut. Bu sayaç yerine biz uygulamada mikrodenetleyicinin zamanlayıcısını kullanacağız. En son olarak DAC yani dijital analog dönüştürücü olarak ise direnç ve kondansatör ikilisini kullanacağız. Kondansatör önce şarj edilecek, sayaç başlatılacak sonra ise deşarj edilip karşılaştırma zamanına kadar geçen süreyi hesaplayacak. Bu süreci osiloskopta incelediğinizde üçgen dalga benzeri bir sinyal elde edersiniz.

Devrenin analog girişi belli bir aralıkta sağlıklı çalışmakta. Direnç ve kondansatörün değerlerini değiştirerek bu çalışma aralığını daraltıp genişletme imkanınız mevcut ama yukarıdaki değerlerin şu ana kadar en iyi sonucu verdiğini söylemeliyim. Bu çalışma aralığı 5V çalışma geriliminde 1–4V arasıdır ve 4–5V arası ile 0–1V arası sağlıklı sonuç vermemektedir. 0 volta düşünce tekrar okuma yapmama gibi bir sorun da mevcuttur. Bu yüzden analog giriş olarak kullanacağım potansiyometreye sınırlandırıcı iki direnç ekledim.

Bu dirençler uygulamada 4.34V ile 0.850V arasında gerilim elde etmemi sağlayacaktır. Sınırlayıcı dirençlerin değerini biraz daha büyütebilirsiniz.

Şimdi kodu yükleyelim ve çalıştıralım.

Burada daha önce bahsettiğim port kütüphanesini kullandım. Port kütüphanesi bana oldukça kolaylık sağladı ve bundan sonra neredeyse bütün AVR uygulamalarında bu kütüphaneyi kullanmayı düşünüyorum.

Öncelikle burada yer alan D2 ayağını şarj/deşarj ayağı olarak bilmemiz gereklidir. D2 ayağı çıkış olarak ayarlanıp başta HIGH konumuna getirilmekte yani kondansatör şarj edilmektedir. Sonrasında ACSR yazmacında ACIE biti ile analog karşılaştırıcı kesmesi etkinleştirilmekte ve sonrasında düşen kenarda kesmeye götürülmesi ayarlanmaktadır. Kondansatör deşarj edileceği zaman düşen kenar söz konusu olacağından kesme anı iki gerilimin eşleşme anı olacaktır.

Ana program akışında ise adc_oku() fonksiyonunu işletmekteyiz. Şimdi bu fonksiyonu inceleyelim.

D2_out = LOW; // Deşarj  
TCCR1B |= (1<<CS10); // Zamanlayıcıyı başlat kesme bekle

Burada D2 ayağı LOW yani sink konumuna çekilerek kondansatördeki akımı şaseye akıtmaya başlıyoruz. Hemen ardından TC1 zamanlayıcısını hızlı bir şekilde başlatıyoruz. Bu zamanlayıcı ne kadar hızlı çalışırsa o kadar hassas okuma yapabiliriz. Ama bir yandan da 16-bitlik değer aralığının taşmaması gereklidir. Uygulamada bunun taşmayacağını, gayet hızlı ölçüm yapıldığını görebilirsiniz. Bundan sonra ise _delay_us(1000) diyerek artık kesmenin gerçekleşmesini bekliyoruz. Mikrodenetleyici delay işini yürütürken kesme gerçekleşecek ve kesme fonksiyonuna gideceğiz. Şimdi kesme fonksiyonunu inceleyerek devam edelim.

ISR(ANALOG_COMP_vect)
{
analog_deger = TCNT1;
TCCR1B &=~(1<<CS10);
TCNT1 = 0;
D2_out = HIGH; // şarj Et
_delay_us
(1000);
}

Kesme fonksiyonuna gittiğimizde yapacağımız ilk iş zamanlayıcının sayaç değerini okumaktır. Sonrasında ise zamanlayıcıyı durdurup sayacı sıfırlıyoruz. Okuduğumuz sayaç değeri ADC okuma değeri yerine geçmekte. ADC’yi tekrar okuma yapması için hazır konuma getirmek için şarj etmemiz gerekli. Bunu da D2 ayağını HIGH yaparak yapmaktayız. Sonrasında ise şarj için 1000 mikrosaniyelik bir bekleme yapmaktayız.

Yani algoritmayı şöyle özetleyebiliriz

  • Kondansatörü deşarj et
  • Zamanlayıcıyı başlat
  • Kesmeyi bekle
  • Zamanlayıcıyı oku
  • Kondansatörü şarj et

Şimdi yaptığımız ADC’nin ne kadar iyi sonuçlar verdiğine bakalım. Uygulama notunda 6 bit çözünürlük dense de ben burada 880–9400 arası değer almaktayım. Bu da yaklaşık 13 bitlik bir değer aralığına denk gelmektedir. Mesela yaklaşık 2V’da 4800 değerini alırken yaklaşık 3V’da 2721 değerini aldım. Bu da yaklaşık 0.5mV hassasiyette ölçüm yapacağımız anlamına geliyor.

Yalnız bunları duyup çok heveslenmemeniz gerekir. Öncelikle bizim ölçümün tamamen direnç ve kondansatör devresine bağlı olduğunu unutmayın. Direncin ve kondansatörün toleransı her bir devreyi etkileyebileceği gibi bunların sıcaklıktan da etkilendiğini unutmayın. Üstelik okumaların biraz gürültülü olduğunu göreceksiniz. Bunun için pek çok okuma yapıp ortalamasını alabiliriz.

Bunun için ana program akışını şu şekilde değiştirelim.

Bu programı çalıştırdığınızda değerlerin oldukça stabil olduğunu ve mikrodenetleyicinin ADC birimi dahil alacağınız pek çok ADC entegresini aratmadığını görebilirsiniz.

Burada toplam 30 ölçüm yapılıp ortalama alındığından toplamda 30 kat daha yavaş bir ölçüm söz konusudur. Tek kötü yönü doğrusallıktan oldukça uzak olması diyebiliriz. Bunun için tamamen düzgün bir ters testere dişi dalgaya ihtiyaç duyulur bunun için de normal ADC’lerde yukarıda gördüğünüz gibi DAC kullanılmakta.

Mikrodenetleyici ile yapılan bu tarz analog uygulamalar ayrı bir keyifli olmaktadır. Burada çözünürlüğü şarj-deşarj süresi etkilediğinden bu süreyi uzatarak daha yüksek çözünürlüklü bir ADC geliştirmeye çalışabilirsiniz.

--

--