|
|
View previous topic :: View next topic |
Author |
Message |
overmindx
Joined: 06 Oct 2008 Posts: 43
|
adc accuracy problem |
Posted: Wed Feb 04, 2015 2:56 am |
|
|
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
|
|
Posted: Wed Feb 04, 2015 3:39 am |
|
|
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
|
|
Posted: Wed Feb 04, 2015 5:50 am |
|
|
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
|
|
Posted: Wed Feb 04, 2015 7:03 am |
|
|
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: 9221 Location: Greensville,Ontario
|
|
Posted: Wed Feb 04, 2015 7:57 am |
|
|
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: 19499
|
|
Posted: Wed Feb 04, 2015 9:40 am |
|
|
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
|
|
Posted: Thu Feb 05, 2015 1:44 am |
|
|
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
|
|
Posted: Thu Feb 05, 2015 2:51 am |
|
|
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: 19499
|
|
Posted: Thu Feb 05, 2015 5:50 am |
|
|
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
|
|
Posted: Thu Feb 05, 2015 1:37 pm |
|
|
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... |
|
|
overmindx
Joined: 06 Oct 2008 Posts: 43
|
|
Posted: Thu Feb 05, 2015 9:31 pm |
|
|
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
|
|
Posted: Thu Feb 05, 2015 10:49 pm |
|
|
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: 19499
|
|
Posted: Fri Feb 06, 2015 1:41 am |
|
|
'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
|
|
Posted: Sun Feb 08, 2015 11:10 pm |
|
|
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: 19499
|
|
Posted: Mon Feb 09, 2015 1:51 am |
|
|
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. |
|
|
|
|
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
|