3、数据采集基础:ADC采样原理、采样率与分辨率、噪声抑制基础

做智能照明,说白了就是让传感器替我们「看」环境。但传感器输出的都是模拟信号——电压、电流、电阻变化。单片机不认识这些,它只认0和1。所以,ADC(模数转换器)就成了连接物理世界与数字世界的桥梁。

这一节,我带你从实战角度把ADC吃透。不扯太深的理论,重点讲清楚采样原理、采样率与分辨率怎么选,以及怎么把噪声压下去。这些都是我在项目里踩过坑、填过土的经验。

3.1 ADC采样原理:从模拟到数字的「翻译」过程

ADC的核心任务很简单:把连续的模拟电压,转换成离散的数字值。但这个过程里藏着不少门道。

我习惯把ADC比作一把「电子尺子」。这把尺子有固定的量程,比如0~3.3V。尺子上有刻度,刻度数量取决于分辨率。比如10位ADC,就有2^10=1024个刻度。每个刻度对应一个数字值。

采样过程分三步:

  • 采样:在某个时间点,把模拟电压「拍个快照」并保持住。
  • 量化:把保持的电压与参考电压比较,找到最接近的刻度值。
  • 编码:把刻度值转换成二进制数字,送给单片机。

嗯,这里要注意一个关键点:采样不是连续的。它是在时间轴上打点,每个点之间有时间间隔。这个间隔决定了你能采集多快的变化信号。

核心公式:

数字值 = (模拟电压 / 参考电压) × (2^分辨率 - 1)

举个例子:参考电压3.3V,12位ADC,输入1.65V,那么数字值 = (1.65/3.3) × 4095 ≈ 2047

我在项目中遇到过一个问题:某次用光敏电阻做环境光检测,ADC读数总是跳来跳去。后来发现是参考电压没选好,用了内部参考,结果电源纹波直接耦合进去了。换成外部精密参考后,数据就稳了。

3.2 采样率与分辨率:一对需要权衡的「冤家」

采样率和分辨率,是ADC选型时最纠结的两个参数。它们就像跷跷板的两头——一头高了,另一头就得低。

3.2.1 采样率:你拍照片的速度

采样率就是ADC每秒采样的次数,单位是SPS(Samples Per Second)。比如1000 SPS,就是每秒采1000个点。

为什么采样率重要?因为奈奎斯特采样定理告诉我们:要还原一个信号,采样率至少要是信号最高频率的2倍。比如你要采集50Hz的工频干扰,采样率至少得100 SPS。

但实际项目中,我建议留3~5倍余量。为什么?因为真实信号不是完美的正弦波,还有谐波成分。我曾经用刚好2倍采样率去采一个PWM调光信号,结果波形完全失真,根本看不出占空比。后来把采样率提到10倍,才看清细节。

实战建议:

智能照明场景中,环境光变化很慢(秒级),50~100 SPS就够用。但如果你要做人体感应或PWM调光反馈,建议至少1k SPS以上。

3.2.2 分辨率:你尺子的精细度

分辨率决定了ADC能分辨的最小电压变化。8位ADC有256个刻度,12位有4096个,16位有65536个。

计算公式很简单:最小分辨率 = 参考电压 / 2^分辨率

举个例子:参考电压3.3V,8位ADC的最小分辨率是3.3/256 ≈ 12.9mV。12位则是3.3/4096 ≈ 0.8mV。

你想想看,如果你要检测光敏电阻从亮到暗的微小变化,8位ADC可能只能分出几十个等级,画面会非常「粗糙」。而12位就能分出上千个等级,调光过渡就平滑多了。

分辨率 刻度数 最小分辨率(3.3V参考) 适用场景
8位 256 12.9 mV 开关检测、粗略亮度判断
10位 1024 3.2 mV 普通环境光检测
12位 4096 0.8 mV 精密调光、颜色传感器
16位 65536 0.05 mV 高精度测量、微弱信号

我个人习惯,做智能照明传感器,至少选12位ADC。8位太粗糙,16位又贵又慢,12位是性价比甜点。

3.2.3 采样率与分辨率的权衡

这里有个残酷的现实:高分辨率往往意味着低采样率。因为ADC内部需要时间去做比较和转换,分辨率越高,转换时间越长。

比如STM32的12位ADC,最快能跑到1M SPS。但如果你要16位精度,就得用外部ADC,速度可能降到100k SPS甚至更低。

怎么选?我的经验是:先确定信号带宽,再选采样率。采样率定下来后,在满足速度的前提下,尽量选高分辨率。

注意:不要盲目追求高分辨率!如果你的传感器本身噪声就有10mV,那12位ADC的0.8mV分辨率就是浪费。分辨率再高,也测不出比噪声更小的信号。

