A2D not working on PIC16f

Thread Starter

rocky79

Joined Apr 23, 2010
6
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!

Rich (BB code):
#include <16F1939.h>
#device adc=10 
#fuses   INTRC_IO, NOWDT, NOPROTECT, NOMCLR,VCAP_A6
#use     delay(clock = 32000000)//16000000)//8000000)
#include "flex_lcd.c"
#use     i2c(master, sda=PIN_C4, scl=PIN_C3)
#include <stdlib.h>
#use     fast_io (B)
 
#byte GPIOB = 0xd 
#bit  GPIOB_1 = GPIOB.1
 
#byte TRISIOB  = 0x08D
#bit  TRISIOB_1=  TRISIOB.1
 
#byte ANSELB = 0x18D
#bit  ANSELB_1= ANSELB.1
 
#byte ADCON0=0x9d
#bit  ADCON0_0=ADCON0.0
#bit  ADCON0_1=ADCON0.1
#bit  ADCON0_2=ADCON0.2
#bit  ADCON0_3=ADCON0.3
#bit  ADCON0_4=ADCON0.4
#bit  ADCON0_5=ADCON0.5
#bit  ADCON0_6=ADCON0.6
 
#byte ADCON1=0x9e
#bit  ADCON1_7=ADCON0.7
#bit  ADCON1_6=ADCON0.6
#bit  ADCON1_5=ADCON0.5
#bit  ADCON1_4=ADCON0.4
 
//PERIPHERAL INTERRUPT ENABLE REGISTER 1
#byte PIE1= 0x91
#bit  PIE1_6=PIE1.6
 
#byte ADRESH=0x9c
#byte ADRESL=0x9B
 
//FUNCTION PROTOTYPE
void  adc_init(void);
int16 sample(void);
 
void  main()
{  
   int16 adc=0;
 
   lcd_init();
   adc_init();
 
   While(1)
   {
    adc=sample();
    delay_ms(10);
    printf(lcd_putc,"\f%s%5lu","ADC:",adc);
   }
}
 
void adc_init(void)
{
 
   //CONFIGURE PORT RB1/AN10 AS DIGITAL:
     TRISIOB_1=1; //CONFIGURE PIN RB1 AS INPUT.
 
   //CONFIGURE PIN AS ANALOG
      ANSELB_1=1;  // SET UP PIN RB1 AS ANALOG.
 
   //Select ADC conversion clock Fosc/64
      ADCON1_6=1;
      ADCON1_5=1;
      ADCON1_4=0;
 
   //Select Analog Channel 10 (AN10)
   //ADCON0 |=01010000;    
      ADCON0_6=0;
      ADCON0_5=1;
      ADCON0_4=0;
      ADCON0_3=1;
      ADCON0_2=0;
 
   //10 bit results should be right justified
    ADCON1_7=1;
}
 
int16 sample(void)
{
   // variable declaration
   int high;
   int low;
   int16 adc;
 
   //Disables the ADC interrupt
   PIE1_6=0;
   //Turn on the A2D converter
   ADCON0_0=1;
   //start the conversion (Go)
   ADCON0_1=1;
   //wait for the acquisition time
   delay_us(60);
 
   //when ADCON0_1=1 the A2D conversion is not done
   while(ADCON0_1==1)
   //wait till the conversion is finished
   //Read the value of ADRESH and ADRESL
   high= ADRESH;
   low=  ADRESL;
 
   //store the resulting A2D conversion
   //high and low byte into one 16 bit variable
   adc=make16(high,low);
 
   return(adc);
}
 

JohnInTX

Joined Jun 26, 2012
4,787
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:

Thread Starter

rocky79

Joined Apr 23, 2010
6
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.
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.
 

JohnInTX

Joined Jun 26, 2012
4,787
I am still getting the result of the A2d as 65472 counts. I should be getting around 500 counts. Any other suggestions?
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:

Thread Starter

rocky79

Joined Apr 23, 2010
6
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
 
Top