ZZZ-19-ADC采样数据滤波

本篇继 [[ZZZ-18-ADC多通道采集]]。你可能会发现传感器传回来的数据:即便你不动传感器,串口打印出的电压值最后两位可能还在不停跳动

这是由于环境噪声、电源波动等引起的 ADC 采样噪声。在工业级仪器中,我们通常不会直接使用单次采样的结果,而是需要一个“数字滤镜”。

这里我们学习滑动平均值滤波 (Moving Average Filter),让 DMA 一次搬运更多的数据(比如每个通道搬 10 个数),然后求平均值。

1. 滤波原理:以多胜少

原理非常简单:单次采样可能会由于瞬时干扰产生误差,但如果我们连续采 10 次甚至 100 次,取它们的平均值,那些随机的噪声就会互相抵消。

数学公式表示为:$$V_{avg} = \frac{1}{N} \sum_{i=1}^{N} V_i$$

2. 硬件与配置调整

为了实现这个功能,我们需要让 DMA 这个“快递员”一次多搬一些货【这里在 [[ZZZ-18-ADC多通道采集]] 的基础上修改代码】。

  1. 修改数组大小:
    • 假设我们要对每个通道取 10 次采样的平均值。因为你有 2 个通道,所以数组大小应改为 $2 \times 10 = 20$。
  2. DMA 配置(无需修改):
    • 保持之前的 Circular(循环模式) 和 Memory Increment。DMA 会自动按照 CH0, CH1, CH0, CH1 … 的顺序把 20 个数据填满。

3. 代码实现

我们需要在 main.c 中修改数组定义和读取逻辑。

第一步:定义更大的缓冲区

1
2
3
4
5
6
/* USER CODE BEGIN PV */
#define SAMPLES 10  // 每个通道采样 10 次
#define CHANNELS 2 // 共 2 个通道

uint16_t adc_buffer[SAMPLES * CHANNELS]; // 长度为 20 的数组
/* USER CODE END PV */

第二步:启动采集

1
2
3
4
5
6
7
8
/* USER CODE BEGIN 2 */
// 为了精准,建议先进行 ADC 校准(F1系列特有_
HAL_ADCEx_Calibration_Start(&hadc1);

// 核心命令:启势 ADC + DMA
// 参数:ADC句柄,存储的目标地址,要搬运的数据个数
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, SAMPLES * CHANNELS);
/* USER CODE END 2 */

第三步:计算平均值(算法核心)

由于 DMA 存放的顺序是交替的,我们需要跳着加和:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
while (1)
{
    uint32_t sum_A = 0;
    uint32_t sum_B = 0;

    // 累加各个通道的数据
    for (int i = 0; i < SAMPLES; i++)
    {
        // adc_buffer[0, 2, 4...] 是通道 A
        sum_A += adc_buffer[i * CHANNELS]; 
        // adc_buffer[1, 3, 5...] 是通道 B
        sum_B += adc_buffer[i * CHANNELS + 1];
    }

    // 计算平均值
    uint16_t avg_A = sum_A / SAMPLES;
    uint16_t avg_B = sum_B / SAMPLES;

    // 打印转换后的电压
    printf("Stable PA0: %.3fV | Stable PA1: %.3fV\r\n", 
           (float)avg_A * 3.3 / 4096, (float)avg_B * 3.3 / 4096);

    HAL_Delay(200);
}

滤波后数据:

4. 滤波前后的对比图

通过这种方式,数据流转的逻辑发生了微妙的变化:

graph TD A[ADC 硬件] -- "快速采样(1kHz)" --> B[DMA 搬运工] B -- "填充" --> C["adc_buffer[20] (原始数据)"] C -- "循环覆盖" --> C subgraph "CPU 软件处理" D[for 循环累加] --> E[除以次数] E --> F[得到稳定的平均值] end C -.-> D F --> G[串口/OLED显示] style C fill:#fff2cc,stroke:#d6b656 style F fill:#d5e8d4,stroke:#82b366,stroke-width:2px

5. 进阶小贴士:为什么选 10 次?

  • 次数越多越稳:如果你取 100 次平均,数值会像粘在屏幕上一样稳。
  • 副作用:次数越多,数值对真实电压变化的响应就会变慢(滞后感)。
  • 黄金法则:在工业仪表中,通常选择 8、16 或 32 次采样。为什么选 2 的次方? 因为在单片机中,除以 8 可以直接通过“位移操作”(>> 3)完成,计算速度极快!
Licensed under CC BY-NC-SA 4.0