I2S
简介
I2S(Inter-IC Sound)是一种串行、同步通信协议,通常用于传输两个数字音频设备之间的音频数据。本模块支持多种数据格式和通信标准,适用于多种音频传输场景。
功能列表
支持多种接口:I2S 接口、PCM 接口
支持主/从模式
过零检测
支持 FIFO 功能
支持采样位宽 8/16/24/32 bits
支持立体声音频传输
支持多种数据格式,包括 I2S、MSB Justified、PCM A/B
支持中断和 DMA
静音功能
功能概述
备注
上图中分别是发送和接收过程,在收发双方时钟信号不兼容情况下,用外部 CONTROLLER 来提供同步时钟供双方使用
模块支持多种功能选择
模式选择
master、slave
传输方向
输入、输出、双工
协议标准
I2S、MSB、PCMA、PCMB
帧格式
每个通道位宽为8bit,支持的帧格式:
8/16/24/32 bits
通道类型
立体声(双声道)、单声道
采样率
8KHz、11.025KHz、16KHz、22.05KHz、24KHz、32KHz、44.1KHz、47.25KHz、48KHz、50KHz、50.04KHz、96KHz、192KHz
使用配置
参数配置
调用结构体 wm_drv_i2s_cfg_t
来配置
typedef struct {
enum wm_i2s_mode mode;
enum wm_i2s_dir dir;
enum wm_i2s_std std;
enum wm_i2s_fmt fmt;
enum wm_i2s_chan_type ctype;
enum wm_i2s_xfer_type xtype;
uint32_t sample_rate_hz;
uint32_t rx_pkt_size;
uint32_t rx_ready_th;
uint32_t tx_ready_th;
} wm_drv_i2s_cfg_t;
mode
dir
std
fmt
备注
每个通道位宽为8bit
ctype
xtype
sample_rate_hz
rx_pkt_size
rx_ready_th
tx_ready_th
备注
rx/tx treshold设置原则:内存越大,延迟要求越低,可以设置的值越大,从而可以兼容的链路抖动(出现数据阻塞,突发)情况
用户可以动态调用IOCTL API进行修改的参数
typedef struct {
enum wm_drv_i2s_ioctl_cmd cmd;
bool tx; /* tx/rx select for this setting */
union {
bool mclk_en; /**< true if enable mclk */
bool mute; /**< true if enable mute */
bool inverse; /**< true if enable clock inverse */
bool lzc; /**< true if enable left zero cross check */
bool rzc; /**< true if enable right zero cross check */
bool left; /**< if set to left channel when work in mono mode */
} u;
} wm_drv_i2s_ioctl_args_t;
cmd
WM_DRV_I2S_CMD_SET_MUTE
: 启用或禁用 TX 或 RX 的静音功能。当启用时,相应的通道将不会传输或接收音频数据WM_DRV_I2S_CMD_SET_INVERSE
: 设置时钟相位为反转模式。这通常用于与某些特定硬件的兼容性,以确保时钟信号的相位正确WM_DRV_I2S_CMD_CLS_FIFO
: 清除 TX 或 RX 的 FIFO 缓冲区。这有助于在需要时重置或清除缓冲区中的数据WM_DRV_I2S_CMD_SET_MONO_CH
: 在单声道模式下设置使用的通道。这允许选择是使用左声道还是右声道的数据WM_DRV_I2S_CMD_SET_LZC
: 启用或禁用左声道的零点交叉检查。零点交叉检查是一种技术,用于确保音频信号在交叉零点时的相位连续性WM_DRV_I2S_CMD_SET_RZC
: 启用或禁用右声道的零点交叉检查,与左声道类似
tx
u
mclk_en: 当 cmd 需要启用或禁用主时钟时使用
mute: 当 cmd 是设置静音时使用
inverse: 当 cmd 是设置时钟相位反转时使用
lzc: 当 cmd 是启用或禁用左声道零点交叉检查时使用
rzc: 当 cmd 是启用或禁用右声道零点交叉检查时使用
left: 当 cmd 是设置单声道模式下的通道时使用,选择是否使用左声道
DMA 配置
该模块利用 DMA 来实现高效收发,即 I2S 模块会依赖 DMA 的 HW,故需要 DMA 有足够的可用的 channel(2S 每个方向占用一个 DMA channel)
时钟配置
I2S 中包含多种时钟:
BCLK(串行时钟,也叫位时钟):对应数字音频的每一位数据。
WS/LRCLK(字段选择,用于选择左右声道):为 “0” 表示正在传输的是左声道的数据,为“1”表示正在传输的是右声道的数据。
SD(串行数据):用二进制补码来表示音频数据(数据传输从高位到低位)。
主要功能
发送数据功能
起始条件:
I2S 模块初始化
相关时序API:
调用
wm_drv_i2s_write_async
异步发送I2S数据,返回成功表示已经被 I2S 驱动成功处理,否则需要用户释放根据需要调用
wm_drv_i2s_write_pause
暂停 I2S 设备的写操作,此接口不会复位 HW,在 resume 可以快速进行发送根据需要调用
wm_drv_i2s_write_resume
恢复 I2S 设备的写操作,重新开始异步发送数据根据需要调用
wm_drv_i2s_write_stop
停止 I2S 设备的写操作根据需要调用
wm_drv_i2s_register_write_cb
用于注册回调函数,以便在异步写操作成功完成时被调用
结果:
成功返回
WM_ERR_SUCCESS
,失败返回对应错误码
接收数据功能
起始条件:
I2S 模块初始化
相关时序API:
调用
wm_drv_i2s_read_async
异步从 I2S 设备读取数据,返回成功表示已经被 I2S 驱动成功处理,否则需要用户释放根据需要调用
wm_drv_i2s_read_pause
暂停 I2S 设备的写操作,此接口不会复位 HW,在 resume 可以快速进行接收根据需要调用
wm_drv_i2s_read_resume
恢复 I2S 设备的读操作,重新开始异步接收数据根据需要调用
wm_drv_i2s_read_stop
停止 I2S 设备的读操作根据需要调用
wm_drv_i2s_register_read_cb
用于注册回调函数,以便在异步读操作成功完成时被调用
结果:
成功返回
WM_ERR_SUCCESS
,失败返回对应错误码
I2S设备查询功能
起始条件:
I2S 模块初始化
相关时序API:
调用
wm_drv_i2s_dump_cfgs
查询I2S设备的配置信息并打印
结果:
返回设备信息
注意事项
应用实例
int main(void)
{
int ret;
uint8_t *buf;
wm_drv_i2s_cfg_t cfg;
memset(&cfg, 0x0, sizeof(cfg));
cfg.mode = WM_I2S_MODE_MASTER;
cfg.dir = WM_I2S_DIR_OUT;
cfg.std = WM_I2S_STD_I2S;
cfg.fmt = WM_I2S_FMT_16BIT;
cfg.ctype = WM_I2S_CHAN_TYPE_STEREO;
cfg.xtype = WM_I2S_XFER_DMA;
cfg.sample_rate_hz = 8000;
wm_device_t *i2s_device = wm_drv_i2s_init("i2s", &cfg);
if (i2s_device == NULL) {
wm_log_error("I2S driver init Failed!\n");
return WM_ERR_FAILED;
}
wm_drv_i2s_register_write_cb(i2s_device, txdone_cb);
wm_drv_i2s_register_read_cb(i2s_device, rxready_cb);
buf = calloc(1, block_size);
ret = wm_drv_i2s_write_async(i2s_device, (void *)buf, block_size);
if (ret != WM_ERR_SUCCESS) {
wm_log_error("I2S driver init Failed!\n");
free_buf(buf);
return WM_ERR_FAILED;
}
while (send_pkts == 0) {
wm_os_time_delay_ms(10);
}
wm_log_info("I2S send 1 pkt done!\n");
ret = wm_drv_i2s_write_stop(i2s_device);
if (ret != WM_ERR_SUCCESS) {
wm_log_error("I2S driver stop Failed!\n");
free_buf(buf);
return WM_ERR_FAILED;
}
ret = wm_drv_i2s_deinit(i2s_device);
if (ret != WM_ERR_SUCCESS) {
wm_log_error("I2S driver deinit Failed!\n");
free_buf(buf);
return WM_ERR_FAILED;
}
return WM_ERR_SUCCESS;
}
API参考
查找TIMER相关API请参考: