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 CCS Technical Support

Help reading in analog signal
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
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

Help reading in analog signal
PostPosted: Wed Nov 15, 2006 10:00 am     Reply with quote

Hi all,

I'm reading an analog signal from a Sharp IR sensor (GP2Y0A02YK) into my PIC18F8722. I have a few questions I was hoping someone could answer:

1. The values I get back from the sensor range from about 700-840 depending on distance. However, once in a while I will get a value of 1023. I'm wondering is this a problem with my code (i.e. I'm reading the values too fast) or is it just something to expect from the sensor (see code below). I'm hoping it's software, but if not I can always just feed it through a low-pass filter.

2. Is there anyway to expand this range...from 0-800 for example.

3. It seems like setting up the adc during each iteration of the while loop is not efficient. However, if I take the setup_adc_ports, setup_adc, and set_adc_channel commands outside the loop, it doesn't work. Do I need to keep these inside the while loop?

Thanks in advance,
-weg

Code:

#include <18F8722.h>
#device adc=10
#include <stdlib.h>
#include <math.h>

#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP
#use delay(clock=10000000)
#use rs232(baud=9600, xmit=PIN_A4, rcv=PIN_A5, stream=PC)

#define LED PIN_F0

main()
{
    long value=0;

   output_high(LED);

   while(1)
   {
      // setup to do A/D conversion on AN5
        setup_adc_ports(AN0);
        setup_adc(ADC_CLOCK_DIV_8);
        set_adc_channel(0);

      delay_us(20);
        value = read_adc();
      setup_adc(ADC_OFF);

        fprintf(PC, "%ld\r\n", value);
   }

} // end of main
newguy



Joined: 24 Jun 2004
Posts: 1907

View user's profile Send private message

PostPosted: Wed Nov 15, 2006 10:26 am     Reply with quote

You're right - you're reading the A/D too fast. Whether that will give you a value of 1023 or not, I don't know. Since you are seeing 1023 sometimes, I guess so.

When it comes to using the PIC's internal A/D, I like to use it a little differently. There is an interrupt, INT_AD, that fires when the A/D converter is done doing a conversion. I use a timer to trigger an A/D conversion at a steady, repeatable rate and when the conversion is finished, the AD interrupt lets me know it's finished. It is then safe to read the result of the A/D conversion.

That aside, your code should work just fine if you change your loop delay from 20 us to 1 ms. Also move the setup code OUT of the while loop. That stuff only needs to execute once, not continually. Also get rid of the line disabling the A/D - not needed, and may be the source of some of your troubles.

Once you make these minor changes, see what kind of readings you get from the sensor. I've used similar sensors, and they output voltages down to almost 0V, so your lower bound of 700 is suspect.
Ttelmah
Guest







PostPosted: Wed Nov 15, 2006 10:31 am     Reply with quote

You certainly should not need to setup the ADC on each loop.
Code:

   setup_adc_ports(AN0);
   setup_adc(ADC_CLOCK_DIV_8);
   set_adc_channel(0);
   while(1) {
       value = read_adc();
       fprintf(PC, "%ld\r\n", value);
   }

Should work fine.
Realistically, you are going to need an external amplifier for the signal. An op-amp, set to subtract the voltage corresponding to about 700 counts, and then give *5 gain, will give you a much more worthwhile reading. You could potentially expand the range a little, by rising the VrefL pin of the PIC, but the range you have is small enough, that you are not going to get full accuracy from the PIC's ADC.

Best Wishes
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

PostPosted: Wed Nov 15, 2006 12:26 pm     Reply with quote

I tried both suggestions and neither of them seem to work...although I believe both should since my code above works?? The first listing of code (below) just moves the analog setup commands outside the while loop and all I'm getting are max values (i.e. 1023). The second listing of code attempts A/D with the interrupt, but the interrupt is never triggered (i.e. LED never lights) so I just get a value of 0. Any suggestions would be appreciated.

Code:

#include <18F8722.h>
#device adc=10
#include <stdlib.h>
#include <math.h>

