☰
24bit DAC频响获取异常排查:从数据通路到测量端的完整思路
2026/10/24 2:44:31 网站建设 项目流程

做音频SoC方案调试的朋友大概都有这种经历:代码能跑、声音也能响,但拿测试软件去抓频响曲线,结果跟参考设计差了十万八千里。我这次遇到的问题,就是杰理平台的24bit DAC频响获取异常。现象很典型:从板子的模拟输出端录音,跑一遍20Hz到20kHz扫频,低频往下塌,中高频段像水波纹一样起伏,个别频点还冒出莫名其妙的尖峰。前前后后调了三块类似板子,最后发现根因分布在数据通路、DAC寄存器设置和测量工具三个层面,真正出在DAC本身的问题反而很少。这篇文章把排查过程、整改方法和平时的避坑习惯整理出来,给正在调24bit模式、遇到频响曲线不平整的朋友一个参考。

1. 项目背景与问题现场

1.1 这个异常是怎么暴露出来的

先说现场情况。板子上用的处理器内部集成了一颗24bit DAC,外部经过耳机功放输出。功能测试阶段一切正常:播放1kHz正弦没问题,播放音乐也没听出明显底噪,听起来完全是块“正常板子”。但一旦进入电声指标验证环节,问题就藏不住了。

测试方法很常规:电脑播放一个20Hz到20kHz的对数扫频文件,DAC模拟输出接到声卡Line-In录音,录音完成后对数据做FFT,计算每个频点的功率,再以1kHz为0dB基准做归一化,得到频响曲线。这一套流程很多人都在用,问题在于测出来的曲线非常难看:

  • 20Hz到100Hz这段,响应相对1kHz掉了差不多4~6dB,低频明显塌陷;
  • 1kHz到8kHz之间不是一条平线,而是呈波浪状起伏,峰峰幅度大约有2.5dB;
  • 用24bit配置采集,-60dB小信号测试时,频谱上多出一堆毛刺,看起来像量化噪声和测量底噪混在了一起;
  • 左右声道的曲线还不完全重合,中高频段差了将近1dB。

这几个现象单看哪一个都不算离谱,但叠加在一起就足以让整条响应曲线“不合格”。总监开品质会的时候,第一句就问:这DAC是不是没有调好?实际上,DAC本身在24bit模式下标准输出是相当平的,问题出在“拿到的数据”有偏差。这就是我标题里强调的“获取异常”,而不是“DAC异常”。

1.2 为什么频响异常值得单独写一篇

频响是音频系统最基础、最客观的指标。扬声器、功放、DAC、ADC,任何一个环节出问题,最终都会反映在这条曲线上。对于产品开发来说,频响不平直意味着量产一致性不达标,也很难过客户的验收标准。

更麻烦的是频响异常的排查链条特别长。从软件里的PCM数据到DAC引脚上的模拟信号,中间隔着数据格式、DMA搬运、数字滤波、音量控制、模拟耦合电容、运放电路,最后还要经过声卡和FFT工具才能变成一条曲线。任何一个环节配置错误,测出来的曲线都会“有罪”。我第一次碰到这个项目时,也一度怀疑是板子Layout问题,后来反复测才发现根源是软件里一个16bit搬运配置和测量端的采样率不匹配。

这篇内容适合谁看?正在调杰理或其他国产音频SoC的朋友,做音频产品硬件测试的工程师,以及想搞清楚24bit和16bit在调试上到底有什么差异的人。看完之后,至少能少走我当初走过的弯路。

2. 先拆解:24bit DAC频响测试链路

2.1 频响到底是什么,怎么测

频响全称是频率响应,指系统增益随信号频率的变化。你可以把它理解成一个水龙头在不同频率下的“口径”:理想状态下,所有频率的水流都应该一样大;但实际电路对低频和高频会有不同程度的“阻塞”,于是测出来的曲线就会起伏。

