CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Problem Read ADC

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
MotronixGS



Joined: 13 Jul 2011
Posts: 19

View user's profile Send private message

Problem Read ADC
PostPosted: Tue Dec 27, 2011 6:59 pm     Reply with quote

I am programing a voltmeter with the PIC18F458. Using a simple code, don't works, like this:

Code:


#include <18F458.h>
#device adc=10
#fuses HS, NOLVP, PUT

#use delay(clock=20000000)

#build(reset=0x200)
#build(interrupt=0x208)
#org 0x0000,0x01ff

void bootloader() {
#asm
  nop
#endasm
} // Reserve space for the bootloader


#use rs232(baud=57600, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8)
#include <DS1307.c>                                           
#use I2C(MULTI_MASTER,sda=RTC_SDA, scl=RTC_SCL)           
#include <math.h>         
#include <24128.c>                                       
#include <external_eeprom.c>
#include <portD_lcd2.c>                                         


int16 adc_value;
float voltage;


//======== Main Program =========//
void main()
{
   set_tris_A(0xFF);
   port_b_pullups(TRUE);
   lcd_init();
   delay_ms(2);
   setup_port_a(ALL_ANALOG);
   //setup_port_a(AN0);
   setup_ADC(ADC_CLOCK_INTERNAL);
   //setup_ADC(ADC_CLOCK_DIV_32);       
   delay_ms(5);
   
   while (1)
   {
      set_adc_channel(0);
      delay_ms(1);                  //20us
      adc_value = read_adc();
      delay_ms(50);
      voltage = (float)adc_value * (100 / 1023.0) ;
      delay_ms(50);
      lcd_gotoxy(1,1);
      printf(lcd_putc,"%2.1f", voltage);
      lcd_gotoxy(1,2);
      printf(lcd_putc,"%lu", adc_value);
      delay_ms(50);     
   }
}



When I charge the program, the lcd shows adc_value=512, when the voltage is 0,59V (namely 12V).
I use a voltage divider with 2 resistances (2k5 and 47,5k) and ceramic and electrolytic capacitor for decoupling.

Pic break?
Code wrong?

Sorry for my poor english
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Dec 27, 2011 7:42 pm     Reply with quote

Quote:
with 2 resistances (2k5 and 47,5k)

The 47.5K series resistor value is too high. It violates the specification
for maximum source impedance for the A/D. See this section of the
18F458 data sheet:
Code:

TABLE 27-23: A/D CONVERTER CHARACTERISTICS:

Param No.   Symbol  Characteristic            Max value
A30         ZAIN    Recommended Impedance     10K ohms
                    of Analog Source Voltage


You need to reduce the resistance values in your voltage divider circuit.


You should also use an ADC clock divisor of 32 (not the Internal RC clock).
dyeatman



Joined: 06 Sep 2003
Posts: 1938
Location: Norman, OK

View user's profile Send private message

PostPosted: Tue Dec 27, 2011 7:50 pm     Reply with quote

Interesting, in the first paragraph of 20.1 it says:
Quote:
The
maximum recommended impedance for analog
sources is 2.5 kΩ.

_________________
Google and Forum Search are some of your best tools!!!!
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Dec 27, 2011 8:17 pm     Reply with quote

You're right. They have two conflicting values. But in any case, his
voltage divider needs to have lower values.
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

Source impedance
PostPosted: Wed Dec 28, 2011 3:38 am     Reply with quote

MotronixGS is using a voltage divider which is OK.

His source impedance is 2k375 (not 47k5).

From the point of view of the PIC input circuit, the output impedance of the voltage divider is effectively 2k5 in parallel with 47k5.

If you want prove it to yourself either model it, in say PSpice, or use real resistors and a DMM. Calculate, or measure, the output without a load then add a 2k375 load across the output. With a 12V source, the output will be 0V6 into an open circuit, and 0V3 loaded with 2k375.

There is no need for MotronixGS to reduce the values of his divider network.

Mike
MotronixGS



Joined: 13 Jul 2011
Posts: 19

View user's profile Send private message