3.3 噪声抑制基础:让ADC读数「稳如老狗」

ADC最怕什么?噪声。明明环境光没变,读数却像心电图一样上下跳。这种情况我见得太多了。

噪声来源主要有三个:

  • 电源噪声:开关电源的纹波、数字电路的串扰
  • 传感器噪声:光敏电阻的热噪声、运放的1/f噪声
  • 环境干扰:50Hz工频、射频干扰

我曾经做一个调光项目,ADC读数在暗光下跳了±50个LSB。排查了两天,最后发现是LED驱动器的PWM频率正好落在ADC采样频段内。改了采样时序后,问题解决。

3.3.1 硬件层面的噪声抑制

硬件是基础,硬件没做好,软件再折腾也白搭。

  • 去耦电容:ADC电源引脚附近放一个0.1μF陶瓷电容,再加一个10μF电解电容。这是标配,别省。
  • 模拟地与数字地分离:用0欧电阻或磁珠单点连接。我见过有人直接铺铜连一起,结果数字噪声全灌进模拟部分了。
  • RC低通滤波:在ADC输入引脚前加一个RC滤波器,截止频率设为信号最高频率的3~5倍。比如环境光信号最高10Hz,截止频率设30~50Hz就够。

RC滤波器设计小公式:

截止频率 f = 1 / (2π × R × C)

比如R=10kΩ,C=1μF,那么f ≈ 16Hz。这个组合很适合滤除工频干扰。

3.3.2 软件层面的噪声抑制

硬件做完了,软件还能再补一刀。我常用的方法有几种:

1. 多次采样取平均

最简单也最有效。连续采N次,去掉最大最小值,再取平均。N一般取8~16次。注意:采样次数太多会降低响应速度。

// 中位值平均滤波法(防脉冲干扰)
#define FILTER_N 12
uint16_t adc_filter(void) {
    uint16_t buf[FILTER_N];
    uint16_t sum = 0;
    uint16_t min, max;
    
    // 连续采样
    for(int i=0; i<FILTER_N; i++) {
        buf[i] = adc_read();
    }
    
    // 找最大最小值
    min = buf[0];
    max = buf[0];
    for(int i=1; i<FILTER_N; i++) {
        if(buf[i] < min) min = buf[i];
        if(buf[i] > max) max = buf[i];
    }
    
    // 求和(去掉最大最小)
    for(int i=0; i<FILTER_N; i++) {
        sum += buf[i];
    }
    sum -= min + max;
    
    return sum / (FILTER_N - 2);
}

2. 软件低通滤波(一阶滞后滤波)

适合变化缓慢的信号,比如环境光。公式很简单:

// 一阶低通滤波
#define ALPHA 0.2  // 滤波系数,越小越平滑
static uint16_t last_value = 0;

uint16_t adc_lowpass(uint16_t new_value) {
    uint16_t result = (uint16_t)((float)last_value * (1 - ALPHA) + (float)new_value * ALPHA);
    last_value = result;
    return result;
}

3. 采样时序同步

如果系统里有PWM调光或开关电源,ADC采样要避开噪声尖峰。我习惯在PWM的「关断时间」中间段采样,这时候噪声最小。

避坑指南:我曾经在项目中直接用ADC的连续转换模式,结果读数被电源纹波调制得一塌糊涂。后来改成定时触发采样,并且触发时刻避开PWM边沿,数据瞬间就干净了。

3.3.3 一个完整的噪声抑制流程

在实际项目中,我通常这样组合使用:

  1. 硬件上:RC低通滤波(截止频率10~50Hz)+ 去耦电容
  2. 软件上:中位值平均滤波(N=8)+ 一阶低通滤波(α=0.1~0.3)
  3. 时序上:避开PWM边沿,在噪声低谷期采样

这样一套组合拳下来,ADC读数基本能稳定在±1~2个LSB以内。对于智能照明来说,这个精度完全够用了。

好了,ADC的基础就讲到这里。下一节我们聊聊具体的传感器——光敏电阻、环境光传感器、人体红外传感器,看看它们怎么跟ADC配合,把物理世界的信号变成数字世界的「语言」。

本节要点回顾:

  • ADC是把模拟电压转成数字值的「电子尺子」
  • 采样率决定你能采多快的信号,分辨率决定你能看多细
  • 采样率和分辨率需要权衡,12位ADC是智能照明的甜点
  • 噪声抑制要硬件+软件双管齐下,别指望单方面解决
  • 滤波算法选型要看信号特点:慢变信号用低通,有脉冲干扰用中位值平均