SAM C21 Sigma-Delta ADC Configuration
Contents
This page provides a more detailed description of how to configure the SAM C21 Sigma-Delta ADC peripheral. After reviewing the basic module functionality, we describe the key configuration steps (and associated registers) that need to be configured to implement a simple application where the SAM C21 digitizes a light sensor signal.
Module Overview
Description
The Sigma-Delta Analog-to-Digital Converter (SDADC) converts an analog voltage to a signed 16-bit value by integrating and decimating the output of a sigma-delta modulator. The filtering and decimation are done using a SINC-based filter that has zeroes placed to minimize the aliasing effects of the decimation. This is a third order SINC filter (see the figure below) with an adjustable Oversampling Ratio (OSR) to achieve higher throughput versus signal-to-noise-ratio (SNR). For example, an OSR of 64 provides a an output data rate of 23 ksps while an OSR of 1024 produces an output data rate of 1.4 ksps.
The SDADC provides a signed result in a 24-bit register to allow for gain and offset correction without overflow in hardware. Because the result is in 2’s complement format, the SDADC result is signed 16-bit (maximum) using ±VREF. Using the internal VREF set at 1.024 V, the SDADC will produce codes to 1.024 V. The internal VREF can be configured to supply a reference of 1.024 V, 2.048 V, and 4.096 V.
Register Interface
Functional Description
The Sigma Delta Analog-to-Digital Converter (SDADC) can be used for high-resolution DC measurements. These measurements can include temperature sensors, thermocouples, cold-junction compensation, 3-4 Wire RTD sensors, 4-20 mA current loops, current (Shunt), as well as scales/load cells. The Sigma-Delta architecture provides a low-cost solution for these precision measurements, requiring only a simple R-C low pass filter for anti-aliasing.
A generic clock (GCLK_SDADC) is used to generate the CLK_SDADC via a 7-bit prescaler. The GCLK must be configured and enabled before the SDADC can be used. The sampling clock is derived from the CLK_SDADC/4. Therefore, the maximum CLK_SDADC is 6 MHz and the maximum sampling frequency is 1.5 MHz. The GCLK_SDADC is asynchronous to the APB bus clock and, therefore, writes to registers require synchronization. This flexible clocking system allows configuring CLK_SDADC to run in any Sleep Mode. The 7-bit prescaler enables flexible sampling frequency adjustment.
The SDADC supports differential measurements on three analog input channels. The measurements are done using one of four references; internal bandgap, an external voltage on AREFB, DAC output, or AVCC. The voltage reference has a selectable buffer to offers higher input impedance to the external reference.
The SDADC filters and decimates the sigma-delta output bit stream at 16-bit (signed) with programmable rates of CLK_SDADC_FS (prescaled ADADC clock frequency) divided by 64 to 1024. The Output rate is set by modifying the programmable Over Sampling Ratio (OSR). The result is a 2’s complement 24-bit result with programmable gain and offset correction.
The SDADC peripheral supports three interrupts. The result ready flag (INTFLAG.RESRDY) can trigger a DMA transfer or event. The window monitor can generate an event by setting the EVCTRL.WINMONEO] bit. The INTFLAG.OVERRUN flag is set when the previous result is not read before a new result is ready.
Automatic sequences can be configured to enable multiple samples from a single start of conversion request. The order of this conversion is from the lower positive input pair to the upper positive input pair (AINN0, AINP0, AINN1, AINP1 …).
Basic Operation
Initialization
The SDADC must be configured with the peripheral disabled. The sequence to configure and enable the SDADC is:
- Enable the Generic Clock
- GCLK_SDADC
- Select the Voltage Reference and Range
- REFCTRL.REFRANGE[1:0]
- REFCTRL.REFSEL[1:0]
- Set the Conversion Rate and Resolution
- GCLK_SDADC
- CTRLB.PRESCALER[7:0] (SDADC_CLK)
- CTRLB.OSR[2:0] (Over Sampling Ratio)
- Select trigger source or interrupts
- SWTRIG.START (Software Trigger)
- Free Run Mode
- DMA/Event
- Timers
- Sequence Control
- Automatic Sequences (SEQCTRL)
- Lower to Upper Positive Pairs
- SEQSTATUS.SEQBUSY bit will be set when a conversion is initiated and cleared when the sequence is complete
- The input number is stored with the RESULT
- Window Monitor Control
- RESULT compare to threshold
- WINUT/WINLT (Upper and Lower Thresholds)
Reading the Results
The SDADC result ready flag (INTFLAG.RESRDY) is set when a conversion is complete. When the peripheral is first initialized, the results are only valid after the third conversion. To automate this limitation in hardware, the register set includes the skip register (CTRLB.SKPCNT[3:0]) to automatically skip the first n results.
When in free running mode, the application must read the result before the next result is ready. If this does not happen, the overrun flag (INTFLAG.OVERRUN) will be set.
The result is read from RESULT and is 24-bits wide. To get the 12- to 16-bit conversion result based on the OSR, the application needs to shift the results. However, this can be accomplished in hardware using the SHFTCORR register to define a number of right shifts to be done automatically. Also, a fixed gain can be applied in the same way. Care must be taken to set the gain to at least 1 if not used as it is automatically applied.
Configuration Example
This section explores a simple and basic SDADC configuration, where the SAM C21 SDADC is connected to a light sensor circuit:
To see the full project details, visit the SAM C21 Sigma-Delta ADC Example Project page.
Summary of Steps
- Initialization
- Enable Generic and Synchronous (APBx) Clocks
- Configure I/O Pins & PMUX
- Set Sampling Frequency (CLK_SDADC)
- Set VREF
- Set Correction Values
- Set Input Channel
- Enable Module
- Reading Results
- Start Conversion(s)
- Wait for the RESRDY Flag
- Read RESULT and shift right in the application or in the hardware via SHIFTCORR
Initialization
Configure Clocks
The following diagram summarizes the clocking configuration for this application:
The default oscillator source clock is the internal OSC48M 48 MHz oscillator, which is divided by 12 to provide a 4 MHz clock source on reset.
This clock is fed to the GCLK generator module, and is used to feed two GCLK generators:
- GCLK_0 is used to supply the CPU and synchronous (APBx) bus clocks (via the MCLK unit), and
- GCLK_1 is used to source the asynchronous clocks for the SDADC peripheral channel (Channel 35).
As indicated in the above diagram, the SDADC module requires two clocks. The first is the APB clock (peripheral bus), which comes from the MCLK module. The bit that enables the SDADC clock is in the APBCMASK register:
The following code sample performs this assignment:
MCLK->APBCMASK.reg = MCLK_APBCMASK_SDADC;
Next, we configure asynchronous clock GCLK_1, which will be used as the clock for sampling the analog input. In this example, it is configured for 1 MHz:
GCLK->GENCTRL[1].bit.SRC = GCLK_GENCTRL_SRC_OSC48M_Val;
// Wait for synchronization
while(GCLK->SYNCBUSY.bit.GENCTRL1);
// Divide by 4... 1 MHz
GCLK->GENCTRL[1].bit.DIV = 4;
// Wait for synchronization
while(GCLK->SYNCBUSY.bit.GENCTRL1);
// Enable GCLK1
GCLK->GENCTRL[1].bit.GENEN = 1;
// Wait for synchronization
while(GCLK->SYNCBUSY.bit.GENCTRL1);
// Enable Output Pin (optional - to verify GCLK_1 frequency)
GCLK->GENCTRL[1].bit.OE = 1;
// Wait for synchronization
while(GCLK->SYNCBUSY.bit.GENCTRL1);
Finally, we select GCLK_1 as the clock source for the SDADC module, noting from the SAM C21 datasheet that the SDADC module is enumerated as Peripheral Channel 35:
Also, update the appropriate peripheral channel control register as shown here:
GCLK->PCHCTRL[35].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK1;
Configure I/O Pins & PMUX
The first thing is to drive the output that is connected to the ADC- input to 0 V:
// Drive negative side of SDADC input
PORT->Group[1].DIRSET.reg = NEGDRV;
// Negative side is driven low, to provide GND reference to light sensor
PORT->Group[1].OUTCLR.reg = NEGDRV;
Next, set the ADC+ and ADC- pins as inputs and use the PMUX to select the SDADC function:
The following code sample performs this assignment:
#define SDADCIN1P_PIN (PIN_PB09 & 0x1F)
#define SDADCIN1N PORT_PB08
#define SDADCIN1N_PIN (PIN_PB08 & 0x1F)
// Disable Output Driver on the ADC+ pin
PORT->Group[1].DIRCLR.reg = SDADCIN1P;
// Use the PMUX to select the function for the ADC+ pin
PORT->Group[1].PINCFG[SDADCIN1P_PIN].reg = PORT_PINCFG_PMUXEN;
// Disable Output Driver on the ADC- pin
PORT->Group[1].DIRCLR.reg = SDADCIN1N;
// Use the PMUX to select the function for the ADC- pin
PORT->Group[1].PINCFG[SDADCIN1N_PIN].reg = PORT_PINCFG_PMUXEN;
// Choose SDADC function for the ADC+ and ADC- pins
PORT->Group[1].PMUX[(SDADCIN1N_PIN>>1)].reg = PORT_PMUX_PMUXO(GPIO_PIN_FUNCTION_B) |
PORT_PMUX_PMUXE(GPIO_PIN_FUNCTION_B);
Sampling Frequency (CLK_SDADC)
To configure the SDADC module, several registers require the module to be disabled to write to them, so clear the CTRLA.Enable bit. You must wait for synchronization on this bit, otherwise register writes you make after that may not be accepted:
SDADC->CTRLA.bit.ENABLE = 0;
// Wait for synchronization
while(SDADC->SYNCBUSY.bit.ENABLE);
Register CTRLB holds settings for the Skip Count (which must be higher than 3), Prescaler (which is set to provide a CLK_SDADC (FS) of 125 kHz), and Oversampling Ratio (which is set to 1024 to provide OSR of ~122 samples/second):
SDADC->CTRLB.bit.SKPCNT = 4;
// Set CLK_SDADC to GCLK_1 / 2 (= 1 MHz / 2 = 500 kHz)
// Set FS = CLK_SDADC / 4 = 125 kHz
SDADC->CTRLB.bit.PRESCALER = 0;
// Oversampling Ratio - 1024
// Samples come out at FS/1024 = 125 kHz / 1024 (~122 Hz)
SDADC->CTRLB.bit.OSR = OSR1024;
Set VREF
The conversion is performed on a full range between 0 V and the reference voltage. Analog inputs between these voltages convert to values based on a linear conversion.
To set the voltage reference, you must set two different fields in REFCTRL:
REFCTRL.REFSEL[1:0] selects the source for the reference. For this example, the light sensor output voltage covers the full range from VDD to GND, so we want to set VREF to VDDANA (3.3 V).
REFCTRL.REFRANGE[1:0] needs to be set based on the voltage level used for the reference. The required setting is the 2.4 V - 3.6 V value since VDDANA is 3.3 V.
The following code sample performs this assignment:
SDADC->REFCTRL.bit.REFSEL = SDADC_REFCTRL_REFSEL_INTVCC_Val;
// 2.4v < Vref < 3.6v
SDADC->REFCTRL.bit.REFRANGE = 2;
Step 1.5 Set Correction Values
Typically, you would measure a GND input voltage level and the value you receive from that would be used to set the offset correction value in OFFSETCORR), then you would measure a full-scale input value and use that to set the Gain correction value (in GAINCORR. To keep this example simple, we did NOT do any of that. The result is calculated in a 24-bit value, but the output is really only 16-bit, so we will use the shift correction register (SHIFTCORR) to automatically shift the result down by eight bits into a 16-bit field:
SDADC->GAINCORR.reg = 1;
// Wait for synchronization
while(SDADC->SYNCBUSY.bit.GAINCORR);
// Set Offset Correction to 0
SDADC->OFFSETCORR.reg = 0;
// Wait for synchronization
while(SDADC->SYNCBUSY.bit.OFFSETCORR);
// Set Shift Correction to 8: 24-bit->16-bit
SDADC->SHIFTCORR.reg = 8;
// Wait for synchronization
while(SDADC->SYNCBUSY.bit.SHIFTCORR);
Set Input Channel
There are three differential inputs available to the SDADC, so we need to select which pair is connected to the module via the INPUTCTRL register.
In this example, the Light Sensor signal is connected to input "1" on the mux (AIN1):
SDADC->INPUTCTRL.bit.MUXSEL = 1;
// Wait for synchronization
while(SDADC->SYNCBUSY.bit.INPUTCTRL);
Enable The Module
At this point, the SDADC module is configured, so now the module can be enabled:
SDADC->CTRLA.bit.ENABLE = 1;
// Wait for synchronization
while(SDADC->SYNCBUSY.bit.ENABLE);
Reading Results
Start Conversion
Now the SDADC module can be easily triggered to sample and convert the input voltage. To do this, first, you need to clear the INTFLAG.RESRDY flag.
SDADC->INTFLAG.reg = 0;
Then you set the SWTRIG.START bit to trigger the conversion.
SDADC->SWTRIG.bit.START = 1;
Wait for RESRDY Flag
Then wait for the INTFLAG.RESRDY bit to be set, indicating that the result is ready.
while (!SDADC->INTFLAG.bit.RESRDY);
Read/Shift the Result
Finally, read the SDADC result from the RESULT register into a variable. Recall in this example, we configured the SDADC module to automatically shift the raw 24-bit result into a 16-bit result, so we do not need to perform any shifting in our application.
The following code sample adds a NOP() instruction, so that you can set a breakpoint there to check the converted value:
result = SDADC->RESULT.reg;
// Place Breakpoint Here
__NOP();
Learn More
- Sigma-Delta ADC Overview
- Learn more >
- Sigma-Delta vs SAR ADCs
- Learn more >
- Sigma-Delta ADC Example Project
- Learn more >