2kHz 方波滤成正弦波要先算清源阻抗

2026-11-105
AI 快速预览详细 收起
本文介绍了一种解决2kHz方波通过带通滤波器输出很小问题的方法。通过555定时器和TL072运放组成的电路模型,详细解析了从Vo1到Vo2的信号传递过程。文章提供了电路调谐和源阻抗计算的具体步骤,并给出了Python代码以供复现。通过计算源阻抗和调谐反算,可以得到合适的电阻值和Q值,从而优化滤波器的输出。此外,还讨论了高Q值对谐波的影响以及上板前需要进行的检查。

方波频率对了,接上带通滤波器却只剩很小的输出,调电位器时又只有很窄的一段能用。遇到这种情况,先把输入网络算进去,往往比继续追着运放增益改阻值更有效。

下面以一组 555+TL072 电路参数为例,把从 Vo1 到 Vo2 的整条通道重新建模。实际完成的工作是节点求解、独立公式交叉检查、调谐反算和谐波预算;没有上板测量,也没有把理想模型的曲线当作示波器波形。

先沿着信号走一遍

同版 EDA 原理图整体

图1 工程 SCH Rev1.0 的实际 EDA 导出,保留 EDAmake、立创EDA 署名。左侧是可调方波发生器,右侧是带通滤波器。本文只验证右侧线性通道,不承诺按整板 BOM 直接装配即可达到指标。

Vo1 经 C2、R4 进入 R4/R5/R6 的交点,再经 R5 进入滤波器。RP3、C3、C8 也接在 R5 后面的节点上;R8 从运放输出反馈到反相输入。输出经 C4 耦合到 Vo2,R7 是输出侧的 1kΩ 负载。

带通滤波器原理图局部

图2 滤波器局部。RP3 图面仍显示 10kΩ,这是原图标注,不能把它理解为本文计算采用的有效电阻。

采用的数值为 R4=20kΩ、R5=8.2kΩ、R6=100kΩ、R8=16kΩ,C3=C8=100nF,C2=C4=10µF,R7=1kΩ。C3/C8 图面的“104”对应 100nF。

这里需要先处理一个会改变结论的冲突。SCH 中 RP3 显示和 Resistance 属性都是 10k,但型号字段是 3296W-1-101;Sim 中 RX3 显示“100”,其 Resistance 属性却是 100K。它们不能相互证明实物到底装了什么阻值。

所以,下文 Rp 一律指 RP3 接地端与接入节点之间的有效电阻,不是电位器铭牌总阻值,也不从“80%”推断实物电阻。准备复现时应断电测量,必要时隔离并联支路;采购前重新核对型号和量程。未经这个步骤,旋钮位置没有可比较的意义。

8.2kΩ 并不是滤波器看到的全部源阻抗

在 2kHz,10µF 的容抗约 7.96Ω。先将 C2 近似短路,Vo1 侧网络可以做戴维南等效。

开路等效电压系数为 k=R6/(R4+R6)=0.833333,R5 前面的等效电阻为 R4∥R6=16.667kΩ。因此,滤波器输入支路实际面对的是 Ra=R5+(R4∥R6)=24.867kΩ,同时输入电压先乘了 k。

只看 R5、R8 和两个相等电容,容易得到接近 1 的局部峰值增益。把源网络接回来,Vo2/Vo1 的峰值才是我们真正要比较的量。

令 s=j·2πf,理想运放的反相输入在交流小信号中跟随参考电压。R2/R3 的中点在本模型中视为交流地;这不等于把芯片 2 脚在实物上接到 GND。

忽略 C2/C4 的容抗时,从等效源到运放输出的传递函数为:

Hlocal(s)=−(R8·C3/Ra)·s / [R8·C3·C8·s²+(C3+C8)·s+1/Ra+1/Rp]

Vo1 到输出还需乘 k。由分母可得到:

f0=sqrt[(1/Ra+1/Rp)/(R8·C3·C8)]/(2π)

Q=2πf0·R8·C3·C8/(C3+C8)

|Hpeak|=k·R8·C3/[Ra·(C3+C8)]

代入这组数值,若希望中心频率约为 2kHz,反算得到 Rp≈39.64Ω、Q≈10.05、峰值增益约 0.2681,对应约 199Hz 的理想带宽。电位器调到“100Ω”本身并不代表中心频率已经对准 2kHz。

调频与加增益要分开处理

为了检查近似有没有掩盖问题,程序保留了 C2、C4 的复数阻抗,直接求解三个未知量:R4/R5/R6 交点 A、R5/RP3/C3/C8 交点 X、运放输出 O。

再用含 C2 的复数戴维南等效独立计算一次,在 1Hz~1MHz 的 151 个频率、5 个有效电阻上对比,共 755 点,最大相对差约 3.3×10⁻¹⁵。这个检查验证了两种实现的一致性,不代表真实 TL072 已被完整模拟。

含输入网络的频率响应计算

图3 同一组元件、同一理想模型的扫频结果。纵轴是 Vo2/Vo1,不是运放局部增益。

