View previous topic :: View next topic |
Author |
Message |
ccowley
Joined: 12 Sep 2009 Posts: 19
|
ADC always reads 0 on PIC24EP512GU810 |
Posted: Thu Oct 02, 2014 10:03 am |
|
|
Hi,
I can't get the ADC working on this chip. I have searched for similar situations, but couldn't find a solution that helped me. Any help would be greatly appreciated.
I am using PCWHD Comiler version 5.028
The start of my header file looks like this:
Code: |
#include <24EP512GU810.h>
//#DEFINE Rev0
#DEFINE RevB
#FUSES NOWDT //No Watch Dog Timer
#FUSES WRT //Program Memory Write Protected
#FUSES PROTECT //Code protected from reads
#FUSES CKSFSM //Clock Switching is enabled, fail Safe clock monitor is enabled
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOJTAG //JTAG disabled
#device ICSP=1
#use delay(crystal=20000000)
|
My ADC init code looks like this:
Code: |
//********************** ADC ***********************************************
setup_adc_ports(sAN20, VSS_VDD);
setup_adc(ADC_OFF | ADC_TAD_MUL_0);
//setup_adc(ADC_CLOCK_INTERNAL);
//***************************************************************************
|
This code was produced by the CCS Code Wizard when I set up the chip.
I currently have 3.0 volts on AN20 (Pin E8).
My code to read the data and return the proper information is:
Code: |
void batt_display (void)
{ //Read battery level and display it.
//int16 batt_lvl = 932;
int16 batt_lvl;
batt_lvl = READ_ADC(ADC_START_AND_READ);
//int batt_lvl = 142;
//batt_lvl = read_adc(ADC_READ_ONLY);
//batt_lvl = read_adc();
if (batt_lvl >931) //233
{
print_img_batt100(60,15);
return;
}
if (batt_lvl >838)//210
{
print_img_batt90(60,15);
return;
}
if (batt_lvl >745)//187
{
print_img_batt80(60,15);
return;
}
if (batt_lvl >652)//164
{
print_img_batt80(60,15);
return;
}
if (batt_lvl >559)//141
{
print_img_batt70(60,15);
return;
}
if (batt_lvl > 559)//118
{
print_img_batt60(60,15);
return;
}
if (batt_lvl >466)//95
{
print_img_batt50(60,15;
return;
}
if (batt_lvl >373)//72
{
print_img_batt40(60,15);
return;
}
if (batt_lvl >280)//49
{
print_img_batt30(60,15);
return;
}
if (batt_lvl >187)//26
{
print_img_batt20(60,15);
return;
}
if (batt_lvl >94)//3
{
print_img_batt10(60,15);
return;
}
print_img_batt00(60,15);
return;
}
|
Everything works fine except for reading the ADC. I am not sure that the ADC_START_AND_READ does what I am expecting it to since I can't find any real documentation for the ADC commands, but from what I have found I expected my code to work.
Thanks for any suggestions that can be provided! |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Oct 02, 2014 11:02 am |
|
|
KISS
I only had a very short look at your program and that is mainly because:
- Your program is incomplete
- You post a huge function where only 2 lines are relevant.
- I'm lazy. If you don't take the effort to present a clean program then I'm out.
Please post a very short program demonstrating your problem, it's possible to do in about 15 lines to read the analog port and write to a display. The program should be complete so that we can copy/paste it into our compilers.
Oh yes: Code: | setup_adc(ADC_OFF | ADC_TAD_MUL_0); | This turns of the ADC. I hope you do switch it on again in the not posted parts of your program?.... |
|
|
ccowley
Joined: 12 Sep 2009 Posts: 19
|
|
Posted: Thu Oct 02, 2014 11:44 am |
|
|
Well thanks for calling me "stupid" for asking a question! I certainly am in this area, which is why I'm asking the question.
First you tell me my program is incomplete and then tell me I posted too much irrelevant information. I am just trying to show what I have done. I show everything that has anything to do with me reading and applying the ADC.
I thought that READ_ADC(START_ADC_AND_READ) started and read the ADC input that had been previously specified. Do I have to turn it on in some other way.
Thanks! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Oct 02, 2014 12:08 pm |
|
|
You need to change the setup_adc() line so it specifies the ADC clock.
You could use this initially, just to get your program working:
Code: | setup_adc(ADC_CLOCK_INTERNAL); |
Then change it to the correct ADC clock divisor, based on your PIC
oscillator frequency and the recommendations in the PIC data sheet. |
|
|
ccowley
Joined: 12 Sep 2009 Posts: 19
|
|
Posted: Thu Oct 02, 2014 12:50 pm |
|
|
Thanks for input! I really appreciate it!
My initialization code now looks like this:
Code: |
//********************** ADC ***********************************************
setup_adc_ports(sAN20, VSS_VDD);
setup_adc(ADC_OFF | ADC_TAD_MUL_0);
setup_adc(ADC_CLOCK_INTERNAL);
//***************************************************************************
|
My code to read the ADC now looks like this:
Code: |
void batt_display (void)
{ //Read battery level and display it.
int16 batt_lvl;
batt_lvl = READ_ADC(ADC_START_AND_READ);
. . . . the rest has been removed for brevity
|
I still only get a reading of 0 from the ADC. What else am I missing? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Oct 02, 2014 1:01 pm |
|
|
You should make a really simple program that displays the hex value of
the ADC result on a terminal window in your PC.
Example:
http://www.ccsinfo.com/forum/viewtopic.php?t=34386&start=1
This will prove if you can actually read the ADC or not. Once you have
it working, then you can add your chain of if() statements.
In the code below, the line in bold doesn't do anything. Its settings are
replaced by the last setup_adc() statement. Only the last one takes effect.
Quote: | setup_adc_ports(sAN20, VSS_VDD);
setup_adc(ADC_OFF | ADC_TAD_MUL_0);
setup_adc(ADC_CLOCK_INTERNAL); |
|
|
|
ccowley
Joined: 12 Sep 2009 Posts: 19
|
|
Posted: Thu Oct 02, 2014 1:16 pm |
|
|
Thanks for trying PCM!
I will keep trying to mess with it. I have wasted a couple of days trying to get this silly thing to read something, but just haven't got it yet. I have read ADC's on 8 bit units before without a problem, but seem to really be missing something on this 16 bit. I have tried every combo I could think to try, but I guess I'll start over again. I was hoping somebody might notice an obvious software switch I was missing or something, but that doesn't seem to be the case.
Thanks again! |
|
|
ezflyr
Joined: 25 Oct 2010 Posts: 1019 Location: Tewksbury, MA
|
|
Posted: Thu Oct 02, 2014 1:25 pm |
|
|
Hi,
I don't see anywhere that you are actually selecting the channel to read. A line similar to this is required:
Code: |
set_adc_channel(0);
|
Note that this is separate and distinct from this function call:
Code: |
setup_adc_ports(sAN20, VSS_VDD);
|
John |
|
|
ccowley
Joined: 12 Sep 2009 Posts: 19
|
|
Posted: Thu Oct 02, 2014 2:10 pm |
|
|
Hi Jon,
Thanks for the info. I have seen mention of the channel, but since the Wizard didn't include it I didn't think I needed it. I tried channel 0 and that didn't work, so it looks like it's time to delve into the data sheet again to check out the channel settings.
Code: |
//********************** ADC ***********************************************
setup_adc_ports(sAN20, VSS_VDD);
setup_adc(ADC_CLOCK_INTERNAL);
set_adc_channel(0);
//***************************************************************************
|
Thanks again for the additional guidance!
Carl |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19515
|
|
Posted: Thu Oct 02, 2014 3:02 pm |
|
|
Code: |
setup_adc_ports(sAN20, VSS_VDD);
setup_adc(ADC_OFF | ADC_TAD_MUL_0);//this line is now pointless
setup_adc(ADC_CLOCK_INTERNAL); //This is the one used....
|
The _last_ setup_adc, is the one being used. |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1349
|
|
Posted: Thu Oct 02, 2014 3:12 pm |
|
|
ccowley wrote: | Hi Jon,
Thanks for the info. I have seen mention of the channel, but since the Wizard didn't include it I didn't think I needed it. I tried channel 0 and that didn't work, so it looks like it's time to delve into the data sheet again to check out the channel settings.
Code: |
//********************** ADC ***********************************************
setup_adc_ports(sAN20, VSS_VDD);
setup_adc(ADC_CLOCK_INTERNAL);
set_adc_channel(0);
//***************************************************************************
|
Thanks again for the additional guidance!
Carl |
sAN20 is channel 20:
sAN0 is channel 0
sAN1 is channel 1
sAN2 is channel 2
and so on |
|
|
ccowley
Joined: 12 Sep 2009 Posts: 19
|
|
Posted: Thu Oct 02, 2014 4:02 pm |
|
|
Hi Jeremiah,
I tried changing it to 20 and it is still not working.
Thanks for the suggestion! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19515
|
|
Posted: Fri Oct 03, 2014 1:09 am |
|
|
Start at the beginning.
The 'wizard' is thick. It'll accept what _you_ tell it. This leads to it often generating things that don't work.
You can use the wizard fine, but _you_ need to understand what the options do, and set them up correctly.
So, ignoring the wizard.
The 'setup_adc_ports' command, configures which ADC pins are connected to the multiplexer, and to the reference.
The setup_adc command configured the ADC clock, and delays used internally.
The 'set_adc_channel' command then selects which of the input pins is actually fed to the ADC.
The commands are not 'cumulative'. If you send a new setup_adc command, it _overwrites_ any previous one.
Code: |
setup_adc_ports(sAN20, VSS_VDD); //connects AN20 to the multiplexer
//and Vss/Vdd as the Vref.
setup_adc(ADC_CLOCK_INTERNAL); //starts the ADC clock
set_adc_channel(20); //Now physically connects AN20 from the
//multiplexer to the ADC. Note the 20...
//At this point one has to wait for the capacitor in the ADC to charge to
//the voltage. This can be done automatically (this is the ADC_TAD_MUL
//setting). However ignore this for now.
delay_us(5); //more than the time needed.
batt_lvl = READ_ADC(); //START_AND_READ, is the _default_
|
You have to set the channel to the same channel you want to read. You can though connect several pins to the multiplexer. So if (for instance), you wanted to read AN20, and AN18, you could use:
setup_adc_ports(sAN20 | sAN18, VSS_VDD);
and both pins would be connected to the multiplexer. Then to read the first, you would use set_adc_channel(18), and the ADC would then read sAN18. set_adc_channel(20), and the ADC would read sAN20.
The channel has to both be connected to the multiplexer, and selected.
Currently you are connecting sAN20, and trying to read sAN0....
The 'start', versus 'start_and_read' etc., on the read_ADC, starts the actual 'conversion' process. But the physical ADC 'clock', is enabled by the setup_adc command. Without a clock, it won't convert.
The default operation, is 'start_and_read'. With this, the ADC will start a conversion, wait for it to complete, and read the result. The other options only become wanted, when you use an interrupt, where (for instance) the physical 'start' can be issued automatically by a timer, so then you only want to read the result, when the interrupt triggers to say this has happened. |
|
|
ccowley
Joined: 12 Sep 2009 Posts: 19
|
|
Posted: Fri Oct 03, 2014 10:35 am |
|
|
Hi Ttelmah,
Thanks for great expanation of what the commands are doing! I really appreciate the help!
I have tried the settings you suggest and it still doesn't work. This morning I read the settings of AD1CON1 to see if I could gather any additional clues. I found it was set to:
ADD NAME HEX DEC BINARY
0320 AD1CON1 0xC6E1 50913 11000110 11100001
Which shows the ADC is on and functioning, but it is also in 12 bit mode, which I prefer 10 bit mode. Also, it's output is a Fractional Out (Dout = dddd dddd dddd 0000), where I would prefer just an integer (Dout = 0000 dddd dddd dddd).
Does anyone know how to tell the compiler to set the AD1CON1 register to 10 bit mode and to use the integer out? Am I right in what I am gathering from the register settings? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19515
|
|
Posted: Fri Oct 03, 2014 10:53 am |
|
|
#device ADC=xx
ADC=16 will give a left justified 16bit result.
etc.. |
|
|
|