![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
benoitstjean
Joined: 30 Oct 2007 Posts: 553 Location: Ottawa, Ontario, Canada
|
PIC24EP512GP806 ADC QUESTION [SOLVED] |
Posted: Tue Mar 21, 2017 1:20 pm |
|
|
Compiler: 5.024
Device: PIC24EP512GP806
Hello all,
I haven't used the A to D modules in the past on the PIC devices and I'm pretty sure I'm doing something silly. I'm using AN0 on pin B0 (pin 16).
My goal is only to check the ADC once every minute or so.... turn-on the A to D, get a reading, then turn-it off.
I will add better circuitry when time permits but for now, I just want to implement the code to read the ADC, that's all. The electronics component of it will be looked into at a later stage. But what I have so far is a simple voltage divider with:
R1 = 60.4K Ohms
R2 = 5.4K Ohms
*Those are the only two parts left I have on-hand and work ok for the calculations.
At the input of the voltage divider (R1), I want to have up to 40V. At the output of the divider (R1 <-> R2 junction), I want a voltage proportional to the input voltage from, let's say, 3.5V to 40V. I didn't go all the way up to 40V but I tested with the following:
input = 18V, output = 1.47V
input = 14V, output = 1.15V
input = 12V, output = 0.98V
And so on. This is all nice and fine, numbers are OK, it's linear etc. Measured voltages match calculated voltages.
I have these lines for the ADC initialization that I got out of an example:
Code: |
setup_adc_ports( sAN0, VSS_VDD );
setup_adc( ADC_CLOCK | ADC_TAD_MUL_2 );
set_adc_channel( 0 ); |
BUT... As soon as power is applied to the circuit, the voltage at the output of the divider is fine... but as soon as the PIC gets to the A to D initialization code (anyways, from what I can tell, that's when it happens), then the voltage at the output of the voltage divider gets clamped and goes down drastically and the measured voltages are totally wrong.
So obviously, I'm doing something wrong (I feel stupid... I should know this :( )
Any idea?
Thanks again,
Ben
Last edited by benoitstjean on Wed Mar 22, 2017 4:05 pm; edited 1 time in total |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Mar 21, 2017 6:44 pm |
|
|
Test each of those lines individually. Comment out all but one.
Find out the exact line that causes the problem. Then look at the .LST
file ASM code and see what the function is doing. Compare it to the
PIC data sheet.
Since you said you're not sure if the ADC lines cause the problem, then
comment ALL of them OUT. If you still get the problem, then it's not
the ADC lines.
Also, when you report a problem, be more exact. Don't say "gets
clamped and goes down". Post the exact output voltage from the
voltage divider. Also post the exact input voltage.
Based on your description, it's possible that the PIC pin is being made
into an output pin, and iis probably set as a low level output. This could
be due to a compiler bug. It's also possible that you could have your
voltage divider connected to the wrong pin (something other than Pin B0).
Or, some other necessary configuration code is missing from your program. |
|
![](templates/subSilver/images/spacer.gif) |
benoitstjean
Joined: 30 Oct 2007 Posts: 553 Location: Ottawa, Ontario, Canada
|
|
Posted: Tue Mar 21, 2017 7:02 pm |
|
|
Thanks for the reply.
When I say 'clamped down', I mean it goes from like 1.15V for a 14V input down to like 60mV. And this happens really like half a second after the MCU powers-up which coincides with when I call the three lines stated in my original post where I configure the ADC.
I'll do more investigating tomorrow when I get to work.
One thing I just noticed, although I don't know if it has any effect, is that I typed "#DEFINE ADC=16" rather than "#DEVICE ADC=16". So the #define of course will be ignored but then, does the ADC still get setup with a default value? Or is it that if the compiler does not see the #device ADC line, it shuts-down the ADC? I don't have the circuit with me at the moment, just my PC so that's why I can't test.
To see which line causes that behaviour, since I have a serial menu through Tera Term, I will simply add menu options that will execute the lines one at a time and I will check the voltage after every function.
Also, could it be with the resistors I am using (e.g. too high value)? I saw on another post that some guy was also trying to monitor in the 40V range and he also had like a 36K resistor in his voltage divider and said all was working fine.
The pin _is_ the right pin - it's B0 pin 16. I made both the schematic and the PCB layout and I have checked and re-checked and even checked under a microscope today, so that's confirmed it is correct.
Another thing I thought of - it came to mind that when I put the unti to sleep, I set all PMDx registers to 0xFF. Upon booting the unit today, I added code that checks PMD1 after the ADC1 is configured and the result is 0x0009:
Code: |
#byte PMD1 = getenv("byte:PMD1")
fprintf( MONITOR_SERIAL, "\n\rPMD1: 0x%04X", PMD1 );
|
On my serial console: PMD1: 0x0009
Bit 3 is for SPI1 and it is in fact turned-off, bit 4 is SPI2 and it is in fact turned-ON (therefore bit = 0), but bit 0 is ADC1 and based on the result, it means that ADC1 is DISABLED.
So should I force this bit to '1'? Why would it be disabled when the three lines prior are the lines that setup the ADC?
Thanks again,
Benoit |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19382
|
|
Posted: Wed Mar 22, 2017 1:48 am |
|
|
As a general comment, your divider does not have a low enough source impedance to reasonably drive the ADC on your chip. The recommendation in the sheet for this (the family note about ADC's, not the data sheet), says the source impedance should be <200R.....
However this is to use the full 12bit accuracy. There is a leakage current inside the ADC which will bias the readings if the source impedance is significantly above this, which for 12bit operation starts to become significant. However it won't pull the signal down as hard as you are seeing.
So the obvious thing is that in fact somehow the pin is being set as an output.
You really need to forget your code for a moment. Write a 'mini' program that just sets up the ADC. Then test this line by line. Suggest if you don't have the ability to single step the code, but you have a serial port, or a button, then write the code so it does one line, stops and waits for a serial character (or the button), then the next line etc.. You can then identify exactly which line is giving the problem (PCM_Programmer has already suggested areas of this). If this works, then you have ruled out the ADC.
Though it wouldn't cause the pull down, you don't tell us your clock rate, so we can't really comment on whether the ADC clock rate is acceptable.
5.024, is early enough that it does have problems with some parts on some of the more complex chips.
However compiling just the setup lines you post, with this, gives:
Code: |
.................... setup_adc_ports(sAN0, VSS_VDD);
00218: CLR E0E
0021A: MOV #1,W4
0021C: MOV W4,E1E
0021E: CLR E2E
00220: CLR E3E
00222: CLR E4E
00224: CLR E5E
00226: CLR E6E
00228: CLR AD1CON2
0022A: CLR AD2CON2
.................... setup_adc(ADC_CLOCK | ADC_TAD_MUL_2);
0022C: MOV #200,W4
0022E: MOV W4,AD1CON3
00230: MOV #84E0,W4
00232: MOV W4,AD1CON1
.................... set_adc_channel(0);
00234: CLR AD1CHS0
|
Which has the first line setting up all the ANSEL registers. The second sets up ADC1, and the third sets up the channel selection. All perfect, and not touching anything else.
If you override the PMD settings, it becomes your responsibility to put them back. The adc setup won't change them. All modules are enabled by default.
Normally I'd expect to 'snapshot' the PMD settings before setting the bits, and then after sleep, write the snapshot back. |
|
![](templates/subSilver/images/spacer.gif) |
benoitstjean
Joined: 30 Oct 2007 Posts: 553 Location: Ottawa, Ontario, Canada
|
|
Posted: Wed Mar 22, 2017 5:03 am |
|
|
Hi TTelmah,
My external crystal is the infamous 36.864MHz and internally, I run the PLL at 129.024MHz (remember from my previous post on PLL configuration?).
For the PMD1 bits, I guess I should have written that I _do_ read the register at PIC power-up immediatedly after I enable the WDT. The value is the same, 0x0009. Then I start setting-up a few peripherrals like SPI1 = off, SPI2 = on, SPI3 = on then setup the ADC and read PMD1 and it's still 0x0009. The PMD registers are turned-off only when the device goes to sleep and that's if I have power management enabled. In this case, I have power management turned-off so that I can work on the device.
Back to my previous post - is it possible that the <#define ADC=16> caused the problem since it is obviously a typo on my behalf and should have been <#device ADC=16>? I'm pretty sure it's not an issue other than the ADC bits revert to a default value but given that it is a typo, I am asking.
As for the input impedance, perhaps you can shed a bit of light... I need a voltage divider that can take up to 42V. I want the output to be proportional so that at 42V inpupt, the output will be 3.5V. What resistor values do you suggest I use then? Should I go with like 5.5K and 500 ohms? That gives a perfect 3.5V output at 42V input.
I will be at work in about an hour, so I will be able to make some tests such as initializing each line one by one with a button.
Thanks again,
Benoit |
|
![](templates/subSilver/images/spacer.gif) |
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Wed Mar 22, 2017 6:36 am |
|
|
As to the impedance, there's a well-known bit of circuit theory: Thévenin's theorem, that helps here. Basically a Thévenin source is an ideal voltage source in series with a resistance that defines the impedance of the resulting Thévenin source.
In your circuit, the Thévenin source voltage is your divided-down voltage, i.e. your 3.5V and the series impedance is the parallel combination of the two resistors, i.e. 5K5 || 500R = 438.3 Ohms. That's not bad, and will give a pretty reasonable, but not perfect result.
One issue (apart from the input impedance/leakage of the ADC reduing the voltage a little) will be that the internal hold capacitor will not charge to the required voltage as quickly as it should, so Tacq would have to be longer than with a lower impedance source. I don't see these as being a big issue in the sort of use you are suggesting, and I suspect will be quite usable.
Another issue to watch for in circuits like this is the voltage across the upper dropper resistor. 40V is probably not going to cause any issues, but if 100V or more is being measured would need the resistor to be uprated, and may benefit from becoming two or more series resistors, which has the added benefit of if one fails short circuit, the PIC is not subjected to the full voltage. Another thing to consider is overvoltage protection - do you need something to prevent the ADC input going over the supply? Yes, there are internal clamp diodes in the PIC (they are generally inherent to the semiconductor structure/process), but can they deal with the current that a short-circuited resistor would pass? Also clamp diodes to the supply rail aren't so good as the supply generally cannot sink current: it only supplies it, so a high external voltage simply lifts the supply rail via the clamp diode. The result is a possibly a big, expensive blow-up :-( |
|
![](templates/subSilver/images/spacer.gif) |
jeremiah
Joined: 20 Jul 2010 Posts: 1329
|
|
Posted: Wed Mar 22, 2017 6:50 am |
|
|
Those new values give you an input impedance of 458.33 ohms, which is much closer than your original values were (4956.84 ohms). 3.5v volts is high though if you are using a 3.3v device.
To calculate the input impedance, you need to take the Thevenin voltage and current and divide them. For a standard resistor divider, the equation for input impedance will be:
R1*R2/(R1+R2) = Rinput_to_ADC
One other thing you can do if you need the higher value resistors is to use a unity follower configuration on an opamp. The opamp will give you the low impedance into the ADC. You have to make sure though that if the opamp has a high input bias current that you put a resistor in the feedback loop that matches the impedance of your divider (same equation) as that can add an offset as well if you don't.
That said, it's much easier just to change out resistors. Maybe a 200 and something at or above 2.5k |
|
![](templates/subSilver/images/spacer.gif) |
benoitstjean
Joined: 30 Oct 2007 Posts: 553 Location: Ottawa, Ontario, Canada
|
|
Posted: Wed Mar 22, 2017 6:52 am |
|
|
OMG... Thévenin's theorem... I haven't heard about that for ages!
I guess some parts of my electronics is a bit rusty!
Ok so then, based on what TTelmah said, the source impedance should be <200R. So does this mean that if I apply Thévenin's theorem, the calculated resistance should be <200R? That's more than half of what you calculated with my setup where the calculated resistance is 483R. Or am I completely off the track here?
I am not measuring anything fancy here, just a voltage from 42VDC down at fixed (slow) intervals.
Guaranteed it will never go above 42V and even then, I don't think that it will _ever_ be used that high... 42V is the power supply rating but most of the time, the main supply will be 14V and less.
As for the clock source, what would be your suggestion?
Thanks again,
Benoit |
|
![](templates/subSilver/images/spacer.gif) |
jeremiah
Joined: 20 Jul 2010 Posts: 1329
|
|
Posted: Wed Mar 22, 2017 7:38 am |
|
|
In practice, your input impedance is going to be slightly less than the lower resistor, so if you want <200 ohms, a 200 ohm resistor would be a good target. then you just pick an upper resistor that gets you safely under 3.3v
Like I said earlier. a 200 ohm and a 2.5k (or larger) upper resistor will work as long as you don't go above 42V
42*200/(200 + 2500) = 3.11v and has an impedance of around 185 ohms (which is <200). |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19382
|
|
Posted: Wed Mar 22, 2017 8:15 am |
|
|
It depends on what accuracy you actually want from your application....
You are not going to get very good accuracy anyway, since you are using the supply voltage as the ADC reference., Why not switch down and run the ADC in 10bit mode?.
The point is that the ADC has an internal resistance between the input and the capacitor, through which the voltage has to travel. Then the capacitor has internally a small leakage current pulling it down. The specified impedance, is to give a total error less than 0.5bit from these sources, when running in the 12bit mode. Potentially run in 10bit mode and you will be within 0.5 bit with about 4* the impedance (this is one reason why the required impedance is specified as higher on the 10bit ADC's). The other reason is to give fast charging or the capacitor. |
|
![](templates/subSilver/images/spacer.gif) |
benoitstjean
Joined: 30 Oct 2007 Posts: 553 Location: Ottawa, Ontario, Canada
|
|
Posted: Wed Mar 22, 2017 8:44 am |
|
|
Ideally, for accuracy, I'd go with a higher resolution but 12-bit or 10-bit mode works, then that's good enough. At 10-bits, it would yield 1-bit per 0.041V. I'm interested in thenths of volts (e.g. 9.8, 7.4, 4.2 etc... not 9.874, 7.413, 4.237).
So at 3.5V, I'd have an approximate ADC value of 85 in 10-bit mode. If I read a value of 85, then that is actually the 'critical' point meaning that the battery is getting low. In general, the battery would be 12V. If I read an ADC value of around 292, then I know I'm at 12V. When I do the reads, perhaps I could do like 10 reads, do an average and spew that out.
The circuit has a modem that has its own voltage monitoring mechanism that monitors its main power and notifies me when its voltage is below 3.4V (I set it at that).
What I am doing is monitoring, at fixed intervals, the main battery voltage. That's it.
So now, for the test results:
Input voltage on voltage divider: 14V
Resistors used: 5.43K and 500ohms 1% resistors
Calculated output voltage for 14V: 1.184V
Unit powers-up....
Initial voltage measured at ADC pin 16 (B0) = 1.1908V
After running setup_adc_ports( sAN0, VSS_VDD ), pin B0 = 1.1908V
After running setup_adc( ADC_CLOCK_DIV_4 | ADC_TAD_MUL_8 ) = 1.1908V
CODE immediately at start of main():
fprintf( MONITOR_SERIAL, "\n\rMeasure voltage then press a key to run setup_adc_ports( sAN0, VSS_VDD )" );
fgetc( MONITOR_SERIAL );
setup_adc_ports( sAN0, VSS_VDD );
fprintf( MONITOR_SERIAL, "\n\rMeasure voltage then press a key to run setup_adc( ADC_CLOCK_DIV_4 | ADC_TAD_MUL_8 )" );
fgetc( MONITOR_SERIAL );
setup_adc( ADC_CLOCK_DIV_4 | ADC_TAD_MUL_8 );
fprintf( MONITOR_SERIAL, "\n\rMeasure voltage then press a key to run set_adc_channel( 0 )" );
fgetc( MONITOR_SERIAL );
set_adc_channel( 0 );
fprintf( MONITOR_SERIAL, "\n\rDone... Press a key to continue normal execution" );
fgetc( MONITOR_SERIAL );
Then, code starts and voltage drops. Getting close I guess... ADC seems to run fine, some obviously something else is causing the issue.
Back soon...
Benoit |
|
![](templates/subSilver/images/spacer.gif) |
jeremiah
Joined: 20 Jul 2010 Posts: 1329
|
|
Posted: Wed Mar 22, 2017 9:24 am |
|
|
What does the voltage drop to at the ADC and did you check the input 42v after the drop to make sure that didn't drop? |
|
![](templates/subSilver/images/spacer.gif) |
newguy
Joined: 24 Jun 2004 Posts: 1903
|
|
Posted: Wed Mar 22, 2017 9:43 am |
|
|
I suspected you were measuring battery voltage. Time to chime in with my $0.02.
By lowering your resistive divider's values, you're increasing the draw on your battery unless you add a FET to connect battery then measure voltage then disconnect battery.
Without a FET you're adding a bleed. This may or may not have an effect on the overall lifetime of your battery, but for really low power operation, you must consider all current draws. |
|
![](templates/subSilver/images/spacer.gif) |
benoitstjean
Joined: 30 Oct 2007 Posts: 553 Location: Ottawa, Ontario, Canada
|
|
Posted: Wed Mar 22, 2017 10:31 am |
|
|
Doh! Found it... (as I bang my head on the table)...
So during the MCU initialization, I was adding printf statements to print the ADC caputred value after every peripherral I initialize.... until I realized way further down the code that some port B bits are used for other things on the device...
During the initialization, I read data from my EEPROM (the data read corresponds to the last state of the two of the port B bits) and I thought I was setting only these two bits to their respective states but I was doing an & with 0xFFFF and that, of course, was setting pin B0!
Now, instead, I read the EEPROM and set those two bits only individually.
The ADC is a new option I'm adding but that other code setting the port B has been there for the longest time and never had to touch it so I sort of forgot about it and didn't realize that it was messing-up the ADC port.
All works fine now. I can change the voltage on my power supply and I see the A to D value change on the console.
I feel shame.
Thanks all for your help and time. It's always appreciated.
Benoit |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|