How to Get an Analog Input on a PIC Microcontroller
by kdjohnson123 in Circuits > Microcontrollers
24658 Views, 4 Favorites, 0 Comments
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.
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
MPLAB X Project Setup
We want to create a new project in the MPLAB IDE.
- Open MPLAB X IDE
- File->New Project
- Category: = Microchip embedded and Projects: = Standalone Project
- Select your device from the dropdown list (mine is a PIC16F1827 for example)
- Debug Header = None
- Select your tool (I selected PICkit 3)
- Select Compiler (We will be using XC8)
- 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.
- Right click the project and select properties
- Select your PICkit programmer on the left hand side
- Select Power from the the dropdown menu
- check the power circuit from PICkit x box and set the voltage level to 3.375
- Press ok
It's now time to open the code configurator (MCC)
- Click Generate to compile the default main.c file (save the default config file then select yes)
- 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 */
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
- 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 - - - - - -]).
- 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)
- Setting your negative and positive voltage references (we are just using Vss and Vdd).
ADCON0 allows you to do 3 things as well
- Set which analog channel you woulld like to look at
- 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)
- 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!