☰
ES8311音频Codec时钟树配置详解:MCLK、PLL与分频计算实战
2026/9/25 6:38:07 网站建设 项目流程

做嵌入式音频项目,最容易被忽略又最让新人头疼的就是 codec 的时钟树配置。ES8311 这颗低功耗音频编码芯片,我前后在三个项目里用过,每次出问题几乎都出在时钟上:有时是播放变调,有时是录音带噪声,查到最后都是分频系数不对。这篇文章就把 ES8311 的时钟树配置和分频计算一次讲透,从 MCLK 到内部 ADC/DAC 时钟,再到 I2S 的 BCLK/LRCK,全部用实际算例走一遍。

如果你是刚接触音频 codec 的嵌入式开发,或者已经在用 ES8311 但还在靠抄别人的初始化数组碰运气,这篇应该能帮你把“为什么这么配”这块补上。看完之后,拿到一块新板子、一个非常规 MCLK 频率,你也能自己算出来该怎么写寄存器。

1. 先看懂ES8311的时钟拓扑:MCLK从哪来、分频到哪去

1.1 MCLK到内部时钟树的三段路径

ES8311 的时钟系统说简单很简单,说复杂也挺复杂,关键是看你从哪个粒度去理解。我习惯把它拆成三段看:

第一段是外部输入的 MCLK,这颗芯片的 MCLK 通常由主控 SoC 提供,常见频率有 12.288MHz、24.576MHz、22.5792MHz、11.2896MHz,也有直接给 19.2MHz 或 13MHz 这种非音频专用晶振的系统。MCLK 进来之后不是直接拿去用,而是要经过一段“时钟管理”逻辑,决定是走 PLL 还是走直通。

第二段是芯片内部的时钟源选择。ES8311 允许你选择直接从 MCLK 分频得到 ADC/DAC 的工作时钟,也允许先把 MCLK 送进内部 PLL,倍频/分频到一个更合适的频率,然后再分频。这一步是时钟树配置的核心,也是大多数人抄初始化数组却不知道所以然的地方。

第三段是分频输出。从 PLL 或 MCLK 得到的那个“干净”的高频时钟,会被分配到 ADC 时钟路径和 DAC 时钟路径,经过各自的分频器,最终变成 ADC 调制器、DAC 调制器和数字滤波器的时钟。

弄懂这三段,后面对各种配置就会清晰很多。很多人出问题,就是因为直接把三段混在一起,以为只要设一个 MCLK 频率就完事了。实际上,ES8311 手册里时钟相关的寄存器之所以有那么多位域,正是因为三段路径分别可配。

1.2 从机模式与主机模式下,分频责任在哪里

还有一个很容易踩坑的点:ES8311 在 I2S 总线上可以工作在从机模式,也可以工作在主机模式。这两种模式下,BCLK 和 LRCK 的产生方式完全不同,时钟配置的关注点也不一样。

从机模式下,BCLK 和 LRCK 由主控 SoC 提供,ES8311 只需要在外部 MCLK 的帮助下产生自己 ADC/DAC 内部需要的工作时钟。也就是说,LRCK 的频率就是采样率 fs,这个 fs 是主控给的,ES8311 内部时钟树要做的,是保证 MCLK 分频出来的一路时钟能在时序上稳定地和 BCLK/LRCK 匹配。此时你在寄存器里配的 ADC/DAC 分频,主要影响的是内部调制器工作频率,而不是直接决定 LRCK。

主机模式下,ES8311 自己产生 BCLK 和 LRCK 输出给外部 Codec 或 MCU。这时,MCLK 或 PLL 的输出频率就必须分频得到准确的 BCLK 和 LRCK。比如你要输出 48kHz 采样率、32bit 槽位的 I2S 数据,BCLK 应该是 48kHz × 64 = 3.072MHz,LRCK 就是 48kHz。这些都需要靠内部时钟树精确分频得到。

