Using the internal DAC in a PIC Micro


A DAC or digital to analogue converter is usually a component I.C. that you buy separately and connect to your microcontroller, but did you know there is usually a simple DAC built into microcontrollers; those containing a comparator module.

The comparator module usually allows you to set it up in multiple ways including the traditional stand-alone one where two there are two inputs (labeled plus, and minus) and the output. You often just want to compare an input signal to a known voltage level so you can capture the time that it is high (or generate an interrupt) e.g. in an ultrasonic level detector you want to ignore noise and detect the demodulaterd envelope of the signal.

However you really don't want to use three pins when you only want the input signal connected to the microcontroller comparator input. You can set this up since the comparator has an option to use an internal DAC to set one input of the comparator. and the output can be routed internally to trigger an interrupt. In this way only one pin is used to do the voltage comparison.

For some PIC microcontrollers the DAC output can also be sent to an output pin for your own use.

Note: Some PIC micros do not let you output the voltage reference to anexternal pin - you just have to read the datasheet (if the see CVRCONhas a control bit labeled CVROE)- if it does then you can use the DAC output.

Although the internal voltage reference is not that versatile (as it was not really intended for external use) it is free, programmable and ready to use.

It has a few limitations but the most important is that you can not drive high currents out so as long as it is driving a high impedance it will be OK. dac
Note: If you need to drive more current just buffer it with an opamp.

Normal use of the DAC

The voltage reference module was only intended to generate a voltage for the internal comparator module but some devices include a control bit to output this voltage to a pin.  

Note: The comparator module is flexible and does not have to use the reference - it can also use the analogue input from a pin.  There's more information on using the comparator here.

Not all devices have this control bit so you have to look in the data sheet to check if your device will output the voltage.  See the next section to look at typical registers in the voltage reference part of the datasheet.

Note: This control was only intended for testing the reference generator but with careful use you can use it in your projects.

PIC DAC Control Registers

To find out if you can use the PIC's internal voltage reference DAC just check the data sheet for your device and look for the CVRCON control register.  There should be a control bit labeled CVROE which is the bit that controls whether the  voltage is sent to the output pin (usually on RA2).

Here's part of the 16F88 datasheet:

CVRCON voltage reference output control - 16F88

Here bit 6 controls output of the voltage reference to a pin (For the 16F88 this is the "RA2/AN2/CVREF/VREF-" pin i.e. pin 1).

Note If you can only find a control register labeled VRCON then youcan't output the DAC voltage.

Voltage Reference block diagram

The block diagram of the generator is the internal hardware representation of the system - you can identify each control bit with each functional block so it's not difficult to understand.

16F88 voltage reference block diagram
Voltage reference block diagram 16F88