PostPosted: Wed Dec 28, 2011 6:02 am     Reply with quote

Thanks for the responses, i will test your idea Mike Walne (thank you very much for your reply), adding a load.
Append that in Proteus, the code and circuit is OK and it measures the correct voltage, perhaps my pic is broken...
Ttelmah



Joined: 11 Mar 2010
Posts: 19559

View user's profile Send private message

PostPosted: Wed Dec 28, 2011 6:15 am     Reply with quote

Also, the AC impedance will be reduced by the capacitor.
Microchip themselves use this approach when measuring high voltages like mains, with very large resistors in the upper leg of the divider.

Compiler version?.
While the 458, is a fairly 'old' chip, and on any reasonably modern compiler everything should be OK, something silly might exist on an old compiler.

The clock should be changed, but will only degrade the value a little.

The code is 'unnecessary', in quite a few places. The ADC channel only needs to be selected once, there are delays floating around that are not needed, and quite a few things like the TRIS settings that are not needed, but it basically should work. However a cleaner version would be:
Code:

#include <18F458.h>
#device adc=10
#fuses HS, NOLVP, PUT

#use delay(clock=20000000)

#build(reset=0x200)
#build(interrupt=0x208)
#org 0x0000,0x01ff

void bootloader() {
#asm
  nop
#endasm
} // Reserve space for the bootloader

#use rs232(baud=57600, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8, ERRORS)
#include <portD_lcd2.c>                                         

int16 adc_value;
float voltage;

//======== Main Program =========//
void main(void) {
   delay_ms(2); //Delay before initialising the LCD - some require extra here
   lcd_init();
   setup_vref(FALSE);
   setup_adc_ports(AN0);
   setup_adc(ADC_CLOCK_DIV_32);   
   set_adc_channel(0);
   delay_ms(5);
   
   while (1) {
      adc_value = read_adc();
      voltage = adc_value * 0.09766 ;
      lcd_gotoxy(1,1);
      printf(lcd_putc,"%2.1f", voltage);
      lcd_gotoxy(1,2);
      printf(lcd_putc,"%lu", adc_value);
      delay_ms(250);     
   }
}

The only big difference is making sure the internal Vref is off. This is the only other peripheral on the AN0 pin, and a general 'rule' when trying to find problems is to turn off everything on the same pin.

Best Wishes
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Dec 28, 2011 5:51 pm     Reply with quote

Quote:

From the point of view of the PIC input circuit, the output impedance of
the voltage divider is effectively 2k5 in parallel with 47k5.

I disagree. The large series resistance will slow down the charging rate
of the sampling capacitor in the PIC's A/D module.

If he wants to use 47.5K as the series resistor in his voltage divider,
then he must increase the acquisition time so it's larger than the nominal
value of 12.86 usec given in the 18F458 data sheet.

To prove this, I made a test circuit and a test program. I didn't have
a 50K trimpot, so I built the voltage divider from 10K and 30K fixed
resistors and a 10K trimpot. I put +12v from a linear bench power supply
on the input, and adjusted the trimpot to give 0.6v output to pin AN0.
This duplicates what the original poster is doing.
Code:

                                ---------->  0.6v
                                |            to pin AN0 on PIC.
          30K       10K         |
+12v ---/\/\/\----/\/\/\-----/\/\/\-----
input                          10K     |
                             Trimpot  --- Ground
                                       -

Next, I compiled and ran the program shown below. It switches pin AN0
between 0 volts and the test voltage of 0.6v. The acquisition time can
be set with a line of code as noted in the program. If everything is
working as desired, we should see the ADC output go from 0 to 123
as it switches between input voltages of 0v and 0.6v. The 123 value
comes from (0.6v/5.0v) * 1024 = 123. The 0.6v is the A/D input voltage
and 5v is the A/D reference voltage (same as Vdd for the PIC).

For an acquisition time of 20 usec, I get this. Clearly not correct.
Quote:

4
71
2
54
2
49
1
48
1
47


For 500 usec I get this. Still not correct:
Quote:

1
115
0
115
0
115
0
114
1
114


I found that I must use at least 1000 usec to get results that are nearly
in the correct range:
Quote:

