news 2026/10/26 1:06:33

24bit音频ADC与STM32 I2S/DMA采集实战:差分输入与高保真录音

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
24bit音频ADC与STM32 I2S/DMA采集实战:差分输入与高保真录音

说实话,第一次拿到 GC1843 这颗 24bit 立体声差分输入 ADC 的样品时,我第一反应不是兴奋,是怀疑——超小封装、模拟差分输入、数字 I2S 输出,这么小一颗真要扛起 96kHz/24bit 的音频采集,板子设计和软件链路都得重新调教。结果折腾了小半个月,把前端电路、I2S 对接、DMA 缓冲和 24bit 数据拼接这一整条链路全部跑通之后,我才敢说:这颗芯片适合需要高保真音频采集、又不想在板卡面积上妥协的项目。如果你是做便携录音、声学测量、语音唤醒的远场采集,或者想给 MCU 方案补上 Hi-Res 音频输入能力,这篇实录应该能帮你少走不少弯路。后面所有内容都基于我实际跑过的板子和录出来的文件,不搞 Datasheet 复读。

1. 为什么放弃 MCU 内置 ADC,非要外挂这颗音频采集芯片

经常有人问我:STM32 不是自带 ADC 吗,12bit 也够用吧,为什么非要多加一颗芯片、多铺一层电路?这个问题我在做这个项目之前也问过自己。用真实需求算一笔账之后,答案就很清楚了。

1.1 12bit SAR ADC 的短板

STM32 内部那个 12bit SAR ADC,理论量化信噪比大概是 72dB,这个数字怎么来的?20 × log10(2^12),也就是 12bit 位深在理想情况下能分辨的动态范围上限。72dB 什么概念?普通 CD 也只有 96dB 的理论动态范围,现场录音里一个管弦乐团从弱奏到强奏轻松超过 90dB。换句话说,用 12bit 去采声音,小信号直接埋进量化噪声里。

更麻烦的是,内部 ADC 做音频采样时有几个天生的毛病:采样保持电容会引入电荷注入,参考电压 VREF 往往和 MCU 的数字核心共用电源域,芯片内部的地弹和开关噪声会直接耦合进模拟信号。我在一个语音远场采集项目里试过直接用 STM32 内置 ADC 采 MEMS 麦克风,录出来的 50Hz 工频噪声和数字电路辐射噪声叠在一起,后期怎么滤波都救不回来。不是说内部 ADC 没用,而是场景不对——它更适合温度、电池电压这类慢变信号的采集,加上定时器触发和 DMA 确实很好用,但要达到音频级动态范围和失真指标,还是得交给专门的 Sigma-Delta ADC。

1.2 差分输入不是玄学,是抗干扰的硬需求

GC1843 最让我心动的是立体声差分输入。很多人不理解,说麦克风输出明明是单端的,为什么要接两根线?

差分输入的价值在于共模抑制。实际设备里,扬声器的瞬态电流、电源适配器的纹波、数字总线的串扰,都会在地平面上制造电位差。如果是单端输入,这些干扰原封不动进入 ADC 的参考地,最终叠加在采样结果里。而差分输入把信号表示为 L+/L- 两条线上的电压差,两条线同时被抬升的干扰就成了共模信号,会被 ADC 前端的差分放大器直接压掉。我在手持设备上实测过同样的麦克风信号,单端输入底噪比差分输入差了大概 6~8dB,这个差距人耳是能明确感知的“沙沙声”。

代价是接线多两条,但换来的是实打实的底噪改善。你要是做长距离走线、或者设备内部有开关电源,差分输入基本是必选项。

2. 芯片规格与实际选型:看的不是位数,是整套链路

2.1 24bit 到底能装下多少动态

先说个容易误解的点:24bit 分辨率不等于芯片实际性能就一定是 24bit 那么好。ADC 的理论量化信噪比是 6.02N + 1.76dB,24bit 算下来是 144dB,但这是纯量化级的极限,现实中任何音频 ADC 都做不到——模拟前端的热噪声、时钟抖动、电源噪声会先把底噪抬高。

