Electronic – Reading analog input pic18

adcanalogmicrocontrollerpic

I'm trying to read an analog input on a PIC18F65J11, and I'm having trouble. I don't think the pin is set up correctly because I'm getting a 0 for the value I read in, and the input is measured to be about 1V (but can get up to 3V). I want to use Here is my code:

#include "p18f65j11.h"
#include <adc.h>

/**CONFIGURATION **************************************************/
/* Device Configuration */
#pragma config XINST = OFF  // Turn off extended CPU instructions
#pragma config FOSC = HSPLL // HS oscillator will PLL that multiplies oscillator frequency by 4 (system clock is running at 16 MHz)
#pragma config FOSC2 = ON   // System clock selected by FOSC1:FOSC0 (used #config to set FOSC to HSPLL) 
#pragma config DEBUG = ON   // Background debugger enabled; RB6 and RB7 are dedicated to In-Circuit Debug.
#pragma config WDTEN = OFF  // Disable watchdog timer.
#pragma config WDTPS = 8    // Watchdog timer poscaler select bits is 1:8 (not important b/c watchdog timer disabled).
#pragma config STVREN = OFF // Reset on stack overflow/underflow disabled.
#pragma config CP0 = OFF    // Program memory is not code-protected
#pragma config IESO = ON   // Oscillator Switchover mode disabled.
#pragma config FCMEN = OFF  // Fail-Safe Clock Monitor disabled.
#pragma config CCP2MX = DEFAULT // ECCP2/P2A multiplexed with RC1 instead of RE7 pin (doesn't matter b/c not using ECCP, which is pwm output)

int myVoltage;

void main(void) 
{
    myVoltage = 0;

    // Configure clock registers (FOSC is set to HSPLL)
    OSCCONbits.SCS1 = 0;    // SCS1:SCS0 = 00 means use primary
    OSCCONbits.SCS0 = 0;    // clock source (CPU divider output) if FOSC2 is on (FOSC2 is indeed on from the #pragma config above)
    OSCCONbits.IRCF2 = 1;   // IRCF2:IRCF0 = 110 means the internal oscillator is set to 4 MHz
    OSCCONbits.IRCF1 = 1;
    OSCCONbits.IRCF0 = 1;
    OSCCONbits.IDLEN = 0;   // on SLEEP instruction, device enters sleep mode instead of idle mode
    OSCTUNEbits.PLLEN = 1;  // enable PLL (4x multiplier), so system clock is 4 * 4 MHz = 16 MHz
    // Basically, I've just set it to use primary oscillator, told it the oscillator is 
    // 4 MHz, and told it to enable PLL so that my system clock is 16 MHz.

    // Initialize Port A
    TRISA &= 0;             // set Port A as outputs
    LATA &= 0;              // all pins outputting 0
    TRISAbits.RA0 = 1;      // set pin AN0 as input to read voltage

    while(1) {
        OpenADC(ADC_FOSC_8 & ADC_RIGHT_JUST & ADC_12_TAD, ADC_CH0 & ADC_INT_OFF & ADC_VREFPLUS_VDD, 0);
        Delay10TCYx( 5 );
        ConvertADC();               // initiate conversion of sensor1 @ AN0
        while(BusyADC());           // waiting to complete conversion
        myVoltage = ReadADC();      // read the result of sensor1 @ AN0
        CloseADC();
        printf("%i volts \n\r", myVoltage);
    }
}

What am I doing wrong or missing? I suspect it's the OpenADC() part that may be wrong. I've got it using Fosc/8, result in least significant bits, 12 Tad, channel 0 (AN0 pin), interrupt off, VDD as the highest limit, and 0 so that all analog pins are analog.

Best Answer

I found out the problem was that my reference was not set correctly in the OpenADC() function. I needed ADC_REF_VDD_VSS instead of ADC_VREFPLUS_VDD. Here is the correct way to read an A/D value. This part goes inside the while(1) loop shown in the question.

// Constraints: A/D acquisition time must be >= 2.4 us (data sheet p264, Equation 20-3), conversion time (Tad) must be >= 0.7 us (data sheet p385, Table 26-26)
// Given that our Fosc = 4 MHz, which means Tosc = 1/Fosc = 1/4 MHz = 0.25 us,
// we must have at least 4 * Tosc = 1 us of conversion time (Tad).
// This is why we choose ADC_FOSC_4.
// Since Tad = 1 us, and we need a minimum acquisition time of of 2.4 us,
// we choose acquisition time to be 4 * Tad, which is ADC_4_TAD
// Use channel 0 (ADC_CH0) to read AN0
OpenADC(ADC_FOSC_4 & ADC_RIGHT_JUST & ADC_4_TAD, ADC_CH0 & ADC_INT_OFF & ADC_REF_VDD_VSS, 0);
Delay10TCYx( 5 );        // allow for time to open adc (don't know if need this much)
ConvertADC();            // initiate conversion of sensor1 @ AN0
while(BusyADC());        // waiting to complete conversion
myVoltage = ReadADC();  // read A/D value. math needs to be done to convert to voltage
CloseADC();