AD100 录音和播放
一 硬件环境
该文档使用的开发板为:RD_AD100_EVB_V1.0开发板 , 芯片为 AD100 . 内置 64MB DDR2/L ; 外置 64MB Nor Flash.
二 功能简介
AD100处理器内部自带Audio Codec,支持输入/输出模拟音频差分信号,即AMICP+AMICN(模拟音频差分输入),和HPOUTP+HPOUTN(音频输出);支持1路数字麦克风通道; RD_AD100_EVB_V1.0开发平台默认设计 MICP、MICN用于模拟语音输入;HP_OUTP、HP_OUTN用作模拟音频输出,外接音频功放IO/SPK_EN(PB11)为控制音频功放使能管脚,高电平有效; J7/CON8为连接器接口,可用于数字DMIC或者UART外设。如下图所示:


AIC 控制器
-
支持 8/16/18/20/24bit 位宽
-
支持 8/12/16/32/44.1/48/96kHz 采样率
-
支持 I2S 和 MSB-Justified 格式
-
支持 Master 和 Slave 模式
内部 Codec
-
24bit DAC with 90db SNR
-
24bit ADC with 90db SNR
-
支持 16/20/24bit 位宽
-
支持 8/12/16/32/44.1/48/96kHz 采样率
-
ADC 支持自动电平控制(ALC)
-
输入输出支持差分信号
-
支持 loopback
DMIC 控制器
- 支持 1路数字 mic
- SNR:90dB, THD:-90dB @ FS-20dB
- 支持 16/24bit 位宽
- 支持 8/12/16/48/96kHz 采样率
- DMIC 时钟频率支持 2.4/3.072Mhz
- 支持同时使能 DMIC 和 AIC
三 原理介绍
3.1 录音有两种方式
第一: 采用 dmic 硬件采样音频模拟信号并转成数字信号,软件直接读 dmic 输出的数字信号并编码成指定的文件格式,从而录音产生相应格式的音频文件,比如 wav 格式。第二:采用内部 codec (简称 icodec ) + amic (采样音频模拟信号的传感器),从而,接上 amic 之后,硬件上可以实现录音。
3.2 播放
播放统一采用内部 codec (简称 icodec )这个设备实现。所以, icodec 也是要软件配置的。使用HPOUT用作模拟音频输出,外接功放SPK。
四 软件实现方法

主配置选择 ad100_nor_defconfig

勾选 icodec(内部codec),dmic(外部数字mic)