测量方式有好几种。工程上最常用的是对数扫频法。生成一个从20Hz线性或对数变化到20kHz的正弦扫频信号,经过DAC播放,在输出端录音,然后用FFT把录音转换到频域,提取每个频点的幅度。还有一种做法是用单一频率逐点测量,比如在100Hz、200Hz、500Hz、1kHz、2kHz、5kHz、10kHz、20kHz各发一个正弦波,分别记录幅度,把点连起来。逐点法更准确但慢,扫频法效率高但需要对FFT结果做峰值搜索,处理不当容易引入窗函数泄漏误差。

判断一条频响曲线是否合格,主要看三点:通带内波动是多少、-3dB带宽在哪、两个声道是否一致。一般消费类音频产品要求20Hz到20kHz内波动不超过±1dB,再好一点的要求±0.5dB。我这次调试的目标就是后者。

2.2 完整链路拆解:四个环节缺一不可

一条频响曲线从数据到最终显示,至少要经过四个环节,我把它们列出来:

环节主要成员对频响的影响
数据源音频文件、解码器、DMA搬运位宽截断、数据错位、符号错误
数字接口I2S/TDM配置、BCLK/LRCK极性数据对齐错误,中高频段幅度变形
DAC数字域插值滤波、音量寄存器、静音控制通带波动、整体增益偏移
模拟链路耦合电容、运放、负载阻抗低频滚降、高频跌落、带外噪声
测量端声卡ADC、采样率、FFT工具混叠、量化底噪、显示假异常

调试时最容易犯的错是只盯着DAC本身看,实际上任何一个环节都会在最终曲线上“背锅”。举个最简单的例子:如果你的声卡采样率是44.1kHz,而DAC工作在48kHz,那么高频段信号就会被错误采样,FFT结果看起来像高频响应异常下陷,其实DAC输出一点问题没有。

2.3 24bit模式下为什么更容易出“获取异常”

很多人不理解,同一个DAC,用16bit模式测频响正常,切到24bit模式就出问题,这是为什么?其实不是DAC变差了,而是24bit模式对配置和测量环境更敏感。

第一个坑是位宽处理不一致。24bit有效数据在传输时通常放在32bit槽里,采用左对齐、右对齐或I2S延迟对齐等方式。如果主控按右对齐发送,DMA按左对齐取用,或者只取了16bit,高字节有效位就丢了,波形幅度变小、失真增加。在FFT上表现为整体增益偏低、底噪抬升,感觉像“频响获取异常”。

第二个坑是量化底噪暴露。24bit模式下,工程师习惯测试更小的信号,比如-60dBFS、-80dBFS。此时测量端的ADC若只有16bit分辨率,量化台阶大约在-96dBFS,信号本身的电平已经接近量化噪声,频谱上自然出现毛刺。这个问题在16bit模式下不明显,因为被测信号本来就在0dBFS附近。

第三个坑是滤波器和采样率的叠加。很多DAC在24bit模式下会启用数字插值滤波器,在接近奈奎斯特频率的地方自带轻微滚降。如果用48kHz采样,20kHz处轻微回落是正常的。但如果你外接的测量设备采样率不一致,或者驱动里开了两级滤波器,滚降就会被放大,看起来像DAC高频衰减异常。

这三个坑合在一起,导致24bit模式的频响测试比16bit模式更容易暴露各种配置错误。

3. 异常排查:从现象倒推根因

3.1 数字通路检查:位宽、槽位与DMA

排查异常我习惯从数字通路开始,因为这一层最好验证,也不用动用示波器。

先看DMA搬运配置。24bit数据在内存里是三个字节,但很多DMA控制器只支持8bit、16bit或32bit搬运。如果用16bit半字搬运,就必须考虑字节序和有效位的位置。最常见的错误是把低16位搬走了,高位字节丢掉,导致波形严重失真。检查方法很简单:把DMA搬运到内存里的数据直接读出来,用十六进制打印第一帧左右声道的采样值,跟原始PCM文件对比。如果发现符号位没了,或者数据整体往低位缩,那就是搬运配置的问题,频响不管怎么测都平不了。

