PIC16F877 ADC için yardım

Başlatan ygunbt, 09 Aralık 2008, 13:47:36

ygunbt

merhaba arkadaşlar.
acemi bir PIC kullanıcısı olarak PIC C ile bir ADC uygulaması yapmak istedim.  
uygulamam da (resimdeki gibi) RA0 analog girişine 2,5V tan küçük bir deger uygulandıgında RB0 a bağlı ledin yanmasını istedim. ama çalışmadı.
analog girişe uygulanan deger 10 bit le ifade edilir diye biliyorum, yani
1024 5V a karşılık gelir. bu bağlamda 2,5V  512 ile ifade edilir diye düşündüm. acaba nerede hata yaptım?
yardımlarınız için şimdiden çok teşekkür ederim.
bunlara ek olarak PIC C için ADC kodlarını, döküman bulamadığım için internetteki uygulamalardan öğrenmeye çalıştım. ama yeterli olmadı sanırım.
elinde kodlarla ilgli döküman olan varsa eklerse çok sevinirim.
#include <pic.h>


main (void)
{
						// Değişken tanımlama

double gerilim;
 

						// Port konfigürasyonu
TRISB=0;				 // PORTB çıkış  
TRISA=1;				 // RA0 analog giriş 


						// ADC ayarları
ADCON1=0x8E; 			// AN0 analog giriş
ADCON0=0x41; 			// A/D aktif 

for(;;)
{						// A/D çevrimi başlat 
ADCON0=0x45; 
						// Dönüşümün bitmesini bekle
while((ADCON0&4)!=0); 

							// Dönüşüm sonucunu kaydet
gerilim=ADRESH; 
gerilim=256.0*gerilim+ADRESL;
} 						// Ölçümü tekrarla
RB0=0;
	 for(;;)
{
	if(gerilim<512) RB0=1;
	else RB0=0;
}
} 					// Programın sonu

[/URL][/img]

Tagli

Nerdeyse ASM kodu gibi olmuş :). Bu dili bilmiyorum ama belki de A/D dönüşümü için hazır fonksiyonlar vardır onları kullanmayı bir dene.

ADRESH'nın büyük 6 biti 0'dan farklı olabilir mi acaba? Bazı register'lar PIC çalışmaya başladığı zaman garip değerler alıyor ve A/D dönüşüm sağa dayalı ayarlanmışsa (ki sen bu şekilde kullanıyorsun) dönüşüm ADRESH'nın büyük 6 bitini etkilemiyor sanırım. Eğer öyle ise bu sonucu etkileyecektir. 256 ile çarpma işleminden önce söz konusu büyük 6 biti basit bir AND işlemi ile sıfırlayabilirsin.

Programı çalıştırdığında gözlemin ne oluyor? LED hiç mi yanmıyor?

