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 support@ccsinfo.com

adc accuracy problem
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
overmindx



Joined: 06 Oct 2008
Posts: 43

View user's profile Send private message

adc accuracy problem
PostPosted: Wed Feb 04, 2015 2:56 am     Reply with quote

hi everyone,
i am trying to measure a voltage that passes through a certain pin via adc. however, the voltage i get is slightly off compared to the voltage when measured via a multimeter. the results are:
- from multimeter = 2.40v
- from mcu = 2.58v
is this normal or i did something wrong? below is my code. please help.

Code:

#include <18F87K22.h>
#device adc=12
#device PASS_STRINGS = IN_RAM

#FUSES WDT_SW, INTRC_IO, SOSC_HIGH, NOPROTECT, NOIESO, BROWNOUT, PUT, NOCPD, STVREN, NODEBUG, NOWRT, NOWRTD, NOEBTR, NOCPB, NOEBTRB, NOWRTC, NOWRTB, FCMEN, NOXINST, MCLR, RTCOSC_T1
#use delay(clock=16000000)

#case

#include <pinDEFINITIONS.c>

#use rs232(baud=57600, xmit=TX_PC, rcv=RX_PC, stream=PC)
#use rs232 (baud=19200, xmit=PIN_C6, rcv=PIN_C7, stream=SERIAL_INT)
//#include "bootloader.h"

#include <math.h>
#include <stdlib.h>
#include <string.h>

void enableBOOST2()
{
   output_high(SHDN2);
}


void getVoltage(int channel)
{
   int x=0;
   int32 adcValue=0;
   int32 adcValueAve=0;
   float voltage=0;
   
   set_adc_channel(channel);
   delay_us(25);

   for (x=0;x<10;x++)
   {
      adcValue = read_adc();
      adcValueAve = (adcValueAve + adcValue) / 2;
      delay_us(30);
   }
   
   voltage = ((adcValueAve * 3.315) / 4096.0);
   fprintf(PC,"voltage=%2.2f\n",voltage);
   
}


void main(){
   
   delay_ms(2000);
   enableBOOST2();
   delay_ms(1000);
   setup_adc(ADC_CLOCK_DIV_16);
   setup_adc_ports(sAN0 | sAN4 | sAN5 | sAN7 | sAN8 | sAN12 | sAN13 | sAN14 | sAN15);
   
   setup_timer_1(T1_EXTERNAL|T1_DIV_BY_1|T1_ENABLE_SOSC);
   setup_spi(SPI_MASTER|SPI_L_TO_H | SPI_XMIT_L_TO_H | SPI_CLK_DIV_64);
   
   enable_interrupts(GLOBAL);
   
   do{
      getVoltage(15);
      delay_ms(1000);   
   }while(1);

ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Wed Feb 04, 2015 3:39 am     Reply with quote

2.58V and 2.40V is a 7.5% difference. Seems large to me.

Some questions:
1) How trustworthy is your multimeter?
2) Are the PIC readings stable?
3) Is your power supply exactly the 3.315 volts you use in the formula?
4) What happens when you configure the reference voltage for the ADC from using Vdd instead to use the 4.096Vref?
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

PostPosted: Wed Feb 04, 2015 5:50 am     Reply with quote

The "averaging" is very strange, it sort of binary weights the readings, with earlier ones having less and less contribution to the end result. So, it won't work correctly - the result is not the average of the ten readings, and, as written, will under-read. Even if you take an initial reading, and then "average" in a further nine readings, you won't get a proper result, though it will look like it at first sight. As an example, with your code if the readings were 2.4, 0, 2.4, 0... i.e. five 2.4s and five 0s, then you result would be 0.7992, but the average should be 1.2!

With a 12 bit ADC result, you can add together up to 16 readings in an unsigned int16 (and you are using int32s!) before worrying about overflow. So, simply add the readings together in an int16 and divide by ten at the end.

What do you get if you take just one reading, rather than ten?
asmboy



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

View user's profile Send private message AIM Address

PostPosted: Wed Feb 04, 2015 7:03 am     Reply with quote

I second the need for certainty about the reference ADC voltage and concur that the running-average you attempt - is inherently biased "low".

You might consider this averaging method.