我拿到的 GC1843 这颗,按典型规格看,动态范围大概能做到 100dB 上下,信噪比指标也在这个量级。这个性能对应什么水平?市面上一票百元级录音笔用的主力 ADC 也就是这个档位。实际做出来的文件,24bit/96kHz 下录一段钢琴,低电平的混响尾音能清晰保留下来;同场景换 16bit/44.1kHz,尾巴上的细节就被量化噪声糊掉了。

位深理论量化动态范围实际听感场景
12bit约 72dB勉强听清语音,音乐细节丢失明显
16bit约 96dBCD 标准,动态够用但低电平有底噪
24bit约 144dB(理论)Hi-Res 录音,留足后期动态余量

选 24bit 的另一个实用理由是调制余量。录音时你不可能永远把增益调到完美,信号一旦偏小,位深不够就直接切掉了低电平细节。24bit 相当于给信号链路留了一个很大的“安全垫”。

2.2 立体声差分接口与引脚规划

GC1843 的典型信号链是这样:每个声道一对差分输入(L+/L-、R+/R-),经过内部 PGA 和 Sigma-Delta 调制器,最后由数字滤波器输出 24bit PCM,通过 I2S 或左对齐格式送给 MCU 或 DSP。芯片对外接口大致包括这些:

  • AVDD/DVDD:模拟电源和数字电源,通常分开供电
  • VCOM:内部共模电压输出,给输入偏置用的
  • MCLK:主时钟,一般按 256fs 或 512fs 输入
  • BCLK:位时钟,I2S 总线的同步信号
  • LRCK:左右声道帧时钟,频率等于采样率
  • SDIN/DOUT:串行 PCM 数据输出

画原理图的时候我习惯先把电源和时钟引脚规划好,再排音频输入。差分输入的两对线尽量对称走线,不要让 L+ 走顶层、L- 穿两层板再回来,这样两个人的走线电感和寄生电容完全不对称,共模抑制会被自己亲手破坏掉。

2.3 超小封装是省空间还是坑

超小封装最大的卖点是省面积,GC1843 这类 QFN 封装的占板空间比传统 DIP/SOP 小一个数量级。但小封装对你焊接、布局、调试的要求是成倍上升的。

QFN 引脚间距经常只有 0.5mm 甚至更小,手工焊接时助焊剂稍微多一点、锡量稍微大一点,就会在引脚间搭桥。中间那个大散热焊盘通常还接地,焊不好会出现两种极端:要么中间焊盘没吃上锡导致地没接牢,要么锡膏挤出来把相邻引脚短路。我的做法是样机阶段用钢网开窗 50% 的锡膏印刷,加热台 280℃ 回流之后,再用热风枪补一轮,最后拿镊子逐个引脚推一下,看有没有回弹,回弹好说明焊点扎实。如果没有钢网条件,就老老实实用 TQFP 转接板调通软件,再落 QFN 大规模量产板。

小封装还有一层隐性优势:引脚到内部 die 的距离短,寄生电感小,对 ΣΔ ADC 这种高精度模拟器件其实更友好——前提是去耦电容必须贴着引脚放,别放远。我见过有人为了布局好看把 100nF 电容放到了 2cm 之外,结果底噪整整上了 10dB。

3. 前端电路与供电设计:决定底噪的 60% 都在这里

芯片选得再好,前端电路画得稀烂也是白搭。我一开始也想偷懒,直接抄评估板,后来发现评估板的布局在两层板上根本复现不了,被迫重新设计了一遍,反而把原理吃透了。

3.1 输入耦合与直流偏置

音频输入通常是交流耦合,因为信号源往往自带直流偏置,不能直接灌进 ADC。耦合电容和芯片输入阻抗组成了一个高通滤波器,截止频率是 f = 1/(2πRC)。比如耦合电容 10μF、芯片输入阻抗 8kΩ,算下来截止频率约 2Hz,对 20Hz 以下人耳几乎听不到的超低频有点衰减,但这是可接受的。我实际用的是 10μF 加 1kΩ 串联电阻的配置,低频响应够用,还能顺便限流保护。

