View previous topic :: View next topic |
Author |
Message |
haronraziq
Joined: 25 Aug 2011 Posts: 17
|
Problem with ADC |
Posted: Sun Oct 16, 2011 9:35 pm |
|
|
I have been trying to get the ADC to work correctly based on a CCS C example, but for some reason the output of the ADC is incorrect. For example, an input of 5 volts displays only 3.76 volts.
Below is my code, any help is greatly appreciated.
Code: |
void displayVoltage(int adc)
{
char voltage[9];
sprintf(voltage, "%f", (float)adc * .01960784); // Converts adc to text
voltage[4] = '\0'; // Limit shown digits to 3
glcd_rect(45, 18, 69, 25, YES, OFF); // Clear the old voltage
glcd_text57(45, 18, voltage, 1, ON); // Write the new voltage
}
void main()
{
int8 adc = 0;
setup_adc_ports(AN0_AN1_AN3);
setup_adc(ADC_CLOCK_DIV_2);
set_adc_channel(1);
glcd_init(ON);
adc = read_adc(); // Read a value from the ADC
displayVoltage(adc); // Display the reading
|
|
|
|
haronraziq
Joined: 25 Aug 2011 Posts: 17
|
|
Posted: Sun Oct 16, 2011 9:40 pm |
|
|
I am using the PIC16f877 and a 20 MHz oscillator. |
|
|
haronraziq
Joined: 25 Aug 2011 Posts: 17
|
|
Posted: Sun Oct 16, 2011 10:25 pm |
|
|
Also, for some reason it does not display any voltage greater than 5 volts or less than .1 volts. Please if anyone knows what the problem is, I would really appreciate the help. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Oct 16, 2011 10:44 pm |
|
|
Quote: | setup_adc(ADC_CLOCK_DIV_2);
I am using the PIC16f877 and a 20 MHz oscillator.
For example, an input of 5 volts displays only 3.76 volts.
|
Download the 16F877 data sheet:
http://ww1.microchip.com/downloads/en/DeviceDoc/30292c.pdf
Read this section. Look specifically at Table 11-1:
Quote: |
11.2 Selecting the A/D Conversion Clock
TABLE 11-1: TAD vs. MAXIMUM DEVICE OPERATING FREQUENCIES (STANDARD DEVICES (C))
|
Note the clock divisor value that you should be using at 20 MHz.
Compare it to the one you're currently using.
Look in the 16F877.h file to see the list of clock divisor constants
that you are allowed to use. You will see the correct one in the list. |
|
|
haronraziq
Joined: 25 Aug 2011 Posts: 17
|
|
Posted: Sun Oct 16, 2011 11:25 pm |
|
|
I tried changing it to the corrected value that but still no change. The output still will not show anything greater than 5 V or anything less than .1
Here is the new code.
Code: |
void displayVoltage(int adc)
{
char voltage[12];
sprintf(voltage, "%f", adc * .01960784); // Converts adc to text
glcd_rect(45, 18, 69, 25, YES, OFF); // Clear the old voltage
glcd_text57(0, 24, voltage, 1, ON); // Write the new voltage
}
void main()
{
int16 adc = 0;
setup_adc_ports(AN0_AN1_AN3);
setup_adc(ADC_CLOCK_DIV_32);
set_adc_channel(1);
glcd_init(ON);
adc = read_adc(); // Read a value from the ADC
displayVoltage(adc); // Display the reading
} |
and the defines/headers I have, which i did not mention before are:
Code: |
#include <16f877.h>
#include <math.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //Crystal osc <= 4mhz for PCM/PCH , 3mhz to 10 mhz for PCD
#FUSES NOBROWNOUT //No brownout reset
#use delay(clock=20000000)
|
I also include some graphic lcd drivers but I am sure those are not causing the problem. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Oct 16, 2011 11:43 pm |
|
|
Quote: | The output still will not show anything greater than 5 V or anything less than .1 |
I'm not sure I understand this comment. Do you believe you can
directly read the A/D value of input voltages that are greater than +5.0v
and less than 0 volts (negative voltages) ?
You can't. To do it, you would need an external level-shifter circuit,
which is usually done with a resistor network (voltage divider) or an
Opamp circuit.
If you are trying to put voltages on the A/D pin that are outside of the
specified maximum and minimum range, you could damage the A/D pin
on the PIC. Look in the 16F877 data sheet, in the back, in the Electrical
Specifications section. It's all there.
Last edited by PCM programmer on Mon Oct 17, 2011 1:03 am; edited 2 times in total |
|
|
haronraziq
Joined: 25 Aug 2011 Posts: 17
|
|
Posted: Sun Oct 16, 2011 11:57 pm |
|
|
Sorry for the confusion but I got mixed up. I understand the +5 volt limitation, I meant that it cannot read voltages greater than zero but less than .01. Is the resolution of the ADC too small for this? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Oct 17, 2011 1:00 am |
|
|
Quote: | void main()
{
int16 adc = 0;
setup_adc_ports(AN0_AN1_AN3);
setup_adc(ADC_CLOCK_DIV_32);
set_adc_channel(1);
glcd_init(ON);
adc = read_adc(); // Read a value from the ADC
displayVoltage(adc); // Display the reading
}
|
I notice that you don't have a delay after you set the ADC channel.
The spec for the acquisition time for the 16F877 is about 20 usec.
Look in this section. That's where I got the 20 usec value:
Quote: |
11.1 A/D Acquisition Requirements |
The link below has some tests that show what happens if you don't have
the correct acquisition time delay after you set the A/D channel:
http://www.ccsinfo.com/forum/viewtopic.php?t=44201&start=6
Note that the required delay is shorter for the 18F452. For your PIC,
it's 20 usec.
However, there is probably enough delay in the glcd_init() function
to do this. But just to be safe, put a delay_us(20); statement after
the set_adc_channel() line.
What is the output impedance of the device that supplies the voltage
to pin AN1 ? It must be 10K ohms or less (for the 16F877).
If the impedance is too high, you could easily get a reduced voltage
reading. What is the device that supplies the voltage to pin AN1 ?
One last thing. You really should add a while(1); statement at the the
end of main(). It prevents the program from running off the end of the
last brace, where it will execute a hidden SLEEP instruction and put the
pic in power-down mode.
Do it as shown in bold below:
Quote: | .
.
.
adc = read_adc(); // Read a value from the ADC
displayVoltage(adc); // Display the reading
while(1);
} |
|
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
Magic numbers |
Posted: Mon Oct 17, 2011 1:39 am |
|
|
How did you calculate the scaling value .01960784? This is what I call a magic number. It hides what you intended, and it hides whether you've made some error. Magic numbers are poor coding style. I always let the compiler derive these numbers from the relevant basic parameters as constants in#defines utilising constant collapsing. I won't show you how just yet as your number is not quite right and will give you inaccurate results even if everything else is working correctly. Not very wrong, but not precisely correct either. As with a school maths question I want to see the working out, not just the final result.
RF Developer |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Mon Oct 17, 2011 5:49 am |
|
|
ADC probably is in 8 bit mode...( 5V /255= .019......)
so 1 bit of ADC data is equal to = +-.019 volts.
Simply add code( as shown in header file) to get adc into 10 bit mode. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Mon Oct 17, 2011 10:08 am |
|
|
Do you have some reason that you are NOT using the improved 10 bit resolution of the '877 ?
Once you do sort out your clock and ADC setup issues -
4 times 8bit resolution is ready and waiting for you - ...;-)) |
|
|
haronraziq
Joined: 25 Aug 2011 Posts: 17
|
|
Posted: Mon Oct 17, 2011 10:50 am |
|
|
I am just testing by using a dc voltage source input. |
|
|
haronraziq
Joined: 25 Aug 2011 Posts: 17
|
|
Posted: Mon Oct 17, 2011 3:03 pm |
|
|
I cannot find any code in the header file that corresponds to changing the adc resolution. How can I change to the 10 bit mode? |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Mon Oct 17, 2011 3:17 pm |
|
|
#Device ADC=10; _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
haronraziq
Joined: 25 Aug 2011 Posts: 17
|
|
Posted: Mon Oct 17, 2011 3:26 pm |
|
|
That's what I thought. Here take a look at my code, I edited my code accordingly (the define is in another file but i did do it), but I still get the same output. It shows zero for an input of .01 volts.
Code: | void displayVoltage(int adc)
{
char voltage[12];
sprintf(voltage, "%f", (float)adc * .0048875855); // Converts adc to text
glcd_rect(45, 18, 69, 25, YES, OFF); // Clear the old voltage
glcd_text57(0, 24, voltage, 1, ON); // Write the new voltage
}
void main()
{
int adc = 0;
setup_adc_ports(AN0_AN1_AN3);
setup_adc(ADC_CLOCK_DIV_32);
set_adc_channel(1);
delay_us(200);
glcd_init(ON);
adc = read_adc(); // Read a value from the ADC
displayVoltage(adc); // Display the reading
} |
|
|
|
|