上期内容,我们学习了如何通过集成于RVB2601的W800实现Wi-Fi联网,并进行了以太网通讯测试,带大家直观了解了RVB2601 Wi-Fi功能的使用方法。本期我们将为大家带来RVB2601另一基础功能的教程,即通过ES7210进行麦克风数字化采样。
01 概述
RVB2601采用ES7210进行麦克风的数字化采样。本次测试,学习使用I2S接口从ES7210中进行一定时间的麦克风音频数据采集和暂存。
02 驱动描述
2.1 硬件接口原理
CH2601采用I2C接口完成ES7210的配置,采用I2S接口读取ES7210的转换数据,其接口如图所示。
序号 |
ES7210 |
GPIO |
1 |
I2C SCL |
PA8(I2C0_SCL) |
2 |
I2C SDA |
PA9(I2C0_SDA) |
3 |
ADC MCLK |
PA10(I2S0_MCLK) |
4 |
ADC BCLK |
I2S4_SCLK |
5 |
ADC LRLK |
I2S4_LSCLK |
6 |
ADC DAT1 |
I2S4_DATA |
7 |
ADC DAT2 |
I2S5_DATA |
2.2 软件驱动设计 CODEC
CODEC在这里指的是同时具有D/A(数字讯号转换成模拟讯号)和A/D(模拟讯号转换成数字讯号)转换功能的编解码器,播放音乐的时候用到的是D/A转换功能。在录音的时候用到的是A/D转换功能。
在接口中,D/A指的是输出通道,A/D指的是输入通道。我们这里主要使用AD的输入通道
本次使用CODEC的CSI接口如下所示:
函数 |
说明 |
csi_codec_init |
CODEC设备初始化 |
csi_codec_uninit |
CODEC设备去初始化 |
csi_codec_input_open |
CODEC输入通道打开 |
csi_codec_input_config |
CODEC输入通道配置 |
csi_codec_input_analog_gain |
CODEC输入通道设置模拟增益 |
csi_codec_input_start |
CODEC接收输入音频流 |
csi_codec_input_stop |
CODEC结束接收输入音频流 |
csi_codec_input_read_async |
CODEC输入通道异步模式读取数据 |
csi_codec_input_read |
CODEC输入通道同步模式读取数据 |
csi_codec_input_attach_callback |
CODEC输入通道注册回调函数 |
csi_codec_input_detach_callback |
CODEC输入通道注销回调函数 |
csi_codec_input_close |
CODEC输入通道关闭 |
csi_codec_input_link_dma |
CODEC输入通道配置DMA |
CODEC设备初始化CODEC用于输入接口详细说明
- CODEC设备初始化
csi_error_t csi_codec_init(csi_codec_t *codec, uint32_t idx)
功能描述:
通过设备ID初始化对应的CODEC实例。
参数:
codec: 设备句柄(需要用户申请句柄空间)。
idx: 设备ID。
返回值:
CSI_OK: 初始化成功。
CSI_ERROR: 初始化失败。
csi_codec_t
成员 |
类型 |
说明 |
dev |
csi_dev_t |
csi设备统一句柄 |
output_chs |
csi_codec_output_t |
输出通道句柄 |
input_chs |
csi_codec_input_t |
输入通道句柄 |
*priv |
void |
设备私有变量 |
ringbuffer_t
成员 |
类型 |
描述 |
buffer |
uint8_t * |
环形缓冲区地址 |
size |
uint32_t |
环形缓冲区大小 |
write |
uint32_t |
环形缓冲区当前写指针位置 |
read |
uint32_t |
环形缓冲区当前读指针位置 |
data_len |
uint32_t |
环形缓冲区当前可读数据长度 |
csi_codec_output_t
成员 |
类型 |
描述 |
codec |
csi_codec_t * |
CODEC设备句柄 |
ch_idx |
uint32_t |
当前通道的序号 |
callback |
void (callback)(csi_codec_output_t output, csi_codec_event_t event, void *arg) |
当前通道的回调 |
arg |
void * |
当前通道的用户参数 |
ring_buf |
ringbuffer_t * |
当前通道的缓冲器句柄 |
period |
uint32_t |
设置完成多少数据发送上报周期 |
sound_channel_num |
uint32_t |
声道数 |
state |
csi_state_t |
当前通道的状态 |
dma |
csi_dma_ch_t * |
当前通道的DMA句柄 |
next |
struct csi_codec_output * |
下一个输出通道的地址指针 |
priv |
void * |
设备私有变量 |
csi_codec_input_t
成员 |
类型 |
描述 |
codec |
csi_codec_t * |
CODEC设备句柄 |
ch_idx |
uint32_t |
当前通道的序号 |
callback |
void (callback)(csi_codec_input_t input, csi_codec_event_t event, void *arg) |
当前通道的回调 |
arg |
void * |
当前通道的用户参数 |
ring_buf |
ringbuffer_t * |
当前通道的缓冲器句柄 |
period |
uint32_t |
设置完成多少数据接收上报周期 |
sound_channel_num |
uint32_t |
声道数 |
state |
csi_state_t |
当前通道的状态 |
dma |
csi_dma_ch_t * |
当前通道的DMA句柄 |
next |
struct csi_codec_input * |
下一个输入通道的地址指针 |
priv |
void * |
设备私有变量 |
- CODEC设备去初始化
void csi_codec_uninit(csi_codec_t *codec)
功能描述:
codec实例反初始化。该接口会清理并释放相关的软硬件资源。
参数:
codec: 实例句柄。
返回值:
无。
- CODEC输入通道打开
csi_error_t csi_codec_input_open(csi_codec_t *codec, csi_codec_input_t *ch, uint32_t ch_idx)
功能描述:
将输入通道的ch句柄注册到codec句柄中。初始化输入通道有关的硬件资源。
参数:
codec: codec实例句柄。
ch: 输入通道的实例句柄。
ch_idx:通道的ID。
返回值:
错误码csi_error_t。
- CODEC输入通道配置
csi_error_t csi_codec_input_config(csi_codec_input_t *ch, csi_codec_input_config_t *config)
功能描述:
根据传入的配置配置输入通道。配置输入通道采样宽度、采样比率、设置缓冲区地址、设置输入通道的输出模式(差分输入还是单端输入)。
参数:
ch:通道实例句柄。
config:配置参数。
返回值:
错误码csi_error_t。
- CODEC输入通道注册回调函数
csi_error_t csi_codec_input_attach_callback(csi_codec_input_t *ch, void *callback, void *arg)
功能描述:
设置输入通道回调函数。
参数:
csi_codec_input_t:输入通道实例句柄。
callback:codec:输入通道的事件回调函数(一般在上下文执行)。
arg:回调函数参数(可选,由用户定义)。
返回值:
错误码csi_error_t。
callback
void (*callback)(csi_codec_input_t *input, csi_codec_event_t event, void *arg)
其中 input为输入通道句柄,event 为传给回调函数的事件类型,arg 为用户自定义的回调函数对应的参数。
codec 回调事件枚举类型csi_codec_event_t定义如下:
类型 |
说明 |
CODEC_EVENT_PERIOD_READ_COMPLETE |
接收period完成 |
CODEC_EVENT_PERIOD_WRITE_COMPLETE |
发送period完成 |
CODEC_EVENT_WRITE_BUFFER_EMPTY |
发送缓冲区已经空 |
CODEC_EVENT_READ_BUFFER_FULL |
接收缓冲区已经满 |
CODEC_EVENT_TRANSFER_ERROR |
传输错误 |
- CODEC输入通道注销回调函数
void csi_codec_input_detach_callback(csi_codec_input_t *ch)
功能描述:
注销CODEC 输入通道的回调函数。
参数:
ch:通道实例句柄。
返回值:
无。
- CODEC输入通道关闭
void csi_codec_input_close(csi_codec_input_t *ch)
功能描述:
关闭输入通道。调用该接口会马上停止接收数据。
参数:
ch:通道实例句柄。
- CODEC输入通道配置DMA
csi_error_t csi_codec_input_link_dma(csi_codec_input_t *ch, csi_dma_ch_t *dma)
功能描述:
输入通道连接DMA。
参数:
ch:输入通道的实例句柄。
dma:dma实例句柄。
返回:
错误码csi_error_t。
- CODEC接收输入音频流
csi_error_t csi_codec_input_start(csi_codec_input_t *ch)
功能描述:
输入通道开始数据流。
参数:
ch:输入通道的实例句柄。
返回值:
错误码csi_error_t。
- CODEC结束接收输入音频流
csi_error_t csi_codec_input_stop(csi_codec_input_t *ch)
功能描述:
输入通道结束数据流。
参数:
ch:输入通道的实例句柄。
返回值:
错误码csi_error_t。
- CODEC输入通道设置模拟增益
csi_error_t csi_codec_input_analog_gain(csi_codec_input_t *ch, uint32_t val)
功能描述:
设置输入通道模拟增益。
参数:
ch:输入通道的实例句柄。
val: 增益的DB值。
返回值:
错误码csi_error_t。
03 测试程序
本测试程序通过RVB2601建立一个通过I2S接口读取ES7210录音测试程序,数据录取结束后将数据打印到串口终端。
3.1 初始化
初始化代码参考wiki上的基本配置信息完成。
csi_codec_input_config_t input_config; /* init函数的idx参数,请根据soc的实际情况进行选择 */ ret = csi_codec_init(&codec, 0);; if (ret != CSI_OK) { return -1; } /* input ch config */ csi_codec_input_attach_callback(&codec_input_ch, codec_input_event_cb_fun, NULL); codec_input_ch.period = INPUT_BUF_SIZE/2; codec_input_ch.ring_buf = &input_ring_buffer; csi_codec_input_open(&codec, &codec_input_ch, 0); input_config.bit_width = 16; input_config.sample_rate = 8000; input_config.buffer = input_buf; input_config.buffer_size = INPUT_BUF_SIZE; input_config.period = INPUT_BUF_SIZE/2; input_config.mode = CODEC_INPUT_DIFFERENCE; csi_codec_input_config(&codec_input_ch,&input_config); csi_codec_input_link_dma(&codec_input_ch,&dma_ch_input_handle); csi_codec_input_start(&codec_input_ch);
3.2 事件处理
static void codec_input_event_cb_fun(csi_codec_input_t *i2s, csi_codec_event_t event, void *arg) { if (event == CODEC_EVENT_PERIOD_READ_COMPLETE) { cb_input_transfer_flag = 1; } }
3.3 数据的显示
int i; uint16_t *p = (uint16_t *)(repeater_data_addr + 24*1024); for(i=0;i<48*512;i++) { if(i%16 == 0) printf("\n"); printf("%04x ",p); }
3.4 通过console调用
static void mic_handler(char *wbuf, int wbuf_len, int argc, char **argv) { csi_error_t ret; csi_codec_input_config_t input_config; ret = csi_codec_init(&codec, 0); if (ret != CSI_OK) { printf("csi_codec_init error\n"); return ; } codec_input_ch.ring_buf = &input_ring_buffer; csi_codec_input_open(&codec, &codec_input_ch, 0); /* input ch config */ csi_codec_input_attach_callback(&codec_input_ch, codec_input_event_cb_fun, NULL); input_config.bit_width = 16; input_config.sample_rate = 8000; input_config.buffer = input_buf; input_config.buffer_size = INPUT_BUF_SIZE; input_config.period = 1024; input_config.mode = CODEC_INPUT_DIFFERENCE; csi_codec_input_config(&codec_input_ch, &input_config); csi_codec_input_analog_gain(&codec_input_ch, 0xbf); csi_codec_input_link_dma(&codec_input_ch, &dma_ch_input_handle); printf("start recorder\n"); csi_codec_input_start(&codec_input_ch); while (new_data_flag < 48) { if (cb_input_transfer_flag) { csi_codec_input_read_async(&codec_input_ch, repeater_data_addr + (new_data_flag * 1024), 1024); cb_input_transfer_flag = 0U; new_data_flag ++; } } aos_msleep(100); printf("record sound data: \n"); int i; uint16_t *p = (uint16_t *)(repeater_data_addr + 24*1024); for(i=0;i<512;i++) { if(i%16 == 0) printf("\n"); printf("%04x ",p); } printf("stop reorder\n"); csi_codec_input_stop(&codec_input_ch); csi_codec_input_link_dma(&codec_input_ch, NULL); csi_codec_input_detach_callback(&codec_input_ch); csi_codec_uninit(&codec); return; } int cli_reg_cmd_ft(void) { static const struct cli_command mic_cmd_info = { "mic", "mic", mic_handler, }; aos_cli_register_command(&mic_cmd_info); return 0; }
04 实测效果演示
4.1 录取数据开始
4.2 展示数据结束
4.3 波形数据展示
05 下期预告
有关RVB2601的麦克风测试,就先介绍到这里,下期将为大家带来的是基于RVB2601的远程音频采集系统开发介绍。欢迎大家持续关注应用实战精解系列内容。