http://www.ccsinfo.com/forum/viewtopic.php?t=50320
temtronic



Joined: 01 Jul 2010
Posts: 9198
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Wed Feb 04, 2015 7:57 am     Reply with quote

also...

before you 'do the math', dump out the raw ADC 'bits' for every reading.
look at the result of say 500-1000 samples of a stable, known Vin.
The readings should be within 2-3 bits.

maybe cut a small program to sample 1000 times, record adc min and adc max.

also.. be aware that most DVM only sample at a 3Hz rate and not all DVMs are accurate !!

Jay
Ttelmah



Joined: 11 Mar 2010
Posts: 19436

View user's profile Send private message

PostPosted: Wed Feb 04, 2015 9:40 am     Reply with quote

Do the average as:
Code:

void getVoltage(int channel)
{
   int x=0;
   int32 adcValueAve=0;
   float voltage=0;
   
   set_adc_channel(channel);
   delay_us(25);

   for (x=0;x<16;x++)
   {
      adcValueAve+ = read_adc();
      delay_us(30);
   }
   adcValueAve/=16;
   voltage = ((adcValueAve * 3.315) / 4096.0);
   fprintf(PC,"voltage=%2.2f\n",voltage);   
}

and see what happens.

I'd be most suspicious of spike noise on the supply rail that is affecting the result. With a proper Vref, and careful layout, the ADC can get very good accuracy indeed.
overmindx



Joined: 06 Oct 2008
Posts: 43

View user's profile Send private message

PostPosted: Thu Feb 05, 2015 1:44 am     Reply with quote

Hi, thank you all for your replies. I already changed the way i perform my averaging calculations but I'm still getting slightly off readings. Just one question, if i use VSS_4V096 in setup_adc_ports, the voltage reference becomes 4.096 even if my Vdd is only 3.315 ? Is this a correct assessment? If so, when i calculate my voltage reading, i would use this formula? Is this correct?
Code:

     voltage = ((adcValue * 4.096) / 4096.0);
   


bryan
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

PostPosted: Thu Feb 05, 2015 2:51 am     Reply with quote

overmindx wrote:
if i use VSS_4V096 in setup_adc_ports, the voltage reference becomes 4.096 even if my vdd is only 3.315?


No, the PIC cannot generate an internal Vref higher than its power supply. Nor can it run with an external Vref above the supply. The datasheet gives the range of acceptable voltages. You can use the internal reference at 2.048V however, and you can use the internal 1.024V reference to calibrate your ADC when you are using a different ADC reference.

The maths are right, and give a very simple scaling of 1mV per bit (unless you go for the "4095 steps" approach to scaling, in which case the result is anything but convenient!). With 1mV per bit there's no need for any scaling, simply use the ADC count as the value in millivolts. With the 2.048V reference you'd simply have 2mV per bit.
Ttelmah



Joined: 11 Mar 2010
Posts: 19436

View user's profile Send private message

PostPosted: Thu Feb 05, 2015 5:50 am     Reply with quote

However there are several "bewares" here.

1) The minimum specified Reference Voltage range for the ADC, is 3v. It's a really silly bit of design, having a built in voltage reference that generates a voltage the ADC is not specified to work with. I suspect it'll probably work OK, but the accuracy will be down. Normally they specify the minimum as 'minimum reference voltage range for specified accuracy'. However on this chip they just specify 'minimum'....
2) Whatever Vref+ you choose, the voltage you read, must not exceed this. Allowable range for the input voltage, is Vref- to Vref+.
3) 4096, is the correct divisor. Look at document 70225B for the transfer function. Note how the line is actually chosen to make 4096 work.
4) I really have to go back to noise on the supply. How is the 3.315v being generated?. How is it smoothed?. How is the ground connected to the PIC. How do the grounds for the analog signal connect to the supply?.
5) Also how is AVdd supplied.
guy



Joined: 21 Oct 2005
Posts: 297

View user's profile Send private message Visit poster's website

PostPosted: Thu Feb 05, 2015 1:37 pm     Reply with quote

from my experience if you need accuracy neither Vdd from a regulator nor internal Vref are ever as accurate as you would expect. Only a high precision Vref (such as LM4040) or calibrating the internal Vref with an external reference and using the error factor in software will do the trick.