GC1843 内部一般会把输入偏置到 VCOM 这个半电源点位,让信号在 ADC 满量程范围内摆动。这里有个容易忽略的点:VCOM 引脚的去耦电容要足够大,我习惯放 10μF 并联 100nF,否则输入共模电压不稳,直接表现为输出直流漂移。

如果用驻极体麦克风,别忘了它自带的 JFET 需要偏置电阻供电。输入信号幅度也要提前算好——线路电平大概 1Vrms 上下,MEMS 麦克风可能只有几十 mVrms,不做增益匹配的话,24bit 也只是在采噪声。

3.2 供电与去耦的实操参数

音频 ADC 对电源噪声极其敏感,这是我在这个项目里最深的教训。AVDD 不要直接从 MCU 的 3.3V 拉,哪怕它在物理上是相邻引脚也不行,必须用低噪声 LDO 单独供电。我用的是噪声指标在几十 μV 量级的 LDO,前面加磁珠做隔离,后面按 10μF、100nF、1nF 三颗电容并联去耦。三个电容覆盖不同频段:大电解管低频纹波,100nF 管中频,1nF 管高频尖峰,这种组合是音频电路的基本盘。

DVDD 通常可以低到 1.8V,单独一路 LDO,和 AVDD 分开。数字电源噪声再小也是方波开关出来的,跟模拟电源挨在一起必出问题。如果你手头只有一组电源轨,至少要加一个磁珠把数字侧和模拟侧切开。

3.3 接地和布局教训

接地是我最想重点说的事。很多新手喜欢把模拟地和数字地割成两块,结果在高频段愣是做出了一个大天线。音频 ADC 这类混合信号器件,最稳妥的做法是统一地平面,然后在芯片位置附近把 AGND 和 DGND 用星形汇合到一点。模拟输入走线区域上方不要铺数字地、不要走 I2S 高速线,至少留出三倍线宽的间距。

我第一版板子就是在差分输入走线正下方跑了一组数字信号线,实测底噪卡在 -78dBFS 上不去。后来把数字线绕开、完整地平面补回来,同样的芯片底噪掉到 -98dBFS 左右。这个差距在音频里是非常明显的,相当于从“能听但很脏”变成“干净”。

4. STM32 对接实录:I2S 引脚、DMA 缓冲与数据拼包

芯片侧讲完了,说说和 STM32 的对接。这部分踩的坑最多的不是原理,而是寄存器配置和数据的“字节对齐”问题。

4.1 接口信号与主从模式选择

GC1843 输出的是标准 I2S,有 MCLK、BCLK、LRCK、DOUT 这几根线。主从模式我推荐让 MCU 做主机:STM32 产生 MCLK、BCLK、LRCK,ADC 作为从机只吐数据。好处是采样率完全由 MCU 时钟决定,跟后面挂的 DAC、蓝牙音频模块都好同步。反过来让 ADC 做主机也行,但采样率就受限于外部晶振,一旦时钟源不过硬,数据流会产生慢漂移。

接线就是一一对应:MCU 的 I2S 时钟引脚接芯片 MCLK/BCLK,帧同步引脚接 LRCK,接收数据引脚接芯片的 DOUT。五根线,没有握手信号,协议本身不复杂,复杂的是时钟对不对。

4.2 I2S 外设配置要点与时钟树

用 STM32F4 系列举例,I2S 外设挂在 SPI 模块上,支持 16bit、24bit、32bit 数据长度。GC1843 输出 24bit 数据,但 I2S 协议里一个声道时隙往往是 32bit,所以要把 CHLEN(通道长度)设成 32bit、DATLEN(数据长度)设成 24bit。这个组合很多教程没讲透,直接用默认 16bit 去收,数据会错位,出来的音频是高频噪声。

