
ADC Nedir?
Analog dijital dönüştürücü, sürekli bir voltaj değerini (analog), daha sonra dijital hesaplama için kullanılabilen dijital bir cihaz tarafından anlaşılabilen bir binary değere (dijital) dönüştüren bir devredir. Bu ADC devreleri, kendi başlarına ayrı bir ADC IC’leri olarak bulunabilir veya bir mikrodenetleyiciye gömülebilir. Kısaca ADC (Analog to Digital Converter) olarak adlandırılırlar.
Bu yazıda Atmega328p mikrodenetleyicisi içerisinde bulunan dahili ADC biriminin özelliklerine, registerlarına ve kullanımına değineceğiz.
Atmega328p ADC birimi özellikleri;
- 10 bit çözünürlük
- 8 kanal giriş
- Dahili sıcaklık sensörü()
- 65 ile 260 μS arası çevrim zamanı
- 150 kSPS e kadar ölçüm
- 1.1V seçilebilir referans voltajı
- 0-VCC arası ADC giriş voltajı
- ADC çevrim tamalanma kesmesi
Yukarıda orta seviye projelerde adc kullanımı için ilgilendiğimiz özellikleri sıraladım. Fakat Atmega328p deki ADC birimi daha bir çok ileri seviye özelliğe (gürültü önleme, uyku modu vs) sahip bunlara datasheetten ulaşabilirsiniz.
ADC Çevrim Zamanı
ADC çevrimi, ADCSRA registerındaki ADCSC biti set edildikten sonra ADC clock cycle’ının yükselen kenarında başlar. Normal bir ADC çevrimi 13 ADC clock cycle’ı kadar zaman alır. Fakat, ADC birimi aktif hale getirildikten sonraki ilk çevrim 25 ADC clock cycle’ı zamanda gerçekleşir.

Normal çevrimde, 13 ADC clock cycle’ı kadar geçen çevrim zamanına ek olarak 1.5 ADC clock cycle’ı da örnekleme ve tutma zamanı için harcanır. Bir ADC çevrimi toplam olarak 13.5 ADC clock cycle’ı kadar sürer.

ADC Çözünürlük Ve Ölçüm Değeri Arasındaki İlişki
ADC birimimiz 10 bit çözünürlükte olduğu için çevrim sonucumuz 0 ile 210 yani 1024 arasında olacaktır. Elde ettiğimiz binary verinin analog karşılığını aşağıdaki formül ile bulabiliriz.

Buradaki “ADC” çevrim sonucu elde ettiğimiz değer. VIN ADC giriş pinine uygulanan analog değer, VREF ise bizim belirlediğimiz referans değeri yani 1024’e karşılık gelecek olan değer.
ADC Registerları

REFS1:0 : Bu bitleri referans voltajı seçerken kullanırız. Değerleri aşağıdaki tablodaki gibidir.

ADLAR : Bu bit değeri elde ettiğimiz 10 bitlik sonucu ADCH ve ADCL data registerlarına yazma türünü belirler. Eğer ADLAR biti set edilirse 10 bitlik adc değeri 16 bitlik hafızada sola dayalı olarak yerleştirilir, RESET konumunda iken de sağa dayalı olacaktır. Aşağıdaki görsel daha anlaşılır hale getirecektir.

MUX3:0 : Bu bitler adc giriş kanalını seçmemizi sağlar. Değerler aşağıdaki tablodaki gibidir.


ADEN : Bu biti set etmek ADC birimini aktif hale getirir. Aynı şekilde ADC birimini kapatmak için ADEN biti 0 yapılır.
ADSC : Bu bit ADC çevrimini başlatmak için kullanılır. Tek çevrim modunda ADSC biti set edildiği zaman çevrim başlar.
ADATE : Bu bitin bir yapılması ADC otomatik tetikleme modunu aktif hale getirir.
ADIF : ADC kesme bayrağıdır.
ADIE : Bu biti 1 yapmak ADC çevrim tamamlanma kesmesini aktif hale getirir.
ADPS2:0 : Bu bitler, sistem saat frekansı ile ADC saat frekansı arasındaki bölme faktörünü belirler. Değerler aşağıdaki tablodadır. ADC birimini istediğimiz saat frekansında çalıştırabiliriz.

ADC Örnek
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define USART_BAUDRATE 9600
#define UBRR_VALUE (((F_CPU/(USART_BAUDRATE*16UL)))-1)
void serial_init();
void serial_sends(char* s);
void serial_send(unsigned char data);
void adc_init(void);
int adc_read(void);
int main(void)
{
char data[31];
int adcdeger = 0;
DDRC &= ~(1<<5); // ADC5 kanalının bağlı olduğu PC5 i giriş olarak tanımladık
serial_init();
adc_init();
while (1)
{
adcdeger = adc_read(); // ADC5 kanalından ölçüm yap
sprintf(data,"ADC deger : %d \n \r",adc deger); // ölçülen degeri karakter dizisine at
serial_sends(data); // karakter dizisini uart a gönder
_delay_ms(1); // 1ms bekle
}
}
void adc_init(void)
{
ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); // 125Khz ADC referans saati
ADMUX |= (1<<REFS0); // Referans AVCC yani 5V
ADCSRA |= (1<<ADEN); // ADC'yi Aç
ADCSRA |= (1<<ADSC); // İlk deneme ölçümünü yap ve diğer ölçüme hazır hale getir.
}
int adc_read(void)
{
ADMUX &= 0XF0;
ADMUX |= (1<<2)|(1<<0); //ADC5 Kanalı seçildi
ADMUX &= ~(1<<3);
ADMUX &= ~(1<<1);
ADCSRA |= (1<<ADSC); //ADC çevrimi başladı
while(!(ADCSRA & (1<<ADIF))); // çevrimin bitmesini bekle
return ADCW;
// ADCH ve ADCL degerlerinin bir arada bulunduğu register
}
void serial_init()
{
UBRR0=UBRR_VALUE; // baud rate ayarla
UCSR0B|=(1<<TXEN0); //TX aktif
UCSR0B|=(1<<RXEN0); //RX aktif
UCSR0B|=(1<<RXCIE0); //RX complete interrupt
UCSR0C|=(1<<UCSZ01)|(1<<UCSZ01); // no parity, 1 stop bit, 8-bit data
}
void serial_sends(char* s)
{
// gönderilen karakter NULL olana kadar adresi bir arttırarak gönder
while(s > 0) serial_send(s++);
}
void serial_send(unsigned char data)
{
UDR0 = data; // data yı gönder
while(!(UCSR0A&(1<<UDRE0))){}; //Bir önceki byte gönderilene kadar bekle
}
Bu örnekte kullandığım UART ile ilgili daha fazla bilgiye AVR Programlama USART yazımdan ulaşabilirsiniz.
