跳转至

29 i2s-音频总线控制

作者:陈媛媛 | 最后修改:2026-03-10

一、概述

I2S(Inter-IC Sound,集成电路间音频总线)是由飞利浦(Philips) 于 1986 年制定的串行音频传输标准,专门用于解决数字音频设备(如 MCU、DSP、DAC、ADC、Codec 等)之间的高效、低干扰音频数据传输问题。它通过分离 “数据信号” 与 “时钟信号”,避免了通用总线(如 I2C、SPI)在音频传输中易出现的时序抖动(Jitter) 问题,是嵌入式音频系统中最常用的接口之一。

合宙的模块的音频接口中,有 I2S_BCLK、I2S_LRCK、I2S_DIN、I2S_DOUT、I2S_MCLK 是常见的信号管脚,它们的含义和作用如下:

1. I2S_BCLK(Bit Clock,位时钟)

含义:也称为串行时钟(Serial Clock,SCK),是数字音频传输中的基础时钟信号。

作用:用于同步每一位音频数据的传输节奏。每传输 1 位二进制数据(0 或 1),BCLK 就会产生 1 个时钟脉冲。例如,传输 16 位音频数据时,需要 16 个 BCLK 脉冲;传输 24 位数据时,需要 24 个 BCLK 脉冲。

关键:BCLK 的频率 = 音频采样率 × 位深 × 声道数(如 44.1kHz 采样率、16 位深、立体声时,BCLK = 44100 × 16 × 2 = 1.4112MHz)。

2. I2S_LRCK(Left/Right Clock,左右声道时钟)

含义:也称为字选择线(Word Select,WS),用于区分左声道和右声道的音频数据。

作用:通过电平高低标识当前传输的是左声道还是右声道数据:

通常高电平表示左声道,低电平表示右声道(可通过芯片配置调整); 每个声道的一组完整数据(如 16 位 / 24 位)对应 LRCK 的一个电平周期,其频率等于音频采样率(如 44.1kHz 采样率时,LRCK 频率为 44.1kHz)。

3. I2S_DIN(Data In,数据输入)

含义:串行数据输入线。

作用:用于接收外部设备传输过来的数字音频数据(如 PCM 格式),数据在 BCLK 和 LRCK 的同步下,按位依次传入芯片(如 DAC 解码器从 DIN 接收数据,再转换为模拟音频)。

4. I2S_DOUT(Data Out,数据输出)

含义:串行数据输出线。

作用:用于向外部设备发送数字音频数据。例如,ADC 编码器将模拟音频转换为数字信号后,通过 DOUT 输出给 MCU 或其他处理设备,数据同样在 BCLK 和 LRCK 的同步下传输。

5. I2S_MCLK(Master Clock,主时钟)

含义:也称为系统时钟(System Clock),是音频系统的基准时钟。

作用:为整个音频传输链路提供时钟参考,确保各设备(如 Codec、DAC、ADC)的工作节奏一致,减少因时钟偏差导致的音质失真(如噪声、失真)。

特点:频率通常是采样率的整数倍(例如 44.1kHz 采样率时,256×MCLK = 44100 × 256 = 11.2896MHz)。

二、核心示例

1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

-- 注意:此示例仅演示I2S API的使用方法
-- 实际录音需要配合音频编解码芯片和适当的硬件连接

-- 定义录音文件路径
local recordPath = "/record.pcm"

-- i2s数据接收buffer
local rx_buff = zbuff.create(320000)

-- i2s数据接收回调
local function record_cb(id, buff_ptr)
    if buff_ptr then
        -- buff_ptr是zbuff指针,表示接收到数据
        log.info("I2S", "通道", id, "已接收", rx_buff:used(), "字节")
        -- 注意:这里不要关闭I2S,否则只能接收一次数据
    else
        log.info("I2S", "通道", id, "发送完成")
    end
end

-- 开启i2s(录音配置:8kHz,16位,单声道)
if i2s.setup(0, 0, 8000, 16, i2s.MONO_L, i2s.MODE_I2S, 16) then
    log.info("I2S", "初始化成功")

    -- 注册i2s接收回调
    i2s.on(0, record_cb)

    -- 开始接收数据
    if i2s.recv(0, rx_buff, 3200) then
        log.info("开始录音...")

        -- 等待录音时间(5秒)
        sys.wait(5000)

        log.info("录音5秒结束")

        -- 关闭I2S
        i2s.close(0)

        -- 保存录音数据(PCM格式)
        if rx_buff:used() > 0 then
            local file = io.open(recordPath, "wb")
            if file then
                file:write(rx_buff:query())
                file:close()
                log.info("录音文件已保存:", recordPath, ",大小:", rx_buff:used(), "字节")
            else
                log.error("保存文件失败")
            end
        else
            log.warn("未接收到音频数据")
        end
    else
        log.error("I2S接收启动失败")
    end
