I2S音频总线协议:从波形到配置
各位同学,今天我们来啃一块硬骨头——I2S总线协议。说实话,很多做嵌入式音频的朋友,一听到I2S就觉得头大。其实没那么玄乎,说白了它就是一套专门给音频数据搬家的规矩。我当年第一次调WM8960的时候,也被I2S的时序折腾过,后来用逻辑分析仪一看,瞬间就明白了。
这一章,我会把I2S标准格式、飞利浦格式的区别、主时钟和位时钟的关系,以及STM32的I2S外设配置要点,一次性给你讲透。最后还会教你用逻辑分析仪抓波形,自己验证配置对不对。
I2S标准格式与飞利浦格式的差异
先澄清一个常见的误区。很多人以为I2S就是飞利浦格式,其实不完全对。飞利浦公司在1986年定义了I2S总线规范,但后来各家芯片厂商又衍生出了几种变体。我们常说的「I2S标准格式」和「飞利浦格式」,其实是两种不同的数据对齐方式。
它们的核心区别在于:WS(声道选择)信号跳变沿与数据位的对齐关系。
| 对比项 | 飞利浦格式(标准I2S) | I2S标准格式(左对齐) |
|---|---|---|
| WS跳变沿 | WS在BCLK下降沿变化 | WS在BCLK上升沿变化 |
| 数据起始位 | WS跳变后的第2个BCLK上升沿开始传MSB | WS跳变后的第1个BCLK上升沿开始传MSB |
| 数据对齐 | MSB延迟1个BCLK周期 | MSB与WS跳变对齐 |
| 兼容性 | 最广泛,WM8960默认支持 | 部分DSP/CODEC支持 |
我的经验:WM8960的数据手册里明确写着支持「飞利浦I2S格式」和「左对齐格式」。我建议你优先用飞利浦格式,因为STM32的I2S外设默认就是飞利浦模式,省去很多配置麻烦。
为什么会这样设计?说白了,飞利浦格式多延迟一个BCLK周期,是为了给WS信号留出建立时间。早期硬件速度慢,这个延迟很必要。现在主频高了,但为了兼容老设备,这个习惯保留了下来。
主时钟MCLK与位时钟BCLK的关系
这两个时钟是I2S总线的心脏。很多新手搞不清它们的关系,我简单给你拆解一下。
- MCLK(主时钟):由STM32或外部晶振提供,是CODEC内部Σ-Δ调制器的工作时钟。频率通常是采样率FS的256倍或384倍。
- BCLK(位时钟):决定每一位数据的传输速率。对于16位立体声,BCLK = FS × 16 × 2 = 32 × FS。但实际常用BCLK = 64 × FS(因为很多CODEC内部用24位或32位处理)。
举个例子:采样率FS = 48kHz,那么:
- MCLK = 256 × 48kHz = 12.288MHz
- BCLK = 64 × 48kHz = 3.072MHz
WM8960的典型配置中,MCLK和BCLK的关系可以是MCLK = 4 × BCLK。你算一下:12.288MHz ÷ 3.072MHz = 4,没错吧?
避坑指南:我曾经在一个项目里,MCLK配成了12MHz而不是12.288MHz,结果WM8960出来的声音一直有杂音。后来用频率计一测,发现MCLK偏差了0.288MHz,导致内部PLL失锁。记住,音频时钟必须精确,差一点都不行。
STM32 I2S外设配置要点
STM32的I2S外设其实是SPI的变体。HAL库把I2S封装成了独立的初始化函数,但底层寄存器是复用的。配置时,有几个关键点你必须注意。
1. 时钟源选择
STM32的I2S时钟可以来自内部PLL或者外部引脚。我建议用内部PLL,因为频率更精确。在CubeMX里,你需要设置:
- I2S时钟源:PLLI2S R
- PLLI2S N/R/M值:根据目标MCLK频率计算
2. 数据格式与帧格式
HAL库的I2S初始化结构体里,有几个参数容易搞混:
| 参数 | 可选值 | 说明 |
|---|---|---|
| Mode | I2S_MODE_MASTER_TX / RX | STM32做主设备,产生BCLK和WS |
| Standard | I2S_STANDARD_PHILIPS / I2S_STANDARD_MSB | 选飞利浦还是左对齐 |
| DataFormat | I2S_DATAFORMAT_16B / 24B / 32B | 每声道数据位数 |
| MCLKOutput | I2S_MCLKOUTPUT_ENABLE | 使能MCLK输出给CODEC |
| AudioFreq | I2S_AUDIOFREQ_48K / 44K1K / 8K | 采样率,HAL库自动算BCLK |
注意:DataFormat参数要和WM8960的配置一致。如果WM8960配的是24位数据,STM32这边也必须是24位,否则数据会错位。我见过有人配了16位格式,但WM8960那边设成24位,结果声音全是噪声。
3. 代码示例:HAL_I2S_Init配置
hi2s3.Instance = SPI3;
hi2s3.Init.Mode = I2S_MODE_MASTER_TX;
hi2s3.Init.Standard = I2S_STANDARD_PHILIPS;
hi2s3.Init.DataFormat = I2S_DATAFORMAT_16B;
hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_48K;
hi2s3.Init.CPOL = I2S_CPOL_LOW;
hi2s3.Init.ClockSource = I2S_CLOCK_PLL;
hi2s3.Init.FirstBit = I2S_FIRSTBIT_MSB;
if (HAL_I2S_Init(&hi2s3) != HAL_OK)
{
Error_Handler();
}
这段代码里,CPOL我设成了LOW,这是飞利浦格式的要求。如果你用左对齐格式,CPOL可能需要改成HIGH。具体看数据手册的时序图。
使用逻辑分析仪抓取I2S波形
理论说再多,不如亲眼看到波形。逻辑分析仪是调试I2S的利器。我建议你买一个带8通道以上的,采样率至少100MHz。便宜点的几十块钱的也能用,但采样率太低会漏掉细节。
抓取步骤
- 接线:逻辑分析仪的通道0接BCLK,通道1接WS,通道2接SD(数据线)。GND共地。
- 设置触发:用WS的上升沿触发,这样能抓到一帧完整的左右声道数据。
- 开始抓取:播放一段已知频率的音频(比如1kHz正弦波),然后停止抓取。
- 解码:在逻辑分析仪软件里选择I2S协议解码器,设置BCLK和WS的通道映射。
我的习惯:抓波形时,我会同时抓MCLK。这样能一眼看出MCLK和BCLK的比例对不对。如果MCLK是BCLK的4倍,说明时钟配置正确。如果比例不对,先查PLL配置。
波形解读要点
- WS信号:频率应该等于采样率FS。48kHz采样时,WS周期约20.8μs。
- BCLK信号:频率应该是64×FS。48kHz时,BCLK频率3.072MHz,周期约325ns。
- 数据位:在WS为高电平时,SD线上传输的是右声道数据;WS为低电平时,传输左声道数据。
- MSB位置:飞利浦格式下,WS跳变后的第2个BCLK上升沿,SD线上出现MSB。
如果你看到WS频率不对,或者数据位错位,别急着改代码。先检查逻辑分析仪的采样率够不够,有时候是采样率太低导致波形失真。我遇到过好几次,换了更高采样率的分析仪,问题就解决了。
小技巧:用逻辑分析仪抓I2S时,把BCLK和WS的电压阈值设成1.65V(对于3.3V系统)。如果阈值设错了,波形会显示为全高或全低,让你误以为没信号。
好了,这一章的内容就到这里。I2S协议说复杂也复杂,说简单也简单。你只要记住:格式决定数据怎么对齐,时钟决定数据跑多快,配置决定STM32怎么干活,波形验证一切。把这四点吃透,WM8960的I2S接口对你来说就是小菜一碟。