How to Get an Analog Input on a PIC Microcontroller

This guide hopefully will show you how to write C code that will allow you to read in an analog input (AI) value to your PIC microcontroller. I am using a PICkit 3 programmer with a PICkit 2 18-pin demo board. The microcontroller is a PIC1827, but the ideas discussed in this article will be applicable to many other PICs as well.

Step 1: Initial Requirments

Hardware Required

– PICkit programmer

– PIC microcontroller

– Some LEDs

– Some resistors (to limit the current sent to the LEDs)

– Sensor that produces an analog output (like a potentiometer)

Software Required

– MPLAB X IDE

– XC8 Compiler (this is because we are writing the program in C)

Download it here if you don’t have it already

– MPLAB Code Configurator (MCC) Plugin

Plugin Setup details found here

Step 2: MPLAB X Project Setup

We want to create a new project in the MPLAB IDE.

– Open MPLAB X IDE

– File->New Project

  1. Category: = Microchip embedded and Projects: = Standalone Project
  2. Select your device from the dropdown list (mine is a PIC16F1827 for example)
  3. Debug Header = None
  4. Select your tool (I selected PICkit 3)
  5. Select Compiler (We will be using XC8)
  6. Select a project name+file location then click finish

Assuming you are using a PICkit programmer, you then have the option to power the microcontroller from it.

  1. Right click the project and select properties
  2. Select your PICkit programmer on the left hand side
  3. Select Power from the the dropdown menu
  4. check the power circuit from PICkit x box and set the voltage level to 3.375
  5. Press ok

It’s now time to open the code configurator (MCC)

  1. Click Generate to compile the default main.c file (save the default config file then select yes)
  2. under the files tab you should now see the main.c file. Open it

Paste the following code

/**
Generated Main Source File

Company: Microchip Technology Inc.

File Name: main.c

Summary: This is the main file generated using PIC10 / PIC12 / PIC16 / PIC18 MCUs

Description: This header file provides implementations for driver APIs for all modules selected in the GUI. Generation Information : Product Revision : PIC10 / PIC12 / PIC16 / PIC18 MCUs – 1.45 Device : PIC16F1827 Driver Version : 2.00 The generated drivers are tested against the following: Compiler : XC8 1.35 MPLAB : MPLAB X 3.40 *

* (c) 2016 Microchip Technology Inc. and its subsidiaries. You may use this software and any derivatives exclusively with Microchip products.

THIS SOFTWARE IS SUPPLIED BY MICROCHIP “AS IS”. NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.

IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP’S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.

MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE TERMS. */

#include “mcc_generated_files/mcc.h”

/* Main application *

//prototypes

void initComponents(); //Setup the output pin(s)

void configAI(); //Setup the AI pin(s)

void captureAI(); //Read the AI pin

void setLEDS(uint16_t); //Set LEDs to the AI value

void main(void)

{

// initialize the device

SYSTEM_Initialize();

// When using interrupts, you need to set the Global and Peripheral Interrupt Enable bits

// Use the following macros to:

// Enable the Global Interrupts //INTERRUPT_GlobalInterruptEnable();

// Enable the Peripheral Interrupts //INTERRUPT_PeripheralInterruptEnable();

// Disable the Global Interrupts //INTERRUPT_GlobalInterruptDisable();

// Disable the Peripheral Interrupts //INTERRUPT_PeripheralInterruptDisable();

initComponents(); //Setup Registers

while (1)

{

// Add your application code

captureAI();

__delay_ms(100);

}

}

void initComponents()

{

TRISB = 0x00; //Set PORTB as outputs

PORTB = 0x00; //Clear outputs

configAI();

return;

}

void configAI()

{

//Configure port

TRISA = 0xff; //Set PORTA as inputs

ANSELA = 0X01; //Set RA0 as an AI

//Configure ADC module //b[7] sets right justification, b[6:4] sets CS = FRC,

//b[2]+b[1:0] sets Vss and Vdd as refrences

ADCON1 = 0b11110000;

return;

}

void captureAI()

{

uint8_t delayTime = 20; //20ms acquasition delay

__delay_ms(delayTime);

ADCON0 = 0x01; //Turn ADC on

ADCON0 |= 1 << 1; //set b[1] “go” bit,VAR |= 1 << 3 sets bit 3 fyi

uint8_t doneBit;

do

{

//wait for ADC to complete (go bit switches to 0 automatically when done)

doneBit = (ADCON0 >> 1) & 1;

} while(doneBit); //while go bit is on (AD conversion in progress)

uint16_t result = (ADRESH << 8) | ADRESL; //combine two 8bit values into a 16bit val

setLEDS(result);

ADCON0 = 0x00; //Turn ADC off return;

}

void setLEDS(uint16_t AI)

{

//Light LEDs accordingly

if(AI >= 768) { PORTB = 0x08; }

else if(AI < 768 && AI >= 512 ) { PORTB = 0x04; }

else if(AI < 512 && AI >= 256) { PORTB = 0x02; }

else if(AI < 256 && AI >= 0) { PORTB = 0x01; }

}

/** End of File */

Step 3: Configure Your IO

The code you just pasted should work just fine for a PIC1827 but each PIC is a little bit different.

Check your data sheet to see which pin (and therefore PORT) you would like to hook up the AI to. I will be configuring pin RA0 since that is the pin wired to the potentiometer on my board. I will be using RB0-RB3 to display the LED outputs as well.

Special Function Register (SFR) Refresher:

– The TRIS (tri-state) register controls the direction (Input or Output). A bit value of 0 means it is configured as an output and a value of 1 indicates input. A quick way to remember is 0 = Output and 1 = Input.

TRISB = 0x05; //0b00000101 sets pins 0 and 2 as inputs for example

– The PORT register sets up the values actually being sent out to or read in from the pins

PORTB = 0x00; //sets all the outputs low for example

– The ANSEL (analog select) register configures the pins as analog inputs. a bit value of 1 will mark a pin as an analog input.

ANSELA = 0x09; // Sets RA0 and RA3 as analog inputs for example

Using this information, you may have to edit some of the code to reflect your specific setup.

Additonal ADC SFR Info:

ADRESH and ADRESL (ADC result high byte and low byte registers)

These SFRs simply store the 10bit ADC value.

ADCONx SFR is used to configure the ADC conversion registers

ADCON1 allows you to do 3 things

  1. Setting right/left justification when the 10bit ADC result gets stored in the 2 ADRES registers (right=[- – – – – – 9 8] [7 6 5 4 3 2 1 0], left = [9 8 7 6 5 4 3 2] [1 0 – – – – – -]).
  2. Set your clocksource (essentially the ADC works by tracking how long a capacitor takes to charge. You can set how long it takes to measure it by changing your clocksource. Depending on your clock speed, setting it too fast can result in the capacitor not charging fully, so be careful)
  3. Setting your negative and positive voltage references (we are just using Vss and Vdd).

ADCON0 allows you to do 3 things as well

  1. Set which analog channel you woulld like to look at
  2. Toggle the go/done bit on (once you toggle it on it begins reading in the value. it will toggle itself back off when its done)
  3. To enable/disable

Check out the data sheet for more detailed info

You Should now be ready to hit the good ‘ol green play button and test your circuit out!

Source: How to Get an Analog Input on a PIC Microcontroller

About The Author

Muhammad Bilal

I am a highly skilled and motivated individual with a Master's degree in Computer Science. I have extensive experience in technical writing and a deep understanding of SEO practices.