Audprc/Audcodec Audio设备

芯片内部有Audprc和Audcodec,Audprc用于数字处理,Audcodec是数字转模拟。 常用的音频数据流程是Audprc–>Audcodec–>Speaker 还有的方式是Audprc–>I2S–>芯片外围Codec芯片
主要功能包括:

  • 输入和输出采样率设置

  • 输入和输出通道数设置

  • 每个采样的位深设置

  • 主从模式设置

I2S Audio设备

音频驱动程序包括两层:用于 I2S 的硬件访问层 (HAL) 和用于 RT-Thread 的适配层。
硬件访问层提供用于访问 I2S 外设寄存器的基本 API。 有关详细信息,请参阅 I2S 的 API 文档。
适配层提供对 RT-Thread 驱动框架的支持。 用户可以使用 RT-Thread POSIX 驱动程序接口进行音频编程。 请参阅 RT-Thread 驱动程序的 API 文档。
主要功能包括:

  • 麦克风设备和扬声器设备支持

  • 用于音频捕获和播放的 DMA

  • 音频捕获转储工具支持并保存在 PC 中

  • 两路I2S硬件支持,其中I2S1只用来输入, I2S2 既支持输入也支持输出

驱动配置

On-Chip Peripheral RTOS Drivers ‣ Enable I2S Audio Driver菜单中选择要使用的I2S设备

下面宏开关表示使能了I2S_MIC和I2S_CODEC两个设备

#define BSP_USING_DMA 1
#define RT_USING_AUDIO 1
#define BSP_USING_I2S 1
#define BSP_ENABLE_I2S_MIC 1
#define BSP_ENABLE_I2S_CODEC 1

设备名称

  • i2s<x>, 其中x为设备编号,如i2s1i2s2,与操作的外设编号对应 见audio_config.h里的定义,可以看到设备名为i2s2, 实际对应的硬件为i2s1(hwp_i2s1), 对只有1个i2s的芯片,或实际使用的是I2s1, 注意这里hwp_i2s2要改为hwp_i2s1 如果配置BF0_MIC_CONFIG, 使用的i2s作为mic使用,用的drv_i2s_mic.c 如果使用BSP_ENABLE_I2S_CODEC,用的drv_i2s_audio.c一般播放数据用。 注意 rt_device_find(“i2s1”)或rt_device_find(“i2s2”)要与下面的.name对应。 具体要看看配置用的下面哪一个。

struct i2s_audio_cfg_t bf0_i2s_audio_obj[] = { … }

#ifdef BSP_USING_I2S
#ifdef BSP_ENABLE_I2S_MIC
#ifndef BF0_MIC_CONFIG
#define BF0_MIC_CONFIG \
    {                                           \
       .name                    = "i2s1",       \
       .dma_handle              = MIC_DMA_INSTANCE, \
       .dma_request             = I2S1_RX_DMA_REQUEST,  \
       .is_record               = 1,            \
       .i2s_handle              = hwp_i2s1,      \
       .reqdma_tx               = I2S1_TX_DMA_REQUEST, \
       .hdma_tx                 = MIC_TX_DMA_INSTANCE, \
    }
#endif /* BF0_MIC_CONFIG */
#endif /* BSP_ENABLE_I2S_MIC */

#ifdef BSP_ENABLE_I2S_CODEC
#ifndef BF0_I2S2_CONFIG
#define BF0_I2S2_CONFIG \
    {                                           \
       .name                    = "i2s2",       \
       .dma_handle              = I2S_RX_DMA_INSTANCE, \
       .dma_request             = I2S2_RX_DMA_REQUEST,  \
       .is_record               = 1,            \
       .i2s_handle              = hwp_i2s2,      \
       .reqdma_tx               = I2S2_TX_DMA_REQUEST, \
       .hdma_tx                 = I2S_TX_DMA_INSTANCE, \
    }
#endif /* BF0_I2S2_CONFIG */
#endif /* BSP_ENABLE_I2S_CODEC */

使用流程

1. 打开I2s设备

    /*name用i2s2还是i2s1, 看前面的audio_config.h里的配置*/
    rt_device_t i2s = rt_device_find("i2s2");
    if (i2s)
    {
        rt_device_open(i2s, RT_DEVICE_FLAG_RDWR);
    }

2. 配置参数

    struct rt_audio_caps caps;
    caps.main_type = AUDIO_TYPE_INPUT;
    caps.sub_type = AUDIO_DSP_PARAM;

    /* 1 单声道, 2为双声道*/
    caps.udata.config.channels = 2);
    /*1个采样多少位*/
    caps.udata.config.samplefmt = 16;
    /*采用率*/
    caps.udata.config.samplerate = 44100;
    rt_device_control(i2s, AUDIO_CTL_CONFIGURE, &caps);

    caps.main_type = AUDIO_TYPE_INPUT;      // for I2S2, configure RX will configure RX+TX
    caps.sub_type = AUDIO_DSP_MODE;
    /*mode setting, 0---master mode; 1--- slave mode*/
    caps.udata.value = 0;
    rt_device_control(i2s, AUDIO_CTL_CONFIGURE, &caps);

