A2D not working on PIC16f

Discussion in 'Embedded Systems and Microcontrollers' started by rocky79, Nov 12, 2013.

  1. rocky79

    Thread Starter New Member

    Apr 23, 2010
    6
    0
    Hello All,

    I am having trouble getting the A2D to work on PIC16f1939. I am using CCS compiler 5.007.
    The CCS comes with the A2D function that works fine but when I follow the manual method( more control) I couldn't get it work.
    Can anyone point out what I am doing wrong? your help is appreciated it.
    Thank you!

    Code ( (Unknown Language)):
    1.  
    2. #include <16F1939.h>
    3. #device adc=10
    4. #fuses   INTRC_IO, NOWDT, NOPROTECT, NOMCLR,VCAP_A6
    5. #use     delay(clock = 32000000)//16000000)//8000000)
    6. #include "flex_lcd.c"
    7. #use     i2c(master, sda=PIN_C4, scl=PIN_C3)
    8. #include <stdlib.h>
    9. #use     fast_io (B)
    10.  
    11. #byte GPIOB = 0xd
    12. #bit  GPIOB_1 = GPIOB.1
    13.  
    14. #byte TRISIOB  = 0x08D
    15. #bit  TRISIOB_1=  TRISIOB.1
    16.  
    17. #byte ANSELB = 0x18D
    18. #bit  ANSELB_1= ANSELB.1
    19.  
    20. #byte ADCON0=0x9d
    21. #bit  ADCON0_0=ADCON0.0
    22. #bit  ADCON0_1=ADCON0.1
    23. #bit  ADCON0_2=ADCON0.2
    24. #bit  ADCON0_3=ADCON0.3
    25. #bit  ADCON0_4=ADCON0.4
    26. #bit  ADCON0_5=ADCON0.5
    27. #bit  ADCON0_6=ADCON0.6
    28.  
    29. #byte ADCON1=0x9e
    30. #bit  ADCON1_7=ADCON0.7
    31. #bit  ADCON1_6=ADCON0.6
    32. #bit  ADCON1_5=ADCON0.5
    33. #bit  ADCON1_4=ADCON0.4
    34.  
    35. //PERIPHERAL INTERRUPT ENABLE REGISTER 1
    36. #byte PIE1= 0x91
    37. #bit  PIE1_6=PIE1.6
    38.  
    39. #byte ADRESH=0x9c
    40. #byte ADRESL=0x9B
    41.  
    42. //FUNCTION PROTOTYPE
    43. void  adc_init(void);
    44. int16 sample(void);
    45.  
    46. void  main()
    47. {  
    48.    int16 adc=0;
    49.  
    50.    lcd_init();
    51.    adc_init();
    52.  
    53.    While(1)
    54.    {
    55.     adc=sample();
    56.     delay_ms(10);
    57.     printf(lcd_putc,"\f%s%5lu","ADC:",adc);
    58.    }
    59. }
    60.  
    61. void adc_init(void)
    62. {
    63.  
    64.    //CONFIGURE PORT RB1/AN10 AS DIGITAL:
    65.      TRISIOB_1=1; //CONFIGURE PIN RB1 AS INPUT.
    66.  
    67.    //CONFIGURE PIN AS ANALOG
    68.       ANSELB_1=1;  // SET UP PIN RB1 AS ANALOG.
    69.  
    70.    //Select ADC conversion clock Fosc/64
    71.       ADCON1_6=1;
    72.       ADCON1_5=1;
    73.       ADCON1_4=0;
    74.  
    75.    //Select Analog Channel 10 (AN10)
    76.    //ADCON0 |=01010000;    
    77.       ADCON0_6=0;
    78.       ADCON0_5=1;
    79.       ADCON0_4=0;
    80.       ADCON0_3=1;
    81.       ADCON0_2=0;
    82.  
    83.    //10 bit results should be right justified
    84.     ADCON1_7=1;
    85. }
    86.  
    87. int16 sample(void)
    88. {
    89.    // variable declaration
    90.    int high;
    91.    int low;
    92.    int16 adc;
    93.  
    94.    //Disables the ADC interrupt
    95.    PIE1_6=0;
    96.    //Turn on the A2D converter
    97.    ADCON0_0=1;
    98.    //start the conversion (Go)
    99.    ADCON0_1=1;
    100.    //wait for the acquisition time
    101.    delay_us(60);
    102.  
    103.    //when ADCON0_1=1 the A2D conversion is not done
    104.    while(ADCON0_1==1)
    105.    //wait till the conversion is finished
    106.    //Read the value of ADRESH and ADRESL
    107.    high= ADRESH;
    108.    low=  ADRESL;
    109.  
    110.    //store the resulting A2D conversion
    111.    //high and low byte into one 16 bit variable
    112.    adc=make16(high,low);
    113.  
    114.    return(adc);
    115. }
    116.  
    117.  
     
  2. JohnInTX

    Moderator

    Jun 26, 2012
    2,338
    1,018
    What's not working?

    The main thing that jumps out is that you turn on the ADC then start a conversion immediately. There is an aperature (settling) time associated with channel changes and between turning on the ADC and starting the conversion. You have a 60us delay after GO=1 but that doesn't do anything. It should be between turning the ADC on and starting the first conversion.

    You can turn the ADC on and leave it on. Change channels by jamming the entire byte (with the other bits constant but written too) onto the control register.

    I think your initialization is OK but its better, IMHO, to init each register as a byte to avoid missing bits that may affect operation.

    If you have included the PIC header .h file, why do you need to define the registers? That should be done by the header. Your addressing appears correct but you may be missing some qualifiers (volatile?) that the compiler needs to correctly generate code for the IO operations. (but I don't use CCS so..)

    One thing you might do is look at the assembler output for the CCS library code and see what they do.. That might be instructive.

    A few other things to look at:
    #device adc=10 does what? I ask because you later declare int16 adc. Same name.
    The printf calls for a long int %5lu but adc is a regular int. Try %5u.
    You don't need delay(10) after adc_sample();
    The function make16(high,low) is not shown. Presumably it works?



    Have fun.
     
    Last edited: Nov 12, 2013
  3. rocky79

    Thread Starter New Member

    Apr 23, 2010
    6
    0
    Thank you for your reply. I have followed your suggestions and I am still getting the result of the A2d as 65472 counts. I should be getting around 500 counts. Any other suggestions? Thanks again

    I added a delay right after turning on the ADC ( 60us)
    adc=10 is a predefined variable in the compiler. It tells the compiler we are using a 10 bit instead of 8bit mode.I found that this is not needed, so I removed it.

    I am used to using different terminology for pins but you're right they should be in the header file.
    the results of the adc is 10 bits. The variable that houses a 10 bit is a long variable. %5lu tells the printf that I am using a long variable with 5 digits.
     
  4. JohnInTX

    Moderator

    Jun 26, 2012
    2,338
    1,018
    If ADCON1:ADFM =1 (right justified 10 bit result) you can't get 65472 (FFC0h) as a conversion result. It has to be 1023 (03FFh) or less. Some observations:

    FFC0h is consistent with a railed ADC (reading full scale) with a LEFT-justified result. Two problems here:
    1)why is it railed, Vref selection? What is the input voltage? and
    2)why is it left-justified? I would check that out first.

    Second, I would look hard at the routine that combines ADRESH/L to a 16 bit unsigned integer. It should look something like this:
    adc_result = (unsigned int)ADRESH; // get it to lower 8 bits of int
    adc_result = adc_result << 8; // shift it up
    adc_result = adc_result + (unsigned int)ADRESL; // combine

    As I indicated, I don't like individual bit setting in control registers. Its probably OK here but consider that the PIC has to read the whole register, flip a bit then write it back. Not all registers are fully read/write in all bits (although it looks like ADCON1 is). Its just my preference but it makes it easier to know what you have set up and less likely to overlook Vref selection. I always initialize everything, even if its the same as power up defaults.. Just because..

    Consider reading/displaying ADRESH/L individually to see what the ADC is generating. That may shed some light on what's going on. Hopefully, you have a PICkit or some other debugger that you can step the code. If not, run it under MPSIM in MPLAB. Set a breakpoint after the ADC is done, poke in expected values to ADRESH/L then see if your other conversion stuff is working. Also break just before doing a conversion and inspect all of the IO settings - pin set to analog etc. Don't forget that Vref pins have to be analog as well if using external Vref.

    As a hardware test, left-justify the result and just read ADRESH. It should run from 0-255 from 0-full scale and eliminates any possible issue with combining ADRESH/L for now.

    Good luck!
     
    Last edited: Nov 13, 2013
  5. rocky79

    Thread Starter New Member

    Apr 23, 2010
    6
    0
    It's working now, thanks again. It turnes out that eventhough the Vref- and Vref+ in ADCON1 are setup by default to '0' once I explicitly made them '0' I started getting the right result.
    I never thought this step was necessary but it turned out it is.

    Thanks again for the help and the tips
     
  6. JohnInTX

    Moderator

    Jun 26, 2012
    2,338
    1,018
    Well there you go!
    Glad its working.
     
Loading...