很多项目里,ES8311 默认配置成从机模式,结果有人拿了一份“主机模式”的初始化代码去改,改了半天下不来。所以拿到一个配置表,先搞清楚它的 I2S 主从模式是什么,再讨论分频计算才有意义。

1.3 抓住一个核心等式:所有分频都在为 fs 服务

无论从机还是主机,也不管开不开 PLL,ES8311 的时钟树最终目的只有一个:让 ADC 和 DAC 的工作时钟与采样率 fs 保持严格的倍频关系。

数字音频系统里,fs 就是采样率,比如 48000Hz、44100Hz。ADC 调制器和数字滤波器通常需要工作在 fs 的整数倍上,比如 64 倍、128 倍、256 倍、512 倍。这个倍数关系一旦不是整数,音频数据就会产生采样点错位,表现就是变调、杂音、或者两个字:难听。

所以你拿到一个 MCLK 频率后,最开始要做的不是翻寄存器,而是算一个除法:MCLK / fs。如果结果是 256、512、1024 这类支持的值,直通就行;如果不是,那就要考虑启用 PLL,或者干脆换一颗合适的 MCLK。

2. 不开PLL的直通路径:MCLK整除采样率时的计算

2.1 什么时候能走直通

ES8311 的 PLL 不是必须要开。如果你的系统 MCLK 已经是音频领域的标准频率,比如 12.288MHz、24.576MHz、11.2896MHz、22.5792MHz,那这些频率和常用的 48kHz、44.1kHz 采样率之间天然就是整数倍关系,完全可以关闭 PLL,直接走 MCLK 分频路径。

这样做的好处很明显:省电,启动快,时钟抖动也更小。PLL 本质上是一个反馈环路,从锁定到稳定需要时间,而且是额外的功耗来源。对于 ES8311 这种主打超低功耗的芯片,能不开 PLL 就不开,这是我在实际项目里的一个原则。

判断能不能直通,最简单的办法就是做除法,同时确认倍数在芯片支持的范围内。ES8311 手册里通常会给出一个很直观的表格,列出不同 MCLK 频率和不同采样率下可以配置的倍率关系,但核心逻辑就是那一个。

2.2 实例:12.288MHz MCLK配48kHz采样

拿最常见的组合来算:MCLK = 12.288MHz,目标 fs = 48kHz。

除一下:12,288,000 / 48,000 = 256。

这个 256 意味着芯片可以从 MCLK 直接分频得到 ADC/DAC 工作时钟,而不需要 PLL 参与。很多 codec 方案里,256fs 是 ADC/DAC 调制器的一个标准工作频率,ES8311 也不例外。

如果系统里 I2S 是 16bit 双声道,BCLK 通常是fs × 32 = 1.536MHz;如果 I2S 按 32bit 槽位发,BCLK 就是fs × 64 = 3.072MHz。这些 BCLK 频率也都是从同一个 MCLK 分频出来的,比如 12.288MHz / 8 = 1.536MHz,12.288MHz / 4 = 3.072MHz,分频关系非常干净。

所以在直通模式下,你只需要把“MCLK 分频到 ADC/DAC 时钟”的分频系数配对,再把 I2S 格式配对,整个音频链路就能跑起来。我见过很多初始化数组里写 PLL 配置的机会其实根本用不上,那里面的 PLL 参数全是无效配置。

2.3 不能整除的场景识别

反过来,如果你拿到的 MCLK 是 12MHz,想配 48kHz,除法结果是12,000,000 / 48,000 = 250。250 虽然也是整数,但通常在 ES8311 的直通分频路径里并不支持这种任意整数倍,而且 250 和 256 差一点,直接把 MCLK 当作 12.288MHz 去配置,最终实际采样率会变成12,000,000 / 256 = 46,875Hz,也就是 46.875kHz。

