Hi-Tech Eğitim Bölüm10 ( ADC okuma )

Başlatan arslan74, 09 Şubat 2008, 10:43:45

arslan74

Merhaba,

Bir önceki dersimizde LCD ekrana değişik türlerde ki değerleri ekrana nasıl yazabileceğimizi anlatmıştım.

Şimdi ise ADC kullanımı anlatacağım. Basit bir ADC okuma yapacağız ve okuduğumuz değeri LCD ekranda göstermeye çalışacağız.

Önce protları ayarlayacağız. ADC bacağını giriş olarak ayarlanmak zorundayız. ADC ayarların nasıl yapıldığını öğrenmek için 16F877 datasheetlerini acıp inceleyelim.

Bölüm "11.0 ANALOG-TO-DIGITAL CONVERTER (A/D) MODULE" ayrıntılı anlatılıyor.

Bizim için iki tane önemli register var. ADCON0 ve ADCON1 registerleri.

PIC ADC özelliklerinden bahs etmek istiyorum içinde sadece bir tane 10bit ADC var. Ama programla secebileceğimiz 8 tane farklı okuma kanalı var. Biz RA0 bacağından okuma yapacağız dolaysıyla "channel 0" secememiz gerekiyor. ADC okuma hizi ayarını seceneği var. Bu hızı kristal hızına görede ayarlayabiliriz, yada kristalden bağımsız kendi iç RC osilatörüne ilişkilendirebiliriz. Ben Kendi iç RC olsilaörüne bağladım. Dolaysıyla Kristal hizlarınıda değiştirmiş olsak bile bizim için önemli olmayacak. ADC okumayı devreye alıyoruz.




ADCON1 register ayarında ise;
Sadece RA0 Bacağını analog yapacağız. Diğer bacakları dijital olacak. Vref+ yi VDD ye, Vref- yi VSS ye ilişkindendireceğiz.



Aşağıdaki ADC devresinin blok şeması var.



Bildiğimiz gibi kullandığımız PIC 8 bit bir işlemci. dolaysıyla içide tutuğu değerleri. 8bit olarak tutuyor. Peki PIC 10 bit ADC nasıl ve nerede tutuyor?

PIC ADC değerlerini ADRESL ve ADRESH diye iki değikende tutuyor.
Bu iki değişkende nasıl tutulacağını ADFM biti belirliyor.

Aşağıda şekilde örnek şeması var.



ADFM 0 yaparsak sadece ADRESH değeri ile 8 bit ADC okuma yapabiliriz.

Biz ADFM yi 1 yapacağız ve ADRESL ve ADRESH değerlerini "unsigned int" değerine taşıyacağız.

Bunu iki yolla yapabiliriz. int değerine ADRESL değerini tararız. Sonra ADRESH değerini 256 ile carpib int değerine ekleriz.

Yada bir başka yol ise. PIC'te int değeri de iki byte tutuluyor. int değerinin düşük byte ine ADRESL değerini, int değerinin yüksek byte ina ise ADRESH değerini atadığımızda ADC değerini bir int değişkene atamış oluruz.

Bir başka yol ise struct ve union yapılarını kullarakta olabilir. Onlara fazla girmeden en basit yolla yapalım.

Şimdi kod kısmına gecelim.

Programın başında ön ayarları yapalım.



Şimdi ise ADC okuma işlemimi yapan basid bir fonksiyon yapalım. Bahs ettiğim ilk yöntemi yapiyorum. Okuma işlemi bittikten sonra, önce ADRESL değerini int atiyorum, sonra  ADRESH int türüne dönüştürüb 256 ile çarpiyorum. Bu dönüştürmeişlemi olmaz ise hata verecektir. Cünkü 8 byte zaten en fazla 256 değerini saklayabilir, mecburen türünü değiştirmek zorundayız.



şimdi ana program içine söyle kısa bir program yaziyorum. Önce ADC değerini okuyan fonksiyonunu cağriyorum. bu değeri adc_val de sakliyorum. Sonra adc_val değerini ekrana yaziyorum.



Şimulasyonu çalıştırdığımızda karşımıza söyle bir görüntü ortaya cıkıyor. PIC'e 3.75Volt gelmesine rağmen ADC gösterdiği değer 767 dir.



Nedenine geline. ADC çalışman mantığı söyle Vref+ ve Vref- parametrelerini ADC nin cözünürlüğü oranında böler. Yani burada 0 ile 5 volt arasını 1023 parcaya  bölmüş olur. Bu değeri volt olarak görmek istiyorsak. o zaman matematiksel bir işlem yapacağız. basit bir oran orantı hesabı. 5 V -> 1023 ise, 767 kac kac volt eder?

Volt = (5/1023)*adc_val  şeklinde bir formul karşımıza cıkıyor. küsüratlı sayıları göstereceğimiz için bir tane float değişkeni tanımliyacağım.

Kodun yeni hali bu şeklide oldu. 5 değerinin yanına nokta koymamın nedeni onu float türü olarak belirtmek için.



Simulasyonu çalıştırtıralım.



Artık ADC değerinide Volt cinsinden görebiliyoruz.

