2. I2S/PCM总线协议:I2S时序、数据格式与主从模式配置
I2S(Inter-IC Sound)总线是嵌入式音频系统中连接SoC与Codec的事实标准串行接口。理解I2S的时序、数据格式以及主从模式是配置Codec驱动寄存器的基础。本章从物理层时序出发,逐步深入到数据对齐格式与模式配置。
2.1 I2S总线信号与基本时序
I2S总线由三条主要信号线组成,所有数据传输均与位时钟同步:
| 信号名称 | 缩写 | 方向(主模式) | 功能描述 |
|---|---|---|---|
| 位时钟 | BCLK / SCK | 输出 | 每个脉冲对应一个数据位的传输,频率 = 采样率 × 位宽 × 通道数 |
| 帧时钟 | LRCK / WS | 输出 | 左右声道选择,高电平通常表示右声道,低电平表示左声道 |
| 串行数据 | SD / DATA | 双向 | 按位串行传输音频采样数据,通常为二进制补码格式 |
BCLK与LRCK的关系是I2S时序的核心:LRCK在BCLK的下降沿变化,而数据位在BCLK的上升沿被采样。标准I2S协议要求数据在LRCK变化后的第二个BCLK上升沿开始传输,且数据最高位(MSB)最先发送。
关键时序参数: BCLK频率 = fs × bits_per_sample × 2(立体声)。例如,48kHz采样率、16位立体声:BCLK = 48000 × 16 × 2 = 1.536 MHz。
2.2 数据对齐格式:左对齐、右对齐与I2S标准
数据对齐格式决定了音频采样数据在BCLK周期内的放置位置。三种主流格式在嵌入式Linux驱动中均需支持,Codec的寄存器配置通常通过数字音频接口(DAI)格式选择位来切换。
| 格式名称 | 数据起始位置 | MSB位置 | 典型应用 |
|---|---|---|---|
| I2S标准(飞利浦标准) | LRCK变化后的第2个BCLK上升沿 | 紧接起始位 | 最广泛,多数Codec默认支持 |
| 左对齐(Left Justified) | LRCK变化后的第1个BCLK上升沿 | 起始位即为MSB | 部分DAC/ADC,如CS43L22 |
| 右对齐(Right Justified) | 数据在BCLK周期末尾对齐 | MSB位于LSB之前,但整体靠右 | 日本厂商Codec,如AKM系列 |
驱动配置要点: 在Linux ALSA SoC框架中,通过
snd_soc_dai_set_fmt() 设置DAI格式。例如:SND_SOC_DAIFMT_I2S 对应I2S标准,SND_SOC_DAIFMT_LEFT_J 对应左对齐,SND_SOC_DAIFMT_RIGHT_J 对应右对齐。Codec侧寄存器通常位于 Digital Audio Interface Format 或 Serial Audio Data Format 寄存器中。
2.3 主从模式配置
I2S总线的主从模式决定了BCLK和LRCK信号的生成方。在嵌入式系统中,SoC通常作为主设备,Codec作为从设备,但某些场景下Codec也可以作为主设备(例如USB音频桥接芯片)。
2.3.1 主模式(Master)
- 职责: 生成BCLK和LRCK时钟信号,从设备同步于这些时钟。
- 驱动配置: 在SoC侧设置
SND_SOC_DAIFMT_CBS_CFS(Codec作为从,SoC作为主)。 - Codec寄存器: 通常需要将Codec的
Master/Slave位设置为Slave(0),并配置时钟分频器以匹配SoC输出的时钟。
2.3.2 从模式(Slave)
- 职责: 接收外部BCLK和LRCK,不主动产生时钟。
- 驱动配置: 在SoC侧设置
SND_SOC_DAIFMT_CBM_CFM(Codec作为主,SoC作为从)。 - Codec寄存器: 将
Master/Slave位设置为Master(1),并配置内部PLL或时钟源以生成正确的BCLK/LRCK。
常见配置陷阱: 主从模式不匹配是I2S通信失败的常见原因。如果SoC和Codec都配置为主模式,总线将出现时钟冲突;如果都配置为从模式,则无时钟信号,数据无法传输。调试时首先检查
/sys/kernel/debug/asoc/ 下的DAI链路状态,确认时钟方向。
2.4 典型Codec寄存器配置示例
以Wolfson WM8960为例,其数字音频接口寄存器(地址0x04)的配置如下:
/* WM8960 数字音频接口寄存器 (0x04) 位定义 */
#define WM8960_IFACE_FORMAT_MASK (3 << 0) /* 位[1:0]:数据格式 */
#define WM8960_IFACE_FORMAT_I2S (0 << 0) /* 00: I2S标准 */
#define WM8960_IFACE_FORMAT_LEFT_J (1 << 0) /* 01: 左对齐 */
#define WM8960_IFACE_FORMAT_RIGHT_J (2 << 0) /* 10: 右对齐 */
#define WM8960_IFACE_LRP (1 << 2) /* 位2:LRCK极性反转 */
#define WM8960_IFACE_BCP (1 << 3) /* 位3:BCLK极性反转 */
#define WM8960_IFACE_MS (1 << 4) /* 位4:主从选择,1=主,0=从 */
#define WM8960_IFACE_DL_MASK (3 << 5) /* 位[6:5]:数据字长 */
#define WM8960_IFACE_DL_16BIT (0 << 5) /* 00: 16位 */
#define WM8960_IFACE_DL_20BIT (1 << 5) /* 01: 20位 */
#define WM8960_IFACE_DL_24BIT (2 << 5) /* 10: 24位 */
#define WM8960_IFACE_DL_32BIT (3 << 5) /* 11: 32位 */
/* 配置示例:I2S标准、16位、从模式 */
snd_soc_component_write(component, 0x04,
WM8960_IFACE_FORMAT_I2S |
WM8960_IFACE_DL_16BIT |
(0 << 4)); /* MS=0,从模式 */
寄存器配置流程: 在Codec驱动的
hw_params() 回调中,根据传入的采样率、位宽和DAI格式,动态计算并写入上述寄存器字段。注意:主从模式通常在 set_fmt() 回调中配置,而非 hw_params()。
2.5 调试与验证方法
- 示波器测量: 测量BCLK频率是否等于 fs × bits × channels;检查LRCK频率是否等于采样率;观察数据起始位置是否符合所选格式。
- ALSA调试文件系统: 查看
/proc/asound/card0/codec#0中的寄存器转储,确认DAI格式寄存器值。 - 逻辑分析仪: 使用廉价逻辑分析仪捕获I2S总线,对比数据手册中的时序图。
- 驱动日志: 在
snd_soc_dai_set_fmt()和snd_soc_dai_hw_params()中添加dev_dbg()打印,确认传入的参数值。
掌握I2S时序、数据格式和主从模式后,下一层就是Codec内部音频路径的寄存器配置——从数字接口到模拟输出的完整信号链路。