Feb 06

Getting Started with PIC Microcontrollers – The ADC

This time, we shall take a look at the Analogue to Digital Converter (ADC) This peripheral is the link between the analogue world and the digital domain of the microcontroller.

It can be used for many things, e.g. (analogue) sensor/transducer readings such as microphones, thermistors, light sensors, pressure sensors, accelerometers, and much more. In this entry we will take a look at one of the simplest uses, reading the voltage from a potentiometer. We will look at how to set the ADC peripheral up, and how to take the reading and use it to display this information roughly on a line of LEDs.

If you are completely new to the ADCs and how they work, or want to learn a little more about the different types and theory behind them, then I suggest taking a look at these links:

Decent Sparkfun page on ADC basics

At its most basic, the ADC takes a voltage level, compares it with a reference voltage, and turns it into a corresponding binary value. The PIC ADC is a 10-bit ADC, and we will be using the Vdd (+V power pin, it is possible to use a dedicated Vref pin if a different voltage to Vdd is required as reference) as the reference so the range will be from 0 – 5V (assuming you are running your PIC at 5V) The resolution, which at 10-bits equates to $$2^{10} = 1024$$ steps means that each step equals $$\frac{5V }{1024} = 4.88mV$$. For example an input of 0.5V would convert to $$\frac {0.5V}{0.00488V} = 102$$ decimal or  $$0001100110$$ binary. An input of 2.5V would convert to  $$\frac {2.5V} {0.00488V} = 512$$ decimal or $$1000000000$$ binary.

Okay, now to setup the PIC. We will use the PIC16F690 again with just a few additions to the original “Getting Started with Microcontrollers” schematic, namely a potentiometer and a few extra LEDs. The potentiometer can be pretty much anything you have to hand, preferably in the range of $$1k \Omega\ –\ 100k \Omega$$. The LEDs used are standard 5mm red LEDs, but you can swap these if you need to (make sure you size the resistors correctly if the Vf / max current is different – see here if you need help) Also, don’t worry if you don’t have enough LEDs, you can use less if you alter the #define value accordingly in the code below and comment out the references to unused pins e.g. for 5 LEDs then the LED_INT value should be $$\frac{1024}{5} = 204$$.

Here is the schematic: And the code:

/*
* File:   comet2.c
* Author: Oli Glaser
*
* Created on 31 Jan 2015, 07:37
*/

#include <xc.h>

#pragma config FOSC = INTRCIO   // Internal RC Oscillator
#pragma config WDTE = OFF       // Turn watchdog off
#pragma config BOREN = OFF      // Brown out enable off

// Set which pin controls which LED, change if you wish to use
// different pin
// Define which pin each LED is on
#define LED1    PORTAbits.RA2
#define LED2    PORTCbits.RC0
#define LED3    PORTCbits.RC1
#define LED4    PORTCbits.RC2
#define LED5    PORTBbits.RB4
#define LED6    PORTBbits.RB5
#define LED7    PORTBbits.RB6
//#define LED8    PORTBbits.RB7
//#define LED9    PORTCbits.RC7

//#define LEDD    PORTCbits.RC6

// Function definitions
void init(void);
void delay(unsigned int ms);

// Global variables
volatile unsigned int result = 0;

int main(int argc, char** argv) {

init();

while(1)
{
delay(10);

unsigned int temph, templ;
{
result = temph + templ;

if(result > LED_INT)
{
LED1 = 1;
}
else
{
LED1 = 0;
}
if(result > LED_INT*2)
{
LED2 = 1;
}
else
{
LED2 = 0;
}
if(result > LED_INT*3)
{
LED3 = 1;
}
else
{
LED3 = 0;
}
if(result > LED_INT*4)
{
LED4 = 1;
}
else
{
LED4 = 0;
}
if(result > LED_INT*5)
{
LED5 = 1;
}
else
{
LED5 = 0;
}
if(result > LED_INT*6)
{
LED6 = 1;
}
else
{
LED6 = 0;
}
if(result > LED_INT*7)
{
LED7 = 1;
}
else
{
LED7 = 0;
}

}
}
}

// Basic delay routine
void delay(unsigned int ms)
{
unsigned int i = 1000;
while(ms)
{
i = 1000;
while(i) i--;
ms--;
}
}

// Set up various things - I/Os, interrupts, etc
void init(void)
{
// Set internal oscillator to 8MHz
OSCCON = 0x70;
ANSEL = 0;
ANSELH = 0;
// Set all pins to outputs
TRISA = 0;
TRISB = 0;
TRISC = 0;

TRISCbits.TRISC6 = 1;   // Set RC6/AN8 as input for ADC
ANSELHbits.ANS8 = 1;     //
ADCON1bits.ADCS = 0b010; // Set conversion clock as Fosc / 32 = 1(8MHz / 32) = 4us
}

1. MIke

Hi Olie,
I wanted to thank you for the time you are taking to help me and others out there, it is very much appreciated. I started out with a couple of projects using the PicAxe basic which was quick and easy for a beginner to get into the pic world and provided some understanding of micro-controllers and peripheral interfacing. So, while I was able to get a functional result, I soon found that the interpreted environment became constraining in terms of code size, performance and advance features such as interrupts etc.

Well, it turns out that the PicAxe 18M2 I used is actually a Pic16F1847 under the covers so the PCB I had fabbed for the projects would work for both. So I swapped the chips out, used mikroBasic compiler (a first step to ease the transition to compiled C environment) and rewrote my code. Unfortunately, the part of the code that uses I2C bus control of HMC5883L (among other things like real time clock) is not working out well (probably pilot error and/or some issues with libraries/initialization/configuration of the pic), so I have decided to bite the bullet and move to C (which at first glance seems very daunting with all the unfamiliar structure, configuration and setup) because the breadth of example code being available in C for the pic. Your tutorials are making that much easier to understand I thank you for that. Please continue the great work on your blog and look forward to learning more from you…

All the best,
Mike

1. Oli G

Thanks Mike, you are quite welcome 🙂 I have had other commitments recently, but I intend to add more tutorials as soon as possible.

2. Joseph

Thanks for your work on these, Oli….Look forward to more!!

3. black and white kitten names the letter W

i found my errors thanks anyway br the for statement needs to end in a semicolon which is missing here Hi noob here, im getting errors at this point unable to resolve identifiers time amp; time1 br should these be defined somewhere else? thanks in advance