#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP
#use delay(clock=10000000)
#use rs232(baud=9600, xmit=PIN_A4, rcv=PIN_A5, stream=PC)

#define LED PIN_F0

main()
{
    long value=0;

   // setup to do A/D conversion on AN5
    setup_adc_ports(AN0);
    setup_adc(ADC_CLOCK_DIV_8);
    set_adc_channel(0);

    output_high(LED);

    while(1)
   {
        delay_ms(1);
        value = read_adc();

        fprintf(PC, "%ld\r\n", value);
   }

} // end of main


Code:

#include <18F8722.h>
#device adc=10
#include <stdlib.h>
#include <math.h>

#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP
#use delay(clock=10000000)
#use rs232(baud=9600, xmit=PIN_A4, rcv=PIN_A5, stream=PC)

#define LED PIN_F0

long value=0;

#INT_AD
void adc_isr()
{
   value = read_adc();
   output_high(LED);
}


main()
{
   // setup to do A/D conversion on AN5
    setup_adc_ports(AN0);
    setup_adc(ADC_CLOCK_DIV_8);
    set_adc_channel(0);

    enable_interrupts(INT_AD);
   enable_interrupts(GLOBAL);

   while(1)
   {       
      fprintf(PC, "%ld\r\n", value);
   }

} // end of main
newguy



Joined: 24 Jun 2004
Posts: 1907

View user's profile Send private message

PostPosted: Wed Nov 15, 2006 12:33 pm     Reply with quote

Forget the interrupt for now - you have to manually trigger the A/D to start a conversion, and that's why it isn't working.

I notice that in your setup_adc_ports() function that you have AN0 as the only argument. You should have:

Code:
setup_adc_ports(AN0|VSS_VDD);


If you omit the VSS_VDD, I'm not sure what default state the A/D uses for its voltage limits. Make this small change and see if that helps.

Before I forget, also try this:

Code:
setup_adc(ADC_CLOCK_DIV_64|ADC_TAD_MUL_0);


The A/D will give strange results if the clock feeding it is too fast. This will slow it down.
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

PostPosted: Wed Nov 15, 2006 12:59 pm     Reply with quote

Still the same result. I don't know what can be going wrong? How can it work with the adc setup commands inside the while loop, but not outside?
Ttelmah
Guest







PostPosted: Wed Nov 15, 2006 1:49 pm     Reply with quote

You are using A4, as the serial transmit output, yet this pin is an open collector (pull down only) output. Has this got a pull-up resistor?. If not, the serial is 'dubious'.
Vss-Vdd for the reference, is the default. The value 'ored' in to enable this, is zero.
It is almost never worth trying to use interrupts with the ADC. The time needed to enter the interrupt, is as long as the conversion takes. You still have to trigger the conversion. The only time this is worth doing, is when the conversion is being triggered off an external source using the CTC for example.
The only thing I'd suspect, is a lack of supply decoupling, and the contents of the registers are getting corrupted when you do the serial I/O. I think you have a hardware problem like this...

Best Wishes
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

PostPosted: Wed Nov 15, 2006 2:04 pm     Reply with quote

I removed the rs232 statement and used the LED to tell me if the value is above or below a threshold...and still the same result. It doesn't work if take the adc setup statements outside the while loop. I'm stumped.

On a side note, I checked the voltage range with a multimeter and it looks like it goes from 0.6-1.2 volts. Pretty small to get any kind of resolution with the PIC I guess.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Nov 15, 2006 2:27 pm     Reply with quote

Quote:

I checked the voltage range with a multimeter and it looks like it goes
from 0.6-1.2 volts. Pretty small to get any kind of resolution with the
PIC I guess.

Do a web search for GP2Y0A02YK. There are lots of articles on this
topic (compensating for non-linear output of the sensor).
http://www.acroname.com/robotics/info/articles/sharp/sharp.html
http://www.acroname.com/robotics/info/articles/irlinear/irlinear.html
http://www.robotshop.ca/PDF/Sharp_GP2Y0A02YK_Ranger.pdf
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