Projenin download linki,

http://www.4shared.com/file/37297171/fe598cbd/Blm10.html?dirPwdVerified=5a7b6e3a

Bütün dosyaları indirmek için link,

http://arslanweb.4shared.com/

Çalışmalarızda başarılar

semesu

mrb oncelikle teşekkür ederım  bu sekılde calısmalar yaptıgınız icin.
ben ilk defa adc uygulaması yapacagım programda anlamadgım yer surası;
   while(ADGO || ! ADIF)
   contiune;
   val=ADRESL;
   val=((unsigned int)ADRESH*256);


bende olan kıtapta ise bu kısım su sekılde yazılmıs ;

while((ADCON&4) !=0);
yuksek=ADRESH;
dusuk=ADRESL;
volt=256.0*yuksek+dusuk;
volt=volt*lsb;


bu iki programdada bu kısımları  anlamamda yardımcı olursanız sevınırım

iyi calısmalar

arslan74

Alıntı yapılan: "semesu"mrb oncelikle teşekkür ederım  bu sekılde calısmalar yaptıgınız icin.
ben ilk defa adc uygulaması yapacagım programda anlamadgım yer surası;
   while(ADGO || ! ADIF)
   contiune;
   val=ADRESL;
   val=((unsigned int)ADRESH*256);


bende olan kıtapta ise bu kısım su sekılde yazılmıs ;

while((ADCON&4) !=0);
yuksek=ADRESH;
dusuk=ADRESL;
volt=256.0*yuksek+dusuk;
volt=volt*lsb;


bu iki programdada bu kısımları  anlamamda yardımcı olursanız sevınırım

iyi calısmalar

Merhaba,

Hangi PIC le çalışma yaptığınız ve hangi kitaptan örnek aldığınızı belirtmemişsiz. Eğer benim kullandığı PIC göre örnek veriyorsanız. En başta 16F877 de ADCON diye bir register yok.

ACD işlemleri için iki tane resigter var ADCON0 ve ADCON1 registerleri. Onların özelliklerini de zaten PIC kendi datasheetlerinden örnek gösterek en başta anlattım.

Şimdi diğer arkadaşın yaptıklarını inceleyelim.

((ADCON&4) !=0)


Eğer 877 kullaniyorsan ADCON0 yazılması gerek. ADCON0 'i 4 değeri ile bitsel and işlemine tabi tutmuş. 4 binary karşığı 0b00000100 dür. Yani ADCON0 2.ci bitini kontrol ediyor, oda ADGO bitine karşılık geliyor. Yani sadece ADC işleminde ADGO bitini kontrol ediyor. Sadece bu biti kontrol edilerekte bu işlem gercekleşebilir. Bizimkisi fazladan bir kontrol mahiyetinde ADIF bitini kontrol ediyor.

ondan sonraki kod da ise,

yuksek=ADRESH;
dusuk=ADRESL;
volt=256.0*yuksek+dusuk;
volt=volt*lsb;


Benim yaptığıma benzer bir örnek yapmış. yanlız sen burda değişkenlerin türünü yazmamışsın. Ama yapılan işleme bakılırsa "volt" float bir değişen olması gerek. Bense önce ADC unsigned int türüne dönüştürmüşüm. Burda ise ADC değerini doğrudan float türüne dönüştürmüş. Kendim ADC değerini Volta dönüştürmek için aşağıdaki formülü kullanmışım.

Volt = (5./1023)*adc_val;


senin örnekte ise aşağdaki gibi yapmışlar.

volt=volt*lsb;


"lsb" ne olduğunu yazmamışsın ama benim örnekten yola cıkarsak (5./1023) sonucunu "lsb" diye bir değişkende saklamış ve ADC değerini bu sabitle carptığı için, ADC değerini Volt olarak elde etmiş. Ama volta cevirme formülünü aşağıdaki gibi

volt *= lsb;


deseydi biraz daha professiyonel C kodu olmuş olurdu. İkiside aynı anlama geliyor. Yanlız C programcıları aşağıdaki şeklini daha cok kullanır.

Birde bütün işlemleri float türde yaptığı için bunun maliyeti PIC'e daha cok olur. Hem hafiza kullanımı hemde işlem süresi olarak fazla olur.

Selamlar

picusta

Semesu arkadasimizin LCD ile olan sorunu bu konu altinda devam etmektedir :
http://picproje.org/index.php/topic,19801

zafertryk

peki hocam 2,3,4 yada bütün ADC kanallarını kullanmak istersek nasıl yapmamız gerekir. ben yapmaya çalıştım okuma kanal ına müdahale ederek ama olmadı. yardım ederseniz yada böyle bir örnek yayınlarsanız çok büyük yardım etmiş olursunuz



                       Saygılarımla...

PARSIC

selamlar bir sorum olacaktı;

voltaj_1=(int)((ADRESH*256+ADRESL)/2); // Hesaplama yapılıyor
lcd_gotoxy(1,10);    // Okunan değer LCD'ye yazılıyor
veri_yolla(voltaj_1/1000+48);
veri_yolla((voltaj_1%1000)/100+48);
veri_yolla('.');
veri_yolla((voltaj_1%100)/10+48);
veri_yolla(voltaj_1%10+48);
veri_yolla('V');

