2.6.1 Code Example

The code example below shows an application example where an ADC reading of below 2000 or above 3000 is considered an invalid signal spike. The window comparator is used to filter these out by restarting the sample accumulation in the SAMPRDY interrupt when the signal is outside of the thresholds. The voltage of the signal is calculated in the RESRDY Interrupt Service Routine (ISR) when all samples are accumulated.

#define F_CPU 3333333ul

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

#define TIMEBASE_VALUE      ((uint8_t) ceil(F_CPU*0.000001))
#define ADC_MAX_VALUE       ((1 << 12) - 1) /* In single-ended mode, the max value is 4095 */

/* 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 uint32_t adc_reading;
static volatile float voltage;

/**********************************************************************************
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_VDD_gc | (TIMEBASE_VALUE << ADC_TIMEBASE_gp);
	ADC0.CTRLE = 17; /* (SAMPDUR + 0.5) * fCLK_ADC = 10.5 µs sample duration */
	ADC0.CTRLF = ADC_SAMPNUM_CONFIG;

	ADC0.MUXPOS = ADC_MUXPOS_AIN6_gc; /* ADC channel AIN6 -> PA6 */

	ADC0.WINHT = 3000; /* Window High Threshold */
	ADC0.WINLT = 2000; /* Window Low Threshold */
	/* Window Comparator mode: Outside. Use SAMPLE register as Window Comparator source */
	ADC0.CTRLD = ADC_WINCM_OUTSIDE_gc | ADC_WINSRC_SAMPLE_gc;
	/* Enable Window Compare and Result Ready interrupt */
	ADC0.INTCTRL = ADC_WCMP_bm | ADC_RESRDY_bm;

	ADC0.COMMAND = ADC_MODE_SERIES_gc; /* Series Accumulation mode */
}

/***********************************************************************************
Window Compare interrupt:
In this example, when a sample is outside a certain window, this is considered an
invalid signal spike. When such a spike is detected, the accumulated ADC result is
disregarded by restarting the series conversion.
***********************************************************************************/
ISR(ADC0_SAMPRDY_vect)
{
	ADC0.INTFLAGS = ADC_WCMP_bm;        /* Clear WCMP flag */

	/* Clear the accumulator by resetting the Mode bit field */
	ADC0.COMMAND = ADC_START_STOP_gc;
	/* Reconfigure Series Accumulation mode */
	ADC0.COMMAND = ADC_MODE_SERIES_gc;
}

/***********************************************************************************
Result Ready interrupt:
If no spike was detected, the result is read and the corresponding
voltage is calculated
***********************************************************************************/
ISR(ADC0_RESRDY_vect)
{
	ADC0.INTFLAGS = ADC_RESRDY_bm;      /* Clear RESRDY flag */

	/* Check if the last sample was inside the window */
	if(!(ADC0.INTFLAGS & ADC_WCMP_bm))
	{
		adc_reading = ADC0.RESULT; /* Read ADC result */
		/* Calculate voltage on ADC pin, VDD = 3.3V, 12-bit resolution, 256 samples */
		voltage = (float)(((adc_reading * 3.3) / ADC_MAX_VALUE) / ADC_SAMPLES);
	}
}

int main(void)
{
	adc_init();
	sei(); /* Enable global interrupts */

	while(1)
	{
		/* Start a conversion once every 1 ms */
		ADC0.COMMAND |= ADC_START_IMMEDIATE_gc;
		_delay_ms(1);
	}
}