else
    log.error("I2S初始化失败")
end

三、常量详解

核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;

常量 类型 解释 参数示例
i2s.MODE_I2S number I2S 标准格式 i2s.setup(0, 0, 8000, 16, i2s.MONO_L, i2s.MODE_I2S) -- 开启i2s。传输数据从最高位开始传输
i2s.MODE_LSB number LSB 优先格式,数字音频数据的传输顺序从 “最低位” 开始 i2s.setup(0, 0, 8000, 16, i2s.MONO_L, i2s.MODE_LSB) -- 开启i2s,传输数据从最低位开始传输
i2s.MODE_MSB number MSB 优先格式,数字音频数据的传输顺序从 “最高位” 开始 i2s.setup(0, 0, 8000, 16, i2s.MONO_L, i2s.MODE_MSB) -- 开启i2s,,传输数据从最高位开始传输
i2s.MONO_L number 左声道 i2s.setup (0, 0, 8000, 16, i2s.MONO_L, i2s.MODE_I2S) -- 开启 i2s,使用左声道
i2s.MONO_R number 右声道 i2s.setup (0, 0, 8000, 16, i2s.MONO_R, i2s.MODE_I2S) -- 开启 i2s,使用右声道
i2s.STEREO number 立体声 i2s.setup (0, 0, 8000, 16, i2s.STEREO, i2s.MODE_I2S) -- 开启 i2s,使用立体声

四、函数详解

4.1 i2s.setup(id, mode, sample, bitw, channel, format, framebit)

功能

初始化 I2S

参数

id

参数含义:I2S的通道号
数据类型:number
取值范围:0
是否必选:必须传入此参数;
注意事项:仅支持0
参数示例:0,通道0 

mode

参数含义:主从模式;
数据类型:number
取值范围:01
是否必选:非必须传入此参数;
注意事项:0为主机模式,1为从机模式,默认为主模式;
参数示例:0,开启主机模式;

sample

参数含义:采样率;
数据类型:number
取值范围:8000,16000,24000,32000,48000,20050,44100
是否必选:非必须传入此参数;
注意事项:默认44100
参数示例:16000,使用每秒16000的采样率进行音频采样;

bitw

参数含义:采样位深;
数据类型:number
取值范围:8162432
是否必选:非必须传入此参数;
注意事项:默认使用16
参数示例:16,每次采样使用16位存放模拟量;

channel

参数含义:声道;
数据类型:number
取值范围:i2s.MONO_L(左声道),i2s.MONO_R(右声道),i2s.STEREO(立体声);
是否必选:必须传入此参数;
参数示例:i2s.MONO_L

format

参数含义:通讯格式;
数据类型:number
取值范围:i2s.MODE_I2Si2s.MODE_LSBi2s.MODE_MSB
是否必选:非必须传入此参数;
注意事项:需要根据实际的芯片支持的格式设置,比如ES8311 支持的是i2s.MODE_I2S
参数示例:i2s.MODE_I2S,使用标准I2S 协议通信;

framebit

参数含义:i2s通道数据位数
数据类型:number
取值范围:1632
是否必选:非必须传入此参数;
注意事项:默认为16,根据具体的音频编解码芯片要求设置
参数示例:16,数据通道位数为16位;

返回值

成功 true, 失败 false

示例

i2s.setup(0, 0, 8000, 16, i2s.MONO_L, i2s.MODE_I2S, 16)           -- 开启i2s

4.2 i2s.send(id, data, len)

功能

发送 I2S 数据

参数

id

参数含义:I2S的通道号
数据类型:number
取值范围:0
是否必选:必须传入此参数;
注意事项:仅支持0
参数示例:0,通道0 

data

参数含义:需要发送的数据;
数据类型:string或者zbuff
取值范围:无;
是否必选:必须传入此参数;
注意事项:数据, 可以是字符串或zbuff
参数示例:"xxx",发送xxx 的数据 

len

参数含义:数据长度,单位字节;
数据类型:number
取值范围:大于等于0
是否必选:非必须传入此参数;
注意事项:
        1. data 为字符串,可以不传入此参数
            1.1 如果没有传入此值,或者此值为nil,则表示data字符串长度
            1.2 如果传入此值,则表示发送data 的长度,单位为字节
        2. data zbuff,必须传入此参数
            2.1 如果值小于zbuff 实际长度,则表示发送的实际数据长度(发送位置从zbuff起始位置开始)
            2.2 如果值大于或者等于zbuff 实际长度,将zbuff 所有的数据全部发送