这种问题非常隐蔽,因为代码逻辑上看起来“一切正常”:I2C 写入成功,寄存器回读正确,I2S 信号也有,但播放出来的声音就是比正常慢一点点,音调偏低。如果你用频率计去量 BCLK,会发现它也不是标准的 1.536MHz,而是偏低的频率。

所以我的建议是:拿到一个 MCLK,先别急着写代码,花五分钟把各种可能采样率下的MCLK / fs算一遍,看看结果是不是 256、512 这种规整的数。不是的话,直接进入 PLL 方案,不要在直通路径上硬配。

3. PLL场景的完整算例:19.2MHz MCLK生成48kHz时钟

3.1 PLL参数与公式拆解

很多主控平台不会专门给音频 codec 一颗 12.288MHz 的晶振,而是共用系统主晶振,比如 19.2MHz、24MHz、26MHz。这时候就要依靠 ES8311 内部 PLL,把 MCLK 转换成一个和 fs 有整数倍关系的工作时钟。

PLL 的基本思想是:先把输入频率分频到一个比较小的值,也就是“参考频率”,然后再通过倍频环路乘上一个系数,得到输出频率。通用的公式可以写成:

f_pll_out = f_mclk × N / M

其中 M 是输入分频系数,N 是倍频系数。实际芯片里可能还有额外的输出分频、后分频等,但核心逻辑就是通过一组整数比例,让输出频率落在期望的音频时钟频率上。

一个特别要注意的坑:很多芯片寄存器里的 M、N 字段存的是“减一”后的编码值。比如你算出来 M = 25,寄存器里可能写的是 24,也就是 0x18。所以我每次拿到一个新 codec,都会先去看手册的寄存器描述,确认是“直接值”还是“编码值”,否则写进去的配置频率可能整整差了一倍或者差一个计数。

3.2 算例:19.2MHz到24.576MHz的整数分频组合

我选一个真实项目里用过的组合来演示:MCLK = 19.2MHz,目标 fs = 48kHz。

先定目标。ES8311 内部 ADC/DAC 工作时钟比较常用的一个档位是 512fs,也就是512 × 48,000 = 24,576,000Hz,即 24.576MHz。我们要做的就是让 PLL 输出 24.576MHz。

计算倍频比:

f_pll_out / f_mclk = 24.576MHz / 19.2MHz = 1.28

1.28 换算成分数就是128 / 100 = 32 / 25。这就好办多了,直接用 M = 25、N = 32 就能得到:

19.2MHz × 32 / 25 = 24.576MHz

这个组合非常漂亮,因为 25 和 32 都是常规分频器能处理的整数。如果你觉得 VCO 工作在 24.576MHz 偏低,担心超出 PLL 锁相环的 VCO 范围,可以再整体放大四倍,变成 M = 100、N = 128,那么 PLL 输出就是 98.304MHz,再经过内部 4 分频回到 24.576MHz。具体选哪组,取决于 ES8311 手册里 PLL 模块的 VCO 频率范围。有些芯片要求 VCO 至少工作在 50MHz 以上,那就不能用 24.576MHz 直接输出。

所以在配置 PLL 之前,先翻手册确认三个边界:输入 MCLK 允许的范围、PLL 参考频率允许的范围、VCO 输出频率允许的范围。把这三条线找出来,再选 M、N,就不会写出一个看起来合理但芯片根本不接受的配置。

3.3 分频系数约束与校验清单

算完 M、N 不等于配置结束,还要检查整个时钟链路能否落到正确的 BCLK 和 LRCK 上。我每次都会列一个校验清单:

  • PLL 输出频率是否在手册规定的 VCO 范围内。如果太高,就在 PLL 后面加输出分频;如果太低,就整体放大 M、N 再分频回来。
  • PLL 参考频率是否在可接受范围内。比如 MCLK / M 得到的参考频率不能太低,否则环路滤波器很难锁定。一般芯片会给出几百 kHz 到几 MHz 的参考范围。
  • 最终送给 ADC/DAC 的工作时钟是否是 fs 的整数倍。PLL 输出 24.576MHz,分频到 512fs、256fs 或者 128fs,都能对应上。
  • I2S 主从模式下,BCLK/LRCK 是否也需要从这一路时钟分频出来。如果需要,那么 PLL 输出的频率还得能整除 BCLK 和 LRCK。

