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的步骤就是:

  1. 确定采样率和数据格式:比如48kHz、16bit、双声道
  2. 计算BCLK:48k × 2 × 16 = 1.536MHz
  3. 配置PLLI2S:算出分频系数,使BCLK尽量接近目标值
  4. 设置主从模式:STM32做主,WM8960做从
  5. 配置数据格式:STM32和WM8960都设成16bit
  6. 确认帧同步极性:两边一致,标准I2S模式
  7. 使能I2S:先使能WM8960,再使能STM32的I2S外设

下面这张图,把整个时钟树和配置流程画出来了:

STM32 I2S时钟树与配置流程 STM32 I2S时钟树与配置流程 PLLI2S 系统时钟 外部时钟 I2S分频器(I2SDIV + MCKOE) BCLK(位时钟) LRCK(帧同步) 配置参数 • 主从模式选择 • 数据格式(16/24/32bit) • 帧同步极性 • 时钟分频系数 • 数据对齐方式 • 使能顺序 WM8960 Codec

配置完成后,用逻辑分析仪抓一下BCLK和LRCK的波形。BCLK应该是均匀的方波,LRCK的频率等于采样率。如果波形不对,先查时钟源,再查分频系数——这是最有效的排查路径。

总结一下:I2S时序配置,核心就是让STM32和WM8960在时钟、数据格式、帧同步极性这三个维度上完全对齐。时钟树要算清楚分频系数,主从模式要一致,数据格式要匹配,帧同步极性不能反。把这四点做到位,I2S通信就成功了一大半。