5.3 Using the Internal DAC

Combine the advantages of both the previous methods by using the internal Digital to Analog Converter (DAC). No external components are needed, and one can provide virtually any voltage to the positive input of the voltage follower.

Setting the DAC reference to VDD, the DATA register value for the desired output voltage becomes:

D A T A = V O U T V D D 1023

The following helper function will set the output voltage of the DAC to be fed into the OPAMP.

void dac_set_voltage(float v_out)
{
	/*Ensure we set the output voltage within the limits of the OPAMP drive capabilities*/
	if (v_out>VDD-1)
	{
		v_out=VDD-1;
	}
	volatile uint16_t data = (v_out/VDD)*1023;
	DAC0.DATA = data << DAC_DATA_gp;
}
A basic setup function for the DAC is useful. For this application, it is necessary to do the following:
  1. Set the reference for the DAC to VDD.
  2. Enable both the DAC and the DAC output.
void dac_init()
{
	/*Setting the reference for the DAC to VDD*/
	VREF.DAC0REF = VREF_REFSEL_VDD_gc;

	/*Enable DAC and enable the DAC output*/
	DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm;
}

To connect the DAC output to the positive input of the OPAMP, the following setup function is made:

void op_amp_setup_dac()
{
	OPAMP.OP0INMUX = OPAMP_OP0INMUX_MUXNEG_OUT_gc | OPAMP_OP0INMUX_MUXPOS_DAC_gc;
}
Figure 5-3. Using Internal DAC

The code below provides the above functionality.

#define F_CPU 4000000ul
#define CLK_PER 4000000ul
#define OPAMP_MAX_SETTLE_TIME 0x7F
#include <avr/io.h>
#include <math.h>

#define VDD 5

void op_amp_init();
void op_amp_setup_dac();
void dac_init();
void dac_set_voltage(float v_out);

int main(void)
{
	dac_init();
	op_amp_init();
	/*Default voltage is set to 1.8V*/
	dac_set_voltage(1.8);
	while (1) 
    {
		/*Your application goes here*/
    }
}

void dac_set_voltage(float v_out)
{
	/*Ensure we set the output voltage within the limits of the OPAMP drive capabilities*/
	if (v_out>VDD-1)
	{
		v_out=VDD-1;
	}
	volatile uint16_t data = (v_out/VDD)*1023;
	DAC0.DATA = data << DAC_DATA_gp;
}

void dac_init()
{
	/*Setting the reference for the DAC to VDD*/
	VREF.DAC0REF = VREF_REFSEL_VDD_gc;

	/*Enable DAC and enable the DAC output*/
	DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm;
}

void op_amp_init()
{
	/*Disable input on op amp output pin*/
	PORTD.PIN2CTRL = PORT_ISC_INPUT_DISABLE_gc;
	
	/*Set up op amp*/
	OPAMP.CTRLA = OPAMP_ENABLE_bm;
	OPAMP.TIMEBASE = (uint8_t) ceil(CLK_PER*0.000001)-1; /*Number of peripheral clock cycles that amounts to 1us*/
	OPAMP.OP0CTRLA = OPAMP_RUNSTBY_bm | OPAMP_ALWAYSON_bm | OPAMP_OP0CTRLA_OUTMODE_NORMAL_gc;
    OPAMP.OP0SETTLE = OPAMP_MAX_SETTLE_TIME; //As the settle time is unknown, the maximums should be set
}
void op_amp_setup_dac()
{
	OPAMP.OP0INMUX = OPAMP_OP0INMUX_MUXNEG_OUT_gc | OPAMP_OP0INMUX_MUXPOS_DAC_gc;
}

The code for this example is available in the using-internal-DAC folder in these github repositories: