4、时钟树与音频采样率:PLL配置计算,MCLK/LRCK/BCK关系推导
时钟,是音频系统的命脉。我见过不少开发者,I2S配置看起来全对,WM8960寄存器也写满了,但就是不出声——最后查出来,是MCLK和采样率没对上。说白了,时钟不对,音频数据就是一堆乱码。
这一章,我们就把时钟树彻底讲透。你搞懂了PLL怎么配、MCLK/LRCK/BCK之间什么关系,后面调音频就是水到渠成的事。
4.1 音频时钟的三个关键角色
先认识一下这三个家伙:
- MCLK(主时钟):整个音频系统的基准时钟。WM8960内部的所有操作,包括ADC/DAC的调制器、数字滤波器,都依赖MCLK。通常频率是256×Fs或384×Fs。
- BCK(位时钟):每个音频数据位对应一个BCK周期。I2S标准下,BCK频率 = 声道数 × 位深 × 采样率。比如双声道、16位、48kHz:BCK = 2 × 16 × 48000 = 1.536MHz。
- LRCK(帧时钟):也叫字时钟,频率等于采样率。左声道数据在LRCK低电平传输,右声道在高电平传输。48kHz采样率,LRCK就是48kHz。
核心公式(请刻在脑子里):
BCK = 声道数 × 位深 × Fs
MCLK = BCK × 倍数(通常是4、8、16倍,取决于WM8960配置)
LRCK = Fs
举个例子:双声道、16位、48kHz采样,BCK = 2×16×48000 = 1.536MHz。如果WM8960配置为MCLK = 256×Fs,那MCLK = 256×48000 = 12.288MHz。你看,这三个时钟是严格绑定的。
4.2 STM32的时钟树与PLL配置
STM32的时钟树,说实话,刚接触时确实有点绕。但音频应用里,我们只需要关注一条路径:HSE → PLL → I2S时钟。
我个人习惯用外部8MHz晶振作为HSE。为什么?因为8MHz好分频,能精确得到各种音频采样率需要的MCLK。
STM32的I2S时钟源有两个选择:
- PLLI2S:专用的I2S PLL,精度高,推荐使用。
- 系统PLL(PLL):也可以分频给I2S,但灵活性差一些。
我们重点讲PLLI2S的配置。它的计算公式是:
PLLI2S输出频率 = HSE值 × PLLI2SM / (PLLI2SN × PLLI2SR)
其中:
- PLLI2SM:预分频系数,通常固定为8(对应8MHz HSE)
- PLLI2SN:倍频系数,范围192~432
- PLLI2SR:输出分频系数,范围2~7
嗯,这里要注意:PLLI2SN和PLLI2SR的取值不是随便填的,必须保证输出频率在I2S外设的允许范围内(通常是1MHz~25MHz)。
4.3 常见采样率的MCLK计算
我们直接上实战。假设WM8960配置为MCLK = 256×Fs,HSE = 8MHz,双声道16位数据。
| 采样率 (Fs) | MCLK (256×Fs) | BCK (2×16×Fs) | PLLI2SN | PLLI2SR |
|---|---|---|---|---|
| 8 kHz | 2.048 MHz | 256 kHz | 256 | 5 |
| 16 kHz | 4.096 MHz | 512 kHz | 256 | 5 |
| 44.1 kHz | 11.2896 MHz | 1.4112 MHz | 271 | 3 |
| 48 kHz | 12.288 MHz | 1.536 MHz | 384 | 5 |
避坑指南:44.1kHz的MCLK是11.2896MHz,这个频率用8MHz HSE很难精确得到。我曾在项目里直接用PLLI2SN=271、PLLI2SR=3,算出来是11.2896MHz吗?不是,是11.2896...MHz,误差在0.001%以内,人耳根本听不出来。但如果你用系统PLL去凑,误差可能到0.1%,那就能感觉到音调偏了。
4.4 WM8960的时钟配置
WM8960内部也有一个时钟分频器。它接收MCLK后,可以进一步分频得到内部需要的时钟。寄存器0x04(Clocking Register)控制这部分。
关键位:
- BIT 6:4 (CLKSEL):选择MCLK来源,通常设为000(外部MCLK)
- BIT 3:2 (SYSCLKDIV):系统时钟分频,00 = 不分频,01 = 2分频
- BIT 1:0 (BCLKDIV):BCK分频,00 = 不分频,01 = 2分频
举个例子:如果MCLK = 12.288MHz,WM8960内部需要SYSCLK = 12.288MHz,那CLKSEL=000,SYSCLKDIV=00。BCK需要1.536MHz,而MCLK/BCK = 8,所以BCLKDIV也得配成对应的分频比。
我曾经踩过的坑:WM8960的BCLKDIV不是直接写分频系数,而是写一个索引值。比如MCLK/BCK=8时,BCLKDIV要写0x03(对应8分频)。如果你直接写8,WM8960会当成无效值,BCK就乱掉了。一定要看数据手册Table 42的映射表。
4.5 时钟树整体关系图
下面这张图,把整个时钟链路串起来了。你对照着看,思路会清晰很多。
4.6 代码实战:配置PLLI2S生成48kHz采样率
理论讲完了,我们直接上代码。下面这段是STM32CubeMX生成的HAL库代码,我加了详细注释。
/* 配置PLLI2S,生成48kHz采样率所需的12.288MHz MCLK */
void MX_I2S_ClockConfig(void)
{
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
/* 选择PLLI2S作为I2S时钟源 */
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
/* PLLI2S配置:HSE=8MHz, M=8, N=384, R=5 */
/* 输出频率 = 8MHz × 384 / (8 × 5) = 12.288MHz */
PeriphClkInitStruct.PLLI2S.PLLI2SM = 8;
PeriphClkInitStruct.PLLI2S.PLLI2SN = 384;
PeriphClkInitStruct.PLLI2S.PLLI2SR = 5;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler(); /* 配置失败,检查参数 */
}
}
个人经验:配置完PLLI2S后,最好读一下RCC寄存器确认输出频率。我习惯在调试串口打印PLLI2SR的值,确保它不是你手滑写错的。曾经有一次我把PLLI2SR写成6,结果MCLK变成10.24MHz,WM8960直接不工作,查了半天才发现。
4.7 I2S初始化中的时钟参数
PLLI2S配好了,I2S外设本身也要设置时钟分频。HAL库的hpi2s.Init结构体里,AudioFreq字段就是采样率。HAL库会根据这个值自动计算分频系数。
/* I2S初始化结构体 */
hi2s3.Instance = SPI3; /* I2S3挂载在SPI3上 */
hi2s3.Init.Mode = I2S_MODE_MASTER_TX; /* 主模式发送 */
hi2s3.Init.Standard = I2S_STANDARD_PHILIPS; /* I2S Philips标准 */
hi2s3.Init.DataFormat = I2S_DATAFORMAT_16B; /* 16位数据 */
hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE; /* 使能MCLK输出 */
hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_48K; /* 48kHz采样率 */
hi2s3.Init.CPOL = I2S_CPOL_LOW; /* 时钟极性 */
hi2s3.Init.ClockSource = I2S_CLOCK_PLL; /* 使用PLLI2S */
hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE; /* 全双工 */
if (HAL_I2S_Init(&hi2s3) != HAL_OK)
{
Error_Handler();
}
这里有个细节:AudioFreq填的是采样率,但HAL库内部会把它转换成BCK频率,再根据MCLK输出使能情况计算分频比。如果你使能了MCLK输出,HAL库会自动保证MCLK = 256 × AudioFreq。
4.8 常见问题与调试方法
时钟配置出问题,音频系统基本就废了。我总结几个常见现象:
- 无声:先量MCLK引脚有没有波形。没有?查PLLI2S配置。有但频率不对?查分频系数。
- 声音变调:采样率不对。比如你配了48kHz,但实际MCLK是11.2896MHz(44.1kHz的),声音就会变慢变低。
- 噪声/爆音:BCK和LRCK相位不对,或者MCLK抖动太大。检查I2S时序和电源滤波。
调试利器:用示波器同时抓MCLK、BCK、LRCK三根线。MCLK应该是BCK的整数倍(通常是4倍或8倍),LRCK应该是BCK的32倍(16位双声道)。如果比例不对,I2S配置肯定有问题。
时钟树这部分,说白了就是小学数学——乘除法算清楚,寄存器填对,就没问题。但恰恰是这种基础计算,最容易手滑。我建议你每次改采样率,都拿计算器按一遍MCLK和BCK,再和示波器实测值对比。养成习惯后,音频时钟这块就再也不会出幺蛾子了。