第3章:过采样率与信噪比

好,咱们进入正题。这一章要聊的,是过采样技术里最核心的一个关系——过采样率(OSR)和信噪比(SNR)。说白了,就是你多采了多少样,能换来多少性能提升。

我刚开始接触ADC时,总觉得“多采点样就能提高精度”是个很玄乎的说法。直到自己动手搭了个16位ADC的测试板,才发现这里面门道很深。嗯,今天咱们就把这层窗户纸捅破。

3.1 过采样率(OSR)的定义

先给个最直白的定义:

过采样率(OSR) = 实际采样频率(fs) / 奈奎斯特频率(2 × fmax

举个例子你就明白了。假设你关心的信号最高频率是1kHz,按奈奎斯特定理,至少要用2kHz采样。但如果你用16kHz去采,那OSR就是:

OSR = 16kHz / 2kHz = 8

我个人习惯把OSR理解成“冗余倍数”。你想想看,本来2倍就够了,你非要采8倍,多出来的6倍就是冗余。这些冗余不是白费的,它们能帮你换信噪比。

这里有个容易混淆的点——OSR和采样率不是一回事。采样率是绝对数值,OSR是相对倍数。我在项目中遇到过有人把这两个概念搞混,结果算出来的增益差了十万八千里。

3.2 OSR与SNR的关系公式推导

好,接下来是重头戏。为什么过采样能提高SNR?咱们一步步推。

先回忆一下量化噪声的基本特性。ADC的量化噪声可以近似为白噪声,它的功率谱密度是平坦的。总噪声功率是固定的,等于LSB²/12。

但注意——这个总功率是分布在0到fs/2的频带内的。如果你把采样率提高,噪声就被摊薄了,分布到更宽的频带上。

咱们用数学说话:

  • 奈奎斯特采样时,噪声带宽 = fs/2
  • 过采样时,噪声带宽 = OSR × (fs/2)

噪声功率谱密度(PSD)是:

PSD = (LSB²/12) / (fs/2) = LSB² / (6 × fs)

过采样后,你关心的信号带宽没变(还是fmax),但落在信号带宽内的噪声功率变成了:

Pnoise, in-band = PSD × fmax = [LSB² / (6 × fs)] × fmax

因为fs = 2 × OSR × fmax,代入得:

Pnoise, in-band = LSB² / (12 × OSR)

看到了吗?噪声功率和OSR成反比。OSR每翻一倍,带内噪声功率减半,也就是降低3dB。

那SNR怎么变?信号功率不变,噪声功率降低,所以:

SNROS = 6.02N + 1.76 + 10log₁₀(OSR) [dB]

这就是过采样带来的SNR增益公式。我当年推导到这里时,心里咯噔一下——原来每4倍过采样,才能换来6dB的SNR提升,也就是1位有效位数。

经验之谈: 很多初学者以为过采样能无限提高精度。其实不然,因为ADC本身还有热噪声、时钟抖动等非理想因素。我做过一个测试,OSR超过256后,SNR提升就明显变慢了。

3.3 理论增益计算

咱们直接上表格,看看不同OSR下的理论增益:

OSR SNR增益(dB) 等效位数提升
1(奈奎斯特) 0 0
2 3.01 0.5
4 6.02 1.0
8 9.03 1.5
16 12.04 2.0
32 15.05 2.5
64 18.06 3.0
128 21.07 3.5
256 24.08 4.0

从表格里能看出一个规律:OSR每4倍,SNR提升6dB,等效位数提升1位。这个关系很实用,你可以在设计初期快速估算需要多少过采样率。

举个例子。假设你有一个12位ADC,想要达到14位的有效分辨率。那需要提升2位,也就是12dB的SNR增益。查表可知,OSR需要16倍。

但这里有个坑——理论增益是理想情况。实际电路中,ADC的模拟前端、参考电压噪声、PCB布局都会吃掉一部分增益。我曾经在一个项目中,理论计算OSR=16能提升2位,结果实测只提升了1.3位。排查后发现是电源纹波太大,把噪声基底抬高了。

避坑指南: 设计时建议留出20%~30%的余量。比如需要提升2位,实际按OSR=32来设计。别问我怎么知道的——吃过亏的人都会这么做。

3.4 一个简单的计算示例

咱们用代码来验证一下理论。下面是一个Python脚本,计算不同OSR下的理论SNR:

import numpy as np

def calc_snr_oversampling(N, OSR):
    """
    计算过采样后的理论SNR
    N: ADC位数
    OSR: 过采样率
    """
    snr_base = 6.02 * N + 1.76  # 奈奎斯特采样时的SNR
    snr_os = snr_base + 10 * np.log10(OSR)
    return snr_os

# 测试12位ADC,不同OSR
N = 12
for osr in [1, 4, 16, 64, 256]:
    snr = calc_snr_oversampling(N, osr)
    print(f"OSR={osr:3d}, SNR={snr:5.2f} dB, 等效位数={snr/6.02:.1f}")

运行结果:

OSR=  1, SNR=74.00 dB, 等效位数=12.3
OSR=  4, SNR=80.02 dB, 等效位数=13.3
OSR= 16, SNR=86.04 dB, 等效位数=14.3
OSR= 64, SNR=92.06 dB, 等效位数=15.3
OSR=256, SNR=98.08 dB, 等效位数=16.3

注意看,等效位数比N多了0.3位,这是因为1.76dB的常数项贡献的。实际工程中,我们通常只看有效位数(ENOB),它才是真正能用的分辨率。

好了,这一章的内容就到这里。核心就三句话:

  1. OSR定义:实际采样率除以奈奎斯特率
  2. SNR增益公式:10log₁₀(OSR) dB
  3. 4倍OSR换1位:这是工程上的黄金比例

下一章咱们聊聊具体的实现方法——怎么在代码里做累加和抽取。到时候我会分享一个我在STM32上踩过的坑,保证让你少走弯路。