6.5.5. 设计指南¶
6.5.5.1. 源码说明¶
源代码位于:
bsp/artinchip/drv/i2s/drv_i2s_sound.c: I2S的driver层驱动
bsp/artinchip/hal/i2s/hal_i2s.c: i2s模块的hal层驱动
bsp/artinchip/include/hal/hal_i2s.h: i2s模块的hal层头文件,用于定义寄存器相关
bsp/artinchip/include/hal/hal_i2s_format.h: i2s模块的hal层头文件,用于i2s传输格式的定义
6.5.5.2. 模块架构设计¶
6.5.5.2.1. RT-Thread 音频框架¶
RT-Thread定义了一套音频框架,driver层的驱动就是为了对接该音频框架。该框架在录音端和播放端采用了两种不同的机制,现对录音端和播放端的框架进行简单说明。
6.5.5.2.1.1. 播放端框架¶

如上图所示,应用层读取的音频数据写入到内存池的block中,并用数据队列对内存池的block数据进行管理。从数据队列中依次取出音频数据,写入到TX buffer中,TX buffer在原理上是一个环形缓冲区,最后通过DMA将音频数据写入到硬件的TXFIFO中。所以,playback端的driver层驱动主要是负责TX buffer环形缓冲区的管理,及时写入音频数据,并用DMA搬运到硬件。
6.5.5.2.1.2. 录音端框架¶

