设计指南
源码说明
源代码位于:
- bsp/artinchip/drv/audio/drv_audio.c: Playback的driver层驱动
- bsp/artinchip/drv/audio/drv_dmic.c: DMIC的driver层驱动
- bsp/artinchip/drv/audio/drv_amic.c: AMIC的driver层驱动
- bsp/artinchip/hal/audio/hal_audio.c: audio模块的hal层驱动
- bsp/artinchip/include/hal/hal_audio.h: audio模块的hal层头文件
- bsp/artinchip/include/hal/hal_audio_reg.h: audio模块的hal层头文件,用于寄存器定义
模块架构设计
RT-Thread audio框架
RT-Thread定义了一套音频框架,driver层的驱动就是为了对接该音频框架。该框架在录音端和播放端采用了两种不同的机制,现对录音端和播放端的框架进行简单说明。
播放端框架
如上图所示,应用层读取的音频数据写入到内存池的block中,并用数据队列对内存池的block数据进行管理。从数据队列中依次取出音频数据,写入到audio buffer中,audio buffer在原理上是一个环形缓冲区,最后通过DMA将音频数据写入到硬件的TXFIFO中。所以,playback端的driver层驱动主要是负责audio buffer环形缓冲区的管理,及时写入音频数据,并用DMA搬运到audio硬件。
录音端框架
如上图所示,RT-Thread audio框架在录音端的设计与播放端不同。在录音端框架虚拟了一个pipe设备,其实就是一个ringbuffer,读写pipe设备就是对ringbuffer的读写。driver层驱动负责管理一个RX buffer,RX buffer在逻辑上也是一个环形缓冲区。DMA负责将MIC端接收到的数据搬运到RX buffer,再通过写pipe设备将音频数据写入到pipe的ringbuffer中。应用层代码则通过 rt_device_read
每次从pipe设备中读取音频数据,再将音频数据写入到wav文件。
关键流程设计
初始化流程
- 初始化audio模块时钟频率
- 释放reset和clock信号
- 注册音频设备,playback端注册为
sound0
设备,DMIC注册为dmic0
设备,AMIC注册为amic0
设备
playback流程
init流程
- 初始化DMA传输的起始地址,
buf_len
以及period_len
- 注册hal层的回调函数
audio模块使用DMA传输音频数据,DMA采用环形链表形式,依次将音频数据传送到硬件。所以需要配置DMA传输时的起始地址(即TX buffer地址)以及 buf_len
,period_len
。在driver层驱动,将 buf_len
配置为 period_len
的4倍,DMA每传输 period_len
长度的数据,触发一次DMA中断,通知CPU向TX buffer中写入数据。
start流程
- 根据menuconfig配置音频通路
- 填充TX buffer
- 设置DMA传输的参数,调用
hal_audio_playback_start
开始音频数据传输 - 使能PA
为保证DMA传输音频数据的连续性,需要在DMA开始传输前,先向TX buffer中填充数据。在playback的driver层驱动,是将TX buffer填充满后,才开始DMA的传输。
DMA中断流程
DMA每传输完 period_len
长度的数据后,触发一次DMA中断,然后通过DMA回调函数的逐级调用,最终调用 rt_audio_tx_complete
对TX buffer进行填充,每次填充 period_len
长度的音频数据。
record流程
init流程
- 初始化DMA传输的起始地址,
buf_len
以及period_len
- 注册hal层的回调函数
audio模块使用DMA传输音频数据,DMA采用环形链表形式,依次将音频数据传送到硬件。所以需要配置DMA传输时的起始地址(即RX buffer地址)以及 buf_len
,period_len
。在driver层驱动,将 buf_len
配置为 period_len
的2倍,DMA每传输 period_len
长度的数据,触发一次DMA中断,通知CPU向pipe设备写入数据。
start流程
按照RT-Thread audio的框架,在执行 rt_device_open
时,就会调用start流程,开始音频的录制,然后再通过 rt_device_control
设置音频的格式(采样率,通道数等)。按照这个流程,最开始可能会录制一段不符合设置的音频格式的数据,这显然是不合理的。所以,在driver层的驱动实现中,start流程并未做任何处理,而是在设置完音频格式后才开始音频的录制。
DMA中断流程
DMA每传输完 period_len
长度的数据后,触发一次DMA中断,然后通过DMA回调函数的逐级调用,最终调用 rt_audio_rx_done
,将RX buffer的数据写入到pipe设备,每次写入 period_len
长度的音频数据。
数据结构设计
hal层数据结构
struct aic_audio_buf_info
{
void *buf;
uint32_t buf_len;
uint32_t period_len;
};
struct aic_audio_transfer_info
{
struct aic_dma_chan *dma_chan;
struct aic_audio_buf_info buf_info;
int transfer_type;
};
struct aic_audio_ctrl
{
unsigned long reg_base;
uint32_t irq_num;
uint32_t clk_id;
struct aic_audio_transfer_info tx_info; //TX buffer的参数
struct aic_audio_transfer_info dmic_info; //DMIC RX buffer的参数
struct aic_audio_transfer_info amic_info; //AMIC RX buffer的参数
audio_callback callback;
void *arg;
struct aic_audio_config config;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
driver层数据结构
struct aic_audio
{
struct rt_audio_device audio;
aic_audio_ctrl codec;
rt_uint8_t volume; //playback音量
char *pa_name; //PA引脚的名字
unsigned int gpio_pa; //PA引脚的IO口号
};
struct aic_dmic
{
struct rt_audio_device audio;
aic_audio_ctrl codec;
rt_uint8_t volume;
uint8_t index;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
接口设计
driver层接口设计
drv_audio_init
函数原型 | rt_err_t drv_audio_init(struct rt_audio_device *audio) |
---|---|
功能说明 | playback端的初始化函数 |
参数定义 | audio:指向playback设备的指针 |
返回值 | RT_EOK:执行成功 |
注意事项 | 无 |
drv_audio_buffer_info
函数原型 | void drv_audio_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info) |
---|---|
功能说明 | 获取playback端的TX buffer参数 |
参数定义 | audio:指向playback设备的指针 info:用于获取TX buffer参数的指针 |
返回值 | 无 |
注意事项 | 无 |
drv_audio_start
函数原型 | rt_err_t drv_audio_start(struct rt_audio_device *audio, int stream) |
---|---|
功能说明 | 开始playback端播放 |
参数定义 | audio:指向playback设备的指针 stream:音频数据流方向 |
返回值 | RT_EOK:执行成功 -RT_EINVAL:参数非法 |
注意事项 | 无 |
drv_audio_stop
函数原型 | rt_err_t drv_audio_stop(struct rt_audio_device *audio, int stream) |
---|---|
功能说明 | 结束playback端播放 |
参数定义 | audio:指向playback设备的指针 stream:音频数据流方向 |
返回值 | RT_EOK:执行成功 -RT_EINVAL:参数非法 |
注意事项 | 无 |
drv_audio_pause
函数原型 | rt_err_t drv_audio_pause(struct rt_audio_device *audio, int enable) |
---|---|
功能说明 | 暂停/恢复playback端播放 |
参数定义 | audio:指向playback设备的指针 enable:playback端暂停和恢复播放使能位(0为恢复,非0为暂停) |
返回值 | RT_EOK:执行成功 |
注意事项 | 无 |
drv_audio_configure
函数原型 | rt_err_t drv_audio_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps) |
---|---|
功能说明 | 音频设备配置接口,用于配置采样格式,采样率,通道数等接口 |
参数定义 | audio:指向playback设备的指针 caps:指向配置参数的指针 |
返回值 | RT_EOK:执行成功 -RT_ERROR:参数不支持 |
注意事项 | 无 |
drv_audio_getcaps
函数原型 | rt_err_t drv_audio_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps) |
---|---|
功能说明 | 获取音频设备的参数 |
参数定义 | audio:指向playback设备的指针 caps:指向配置参数的指针 |
返回值 | RT_EOK:执行成功 -RT_ERROR:参数不支持 |
注意事项 | 无 |
drv_audio_get_playback_avail
函数原型 | rt_size_t drv_audio_get_playback_avail(struct rt_audio_device *audio) |
---|---|
功能说明 | 获取playback端缓存的数据大小 |
参数定义 | audio:指向playback设备的指针 |
返回值 | 缓存数据的大小 |
注意事项 | 无 |
hal层接口设计
hal层接口也是分playback、DMIC、AMIC三部分进行设计,下面以playback端的接口进行说明。
hal_audio_set_samplerate
函数原型 | void hal_audio_set_samplerate(aic_audio_ctrl *codec, uint32_t samplerate) |
---|---|
功能说明 | 设置采样率 |
参数定义 | codec:指向aic_audio_ctrl的指针 samplerate:采样率 |
返回值 | 无 |
注意事项 | 无 |
hal_audio_set_playback_channel
函数原型 | void hal_audio_set_playback_channel(aic_audio_ctrl *codec, uint32_t ch) |
---|---|
功能说明 | 设置playback端的通道数 |
参数定义 | codec:指向aic_audio_ctrl的指针 ch:通道数 |
返回值 | 无 |
注意事项 | 无 |
hal_audio_set_playback_by_spk0
函数原型 | void hal_audio_set_playback_by_spk0(aic_audio_ctrl *codec) |
---|---|
功能说明 | 配置SPK0音频通路 |
参数定义 | codec:指向aic_audio_ctrl的指针 |
返回值 | 无 |
注意事项 | 无 |
hal_audio_set_playback_by_spk1
函数原型 | void hal_audio_set_playback_by_spk1(aic_audio_ctrl *codec) |
---|---|
功能说明 | 配置SPK1音频通路 |
参数定义 | codec:指向aic_audio_ctrl的指针 |
返回值 | 无 |
注意事项 | 无 |
hal_audio_playback_start
函数原型 | void hal_audio_playback_start(aic_audio_ctrl *codec) |
---|---|
功能说明 | 开始播放 |
参数定义 | codec:指向aic_audio_ctrl的指针 |
返回值 | 无 |
注意事项 | 无 |
hal_audio_playback_stop
函数原型 | void hal_audio_playback_stop(aic_audio_ctrl *codec) |
---|---|
功能说明 | 结束播放 |
参数定义 | codec:指向aic_audio_ctrl的指针 |
返回值 | 无 |
注意事项 | 无 |