音视频——I2S 协议详解

发布于:2025-06-09 ⋅ 阅读:(21) ⋅ 点赞:(0)

I2S 协议详解


I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。

1. 信号线

I2S 协议通常使用三根或四根信号线:

  • SCK (Serial Clock / Bit Clock): 串行时钟,也称位时钟。它决定了数据传输的速率,每个时钟周期传输一个数据位。
  • WS (Word Select / Left/Right Clock / Frame Sync): 字选择信号,也称左右声道时钟或帧同步信号。它指示当前传输的是左声道数据还是右声道数据,并标志一个音频帧的开始。
  • SD (Serial Data / Data Line): 串行数据线。音频数据通过这根线传输。
  • MCLK (Master Clock / System Clock - Optional): 主时钟,也称系统时钟。这根线是可选的,用于提供系统级的时钟,通常是SCK的整数倍。如果存在,它可以用于生成SCK和WS。

图1: I2S 信号线连接示意图

              -----------------------
              |       Master        |
              | (e.g., Audio Source)|
              -----------------------
                     |   |   |   |
                     |   |   |   | (Optional)
                     SCK WS  SD  MCLK
                     |   |   |   |
                     |   |   |   |
              -----------------------
              |       Slave         |
              | (e.g., Audio DAC)   |
              -----------------------

2. 时序

I2S 协议的时序是理解其工作方式的关键。

图2: I2S 数据传输时序图 (标准 I2S 模式)

             _______________   _______________   _______________
SCK: _______|               |_|               |_|               |___ ...
             _________________                 _________________
WS: _________|                 |_______________|                 |___ ...
             _________________ _______ _______ _________________
SD: _________| D0_L | D1_L |...| Dn_L | D0_R | D1_R |...| Dn_R |___ ...
             <----- Left Channel ------> <----- Right Channel ------>
             <-------------------- Audio Frame -------------------->

时序解释:

  • SCK (位时钟): 在 SCK 的每个上升沿或下降沿(取决于实现)会传输一个数据位。通常在 SCK 的下降沿采样数据,在 SCK 的上升沿输出数据。
  • WS (字选择):
    • 当 WS 为低电平(通常为 0)时,表示传输的是左声道数据。
    • 当 WS 为高电平(通常为 1)时,表示传输的是右声道数据。
    • WS 信号在每个音频帧开始时翻转,并且在整个声道数据传输过程中保持稳定。
    • WS 信号的变化发生在 SCK 信号的第一个位之前一个 SCK 周期。
  • SD (串行数据):
    • 数据通常是 MSB (Most Significant Bit) 在前,LSB (Least Significant Bit) 在后。
    • 每个声道的字长可以是 16 位、24 位、32 位等,具体取决于设备支持的精度。
    • 数据的有效位通常从 WS 变化后第二个 SCK 周期开始。这意味着在 WS 变化后的第一个 SCK 周期,数据线上是无用的。这种偏移是 I2S 标准的一个特点。

图3: I2S 数据位对齐方式 (举例: 16位数据)

             _______________   _______________   _______________
SCK: _______|               |_|               |_|               |___ ...
             _________________                 _________________
WS: _________|                 |_______________|                 |___ ...
             _________________ _________________________________
SD: _________|  Dummy Bit(s)   |  D15 | D14 | ... | D1 | D0     |___ ...
             <----------------> <----------- 16-bit Data ----------->
             ^WS change
             ^First SCK after WS change
                               ^Actual data starts here

数据位对齐方式的特点:

  • I2S 协议规定,在 WS 信号切换后,SD 线上的第一个有效数据位(MSB)在第二个 SCK 的上升沿或下降沿开始。这意味着在 WS 切换后的第一个 SCK 周期,SD 线上的数据是无效的,或者说是一个“虚拟位”。这种对齐方式被称为“左对齐,数据延迟一位”。
  • 数据字长可以小于或等于 SCK 周期数减去一个虚拟位的数量。例如,如果一个声道传输 16 位数据,并且 SCK 周期数为 32 (对应一个 32 位字),那么 I2S 会在传输完 16 位数据后,在 SD 线上输出 16 个无效位。

3. I2S 的几种模式

除了标准的 I2S 模式,还有一些其他的模式,但标准 I2S 是最常见的。

  • 标准 I2S 模式 (Standard I2S): 如上所述,数据在 WS 变化后延迟一个 SCK 周期。
  • 左对齐模式 (Left-Justified / MSB-Justified): 数据没有延迟,MSB 在 WS 变化后的第一个 SCK 周期就出现。
  • 右对齐模式 (Right-Justified / LSB-Justified): 数据被填充到字的右侧,LSB 在字的最后一个 SCK 周期出现。
  • PCM 模式 (Pulse Code Modulation / DSP Mode): 用于传输 TDM (Time Division Multiplexed) 数据,通常用于多通道音频。

I2S 协议的应用流程