1
122
1
122
0
122
0
123
0
123

I have proven here that the Thevenin equivalent resistance isn't
important. It's the amount of charging current that the series resistor
lets through to the sampling capacitor inside the PIC's A/D circuit, in the
alloted acquisition time. If you increase the series resistance, you must
increase the acquisition time to compensate for this.

Note that I'm using an 18F452 for this test. I don't have an 18F458 in
a 40-pin DIP package, so I used 18F452 instead. It has the same source
impedance requirements for the A/D as the 18F458, so it's good enough
for the purpose of this test. I used compiler vs. 4.128. I tested this on
a PicDem2-Plus board, and I temporarily replaced the built-in AN0
trimpot circuit with the voltage divider circuit shown earlier in this post.
Also note that Pin B0 is jumpered to Pin A0 to allow me to temporarily
pull pin A0 to ground by setting Pin B0 low. This is shown in the code.

For reference, see this other thread where I experiment with the
acquisition time on the 18F452:
http://www.ccsinfo.com/forum/viewtopic.php?t=44201
Code:

#include <18F452.h>
#device adc=10
#fuses HS,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=20M)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)

#define SAMPLES 10
//======================================
void main(void)
{
int8 i;
int16 results[SAMPLES];

setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_32);   
set_adc_channel(0);

output_low(PIN_B0);

// Take several consecutive A/D readings.
// Store them in an array.
for(i = 0; i < SAMPLES; i++)
   {
    delay_us(20);   // Set acquisition time delay here
    results[i] = read_adc();
    if(i & 1)                // On odd-numbered samples,
      output_low(PIN_B0);    // Hold pin AN0 at 0 volts
    else
      output_float(PIN_B0);   // Allow AN0 to rise up to the test voltage
   }

// Now display the results.
for(i = 0; i < SAMPLES; i++)
   {
    printf("%lu \n\r", results[i]);
   }

printf("\n\r");

while(1);
}
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Thu Dec 29, 2011 4:24 pm     Reply with quote

PCM programmer and Ttelmah are both right in their statements:

As far as the PIC goes - with an appropriately large HI q /LOW-d cap --
the value size of a resistor in the divider chain does NOT limit accuracy however it DOES place limits on how often you can get a "correct" reading.

The issue is really about having enough charge available from
the sampling pin to swamp , and fully charge the internal capacitor in the PIC AD converter during the sample time window.

So even if there is a 100K resistor in the top of the divider with a .1uf metal film cap on the inlet pin -if you allow LOTS of settling time - the error will be the ratio of the internal capacitor to the external one, or with this example appx 1 BIT absolute.

The RUB is that the time between readings has to be about 10 time constants of 100K and .1uf for full settling to occur and for the .1uf cap to integrate and hold the "true value".

With 100k and .1 uf this means you can ONLY get a true reading ONCE every 100 msecs !!

But for best results - always buffer with an op amp anyway.
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

Thevenin
PostPosted: Fri Dec 30, 2011 11:27 am     Reply with quote

I insist; a Thevenin equivalent circuit is the way to go.

The idea of using a Thevenin equivalent circuit is to create a simpler circuit which is easier to understand. If your circuit does not behave in the way predicted by the Thevenin equivalent then something is wrong. Either conversion to Thevenin, real circuit or software.

I duplicated PCM programmer's test on my PICDEM 2 PLUS board with a 18F458 @ 4MHz.

Using RB0 to pull RA0 to ground I got similar results.

Then I noticed that, on my PICDEM 2 PLUS board, RB0 is tied to 0V ground by a 100nF capacitor via a 470R resistor. The capacitor is also pulled to +5V via a 4k7 resistor. The 100nF capacitor is three orders of magnitude bigger than the ADC sampling capacitor; of course things are slowed down with it in circuit.

I then swapped RB0 for RB1 and got the fast ADC settling I expected.

Using a high value source resistor does not, of itself, slow things down.

In MotronixGS's case he's trying to pull the ADC sampling capacitor to 0V6. When the capacitor is in the fully discharged state it approaches 0V6 at the same rate with a 47k5 from a 12V source as it does with a 2k375 from a 0V6 source. In both cases the charging current is ~253uA.

