1
\$\begingroup\$

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);
}
\$\endgroup\$
  • \$\begingroup\$ PS I don't like posting, "fix my code" questions but I'm really stuck with this one. I've tried on two different PICs (PIC18f45k20 and a PIC18f4550) and I get it wrong on both. There are lots of questions on the net about similar issues but the answers have not helped me in any way. Many of the answers seem to contradict the data sheet too... So I've investigated as much as I can but I am now at a loss. Thanks \$\endgroup\$ – Andre Jun 13 '14 at 11:32
  • 1
    \$\begingroup\$ Quick random idea if you don't get any ideas on the software side is to double-check both pairs Vss / Vdd pins are connected if you haven't checked that already. \$\endgroup\$ – PeterJ Jun 13 '14 at 12:07
  • \$\begingroup\$ @PeterJ: I don't have the board here but I'll definitely do that. Thanks. 99% sure they are but I'll check \$\endgroup\$ – Andre Jun 13 '14 at 12:13
  • \$\begingroup\$ You haven't posted the code for ConvertADC(), BusyADC(), ReadADC() functions? Also, does RB4 have the PWM function multiplexed on it? Perhaps you could try turning it OFF. Section 16 of the datasheet? \$\endgroup\$ – Sohail Jun 13 '14 at 12:19
  • \$\begingroup\$ @captainsomewhere ConvertADC() etc are all from microchip's ADC.h. Very thin abstractions over the bit sets. I assumed that was the more accepted method, but I'll be going back to direct bit manipulation as its much clearer IMO as its closer to the datasheet. PWM... That is a good idea. I'll test that thanks \$\endgroup\$ – Andre Jun 13 '14 at 12:22
1
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ I'll test that later, thanks. What I'm not comfortable with is that these other items are not mentioned in the datasheet for the ADC from what I remember. If it is this then IMO the documentation is misleading. There is even an assembler example in the pic18f45k20 datasheet and it does not seem to set these either. Again I'll definitely try this and will then accept if it works. \$\endgroup\$ – Andre Jun 13 '14 at 12:17
  • 1
    \$\begingroup\$ Documentation is misleading quite often... Usually to have a working peripheral you must refer to two if not three different documents. \$\endgroup\$ – Vladimir Cravero Jun 13 '14 at 12:22
  • \$\begingroup\$ @VladimirCravero I'm new to electronics and that does seem to be what I'm finding. The issue I've had is finding a reliable second document. I've also been surprised by the lack of (easily discoverable) minimal working code samples. I suppose that I'll start learning where to look for these as I spend more time doing this. Hopefully :) \$\endgroup\$ – Andre Jun 13 '14 at 12:30
  • \$\begingroup\$ Yeah that's painful sometimes, I hope I'll become expert enough to have working things at first attempt, I am still (hard) working on that. \$\endgroup\$ – Vladimir Cravero Jun 13 '14 at 12:31
  • \$\begingroup\$ @Andre I wrote this code by reading documentation from Microchip and nothing else. Informations were distributed over few pages of datasheet, you probably missed something. You have to read documentation (more?) carefully and everything will be fine :) \$\endgroup\$ – Kamil Jun 13 '14 at 13:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.