2. I2S总线协议详解:物理层、数据格式与帧同步
I2S(Inter-IC Sound)总线是嵌入式Linux音频系统中应用最广泛的数字音频接口标准,由飞利浦半导体(现恩智浦)于1986年制定。它专门用于连接音频编解码器(CODEC)、DSP、FPGA或SoC内部的I2S控制器,实现立体声PCM音频数据的串行传输。本章从物理层信号、数据格式(左对齐、右对齐、标准I2S)以及帧同步与位时钟三个维度进行深入剖析。
2.1 I2S物理层:三线制与四线制
标准I2S总线使用三条主要信号线,部分实现会额外增加一条主时钟线(MCLK),形成四线制。物理层信号定义如下表所示:
| 信号线 | 缩写 | 方向(典型) | 功能描述 |
|---|---|---|---|
| 位时钟 | BCLK / SCK | 主机→从机 | 每个脉冲对应一位数据的移位时钟,频率 = 采样率 × 位宽 × 通道数 |
| 帧同步 | LRCK / WS | 主机→从机 | 指示当前传输的是左声道还是右声道,频率等于采样率 |
| 串行数据 | SD / SDATA | 双向(半双工) | 按位串行传输PCM采样数据,通常为二进制补码格式 |
| 主时钟(可选) | MCLK / MCK | 主机→从机 | 为CODEC内部Δ-Σ调制器提供高精度时钟,频率通常为256×Fs或384×Fs |
设计要点:在嵌入式Linux系统中,SoC的I2S控制器通常作为主机(Master)提供BCLK和LRCK,CODEC作为从机(Slave)接收时钟并同步输出数据。MCLK并非I2S标准强制要求,但大多数高性能音频CODEC需要MCLK来驱动内部数字滤波器,否则会产生时钟抖动或无法锁定。
下图展示了I2S物理层三线制连接的基本拓扑结构:
2.2 数据格式:左对齐、右对齐与标准I2S
I2S协议定义了多种数据对齐方式,核心差异在于串行数据(SD)相对于帧同步信号(LRCK)和位时钟(BCLK)的相位关系。三种主流格式的对比如下:
| 格式名称 | 数据与LRCK对齐关系 | MSB位置 | LSB填充 | 典型应用 |
|---|---|---|---|---|
| 标准I2S格式 | 数据延迟1个BCLK周期,MSB在LRCK跳变后第2个BCLK上升沿 | MSB固定在第2个BCLK | LSB后多余BCLK填充0 | 飞利浦标准、主流CODEC |
| 左对齐格式 | 数据与LRCK跳变同时开始,MSB在LRCK跳变后的第1个BCLK上升沿 | MSB在第1个BCLK | LSB后多余BCLK填充0 | DSP模式、部分ADI CODEC |
| 右对齐格式 | 数据对齐到LRCK跳变前的最后一个BCLK,MSB在数据帧末尾 | MSB靠近帧结束 | MSB前多余BCLK填充0 | 日本电子协会(JEITA)标准 |
常见配置陷阱:在Linux ALSA驱动中,
snd_soc_dai_set_fmt() 函数通过 SND_SOC_DAIFMT_I2S、SND_SOC_DAIFMT_LEFT_J、SND_SOC_DAIFMT_RIGHT_J 枚举值选择格式。如果CODEC数据手册标注为“I2S格式”,但实际波形显示MSB与LRCK同时开始,则可能是左对齐格式,务必用示波器或逻辑分析仪确认。
下图以16位数据、32位BCLK周期为例,对比三种格式的时序差异:
2.3 帧同步与位时钟的时序关系
帧同步信号(LRCK/WS)和位时钟(BCLK)共同决定了音频数据的采样率、位宽和通道数。其核心计算公式如下:
BCLK频率 = 采样率(Fs) × 位宽(bits) × 通道数(channels)
示例:
Fs = 48000 Hz, 位宽 = 16 bits, 立体声(2通道)
BCLK = 48000 × 16 × 2 = 1.536 MHz
Fs = 48000 Hz, 位宽 = 32 bits, 立体声(2通道)
BCLK = 48000 × 32 × 2 = 3.072 MHz
帧同步信号LRCK的频率始终等于采样率Fs,其占空比通常为50%(方波),但部分CODEC也支持非50%占空比。LRCK的跳变沿(上升沿或下降沿)用于标识新数据帧的开始:
- 标准I2S约定:LRCK为低电平时传输左声道数据,高电平时传输右声道数据。数据在LRCK跳变后的第2个BCLK上升沿开始移位。
- 左对齐约定:LRCK为低电平时传输左声道,高电平时传输右声道。数据在LRCK跳变后的第1个BCLK上升沿开始移位。
- 右对齐约定:数据在LRCK跳变前的最后一个BCLK周期完成传输,MSB靠近帧结束位置。
Linux内核配置要点:在设备树(Device Tree)中,I2S控制器节点通常通过
clock-frequency 属性指定BCLK频率,或通过 frame-master 和 bitclock-master 属性指定时钟主从关系。例如:
cpu_dai: &i2s0 {
#sound-dai-cells = <0>;
clocks = <&clk IMX6QDL_CLK_SSI1>;
clock-names = "ipg", "per";
dmas = <&sdma 14 7 1>,
<&sdma 15 7 1>;
dma-names = "rx", "tx";
status = "okay";
};
2.4 数据位宽与BCLK周期数的匹配
实际系统中,BCLK周期数(即每帧的BCLK脉冲数)必须大于或等于位宽 × 通道数。常见配置如下:
| 采样率 (Fs) | 位宽 | 通道数 | 理论最小BCLK | 常用BCLK配置 | 每帧BCLK数 |
|---|---|---|---|---|---|
| 48000 | 16 | 2 | 1.536 MHz | 1.536 MHz | 32 |
| 48000 | 24 | 2 | 2.304 MHz | 3.072 MHz | 64 |
| 48000 | 32 | 2 | 3.072 MHz | 3.072 MHz | 64 |
| 96000 | 24 | 2 | 4.608 MHz | 6.144 MHz | 64 |
| 192000 | 32 | 2 | 12.288 MHz | 12.288 MHz | 64 |
工程经验:当BCLK周期数大于实际数据位数时(例如16位数据使用64BCLK/帧),多余位在标准I2S和左对齐格式中填充0,在右对齐格式中则填充在MSB之前。这会导致数据帧中存在无效位,但不会影响有效数据的传输。Linux ALSA驱动通过
snd_pcm_hw_params_set_format() 设置物理格式,硬件会自动处理填充位。
下图展示了BCLK、LRCK与数据位宽之间的匹配关系:
2.5 I2S协议在Linux内核中的抽象
在Linux ALSA框架中,I2S协议参数通过 snd_soc_dai_set_fmt() 和 snd_soc_dai_set_tdm_slot() 等API进行配置。关键参数包括:
- 格式(Format):I2S、左对齐、右对齐、DSP_A、DSP_B等。
- 时钟极性(Clock Polarity):BCLK和LRCK的有效沿(上升沿/下降沿)可配置。
- 帧长度(Frame Length):每帧的BCLK周期数,通常为32、48、64等。
- 数据偏移(Data Offset):数据相对于LRCK跳变沿的延迟BCLK数。
内核配置示例:在ASoC机器驱动中,通常通过以下代码片段配置I2S格式:
/* 设置DAI格式:标准I2S,主机模式 */
ret = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0) {
dev_err(rtd->dev, "Failed to set CPU DAI format: %d\n", ret);
return ret;
}
/* 设置TDM时隙:立体声,16位数据,32BCLK/帧 */
ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 16);
if (ret < 0) {
dev_err(rtd->dev, "Failed to set TDM slot: %d\n", ret);
return ret;
}