Ctrl + s ,点击 Yes 保存配置
五 测试程序
5.1 aic 播放测试 example
aic播放测试程序:freertos$ ls example/driver/audio_pcm_example.c 内容如下:
#include <driver/gpio.h>
#include <driver/pcm.h>
#include <include_bin.h>
#include <wav_utils.h>
INCBIN(audio, "example/resource/test_16k_s16_le.wav"); //使用 单声道的音频文件,该文件存放在源码根目录下:
static struct pcm_params playback_params = {
.channels = 1, //设置为单通道
.pcm_data_fmt = pcm_fmt_S16LE,
.pcm_sample_rate = pcm_rate_16000,
.pcm_interface = pcm_interface_i2s,
.i2s_frame_mode = i2s_LR_mode,
.i2s_bclk_direction = i2s_bclk_codec_master,
.i2s_frame_direction = i2s_frame_codec_master,
};
static struct pcm_params capture_params = {
.channels = 1, //设置为单通道
.pcm_data_fmt = pcm_fmt_S16LE,
.pcm_sample_rate = pcm_rate_16000,
.pcm_interface = pcm_interface_i2s,
.i2s_frame_mode = i2s_LR_mode,
.i2s_bclk_direction = i2s_bclk_codec_master,
.i2s_frame_direction = i2s_frame_codec_master,
};
static volatile int playback_ok = 0;
static int gpio_pa_enable = GPIO_PB(11); // RD_AD100_EVB_V1.0开发板的功放引脚是PB11
void write_thread_func(void *data)
{
struct pcm_device *dai = pcm_get("aic-playback");
struct pcm_device *codec = pcm_get("icodec-playback");
/* 注意!
* 如果为外部codec,则必须在使能aic之前使能。
* 不是外部则无此规定。
*/
pcm_enable(codec, &playback_params);
pcm_enable(dai, &playback_params);
pcm_start(codec);
pcm_start(dai);
// 使能功放引脚
if (gpio_pa_enable >= 0)
gpio_direction_output(gpio_pa_enable, 1);
/* 解析wav格式音频,得到数据起始和帧数 */
void *audio = wav_pcm_data((void *) audioData);
int frames = wav_pcm_frames((void *) audioData);
pcm_write_frame(dai, (void *)audio, frames);
pcm_disable(dai);
pcm_disable(codec);
// 关闭功放引脚
if (gpio_pa_enable >= 0)
gpio_direction_output(gpio_pa_enable, 0);
playback_ok = 1;
}
static unsigned int buffer[10 * 16 * 1024];
void pcm_test(void)
{
struct pcm_device *c_dai = pcm_get("aic-capture");
struct pcm_device *c_codec = pcm_get("icodec-capture");
if (gpio_pa_enable >= 0)
gpio_request(gpio_pa_enable, "pa_enable");
pcm_private_ctrl(c_dai, "sysclk-set-rate", 16000 * 768);
pcm_private_ctrl(c_dai, "sysclk-set-output", 1);
/*
* 注意!
*
* 对于外接的模拟mic
* 内部codec需要提前设置enable的时候是否要打开偏置电压
*
* 如果是回采电路,则不需要打开偏置电压
*/
pcm_private_ctrl(c_codec, "bias-on", 1);
printf("We are bias-on, please know that\n");
/*
* 开启播放线程
* 内部codec实际上只能播放左声道的数据
*/
thread_create("write_thread", 4096, write_thread_func, NULL);
/*
* 开始录音
* 内部codec实际上只能录制一个通道的数据在左声道
*/
pcm_enable(c_dai, &capture_params);
pcm_enable(c_codec, &capture_params);
pcm_start(c_codec);
pcm_start(c_dai);
pcm_read_frame(c_dai, buffer, sizeof(buffer)/pcm_frame_size(&capture_params));
pcm_disable(c_codec);
pcm_disable(c_dai);
/*
* 等待播放线程结束
*/
while (!playback_ok)
msleep(1);
/*
* 播放录音数据
* 此时录制的音量可能比较小
*/
struct pcm_device *dai = pcm_get("aic-playback");
struct pcm_device *codec = pcm_get("icodec-playback");
pcm_enable(dai, &playback_params);
pcm_enable(codec, &playback_params);
// 使能功放引脚
if (gpio_pa_enable >= 0)
gpio_direction_output(gpio_pa_enable, 1);
pcm_start(codec);
pcm_start(dai);
pcm_write_frame(dai, (void *)buffer, sizeof(buffer) / pcm_frame_size(&playback_params));
pcm_disable(dai);
pcm_disable(codec);
// 关闭功放引脚
if (gpio_pa_enable >= 0)
gpio_direction_output(gpio_pa_enable, 0);
/*
* 打印录音数据
*/
dump_mem32(buffer, sizeof(buffer), 8);
}
修改 vendor/vendor.c ,测试AIC SPEAKER 播放 内容如下:
#include <stdio.h>
#include <../example/driver/audio_pcm_example.c>
void vendor_init(void *arg)
{
pcm_test(); //测试AIC播放预置的音频数据 “example/resource/test_16k_s16_le.wav”
printf("vendor init...\n");
}
编译烧录以后,开机运行如下命令,结果如下:
U-Boot SPL 2013.07-00035-gc0c7a2ebd-dirty (Dec 27 2023 - 11:45:03)
ERROR EPC 83f9c84c
CPA_CPAPCR:0320490d
CPM_CPMPCR:0320490d
CPM_CPEPCR:0190510d
CPM_CPCCR:9a073310
DDR: M14F5121632A type is : DDR2
GD25B512ME 00c8471a 00c8471a
[0.003652] sfc_nor: find chip:GD25B512ME id:0xc8471a
[0.004463] pcm_private_ctrl sysclk-set-output fail! return value 0xffffffea
[0.004720] We are bias-on, please know that
[0.004875] AIC: no support clk freq 12288000 and forced to use clk freq 4096000
[0.037088] n: 36
[0.037147] n: 12
$ [40.998086] vendor init...
5.2 dmic 录音和播放测试 example
dmic 录音和播放测试程序: freertos$ ls example/driver/dmic_aic_pcm_example.c 内容如下:
#include <stdio.h>
#include <driver/pcm.h>
#include <common.h>
#include <driver/gpio.h>
#include <wav_utils.h>
#include <include_bin.h>
INCBIN(audio, "example/resource/end_playing.aiff");
// 以 RD_AD100_EVB_V1.0 板子为例
// icodec 播放 end_playing.aiff 音频, 同时 dmic 录音, 录音结束且播放结束后播放录到的数据, 播放结束退出
static struct pcm_params playback_params = {
.channels = 1,
.pcm_data_fmt = pcm_fmt_S16LE,
.pcm_sample_rate = pcm_rate_48000,
.pcm_interface = pcm_interface_i2s,
.i2s_frame_mode = i2s_LR_mode,
.i2s_bclk_direction = i2s_bclk_codec_master,
.i2s_frame_direction = i2s_frame_codec_master,
};
static struct pcm_params capture_params = {
.channels = 1,
.pcm_data_fmt = pcm_fmt_S16LE,
.pcm_sample_rate = pcm_rate_48000,
.pcm_interface = pcm_interface_dmic,
};
static volatile int playback_ok = 0;
static int gpio_spk_enable = GPIO_PB(11); // RD_AD100_EVB_V1.0开发板的功放引脚是PB11
void write_thread_func(void *data)
{
struct pcm_device *dai = pcm_get("aic-playback");
struct pcm_device *codec = pcm_get("icodec-playback");
pcm_enable(codec, &playback_params);
pcm_enable(dai, &playback_params);
pcm_start(codec);
pcm_start(dai);
if (gpio_spk_enable >= 0)
gpio_direction_output(gpio_spk_enable, 1);
void *audio = wav_pcm_data((void *) audioData);
int frames = wav_pcm_frames((void *) audioData);
pcm_write_frame(dai, (void *)audio, frames);
pcm_disable(dai);
pcm_disable(codec);
if (gpio_spk_enable >= 0)
gpio_direction_output(gpio_spk_enable, 0);
playback_ok = 1;
printf("We are playback_ok\n");
}
static unsigned int buffer[5 * 16 * 1024];
void dmic_aic_pcm_test(void)
{
struct pcm_device *dai = pcm_get("dmic-capture");
if (gpio_spk_enable >= 0)
gpio_request(gpio_spk_enable, "spk_enable");
thread_create("write_thread", 4096, write_thread_func, NULL);
pcm_enable(dai, &capture_params);
pcm_start(dai);
pcm_read_frame(dai, buffer, sizeof(buffer)/pcm_frame_size(&capture_params));
pcm_stop(dai);
pcm_disable(dai);
while (!playback_ok)
msleep(1);
printf("We are capture_ok\n");
struct pcm_device *pdai = pcm_get("aic-playback");
struct pcm_device *codec = pcm_get("icodec-playback");
pcm_enable(pdai, &playback_params);
pcm_enable(codec, &playback_params);
if (gpio_spk_enable >= 0)
gpio_direction_output(gpio_spk_enable, 1);
pcm_start(codec);
pcm_start(pdai);
pcm_write_frame(pdai, (void *)buffer, sizeof(buffer) / pcm_frame_size(&playback_params));
pcm_disable(pdai);
pcm_disable(codec);
if (gpio_spk_enable >= 0)
gpio_direction_output(gpio_spk_enable, 0);
printf("We are playback_ok again\n");
}
修改 vendor/vendor.c ,测试dmic录音和使用CODEC SPEAKER 播放 内容如下:
#include <stdio.h>
#include <../example/driver/dmic_aic_pcm_example.c>
void vendor_init(void *arg)
{
dmic_aic_pcm_test(); //测试dmic的录音,并且播放预置的音频数据 “example/resource/end_playing.aiff”
printf("vendor init...\n");
}
编译烧录以后,开机运行如下命令,结果如下:
U-Boot SPL 2013.07-00035-gc0c7a2ebd-dirty (Dec 27 2023 - 11:45:03)
ERROR EPC 8002c720
CPA_CPAPCR:0320490d
CPM_CPMPCR:0320490d
CPM_CPEPCR:0190510d
CPM_CPCCR:9a073310
DDR: M14F5121632A type is : DDR2
GD25B512ME 00c8471a 00c8471a
[0.003662] sfc_nor: find chip:GD25B512ME id:0xc8471a
[0.036095] n: 36
[0.036155] n: 12
[1.446093] We are playback_ok
[3.427424] We are capture_ok
[6.809092] We are playback_ok again
[6.809216] vendor init...