PIC18f45k20 ADC – always returns zero

adcpic

I'm trying to read a value from a PIC18f45k20 ADC but I always get a zero. It has to be my config that is wrong but I can't see where. I've checked the data sheet and googled (lots of conflicting info though…).

Details

  • PIC18f45k20
  • 16 Mhz crystal, PLL=4 so 64Mhz
  • Input on AN11/RB4
  • XC8
  • Reading input from a LDR acting as a voltage divider from +3.3V. (I've confirmed with a multi-meter that this generates voltages around 1.5v-3v depending on light levels)

What I've tried to ensure is

  1. TRISB for RB4 is input
  2. ANSEL for ANS11 is analogue
  3. Channel 11 is selected
  4. Using Vdd and Vss as references rather than external refs
  5. Appropriate TAD and speed – I hope this is correct. Fosc/64, 20 TAD

I've checked the ADC checklist (data sheet 19.2.9) and as far as I can see everything is correct. I've also tried without using the XC8 ADC.h functions (i.e. just setting the bits) and I get exactly the same result.

Any ideas?

///////////////////////////////////////////////////////////////////////////////
#pragma config PBADEN = OFF     // PORTB A/D Enable bit (PORTB<4:0> pins are configured as digital I/O on Reset)
#pragma config LPT1OSC = OFF    // Low-Power Timer1 Oscillator Enable bit (Timer1 configured for higher power operation)
//more setting here...//////////////////////////////////////////////////////////////////////////////

#define _XTAL_FREQ  64000000
#define _CLOCK_FREQ 16000000 // xtal / 4

int main(int argc, char** argv)
{
    TRISA = 0b00000000;
    TRISB = 0b00010000;
    TRISC = 0b00000000;
    CM2CON0 = 0;
    CM2CON1 = 0;
    CVRCON = 0x0;

    ADCON0bits.CHS = 11;
    ANSELHbits.ANS11 = 1;

    ADCON1 = 0b00000000; // 00 = unimplemented, 0 = V- is Vss, 0 = V+ is Vdd, 0000 = unimplemented
    ADCON2 = 0b00111110; // 0 = left justified, 0 = unimplemented, 111 = 20 TAD, 110 = Fosc/64
    ADCON0 = 0b00101101; // 00 = unimplemented, 1011 = AN11, 0 = GO/DONE, 1 = ADON

    while(1)
    {
        ConvertADC();
        while(BusyADC())
        {
        }

        int r = ReadADC();       

        delay(500);
    }

    return (EXIT_SUCCESS);
}

void delay( int ms )
{
    while( ms > 0 )
    {
        __delay_ms(1);
        --ms;
    }
}

EDIT- 2014/06/14: I can get the ADC to work on a PIC18f4550 without much trouble. The code is equivalent but I still cant get the 45k20 working.

ADC on AN0/RA0. Based on the example from http://www.pic18f.com/uncategorized/2010/07/06/tutorial-5-ad-conversion-in-c/

TRISA = 0b00000001;
TRISD = 0b00000000;

ADCON1 = 0b00001110;//VSS,VDD ref. AN0 analog only
ADCON0 = 0x00;//clear ADCON0 to select channel 0 (AN0)
ADCON2 = 0b00001000;//ADCON2 setup: Left justified, Tacq=2Tad, Tad=2*Tosc (or Fosc/2)
ADCON0bits.ADON = 0x01;//Enable A/D module

LATDbits.LATD0 = 1;

while(1)
{
    ADCON0bits.GO_DONE = 1;//Start A/D Conversion

    while(ADCON0bits.GO_DONE != 0);//Loop here until A/D conversion completes

    unsigned short r = ADRES;
    LATDbits.LD0 = r == last ? 0 : 1;
    last = r;

    delay(100);
}

Best Answer

I don't like "fix my code" too :) I will not analyse your code, I'm not PIC expert, I just started with Microchip microcontrollers - they are awesome! :)

Edited:

So this is how I do it on PIC18F4550 PIC18F25k50 and it works for sure. It should work on PIC18F4520 too.

End of edited.

You have to change port and probably voltage reference configuration.

volatile unsigned int millivolts = 0; // variable for result

void config_adc(void)
{
    // voltage reference selection

    // VREFCONbits.FVRS
    // 0b00 off
    // 0b01 1024mV
    // 0b10 2048mV
    // 0b11 4096mV
    VREFCON0bits.FVRS = 0b01;
    VREFCON0bits.FVREN = 1; // internal fixed voltage reference turned on

    ANSELAbits.ANSA0 = 1; // analog input A0 on
    TRISAbits.TRISA0 = 1; // pin A0 as input

    ADCON0bits.CHS = 0;   // analog channel select

    ADCON1bits.PVCFG = 0b10; // 0b10 for positive voltage reference = FVR BUF2 ("FVRS" configured earlier)
    ADCON1bits.NVCFG = 0b00; // 0b00 for negative voltage reference = AVss

    ADCON2bits.ADCS = 0b110; // 0b011 use FRC clock (internal clock dedicated for ADC)
    ADCON2bits.ACQT = 0b000; // manual acquisition mode
    ADCON2bits.ADFM = 1; // result bits adjusted to right

    ADCON0bits.ADON = 1; // ADC converter on
}

void main()
{
    config_adc();
    while(1)
    {

        ADCON0bits.GODONE = 1; // start conversion

        while (ADCON0bits.GODONE) // wait until conversion is done
        {
            asm("nop"); // im not sure if this is necessary
        }

    millivolts = (ADRESH * 256 + ADRESL); // im writing result into volatile variable to use it in interrupt routine

    }
}

Im using XC8 too. Code is not complete, this is just ADC part.