Rp 有效电阻 近似中心频率 2kHz 时的幅度比
100Ω 1260.76Hz 0.04366
39.64Ω 2000Hz 0.26809

在这个模型里,先调中心频率,2kHz 的输出幅度就能明显恢复。若输入已经对准通带而幅度仍不够,再讨论前级缓冲或后级增益;改 R8 会同时改变中心频率、Q 和增益,不能把它当作独立音量旋钮。

0.268 还是“同一频率分量”的传递比,不是方波峰峰值直接乘出来的正弦峰峰值。理想 0~V 的 50% 方波,基波峰值是 2V/π;应先算基波,再乘通道响应。

高 Q 也意味着不能只计算一次标称值。另作一组明确假设的容差预算:R4/R5/R6/R8 各±1%,C3/C8 各±5%,有效 Rp±5%,保持标称调谐值不重调。128 个端点组合的近似中心频率落在 1.850~2.171kHz;这些角点在固定 2kHz 的幅度比为 0.134~0.266。

这组范围来自人为设定的公差,不是原 BOM 的标称精度,也不是统计良率或连续参数域的严格最坏值证明。它说明逐板扫频和重新调谐有必要,直接复制旋钮位置并不可靠。

波形变圆以后还剩多少谐波

将输入定义为理想 0~1 脉冲,占空比 D 的第 n 次谐波幅度与 |sin(nπD)|/n 成正比。输出每次谐波还要乘 |H(nf)|,再用基波归一化。

本文计算 THD=sqrt(A2²+…+A99²)/A1,不包括噪声、运放非线性、555 边沿毛刺和电源串扰。

不同占空比的输出谐波计算

图4 Rp≈39.64Ω、输入频率 2kHz 时的线性谐波预算。50% 理想方波的偶次谐波为零,图中低于 −100dBc 的数值项按显示下限处理。

输入占空比 Rp≈39.64Ω Rp=100Ω
50% 1.338% 7.699%
60% 2.313% 12.213%
70% 3.996% 20.626%

这些是 2~99 次谐波的线性模型结果,不是整机实测 THD。把上限扩到 999 次,调谐且占空比 50% 的结果约为 1.33808%,与 99 次结果差约 0.000006 个百分点。对本模型而言,尾部截断已不是主要误差;对实物而言,削顶、共模越界和供电噪声仍可能完全改变结果。

先用这段代码复算自己的阻值

把下面完整代码保存为 mfb_audit.py,使用 Python 3.11 或更新版本,无第三方依赖。它与图2的位号对应,输入为 Vo1,输出为 Vo2;只修改实际量得的元件参数,不需要烧录固件。

默认运行 python mfb_audit.py。检查 100Ω 与 60% 占空比时运行 python mfb_audit.py --f 2000 --rp 100 --duty 0.6。更换电容或固定电阻时修改 BASE 后重新计算。

"""555/TL072 MFB small-signal audit, v1.0.0. Python 3.11+, stdlib only.

Independent analysis code. MIT License, Copyright (c) 2026.
The EDA reference circuit remains under its upstream GPL-3.0 license.
This ideal-op-amp calculation is not a measurement or a complete device model.
"""
import argparse
import cmath
import math

BASE = dict(r4=20000.0, r5=8200.0, r6=100000.0, r8=16000.0,
            c2=10e-6, c3=100e-9, c8=100e-9, c4=10e-6, r7=1000.0)


def linear_solve(matrix, rhs):
    """Complex Gaussian elimination with partial pivoting."""
    a = [list(row) + [b] for row, b in zip(matrix, rhs)]
    n = len(a)
    for k in range(n):
        pivot = max(range(k, n), key=lambda j: abs(a[j][k]))
        a[k], a[pivot] = a[pivot], a[k]
        if abs(a[k][k]) < 1e-30:
            raise ValueError("singular circuit")
        scale = a[k][k]
        a[k] = [v / scale for v in a[k]]
        for j in range(n):
            if j != k:
                scale = a[j][k]
                a[j] = [x - scale * y for x, y in zip(a[j], a[k])]
    return [row[-1] for row in a]


def response(f, rp, p=None):
    """Vo2 / Vo1; RP3 is its effective resistance to ground in ohms.

    A = R4/R5/R6 junction; X = R5/RP3/C3/C8 junction.
    Inverting input is AC ground. Unknowns are A, X, op-amp output.
    """
    if f <= 0 or rp <= 0:
        raise ValueError("frequency and resistance must be positive")
    p = BASE if p is None else p
    s = 2j * math.pi * f
    y = 1 / (p['r4'] + 1 / (s * p['c2']))
    g5, g8 = 1 / p['r5'], 1 / p['r8']
    c3, c8 = s * p['c3'], s * p['c8']
    rows = [[y + 1 / p['r6'] + g5, -g5, 0],
            [-g5, g5 + 1 / rp + c3 + c8, -c8],
            [0, c3, g8]]
    vo = linear_solve(rows, [y, 0, 0])[2]
    return vo * s * p['c4'] * p['r7'] / (1 + s * p['c4'] * p['r7'])


