4. I2S时序与STM32配置:时钟树、主从模式、数据格式与帧同步
I2S时序配置,是音频调试里最容易踩坑的地方。我见过不少工程师,硬件焊得没问题,WM8960也正常,但就是不出声——最后查出来,是STM32的I2S时钟配错了,或者帧同步极性反了。
说白了,I2S就是一根时钟线(BCLK)、一根帧同步线(LRCK)、一根数据线(SD)在干活。你只要把这几个信号的时序对齐,音频就能跑起来。但STM32的I2S外设藏在芯片的时钟树深处,稍不留神就会配出个“无声”或者“爆音”。
核心要点:I2S的时钟源、分频系数、主从模式、数据格式、帧同步极性——这五个参数必须和WM8960完全一致。差一个bit,声音就出不来。
4.1 STM32 I2S外设时钟树
先看时钟怎么来的。STM32的I2S外设,时钟源可以选:
- 系统时钟(SYSCLK):最常用,但受主频影响大
- 外部时钟(I2S_CKIN):精度高,适合对音质要求高的场景
- PLL分频:通过PLLI2S或PLLSAI产生专用时钟
我个人习惯用PLLI2S。为什么?因为系统时钟往往要兼顾CPU和USB,频率不是整数倍,分频后很难得到标准的音频采样率。而PLLI2S可以独立配置,专门给I2S用。
举个例子,你要跑48kHz采样率,16bit数据,双声道。I2S的BCLK频率应该是:
BCLK = 采样率 × 声道数 × 数据位宽
= 48000 × 2 × 16
= 1.536 MHz
然后根据你的PLLI2S输出频率,算出分频系数。STM32的I2S分频器是8位线性分频,公式是:
MCLK = I2SxCLK / (2 × (I2SDIV + MCKOE × 2))
BCLK = MCLK / 4 (对于16bit数据)
嗯,这里要注意:MCKOE是主时钟输出使能,如果你不需要MCLK给WM8960,可以关掉它,分频系数会简单很多。
我的经验:配时钟时,先算好目标BCLK,再反推PLLI2S频率。不要先设PLL再算BCLK,那样很容易得到非整数分频,导致采样率偏移。我曾经在一个项目里,BCLK偏了0.3%,结果播放10秒后音频就开始“跑调”——听起来像磁带卡住了。
4.2 主从模式选择
I2S通信,必须有一方做主,一方做从。主设备产生BCLK和LRCK,从设备只接收或发送数据。
| 模式 | STM32角色 | WM8960角色 | 适用场景 |
|---|---|---|---|
| STM32主、WM8960从 | 产生BCLK/LRCK | 接收时钟 | 最常见,STM32控制时序 |
| STM32从、WM8960主 | 接收时钟 | 产生BCLK/LRCK | WM8960作为时钟源,多设备同步 |
| 外部主设备 | 从模式 | 从模式 | 系统有独立时钟源 |
我建议新手先用STM32做主、WM8960做从。这样时钟完全由MCU控制,调试时逻辑分析仪抓一根时钟线就够了。如果你让WM8960做主,它的时钟来自外部晶振或MCLK,万一晶振不起振,你连时钟都看不到,排查起来更麻烦。
避坑:我曾经在STM32从模式下,忘了配置WM8960的主时钟输出使能。结果WM8960没产生BCLK,STM32一直等时钟,数据发不出去。查了两天才发现是WM8960的寄存器0x04 bit2没置1。嗯,这种低级错误,犯过一次就记住了。
4.3 数据格式:16/24/32bit
I2S的数据格式,说白了就是每个采样点占多少bit。STM32和WM8960必须配成一样的。
- 16bit:最常用,CD音质标准。BCLK频率 = 采样率 × 2 × 16
- 24bit:高精度音频,BCLK = 采样率 × 2 × 24
- 32bit:有些DSP或高端Codec用,BCLK = 采样率 × 2 × 32
STM32的I2S外设,数据格式通过SPI_I2SCFGR寄存器的DATLEN位配置。WM8960则通过寄存器0x04的位5:4设置。
你想想看,如果STM32配了16bit,WM8960配了24bit,会发生什么?WM8960会多读8个bit的无效数据,声音就会“沙沙”响——这就是爆音的一种。
// STM32 I2S配置示例(16bit数据格式)
SPI2->I2SCFGR |= SPI_I2SCFGR_DATLEN_0; // 16bit数据长度
// DATLEN: 00=16bit, 01=24bit, 10=32bit, 11=保留
我的习惯:除非项目明确要求24bit或32bit,否则一律用16bit。原因很简单:16bit的BCLK频率低,对PCB走线要求低,抗干扰能力强。而且大部分音频源(MP3、AAC)本身就是16bit,用24bit反而浪费带宽。
4.4 帧同步极性
帧同步极性,就是LRCK信号在什么电平表示左声道、什么电平表示右声道。I2S标准协议规定:
- LRCK低电平:左声道
- LRCK高电平:右声道
但有些Codec或DSP会反过来。WM8960支持两种极性,通过寄存器0x04的bit3配置:
- bit3=0:左声道低电平(标准I2S)
- bit3=1:左声道高电平(反转)
STM32的I2S外设,帧同步极性通过SPI_I2SCFGR寄存器的CKPOL位控制。注意,CKPOL控制的是BCLK的极性,不是LRCK的极性。LRCK的极性在STM32里是固定的:
- I2S标准模式:LRCK低=左声道
- I2S MSB对齐模式:LRCK低=左声道
- I2S LSB对齐模式:LRCK低=左声道
所以,如果你用STM32的I2S外设,LRCK极性是固定的。如果WM8960那边配反了,左右声道就会互换。听起来就是“人声在左边,乐器在右边”——嗯,严格来说不算故障,但听着别扭。
避坑:我曾经遇到一个案例,客户说“左声道没声音”。我查了半天,发现是WM8960的帧同步极性配反了,导致左声道数据被当成了右声道,而右声道是静音的。解决办法很简单:把WM8960寄存器0x04的bit3取反。前后花了5分钟,但排查过程用了3小时——因为一开始没怀疑极性。
4.5 整体时序配置流程
把上面几个知识点串起来,配置I2S的步骤就是:
- 确定采样率和数据格式:比如48kHz、16bit、双声道
- 计算BCLK:48k × 2 × 16 = 1.536MHz
- 配置PLLI2S:算出分频系数,使BCLK尽量接近目标值
- 设置主从模式:STM32做主,WM8960做从
- 配置数据格式:STM32和WM8960都设成16bit
- 确认帧同步极性:两边一致,标准I2S模式
- 使能I2S:先使能WM8960,再使能STM32的I2S外设
下面这张图,把整个时钟树和配置流程画出来了:
配置完成后,用逻辑分析仪抓一下BCLK和LRCK的波形。BCLK应该是均匀的方波,LRCK的频率等于采样率。如果波形不对,先查时钟源,再查分频系数——这是最有效的排查路径。
总结一下:I2S时序配置,核心就是让STM32和WM8960在时钟、数据格式、帧同步极性这三个维度上完全对齐。时钟树要算清楚分频系数,主从模式要一致,数据格式要匹配,帧同步极性不能反。把这四点做到位,I2S通信就成功了一大半。