我自己在实际调试中,还会把计算结果写成一个注释,留在初始化代码旁边。比如:

// MCLK = 19.2MHz // fs = 48kHz // f_pll_out = 19.2MHz * 32 / 25 = 24.576MHz // 512fs = 24.576MHz

这样过了几个月再回头维护代码,或者同事接手项目,一眼就能看懂当初为什么配这些值,不用重新推一遍。

4. 寄存器落地:用I2C把分频结果写进ES8311

4.1 时钟相关寄存器通常长什么样

ES8311 的时钟配置集中在芯片的时钟管理寄存器区。不同批次、不同驱动版本的命名可能略有差异,但核心的位域大致包括:PLL 使能开关、MCLK 分频选择、PLL 的 M 和 N 参数、ADC 时钟分频、DAC 时钟分频。有的版本里还会有 BCLK/LRCK 极性控制、I2S 格式选择等。

有一个现象很常见:很多人从网上抄来一份初始化数组,直接往 I2C 总线里灌,结果有声音但就是不对。原因往往是网上的版本针对的是另一颗 CPU、另一个 MCLK 频率,或者另一批芯片封装。ES8311 的驱动流程里,时钟配置只是其中一步,后面还跟着模拟电源、ADC/DAC 通路、输出音量等配置。时钟配错了,后面的配置再对也白搭。

我在落地时的做法是,把初始化数组按“模块”拆开,每 3 到 4 个寄存器一组,用注释标清楚这组是干什么的。这样一旦音频信号异常,我可以单独把时钟组拎出来验证,不需要对着 50 多行的数组抓瞎。

4.2 从复位到起振的完整初始化顺序

ES8311 的初始化顺序,我以为要遵循“先复位、再时钟、后通路”的节奏。

第一步是复位。很多 codec 芯片的复位寄存器不仅是软复位用,也是让内部所有状态回到默认值的关键一步。如果上电后没有复位就直接写配置,有些寄存器可能处于未知状态,导致后续配置部分生效。

第二步是配置时钟树。先确定 MCLK 是否已经有输入,然后设置 PLL 的 M、N 参数,选择 ADC/DAC 时钟源是 PLL 还是 MCLK,再设置分频系数。这一步和上一步之间最好加一点延时,给内部电路一个稳定时间。

第三步是配置 I2S 数字接口格式,包括主从模式、位宽、帧格式、BCLK/LRCK 极性。

第四步才是配置模拟通路,比如 ADC 输入选择、DAC 输出音量等。

很多人喜欢把时钟配置放在最后,以为先把音量打开就能听到声音,结果怎么调都是噪声。实际上数字音频系统里,时钟是骨架,模拟通路是血肉,骨架不对,血肉再完整也立不起来。

4.3 一段可以直接改用的C配置函数

下面这段代码是伪初始化框架,用于演示时钟配置的落地方式。寄存器名需要用你手上那颗 ES8311 数据手册的偏移地址替换,这里重点看结构和计算结果的映射。

