I have a PIC12F1571 (datasheet here: http://ww1.microchip.com/downloads/en/DeviceDoc/40001723b.pdf) that I have programmed to read an analog input, do some processing on the value read and then ouput a related value on the onchip DAC.

For testing, I have hooked up a potentiometer to the analog input and I can see in the MPLAB debugger that the correct value is being read by the PIC (ADRESH/ADRESL are set appropriately by the ADC). I have also verified that the DAC bits are being set properly in DACCON0 and DACCON1 registers- in the debugger I can see that the DAC is getting set to the right output, but there is no voltage on the output pin.

I have double checked the direction of my TRISA register for the appropriate pins, made sure that the alternate pin function register has no conflicts and that everything else is disabled.

Code is below. I have set up the PIC so that RA4 - pin 3 (Analog Channel 3) is being used as the ADC input and RA0 - pin 7 is being used as the DAC output.

#if defined(__XC)
  #include <xc.h>         /* XC8 General Include File */

#include <stdio.h>
#include <stdlib.h>

#include <pic12f1571.h>

#define SYS_FREQ        500000L
//#define FCY             SYS_FREQ/4

//Set up using the Memory View (Window -> PIC Memory Views -> Configuration Bits
#pragma config FOSC = INTOSC       //  (ECH, External Clock, High Power Mode (4-32 MHz); device clock supplied to CLKIN pin)
#pragma config WDTE = OFF        // Watchdog Timer Enable (WDT enabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config BOREN = OFF       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)

#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOREN = OFF    // Low Power Brown-out Reset enable bit (LPBOR is disabled)
#pragma config LVP = OFF         // Low-Voltage Programming Enable (Low-voltage programming enabled)

void setup(void)
 /* Configure Interrupts
 * INTCON - Interrupt Control Register:
 * Set GIE (Global Interrupt Enable)
 * Set PEIE (Peripheral Interrupt Enable bit (for ADC interrupts)
 * Set TMR0IE (Timer0 Overflow Interrupt Enable bit)
 * PEI1 - Peripheral Interrupt Enable Register:
 * Set ADIE (ADC Interrupt Enable bit)
    INTCON = 0xE0;
    PIE1 = 0x40;
    PIE2 = 0x00;
    PIE3 = 0x00;

/* Configure PORTA for the following:
 * Configure TRISA for Port A Direction Control
 * only have 1 input pin - RA4
 * 1 output pin - RA0 (for DAC1)
 * Configure ODCONA for Open-drain Control
 * PORTA configured as standard push-pull
 * Configure SLRCONA for Slew Rate Control
 * Slew rate is unlimited
 * Configure INLVLA for Input Threshold Control
 * Doesn't need to be set since all input is analog
 * Configure ANSEL registers for ADC operation on AN3 (RA4/pin 3)
    TRISA = 0x10;
    ODCONA = 0x00;
    SLRCONA = 0x00;
    INLVLA = 0x00;
    ANSELA = 0x10;

    /* Configure TIMER0
     * In OPTION_REG:
     * Select Internal instruction clock
     * Select no prescaler on the timer
     *  */
    OPTION_REG = 0x84;

    /* Configure ADC
     * Set ADCON0 to the following:
     * CHS set to analog channel AN3 = 00011 (RA4/pin 3)
     * ADON set to ADC enabled = 1
     * Set ADCON1 to the following:
     * ADFM set to left justified = 0 (so that we can get away with reading a
     *   single byte for our converted value)
     * ADCS Conversion clock source set to Fosc/8 = 001
     * ADPREF set to Vdd reference = 00
     * Set ADCON2 to trigger on Timer0 overflow
     * ADCON2:TRIGSEL = 0011

    ADCON0 = 0x0D;
    ADCON1 = 0x10;
    ADCON2 = 0x30;

    /* Configure up DAC
     * Set DACCON0 register:
     * DACEN set to DAC enabled = 1
     * DACOE set to voltage level output = 1
     * DACPSS<1:0> is set to Vdd = 00
     * DACCON1 register contains the DACR bits (i.e. the output value)
    DACCON0 = 0xA0;
    DACCON1 = 0x00;

    // Set APFCON register to
    APFCON = 0xFF;

void interrupt isr(void)
        TMR0IF = 0; /* Clear TIMER0 Overflow Interrupt Flag*/
        //ADC will automatically start converting now

    //    ++counter;

    {   /* Clear ADC Completion Interrupt Flag */
        ADIF = 0;
        DACCON1 = ADRESH >> 3;

int main(int argc, char** argv) {
    return (EXIT_SUCCESS);

What am I missing? This should be easy...


I think the code worked all along - the problem was that the DAC would not actually output anything when the PIC was programmed for debug mode. This is because the RA0 pin is also used for ICSPDAT, so there was a conflict and I'm guessing the compiler/IDE decided not to actually output anything - I had checked that it wasn't just a pull down in the PICKit3 programmer by disconnecting the wire. Definitely an issue with programming the PIC in debug mode.

Programmed the PIC in regular mode and the DAC outputs just fine - although if the PICKit3 is left connected to the DAC output pin, the max voltage is 1/3 of Vdd. This makes sense as well.

In the end, its user error.


It doesn't look like you're telling your ADC to do a conversion, even though you've enabled the module. On the PIC18F I use, I have to specifically write a 1 to the GO/~DONE bit of ADCON0 to tell it that I want it to convert something. When it's done, it will raise the interrupt. It doesn't convert without you first requesting it, which looking at your code you never do -- you just wait forever. Glancing at the datasheet, this one looks like it works the same as the chip I use.

  • \$\begingroup\$ The ADC in this PIC12F has a trigger mode where it can (and does as verified in the debugger) get started automatically on an overflow of TIMER0. // Set ADCON2 to trigger on Timer0 overflow // ADCON2:TRIGSEL = 0011 I've tried the code with explicitly setting GO/~DONE and the behaviour is the same. Still the DAC won't quack. \$\endgroup\$ – Antony M. Apr 24 '14 at 4:40
  • 3
    \$\begingroup\$ Best thing to do is cut the problem in half. Ignore the ADC for now and just throw some numbers at the DAC. This eliminates any speculation about the ADC portion. Also makes for less code to post. \$\endgroup\$ – gbarry Apr 24 '14 at 7:53

If you talk about DAC, I will show you a following code.

But I think that you are confusing ADC with DAC.

It works exactly with my own pic12f1572.

int main(void)
    OSCCON = 0b01111000; // for example,16 MHz    
    ANSELA = 0b00000000; // all pins to digital    
    PORTA  = 0b00000000; // all port bits off    
    TRISA  = 0b00001000; // all pins to outputs except RA3 (MCLR)    
    ADCON0 = 0b01111000;  // use DAC   
    DACCON0= 0b10100000;  // DAC enable, VDD as reference  
    DACCON1= 0b00011111;  // for example,32 as output = VDD

I am using the pic12F1572 to provide a DAC but found if I put a scope on the output the correct answer was seen but if I connected to a load much less than 100k the output was nonlinear or zero. I checked the datasheet and it implied that this was relay only intended for generating reference voltages to be used within the chip. My solution was to put a small FET Op Amp on the output as a driver and all is fine.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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