Analog Digital Çevirici

Bu projede Atmega8'de bulunan ADC (Analog Dijital Cevirici) modülünü kullanarak dijital bir termometre yapacağız. Bildiğiniz gibi Atmega8'de 8 kanal 10 Bit ADC modülü vardır. Öncelikle bunun ne anlama geldiğine bir bakalım: Mikroişlemcilere çalışırken sıklıkla analog sinyalleri sayısal değerlere çevirme ihtiyacı duyarız. Piyasada bu gibi işler için üretilmiş özel ADC entegreleri bulunmaktadır, ancak Atmega8 işlemcisinde 10 bitlik çevirici bulunduğundan, birazdan LM35 sıcaklık sensörüyle yapacağımız bu devre için ayrıca bir entegre kullanmayacağız. 10 bitlik bilgi ile elde edilebilecek en büyük sayı 2^10 yani 1024 dür. Böyle bir çevirici ile 0-5V aralığını ölçmek isterseniz, elde edeceğiniz çözünürlük 5/1024=4.8 mV olur. Diğer yandan ADC'nin çalışma saati çevrim işleminin ne kadar çabuk yapılabileceği alakalıdır ve tam çözünürlük için 50 ile 200khz arasında bir değer seçilmelidir. ADC saat hızı işlemcinin saat hızının bölenleri şeklinde özel ADCSRA yazmacı tarafından belirlenir. Projemizde dikkat ederseniz ADCSRA yazmacına yazdığımız değer 0x83'dir (onaltı tabanına göre 83 ve iki tabanına göre karşılığı 0b10000011). En sağdaki üç bit işlemci saatinin kaça bölüneceğini bildirir Atmega8'in elkitabındaki tabloya göre 011 işlemci saatinin 8'e bölünmesi anlamına gelmektedir, dolayısıyla ADC saat hızı 1000000/8=125000 Hz ya da 125 Khz olacaktır. En soldaki bit ise (8'inci bit) ADC modülünün etkinleştirileceği anlamına gelmektedir (Atmega8 elkitabında ADC ile ilgili geniş bir bölüm bulunmaktadır).

ADC modülünün referans bir voltaja ihtiyacı vardır, bu projede mikroişlemcinin içinde bulunan 2.56 Voltluk referans voltaj seçeneğini kullanacağız. ADCMUX yazmacına 0xC0 yani iki tabanına göre 0b11000000 değerini yazarak işlemciye dahili referans voltajını kullanacağımızı söylemiş olduk (7 ve 8 inci bitlerin değeri 1 dir).

read_adc fonksiyonu parametre olarak kanal bilgisini almaktadır, 8 adet ADC kanalından hangisinin değerini okumak istediğimizi bu fonksiyona parametre olarak vermemiz gerekir. Projemizde kanal 0 (sıfır) seçildiğinden LM35'in Vout bacağını (bakınız Resim3) ADC0'a yani PC1 portuna bağlayınız. PC1 portunun aynı zamanda ADC0 kanalı olarak kullanıldığını Atmega8'in bağlantı şemasından görebilirsiniz (Resim4). LM35 entegresinin Vout bacağında cevre sıcaklığı ile doğru orantılı bir voltaj oluşur ve bu voltaj her 1 santigrat derece için 10mV dir.

Projemizdeki termometre +2 ila +150 derece arasındaki sıcaklıkları ölçer, -55 ila +150 arasındaki sıcaklıkları ölçen bir termometre yapmak için Resim5'deki devreyi kullanmanız gerekmektedir.

Ortam sıcaklığı kayan-noktalı (floating point) bir sayı olduğu için Make dosyasındaki Library Options kısmında;

PRINTF_LIB = $(PRINTF_LIB_FLOAT) seçeneğini aktif hale getirdiğimize dikkat edin. Eğer bu seçeneği aktif hale getirmeseydik printf("Sıcaklık= %2.1f", temp); komutu ekrana anlamsız karakterler basacaktı. Bu seçeneğin aktif hale getirilmesi sonucu programımızın işlemcinin program hafızasının %62'sini işgal etmesi ise printf fonksiyonuna eklenen kayan-nokta desteğinin maliyetidir.

#include <stdio.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "lcd.h"

void delay_ms(uint16_t ms);
uint16_t read_adc(unsigned char adc_input);

//Bu satir ile bir stream objesi yaratilip lcd_put_char fonksiyonuna
//yonlendiriliyor. Printf ve turevleri icin gerekli
FILE lcd_str = FDEV_SETUP_STREAM(lcd_putchar, NULL, _FDEV_SETUP_WRITE);

static void adc_init(void){
// ADC Hizi: 125 kHz
// ADC Voltaj Referansi: Dahili 2.56V
ADCSRA=0x83;
}

//Ana Program Fonksiyonu
int main(void){
uint16_t x;
double temp;
adc_init();
lcd_init(); //lcd'yi hazirla

//standart giris ve cikis lcd_str olsun
stdout = stdin = &lcd_str;

lcd_goto(1,4); //satir 1, sutun 4e git
fprintf_P(stdout,PSTR("ADC Test"));
lcd_goto(2,1); //satir 2, sutun 1e git
fprintf_P(stdout,PSTR("www.berik.net"));
delay_ms(2500);
lcd_clr();

while(1){ //sonsuz dongu
x=read_adc(0);
temp=(x*2.56)/1024;
temp*=100;
lcd_goto(1,0);
printf("Sicaklik= %2.1f",temp);
delay_ms(500);
}
}

void delay_ms(uint16_t ms){
for(uint16_t c=0;c<ms;c++)_delay_ms(1);
}

// ADC cevrim degerini oku
uint16_t read_adc(unsigned char adc_input){
ADMUX=adc_input | 0xC0; // cevrime basla
ADCSRA|=0x40; // cevrim islemi bitene kadar bekle
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}

Proje Dosyaları

Proje Dosyaları