时钟树是重点中的重点。96kHz 采样率对应 MCLK = 256 × 96kHz = 24.576MHz,BCLK = 64 × 96kHz ≈ 6.144MHz(左右双声道、每帧 32bit)。如果 MCU 的主晶振是 8MHz,PLLI2S 无论如何也得不到干净的 24.576MHz——除出来是个无限小数,时钟抖动直接变成采样噪声。所以音频项目最省心的方案是直接放一颗 24.576MHz 的有源晶振,或者用独立的音频时钟发生器。CubeMX 虽然能强行配出近似频率,但我实测那块的底噪会明显变差。

配置接口我用 HAL 库:设置 AudioFrequency = 96000,Standard = I2S_STANDARD_PHILIPS,DataFormat = 32bit,然后开 DMA 接收。这里注意,DMA 接收长度是按 32bit 字为单位算的,别按字节算,否则缓冲区长度差四倍,溢出了你自己都找不到原因。

4.3 DMA 乒乓缓冲与 PCM 数据组装

24bit/96kHz 立体声的数据吞吐量有多大?96000 × 2 声道 × 4 字节(按 32bit 时隙)≈ 768KB/s。这点带宽对 DMAC 不是事,但对缓冲区和存储介质有要求。我采用的 DMA 是一圈 2048 个字,用半传输中断和传输完成中断做乒乓处理:上半区在处理,下半区在接收,互相不打架。

收到原始数据后要做一次“拆包”:I2S 帧里的 24bit 数据是左对齐的,也就是有效位在 32bit 的高 24 位。取数据时要先右移 8 位,再按小端序拆成三个字节存进 WAV 或者 PCM 流:

uint32_t raw; int32_t sample24; uint8_t pcm[3]; raw = rx_buffer[i]; sample24 = (raw >> 8) & 0xFFFFFF; // 取低24位有效数据 // 小端模式写入24bit PCM pcm[0] = sample24 & 0xFF; pcm[1] = (sample24 >> 8) & 0xFF; pcm[2] = (sample24 >> 16) & 0xFF;

如果想直接转 16bit 存文件,也别直接砍掉低 8 位,最好先做一次抖动处理或者至少用舍入而不是截断,否则低电平信号会有明显的量化台阶感。我自己最终的录音文件是 24bit WAV 或经压缩存 FLAC,后期处理空间大得多。

5. 实测、滤波与文件处理的三级经验

软件链路跑通之后,真正的工作才开始。我从录到第一段音频到确认声音“能听”,中间折腾了三天,主要在做验证和工具链上的事。

5.1 先看波形再调参数

第一次上电别急着拿耳朵听,先录标准测试信号。我的做法是用手机或者信号发生器播放 1kHz 正弦波,音量控制在 -1dBFS 附近,录 5 秒,然后拖进 Python 或者音频软件看 FFT。主要看三样东西:1kHz 主峰是不是干净、附近有没有电源纹波的边带、底噪平台有多高。如果你用的是 CCS 这类 IDE,可以用图形查看器直接看缓冲区的原始波形,确认数据的幅值和极性是否正常。这个步骤十分钟就能发现八成问题,比如接线松动、时钟频率不对、数据全是 0 之类的低级错误。

5.2 数字滤波该做多少

热词里看到“C 语言 ADC 值滤波函数”这种搜索,估计不少朋友是从传感器 ADC 转向音频的。这里必须说清楚:音频信号链路千万不要套用传感器那套滤波逻辑。ΣΔ ADC 内部已经做了抽取滤波,输出的 PCM 是完整信号,你再套一个全局低通平均,只会把瞬态削掉,声音变得又闷又糊。

滤波在音频项目里的正确定位是针对性处理。比如 DC 偏移,用一阶 DC blocker 就够,公式是 y[n] = x[n] - x[n-1] + R × y[n-1],R 取 0.999 左右,只干掉直流不掉有效频段。再比如要去掉固定工频干扰,可以用窄带陷波器。至于那些滑动平均、中值滤波的代码,留给电池电压采样和温湿度传感器就够了,别用在音频上。

