Mastering STM32 ADC: From Sampling to Accurate Voltage Measurement
This article explains the fundamental concepts and step‑by‑step workflow of ADC conversion in embedded systems, covering sampling, hold, quantization, encoding, and output stages, and provides detailed STM32 HAL code examples along with tips for improving accuracy.
1. Basic Concept of ADC
ADC (Analog‑to‑Digital Converter) transforms continuous analog signals into discrete digital values so that a microcontroller can process real‑world measurements such as sensor voltages or battery levels.
For example, a temperature sensor may output any voltage between 0 V and 3.3 V, but the MCU can only handle discrete numbers like 0, 1, 2, 3. The ADC bridges this gap by converting, say, 2.5 V into a digital code such as 3100 (assuming a 12‑bit ADC with a 3.3 V reference).
2. Main ADC Conversion Process
2.1 Sampling Phase
The ADC first captures the input signal using an internal sampling capacitor. When the sampling switch closes, the capacitor charges until its voltage equals the input voltage. The required time, called the sampling time, directly affects conversion accuracy.
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; // 239.5 clock cycles
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
Error_Handler();
}Longer sampling improves accuracy but slows conversion; the optimal time depends on source impedance and required precision.
2.2 Hold Phase
After sampling, the switch opens and the capacitor holds the captured voltage, freezing the value for the remaining conversion steps. This prevents changes in the external signal from affecting the result.
2.3 Quantization Phase
Quantization maps the held analog voltage to a discrete digital code. A 12‑bit ADC divides the reference range into 4096 levels; each level represents ≈0.8 mV when Vref = 3.3 V. The smallest voltage step is the LSB, and quantization error is bounded by ±0.5 LSB.
Theoretical digital value = 1.65 V / 3.3 V × 4096 = 2048
Actual 1.6504 V → 2048.2 → 2048 after rounding
Actual 1.6508 V → 2048.5 → may become 2048 or 2049
2.4 Encoding Phase
Encoding converts the quantized number into binary code. For a 12‑bit result of 2048, the binary representation is 100000000000. Most STM32 ADCs use straight binary encoding; some devices support two's‑complement for signed measurements.
2.5 Output Phase
After encoding, the result is stored in the ADC data register and a conversion‑complete flag or interrupt notifies the CPU. The CPU can then read the value via polling, interrupt, or DMA.
3. STM32 ADC Practical Example
The following HAL‑based code demonstrates a complete single‑conversion workflow on an STM32 MCU.
// ADC initialization
void MX_ADC1_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; // 4‑fold prescale
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DMAContinuousRequests = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK) {
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
Error_Handler();
}
}
// Read ADC value
uint32_t ADC_Read(void) {
uint32_t value = 0;
HAL_ADC_Start(&hadc1);
if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK) {
value = HAL_ADC_GetValue(&hadc1);
}
HAL_ADC_Stop(&hadc1);
return value;
}
// Convert raw value to millivolts (Vref = 3300 mV, 12‑bit)
uint32_t ADC_ToVoltage(uint32_t raw) {
return (raw * 3300) / 4096;
}
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
while (1) {
uint32_t adc = ADC_Read();
uint32_t voltage = ADC_ToVoltage(adc);
printf("ADC Value: %lu, Voltage: %lu mV
", adc, voltage);
HAL_Delay(1000);
}
}This code shows the full flow: start conversion, poll for completion, read the result, convert to voltage, and repeat.
4. Factors Influencing ADC Accuracy
4.1 Reference Voltage Stability
Since the ADC result is a ratio to the reference voltage, any fluctuation in Vref directly causes measurement drift. High‑precision applications should use a dedicated voltage‑reference IC instead of the supply rail.
4.2 Source Impedance
A high source impedance slows the charging of the sampling capacitor, requiring longer sampling times. Keeping source impedance below ~10 kΩ or adding a buffer op‑amp improves accuracy.
4.3 PCB Layout and Grounding
Analog traces are sensitive to noise. Separate analog and digital grounds, use a single‑point ground, keep analog inputs short, and place decoupling capacitors near the ADC pins to reduce interference.
4.4 Software Filtering
Even with perfect hardware, the raw ADC result can jitter. Applying software techniques such as averaging, median filtering, or moving‑average smoothing mitigates this noise.
// Simple averaging filter
uint32_t ADC_Read_Average(uint8_t times) {
uint32_t sum = 0;
for (uint8_t i = 0; i < times; i++) {
sum += ADC_Read();
HAL_Delay(5); // 5 ms between samples
}
return sum / times;
}5. Summary
ADC conversion consists of five stages: sampling, hold, quantization, encoding, and output. Proper configuration of resolution, sampling time, and conversion mode, together with stable reference voltage, low source impedance, careful PCB layout, and optional software filtering, yields reliable and accurate measurements essential for embedded development.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
