Question about adc's and error

Discussion in 'Embedded Systems and Microcontrollers' started by hunterage2000, May 25, 2015.

  1. hunterage2000

    Thread Starter Active Member

    May 2, 2010
    400
    0
    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 (Text):
    1. for(;;)
    2.     {
    3.         __delay_us(10);
    4.         ADCON0bits.GO = 1;
    5.         while(ADCON0bits.nDONE);
    6.  
    7.         int value =  ADRESH;
    8.         value = (value << 2) + (ADRESL >> 6);
    9.  
    10.         //0 - 171
    11.         if (value >= 0 && value <= 171)
    12.         {
    13.             no_zero();
    14.         }
    15.  
    16.         //172 - 342
    17.         if (value >= 172 && value <= 342)
    18.         {
    19.              no_one();
    20.         }
    21.  
    22.         //343 - 513
    23.         if (value >= 343 && value <= 513)
    24.         {
    25.             no_two();
    26.         }
    27.    
    28.          //514 - 684
    29.          if (value >= 514 && value <= 684)
    30.         {
    31.             no_three();
    32.         }
    33.  
    34.         //685 - 855
    35.         if (value >= 685 && value <= 855)
    36.         {
    37.             no_four();
    38.         }
    39.  
    40.         //856 - 1023
    41.         if (value >= 856 && value <= 1023)
    42.         {
    43.             no_five();
    44.         }
    45. }
    46. }
     
  2. MikeML

    AAC Fanatic!

    Oct 2, 2009
    5,450
    1,066
    What is the pot value?
    From what is the top end of the pot powered?
    What is the ADC reference voltage?
     
  3. hunterage2000

    Thread Starter Active Member

    May 2, 2010
    400
    0
    The pot value is 25k and powered by 5V Vdd which is also the adc ref voltage.
     
  4. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    "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 (Text):
    1.         int value;
    2.         value =  ADRESH;
    3.         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.
     
  5. MikeML

    AAC Fanatic!

    Oct 2, 2009
    5,450
    1,066
    With the pot near center, the 25K pot doesn't meet the <=10K ADC input source impedance requirement, although that is not causing your unexpected result...
     
  6. hunterage2000

    Thread Starter Active Member

    May 2, 2010
    400
    0
    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.
     
  7. JohnInTX

    Moderator

    Jun 26, 2012
    2,339
    1,019
    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: May 25, 2015
  8. hunterage2000

    Thread Starter Active Member

    May 2, 2010
    400
    0
    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 (Text):
    1. int value;
    2.         value =  ADRESH;
    3.         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?
     
  9. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    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.
     
  10. djsfantasi

    AAC Fanatic!

    Apr 11, 2010
    2,790
    827
    I think that value is decimal 256. In case the value of 254 caused any confusion.
     
Loading...