5.2.2.1 Code Example

The following code example shows how to perform differential measurements using the Burst Accumulation with Scaling mode, the PGA with 16x gain and oversampling to achieve a 16-bit resolution.
#define F_CPU 3333333ul

#include <avr/io.h>
#include <math.h>
#include <util/delay.h>

#define TIMEBASE_VALUE              ((uint8_t) ceil(F_CPU*0.000001))
#define ADC_DIFF_MAX_VALUE          (((1 << 12) / 2) - 1)                   /* In differential mode, the max value is 2047 */
#define ADC_DIFF_MAX_VALUE_16BIT    ((uint32_t) ADC_DIFF_MAX_VALUE << 4)    /* In differential mode, the max value for a 16-bit result is 32752 */

/* Defines to easily configure ADC accumulation */
#define ADC_SAMPNUM_CONFIG  ADC_SAMPNUM_ACC256_gc
/* Left shifting (1 << SAMPNUM) results in the number of accumulated samples */
#define ADC_SAMPLES         (1 << ADC_SAMPNUM_CONFIG)

/* Volatile variables to improve debug experience */
static volatile int32_t adc_reading;
static volatile float voltage;
static volatile float current;

/*********************************************************************************
ADC initialization
**********************************************************************************/
void adc_init()
{
	ADC0.CTRLA = ADC_ENABLE_bm;
	ADC0.CTRLB = ADC_PRESC_DIV2_gc; /* fCLK_ADC = 3.333333/2 MHz */
	ADC0.CTRLC = ADC_REFSEL_1024MV_gc | (TIMEBASE_VALUE << ADC_TIMEBASE_gp);
	ADC0.CTRLE = 17; /* (SAMPDUR + 0.5) * fCLK_ADC = 10.5 µs sample duration */
	ADC0.CTRLF = ADC_LEFTADJ_bm | ADC_SAMPNUM_CONFIG; /* Enable left adjust if accumulating < 16 samples */

	ADC0.MUXPOS = ADC_VIA_PGA_gc | ADC_MUXPOS_AIN6_gc; /* ADC channel AIN6 -> PA6 */
	ADC0.MUXNEG = ADC_VIA_PGA_gc | ADC_MUXNEG_AIN7_gc; /* ADC channel AIN7 -> PA7 */
	ADC0.COMMAND = ADC_DIFF_bm | ADC_MODE_BURST_SCALING_gc; /* Burst Accumulation with Scaling */

	/* Enable PGA with 16x gain.
	Set full bias current for fast sampling. Configure ADCPGASAMPDUR according to data sheet. */
	ADC0.PGACTRL = ADC_GAIN_16X_gc | ADC_PGABIASSEL_1X_gc | ADC_ADCPGASAMPDUR_6CLK_gc | ADC_PGAEN_bm;
}

int main(void)
{
	adc_init();

	while(1)
	{
		ADC0.COMMAND |= ADC_START_IMMEDIATE_gc;
		while(!(ADC0.INTFLAGS & ADC_RESRDY_bm)); /* Wait until conversion is done */

		adc_reading = ADC0.RESULT; /* Read 16 bit scaled or left adjusted result */

		/* Calculate the differential voltage, VREF = 1.024V, 16-bit resolution, 16x gain. */
		voltage = (float)((adc_reading * 1.024) / ADC_DIFF_MAX_VALUE_16BIT) / 16;
		//current = voltage / 5;   /* Uncomment this line if measuring across a 5 ohm resistor in series with the power supply */

		_delay_ms(500);
	}
}
A use case example of the code above is current measurement. Like shown in the image below, minor hardware modification must be done to be able to measure the device’s current consumption. Selecting a resistor size for current measurement depends on multiple factors. For example, if the resistance is high, it may introduce a big voltage drop which may lead to shortened battery life in a battery powered application. On the other hand, if the resistance is low, it may lead to lower resolution. A 5Ω resistor in this example leads to a measuring step size of 400 nA and a range of 400 nA to 12.8 mA.
  1. Connect the resistor between VPS and VDD.
  2. Connect PA6 to VPS and PA7 to VDD.
Figure 5-1. Current Measurement Connection
In software, the following must be done to read the current consumption measurements:
  1. Uncomment the line below the voltage calculation in the main() function in the code.
  2. Add a breakpoint in the code and click Start Debugging to start debugging.
  3. When the program has been halted at the breakpoint, right click on the current variable and select Add Watch. The current variable will now be visible in the watch window.
  4. Click Continue to update the value(1).
Note:
  1. If controlling the measurements with a multimeter, the results might deviate. Since the multimeter also measures at times where the ADC is inactive, it will normally show a lower current consumption.