然后再看I2S槽位配置。24bit数据在32bit槽里通常放在高位(左对齐),MSB在LRCK翻转后的第一个或第二个BCLK边沿出现。如果发送端和接收端对这个边沿的理解不一致,数据就会错位半个BCLK。低频段受相位偏差的影响很小,看起来幅度几乎不变;但中高频段会出现明显的波浪状起伏。我这个项目里的1kHz到8kHz波浪,最后定位出来就是LRCK极性和数据对齐方式不匹配导致的。

我在实践中发现一个非常管用的手段:数字回环。先把DAC输出断开,在驱动里把I2S的接收数据源配置为回环模式,或者直接把发送给DAC的同一个DMA buffer再送给录音线程,播放扫频文件并同时录音,然后对回环数据做FFT。如果回环数据的频响曲线是平直的,说明数据通路没问题,问题在DAC模拟段或测量端;如果回环曲线也是波浪状,那问题就一定在数字侧。这个办法能把排查范围缩小一半以上。

3.2 DAC寄存器配置检查:音量、滤波与静音

数字通路确认没问题之后,接着查DAC寄存器。

音量控制是最容易被忽略的。很多DAC芯片的音量寄存器默认值不是0dB,而是带了一个固定的数字衰减,比如-6dB甚至-12dB。这个不算太大的衰减在0dBFS测试时看不出来,因为系统还有其他增益;但做频响归一化时,所有频点都以1kHz为基准,整体衰减会被“扣除”,看似不影响曲线。麻烦的是,有些固件在初始化过程中会把音量寄存器写成一个“随机”值,导致左右声道增益不对称,曲线在中高频段出现分离。

数字滤波器设置也值得细看。部分DAC支持选择插值滤波器类型,比如快速滚降和慢速滚降。前一种适合音频回放,后一种对高频更友好,但带外噪声高。如果驱动里默认选了慢速滚降,高频段响应会微微凸起;选了快速滚降,近20kHz处会有略微跌落。这本身不算异常,但如果你拿同一套数据去对比不同板子,就会误认为是硬件差异。

静音控制和模拟旁路更坑。我遇到过一次频响低频段异常抖动,最后发现是DAC的静音引脚悬空,内部静音开关在半开半闭状态,导致输出信号在低电平时被周期性切断。这种现象在FFT上表现为低频段杂散噪声增加,响应曲线往下掉。检查方法很简单:用万用表量静音引脚电平,确认不是悬空或浮空。

以我的经验,DAC寄存器配置最好的验证方式是先把配置寄存器组完整读回来,和SDK默认值逐一对比。不要只看SDK里写的是什么,芯片上电后寄存器实际跑成什么样,必须读出来才算数。

3.3 模拟链路检查:耦合电容与地回路

数字侧查完,再看模拟侧。

低频端下陷是耦合电容的典型症状。DAC模拟输出通常经过一个隔直电容连接到后级。电容和后级输入阻抗构成一个高通滤波器,其-3dB截止频率约为 f = 1 / (2πRC)。如果电容是10µF,后级输入阻抗是10kΩ,截止频率是1.6Hz,完全没问题;但很多板子为了省面积,用1µF甚至0.47µF,接10kΩ负载时截止频率会升到16Hz甚至更高,20Hz处的响应自然往下掉。

高频端的波浪则要重点检查地回路。DAC、功放、声卡如果接在不同的电源地线上,地回路会引入几十赫兹的工频干扰和射频分量。FFT上表现为低频段离散尖峰加上高频段不规则起伏。区分方法很简单:看波形。真正的高频滚降是平滑下斜线,而地回路干扰会在固定频点上出现尖峰,两者在频谱上长得完全不一样。