Ekleme: Bu arada aklıma geldi de A/D dönüşüm öncesinde kanalı seçtikten sonra bir süre beklemek gerekir (bu süre için datasheet'e bir bak). Buna "acquisition time" deniyor. Senin kodunda bu beklemeyi göremedim. Hazır fonksiyonları kullanırsan sanırım bu beklemeye gerek kalmayacaktır (bekleme hazır fonksiyonların içine dahil edilmiştir).

Düzeltme: Büyük 6 bit konusunda yanılmışım. Bunlar zaten 0 oluyormuş. Datasheet'te A/D dönüştürücü kullanılmazken bu iki byte'lık alanın istenildiği gibi kullanılabileceği yazıyormuş, aklımda yanlış kalmış olay.
Gökçe Tağlıoğlu

Erhan YILMAZ

Dostum A/D modülünü kullanman zorunlu değilse bu uygulamayı comparator modulü ile daha basit yaparsın comparatoru ayarlayıp dahili referans üreteç moduülünü açarak vrefi 2.5 volta ayarlarsın comparator çıkışını tersleyip kullanırsın. 2.5volta kadar çıkış 1 olur 2.5 voltu aşınca çıkış 0 olur  Datasheetten bir incelel bu işlem için comparator modülü daha mantıklı çözüm

ygunbt

Teşekkürler tamirci_Erhan tavsiyen için. Comparatordan bahsettiğin iyi oldu.
PIC de acemiyim bilmiyordum bu özelliği olduğunu. Çalışmamın ileriki aşamalrın da LCD de gerilim değerlerinide görmem gerekiyor, o yüzden ADC işime yarar sanırım.

ygunbt

Teşekkürler Tagli.
Devreyi çalıştırdığımda LED sürekli olarak karasız bir şekilde yanıp sönüyor. Pot dan RA0'a uyguladığım gerilimi değiştirsem dahi sonuç değişmiyor. Belki sorun dediğin gibi ADRESH' a kaydederken olabilir.Datasheet'i birazdaha inceleyim iyisimi.

Tagli

Acquisition time'dan kaynaklanıyor olması daha muhtemel. Ama dediğim gibi, büyük ihtimalle A/D dönüşümü için hazır fonksiyon vardır, bu fonksiyon(lar)ı kullanman daha iyi olur.
Gökçe Tağlıoğlu

cemilkendir

#include <pic.h> 


main (void) 
{ 
                  // Değişken tanımlama 

double gerilim; 
  

                  // Port konfigürasyonu 
TRISB=0;             // PORTB çıkış  
TRISA=1;             // RA0 analog giriş 


                  // ADC ayarları 
ADCON1=0x8E;          // AN0 analog giriş 
ADCON0=0x41;          // A/D aktif 

for(;;) 
{                  // A/D çevrimi başlat 
ADCON0=0x45; 
                  // Dönüşümün bitmesini bekle 
while((ADCON0&4)!=0); 

                     // Dönüşüm sonucunu kaydet 
gerilim=ADRESH; 
gerilim=256.0*gerilim+ADRESL; 
                  

  

   if(gerilim<512) 
        RB0=1; 
   else 
       RB0=0; 

}   // Ölçümü tekrarla 

}                



ben ccs c kullanıyorum aslında ama mantık hatası var programında sonsuz iki for döngüsü kurmuşsun
ilkinden yani adc ölçümünden asla çıkmayacağı için ledleri ayarlama döngüsüne girmeyecekdir

yukarıdaki gibi bir denermisin

ygunbt

Teşekkürler CemilKendir.
ADC kısmına odaklandığım için programdaki mantık hatası hiç dikkatimi çememiş.
dediğin doğru , ikinci for u kaldırınca düzgünce çalıştı devre.

ilginiz için hepinize tekrar teşekkür ederim arkadaşlar..

ygunbt

bir de arkadaslar VREF+ ve  VREF- nin kullanımını anlayamadım.
bu iki referans girişleri RA0 ile birlikte mi kullan malıyım yoksa ayrı da kullana bilirmiyim?
ve de ne işime yarar bu girişler? bunlarla ilgili örneklere baktım ama anlayamadım,
yönlendire bilceğiniz örnek uygulama varsa paylaşırsanız mutlu olurum,
şimdiden tesekkür ederim..

Erhan YILMAZ

Dostum vref -vref referans voltajı girişleridir pic analog bilgiyi dijitale bu değerleri referans alarak çevirir. bu değer picin beleme gerilimi olabileceği gibi ilgili ayalar yapılınca picin vref -vref girişlerindende sağlanır yani picin besleme gerilimini kullanırsan örnek olarak 5 voltluk gerilimi dijitale çevirince dijital 1024 2.5 voltu çevirince 512 gibi değerler elde edilir yaklaşık olarak 16f877 de referans girişlerini kullanmak için tabloyu inceleyebilirsin tablo biraz kısıtlıdır fakat 16f88 16f887 gibi yeni çıkan denetleyicilerde bu girişlerinin kullanımı daha esnektir. Datasheetleri inceleyebilirsin 16f887 pin bağlantıları 877 ile aynıdır 877nin gelişmiş versiyonudur.




ygunbt

çok teşekkür ederim tamirci_erhan

arslan74

Alıntı yapılan: "tamirci_erhan"Dostum vref -vref referans voltajı girişleridir pic analog bilgiyi dijitale bu değerleri referans alarak çevirir. bu değer picin beleme gerilimi olabileceği gibi ilgili ayalar yapılınca picin vref -vref girişlerindende sağlanır yani picin besleme gerilimini kullanırsan örnek olarak 5 voltluk gerilimi dijitale çevirince dijital 1024 2.5 voltu çevirince 512 gibi değerler elde edilir yaklaşık olarak 16f877 de referans girişlerini kullanmak için tabloyu inceleyebilirsin tablo biraz kısıtlıdır fakat 16f88 16f887 gibi yeni çıkan denetleyicilerde bu girişlerinin kullanımı daha esnektir. Datasheetleri inceleyebilirsin 16f887 pin bağlantıları 877 ile aynıdır 877nin gelişmiş versiyonudur.

Merhaba,

Ufak bir düzeltme eklemek istiyorum. PIC 10bit ADC sahib o yüzden en fazla '0b1111111111' sayisini gösterebilir oda 1023 eder. Yani Vref 5Volt ise 5 V olduğunda 1023 görürsünüz.

Selamlar

ygunbt

[/quote]

Merhaba,

Ufak bir düzeltme eklemek istiyorum. PIC 10bit ADC sahib o yüzden en fazla '0b1111111111' sayisini gösterebilir oda 1023 eder. Yani Vref 5Volt ise 5 V olduğunda 1023 görürsünüz.

Selamlar[/quote]

tskürler arslan74 ilgin için

ygunbt

Tekrar merhaba arkadaşlar
bu örneği internetten bakarak oluşturmuştum. o yüzden anlayamadığım kısımları hala var.

dönüşümün bitmesini beklemek için aşağıdaki ifade yazılmıştı.

while((ADCON0&4)!=0);

bu ifade sanırım GO/DONE biti ile alakalı, ama tam anlayamadım.
ne anlama geliyor ?    

birde ADC 10 bit ile çevirim yapıyor biliyorum ama kafam aşağıdaki ifade de karıştı
gerilim=ADRESH;
gerilim=256.0*gerilim+ADRESL;
birde burda yardımcı olursanız sevinirim.
yardımlar için şimdiden teşekkür ederim

arslan74

Sana çalışan örnek kod yolluyorum.

Bunu al doğrudan kullan.

unsigned int 
read_adc(void){

	unsigned int val;

	ADIF = 0; 	//Clear ADIF bit
	ADGO=1;		// initiate conversion on the selected channel
			
	while ( ADGO || !ADIF )
		continue;
		
	val = ADRESL;
	val += ((unsigned int)ADRESH * 256);
	
	return val;
} //





unsigned int adc_read (void){

	unsigned int i;
	
	ADIF = 0; 	//Clear ADIF bit
	ADGO=1;		// initiate conversion on the selected channel
			
	while ( ADGO || !ADIF )
		continue;
			
	DelayUs(100);
		
//	i =  ADRESL + (ADRESL<<8);	// return 8 MSB of the result
	p = (char*)(&i);
	*(p++) = ADRESL;
	*p = ADRESH;

	return i;

}


bu iki fonksiyondan birini kullanabilirsin. ikiside çalışıyor. İki, farklı kodlama mantığı ile hazırlanmıştır.

Öncesinde ADC ayarlaman lazım.

/***********************************************************************
	ADC ayarları
***********************************************************************/

	CHS2 = 0;
	CHS1 = 0;
	CHS0 = 0;

	ADCS1 = 1;
	ADCS0 = 1;
		

	ADFM = 1; //0 = Left justified. 6 Least Significant bits of ADRESL are read as ‘0'.
	
	PCFG0 = 0;
	PCFG1 = 1;
	PCFG2 = 0;
	PCFG3 = 0;
	
	ADON = 1;


/**********************************************************************/


ADC değeri volta dönüştürmek istiyorsan o zaman aşağıdaki kodu kullanabilirsin. Ancak Burada referans olarak 1024 alınmıştır. Dolaysıyla 5Volt olduğunda 4.99 Volt elde edersin.

unsigned int 
convert2mV(unsigned int adc){
	
	unsigned int val,temp;

//  U[mV] = ((5*adc - 5*adc/64) - 5*adc/128)
	temp = val = (5*adc);
	val -= temp >> 6;
	val -= temp >> 7;
		
	return	val;
}


Program içinde söyle bir cağrı yapabilirsin.

adc_val = adc_read();
		adc_val = convert2mV(adc_val);


yada

adc_val = convert2mV(adc_read());


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