There is a 16 stage resistor network with each arm selected using an analogue mux (CVR[3..0]- this sends an analogue voltage to the comparator reference input (see the "Comparator Module" in the datasheet for using the reference voltage in the comparator).

To turn on the voltage reference CVREN enables the Fet at the top of the resistor chain.  CVRR changes the total current through the resistor chain so the range of output voltage is controllable (to some extent).

To send the analogue voltage outside the chip CVROE enables an analogue switch sending the voltage to the 'RA2/AN2/CVREF/VREF' pin.

Configuring the PIC pin for DAC output

The block diagram below shows pin 1 of the16F88 - it's useful to see how the hardware is connected so you can set upthe pin correctly for voltage reference output.

DAC output pin RA2/AN2/CVREF/VREF
(Pin Block Diagram).

The block diagram below shows pin 1 of the 16F88 - it's useful to see how thehardware is connected so you can set up the pin correctly for voltage referenceoutput.

RA2/AN2/CVREF/VREF pin (pin1 16F88)

pin 1 of 16F88 RA2

It shows that the voltage reference is directly connected to the pin so allyou need to do to output a voltage is to set the TRISA bit (to 1) - to make theP & N fets go into high impedance i.e. drawing no current - and set CVROE(1) obviously CVREN has to be 1 to enable the voltage reference. As well asthis set ANSEL (1) (see below).

Note: The TRIS control circuit not shown onthis diagram.

  • ANSEL for pin RA2 to 1
  • TRISA for pin RA2 to 1
  • CVROE to 1
  • CVREN to 1

Controlling ANSEL

The RA2 pin is the only pin that is analogue I/O (because of the voltagereference output) all other analogue pins are analogue input only.

This register (ANSEL) defaults to all ones so they are all analogue I/(O) atpower up but it's best to control the values so you know what they shouldbe.

Making it go.

Measured values

Vsupply = 4.93V

(i) CVRR = 1 (Low range)
Outputs 16 voltage steps from 0.01V to3.05V

(ii) CVRR = 0 (high range)
Outputs 16 voltage steps from 1.22V to3.53V

Data sheet information

Low volt range

(i) for CVRR=1
0.00 CVRSRC to 0.625 CVRSRC with CVRSRC/24 step size
CVREF = (VR<3:0>/24) €¢ (CVRSRC)


High volt range

(ii) for CVRR=0
0.25 CVRSRC to 0.72 CVRSRC with CVRSRC/32 step size
CVREF = 1/4 €¢ (CVRSRC) + (VR3:VR0/32) €¢ (CVRSRC)

So for the second range (higher voltage output range) you can have finer stepsizes and a slightly higher maximum voltage output (but the minimum is notzero).

CVRSRC = 4.93V

Measured (i) 0.01V to 3.05V
For (i) Range is 0V to 4.93*0.625 = 3.08V
Measured (ii) 1.22V to 3.53V
For (ii) Range is 0.25*4.93 = 1.23V to 4.93*0.72 = 3.55V

This shows the practical measurements match the datasheet predictions very well.


Running at 8MHz with the following code produces a pulse width for an output step of 11us (90kHz) - obviously this will change depending on the code used but you could probably create a simple signal generator.

Note: You can increase the output speed by using a 20MHz crystal - for the current project it is using the internal oscillator at 8MHz so there's room for improvement.

Programming and using the DAC in C

DAC voltage generator C code and hex file

Download the zip file containing the hex and source files and mikroC project files:

Download :

Download ZIP file : Download here.


The following code lets you experiment with the DAC generating a staircase output and a trigger signal to synchronize an oscilloscope.

If you don't have an oscilloscope add a delay_ms(1000) in the "for loop" to let you measure the output on a DVM.

Shadow register

The use of CVRCON_shdw allows updates without changing the actual value of CVRCON - the final output is made just before the end of the for loop.  

This is done since setting CVRCON CVR[3..0] to zero immediately changes the output to zero i.e. making glitched output.  So using CVRCON_shdw gives glitch free operation.

// File: 16F88-voltage-reference-dac.c
// Author: J F Main.
// Description:
//   Testing voltage comparator output
// Compiler : mikroC, mikroElektronika C compiler
//            for Microchip PIC microcontrollers
//            Version:
// Note Testing:
//   Tested on 16F88
// Requirements:
//    Clock : 8MHz  (Internal)
// Target : 16F88
// Version:
// 1.00 - Initial release.
// Copyright : Copyright © John Main 
//   Free for non commercial use as long as
//   this entire copyright notice is included
//   in source code and any other documentation.
#include "bit.h"

// Defines
#define RA2 2

// Start here
void main(void) {
unsigned char i=0;
unsigned char CVRCON_shdw = 0;

   /* Setup 16F88 */
   OSCCON  = 0x70;  // b6..4 = 110 (4MHz) 111=(8MHz).

   TRISB = 0;       // set as output

   ANSEL = (1<<RA2); // digital I/O except RA2 ana. I/O
   TRISA = (1<<RA2); // Set RA2 as input i.e. high Z

   // Setup the voltage reference
   // CVREN enable reference
   // CVROE output enable to pin
   // CVRR select coarse range
   CVRCON = (1<<CVREN) | (1<<CVROE) | (1<<CVRR);
   CVRCON_shdw = CVRCON;

   while(1) {      // infinite loop

      // Scope trigger

      for(i=0; i<16; i++) {

         // Note working on the shadow register as
         // the register updates immediately
         // so here output would be zeroed.
         // (if not using shdw).
         CVRCON_shdw &= 0xf0; // clear lower 4, keep top 4

         // Update the voltage reference mux
         CVRCON_shdw |= (i & 0x0f); // OR  in lower 4 bits

         // Output to real register in one go.
         CVRCON = CVRCON_shdw;

Internal DAC Disadvantages

Disadvantages of the internal DAC are that it:

  • Can only drive high impedance.
  • Only has 16 steps also split into high and low ranges.
  • Only uses internal supply as a reference *.
  • Can not go fully to V+.

* Note: For the 18F2550 device range you can select the reference voltage from the pin at Vref+.

If you design a circuit
with these limitations
in mind then you're good to go!


  • Programmable.
  • Already inside your microcontroller (depending on device).
  • No extra wiring.
  • Free!


Have your say about what you just read! Leave me a comment in the box below.

Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.

Privacy Policy | Contact | About Me

Site Map | Terms of Use