I2S默认是直接输出到外设的,还有个高级用法,是I2S输出个芯片的audprc模块, 这个不常用,目前还没有这样用, 如果需要的话,得下面这样设置, 如果不知到什么是i2s–>audprc,忽略下面这段代码,

/*输入直接输出到外部I2s设备,这里不需要*/
rt_uint32_t inter = 1;
rt_device_control(i2s, AUDIO_CTL_SETINPUT, (void *)inter);

3. 收或发的回调函数设置

static rt_err_t audio_rx_ind(rt_device_t dev, rt_size_t size)
{
    LOG_I("audio_rx_ind %d\n", size);
    /*这里可以通知接收线程去rt_device_read(i2s)获取数据*/
    return RT_EOK;
}
rt_err_t audio_tx_done(rt_device_t dev, void *buffer)
{
    LOG_I("audio_tx_done \n");
    return RT_EOK;
}

    rt_device_set_rx_indicate(i2s, audio_rx_ind);
    rt_device_set_tx_complete(i2s, audio_tx_done);

4. 开始收或发

    int stream = 0;
    //select one for stream
#if playback_only
    stream = AUDIO_STREAM_REPLAY; /*playback only*/
#else if capture_only
    stream = AUDIO_STREAM_RECORD; /*record only*/
#else if capture_and_playback   
    stream = AUDIO_STREAM_RXandTX; /*record & playback*/
#endif
    rt_device_control(i2s, AUDIO_CTL_START, &stream);
    
    rt_thread_mdelay(xxxx);

    rt_device_close(i2s);

5. 配置dma ping-pong buffer大小

#define AUDIO_DATA_SIZE 640
ALIGN(4) static uint8_t audio_data[AUDIO_DATA_SIZE];
ALIGN(4) static uint8_t audio_tx_data[AUDIO_DATA_SIZE];

AUDIO_DATA_SIZE为DMA ping-pong的大小,一次数据DMA数据大小的自己数为(AUDIO_DATA_SIZE / 2), 读写数据的时候,一次的数据量应该是(AUDIO_DATA_SIZE / 2)

使用音频驱动程序

适配器层注册 RT-Thread 请求的硬件支持功能,并使用 I2S HAL 实现这些功能。 I2S HAL 的 API 详见 I2S

对于使用 RT-Thread 麦克风设备进行音频捕获的用户,可以使用以下代码作为示例:


uint8_t g_pipe_data[512];

// Find and open device
rt_device_t g_mic = rt_device_find("i2s1");
rt_err_t err = rt_device_open(g_mic, RT_DEVICE_FLAG_RDONLY);

// Configure Microphone deivce, sample rate 16000
struct rt_audio_caps caps;
caps.main_type = AUDIO_TYPE_INPUT;
caps.sub_type = AUDIO_DSP_SAMPLERATE;
caps.udata.value =16000;
rt_device_control(g_mic, AUDIO_CTL_CONFIGURE, &caps);;

// Start capture
int stream = 1; // record = 1, playback = 0
rt_device_set_rx_indicate(g_mic, audio_rx_ind);
rt_device_control(g_mic, AUDIO_CTL_START, &stream);


...

rt_err_t audio_rx_ind(rt_device_t dev, rt_size_t size)
{
    // Processing audio data. Please note this is in interrupt context.
    // User might need to start a thread to read and process data, call  rt_device_read(g_mic, 0, g_pipe_data, AUDIO_BUF_SIZE);
}

对于使用 RT-Thread 喇叭/耳机设备进行音频播放的用户,可以使用以下代码作为示例:


uint8_t g_pipe_data[512];

// Find and open device
rt_device_t g_i2s = rt_device_find("i2s2");
rt_err_t err = rt_device_open(g_i2s, RT_DEVICE_FLAG_RDWR);


// Configure speaker deivce, sample rate 16000
struct rt_audio_caps caps;
caps.main_type = AUDIO_TYPE_INPUT;
caps.sub_type = AUDIO_DSP_SAMPLERATE;
caps.udata.value =16000;
rt_device_control(g_i2s, AUDIO_CTL_CONFIGURE, &caps);;

// Start capture
int stream = 0; // record = 1, playback = 0
rt_device_set_tx_complete(g_i2s, audio_tx_done);
rt_device_control(g_i2s, AUDIO_CTL_START, &stream);


...

rt_err_t audio_tx_done(rt_device_t dev, void *buffer)
{
    // Processing audio data. Please note this is in interrupt context.
    // User might need to start a thread to fill data, call  rt_device_write(g_i2s, 0, g_pipe_data, AUDIO_BUF_SIZE)
}

其他例子代码

$(sdk_root)\example\rt_device\i2s

命令行参考代码

drv_i2s_audio.c和drv_i2s_mic.c文件后面有命令行,也可以参考

RT-Thread参考文档