- 2021-02-28
-
回复了主题帖:
【ESP32-Korvo测评】(5)麦克风音频流抓取的实现
Jacktang 发表于 2021-2-28 22:10
乐鑫的这个 Korvo 板搭载百度鸿鹄语音芯片
期待更多的测评分享
不是的。 语音算法全是在ESP32上软件实现。
-
发表了主题帖:
【ESP32-Korvo测评】(5)麦克风音频流抓取的实现
在 Korvo 开发板上,语音识别需要的声音信号来自三颗 MEMS 模拟麦克风。经过板载 ADC 采样成 PCM 音频数据流之后,从 I2S 接口传入 ESP32. 与音频回放调用相似,程序通过 i2s_read() 函数获取来自 ADC 的 PCM 码流(每调用一次读取确定的一段长度)。但是从麦克风采集的音频信号并不直接用作语音识别的原始数据,而是要经过几步处理:
这个图是我根据例子中 recsrc.c 的代码画出来的,对应于 Korvo 开发板。在第一步,音频数据从 I2S 接口获取之后,交给 AEC (回声消除)算法处理函数进行3通道回声消除处理,回声消除后的音频写入 aec_rb 这个环形缓冲区 (ring buffer). 第二步,MASE (麦克风阵列语音增强) 任务从 aec_rb 获取3通道音频,由 MASE 算法处理后得到单声道的音频数据,写入 mase_rb 环形缓冲区。第三步,AGC (自动增益控制) 任务从 mase_rb 获取音频,对信号进行增益调整后写入 agc_rb 环形缓冲区。最后,语音识别算法再从 agc_rb 缓冲区中取音频进行识别计算。
为什么要用到几个环形缓冲区呢?这是因为语音处理是需要时间的,CPU不可能在每一个音频采样数据到来后立即完成处理——通常算法需要以一定长度的帧(frame)为单位进行处理。环形缓冲区起到 FIFO 的作用,在算法处理期间保证未处理的数据有地方存储。AEC算法之前并没有用环形缓冲区,或许是因为 i2s 软件驱动里面带的缓冲已经够用了。
为了后续的评测工作,我需要将音频数据实时地保存到 SD 卡上,以便评估音频质量。与前面做过的回放操作相比,就刚好是反过来了。不过写文件操作还不能影响已有的信号处理过程,因此我需要用单独的任务来进行文件操作,并使用另外独立的环形缓冲区。
原来的 ADC 音频获取代码片段是这样:
#elif defined CONFIG_ESP32_KORVO_V1_1_BOARD
i2s_read(I2S_NUM_1, rsp_in, 4 *AEC_FRAME_BYTES, &bytes_read, portMAX_DELAY);
for (int i = 0; i < AEC_FRAME_BYTES / 2; i++) {
aec_ref = rsp_in[4 * i + 0];
aec_rec = rsp_in[4 * i + 1];
aec_rec[i + AEC_FRAME_BYTES / 2] = rsp_in[4 * i + 3];
if (nch == 3)
{
aec_rec[i + AEC_FRAME_BYTES] = rsp_in[4 * i + 2];
}
}
aec_process(aec_handle, aec_rec, aec_ref, aec_out);
rb_write(rec_rb, aec_out, AEC_FRAME_BYTES * nch, portMAX_DELAY);
#endif
i2s_read() 调用读取长度是 AEC_FRAME_BYTES, 而存放读取数据的 rsp_in 是 AEC_FRAME_BYTES*I2S_CHANNEL_NUM 字节数的动态申请内存。这里通道数为4, 对应3只麦克风和一个回放参考通道。PCM 是 16-bit 16kHz 格式,因此每个帧采样长度是 AEC_FRAME_BYTES/2. 程序用一个循环把数据复制并重新排布,以满足 aec_process() 函数的数据格式要求。
现在想取出一个麦克风通道的原始音频数据,存入 SD 卡的文件中,就只需要将 aec_rec 的一部分写入文件就可以了。为了不增加过多的延迟,我将这部分数据写到一个环形缓冲区:在 aec_process() 之前,
if(dump_enabled)
rb_write(dump_rb, aec_rec, AEC_FRAME_BYTES, portMAX_DELAY);
另外编写一个写文件用的任务:
void dumpPCMTask(void *arg)
{
if(mount_sdcard())
{
static uint8_t fc;
FILE *fdump;
char fname[20];
uint8_t *buf = malloc(AEC_FRAME_BYTES);
fc++;
sprintf(fname, "/sdcard/rec%d.pcm",fc);
fdump=fopen(fname,"w");
if(fdump)
{
rb_reset(dump_rb);
dump_enabled=1;
while(dump_enabled)
{
rb_read(dump_rb, buf, AEC_FRAME_BYTES, 100);
fwrite(buf, AEC_FRAME_BYTES, 1, fdump);
}
fclose(fdump);
printf("End of dump: %s\n",fname);
}
free(buf);
esp_vfs_fat_sdmmc_unmount();
}
else
printf("Error: SD card not mounted!\n");
vTaskDelete(NULL);
}
启动和停止录音我就用两个语音命令来控制。把例子里默认的 speech_commands_action.c 编辑一下:
extern uint8_t dump_enabled;
extern void dumpPCMTask(void *);
void speech_commands_action(int command_id)
{
printf("Commands ID: %d.\n", command_id);
switch(command_id)
{
case 20: // start recording
xTaskCreatePinnedToCore(&dumpPCMTask, "dump", 4 * 1024, NULL, 8, NULL, 1);
break;
case 21: // stop recording
dump_enabled=0;
break;
}
}
抱怨一下:新增命令词要改工程的配置文件,然而改了以后make, 整个工程都被重新编译了,极不方便。
dumpPCMTask 的堆栈大小第一次设的 2kB 不够用,ESP32 出现异常重起了。改为 4kB 就好用了。
录了一段麦克风原始音频,取出 SD 卡后在电脑上查看录音文件:
播放此文件效果良好,没有发生破音、间断等异常情况。成功。
- 2021-02-27
-
发表了主题帖:
【ESP32-Korvo测评】(4)从SD卡播放PCM音频
ESP-Skainet 里面 examples 目录的几个例子中,有些是还有音频回放的——直接接喇叭或者从3.5mm音频口接有源音箱可以听到声音。比如 garbage_classification 这个例子会对唤醒词以及识别到的命令词作出语音回应。
如前所述,ESP-IDF 这个软件框架包含了很多的组件,给编写程序提供了丰富的软件支持。读一读例子代码就发现,对于音频播放这件室,不需要写什么初始化代码——系统执行到 app_main() 的时候要用的硬件已经初始化好了,只需要调用一个 i2s_write() 函数来向 Codec 输出音频PCM码流就能放出声音。当然,Codec芯片和ESP32怎么连接的是需要配置的,这在工程里面需要设好(例子是为Korvo开发板定制的,默认已经配好了)。
我还需要从外部获取音频数据,毕竟把音频放到程序数据里面每次都要下载Flash不方便。Korvo开发板带有 TF 卡槽,因此从 TF 卡读取音频文件来播放是最适合的。关于 TF (Micro SD) 卡的访问,想必 ESP-IDF 已经提供了支持,于是我到 IDF 的 examples 目录下去找个代码来参考。
简化以后,核心的代码只需要一个函数调用 esp_vfs_fat_sdmmc_mount() 就可以挂载 SD 卡:
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
slot_config.gpio_miso = 21;
slot_config.gpio_mosi = 18;
slot_config.gpio_sck = 5;
slot_config.gpio_cs = 23;
slot_config.gpio_cd = 35;
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = false,
.max_files = 5,
.allocation_unit_size = 16 * 1024
};
sdmmc_card_t* card;
esp_err_t ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card);
这上面 slot_config 指定了 SD 卡如何与 ESP32 连接的,就是引脚的配置信息。这里用的默认的 SPI 模式,就是 Korvo 板子上的接法。根据函数的返回值判断 SD 卡是否成功识别了,如果成功,访问卡上的文件就很方便了。
根据例子代码,在挂载 SD 卡之后就可以直接用 C 标准库的 fopen() 函数来打开 "/sdcard/" 这个路径下面的文件,然后就用标准库的文件读写调用访问文件即可。竟然如此简单!于是我就试着在卡上存一个 PCM 文件,通过 Korvo 板子播放出来:
ESP_LOGI(TAG, "Opening file");
FILE* f = fopen("/sdcard/mazurka.pcm", "r");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open pcm file");
return;
}
uint16_t *pcm = malloc(32000*2);
if(!pcm)
ESP_LOGI(TAG, "malloc() failed");
else
{
while(!feof(f))
{
size_t wbs;
fread(pcm, 2, 32000, f);
i2s_write(0,pcm,64000,&wbs,portMAX_DELAY);
}
free(pcm);
}
fclose(f);
esp_vfs_fat_sdmmc_unmount();
每次从文件读取了 64000 字节,对应 16-bit 单声道 32000 个采样,存放在 pcm 缓冲数组中。注意这里使用 malloc() 动态分配的内存,因为我一开始用的是局部变量,一运行堆栈就溢出了……毕竟是 FreeRTOS 给任务分配的堆栈,还是省着用吧。fread() 读文件之后就用 i2s_write() 来播放。
系统回放的采样率是预定好的,可以通过测量 I2S LRCK 信号的频率来确认:是 16kHz. TF 卡上存的文件要转成 16kHz 采样的,不然回放出来就变调了。
由于是在一个循环中“读文件”然后“写Codec”再反复,从 ESP32 发送到 Codec 的音频码流可能发生断流的情况。这取决于 i2s_write() 函数的设计了——当它返回之后内部缓冲区是否还有未发送的数据,是否未发送的数据能坚持到下一次 i2s_write() 函数的调用“续”上码流?虽然 fread() 读 SD 卡 64000 字节时间不长,如果因为一点等待造成的音频码流中断是可以听出来的。
我上面代码每次播放了 2 秒的音频,可以听出来每2秒之后有短促的间断。要解决这个 bug, 看来需要两个任务分别进行读文件和写Codec的操作了。可是我发现回放的音质并不理想,这一点间断也不是最严重的问题,暂时就不管它喽。
- 2021-02-23
-
发表了主题帖:
【ESP32-Korvo测评】(3)ESP-Skainet工程的编译
既然 ESP-Korvo 具有语音处理的硬件,自然少不了官方的软件支持了。这次评测的重点是乐鑫提供的 ESP-Skainet 这个开放软件包,它提供了若干音频和语音识别的API支持,也包含了这个开发板的硬件驱动库。
我是从 github 上把 esp-skainet 克隆到本地电脑上的。(也许还有其它更便捷的下载方式)因为 ESP-Skainet 自带了某一个版本的 ESP-IDF, 所以就不用单独下载 IDF 了。什么是 ESP-IDF? 这是 ESP32 的软件开发框架,包括了很多部件,并不是一个单纯的硬件支持库。比如说,IDF 提供了 bootloader, 做了比 STM32 MCU 启动代码多很多的工作;比如说,IDF 底层有 FreeRTOS, 编写一个应用的入口函数是 app_main() 而不是 main(). IDF给我的感觉是更接近操作系统的一个东西,把ESP32启动过程隐藏了,能想得到的BLE、网络支持、文件系统支持它都有。如果不用 IDF 的话对 ESP32 开发就很难下手(乐鑫说:我们把框架都给你搭好了,你就用吧……)
有了 ESP-IDF 就可以开发 ESP32 了,那么用什么编译呢?自然需要 Xtensa 处理器的 gcc 编译工具,可以从乐鑫的网站上下载到。想折腾,自己用 GCC 的源代码编译也是可以的。但只有编译器还不够,因为 IDF 每个工程也需要配置,就绕不开使用使用乐鑫的 Python 脚本和辅助程序。这跟开发 STM32/LPC/ATSAM...都很不一样是不是?也难怪,因为 IDF 实在复杂太多了。如果是 Linux 系统,软件工具的问题就看系统上装的够不够使,如果是 Windows 系统呢?需要一个类似 Linux 下的操作环境,比如 cygwin, msys 然后使用经过移植的软件。乐鑫又给了金点子:不好装吗?我给你准备了一套,下载去就能用咯!
我这次使用的是直接下过来的 esp32_win32_msys2_environment_and_esp2020r2_toolchain-20200601.zip 这个压缩包,里面有 msys2 环境下的软件,和 Xtensa 编译工具。直接解压缩就可以使用了,缺点嘛,就是对 windows xp 不友好,这是 msys2 版本的问题。
启动 msys2 终端程序(mingw32.exe),试着编译一下 esp-skainet 里面 examples/get_started 这个工程。因为已存在 Makefile, 就直接 make 好啦。
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := get_started
EXTRA_COMPONENT_DIRS += ../../components/
include $(IDF_PATH)/make/project.mk
这个 Makefile 包含了 IDF 里面实际用的编译指令,所以要设置一下 IDF 的路径:
export IDF_PATH=../../esp-idf
然后再 make 就一路编译下去了……居然编译了很久,可不像一个MCU的工程。结束后在生成的 build 目录下可以发现有超过百兆字节的文件。我还没有配置 IDF 组件, 用的 esp-skainet 原先默认的了。如果想配置,那么执行 make menuconfig, 出现跟配置 linux 内核相仿的字符界面:
在编译完成后有如下的提示,说明怎么下载到板子:
To flash all build output, run 'make flash' or:
python /d/esp-skainet/esp-idf/components/esptool_py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 115200 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0x1000 /d/esp-skainet/examples/get_started/build/bootloader/bootloader.bin 0x10000 /d/esp-skainet/examples/get_started/build/get_started.bin 0x8000 /d/esp-skainet/examples/get_started/build/partitions.bin
要烧写的文件有三个:bootloader.bin, get_started.bin, partitions.bin
我使用连着板子的另一台机器来进行下载:
复位之后运行正常,串口打印的内容和以前不同了。看来get_started例子启动了语音检测,那么就唤醒试试——可以工作。
看看 main.c 里面写了什么。主函数是 app_main()
void app_main()
{
codec_init();
rec_rb = rb_init(BUFFER_PROCESS, 8 * 1024, 1, NULL);
#ifdef CONFIG_ESP32_KORVO_V1_1_BOARD
mase_rb = rb_init(BUFFER_PROCESS, 8 * 1024, 1, NULL);
#else
ns_rb = rb_init(BUFFER_PROCESS, 8 * 1024, 1, NULL);
#endif
agc_rb = rb_init(BUFFER_PROCESS, 8 * 1024, 1, NULL);
model_iface_data_t *model_data = wakenet->create(model_coeff_getter, DET_MODE_90);
model_data_mn = multinet->create(&MULTINET_COEFF, 4000);
xTaskCreatePinnedToCore(&recsrcTask, "rec", 2 * 1024, NULL, 8, NULL, 1);
#ifdef CONFIG_ESP32_KORVO_V1_1_BOARD
xTaskCreatePinnedToCore(&maseTask, "mase", 2 * 1024, NULL, 8, NULL, 1);
#else
xTaskCreatePinnedToCore(&nsTask, "ns", 2 * 1024, NULL, 8, NULL, 1);
#endif
xTaskCreatePinnedToCore(&agcTask, "agc", 2 * 1024, NULL, 8, NULL, 1);
xTaskCreatePinnedToCore(&wakenetTask, "wakenet", 2 * 1024, (void*)model_data, 5, NULL, 0);
printf("-----------awaits to be waken up-----------\n");
}
app_main() 做的是初始化和创建语音处理、识别有关的几个任务,然后就结束了。 在 wakenetTask 任务当中,接收语音并处理:
void wakenetTask(void *arg)
{
model_iface_data_t *model_data = arg;
int frequency = wakenet->get_samp_rate(model_data);
int audio_chunksize = wakenet->get_samp_chunksize(model_data);
int chunk_num = multinet->get_samp_chunknum(model_data_mn);
printf("chunk_num = %d\n", chunk_num);
int16_t *buffer = malloc(audio_chunksize * sizeof(int16_t));
assert(buffer);
int chunks = 0;
int mn_chunks = 0;
bool detect_flag = 0;
while (1) {
rb_read(agc_rb, (uint8_t *)buffer, audio_chunksize * sizeof(int16_t), portMAX_DELAY);
if (detect_flag == 0) {
int r = wakenet->detect(model_data, buffer);
if (r) {
float ms = (chunks * audio_chunksize * 1000.0) / frequency;
printf("%.2f: %s DETECTED.\n", (float)ms / 1000.0, wakenet->get_word_name(model_data, r));
detect_flag = 1;
printf("-----------------LISTENING-----------------\n\n");
rb_reset(rec_rb);
rb_reset(ns_rb);
rb_reset(agc_rb);
}
} else {
int command_id = multinet->detect(model_data_mn, buffer);
mn_chunks++;
if (mn_chunks == chunk_num || command_id > -1) {
mn_chunks = 0;
detect_flag = 0;
if (command_id > -1) {
speech_commands_action(command_id);
} else {
printf("can not recognize any speech commands\n");
}
printf("\n-----------awaits to be waken up-----------\n");
rb_reset(rec_rb);
rb_reset(ns_rb);
rb_reset(agc_rb);
}
}
chunks++;
}
vTaskDelete(NULL);
}
代码和串口打印的内容的确能对应上。后面就可以尝试改代码了。
- 2021-02-05
-
回复了主题帖:
TPS61040升压电路异常
选型错误。你这个应用不该用TPS61040.
- 2021-01-30
-
回复了主题帖:
【ESP32-Korvo测评】(2)初识ESP32
本帖最后由 cruelfox 于 2021-1-31 00:35 编辑
原来是选择了固件文件,填写地址(0)之后,最前面那个复选框要打勾。我先给Erase了,因为没有打勾实际上没有烧入固件,所以就不能启动了。
可惜的是,EFuse里面禁用了DEBUG CONSOLE,没有固件的时候不能玩ROM BASIC
-
发表了主题帖:
【ESP32-Korvo测评】(2)初识ESP32
本帖最后由 cruelfox 于 2021-1-31 11:06 编辑
ESP32这颗芯片,和最为普遍的ARM系MCU之间的差别,还是相当大的。首先是因为处理器核心不同,ESP32用的是Tensilica Xtensa系列处理器(也就是和ESP8266属于近亲),所以基本的开发工具链(汇编、C/C++编译器、调试器等)都是和ARM完全独立的另一套。其次,片上资源、地址空间排布都和ARM Cortex-m那些MCU差异很大,比如STM32开发的应用移植到GD32上面不难,但挪到ESP32就得把底层一套框架都要换了。然后,ESP32是没有内部flash的,只能从内部ROM bootloader程序启动,由bootloader从片外的SPI flash加载程序到SRAM运行(也可能用地址映射方式从外部flash直接运行程序);烧写程序是烧写到片外的flash, 常规办法是上位机和bootloader通过UART交互,间接对flash写入。此外,开发环境也是很不同的风格,除了Xtensa工具链还需要乐鑫的一系列工具(强烈依赖Python)来辅助。
因为ESP32片上SRAM还不算少,又有外扩的(WROVER模块里面)8MB PSRAM可用来存储数据,这块开发板做复杂算法的潜力是具备的。
收到的ESP32-Korvo开发板已经带有演示程序了,不过若仅给板子通电就看不到运行的效果。原因在于USB串口没有启用时其RTS/DTR信号使ESP32的GPIO0和GPIO2拉低(参看我上个帖子对电路的分析),使ESP32 bootloader进入下载模式,没有加载SPI flash中的程序。
当USB串口驱动装上并打开串口之后,ESP32就正常启动了。可以从终端程序看到bootloader输出的日志信息,以及演示程序输出的信息。
ets Jul 29 2019 12:21:46
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:7128
load:0x40078000,len:11824
ho 0 tail 12 room 4
load:0x40080400,len:7344
entry 0x40080798
I (64) boot: Chip Revision: 3
I (71) boot_comm: chip revision: 3, min. bootloader chip revision: 0
I (42) boot: ESP-IDF v3.2.3-201-gfda11a6-dirty 2nd stage bootloader
I (42) boot: compile time 14:40:42
I (42) boot: Enabling RNG early entropy source...
I (48) qio_mode: Enabling default flash chip QIO
I (53) boot: SPI Speed : 80MHz
I (57) boot: SPI Mode : QIO
I (61) boot: SPI Flash Size : 16MB
I (66) boot: Partition Table:
I (69) boot: ## Label Usage Type ST Offset Length
I (76) boot: 0 factory factory app 00 00 00010000 003c0000
I (84) boot: 1 nvs WiFi data 01 02 003d0000 00004000
I (91) boot: End of partition table
I (96) boot_comm: chip revision: 3, min. application chip revision: 0
I (103) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f400020 size=0x16fbd4 (1506260) map
I (510) esp_image: segment 1: paddr=0x0017fbfc vaddr=0x3ffb0000 size=0x00414 ( 1044) load
I (511) esp_image: segment 2: paddr=0x00180018 vaddr=0x400d0018 size=0xc7034 (815156) map
I (733) esp_image: segment 3: paddr=0x00247054 vaddr=0x3ffb0414 size=0x032bc ( 12988) load
I (737) esp_image: segment 4: paddr=0x0024a318 vaddr=0x40080000 size=0x00400 ( 1024) load
I (740) esp_image: segment 5: paddr=0x0024a720 vaddr=0x40080400 size=0x1959c (103836) load
I (797) boot: Loaded app from partition at offset 0x10000
I (797) boot: Disabling RNG early entropy source...
WYB:Slave mode:80 !
WYB:ES7210_MCLK_CTL_REG02 = 0xC3!
************** enter es8311 init **************
Quantized wakeNet5: wakeNet5_v1_hilexin_5_0.95_0.90, mode:0 (Nov 20 2019 14:34:43)
Quantized MN1_2 (Dec 5 2019 20:22:00)
SHIFT: 8, 11, 17, 18, 18, 17, 7, 16, 15, 13,
I (287) MN: ---------------------SPEECH COMMANDS---------------------
I (297) MN: Command ID0, phrase 0: guan bi dian deng
I (297) MN: Command ID1, phrase 1: da kai bai deng
I (307) MN: Command ID2, phrase 2: da kai hong deng
I (307) MN: Command ID3, phrase 3: da kai lv deng
I (317) MN: Command ID4, phrase 4: da kai lan deng
I (317) MN: Command ID5, phrase 5: da kai huang deng
I (327) MN: Command ID6, phrase 6: da kai cheng deng
I (327) MN: Command ID7, phrase 7: da kai zi deng
I (337) MN: ---------------------------------------------------------
ESP32-Korvo V1.1 Firmware V0.1
这时候可以喊“Hi, 乐鑫”唤醒命令词激活(我以前一直以为读作yuexin, 现在才知读作lexin),若成功,则可以看到一圈LED作流水灯状点亮。唤醒后程序会继续等待“打开红灯”之类的语音命令,若识别到语音命令,LED状态作出相应的变化。
从bootloader给的启动信息看到,演示程序加载的镜像分为了6段. 这跟我熟悉的STM32完全不同:STM32编译后的ELF文件提取出代码段连续写到片内flash就完事了。从这里推测,ESP32的程序下载并不是把编译的ELF提取二进制代码写入片外flash这么简单。它的程序怎么启动待我找具体的工程来编译后再分析。
在打开USB串口的状态下,若按着开发板上的Boot按钮再按Reset按钮,也会使板子复位进入下载模式。从串口终端能看到信息,但是要怎么操作就需要乐鑫的工具了。
rst:0x1 (POWERON_RESET),boot:0x23 (DOWNLOAD_BOOT(UART0/UART1/SDIO_REI_REO_V2))
waiting for download
除了使用Python脚本编写的工具,只是下载的话可以用"Flash download tool"程序来做。
不过,我把ESP-skainet SDK里面的default_firmware用这工具先擦除,再下载bin文件,板子就不能启动了,提示“invalid header: 0xffffffff
”错误,反复重启。估计是要写的地址没有填……
- 2021-01-28
-
发表了主题帖:
【ESP32-Korvo测评】(1)开发板电路连接
本帖最后由 cruelfox 于 2021-1-29 09:03 编辑
乐鑫科技ESP32-Korvo开发板,可以看作是ESP32的一个面向语音接收和处理的应用原型。硬件分为上下两块圆形的板子,以FPC排线连接。下面是主板,包含ESP32模块和主要音频电路,以及电源、接口部分;上面的是麦克风阵列板,除了麦克风外还附带了一组按钮开关和一组RGB LED。这套板子是针对语音应用的软件开发设计,并未留太多额外的扩展口,并非通用型的ESP32开发板。一般型应用,买ESP32模块自己搭外围电路也不复杂,只有音频应用涉及到ADC、DAC及功放这些电路才需要ESP32-Korvo这样的专用开发板。
主板的核心是焊在上面的ESP32-WROOVER-E模块,也就是ESP32芯片为主的核心板,两边引出了许多GPIO脚。模块只有一个3.3V的电源输入脚。
板子上的TF(MicroSD)卡座数据线是直接接到ESP32的GPIO上的(额外串联电阻、上拉电阻、ESD二极管的处理),但是电路图上 ESP0_SD_DAT1, ESP0_SD_DAT2 两个信号并未连到ESP32,因此SD卡是以SPI方式操作的。在ESP32模块的右边还有1组4个通孔焊盘,标注为“SDIO”,这是独立的另一个1-bit模式SD接口,4个信号也是直接连到ESP32.
从ESP32出来两组I2S(Inter-IC-Sound)信号,分别连接ES7210(4路输入ADC)和ES8311(单声道Codec). ES7210, ES8311也都接到ESP32引出的I2C总线上,用于配置。还有ESP32的一个GPIO信号PA_CTRL用于板载音频功放芯片的使能控制。
ESP32的I2C,以及另外两个GPIO信号:BT_ARRAY_ADC和WS2812_CTRL都连接到FPC座,也就是连到麦克风阵列板上去了。
ESP32的一组UART(两线)连接到模块右恻的通孔焊盘,也和板载的CP2102芯片(USB转串口)连接。板子的两个Micro-USB接口一个用于供电,另一个就是CP2102构成的虚拟串口。有点特殊的是,CP2102的DTR,RTS信号也用上了,接到三个三极管上面,对ESP32的引脚(Boot_EN, Boot_IO0, Boot_IO2)产生了影响,可以产生拉低的效果。此外,这三个信号可由板子边上的轻触开关来拉低。
汇总一下ESP32模块的引脚分配情况如下表:
模拟部分,ADC具有4路差分输入,但输出只用了一条数据线,怀疑数据为TDM格式而非I2S格式。
4路模拟输入只有其中3路通过FPC座连到了麦克风阵列板(也连接了到主板中央的2x7排针通孔焊盘上)。
另外的一路连接到了DAC的输出(经由一个阻容构成的衰减网络),用于回声消除的参考信号输入。
DAC的差分输出直接连到了3.5mm耳机座。如果不插耳机,这个信号就接入功放芯片NS4150, 用于驱动喇叭。
麦克风阵列板上最外圈是12只RGB LED. PCB设计有两种配置,一种是用驱动芯片IS31FL3236A, 接在I2C总线上由ESP32访问;另一种也就是实际选用的,用带控制器的LED芯片WS2812, 以菊花链形式连接,只用ESP32的一个IO口就能控制了,这样元件数量也少了很多。阵列板上中间有6个轻触开关排成一圈,它们被用于选择BT_ARRAY_ADC信号线上不同的下拉电阻。配合主板上的10k上拉电阻,不同的按键(以及组合)下这条信号线就有不同的电压,再用ESP32的ADC来转换,根据ADC值识别按键。
板子上麦克风阵列有三种配置,实际选用的三只模拟麦克风(MSM381A3729H9BPC)的形式。从电路图上看有一种配置是三只数字麦克风,其CLK和DAT数字引脚与模拟麦克风是共用排线,因此数字麦克风需要ADC支持才能工作,但主板上看不出有这样的支持。
- 2021-01-25
-
加入了学习《2020_Digikey KOL系列:欢迎进入MicroPython的奇妙世界》,观看 欢迎进入MicroPython的奇妙世界
-
加入了学习《2020_Digikey KOL系列:图形算法在深度学习等应用中的加速手段》,观看 图形算法在深度学习中的加速应用
-
加入了学习《2019_Digikey KOL系列:小功率 DC-DC 换流器设计常用技巧 》,观看 小功率 DC-DC 换流器设计常用技巧 (TI TPS560430为例)
- 2021-01-19
-
回复了主题帖:
更好的你我,更好的EEWorld!2020年终庆典
确认个人信息无误。这个获奖很意外呀,谢谢管管。
- 2021-01-12
-
回复了主题帖:
【测评名单公布】乐鑫ESP32-Korvo音频开发板免费测评试用
个人信息无误,确认可以完成评测计划。
- 2020-12-25
-
回复了主题帖:
恩智浦推出跨界处理器i.MX RT1170,主频高达1个G,你怎么看?好玩?好上手不?
玩,来一块呀
- 2020-12-21
-
回复了主题帖:
【NUCLEO-L552ZE测评】+CoreMark跑分测试
Rimas 发表于 2020-12-21 11:02
keil编译的性能竟然不如iar编译的性能。
棒!学到了
官方给的 coremark 跑分是 IAR 编译出来的结果。Keil编译出来没达到,也没什么奇怪的。
我以前测试F7的时候用GCC,比Keil快,也没官方的高。GCC不同版本编译出来的跑分也不一样,不知道现在的GCC优化如何了。
-
回复了主题帖:
1500-5个常用汉字笔画和发音
需要用这玩意儿来教孩子识字? 家长太焦虑了吧。
- 2020-12-16
-
回复了主题帖:
关于USB2.0的拔插切换
Vbus 可以完全不接就工作的。设备自己供电不用这个电源。
- 2020-12-11
-
回复了主题帖:
原来在FPGA里简单比较两个数的大小都是那么的不同?
verilog和C,不要那么多想当然。
- 2020-12-08
-
回复了主题帖:
如何考虑音频模拟信号经过100m的长线传输后的影响?
有专门的音频分析仪。把线接在仪器输出和输入之间测。
- 2020-11-25
-
回复了主题帖:
stm32h755/757, 这样的m7+m4异构多核mcu,能在同一时间执行两条指令吗?
oyhprince 发表于 2020-11-24 20:16
这样的异构多核MCU有两个 PC 寄存器指针吗?
当然有,分别属于不同的核。