前言
最近我有一个项目需要用到带音量控制功能的耳机音频功率放大器芯片。在群友的推荐下,我选择了NS4268,但测试后发现它的噪声性能较差。于是,我制作了测试PCB,对其噪声特性进行简单测量。
测试PCB
测试使用的PCB如下图所示。NS4268被配置为耳机输出模式,输入输出均使用偏脚型SMA连接器。电源由锂电池提供,\(V_{dd} = 3.7V\),通过XH系列连接器连接。输出通过同轴线连接示波器,同时并联飞线到两个鳄鱼夹,以连接3.5mm接口的耳机。
测试数据
用锡短接输入端。通电后,旋转电位器,使芯片退出静音。可以听到输出有明显噪声。
修改示波器设置为:交流耦合,带宽限制20MHz。调整水平时基,使采样率为50MSaps,存储深度14Mpts。可以计算出FFT的频谱分辨率为\(\Delta f = \frac{f_s}{N} =\frac{50M}{14M}\approx 3.57Hz\),可以在避免混叠的同时保证后续操作的精度。此时,示波器界面如下图所示。
在示波器的存储菜单中,选择数据范围为内存,并保存数据为csv文件。
数据处理
本节使用Python对数据进行处理,需要使用 numpy、scipy、matplotlib 等Python库。为了安装方便,我直接使用了Anaconda的虚拟环境,其中默认安装了这些库,安装方法可自行搜索。
使用numpy读取文件,并用matplotlib绘制成图,如下图所示。
在读取数据后,使用scipy提供的快速傅里叶变换(Fast Fourier transform, FFT)函数,将信号从时域变换到频域,并使用帕塞瓦尔定理(Parseval's theorem)求出20-20kHz内信号的均方根(Root mean square, RMS)值。
离散傅里叶变换(Discrete Fourier transform, DFT)下的帕塞瓦尔定理如下:
设 $ x[n] $ 是一个长度为 $ N $ 的离散序列,其DFT为 $ X[k] $,则序列在时域的能量等于其在频域的能量,即:
由此可知,信号的RMS值可由下式求出:
通过改变参与求和的\(X[k]\),即可实现对信号进行滤波,从而求解该信号在某个频段内的分量的RMS值。
根据公式完成代码,可绘制出该噪声信号的频域图如下
并得出该信号在20Hz到20kHz的RMS值为
而在1Hz到80kHz的RMS值为
芯片的数据手册中并未给出\(V_{DD} = 3.7V, R_L = 32 \Omega\)时的噪声性能,故使用5V下的数据近似计算。THD+N vs Output Power图如下图所示。
设总谐波失真\(THD=0\),则此时TND+N可视作全部由噪声提供。取输出功率\(=10mW\)、\(THD+N(\%)=0.02\)。故:
看起来误差很大。但可以确定的是,该功率放大器的噪声性能非常差劲。
附录
绘制时域波形的代码如下。
import matplotlib.pyplot as plt
import numpy as npplt.rcParams["font.family"] = "Source Han Sans SC" # 设置中文字体
plt.rcParams["font.weight"] = "normal" # 设置字体加粗
plt.rcParams["font.size"] = 12 # 设置字体大小
plt.rcParams["axes.unicode_minus"] = False # 解决负号显示问题# 读取数据
filename = "接了耳机的噪声50Meg.csv"print("正在读取数据...")
# 只读取第二列数据(电压),跳过前2行标题
data = np.loadtxt(filename, delimiter=",", skiprows=2, usecols=(0, 1))[:, 1]# 生成时间轴 (根据CSV中的Start和Increment信息)
start_time = -1.400000e-01
increment = 2.000000e-08
time = start_time + np.arange(len(data)) * incrementplt.figure(figsize=(12, 5))
plt.plot(time * 1000, data * 1000, linewidth=0.8) # 转换为ms和mV
plt.title("PA噪声波形 (时域)")
plt.xlabel("时间 (ms)")
plt.ylabel("电压 (mV)")
plt.xlim(time[0] * 1000, time[-1] * 1000)
plt.grid(True, alpha=0.3)# 先保存图片,再显示
print("正在保存图片...")
plt.savefig("PA_noise_waveform.png", dpi=300, bbox_inches="tight")
print("正在显示图表...")
plt.show()
计算RMS值并绘制频域图的代码如下。
import matplotlib.pyplot as plt
import numpy as np
from scipy.fft import fft, fftfreqplt.rcParams["font.family"] = "Source Han Sans SC" # 设置中文字体
plt.rcParams["font.weight"] = "normal" # 设置字体加粗
plt.rcParams["font.size"] = 12 # 设置字体大小
plt.rcParams["axes.unicode_minus"] = False # 解决负号显示问题# 读取数据
filename = "接了耳机的噪声50Meg.csv"print("正在读取数据...")
# 只读取第二列数据(电压),跳过前2行标题
data = np.loadtxt(filename, delimiter=",", skiprows=2, usecols=(0, 1))[:, 1]# 生成时间轴 (根据CSV中的Start和Increment信息)
start_time = -1.400000e-01
increment = 2.000000e-08
time = start_time + np.arange(len(data)) * increment# 进行快速傅里叶变换
yf = fft(data)# 获取FFT的频率轴
xf = fftfreq(len(yf), increment)# 由于FFT结果是对称的,我们只取一半进行绘图
N = len(yf) // 2
yf_half = yf[:N]
xf_half = xf[:N]# 转换为单边频谱并归一化
yf_normalized = yf_half / N * 2# 绘制频域图
plt.figure(figsize=(12, 5))
plt.plot(xf_half / 1e6, np.abs(yf_normalized) * 1000, linewidth=0.5
) # 频率转为MHz,幅度转为mV
plt.title("PA噪声频谱 (频域) - 归一化幅度")
plt.xlabel("频率 (MHz)")
plt.ylabel("幅度 (mV)")
plt.xlim(0, xf_half[-1] / 1e6)
plt.grid(True, alpha=0.3)# plt.tight_layout()# 根据帕斯瓦尔定理,求20Hz到20kHz的RMS值freqMin = 20
freqMax = 20000freqMask = (xf_half >= freqMin) & (xf_half <= freqMax)# 计算20Hz到20kHz的RMS值
rms_value = np.sqrt(sum(np.abs(yf[np.hstack((freqMask, freqMask[::-1]))]) ** 2)) / len(yf
)print(f"20Hz到20kHz的RMS值: {rms_value:.6e} V = {rms_value * 1000000:.3f} uV")# 显示RMS值
plt.text(0.2,0.9,f"20Hz-20kHz RMS: {rms_value * 1000000:.3f} uV",fontsize=20,fontweight="normal",
)# 先保存图片,再显示
print("正在保存图片...")
plt.savefig("PA_noise_result.png", dpi=300, bbox_inches="tight")
print("正在显示图表...")
plt.show()