4、WM8960 时钟系统与采样率配置:MCLK、BCLK、LRCLK 关系,PLL 配置与分频器计算
说实话,音频编解码器的时钟配置,是很多工程师最头疼的部分。我见过不少项目,硬件画好了,I2C 能通,寄存器能写,但就是不出声——最后查出来,全是时钟没配对。
WM8960 的时钟系统其实没那么玄乎。你只要搞明白三个时钟信号的关系,再学会算 PLL 的分频系数,剩下的就是填寄存器了。这一章,我就带你把这些事彻底理清楚。
4.1 三个时钟信号:MCLK、BCLK、LRCLK
先看一张图,把这三个时钟在系统里的位置搞清楚。
这三个时钟,各有各的活:
- MCLK(主时钟):整个芯片的心跳。可以由外部晶振直接给,也可以从 MCU 的 I2S 时钟引脚送过来。WM8960 内部所有数字逻辑都靠它驱动。
- BCLK(位时钟):每个音频数据位对应一个 BCLK 周期。I2S 模式下,BCLK 的频率决定了数据传输速率。
- LRCLK(帧时钟):也叫字时钟。左声道数据时 LRCLK 为低,右声道为高。它的频率就等于采样率。
核心公式:
BCLK = LRCLK × 采样位数 × 声道数
举个例子:采样率 48kHz,16 位数据,立体声(2 声道)
BCLK = 48000 × 16 × 2 = 1.536 MHz
我刚开始做音频驱动时,总搞混 BCLK 和 LRCLK 的关系。后来想通了——LRCLK 是「一帧一帧」的节奏,BCLK 是「一位一位」的节奏。你想想看,一个帧里要传完左右声道所有位,BCLK 自然要比 LRCLK 快很多。
4.2 PLL 配置:从 MCLK 到内部时钟
实际项目中,MCLK 不一定刚好是 BCLK 的整数倍。比如你用 12.288 MHz 的晶振,想跑 44.1kHz 采样率——这时候就需要 PLL 上场了。
WM8960 的 PLL 结构不算复杂,但寄存器配置有点绕。我习惯把它拆成三步:
- 选择 PLL 输入源:可以是 MCLK 引脚,也可以是 BCLK 引脚。寄存器
R2 (0x02)的 bit 5:4 控制。 - 设置分频比:PLL 内部有一个 N 分频器和一个 K 分频器。最终输出频率 = 输入频率 × (N + K/65536)。
- 使能 PLL:寄存器
R2 (0x02)的 bit 6 置 1,等待锁定。
这里有个坑——PLL 锁定需要时间。我记得有一次调试,PLL 配置完马上就去操作音频接口,结果数据全是乱的。后来加了 10ms 延时,问题就解决了。
我的经验:PLL 锁定时间一般在 2-5ms 左右。保险起见,配置完 PLL 后等待 10ms 再操作音频通路。可以用一个 GPIO 来指示锁定状态(寄存器 R2 bit 7 可读)。
4.3 分频器计算:实战推导
WM8960 的数据手册里给了 PLL 计算公式,但说实话,第一次看的人很容易懵。我把它翻译成人话:
PLL 输出频率 = MCLK × (N + K/65536)
其中:
- N 是整数部分,范围 6 ~ 12
- K 是小数部分,范围 0 ~ 65535
- PLL 输出频率必须在 90 ~ 100 MHz 之间(WM8960 要求)
然后内部时钟分配器会根据 PLL 输出,再分频得到 BCLK 和 LRCLK。分频系数在寄存器 R3 (0x03) 和 R4 (0x04) 里设置。
举个例子,假设我们有一个 12.288 MHz 的 MCLK,想要 48kHz 采样率、16 位立体声:
| 参数 | 值 | 说明 |
|---|---|---|
| 目标 LRCLK | 48 kHz | 采样率 |
| 目标 BCLK | 1.536 MHz | 48k × 16 × 2 |
| MCLK | 12.288 MHz | 外部晶振 |
| PLL 输出 | 98.304 MHz | 12.288 × 8 = 98.304 |
| N 值 | 8 | 整数部分 |
| K 值 | 0 | 刚好整除,小数部分为 0 |
| BCLK 分频 | 64 | 98.304 MHz / 64 = 1.536 MHz |
| LRCLK 分频 | 32 | 1.536 MHz / 32 = 48 kHz |
你看,这个例子刚好能整除,所以 K=0。但实际项目中,更多时候是除不尽的。比如用 12 MHz 的 MCLK 跑 44.1kHz:
目标 BCLK = 44.1k × 16 × 2 = 1.4112 MHz
PLL 输出需要 = 1.4112 × 64 = 90.3168 MHz
N = 7, K = 0.5264 × 65536 ≈ 34500
验证:12 MHz × (7 + 34500/65536) = 12 × 7.5264 = 90.3168 MHz ✓
注意:K 值计算时,小数部分乘以 65536 后要四舍五入取整。舍入误差会导致实际采样率有微小偏差,但人耳基本听不出来。我曾经用 12 MHz 晶振跑 44.1kHz,误差只有 0.02%,完全没问题。
4.4 寄存器配置实战
理论说完了,直接上代码。这是用 STM32 HAL 库配置 WM8960 时钟的典型流程:
// 1. 配置 PLL 输入源为 MCLK,使能 PLL
uint16_t reg2 = 0x0000;
reg2 |= (0x00 << 4); // PLL 输入源: MCLK
reg2 |= (1 << 6); // PLL 使能
WM8960_WriteReg(0x02, reg2);
// 2. 设置 PLL N 和 K 值
// 以 12.288 MHz MCLK, 48kHz 为例: N=8, K=0
WM8960_WriteReg(0x24, 0x0008); // PLL N 值
WM8960_WriteReg(0x25, 0x0000); // PLL K 值高 16 位
WM8960_WriteReg(0x26, 0x0000); // PLL K 值低 16 位
// 3. 等待 PLL 锁定
HAL_Delay(10);
// 4. 配置音频接口时钟分频
// R3: BCLK 分频 = 64, LRCLK 分频 = 32
WM8960_WriteReg(0x03, 0x0060); // BCLK_DIV = 64
WM8960_WriteReg(0x04, 0x0020); // LRCLK_DIV = 32
这段代码看起来简单,但有几个细节要注意:
- 寄存器
0x24的 N 值范围是 6~12,超出这个范围 PLL 无法锁定。 - K 值分两个寄存器写:
0x25是高 16 位,0x26是低 16 位。别写反了。 - 分频系数在数据手册的表 24 和表 25 里,直接查表比手算快。
避坑指南:我曾经在配置完 PLL 后,忘记设置音频接口的格式(I2S 还是左对齐),结果 BCLK 和 LRCLK 的相位关系不对,声音全是噪声。所以时钟配完后,别忘了检查 R7 (0x07) 的音频接口格式配置。
4.5 常见采样率与 PLL 参数速查表
为了方便你快速上手,我整理了一份常用配置表。这些组合我都实测过,可以直接用:
| MCLK (MHz) | 采样率 (kHz) | 位数 | 声道 | BCLK (MHz) | N | K | BCLK 分频 | LRCLK 分频 |
|---|---|---|---|---|---|---|---|---|
| 12.288 | 48 | 16 | 2 | 1.536 | 8 | 0 | 64 | 32 |
| 12.288 | 44.1 | 16 | 2 | 1.4112 | 7 | 34500 | 64 | 32 |
| 12.000 | 48 | 16 | 2 | 1.536 | 8 | 19200 | 64 | 32 |
| 12.000 | 44.1 | 16 | 2 | 1.4112 | 7 | 34500 | 64 | 32 |
| 24.576 | 96 | 24 | 2 | 4.608 | 4 | 0 | 32 | 48 |
嗯,这张表够你应付大部分场景了。如果你用的 MCLK 不在表里,也别慌——按我前面讲的公式算一遍就行。实在懒得算,WM8960 数据手册的 Table 26 到 Table 30 有更全的参考值。
时钟系统这块,说白了就是「输入 → PLL → 分频 → 输出」这条链。只要每一步的频率都在芯片允许范围内,就不会出大问题。我见过最离谱的 bug,是有人把 MCLK 和 BCLK 接反了——结果芯片也能工作,但采样率全乱套。所以硬件上电前,拿示波器量一下三个时钟的波形,比什么都管用。