Mike


Last edited by Mike Walne on Sat Dec 31, 2011 2:43 am; edited 1 time in total
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Dec 30, 2011 2:01 pm     Reply with quote

You are right, I missed the cap on pin B0 and it skewed my results and
made me think about the problem incorrectly. Damnit. I hate to lose. Shocked Mr. Green
SherpaDoug



Joined: 07 Sep 2003
Posts: 1640
Location: Cape Cod Mass USA

View user's profile Send private message

PostPosted: Fri Dec 30, 2011 3:15 pm     Reply with quote

Well I am relieved. I doubted PCM programmer was wrong, but I KNEW Thevenin wasn't wrong. I thought I might have to spend a few hours this weekend figuring it out. Now I can play Warcraft instead ;-)
_________________
The search for better is endless. Instead simply find very good and get the job done.
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

Thevenin
PostPosted: Thu Jan 05, 2012 7:39 am     Reply with quote

In MotronixGS's circuit, the Holding capacitor C is being charged from the 12V input via the 47k5.

Code:

                  PIC internal R     ||
                ----/\/\/\-----------||----------
                |    ~8k             ||         | 
                |              Holding Cap C    |     
         47k5   |    2k5                        |
+12v ---/\/\/\------/\/\/\-----------------------
input                                           |
                                               --- 0V Ground
                                                -


All the charge which arrives at C has to pass through the 47k5. It easy to see how this thought could lead to the belief that the 47k5 is the dominant resistor in the network. If you take away the 2K5 then indeed the 47k5 does dominate. You are then trying to charge C exponentially to 12V. However, with the 2k5 in place the settling voltage is now only 0V6. The voltage on C now approaches the 20 times smaller 0V6 on a time constant roughly 5 to 6 times faster than without the 2k5. The time constant is set by the parallel equivalent of the 47k5 and the 2k5 plus the series internal 8k. In other words the Thevenin eqivalent of the external circuit is dominated by the 2k5 and not the 47k5.

When designing an attentuator network for higher voltages, one approach is to keep the lower resistor at roughly 2k5, and scale the upper resistor so that it feeds the same 2mA into the 2k5 for a 5V full scale voltage to the PIC. I.e. you are arranging to have the same current available to charge the holding capacitor whatever the input voltage.

What Thevenin tells you is that the output nodes of circuits a b & c below are EXACTLY equivalent. You simply have to accept it. If you can't, plug the numbers into any analysis package you care to, and try it (or do it with real components and a meter/oscilloscope).

Code:

Circuit (a)

                ---------------  output node
                |     
                |         
         47k5   |    2k5 
+12v ---/\/\/\------/\/\/\-----
                              |
                             --- 0V Ground
                              -





Circuit (b)
            47k5
        ---/\/\/\----
        |           |
        |           |
0V6 -----           ----------- output node
        |           |
        |   2k5     |
        ---/\/\/\----       --- 0V Ground
                             -


Circuit (c)

         2k375
0V6  ---/\/\/\----------------- output node


                            --- 0V Ground
                             -


Hang anything you like from the output node to 0V ground, and all three circuits behave in EXACTLY the same way as far as the output node is concerned.

Quote:

You're right. They have two conflicting values.



I agree, the microchip data sheets for 18F452/8 are not internally consistent, they quote a maximum recommended analogue source impedance of 2k5 and 10k in different places. However other data sheets, such as 18F1320, do quote the same 2k5 throughout.

The PIC internal resistor is ~8k. So an external 10k resistor would roughly double the charging time constant.

I suggest that the primary reason for microchip recommending a maximum 2k5 analogue source resistor is to reduce the offset error to significantly less than one bit.

The internal network for the A2D converter includes a +/-500nA leakage source. This internal source would generate +/-5mV across a 10k source resistor. +/-5mV is ~1 bit error in a 5V referenced 10 bit A2D converter. Reduce the source resistor to 2k5 and the error is +/-quarter of a bit.

Mike
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group