本篇继 [[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多通道采集]] 的基础上修改代码】。
- 修改数组大小:
- 假设我们要对每个通道取 10 次采样的平均值。因为你有 2 个通道,所以数组大小应改为 $2 \times 10 = 20$。
- DMA 配置(无需修改):
- 保持之前的 Circular(循环模式) 和 Memory Increment。DMA 会自动按照 CH0, CH1, CH0, CH1 … 的顺序把 20 个数据填满。
3. 代码实现
我们需要在 main.c 中修改数组定义和读取逻辑。
第一步:定义更大的缓冲区
|
|
第二步:启动采集
|
|
第三步:计算平均值(算法核心)
由于 DMA 存放的顺序是交替的,我们需要跳着加和:
|
|
滤波后数据:
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)完成,计算速度极快!