如上图所示,RT-Thread 音频框架在录音端的设计与播放端不同。在录音端框架虚拟了一个pipe设备,其实就是一个ringbuffer,读写pipe设备就是对ringbuffer的读写。driver层驱动负责管理一个RX buffer,RX buffer在逻辑上也是一个环形缓冲区。DMA负责将MIC端接收到的数据搬运到RX buffer,再通过写pipe设备将音频数据写入到pipe的ringbuffer中。应用层代码则通过 rt_device_read
每次从pipe设备中读取音频数据,再将音频数据写入到wav文件。
注解
与audio不同的是,i2s还需要实现codec芯片的驱动设计才能完成音频的播放和录音功能
6.5.5.3. 关键流程设计¶
6.5.5.3.1. 初始化流程¶
释放reset和clock信号
注册i2s设备
6.5.5.3.2. playback流程¶
6.5.5.3.2.1. init流程¶
初始化DMA传输的起始地址,buf_len以及period_len
注册hal层的回调函数
初始化codec
I2S模块使用DMA传输音频数据,DMA采用环形链表形式,依次将音频数据传送到硬件。所以需要配置DMA传输时的起始地址(即TX buffer地址)以及buf_len,period_len。在driver层驱动,将buf_len配置为period_len的4倍,DMA每传输period_len长度的数据,触发一次DMA中断,通知CPU向TX buffer中写入数据。
6.5.5.3.2.2. start流程¶
填充TX buffer
设置DMA传输的参数,调用
hal_i2s_playback_start
开始音频数据传输调用codec_start启用codec芯片
使能PA
为保证DMA传输音频数据的连续性,需要在DMA开始传输前,先向TX buffer中填充数据。在playback的driver层驱动,是将TX buffer填充满后,才开始DMA的传输。
6.5.5.3.2.3. DMA中断流程¶
DMA每传输完period_len长度的数据后,触发一次DMA中断,然后通过DMA回调函数的逐级调用,最终调用 rt_audio_tx_complete
对TX buffer进行填充,每次填充period_len长度的音频数据。
6.5.5.3.3. record流程¶
6.5.5.3.3.1. init流程¶
初始化DMA传输的起始地址,buf_len以及period_len
注册hal层的回调函数
I2S模块使用DMA传输音频数据,DMA采用环形链表形式,依次将音频数据传送到硬件。所以需要配置DMA传输时的起始地址(即RX buffer地址)以及buf_len,period_len。在driver层驱动,将buf_len配置为period_len的2倍,DMA每传输period_len长度的数据,触发一次DMA中断,通知CPU向pipe设备写入数据。
6.5.5.3.3.2. start流程¶
按照RT-Thread audio的框架,在执行 rt_device_open
时,就会调用start流程,开始音频的录制,然后再通过 rt_device_control
设置音频的格式(采样率,通道数等)。按照这个流程,最开始可能会录制一段不符合设置的音频格式的数据,这显然是不合理的。所以,在driver层的驱动实现中,start流程并未做任何处理,而是在设置完音频格式后才开始音频的录制。
6.5.5.3.3.3. DMA中断流程¶
DMA每传输完period_len长度的数据后,触发一次DMA中断,然后通过DMA回调函数的逐级调用,最终调用 rt_audio_rx_done
,将RX buffer的数据写入到pipe设备,每次写入period_len长度的音频数据。
6.5.5.4. 数据结构设计¶
6.5.5.4.1. hal层数据结构¶
hal_i2s.h的数据结构
struct aic_i2s_buf_info
{
void *buf;
uint32_t buf_len;
uint32_t period_len;
};
struct aic_i2s_transfer_info
{
struct aic_dma_chan *dma_chan;
struct aic_audio_buf_info buf_info;
int transfer_type;
};
struct aic_i2s_ctrl
{
unsigned long reg_base;
uint32_t irq_num;
uint32_t clk_id;
uint32_t idx;
struct aic_audio_transfer_info tx_info; //TX buffer的参数
struct aic_audio_transfer_info rx_info; //RX buffer的参数
i2s_callback callback;
void *arg;
};
hal_i2s_format.h的数据结构
typedef enum
{
I2S_MODE_MASTER,
I2S_MODE_SLAVE,
} i2s_mode_t;
typedef enum
{
I2S_PROTOCOL_I2S,
I2S_PROTOCOL_LEFT_J,
I2S_PROTOCOL_RIGHT_J,
I2S_PCM_SHORT,
I2S_PCM_LONG,
} i2s_protocol_t;
typedef enum
{
I2S_LEFT_POLARITY_LOW,
I2S_LEFT_POLARITY_HIGH,
} i2s_polarity_t;
typedef enum
{
I2S_SAMPLE_RATE_8000 = 8000U,
I2S_SAMPLE_RATE_11025 = 11025U,
I2S_SAMPLE_RATE_12000 = 12000U,
I2S_SAMPLE_RATE_16000 = 16000U,
I2S_SAMPLE_RATE_22050 = 22050U,
I2S_SAMPLE_RATE_24000 = 24000U,
I2S_SAMPLE_RATE_32000 = 32000U,
I2S_SAMPLE_RATE_44100 = 44100U,
I2S_SAMPLE_RATE_48000 = 48000U,
I2S_SAMPLE_RATE_96000 = 96000U,
I2S_SAMPLE_RATE_192000 = 192000U,
I2S_SAMPLE_RATE_256000 = 256000U,
} i2s_sample_rate_t;
typedef enum
{
I2S_SAMPLE_WIDTH_8BIT = 8U,
I2S_SAMPLE_WIDTH_12BIT = 12U,
I2S_SAMPLE_WIDTH_16BIT = 16U,
I2S_SAMPLE_WIDTH_20BIT = 20U,
I2S_SAMPLE_WIDTH_24BIT = 24U,
I2S_SAMPLE_WIDTH_28BIT = 28U,
I2S_SAMPLE_WIDTH_32BIT = 32U,
} i2s_sample_width_t;
typedef enum
{
I2S_LEFT_CHANNEL,
I2S_RIGHT_CHANNEL,
I2S_LEFT_RIGHT_CHANNEL,
} i2s_sound_channel_t;
typedef enum
{
I2S_STREAM_PLAYBACK = 0,
I2S_STREAM_RECORD = 1,
} i2s_stream_t;
typedef struct
{
i2s_mode_t mode;
i2s_protocol_t protocol;
i2s_polarity_t polarity;
i2s_sample_rate_t rate;
i2s_sample_width_t width;
i2s_sound_channel_t channel;
uint32_t sclk_nfs;
uint32_t mclk_nfs;
i2s_stream_t stream;
} i2s_format_t;
6.5.5.4.2. driver层数据结构¶
struct aic_i2s_sound
{
struct rt_audio_device audio;
aic_i2s_ctrl i2s;
struct codec *codec;
i2s_format_t format; //i2s的传输格式
rt_uint8_t volume; //playback音量
char *name; //设备名
uint32_t i2s_idx;
uint8_t record_idx;
};
static struct aic_i2s_sound snd_dev[] =
{
#ifdef AIC_USING_I2S0
{
.name = "i2s0_sound",
.i2s_idx = 0,
},
#endif
#ifdef AIC_USING_I2S1
{
.name = "i2s1_sound",
.i2s_idx = 1,
},
#endif
};
6.5.5.5. 接口设计¶
6.5.5.5.1. driver层接口设计¶
driver层可以同时实现playback和record两个功能,主要根据的是音频数据流的方向进行判别。
6.5.5.5.1.1. drv_i2s_sound_init¶
函数原型 |
rt_err_t drv_i2s_sound_init(struct rt_audio_device *audio) |
---|---|
功能说明 |
i2s的初始化函数 |
参数定义 |
audio:指向音频设备的指针 |
返回值 |
RT_EOK:执行成功 |
注意事项 |
6.5.5.5.1.2. drv_i2s_sound_buffer_info¶
函数原型 |
void drv_i2s_sound_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info) |
---|---|
功能说明 |
获取音频的TX buffer参数 |
参数定义 |
audio:指向音频设备的指针
info:用于获取TX buffer参数的指针
|
返回值 |
无 |
注意事项 |
6.5.5.5.1.3. drv_i2s_sound_start¶
函数原型 |
rt_err_t drv_i2s_sound_start(struct rt_audio_device *audio, int stream) |
---|---|
功能说明 |
开始音频播放 |
参数定义 |
audio:指向音频设备的指针
stream:音频数据流方向
|
返回值 |
RT_EOK:执行成功
-RT_EINVAL:参数非法
|
注意事项 |
6.5.5.5.1.4. drv_i2s_sound_stop¶
函数原型 |
rt_err_t drv_i2s_sound_stop(struct rt_audio_device *audio, int stream) |
---|---|
功能说明 |
结束音频播放 |
参数定义 |
audio:指向音频设备的指针
stream:音频数据流方向
|
返回值 |
RT_EOK:执行成功
-RT_EINVAL:参数非法
|
注意事项 |
6.5.5.5.1.5. drv_i2s_sound_pause¶
函数原型 |
rt_err_t drv_i2s_sound_pause(struct rt_audio_device *audio, int enable) |
---|---|
功能说明 |
暂停/恢复音频播放 |
参数定义 |
audio:指向音频设备的指针
enable:音频暂停和恢复播放使能位(0为恢复,非0为暂停)
|
返回值 |
RT_EOK:执行成功
|
注意事项 |
6.5.5.5.1.6. drv_i2s_sound_configure¶
函数原型 |
rt_err_t drv_i2s_sound_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps) |
---|---|
功能原型 |
音频设备配置接口,用于配置采样格式,采样率,通道数等接口 |
参数定义 |
audio:指向音频设备的指针
caps:指向配置参数的指针
|
返回值 |
RT_EOK:执行成功
-RT_ERROR:参数不支持
|
注意事项 |
6.5.5.5.1.7. drv_i2s_sound_getcaps¶
函数原型 |
rt_err_t drv_i2s_sound_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps) |
---|---|
功能说明 |
获取音频设备的参数 |
参数定义 |
audio:指向音频设备的指针
caps:指向配置参数的指针
|
返回值 |
RT_EOK:执行成功
-RT_ERROR:参数不支持
|
注意事项 |
6.5.5.5.1.8. drv_audio_get_i2s_sound_avail¶
函数原型 |
rt_size_t drv_i2s_sound_get_playback_avail(struct rt_audio_device *audio) |
---|---|
功能说明 |
获取音频缓存的数据大小 |
参数定义 |
audio:指向音频设备的指针
|
返回值 |
缓存数据的大小
|
注意事项 |
6.5.5.5.2. hal层接口设计¶
hal层接口也是分playback,record两部分进行设计,下面以playback端的接口进行说明。
6.5.5.5.2.1. hal_i2s_protocol_select¶
函数原型 |
int hal_i2s_protocol_select(aic_i2s_ctrl *i2s, i2s_protocol_t protocol) |
---|---|
功能说明 |
设置传输协议 |
参数定义 |
i2s:指向aic_i2s_ctrl的指针
protocol:传输协议
|
返回值 |
0:执行成功
-EINVAL:参数不支持
|
注意事项 |
6.5.5.5.2.2. hal_i2s_sample_width_select¶
函数原型 |
int hal_i2s_sample_width_select(aic_i2s_ctrl *i2s, i2s_sample_width_t width) |
---|---|
功能说明 |
设置采样通道的位宽 |
参数定义 |
i2s:指向aic_i2s_ctrl的指针
width:通道的位宽
|
返回值 |
0:执行成功
|
注意事项 |
6.5.5.5.2.3. hal_i2s_mclk_set¶
函数原型 |
int hal_i2s_mclk_set(aic_i2s_ctrl *i2s, i2s_sample_rate_t sample_rate, uint32_t mclk_nfs) |
---|---|
功能说明 |
设置MCLK的时钟频率 |
参数定义 |
i2s:指向aic_i2s_ctrl的指针
sample_rate:采样率
mclk_nfs:MCLK的时钟频率
|
返回值 |
0:执行成功
-EINVAL:参数不支持
|
注意事项 |
6.5.5.5.2.4. hal_i2s_polarity_set¶
函数原型 |
void hal_i2s_polarity_set(aic_i2s_ctrl *i2s, i2s_polarity_t polarity) |
---|---|
功能说明 |
选择LRCK的左右通道极性 |
参数定义 |
i2s:指向aic_i2s_ctrl的指针
polarity:LRCK的左右通道极性
|
返回值 |
无 |
注意事项 |
6.5.5.5.2.5. hal_i2s_sclk_set¶
函数原型 |
int hal_i2s_sclk_set(aic_i2s_ctrl *i2s, i2s_sample_rate_t sample_rate, uint32_t sclk_nfs) |
---|---|
功能说明 |
设置SCLK/LRCK的比率 |
参数定义 |
i2s:指向aic_i2s_ctrl的指针
sample_rate:采样率
sclk_nfs:需要设置的比率
|
返回值 |
0:执行成功
-EINVAL:参数不支持
|
注意事项 |
6.5.5.5.2.6. hal_i2s_channel_select¶
函数原型 |
void hal_i2s_channel_select(aic_i2s_ctrl *i2s, i2s_sound_channel_t channel, i2s_stream_t stream) |
---|---|
功能说明 |
I2S通道数量设置 |
参数定义 |
i2s:指向aic_i2s_ctrl的指针
channel:指向i2s_sound_channel_t的变量,表示要使用的通道
stream:音频数据流的方向
|
返回值 |
无 |
注意事项 |
6.5.5.5.2.7. hal_i2s_playback_start¶
函数原型 |
void hal_i2s_playback_start(aic_i2s_ctrl *i2s, i2s_format_t *format) |
---|---|
功能说明 |
开始播放 |
参数定义 |
i2s:指向aic_i2s_ctrl的指针
format:指向i2s_format_t的指针
|
返回值 |
无 |
注意事项 |
6.5.5.5.2.8. hal_i2s_playback_stop¶
函数原型 |
void hal_i2s_playback_stop(aic_i2s_ctrl *i2s) |
---|---|
功能说明 |
停止播放 |
参数定义 |
i2s:指向aic_i2s_ctrl的指针
|
返回值 |
无 |
注意事项 |