I2S 协议的应用通常涉及以下步骤:

  1. 硬件连接:
    • 连接音频源(如微控制器、DSP)和音频接收器(如 DAC、ADC)的 I2S 引脚。
    • 确保 SCK、WS、SD 甚至 MCLK(如果使用)正确连接。
  2. 配置时钟:
    • 确定 I2S 的主设备和从设备。通常音频源是主设备(提供 SCK 和 WS),音频接收器是从设备。
    • 配置主设备生成正确的 SCK 频率和 WS 频率。SCK 频率通常是采样率的整数倍(例如,32 * 采样率),WS 频率等于采样率。
    • 如果使用 MCLK,确保其频率满足设备要求。
  3. 配置数据格式:
    • 确定数据字长(例如 16 位、24 位、32 位)。
    • 确定是立体声还是单声道传输。
    • 配置数据对齐方式(标准 I2S 模式是最常见的)。
  4. 数据传输:
    • 发送端 (Master): 根据 I2S 时序,将音频数据逐位地写入 SD 线,并同步 SCK 和 WS 信号。通常使用 DMA (Direct Memory Access) 来高效地传输数据,以减轻 CPU 负担。
    • 接收端 (Slave): 根据 I2S 时序,在 SCK 触发时从 SD 线读取数据,并根据 WS 信号区分左右声道。接收到的数据通常存储在缓冲区中,然后由 DAC 转换为模拟信号输出。
  5. 错误处理 (可选):
    • 可以实现一些错误检测机制,例如校验和,以确保数据完整性。
    • 处理同步丢失等问题。

I2S 协议代码示例 (基于 STM32 微控制器)

由于 I2S 协议通常在嵌入式系统中实现,我将提供一个基于 STM32 微控制器的代码示例。STM32 微控制器集成了专门的 I2S 外设,这使得实现变得相对简单。

这个示例将演示如何使用 STM32 的 I2S 外设作为主设备(Master)输出音频数据。假设我们有一个 STM32F4 系列的微控制器,并连接到一个 I2S DAC(例如 PCM5102A)。

重要提示:

  • 这只是一个概念性的示例,实际应用需要根据你的具体硬件(DAC型号、引脚连接、时钟源)进行调整。
  • 你需要配置 STM32CubeMX 或手动配置寄存器来初始化 I2S 外设和相关的 GPIO。
  • 音频数据(audio_buffer)需要由你的应用生成,例如从 ADC 采集、从文件读取或生成测试音。

1. STM32CubeMX 配置 (概念性描述)

在 STM32CubeMX 中,你需要:

  • 启用 I2S 外设: 选择一个支持 I2S 的 SPI 外设 (例如 SPI2)。
  • 配置模式: 设置为 Master Transmit。
  • 时钟配置: 根据你的系统时钟和所需的采样率,配置 I2S 的时钟源和分频器,以生成正确的 SCK 频率。
  • DMA 配置: 为 I2S TX 配置 DMA 通道,以实现高效数据传输。
  • GPIO 配置: 配置 I2S 相关的引脚(SCK, WS, SD)为复用功能。

2. 核心代码示例

#include "main.h" // 包含 CubeMX 生成的头文件
#include "stm32f4xx_hal.h" // HAL 库头文件

// 假设 I2S 外设句柄已在 main.c 中定义并初始化
extern I2S_HandleTypeDef hi2s2; // 假设使用 SPI2 作为 I2S 外设

// 定义音频数据缓冲区
#define AUDIO_BUFFER_SIZE  4096 // 缓冲区大小,可以根据需要调整
#define SAMPLE_RATE        44100 // 采样率 (Hz)
#define CHANNELS           2     // 声道数 (立体声)
#define BITS_PER_SAMPLE    16    // 每样本位数

// 16位立体声音频数据缓冲区 (左声道L,右声道R交替)
// 假设数据格式为 S16_LE (Signed 16-bit, Little Endian)
int16_t audio_buffer[AUDIO_BUFFER_SIZE];

// DMA 回调函数 - 缓冲区传输完成
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
    if (hi2s->Instance == hi2s2.Instance)
    {
        // 缓冲区已传输完成,可以重新填充缓冲区
        // 在这里可以处理音频数据的读取、生成或切换到下一个缓冲区
        // 例如,从SD卡读取下一块音频数据
        // 为了简单,我们这里只是重新启动传输,实际应用中会加载新数据
        HAL_I2S_Transmit_DMA(&hi2s2, (uint16_t*)audio_buffer, AUDIO_BUFFER_SIZE);
    }
}

// DMA 回调函数 - 半缓冲区传输完成
void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
    if (hi2s->Instance == hi2s2.Instance)
    {
        // 缓冲区的前半部分已传输完成
        // 在这里可以填充缓冲区的后半部分,实现“双缓冲”机制
        // 从而确保连续不间断的音频输出
    }
}

