I'm trying to read an analog input with my PIC18F66K22 using ADC. But the supposingly 10-bit value often comes to values like 63.241. I found out that a negative value (printing as unsigned) results in the same behaviour.

I believe there might be something wrong with my ADC settings. My PIC is clocked at default (8Mhz). The range of ADC should be 0v to 2,5v but it doesn't have to be that accurate.

I can't seem to find the right ADC settings I believe. I'm using XC8 compiler with the adc.h library that was with it in MPLAB X IDE.

Below is the code (I made a new project tot test, with simplified code.) First the adc.c, I believe the settings are wrong and an error in the reading should be easy to see.

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

void adcSetup()
   OpenADC (
           ADC_FOSC_8          &
           ADC_RIGHT_JUST       &
           ADC_CH4              &
           ADC_INT_OFF          &
           ADC_REF_VDD_VDD      &

//TODO ADC with interrupts

unsigned int adcGet(void)
   return ReadADC();

also my main.c, the other xport files are not that interesting and seem to work well. I'm not that sure of my conversion from uint to 'c-string' in the function void buttonHandle(void)

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <p18f66k22.h>
#include <delays.h>
#include <usart.h>

#include "defines.h"
#include "configuration-bits.h"
#include "xport.h"
#include "adc.h"

#define __delay_us(x) _delay((unsigned long)((x)*(8000000/4000000UL)))
#define __delay_ms(x) _delay((unsigned long)((x)*(8000000/4000UL)))

volatile char RxBuf[] = "                                        ";
volatile int RxI = 0;
volatile bool RxMessage = false;
volatile bool ButtonPressed = false;

void SetupRegisters(void);
void SetupInterrupts(void);
void interrupt HighISR(void);
void interrupt low_priority LowISR(void);
void delay_ms(unsigned int x);
void buttonHandle(void);
void messageHandle(void);

void SetupInterrupts(void)
    INTCONbits.GIE  = 1;                //Global interrupt enable
    RCONbits.IPEN   = 1;                //Enable priority interrupts
    INTCONbits.GIEH = 1;                //Global interrupt enable High
    INTCONbits.GIEL = 1;                //Global interrupt enable Low
    INTCONbits.PEIE = 1;                //Peripheral Interrupt Enable bit
    INTCONbits.PEIE_GIEL = 1;           //Peripheral Interrupt Enable?

    INTCON3 =   0b00000000;             //Clear intcon. INT1 and INT2 are now low priority
    INTCON3bits.INT1E = 1;              //Enable int1 (BUTTON)

void SetupRegisters(void)
    TRISA = 0b10101110;     //7:RFID en2 6:x 5:POWER_LEVEL(analog) 4:PWRKEY 3:SW_CHRG 2:SW_FAULT 1:EXT_INP
    TRISBbits.TRISB1 = 1;   //BUTTON INPUT
    TRISEbits.TRISE3 = 0;   //XPort RESET
    TRISGbits.TRISG3 = 1;   //LDO pwrgd (input)
    TRISGbits.TRISG4 = 0;   //LDO shdn  (ldo to toggle xport)

/* Main */
int main() {
    //---Set up Registers/interrupts of PIC---
    //See defines.h for al macros for LED_IN and other pin-renames.
    SetupRegisters();       //Registers...
    SetupInterrupts();      //Interrupts (button/uart)
    adcSetup();             //ADC for power-detection (POWER-LEVEL)

    //---Set up peripherals---
    xportSetup();           //Using xport as debugging help.
    xportEnable();          //Switch ldo to enable it.

        }else if(ButtonPressed){
            ButtonPressed = false;
   return 0;

void interrupt high_priority HighIsr(void)    //High priority interrupt
        RxBuf[RxI] = RCREG2;
        if(RxBuf[RxI] == ';'){//TODO or full
            RxMessage = true;
        xportSendText("High - unhandled interrupt");

void interrupt low_priority LowIsr(void)    //Low priority interrupt
    if(INT1IF){                 //Button interrupt
        ButtonPressed = true;   //Set flag (handled in main)
        INT1IF = false;         //clear interrupt flag afterwards  to avoid hardware bounce re-interrupt
    }else{                      //Warning for unhandled interrupt
        xportSendText("[ERROR] Low - unhandled interrupt!");

void delay_ms(unsigned int xc)
    while(xc  > 0);

void buttonHandle(void){
    xportSendText("Button pressed");

    char buffer[] = "                   ";
    sprintf (buffer, "ADC: %u", adcGet());

    //RX DEBUG
    xportSendText("RxBuf: ");

    //XPORT debug

void messageHandle(void){
    xportSendText(RxBuf);                                           //Send/Handle the buffer
    strcpy(RxBuf, "                                        ");      //Empty the buffer!
    RxI = 0;                                                        //Start buffer at pos 0 again;
    RxMessage = false;                                              //Reset the flag

I'm basically receiving uart text in an interrupt. And printing the debug/adc when an button flag has been set (by the button interrupt)

This is my output after pressing the button some times: enter image description here As you can see, the adc is not very persistent. Though I had it hooked on to the power supply (which shouldn't (and doesn't) drop this much).

  • 1
    \$\begingroup\$ Don't quote me on this (it has been several years since I last worked on a pic), but I recall something about needing to change the pin from a GPIO pin to a ADC pin with some sort of register (or maybe it was the other way around). I wish I could dig up some old ADC code to help, but I cant find any. Sorry! \$\endgroup\$
    – Reid
    Mar 6, 2015 at 17:13
  • \$\begingroup\$ You're right on that though ENABLE_AN4_ANA(); should do that trick. But yea, could really be one small thing like that. \$\endgroup\$
    – Paul
    Mar 6, 2015 at 17:42
  • \$\begingroup\$ I noticed that your adc.c #includes adc.h twice. That's probably not the problem - header files should have guards around them to handle multiple #inclusions - but I don't think it was intended either. \$\endgroup\$
    – AaronD
    Mar 6, 2015 at 17:59
  • \$\begingroup\$ Well there is a <adc.h> library which came with the compiler/studio. But I also made an "adc.h" with "adc.c". I agree that the naming is somehow inconvenient, but it should work neverthless? \$\endgroup\$
    – Paul
    Mar 8, 2015 at 0:54

2 Answers 2


I know the examples and libraries exist to try and save work, but I often find it helpful to not use them at first while I verify that my understanding and the datasheet are correct and that the chip isn't broken. (Usually, the problem is my understanding.)

So, without using the library:

  • Set all the ADC and port registers to what they're supposed to be per the datasheet according to what you want to do
  • Make sure that works
  • Then debug the library

You'll probably find that the library is correct and you're missing something that becomes obvious by that time, but every once in a while, there's a library that's wrong or at least poorly written/documented.

  • 1
    \$\begingroup\$ AaronD, @FuaZe: You guys are right, my answer was incorrect. I should make sure I'm fully awake before answering questions :) I've deleted my answer. \$\endgroup\$
    – bitsmack
    Mar 6, 2015 at 18:19
  • \$\begingroup\$ No point! Any help is welcome! \$\endgroup\$
    – Paul
    Mar 8, 2015 at 0:55

I still don't really get why it didn't work in the first place... Might have been the way I printed it. Might have been the library.

Though I did it by setting the registers manually, this works (better) for me.

static unsigned int result = 0;
ANCON0bits.ANSEL4 = 1;      //AN4 to analog channel.
ADCON2 = 0b10111110;        //Select channel
ADCON1 = 0b00110000;
ADCON0 = 0b00010011;        //Start AN4 conversion

while(ADCON0 & 2){}         //ADC busy

result = ADRESH;
result = result<<8;
result = result | ADRESL;
ADCON0 &= 0b11111110;       // Turn converter off
return (result/85);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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