还有一个容易被忽略的点:测试时带了耳机负载或接了后级功放板。负载阻抗太高或太低都会改变DAC输出的实际工作点。比如DAC输出级设计是针对32Ω耳机优化的,你直接接一个10kΩ高阻输入,输出级工作在小电流区,线性变差,高频段会出现轻微失真和响应偏移。所以测频响时要固定负载条件,别一会带载一会空载。

3.4 测量端检查:声卡、FFT与参考基准

这个环节我放在最后,但实际踩坑概率也很大。

声卡位深是第一个大坑。24bit DAC测试需要24bit或32bit的录音链路。如果你用的声卡是16bit,-60dBFS的信号已经接近其量化底噪,测出来全是毛刺;-80dBFS就更不用说了。这种情况下得到的频响曲线根本不是DAC的真实响应,而是声卡ADC的量化噪声特性。解决方案要么换支持24bit录音的声卡,要么降低测试电平要求,或者给信号加dithering。

采样率匹配同样重要。声卡采样率要跟DAC的采样率严格一致。如果DAC工作在48kHz、声卡设置在44.1kHz,数字混叠会让高频段出现大量杂散分量。我见过有人拿着这种数据说DAC高频严重衰减,后来把声卡切到48kHz,曲线立刻恢复正常。这属于“获取方式”导致的假异常,和DAC本身完全无关。

FFT分析时的窗函数和平均次数也会影响结果。扫频信号是用连续变化的频率激励的,FFT是近似估计频点能量,需要用合适的窗函数(比如汉宁窗)来减少频谱泄漏。如果直接做矩形窗或单次FFT,曲线中会出现锯齿状抖动。还有一点,归一化基准不要用播放文件的理论电平,而要用实际录音里1kHz频点的实测幅度。因为声卡输入电平设置不同,播放路径可能有衰减,基准不对整条曲线都会平移。

测量端的自检方法很简单:拿一个已知平坦的参考信号源(比如数字音频发生器)直接进声卡录音跑一遍同样的分析流程,如果得到的是平直曲线,说明测量链路干净;如果参考信号都测不平,那就先把测量端修好再谈DAC。

4. 实操复现与整改记录

4.1 最小复现环境的搭建

为了让问题可复现,我搭了一个最小测试环境。整个环境只需要三样东西:装了测试固件的DAC板子、一台支持24bit录音的USB声卡、一台跑分析脚本的电脑。

测试流程分成四步:

  1. 准备一个24bit/48kHz的对数扫频WAV文件,频率范围20Hz到20kHz,扫频时长10秒,满量程-6dBFS;
  2. DAC板播放这个文件,声卡在Line-In录音,也设为24bit/48kHz;
  3. 录音完成后,脚本对录音做分帧FFT,提取各频点幅度;
  4. 以1kHz实测幅度为0dB基准,画出20Hz到20kHz的频响曲线。

Python分析脚本的骨架大概是这样的:

import numpy as np import wave def analyze_freq_resp(rec_wav, ref_freq=1000.0): with wave.open(rec_wav, 'rb') as w: fs = w.getframerate() data = w.readframes(w.getnframes()) # 24bit数据:三字节有符号,低字节在前 raw = np.frombuffer(data, dtype=np.uint8).reshape(-1, 3) val = (raw[:, 0].astype(np.int32) | (raw[:, 1].astype(np.int32) << 8) | (raw[:, 2].astype(np.int32) << 16)) val = np.where(val >= (1 << 23), val - (1 << 24), val) x = val / (1 << 23) # 分段FFT后按频率提取峰值幅度,fft_freqs为主频点数组 # 具体实现略,核心是取每个目标频点±1Hz范围内的最大幅度 fft_mag = compute_sweep_magnitude(x, fs) ref_db = 20 * np.log10(np.max(fft_mag[ref_freq - 2: ref_freq + 2])) resp_db = 20 * np.log10(fft_mag) - ref_db return resp_db