PostPosted: Wed Nov 15, 2006 2:51 pm     Reply with quote

I'm familiar with the sensor, in particular it's nonlinearities. This I can deal with as I don't need an accurate distance reading. I can just use the digital value of the voltage. The voltage range I'm getting is actually 0.5-2.8 volts as spec'd out in the sensor's datasheet. If this is the case, then I should be seeing a digital value range of 102 to 573 if I'm using a 10-bit number and 26-143 if I'm using an 8-bit number, right?

voltage*(255/5) = digital value // 8-bit
voltage*(1023/5) = digital value // 10-bit

I'm definitely not seeing this range...so I guess my A/D isn't working correctly even inside the while loop.
SherpaDoug



Joined: 07 Sep 2003
Posts: 1640
Location: Cape Cod Mass USA

View user's profile Send private message

PostPosted: Thu Nov 16, 2006 7:34 am     Reply with quote

Quote:

I checked the voltage range with a multimeter and it looks like it goes
from 0.6-1.2 volts. Pretty small to get any kind of resolution with the
PIC I guess.


The range of the sensor is 0.6V which is about 1/8 of the A/D range of 5V. 1/8 scale means you lose 3 bits.
_________________
The search for better is endless. Instead simply find very good and get the job done.
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Thu Nov 16, 2006 7:34 am     Reply with quote

The only real difference between your first code and RJ's is the setup_adc(ADC_OFF) command. You shouldn't need it but try this out:

Code:
#include <18F8722.h>
#device adc=10
#include <stdlib.h>
#include <math.h>

#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP
#use delay(clock=10000000)
#use rs232(baud=9600, xmit=PIN_A4, rcv=PIN_A5, stream=PC)

#define LED PIN_F0

main()
{
    long value=0;

   // setup to do A/D conversion on AN5
    setup_adc(ADC_OFF);
    setup_adc_ports(AN0);
    setup_adc(ADC_CLOCK_DIV_8);
    set_adc_channel(0);

    output_high(LED);

    while(1)
   {
        delay_ms(1);
        value = read_adc();

        fprintf(PC, "%ld\r\n", value);
   }

} // end of main
Ttelmah
Guest







PostPosted: Thu Nov 16, 2006 8:22 am     Reply with quote

The 'ADC_OFF' call, is why he needs to have the setup in the loop. Once the ADC is switched off, it is the 'setup_adc' command, with a clock value, which turns it back on.
I have just tried just about all the code versions, and most work. Seriously, two suggestions still exist. First, there is a hardware problem, and the second, is the question 'what compiler version'. In the past, there have been compiler versions that incorrectly initialise some peripherals, and though I cannot think of a misconfiguration that wuld behave like this, it should be checked.
The other question, regarding the value being returned, is 'what is the output impedance of the device'?. The PIC ADC, requires a fairly low impedance drive, to give reasonable values. Sharp give no figure for this parameter. If the impedance is above about 2.5KR, then a buffer amplifier is needed, and might as well be used to give better use of the ADC range as well. For instance, if the signal was multiplied *4, and VrefL, fed from 2v, then the signal would be 2.4 to 4.8v, and the PIC would read this as 136 to 956.

Best Wishes
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Thu Nov 16, 2006 8:25 am     Reply with quote

My point is that if we believe that his original code works (most of the time) then he should get the same results with the code that you posted. I suggested the adding setup_adc(ADC_OFF) in case the compiler was not setting something up correctly and this made it work. It is really the only difference.
Ttelmah
Guest







PostPosted: Thu Nov 16, 2006 8:31 am     Reply with quote

Yes.
The other 'glaring' difference though, is that he keps re-initialising the ADC, after performing the printf, or LED toggle which then raises back the possibility that this is a supply decoupling issue. There have been quite a few people with 'inexplicable oddities', where it turned out the supply rail was noisy. On the 8722, there are a total of ten power pins (5*Vss, and 5*Vdd), that must all be connected, and properly decoupled, or this sort of problem can appear. Something silly, like Avss not connected, can give exactly this type of behaviour.

Best Wishes
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