6.1.5. 设计指南

6.1.5.1. 源码说明

源代码位于:

  • 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层头文件,用于寄存器定义

6.1.5.2. 模块架构设计

6.1.5.2.1. RT-Thread audio框架

RT-Thread定义了一套音频框架,driver层的驱动就是为了对接该音频框架。该框架在录音端和播放端采用了两种不同的机制,现对录音端和播放端的框架进行简单说明。

6.1.5.2.1.1. 播放端框架

../../../_images/playback_arch.png

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

6.1.5.2.1.2. 录音端框架

../../../_images/record_arch.png

如上图所示,RT-Thread audio框架在录音端的设计与播放端不同。在录音端框架虚拟了一个pipe设备,其实就是一个ringbuffer,读写pipe设备就是对ringbuffer的读写。driver层驱动负责管理一个RX buffer,RX buffer在逻辑上也是一个环形缓冲区。DMA负责将MIC端接收到的数据搬运到RX buffer,再通过写pipe设备将音频数据写入到pipe的ringbuffer中。应用层代码则通过 rt_device_read 每次从pipe设备中读取音频数据,再将音频数据写入到wav文件。

6.1.5.3. 关键流程设计

6.1.5.3.1. 初始化流程

  1. 初始化audio模块时钟频率

  2. 释放reset和clock信号

  3. 注册音频设备,playback端注册为 sound0 设备,DMIC注册为 dmic0 设备,AMIC注册为 amic0 设备

6.1.5.3.2. playback流程

6.1.5.3.2.1. init流程

  1. 初始化DMA传输的起始地址,buf_len以及period_len

  2. 注册hal层的回调函数

audio模块使用DMA传输音频数据,DMA采用环形链表形式,依次将音频数据传送到硬件。所以需要配置DMA传输时的起始地址(即TX buffer地址)以及buf_len,period_len。在driver层驱动,将buf_len配置为period_len的4倍,DMA每传输period_len长度的数据,触发一次DMA中断,通知CPU向TX buffer中写入数据。

6.1.5.3.2.2. start流程

  1. 根据menuconfig配置音频通路

  2. 填充TX buffer

  3. 设置DMA传输的参数,调用 hal_audio_playback_start 开始音频数据传输

  4. 使能PA

为保证DMA传输音频数据的连续性,需要在DMA开始传输前,先向TX buffer中填充数据。在playback的driver层驱动,是将TX buffer填充满后,才开始DMA的传输。

6.1.5.3.2.3. DMA中断流程

DMA每传输完period_len长度的数据后,触发一次DMA中断,然后通过DMA回调函数的逐级调用,最终调用 rt_audio_tx_complete 对TX buffer进行填充,每次填充period_len长度的音频数据。

6.1.5.3.3. record流程

6.1.5.3.3.1. init流程

  1. 初始化DMA传输的起始地址,buf_len以及period_len

  2. 注册hal层的回调函数

audio模块使用DMA传输音频数据,DMA采用环形链表形式,依次将音频数据传送到硬件。所以需要配置DMA传输时的起始地址(即RX buffer地址)以及buf_len,period_len。在driver层驱动,将buf_len配置为period_len的2倍,DMA每传输period_len长度的数据,触发一次DMA中断,通知CPU向pipe设备写入数据。

6.1.5.3.3.2. start流程

按照RT-Thread audio的框架,在执行 rt_device_open 时,就会调用start流程,开始音频的录制,然后再通过 rt_device_control 设置音频的格式(采样率,通道数等)。按照这个流程,最开始可能会录制一段不符合设置的音频格式的数据,这显然是不合理的。所以,在driver层的驱动实现中,start流程并未做任何处理,而是在设置完音频格式后才开始音频的录制。

6.1.5.3.3.3. DMA中断流程

DMA每传输完period_len长度的数据后,触发一次DMA中断,然后通过DMA回调函数的逐级调用,最终调用 rt_audio_rx_done ,将RX buffer的数据写入到pipe设备,每次写入period_len长度的音频数据。

6.1.5.4. 数据结构设计

6.1.5.4.1. 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;
};

6.1.5.4.2. 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;
};

6.1.5.5. 接口设计

6.1.5.5.1. driver层接口设计

driver层将audio定义为 sound0dmic0 , amic0 三个设备,三个设备在driver层的接口基本相同,下面以playback端的接口进行说明。

6.1.5.5.1.1. drv_audio_init

函数原型

rt_err_t drv_audio_init(struct rt_audio_device *audio)

功能说明

playback端的初始化函数

参数定义

audio:指向playback设备的指针

返回值

RT_EOK:执行成功

注意事项

6.1.5.5.1.2. 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参数的指针

返回值

注意事项

6.1.5.5.1.3. drv_audio_start

函数原型

rt_err_t drv_audio_start(struct rt_audio_device *audio, int stream)

功能说明

开始playback端播放

参数定义

audio:指向playback设备的指针
stream:音频数据流方向

返回值

RT_EOK:执行成功
-RT_EINVAL:参数非法

注意事项

6.1.5.5.1.4. drv_audio_stop

函数原型

rt_err_t drv_audio_stop(struct rt_audio_device *audio, int stream)

功能说明

结束playback端播放

参数定义

audio:指向playback设备的指针
stream:音频数据流方向

返回值

RT_EOK:执行成功
-RT_EINVAL:参数非法

注意事项

6.1.5.5.1.5. 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:执行成功

注意事项

6.1.5.5.1.6. 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:参数不支持

注意事项

6.1.5.5.1.7. 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:参数不支持

注意事项

6.1.5.5.1.8. drv_audio_get_playback_avail

函数原型

rt_size_t drv_audio_get_playback_avail(struct rt_audio_device *audio)

功能说明

获取playback端缓存的数据大小

参数定义

audio:指向playback设备的指针

返回值

缓存数据的大小

注意事项

6.1.5.5.1.9. hal层接口设计

hal层接口也是分playback、DMIC、AMIC三部分进行设计,下面以playback端的接口进行说明。

6.1.5.5.1.10. hal_audio_set_samplerate

函数原型

void hal_audio_set_samplerate(aic_audio_ctrl *codec, uint32_t samplerate)

功能说明

设置采样率

参数定义

codec:指向aic_audio_ctrl的指针
samplerate:采样率

返回值

注意事项

6.1.5.5.1.11. hal_audio_set_playback_channel

函数原型

void hal_audio_set_playback_channel(aic_audio_ctrl *codec, uint32_t ch)

功能说明

设置playback端的通道数

参数定义

codec:指向aic_audio_ctrl的指针
ch:通道数

返回值

注意事项

6.1.5.5.1.12. hal_audio_set_playback_by_spk0

函数原型

void hal_audio_set_playback_by_spk0(aic_audio_ctrl *codec)

功能说明

配置SPK0音频通路

参数定义

codec:指向aic_audio_ctrl的指针

返回值

注意事项

6.1.5.5.1.13. hal_audio_set_playback_by_spk1

函数原型

void hal_audio_set_playback_by_spk1(aic_audio_ctrl *codec)

功能说明

配置SPK1音频通路

参数定义

codec:指向aic_audio_ctrl的指针

返回值

注意事项

6.1.5.5.1.14. hal_audio_playback_start

函数原型

void hal_audio_playback_start(aic_audio_ctrl *codec)

功能说明

开始播放

参数定义

codec:指向aic_audio_ctrl的指针

返回值

注意事项

6.1.5.5.1.15. hal_audio_playback_stop

函数原型

void hal_audio_playback_stop(aic_audio_ctrl *codec)

功能说明

结束播放

参数定义

codec:指向aic_audio_ctrl的指针

返回值

注意事项