cruelfox

  • 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 寄存器指针吗?
    当然有,分别属于不同的核。

最近访客

< 1/6 >

统计信息

已有789人来访过

  • 芯币:6414
  • 好友:11
  • 主题:140
  • 回复:1052
  • 课时:1
  • 资源:1

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言