Master STM32 ADC Sampling and Filtering: From Basics to Advanced Algorithms
This tutorial walks through STM32F103ZET6 ADC fundamentals, explains key parameters, demonstrates how to use the VOFA+ PC tool for data visualization, and provides step‑by‑step implementations of several filtering algorithms—including first‑order complementary, median, arithmetic average, moving average, limit‑average, and Kalman filters—complete with code examples and performance analysis.
This article provides a hands‑on guide for performing ADC sampling on an STM32F103ZET6 MCU using the HAL library and visualizing the results with the VOFA+ PC tool.
1. ADC Basics
Analog‑to‑Digital Converters (ADCs) transform continuous analog signals (e.g., temperature, humidity) into discrete digital values that can be processed by a microcontroller. Important ADC parameters include measurement range, resolution, sampling time, and sampling rate.
ADC parameters:
1) Measurement range – limits input voltage (e.g., 0‑3.3 V for STM32).
2) Resolution – smallest detectable voltage (e.g., 12‑bit gives 5 V/4096 ≈ 1.22 mV).
3) Sampling time – duration the internal hold circuit keeps the signal stable.
4) Sampling rate – number of samples per second.2. STM32 ADC Characteristics
STM32F1 series devices have up to three 12‑bit successive‑approximation ADCs with 18 channels (16 external, 2 internal). Conversions can run in single, continuous, scan, or discontinuous modes, and results can be left‑ or right‑aligned in a 16‑bit register.
Resolution = 3300 mV / 4095 ≈ 0.806 mV (full‑scale 3.3 V).The device separates regular and injected channel groups, allowing injected conversions to interrupt regular ones.
3. VOFA+ Overview
VOFA+ is a cross‑platform (Windows, Linux, macOS) PC application that communicates with the MCU via serial, TCP, or UDP. It supports custom protocols (CSV‑style FireWater, binary JustFloat, raw data) and provides widgets such as waveforms, buttons, LEDs, images, sliders, and a 3‑D cube.
4. Using VOFA+
Typical workflow:
Select serial port, baud rate, and other communication parameters.
Add a waveform widget, configure Y‑axis to display two input channels (e.g., I0 and I1), and open the serial connection.
Run the MCU firmware; VOFA+ will plot the incoming data in real time.
Example FireWater usage (CSV string):
#include "math.h"
#include "stdio.h"
int main(void) {
float t1 = 0, t2 = 0;
while (1) {
t1 += 0.1f;
t2 += 0.5f;
printf("simples:%f, %f
", sinf(t1), sinf(t2));
HAL_Delay(100);
}
}5. Filtering Algorithms and Results
Because the STM32 ADC has limited precision and stability, software filtering is often required. The article implements and compares several filters, showing VOFA+ screenshots for each.
5.1 First‑Order Complementary Filter
int firstOrderFilter(int newValue, int oldValue, float a) {
return a * newValue + (1 - a) * oldValue;
}
// Usage in main loop
HAL_ADC_Start(&hadc1);
Filtering_Value = firstOrderFilter(HAL_ADC_GetValue(&hadc1), ADC_value, 0.3f);
HAL_Delay(10);
printf("ADC_value:%d
", ADC_value);Result: modest improvement; still noticeable jitter.
5.2 Median Filter
int middleValueFilter(int N) {
int value_buf[N];
for (int i = 0; i < N; ++i) {
value_buf[i] = HAL_ADC_GetValue(&hadc1);
}
// Bubble sort
for (int j = 0; j < N-1; ++j) {
for (int k = 0; k < N-j-1; ++k) {
if (value_buf[k] > value_buf[k+1]) {
int temp = value_buf[k];
value_buf[k] = value_buf[k+1];
value_buf[k+1] = temp;
}
}
}
return value_buf[(N-1)/2];
}Result: effectively removes outliers and stabilizes the signal.
5.3 Arithmetic Average Filter
int averageFilter(int N) {
int sum = 0;
for (short i = 0; i < N; ++i) {
sum += HAL_ADC_GetValue(&hadc1);
}
return sum / N;
}Result: smooths the signal; larger N yields smoother output but reduces responsiveness.
5.4 Moving Average (Sliding Window) Filter
#define N 10
int value_buf[N];
int sum = 0;
int curNum = 0;
int moveAverageFilter() {
if (curNum < N) {
value_buf[curNum] = HAL_ADC_GetValue(&hadc1);
sum += value_buf[curNum];
curNum++;
return sum / curNum;
} else {
sum -= sum / N; // remove oldest contribution
sum += HAL_ADC_GetValue(&hadc1);
return sum / N;
}
}Result: provides smoother output than simple averaging while keeping memory usage modest.
5.5 Limit‑Average Filter
#define A 50 // amplitude limit
#define M 12
int data[M];
int LAverageFilter() {
int i, temp, sum = 0, flag = 0;
data[0] = HAL_ADC_GetValue(&hadc1);
for (i = 1; i < M; i++) {
temp = HAL_ADC_GetValue(&hadc1);
if ((temp - data[i-1] > A) || (data[i-1] - temp > A)) {
i--;
flag++;
} else {
data[i] = temp;
}
}
for (i = 0; i < M; i++) sum += data[i];
return sum / M;
}Result: eliminates sudden spikes while consuming more RAM.
5.6 Kalman Filter
int KalmanFilter(int inData) {
static float prevData = 0;
static float p = 10, q = 0.001, r = 0.001, kGain = 0;
p = p + q;
kGain = p / (p + r);
inData = prevData + (kGain * (inData - prevData));
p = (1 - kGain) * p;
prevData = inData;
return (int)inData;
}Result: offers good noise reduction and stability when parameters are tuned, though it requires careful calibration.
6. Conclusions
ADC sampling is essential in embedded projects, and software filtering can compensate for limited hardware performance. Simple filters (median, moving average) are often sufficient, while more sophisticated methods (Kalman) provide superior results at the cost of parameter tuning and memory usage. Choose the filter that best matches the dynamics of your specific application.
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.