这个脚本不复杂,核心思路就是“同一个FFT流程既能分析DAC输出,也能分析回环数据”,保证对比时算法一致。

4.2 分层定位法:数字回环优先

搭建好环境之后,第一步不是急着调DAC,而是先跑一次数字回环。

我在驱动里把I2S接收数据源配置为内部回环,也就是TXD发送的数据直接回到RXD接收端。播放扫频WAV的同时,把回环数据采下来,用同一个分析脚本跑一遍频响。回环数据里的DAC模拟电路不存在,只有纯数字信号。如果回环曲线平直,说明PCM数据、DMA、I2S时序这一整条数字链路都是对的;如果回环曲线波浪状,那就直接锁定数字侧。

我这次做回环时发现,回环曲线的中高频段同样有一点波浪,幅度比最终输出的略小。这立刻说明数据通路本身就有问题,DAC模拟段最多是“加重”了问题,而不是根源。后来我把DMA搬运宽度从16bit改成32bit、将槽位配置改为标准I2S左对齐,回环曲线就完全平直了。

这一步的关键价值在于:它把所有“看起来像模拟问题”的异常先做了筛分。如果回环数据已经不正常,你就不需要去动烙铁、换电容,纯软件就能解决很大一部分问题。

4.3 整改点与代码修改

根据回环结果,我做了以下几处修改。

第一处是DMA搬运宽度。原来驱动里用16bit半字搬运,处理24bit数据时只保留了低16位,丢掉了高字节有效位和符号扩展。整改后改为32bit字搬运,软件端再把高8位移至正确位置。仅仅这一处修改,回环曲线就恢复了平直。

第二处是数据格式寄存器配置。原来的配置在32bit槽里使用了右对齐方式,这与接收端默认的左对齐解析不匹配。整改后统一为左对齐、MSB先行,并设置了LRCK极性。这部分修改用伪代码表达如下:

/* 配置DAC数字接口:I2S格式,32bit槽位,24bit有效数据,左对齐 */ dac_config_t cfg; cfg.format = DAC_FMT_I2S; cfg.slot_width = DAC_SLOT_32BIT; cfg.data_width = DAC_DATA_WIDTH_24BIT; cfg.align = DAC_ALIGN_LEFT; cfg.lrck_pol = 0; /* 帧时钟下降沿对齐MSB */ cfg.bck_pol = 0; /* 位时钟上升沿采样 */ dac_init(&dac, &cfg); /* 左右声道数字增益统一为0dB */ dac_set_gain(&dac, DAC_CH_LEFT, 0); dac_set_gain(&dac, DAC_CH_RIGHT, 0);

第三处是音量寄存器。固件初始化过程中音量寄存器被写成了一个带衰减的值,整改后每次开机都显式写入0dB并确认左右一致。

第四处是测量环境。声卡原来设置在44.1kHz/16bit,我改为48kHz/24bit,并跟DAC时钟锁定,避免采样率失配带来混叠。

几处整改前后的对比记录如下:

项目整改前整改后
回环曲线中高频波浪约±1.2dB20Hz-20kHz平直,波动<±0.2dB
DAC输出低频20Hz相对1kHz下跌约4.5dB下跌约0.6dB
中高频1k-8k波动±2.5dB±0.3dB
-60dB小信号底噪毛刺明显,接近-70dB底噪低于-90dB
左右声道一致性高频差1dB差0.1dB以内

这里要特别说明:整改后低频20Hz仍然有约0.6dB的下垂,这是模拟输出端隔直电容的正常滚降,不是异常。判断合格时要把“AC耦合带来的低频自然滚降”考虑进去,不要追求20Hz处完全0dB。

4.4 复测结果与判定标准

整改完成后,我重新跑了一遍完整的扫频测试。20Hz到20kHz整条曲线都落在±0.5dB以内,左右声道基本重合,-60dB小信号测试时频谱干净,没有散落的毛刺。

