Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] Reading more than one analog value is a problem

Status
Not open for further replies.

shomikc

Member level 4
Joined
Apr 19, 2013
Messages
71
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,286
Activity points
1,951
Hello all,

I can read one analog value in one channel just fine. But using two or more seems to be a problem. It seems the ADRES(ult) register does not get refreshed with the new value from the second channel. I am thinking that capacitor in the ADC is not discharged. Can you please help. The code is as listed below.
Code:
void ADC_Init();

unsigned int  ADC_Read(unsigned int);

 void main()
 { 

	int a, b;
	float voltage;

	TRISBbits.TRISB3 = 0; 
	TRISBbits.TRISB2 = 0; 
	TRISBbits.TRISB1 = 0;
	PORTB = 0;

	
	OSCCON = 0x72;			//	set internal oscillator at 8 MHz
	

	while(1)
		{
			ADC_Init();	
			ADCON0 = 0b00001101;	//	select channel1(AN3) and power on A/D Module (ADON =1)	   		
			a = ADC_Read(3);
            		ADCON0 = 0b00010101;	//	select channel1(AN5) and power on A/D Module (ADON =1)
            		b = ADC_Read(5);	//	Read another channel
		
			
			if (a > b)
				{
				    PORTBbits.RB1 = 1;
				     
				}
			else
				{
					PORTBbits.RB1 = 0;
				
                }
  }

void ADC_Init()
	{
		TRISA  = 0xFF;		//	initialise PortA as input Port
		PORTA  = 0;
        	ADCON2 = 0b10111110;	//	1 Right justified; 010 == 20*T_ad; 010 = F_osc/64
		ADCON1 = 0b00000101;	//	AN0 and AN1 set as Analog Inputs
		ADRESH = 0;
		ADRESL = 0;
	}


unsigned int ADC_Read(unsigned int channel)
	{
		unsigned int d;
		ADCON0bits.GO = 1;	//	Set the GO 
		while(ADCON0bits.GO == 1)
		{
			d = (ADRESH*256)|(ADRESL);
		}
		return(d);
	}
 

Your prognosis is almost correct. You don't need to discharge the ADC S&H capacitor but you do need to give it time to adapt to the voltage from the new channel, especially if you are not driving the ADC inputs from a low impedance. Try adding a short delay between selecting channel and taking the measurement. It is explained in all the PIC data sheets.

I'm a little worried about your ADC_Read() routine, you should be waiting for the 'GO' bit to clear before reading the ADC results rather than continuously reading them until it clears. You might also need an 'int' cast on the registers when you combine them into 'd'. You also seem to ignore the channel selection value passed to the routine.

Brian.
 
Furthermore, it is not expected to have the ADC_Init() function inside the while(1) loop.
Run it just once, before the endless loop.
 

Hello all,

I still seem to be having a problem reading the second analog value. I am using PIC18F4550 and MPLAB IDE 8.56 and XC18 compiler. I am also using two analog inputs both are square wave from function generator, both have duty cycle at 50% and 4V as Vpp and both have 2.5V DC offset to eliminate negative input to PIC. But one signal is at 1 Hz given to AN3 and the other is 10 Hz given to AN5.

It seems that when the positive edge of the first is sensed then the second one is not sampled. The code and oscilloscope results are as below. Please tell me how to fix it.

Code:
void ADC_Init();

 void main()
 { 

	int a, b;

    	PORTA  = 0;
        TRISA  = 0xFF;	//	initialise PortA as input Port
		
        ADCON2 = 0b10010010;	//	1 Right justified;  010 == 4*T_ad; 010 = F_osc/32
	ADCON1 = 0b00000101;	//	AN0 and AN1 set as Analog Inputs
	ADRESH = 0;
	ADRESL = 0;
  		
  	PORTB = 0;
	TRISBbits.TRISB3 = 0; 
	TRISBbits.TRISB2 = 0; 
	TRISBbits.TRISB1 = 0;
	
	PORTC = 0;
	TRISCbits.TRISC2 = 0; 
	
	OSCCON = 0b01000110;			//	set internal oscillator at 8 MHz
	
	ADC_Init();

	while(1)
		{
              			ADCON0 = 0b00001101; // select channel1(AN3) and power on A/D Module (ADON =1)      
				ADCON0bits.GO = 1;   // start conversion  
 				while (ADCON0bits.GO == 1) continue; // wait until conversion is complete  
 				a = ((int)ADRESH << 8)|ADRESL; // save value to variable  
				Delay10TCY();
			
				ADCON0 = 0b00010101;	//	select channel1(AN5) and power on A/D Module (ADON =1)
				ADCON0bits.GO = 1;   // start conversion  
 				while (ADCON0bits.GO == 1) continue; // wait until conversion is complete  
 				b = ((int)ADRESH << 8)|ADRESL; // save value to variable  
				Delay10TCY();
			if (a <= 512)
				{
				  	LATBbits.LATB1 = 1;
				 }
			else
				{
					LATBbits.LATB1 = 0;
				}
          		 if (b <= 512)
				{
					LATCbits.LATC2 = 1;   
				}
			else
				{
					LATCbits.LATC2 = 0;
				}
		} 
 }

void ADC_Init()
	{
		TRISA  = 0xFF;	//	initialise PortA as input Port
		PORTA  = 0;
        	ADCON2 = 0b10111110;	//	1 Right justified; 010 == 20*T_ad; 010 = F_osc/64
		ADCON1 = 0b00000101;	//	AN0 and AN1 set as Analog Inputs
		ADRESH = 0;
		ADRESL = 0;
	}

20180711_133507.jpg
 

The program flow looks wrong to me. Try removing the 'continue' instructions completely, you want it to wait until 'GO' is zero then move to reading the result, something like this:

while(ADCON0bits.GO == 1); // so it doesn't pass this point until 'GO' = 0.

Also check the delays in the data sheet, they probably want to be in the tens or hundreds of uS not just a few cycles. Put the delay between selecting the channel and setting the GO bit so the S&H has time to adapt before you take the measurement.

Brian.
 
Hello all,

Please tell me if my code is right or wrong. I can't figure out why it is not working. By my logic it should but it is not. I have gone over it like 10 times. Thanks a million. Also can somebody tell me how to use the CODE tags the right way. The Code window stills takes up a lot of space and the lines are not numbered. Thanks again.

Code:
#pragma config PLLDIV = 5         // PLL Prescaler Selection bits (No prescale (4 MHz oscillator input drives PLL directly))
#pragma config CPUDIV = OSC1_PLL2 // System Clock Postscaler Selection bits ([Primary Oscillator Src: /1][96 MHz PLL Src: /2])
#pragma config USBDIV = 2         // USB Clock Selection bit (used in Full-Speed USB mode only; UCFG:FSEN = 1) (USB clock source comes directly from the primary oscillator block with no postscale)
 
// CONFIG1H
#pragma config FOSC = HSPLL_HS  // Oscillator Selection bits (HS oscillator, PLL enabled (HSPLL))

#include <pic18f4550.h>
#include <delays.h>

unsigned int adc_data[2];
unsigned char analog_selected = 1;

void interrupt(){
  if (PIR1bits.ADIF) 
	{
    	PIR1bits.ADIF=0; // clear AD interrupt flag
    	switch (analog_selected)
	  {
      		case 0:
			adc_data[0] = (ADRESH << 8) | ADRESL; // read data from channel 0
        		analog_selected = 1; // return channel 1
        		ADCON0 = 0b11000101; // select channel 1;
				if (adc_data[0] >= 512)
					{
						LATBbits.LATB2 = 1;   
					}
				else
					{
						LATBbits.LATB2 = 0;
					}
        	     break;
      		case 1:
        		adc_data[1] = (ADRESH << 8) | ADRESL; // read data from channel 1
        		analog_selected--;   // decrease channel index
        		ADCON0 = 0b11000001; // select channel 0
	
				if (adc_data[1] >= 512)
					{
				  	   LATCbits.LATC2 = 1;   
					}
				else
					{
					   LATCbits.LATC2 = 0;
					}		 
        	    break;
       	}
    Nop();		// wait acquisition time for next sample to be available for sample and hold
    Nop();
    Nop();	
    ADCON0bits.GO = 1;  // start conversion again
  }
}

 void main()
 { 

	int a, b;
	unsigned int i=0, temp;
	int first = 1;
  	
	PORTA  = 0;		   	
        TRISA  = 0xFF;		//	initialise PortA as input Port
		
        ADCON2 = 0b10001110;	//	1 Right justified;  001 == 2*T_ad; 110 = F_osc/64
	ADCON1 = 0b00001100;	//	AN0 and AN1 set as Analog Inputs
	ADRESH = 0;
	ADRESL = 0;
  		
  	PORTB = 0;
	TRISBbits.TRISB3 = 0; 
	TRISBbits.TRISB2 = 0; 
	TRISBbits.TRISB1 = 0;
	
	PORTC = 0;
	TRISCbits.TRISC2 = 0; 
	
	OSCCON = 0b01000110;			//	set internal oscillator at 8 MHz
	
	while(1)
		{
              	ADCON0 = 0b11000001; // select channel0(AN0) and power on A/D Module (ADON =1)      
		ADCON0bits.GO = 1;   // start conversion  
			
 			while (ADCON0bits.GO == 1)  // wait until conversion is complete
				{  
				     if(first == 1)
                                       {	
					adc_data[0] = (ADRESH << 8) | ADRESL; // read dadta from channel 0
        				
        				ADCON0 = 0b11000101; // select channel 1
					  if (adc_data[0] >= 512)
						{
				  		  LATBbits.LATB2 = 1;
				 		}
					  else
						{
						  LATBbits.LATB2 = 0;
						} 
					 first = 0;					
					}
                                   interrupt();
				}		
		} 
 }
 

Listen.... Brian is always right! (well... most times)

I find the easiest way to add the code tags is to do it manually. Just type this:
before the code and without the quotes or spaces "[ C O D E ]"
and after the code add "[ / C O D E ]" Note the extra '/' to turn code mode off.

I had to add the spaces or the Forum would have used it as real code tags!

Brian.
 
Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top