programda neden sürekli 48 ile toplama işlemi yapılmış ve ilk satırda 2ye bölme işlemi yapılmış yardımcı olabilir misiniz..teşekkürler
********************************************************************HI-TECH*******************************************************************

X-Fi

ascii kodlamada rakamlar decimal 48 den başlarda o yüzden tam sayıyı ascii karşılığına çevrilip lcd ye gönderilmiş.
http://www.coskunergan.dev/    (Yürümekle varılmaz, lakin varanlar yürüyenlerdir.)

PARSIC

teşekkürler fakat kafama takılan birşey var 0'ın ascii tablosunda decimal karşılığı 48 peki neden her satırda  "48"  ile dolaysıyla "0" ile toplama işlemi yapılmış..
********************************************************************HI-TECH*******************************************************************

X-Fi

her soruya ben cevap yazıyorum garip gelecek ama şöyle diyelim sonuç 8 bu 8 i 48 ile toplarsa 56 çıkar buda yine 8 in ascii karşılığıdır.

birde 2 ye neden böldüğünü sormuşsunuz benim anladığım bu kodlar adc voltaj okuma kodları pici 5V ile beslemişse eğer 5V adc okuduğunda sonuç 1023 çıkar bunu 10.23V olarak yazdıramadığı için 5V=05.12V olarak yazdırmış ancak 0.12V luk bir fazlalık var onu silmemiş orasını anlayamadım devre üzerinde absorbe etmiş olabilir.

kolay gelsin.
http://www.coskunergan.dev/    (Yürümekle varılmaz, lakin varanlar yürüyenlerdir.)

PARSIC

verdiğiniz bilgiler için çok teşekkür ederim gerçekten çok işime yaradı tekrar teşekkürler X-FI ...
********************************************************************HI-TECH*******************************************************************

casanowa123

Öncelikle bu güzel anlatım için teşekkür ediyorum.

Float değişkeni printf ile yazdırırken problem oluyor maalesef. Bu sorunun üstesinden nasıl gelebilirim?

casanowa123

anlatımdaki formülleri sharp gp2d12 uzakluk sensörüne uyguluyorum.Ancak datasheet'e göre bir değer alamıyorum.Sorun 16f877 ile mi alakalı yoksa ben mi bir yerde yanlış yapıyorum anlamadım.BYardımcı olabilirseniz çok makbule geçer.

datashhethttp://www.google.com.tr/url?sa=t&source=web&cd=2&ved=0CCMQFjAB&url=http%3A%2F%2Fwww.parallax.com%2Fdl%2Fdocs%2Fprod%2Facc%2FSharpGP2D12Snrs.pdf&ei=Lv3CTZLmMo72sgb_jeGRDw&usg=AFQjCNEhPunMl7_TuGoL0vlIQmYRj3RoZw&sig2=IBttr0EAusfA86RVeylehw


kod:
#include <htc.h>
#include "lcd.h"
#include "delay.h"
#include <stdio.h>

unsigned int  read_adc(void)
{
	unsigned int value;
	ADIF=0;
	ADGO=1;
	while(ADGO || !ADIF)
	continue;	
	
	value=ADRESL;
	value +=(unsigned int) ADRESH*256;	
return value;
} 

   

void main(void)
{	
	float lsb= 5.0/1023;
	    char yazi[]="SONUC= ";
	    char yazi2[]="SONUC= ";
	     int gerilim;	
	    unsigned char ust,alt;
		float volt;
	   TRISB=0;
	   PORTB=0;
		TRISD=0; 
		PORTD=0;
		TRISA=1; 
	    PORTA=1;
		TRISC=0;
		PORTC=0;
		CHS2=0;
		CHS1=0;  
		CHS0=0;
		ADCS1=1;
		ADCS0=1;
		ADFM=1;
		PCFG0=0;
		PCFG1=1;
		PCFG2=1;
		PCFG3=1;
		ADON=1;
	
		lcd_init(); 
		lcd_clear(); 
		
		ADCON1=0x8E; // AN0 analog giriş 
		ADCON0=0x41; // A/D aktif 

		for(;;)
		{
		ADGO=1;
		while(ADGO);
		gerilim=read_adc();
		volt = (5./1023)* gerilim; 
		ust=(int)volt;
        alt=(int) (10.0*(volt-ust));     
        lcd_goto(0x00);
        sprintf(yazi2,"%d",gerilim);
        lcd_puts(yazi2);
        lcd_goto(0x40);
		sprintf (yazi,"%d.%d",ust,alt);
		lcd_puts(yazi);
		DelayMs(250);
		lcd_clear();
		}
		
		
		
		
}

Seferi

Hocam linkler çalışmıyor. Yenileyebilir misin?

a.zorba

Bilenler bilir . Biz maraşlilar için şehir olarak şubat ay' i bayram ,kurtuluş ,yaz in gelmesiydi. Şimdi karalar in yas in ayı oldu.