经验是:频响测试不能只看曲线“平不平”,还要看“在什么条件下测的”。我后来再做同类项目时,都会先固定一套测量基线,包括声卡型号、采样率、输入电平、扫频文件、分析脚本,全部固定之后再谈调音。别人拿不同声卡、不同采样率测出来的曲线,根本没对比价值。

5. 常见问题速查与避坑清单

5.1 频响异常现象速查表

调试时遇到频响问题,可以先对照下表找方向:

现象大概率方向优先排查
全频段整体上移或下移数字增益、音量寄存器读回全部寄存器配置
低频20-100Hz下陷耦合电容太小、电池内阻计算RC高通截止频率
1k-8kHz呈波浪状I2S对齐、LRCK极性、DMA错位回环验证数字通路
高频15kHz以上平滑滚降插值滤波器、声卡采样率检查采样率和滤波器选型
高频有离散尖峰毛刺地回路、电源纹波、时钟抖动看频谱是否离散窄峰
小信号底噪严重测量声卡位深不够换24bit声卡或降低测试要求
左右声道曲线分离音量寄存器左右不一致显式写0dB并回读
特定频点凸起或凹陷板级谐振、功放自激扫频看是否固定频点

这张表基本覆盖了我这些年碰到的同类问题。表中的“大概率方向”不是绝对结论,只是告诉你先往哪里查。

5.2 推荐排查顺序

排查频响异常,我自己的顺序固定为:配置快照、数字回环、寄存器修正、模拟链路、测量端确认。

具体步骤:

  1. 完整读回DAC所有配置寄存器,保存成快照,跟SDK默认值逐一比对;
  2. 做数字回环测试,确认数据通路是否平直;
  3. 如果不平直,改DMA位宽、格式对齐、时钟极性,每改一次跑一次回环;
  4. 回环平了之后,再去量模拟输出端的耦合电容、负载阻抗和电源纹波;
  5. 最后用声卡录音跑完整链路,同时对测量端做一个已知平坦信号的标准对照;
  6. 每个修改点只动一个变量,改完重新测,不要一次性改三处。

这个顺序的本质是把问题从“软件侧”到“硬件侧”逐步隔离。数字回环是成本最低、速度最快的隔离手段,我建议每个项目都优先做这一步。

5.3 几条实在的工程经验

最后分享几点这几年攒下来的习惯。

第一,测试环境必须先固化。频响是一个相对量,每次都以1kHz为基准,但如果测量端不稳定,昨天和今天测出来的曲线就可能差0.5dB。声卡、电脑、测试线材、扫频文件全部固定,对比才有意义。

第二,数字回环是排查利器。遇到任何频响异常,先不急着怀疑DAC,跑一次回环。回环平了,问题在模拟或测量;回环不平,问题在数字通路。这个习惯能让你少拆至少一半的板子。

第三,寄存器配置要读回,不要只看SDK代码。初始化代码里写的和芯片实际跑的不一定一样。上电复位、固件升级、异常复位都可能导致寄存器被改写。承认这一点,你的调试效率会高很多。

第四,每次只改一个变量。这个问题看起来简单,实际操作时人总是忍不住同时改三处配置然后期望一次成功。三个月后再复盘,你根本不知道是哪一处改动让曲线变平了。我吃过这个亏,后来强制自己每次只改一个配置项,并在代码注释里记录改动时间和原因。

第五,听感只能做辅助判断。耳朵能听出大问题,但听不出±1dB的频响波动。频响曲线是否合格,必须以测量数据为准。

这几年做下来,我的习惯是:凡是频响获取异常,第一反应永远不是怀疑DAC,而是先把数据通路和测量环境洗干净。很多看起来吓人的曲线,修完数据格式和采样率匹配之后,都变成了一根平得像镜子一样的直线。24bit DAC本身并不娇气,真正娇气的是从数据到测量这一路上的每一个配置。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询