#include <stdint.h> /* I2C读写函数由你的平台提供 */ uint8_t es8311_i2c_read(uint8_t reg); void es8311_i2c_write(uint8_t reg, uint8_t val); /* 这里按19.2MHz MCLK、48kHz采样、PLL输出24.576MHz配置 */ #define MCLK_HZ 19200000 #define FS_HZ 48000 #define PLL_M 25 #define PLL_N 32 #define PLL_OUT_HZ 24576000 static void es8311_clock_config_for_48k(void) { /* 复位:所有寄存器回到默认状态 */ es8311_i2c_write(ES8311_RESET_REG, 0x1F); /* 选MCLK作为PLL输入源 */ es8311_i2c_write(ES8311_CLK_SEL_REG, ES8311_MCLK ...); /* 写PLL的M/N参数 */ /* 注意:如果手册说明寄存器里是N-1、M-1,要在这里先减一 */ es8311_i2c_write(ES8311_PLL_M_REG, PLL_M - 1); es8311_i2c_write(ES8311_PLL_N_REG, PLL_N - 1); /* 使能PLL */ es8311_i2c_write(ES8311_PLL_EN_REG, 0x01); /* 等待PLL锁定 */ delay_ms(10); /* 配置ADC/DAC时钟源为PLL输出,并分频到512fs */ /* 512 * 48000 = 24.576MHz */ es8311_i2c_write(ES8311_ADC_CLK_DIV_REG, ES8311_512FS ...); es8311_i2c_write(ES8311_DAC_CLK_DIV_REG, ES8311_512FS ...); }

这个函数写完不是结束,我通常会在后面加一个检查逻辑:延时 10 毫秒后,读取 PLL 的 lock 状态位。如果硬件反馈 PLL 没有锁定,那要么是 M、N 参数算错了,要么是输入的 MCLK 根本没起来。

4.4 写完后一定要做的回读与状态检查

寄存器写进去,不代表芯片就按照写的值工作了。I2C 写时序有误、电压不稳、MCLK 信号质量差,都可能导致寄存器实际内容和预期不一致。我在项目调试中最常用的方法就是回读。

把配置相关的寄存器全部读一遍,和预期值做对比。尤其注意 PLL lock 状态位。很多 ES8311 的寄存器描述里会有一个 PLL 锁定标志位,如果在使能 PLL 后这个位一直是 0,先不要怀疑音频通路,回去查 MCLK 信号是否正常、M/N 配置是否在手册允许范围内。

还有一个经验:不要只回读一次,要隔几十毫秒再读第二次。PLL 锁定需要时间,如果第一次读还没锁上,第二次读锁上了,那一般只是时序问题;如果两次都读不到 lock,那就不是时序问题,而是配置本身有硬伤。

5. 时钟配置错了会怎样:音质表现与定位手段

5.1 声音慢放、变调、爆音,分别对应哪种时钟问题

音质问题是一个很直观的“时钟错误指示器”,但很多人不知道怎么看。根据我踩过的坑,大概可以分成三类:

第一类是声音变调,但能听清楚内容。这种情况最常见的根因是实际采样率和预期采样率不一致。比如你预期 48kHz,实际 LRCK 只有 46.875kHz,声音就会整体变慢、音调偏低。反过来,如果实际采样率高于预期,声音会变尖。这个时候重点查 MCLK 频率和分频系数的匹配关系。

第二类是持续的爆音、啪嗒声、沙沙声。这种往往不是分频系数差一点的问题,而是时钟本身不稳定,或者 PLL 没有真正锁定。PLL 在某些非法的 M/N 配置下会输出一个接近目标但不是目标频率的信号,音频链路里就会有明显的周期性噪声。

第三类是完全没有声音,或者只有上电一瞬间的一点刺啦声。这种情况可能是 ADC/DAC 的时钟路径彻底没通,比如分频器被配到了一个非法值,或者某个时钟使能位没打开。先别急着查模拟通路,回到时钟寄存器翻一遍,很多“无声”其实是时钟没亮。

5.2 用示波器和逻辑分析仪快速判定实际时钟

软件上读寄存器很重要,但硬件量测更直接。示波器探头点几下,比纠结半天寄存器靠谱得多。

