2.7.1 Code Example

Below is a code example showing the configuration of the ADC as an event generator and an event user:
  • Event user: ADC conversion triggered by RTC overflow event
    • RTC is configured to generate an RTC overflow event at the desired ADC sampling rate. The sampling rate in the example is 100 Hz.
    • ADC conversion is triggered at a rate of 100 Hz, and the result is read when the Result Ready (RESRDY) bit in the Interrupt Flags (ADCn.INTFLAGS) register is set.
  • Event generator: Pin PB2 outputs an event (Pulse) when the ADC result is ready
#define F_CPU 3333333ul

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

#define TIMEBASE_VALUE      ((uint8_t) ceil(F_CPU*0.000001))
#define ADC_MAX_VALUE       (((1 << 12) / 2) - 1) /* In differential mode, the max value is 2047 */

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

/*Defines to easily configure RTC event frequency */
#define ADC_SAMPLING_FREQ   100     /* Hz */
#define RTC_CLOCK           32768   /* Hz */
#define RTC_PERIOD          (RTC_CLOCK / ADC_SAMPLING_FREQ)

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

/******************************************************************************
EVSYS initialization:
Channel 0:
            Event system generator: RTC Overflow
            Event system user: ADC0
Channel 1:
            Event system generator: ADC0 Result Ready
            Event system user: EVOUTB (PIN PB2)
*******************************************************************************/
void event_system_init(void)
{
	PORTB.DIRSET = PIN2_bm; /* Configure EVOUTB to output */

	EVSYS.CHANNEL0 = EVSYS_CHANNEL0_RTC_OVF_gc;     /* RTC Overflow ->  Channel 0 */
	EVSYS.USERADC0START = EVSYS_USER_CHANNEL0_gc;   /* Channel 0    ->  ADC0 Start */

	EVSYS.CHANNEL1 = EVSYS_CHANNEL1_ADC0_RES_gc;    /* ADC RESRDY   ->  Channel 1 */
	EVSYS.USEREVSYSEVOUTB = EVSYS_USER_CHANNEL1_gc; /* Channel 1    ->  EVOUTB (PB2) */
}

/*********************************************************************************
RTC initialization
**********************************************************************************/
void rtc_init(void)
{
	while(RTC.STATUS > 0);  /* Wait for all registers to be synchronized */
	RTC.CTRLA = RTC_PRESCALER_DIV1_gc | RTC_RTCEN_bm; /* Enable RTC, no prescaler */
	RTC.CLKSEL = RTC_CLKSEL_INT32K_gc; /* Select 32.768 kHz internal RC oscillator */
	RTC.PER = RTC_PERIOD;
	while(RTC.STATUS > 0);  /* Wait for all registers to be synchronized */
}

/**********************************************************************************
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.MUXNEG = ADC_MUXNEG_AIN7_gc; /* ADC channel AIN7 -> PA7 */
	/* Start ADC Series conversion on event trigger */
	ADC0.COMMAND = ADC_DIFF_bm | ADC_MODE_SERIES_gc | ADC_START_EVENT_TRIGGER_gc;
}

int main(void)
{
	event_system_init();
	rtc_init();
	adc_init();

	while(1)
	{
		if(ADC0.INTFLAGS & ADC_RESRDY_bm)  /* Check if ADC sample is ready */
		{
			/* Read accumulated ADC result, clears the interrupt flag */
			adc_reading = ADC0.RESULT;
			/* Calculate voltage on ADC pin, VDD = 3.3V, 8 samples in 12-bit resolution */
			voltage = (float)(((adc_reading * 3.3) / ADC_MAX_VALUE) / ADC_SAMPLES);
		}
	}
}
Tip: When using VDD as reference and taking an average of accumulated samples, the ADC can effectively be used as a power supply noise filter.