The datasheets specify an error of around 6% for the internal Vref so using 3 digits after the decimal point is somewhat ridiculous. 1.024V is a good idea but never really happens in real life... Cool
overmindx



Joined: 06 Oct 2008
Posts: 43

View user's profile Send private message

PostPosted: Thu Feb 05, 2015 9:31 pm     Reply with quote

Hi thank you all for your replies. anyway the 3.315 is coming from an LDO (xc6201p332mr-g) and it is clean when i check using a scope. Now, here is something interesting. When i changed the clock to
Code:

#use delay(clock=8000000)

and changed setup_adc to
Code:

setup_adc(ADC_CLOCK_DIV_8);

and taking 16 samples (adding everything first and dividing by 16), i now get 2.44v which is closer to actual voltage measured using DMM (2.40v).

Is this normal?

bryan
guy



Joined: 21 Oct 2005
Posts: 297

View user's profile Send private message Visit poster's website

PostPosted: Thu Feb 05, 2015 10:49 pm     Reply with quote

Now I see that your first averaging technique was non standard and Ttelmah corrected you. So this would help. The more samples you take the closer you will get (even beyond the 12 bits).
8MHz and ADC_CLOCK_DIV_8 - theoretically identical to 16MHz and ADC_CLOCK_DIV_16 but there could be slight differences.
I also have a habit of reading and dumping the first ADC sample which is often way off. If you do this before averaging you might get closer.
Ttelmah



Joined: 11 Mar 2010
Posts: 19436

View user's profile Send private message

PostPosted: Fri Feb 06, 2015 1:41 am     Reply with quote

'Clean when I check using a scope', doesn't actually say very much.
Switch the scope to AC, rather than DC, and turn the range up, so it is on say 10mV/division. Is the signal still clean?. Are you testing at the processor pins?.
You have not answered how Avdd is fed?. It is _vital_ that this has it's own decoupling if it is fed from the main supply. If you look at Microchip's examples, they have this fed through perhaps a 10R resistor, with a really good HF performance cap right by the pins of the processor.

The fact that running slower improves things, actually suggests strongly that you have a problem with high frequency noise at the processor.
overmindx



Joined: 06 Oct 2008
Posts: 43

View user's profile Send private message

PostPosted: Sun Feb 08, 2015 11:10 pm     Reply with quote

Hi,
Sorry for the late reply. Anyway, the Avdd is fed through an LDO (xc6201p332mr-g) and it outputs 3.315v. Also very near the Avdd and Avss pins, there is a 0.1uf capacitor. Using a scope, the voltage going to Avdd has a pk-pk voltage of around 90mv.
Also note that the voltage going to the adc pin goes through a voltage divider first since it is around 18v. A voltage divider of 1M and 147k is placed to lower it down. Does the voltage divider somehow affects the accuracy of the adc?
Ttelmah



Joined: 11 Mar 2010
Posts: 19436

View user's profile Send private message

PostPosted: Mon Feb 09, 2015 1:51 am     Reply with quote

90mV. No good at all.....

Your PIC ADC is capable of reading below 1mV. You have 90* this error going into the signal it uses for all it's control/processing. First reason the reading is wrong.
It sounds as if you have the AVdd, and directly connected to the processor Vdd, and just one supply capacitor on this. Not the way to do it. Ask yourself "if this was OK, why do MicroChip bring the AVdd 'out' as a separate pin"?.
Vdd actually generates a lot of noise. You are feeding this straight into the analog circuitry.
Your supply should feed Vdd, with a capacitor at this point, and then from Vdd have either a resistor, or a small inductor, feeding the AVdd pin, then a separate capacitor from AVdd to Gnd. You want well _under_ 1mV of ripple at the AVdd pin.
Then yes the voltage divider will cause problems. Two different reasons. If this uses 1% resistors, this limits the accuracy straight away. The second though is impedance. From the data sheet: "The maximum recommended impedance for analog sources is 2.5kR". Your source is 30* this.....
It takes current to charge and discharge the internal capacitor, and there is also a significant internal leakage. If you must have a high impedance divider, then you need to add a buffer amplifier to feed the ADC. Otherwise drop your resistor values to 4K64, and 31K6, which just meets the impedance spec.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
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