在从机模式下,优先量 BCLK 和 LRCK。BCLK 应该在示波器上看到一个稳定的方波,用示波器的频率测量功能直接读出频率。如果读出来不是 1.536MHz、3.072MHz 这类预期值,那就是主控侧时钟有问题。如果主控给的 BCLK 正常,再量 LRCK,正常情况下 LRCK 就应该等于采样率。如果 LRCK 是 46.875kHz,而程序里写的是 48kHz,那问题大概率出在 MCLK 分频上。

如果是主机模式,ES8311 自己产生 BCLK/LRCK,那就要量 ES8311 的 MCLK 引脚和输出引脚。MCLK 引脚有波形但幅度很低,可能是信号质量问题;MCLK 没波形,那就是主控侧压根没输出。

逻辑分析仪比示波器好在可以解码 I2C。把 SCL、SDA、MCLK、BCLK、LRCK 一起抓下来,既能确认 I2C 写入的寄存器值,又能看到时钟频率。我一般用逻辑分析仪验证“写入是否正确”,用示波器验证“时钟是否稳定”。

5.3 一次“LRCK变成了46.875kHz”的排查复盘

有一次,客户反馈一款设备播放音频时声音偏低。量 LRCK,发现实际频率是 46.875kHz,而系统需求是 48kHz。打开原理图一查,主控输出给 ES8311 的 MCLK 是 12MHz,不是常见的 12.288MHz。

最初同事直接照抄了另一块板上“MCLK = 12.288MHz、fs = 48kHz”的初始化数组。那块板上 MCLK 是 12.288MHz,除以 256 正好是 48kHz。但换到 12MHz MCLK 后,寄存器里配的是 256 分频,实际算出来就是 12,000,000 / 256 = 46,875Hz。音频系统以这个 LRCK 为基准采样,播放的自然就不是标准 48kHz 速度。

排查链路其实不长:听感判断出“变调”问题,示波器量 LRCK 发现频率不对,回头看 MCLK 是 12MHz,算了一下除法,马上就知道问题出在哪。解决方案有两种:一是主控把 MCLK 改成 12.288MHz;二是维持 12MHz MCLK,启用 PLL,把 12MHz 变换成 24.576MHz,然后再分频到 512fs。

这个案例给我最大的启发是:画原理图之前,就应该先确认音频 MCLK 选什么频率。如果主控只能用 12MHz 这种非音频时钟,就必须在设计阶段就把 ES8311 的 PLL 方案考虑进去,而不是等板子打回来了再在软件里想办法。

不同类型时钟错误对应的检查方向,我整理了一张表,方便现场排查时对照:

故障现象最可能的时钟原因首选排查动作
声音变调、音调明显偏低/偏高实际 fs 与预期不一致示波器量 LRCK 频率,和采样率对比
持续爆音、沙沙声PLL 未锁定、MCLK 质量差读 PLL lock 位,示波器量 MCLK 波形
完全无声ADC/DAC 时钟路径未开启回读时钟相关寄存器,确认分频字段
左右声道有串扰BCLK 频率异常导致数据错位量 BCLK,核对 I2S 位宽配置
上电后有噪声但随后安静PLL 锁定时间不足,软件未等待增加 PLL 锁定延时,再初始化后续寄存器

这张表是我在支持同事调音频问题时最常用的工具。很多时候不用想太复杂,照着现象先锁定方向,再回到时钟树算一遍,问题基本就能圈定在一个很小的范围内。

最后分享一个我自己的习惯:每次拿到一套新的 ES8311 初始化配置,我都会在代码里留一个“配置来源注释”,写上 MCLK 是多大的晶振、目标采样率是多少、PLL 目标输出频率是多少、M/N 是怎么算出来的。几个月后如果出了问题,或者换了硬件平台,沿着这段注释去核对,能省下大把时间。

音频 codec 的时钟树配置,看着是寄存器操作,本质上是数字信号处理的整数运算。把 MCLK、fs、BCLK、LRCK、PLL 之间的关系理清楚,ES8311 这颗芯片就不再有神秘感了。

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

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

立即咨询