参数示例:3,需要传输的长度为3 

返回值

成功 true, 失败 false

示例

local f = io.open("/luadb/abc.wav")
while true do
    local data = f:read(4096)
    if not data or #data == 0 then
        break
    end
    i2s.send(0, data)
    sys.wait(100)
end

4.3 i2s.recv(id, buffer, len)

功能

接收 i2s 数据

参数

id

参数含义:I2S的通道号
数据类型:number
取值范围:0
是否必选:必须传入此参数;
注意事项:仅支持0
参数示例:0,通道0 

buffer

参数含义:数据缓存区;
数据类型:zbuff
取值范围:zbuff对象
是否必选:必须传入此参数;
注意事项:1. 必须是通过zbuff.create()创建的zbuff对象
          2. 接收的数据会存储在此zbuff中
参数示例:无;

len

参数含义:单次返回的数据长度;
数据类型:number
取值范围:大于0
是否必选:必须传入此参数;
注意事项:单次返回的数据长度需要和buffer zbuff 的大小保持一致;
参数示例:5,单次返回的数据长度为5

返回值

成功 true, 失败 false

示例

local buffer = zbuff.create(3200)
local succ = i2s.recv(0, buffer, 3200);

4.4 i2s.close(id)

功能

关闭 i2s

参数

id

参数含义:I2S的通道号
数据类型:number
取值范围:0
是否必选:必须传入此参数;
注意事项:仅支持0
参数示例:0,通道0 

返回值

示例

i2s.close(0)

4.5 i2s.on(id, func)

功能

注册 I2S 事件回调

参数

id

参数含义:I2S的通道号
数据类型:number
取值范围:0
是否必选:必须传入此参数;
注意事项:仅支持0
参数示例:0,通道0 

function

参数含义:事件回调函数,函数的定义格式:
         function i2s_callback(id, buff)
             if buff then
                 log.info("i2s get data in zbuff")
             else
                 log.info("i2s tx one block done")
             end
         end
         回调函数携带两个回调参数:
             -- 参数含义:I2S的通道号;
             -- 数据类型:number;
             -- 取值范围:0;
             -- 是否必选:必须传入此参数;
             -- 注意事项:仅支持0;
             -- 参数示例:0,通道0 ;
             id

             --参数含义:标识当前的发送或者接收数据情况;
             --数据类型:userdata或nil;
             --取值范围:当接收到数据时为非nil,发送完成时为nil;
             --是否必选:是;
             --注意事项:1. 当 buff_ptr 不为 nil 时,表示接收到数据,数据存放在调用i2s.recv时传入的zbuff中;
             --          2. 当buff 为 nil 的时候,表示发送数据完毕
             --参数示例:nil 表示发送完成,非nil表示接收到数据;
             buff
数据类型:function
是否必选:是
注意事项:无
参数示例:
        local function i2s_callback(id, buff_ptr)
            if buff_ptr then
                log.info("i2s接收到数据")
            else
                log.info("i2s发送完成")
            end
        end

返回值

示例

local function i2s_callback(id, buff_ptr)
    if buff_ptr then
        log.info("i2s接收到数据")
    else
        log.info("i2s发送完成")
    end
end

i2s.on(0, i2s_callback)

4.6 i2s.txStat(id)

功能

获取 i2s 的发送缓冲区状态

参数

id

参数含义:I2S的通道号
数据类型:number
取值范围:0
是否必选:必须传入此参数;
注意事项:仅支持0
参数示例:0,通道0 

返回值

local max, remain = i2s.txStat(id)

max

参数含义:底层发送缓冲区可用空间总大小,单位为字节;
数据类型:number
取值范围:大于等于0
是否必选:必须接收此参数;
注意事项:
        1. 如果小于2倍的remain,则需等待100ms 后再次判断,一直等到大于2倍的remain(待发送的数据长度),才可以继续进行数据发送;
参数示例:0,底层缓冲区大小为0 

remain

参数含义:待发送的数据长度,单位为字节;
数据类型:number
取值范围:大于等于0
是否必选:必须接收此参数;
注意事项:无;
参数示例:100,待发送的数据长度还剩下。100个字节;

示例

-- 读取发送缓冲区的状态, 从而判断是否需要继续传入音频数据
local max, remain = i2s.txStat(0)
log.info("i2s发送缓冲区状态", max, remain)

五、产品支持说明

I2S 接口支持如下模块,Air780EPM,Air780EHM,Air780EHV,Air780EGP,Air780EGH,Air780EGG,Air8000系列