def tuning(f, p=None):
    """Approximate target RP3, Q and peak gain; C2/C4 treated as shorts."""
    p = BASE if p is None else p
    if f <= 0:
        raise ValueError("frequency must be positive")
    ra = p['r5'] + p['r4'] * p['r6'] / (p['r4'] + p['r6'])
    w = 2 * math.pi * f
    g = w * w * p['r8'] * p['c3'] * p['c8'] - 1 / ra
    if g <= 0:
        raise ValueError("target below the topology's tuning range")
    q = w * p['r8'] * p['c3'] * p['c8'] / (p['c3'] + p['c8'])
    gain = (p['r6'] / (p['r4'] + p['r6']) * p['r8'] * p['c3']
            / (ra * (p['c3'] + p['c8'])))
    return 1 / g, q, gain


def harmonic_thd(f, duty, rp, harmonics=99):
    """Linear-filter output THD, harmonic 2..99; excludes device distortion."""
    if not 0 < duty < 1:
        raise ValueError("duty must lie between 0 and 1")
    amps = [abs(math.sin(math.pi * n * duty) / n * response(n * f, rp))
            for n in range(1, harmonics + 1)]
    return math.sqrt(sum(a * a for a in amps[1:])) / amps[0]


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--f', type=float, default=2000.0)
    parser.add_argument('--rp', type=float)
    parser.add_argument('--duty', type=float, default=0.5)
    args = parser.parse_args()
    target, q, peak = tuning(args.f)
    rp = target if args.rp is None else args.rp
    h = response(args.f, rp)
    print(f'target_RP3={target:.4f} ohm  Q={q:.4f}  peak_gain={peak:.6f}')
    print(f'RP3={rp:.4f} ohm  |Vo2/Vo1|={abs(h):.6f}  phase={math.degrees(cmath.phase(h)):.3f} deg')
    print(f'linear_THD_h2_to_h99={100 * harmonic_thd(args.f, args.duty, rp):.4f}%')

默认运行的关键结果是 target_RP3=39.6417 ohm、Q=10.0531、|Vo2/Vo1|=0.268087、linear_THD_h2_to_h99=1.3381%。参数名中的 linear 是结果的一部分,记录数据时也应保留这个限定。

上板之前还要补齐哪些检查

同版 PCB 顶面布局

图5 同一工程的 PCB 顶面导出。主要走线位于底层,顶面的“跳线”标记需要结合网表逐根核对,不能因为底层覆铜看起来连成一片就省掉它。

同版 PCB 底面铜层

图6 从底面观察的底层铜。镜像的顶层丝印仅用于位置对照,不代表底面实际印有这些文字;此图不是打样文件。

先断电核对 TL072IP 的供电脚 8、4,以及本级信号脚 1、2、3。上电后检查二极管后的 VCC、R2/R3 中点和运放输出直流电位,再用小幅正弦扫频确定峰值位置;最后接入方波,分别记录频率、占空比、输入幅度和输出频谱。比较不同板子时,条件必须一致。

供电边界尤其不能由理想运放模型代替。TI SLOS080 Rev.W 的推荐条件对相应传统器件列出输入电压下限为 VCC−+4V;原图在 9~12V 输入后还串有二极管,低端供电时,VCC/2 与这个下限之间的裕量应按真实 VCC 检查。

数据手册传统 TL07xI 的直流、交流特性表大多以±15V、25℃等条件给出;不能把这些条件下的输出摆幅、失真数字直接搬到本板单电源、1kΩ 负载的场景。还要检查去耦、未用运放的处理、输出电流及高 Q 下有限开环增益造成的偏差。本文没有完成这些实体测试,也没有给出整板 ERC/DRC 或硬件达标结论。

工程与验证范围

参考工程入口及原始工程文件 https://oshwhub.com/course-examples/bo-xing-chan-sheng-yu-bian-huan-she-ji-555-yun-fang

工程页标示 GPL 3.0;原理图署名 EDAmake/立创EDA,SCH Rev1.0,2021-11-23。原理图、PCB 来自同一工程的实际 EDA 导出,保留源文件和署名;原理图仅作字体兼容处理及局部裁切,PCB 标明观察方向,未修改线网。

器件资料 TI TL07xx 数据手册 SLOS080 Rev.W https://www.ti.com/document-viewer/TL072/datasheet

本文独立完成理想小信号建模、调谐反算、代码与数值交叉核验;独立代码按 MIT 许可提供,原工程继续遵循 GPL 3.0。计算不含有限带宽、输出限流、器件非线性和电源噪声,硬件未改版、未上板。

平台的 CC BY 选项仅适用于本文独立文字;独立代码采用 MIT 许可,第三方工程与 EDA 图继续遵循上游 GPL 3.0,均不因平台选项改变许可。

技术交流,欢迎关注微信公众号:美男子玩编程。

创作许可协议

本项目采用 CC BY(署名) 进行许可。

评论(0)
- 没有更多了 -