Question about adc's and error

Thread Starter

hunterage2000

Joined May 2, 2010
487
Hi, I have used the following code to test the adc. I have used a pot as an analog input between 0 and 5V. I am trying to output a:

0 to a 7seg display for a 0 - 1V range
1 for a 1 - 2 V range etc up to 5

The range for 0 is 0V to 0.84V, 1 is 0.85 to 1.72V, 2 is 1.73V to 2.55 etc.

I have just started using the adc and I assume the way I have coded this is the wrong way to do it. can anyone give some tips on the best way to code this?

Code:
for(;;)
    {
        __delay_us(10);
        ADCON0bits.GO = 1;
        while(ADCON0bits.nDONE);

        int value =  ADRESH;
        value = (value << 2) + (ADRESL >> 6);

        //0 - 171
        if (value >= 0 && value <= 171)
        {
            no_zero();
        }

        //172 - 342
        if (value >= 172 && value <= 342)
        {
             no_one();
        }

        //343 - 513
        if (value >= 343 && value <= 513)
        {
            no_two();
        }
   
         //514 - 684
         if (value >= 514 && value <= 684)
        {
            no_three();
        }

        //685 - 855
        if (value >= 685 && value <= 855)
        {
            no_four();
        }

        //856 - 1023
        if (value >= 856 && value <= 1023)
        {
            no_five();
        }
}
}
 

ErnieM

Joined Apr 24, 2011
8,377
"int value = ADRESH;" is a good way to read the value the very first time the loop runs, but what about all the other times it runs?

Better to read it every time the loop runs like so:
Code:
        int value;
        value =  ADRESH;
        value = (value << 2) + (ADRESL >> 6);
Also, this extraction of the value will only work when ADRES is configured for left justified readings.

Several compilers have a macro in their libraries to extract the value via a simple macro.
 

Thread Starter

hunterage2000

Joined May 2, 2010
487
I added to this and made it output in increments of 0.1V but I got this error:

:0: error: (1347) can't find 0x19 words (0x19 withtotal) for psect "text33" in class "CODE" (largest unused contiguous range 0x5)

I think the program is too large. I have about 50 if statements checking analog values between 0 and 19 per 0.1V. I'm guessing this is not the best way to do this but I'm not sure of any other way.
 

JohnInTX

Joined Jun 26, 2012
4,787
If you are testing values in ascending order, you don't need to check the lower value. For example
if (adc < 172) // value is between 0 and 171 the for the next test:
if (adc < 200) // value is between 172 and 199 (you already know its <172 from the previous test - and so on. Also, depending on the compile, testing for '<' is sometimes cheaper than testing for '<='

A large number of tests can use a lookup table. For example if you have ADC values from 0-1000 and need to do 10 things based on an evenly distributed range of values, DIV the ADC result by 100 (getting 0-9) and use that as an index into an array of pointers to function - being SURE to bounds-check the index before using it.

You don't say which PIC you are using but I don't think your ADC value shift stuff is right.. For PICs with 2 8bit result registers:

unsigned int result;
result = ADRESH*256 +ADRESL;

The 16 bit value will be left or right justified in the integer depending on how you've specified it in ADCON0:ADFM.

On the psect error, can mean that the current source file (or a big function in it) compiles to something larger than a program memory bank. Not usually a problem in 18F or enhanced midrange but for the others, if you have other memory banks you can sometime fix this problem by splitting the code into multiple source files and/or using more and smaller functions. This will allow the compiler/linker to spread things around over the available banked ROM.

Good luck.

EDIT: Rereading your original post, if all you need is a value between 0-19, (post #6) adjust the calculation to generate the number directly without all of the 'if' tests. Since you are looking for 20 values you might not need the full 10 bits - if you just scale ADRESH into 0-19 i.e. ADRESH/2o. That gets 12 counts per state, maxing out at 20*12=240. Anything >240 is the max count. The result can be displayed or used as before as an index into an array of functions as before. The array could also be a two dimensional array of 2-digit segment patterns to shove out to the display. That would save some DIV by 10, MOD 10 arithmetic to extract decimal digits from the result. Would that do?

Lots of ways to skin this cat.
 
Last edited:

Thread Starter

hunterage2000

Joined May 2, 2010
487
I am using the picf684 with the adc right justified so ADRESH uses bits 0,1 and ADRESL uses bits 7-0 for a 10 bit value.

To be honest I'm having trouble with working out:

Code:
int value;
        value =  ADRESH;
        value = (value << 2) + (ADRESL >> 6);
Say for example I have ADRESH = xxxxxx01 and ADRESL = 00000000 making a value of 0100000000 = 254

How does xxxxxx01 shift left by if the upper 6 bits are not used? Does this become xxxxxx00?
 

ErnieM

Joined Apr 24, 2011
8,377
How does xxxxxx01 shift left by if the upper 6 bits are not used? Does this become xxxxxx00?
Well it doesn't. I didn't check your code far enough. My bad. :rolleyes:

OK, so you are right justified. So the ADRESL is correct as is, but the ADRESH needs to be shifted over 8 places.

value = ADRESH << 8 + ADRESL;

That may work, depending on if the compiler promotes ADRES to an int or leaves it as an unsigned char. To be very sure try:

value = (int)ADRESH << 8 + ADRESL;

To be a little more defensive, use an unsigned int for both value and the cast of ADRESH:

unsigned int value;
value = (unsigned int)ADRESH << 8 + ADRESL;

Some time spent in the sinulator or the debugger will raise any issues you may still have.
 
Top