// 初始化音频缓冲区 (用于演示,实际应由音频源填充)
void init_audio_buffer(void)
{
    for (int i = 0; i < AUDIO_BUFFER_SIZE / 2; i++)
    {
        // 生成一个简单的正弦波作为测试音
        // 左声道
        audio_buffer[2 * i] = (int16_t)(30000.0 * sin(2.0 * M_PI * 440.0 * i / SAMPLE_RATE));
        // 右声道 (这里和左声道相同,可以根据需要修改)
        audio_buffer[2 * i + 1] = (int16_t)(30000.0 * sin(2.0 * M_PI * 440.0 * i / SAMPLE_RATE));
    }
}


int main(void)
{
    /* MCU Configuration--------------------------------------------------------*/
    HAL_Init(); // 初始化 HAL 库

    SystemClock_Config(); // 配置系统时钟 (由 CubeMX 生成)

    /* Initialize all configured peripherals */
    MX_GPIO_Init(); // 初始化 GPIO (由 CubeMX 生成)
    MX_DMA_Init();  // 初始化 DMA (由 CubeMX 生成)
    MX_SPI2_I2S_Init(); // 初始化 I2S (由 CubeMX 生成)

    // 初始化音频缓冲区
    init_audio_buffer();

    // 启动 I2S DMA 传输
    // hi2s2: I2S 句柄
    // (uint16_t*)audio_buffer: 待传输的数据 (I2S DMA 通常按16位传输)
    // AUDIO_BUFFER_SIZE: 传输的样本数量 (对于16位立体声,这里是总的16位样本数,即声道数 * 单声道样本数)
    HAL_I2S_Transmit_DMA(&hi2s2, (uint16_t*)audio_buffer, AUDIO_BUFFER_SIZE);

    while (1)
    {
        // 主循环,可以执行其他任务
        // 音频数据传输由 DMA 自动完成
    }
}

// 确保在你的 main.c 文件中,hi2s2 句柄已正确初始化,例如:
/*
I2S_HandleTypeDef hi2s2;

void MX_SPI2_I2S_Init(void)
{
  hi2s2.Instance = SPI2;
  hi2s2.Init.Mode = I2S_MODE_MASTER_TX;
  hi2s2.Init.Standard = I2S_STANDARD_PHILIPS; // 标准 I2S 模式
  hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B_EXTENDEDSA; // 16位数据,32位帧
  hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE; // 如果需要输出 MCLK
  hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_44K; // 44.1kHz 采样率
  hi2s2.Init.CPOL = I2S_CPOL_LOW; // 时钟极性
  hi2s2.Init.ClockSource = I2S_CLOCK_PLLR; // I2S 时钟源 (例如 PLLR)
  hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE; // 单工模式 (发送)

  if (HAL_I2S_Init(&hi2s2) != HAL_OK)
  {
    Error_Handler();
  }
}
*/

代码解释:

  • audio_buffer: 这是一个 int16_t 类型的数组,用于存储音频数据。由于是立体声,数据通常是左声道和右声道交替存储。AUDIO_BUFFER_SIZE 定义了缓冲区中 int16_t 元素的数量。

  • HAL_I2S_TxCpltCallbackHAL_I2S_TxHalfCpltCallback:

    这是 HAL 库提供的 DMA 传输完成和半传输完成的回调函数。

    • 当 DMA 传输完整个 audio_buffer 时,会调用 HAL_I2S_TxCpltCallback。你可以在这里加载下一批音频数据。
    • 当 DMA 传输完 audio_buffer 的前半部分时,会调用 HAL_I2S_TxHalfCpltCallback。这允许你在上半部分数据传输的同时填充下半部分数据,从而实现连续的、无间断的音频播放(双缓冲机制)。
  • HAL_I2S_Transmit_DMA(): 这是 HAL 库函数,用于启动 I2S 通过 DMA 进行数据传输。它将 audio_buffer 中的数据发送到 I2S 外设。

  • init_audio_buffer(): 一个简单的函数,用于填充音频缓冲区。在实际应用中,这里的数据可能来自麦克风、SD 卡、蓝牙模块或其他数字音频源。

  • MX_SPI2_I2S_Init() (在 main.c 或其他文件中): 这个函数(通常由 CubeMX 生成)初始化 I2S 外设,包括设置工作模式(主发送)、标准(飞利浦 I2S)、数据格式、时钟源和频率等。

运行流程:

  1. 初始化: 微控制器启动,HAL 库和 I2S 外设被初始化。
  2. 数据填充: audio_buffer 被填充初始音频数据。
  3. 启动 DMA 传输: HAL_I2S_Transmit_DMA() 被调用,I2S 外设开始从 audio_buffer 中通过 DMA 自动读取数据,并按照 I2S 协议时序将其发送到 SD 线。
  4. 连续播放: 当 DMA 传输到 audio_buffer 的一半或全部完成时,会触发相应的回调函数。在这些回调函数中,你可以填充或加载新的音频数据到 audio_buffer,从而实现连续的音频播放。这种双缓冲或多缓冲机制对于不间断音频流至关重要。
  5. DAC 接收: 连接到 STM32 的 I2S DAC 会接收 SCK、WS 和 SD 信号,并根据这些信号将数字音频数据转换为模拟音频信号输出到扬声器或耳机。

网站公告

今日签到

点亮在社区的每一天
去签到