ADC and DAC
ADC (Analog-to-Digital Converter) turns a continuous voltage into a discrete digital number. DAC (Digital-to-Analog Converter) does the reverse. These are the bridge between the analog physical world and the digital MCU.
Why It Matters
Temperature sensors, microphones, light sensors, battery voltage monitors — anything that produces a continuous voltage needs an ADC to be read by software. DAC output drives audio, control voltages for PID loops, and waveform generation.
How It Works
ADC: Sampling Theorem (Nyquist)
To faithfully digitize a signal of frequency f, you must sample at at least 2f (the Nyquist rate). Sampling below this causes aliasing — the signal appears as a lower frequency that cannot be distinguished from the real one.
Signal: 1 kHz sine wave
Minimum sample rate: 2 kHz (Nyquist)
Practical sample rate: 5-10x the signal frequency for clean reconstruction
An anti-aliasing filter (low-pass RC or active filter using Op Amps) must be placed before the ADC input to remove frequencies above Nyquist. Without it, high-frequency noise folds into the measurement band.
Resolution and LSB
Resolution determines how finely the ADC divides the voltage range:
| Resolution | Levels | LSB (at 3.3V Vref) |
|---|---|---|
| 8-bit | 256 | 12.9 mV |
| 10-bit | 1024 | 3.22 mV |
| 12-bit | 4096 | 0.806 mV |
| 16-bit | 65536 | 0.050 mV |
LSB (Least Significant Bit) = Vref / (2^N - 1). This is the smallest voltage change the ADC can detect.
Conversion formulas:
Digital value = (Vin / Vref) * (2^N - 1)
Voltage = digital_value * Vref / (2^N - 1)
Example: Vin = 1.65V, Vref = 3.3V, 12-bit
digital = (1.65 / 3.3) * 4095 = 2047
voltage = 2047 * 3.3 / 4095 = 1.649V
SAR Conversion Process
Most MCU ADCs use Successive Approximation Register (SAR) architecture:
1. Sample-and-hold captures Vin
2. Compare Vin to Vref/2 -> MSB = 1 if Vin > Vref/2, else 0
3. Compare Vin to next threshold -> bit 11 = ?
4. ... repeat for each bit (12 comparisons for 12-bit)
5. Result ready in SAR register
Total time = N clock cycles + sample time
SAR ADCs are fast (1-5 Msps on STM32), low power, and good enough for most embedded use. Delta-sigma ADCs are slower but achieve higher resolution (16-24 bit) for precision measurement.
ADC Configuration on STM32
Key configuration parameters:
- Channel: which analog pin (ADC_IN0 = PA0, ADC_IN1 = PA1, etc.)
- Sampling time: how many clock cycles the sample-and-hold capacitor charges. Longer = more accurate for high-impedance sources. STM32F4 offers 3 to 480 cycles per channel.
- Sequence: ADCs can scan multiple channels in order, storing results via DMA
- Trigger: software start, timer event, or external signal
// ADC1, channel 0 (PA0), 12-bit, software trigger
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
ADC1->CR1 = 0; // 12-bit (default), no scan
ADC1->CR2 = ADC_CR2_ADON; // power on ADC
ADC1->SMPR2 = (7 << 0); // 480 cycles sampling for CH0
ADC1->SQR3 = 0; // first conversion = channel 0
ADC1->SQR1 = 0; // 1 conversion in sequence
// Single conversion
ADC1->CR2 |= ADC_CR2_SWSTART; // start
while (!(ADC1->SR & ADC_SR_EOC)); // wait for end of conversion
uint16_t raw = ADC1->DR; // read 12-bit result (0-4095)
float voltage = raw * 3.3f / 4095.0f;Signal Conditioning
The analog signal path before the ADC matters as much as the ADC itself:
Sensor --> [Voltage divider] --> [Anti-alias LPF] --> [Buffer amp] --> ADC pin
(scale to 0-3.3V) (fc < Nyquist/2) (low impedance
drive for S&H)
- Voltage divider: scale 0-12V battery voltage to 0-3.3V range. See Voltage Current Resistance.
- Anti-aliasing filter: simple RC low-pass, cutoff below half the sample rate. See Capacitors and Inductors.
- Buffer amplifier: op-amp in voltage follower config. The ADC sample-and-hold capacitor needs a low-impedance source to charge quickly during the sampling window.
DAC (Digital to Analog)
Outputs a voltage proportional to a digital value. STM32F4 has two 12-bit DAC channels.
// DAC channel 1 (PA4), output 1.65V
RCC->APB1ENR |= RCC_APB1ENR_DACEN;
DAC->CR = DAC_CR_EN1; // enable channel 1
DAC->DHR12R1 = 2048; // 12-bit right-aligned: 2048/4096 * 3.3V = 1.65VDAC for Waveform Generation
Combine DAC with a timer interrupt to output a sine wave, sawtooth, or arbitrary waveform:
// 1 kHz sine wave using DAC + TIM6 interrupt
// Lookup table: 100 points per cycle -> TIM6 at 100 kHz
const uint16_t sine_lut[100] = { 2048, 2176, 2304, ... }; // precomputed
volatile uint8_t idx = 0;
void TIM6_DAC_IRQHandler(void) {
TIM6->SR &= ~TIM_SR_UIF;
DAC->DHR12R1 = sine_lut[idx];
idx = (idx + 1) % 100;
}PWM as a DAC Alternative
When no hardware DAC is available, PWM output through a low-pass RC filter approximates a DC voltage:
PWM (1 kHz, 50% duty) --> [10k R] --> [1uF C] --> ~1.65V DC
fc = 1/(2*pi*RC) = 16 Hz
The RC filter cutoff must be well below the PWM frequency to smooth the output. Trade-off: slower response vs. less ripple.
Related
- UART SPI I2C — many external ADCs/DACs communicate over SPI or I2C
- Op Amps — signal conditioning and buffering before ADC
- Capacitors and Inductors — anti-aliasing filter components
- Sensor Fusion — what to do with ADC data from multiple sensors
- PID Controller — DAC output drives actuators in control loops
- Voltage Current Resistance — voltage dividers for ADC input scaling