5.3 24bit/96kHz 文件的转换坑

录出来的 24bit/96kHz FLAC 文件,想转 ALAC 放移动设备里听,结果转出来要么报错、要么变成 16bit——这问题我遇到过,周围也有不少人栽了。原因大多数不是格式不支持,而是转换工具的默认采样格式是 16bit,或者没有显式指定位深。

用 ffmpeg 转的时候一定要显式指定参数:

ffmpeg -i input.flac -c:a alac -sample_fmt s32p -ar 96000 output.m4a

如果还是报错,先用 ffprobe 看看源文件的实际编码格式,有些第三方录音软件会把 24bit 数据包在 32bit 容器里,metadata 写得乱七八糟,转码器解析不到正确位深就罢工了。这类问题跟芯片本身没关系,纯粹是文件格式的“地雷”。

6. 这个项目让我印象最深的三次排障

最后分享三个我实际踩过、而且花了很久才定位的问题,每一个都值得你写进 checklist。

6.1 相位接反的诡异现象

有一次录出来的声音怎么听怎么别扭——人声发虚,低频乐器几乎消失了。查遍参数、滤波、时钟全都正常,最后用示波器一对波形才发现:其中一个声道的 L+ 和 L- 接反了。进一步说,如果两个声道都反相,单声道播放时人耳基本听不出来,但左右声道的反相会给混音带来严重的相位抵消,低频直接消失。更隐蔽的情况是只有一个声道接反,听感像声音“从头顶飘出来”,毫无低频。所以原理图上一定标清 L+/L- 极性,打样前逐路核对差分线的走线,别想当然。

6.2 冷焊与虚焊的排查

小封装 QFN 的另一个坑是冷焊。某天设备上电后 I2S 数据全是 0,但寄存器配置看着全对。我拿示波器量 MCLK、BCLK 都有,LRCK 也有,唯独 DOUT 上是一片死寂。后来用万用表量 AVDD 才发现,芯片的供电引脚和焊盘之间虚接,接触电阻忽大忽小。把芯片拆下来重新植锡、加热台重焊一遍,再推引脚确认回弹,问题立刻消失。教训是 QFN 焊接不要迷信加热台一次成功,必须加目检和回弹测试。

6.3 一个晚上才发现的采样率配置错误

最隐蔽的问题出现在时钟树。当时改过一次 CubeMX 配置,重新生成工程后采样率从 96kHz 悄悄变成了 48kHz,但软件里 AudioFrequency 还写着 96000。结果录音音调整体低了八度,像磁带机慢放。排查到后半夜,拿示波器一量 LRCK 频率——正好是 48kHz,才知道 CubeMX 重新生成时把时钟分频改了。从那以后我每次改配置都会做两步验证:示波器量 LRCK 是不是等于目标采样率,以及 BCLK 是不是等于 64 倍采样率。这两根线是对的,I2S 时序就基本没错。

做音频采集这种项目,最忌的是一味堆芯片参数而忽略了整条信号链路。GC1843 确实给了 24bit 分辨率和差分输入,但录出来的底噪、声场、动态,最终还是由前端电路的每一个焊点、每一颗去耦电容、每一段走线决定的。我个人现在的习惯是:画板之前先规划电源和接地,调板时先用示波器量准时钟,最后才去关心位深和文件格式。顺序反过来,你会在噪声和错位里浪费大量时间。这半个月折腾完,我反倒觉得,做高保真采集最难的不是看懂 Datasheet,而是管住硬件每一个“看起来无关紧要”的细节。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 1:06:13

Spring Boot考务管理系统:排考算法、冲突检测与Excel导入实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/26 1:05:48

AUTOSAR网络管理启动时间优化:从NM状态机到BSW调度的实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/26 1:02:01

ESP32-S3 Dev Module Arduino开发实战:环境搭建、外设驱动与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/26 1:01:58

HTML/CSS/JS 示例源码:DOM、布局与响应式实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华