This commit is contained in:
colin 2025-12-18 22:58:00 +08:00
commit 8a2b1dd2e9
14 changed files with 4807 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
build
managed_components
*.lock
sdkconfig

9
CMakeLists.txt Normal file
View File

@ -0,0 +1,9 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(speech_recognition)

4
main/CMakeLists.txt Normal file
View File

@ -0,0 +1,4 @@
idf_component_register(SRCS "img_bilibili120.c" "app_sr.c" "esp32_s3_szp.c" "main.c" "app_ui.c"
INCLUDE_DIRS ".")
spiffs_create_partition_image(storage ../spiffs FLASH_IN_PROJECT)

169
main/app_sr.c Normal file
View File

@ -0,0 +1,169 @@
#include "app_sr.h"
#include "esp_log.h"
#include "esp_wn_iface.h"
#include "esp_wn_models.h"
#include "esp_afe_sr_iface.h"
#include "esp_afe_sr_models.h"
#include "esp_mn_iface.h"
#include "esp_mn_models.h"
#include "esp_mn_speech_commands.h"
#include "model_path.h"
#include "esp32_s3_szp.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_process_sdkconfig.h"
#include "audio_player.h"
#include "app_ui.h"
static const char *TAG = "app_sr";
srmodel_list_t *models = NULL;
static esp_afe_sr_iface_t *afe_handle = NULL;
static esp_afe_sr_data_t *afe_data = NULL;
int detect_flag = 0;
static volatile int task_flag = 0;
void feed_Task(void *arg)
{
esp_afe_sr_data_t *afe_data = arg; // 获取参数
int audio_chunksize = afe_handle->get_feed_chunksize(afe_data); // 获取帧长度
int nch = afe_handle->get_channel_num(afe_data); // 获取声道数
int feed_channel = bsp_get_feed_channel(); // 获取ADC输入通道数
assert(nch <= feed_channel);
int16_t *i2s_buff = heap_caps_malloc(audio_chunksize * sizeof(int16_t) * feed_channel, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); // 分配获取I2S数据的缓存大小
assert(i2s_buff);
while (task_flag) {
bsp_get_feed_data(false, i2s_buff, audio_chunksize * sizeof(int16_t) * feed_channel); // 获取I2S数据
afe_handle->feed(afe_data, i2s_buff); // 把获取到的I2S数据输入给afe_data
}
if (i2s_buff) {
free(i2s_buff);
i2s_buff = NULL;
}
vTaskDelete(NULL);
}
void detect_Task(void *arg)
{
esp_afe_sr_data_t *afe_data = arg; // 接收参数
int afe_chunksize = afe_handle->get_fetch_chunksize(afe_data); // 获取fetch帧长度
char *mn_name = esp_srmodel_filter(models, ESP_MN_PREFIX, ESP_MN_CHINESE); // 初始化命令词模型
printf("multinet:%s\n", mn_name); // 打印命令词模型名称
esp_mn_iface_t *multinet = esp_mn_handle_from_name(mn_name);
model_iface_data_t *model_data = multinet->create(mn_name, 6000); // 设置唤醒后等待事件 6000代表6000毫秒
esp_mn_commands_clear(); // 清除当前的命令词列表
esp_mn_commands_add(1, "bo fang yin yue"); // 播放音乐
esp_mn_commands_add(2, "zan ting"); // 暂停
esp_mn_commands_add(3, "ji xu"); // 继续
esp_mn_commands_add(4, "shang yi shou"); // 上一首
esp_mn_commands_add(5, "xia yi shou"); // 下一首
esp_mn_commands_add(6, "sheng yin da yi dian"); // 声音大一点
esp_mn_commands_add(7, "sheng yin xiao yi dian"); // 声音小一点
esp_mn_commands_update(); // 更新命令词
int mu_chunksize = multinet->get_samp_chunksize(model_data); // 获取samp帧长度
assert(mu_chunksize == afe_chunksize);
// 打印所有的命令
multinet->print_active_speech_commands(model_data);
printf("------------detect start------------\n");
while (task_flag) {
afe_fetch_result_t* res = afe_handle->fetch(afe_data); // 获取模型输出结果
if (!res || res->ret_value == ESP_FAIL) {
printf("fetch error!\n");
break;
}
if (res->wakeup_state == WAKENET_DETECTED) {
printf("WAKEWORD DETECTED\n");
multinet->clean(model_data); // clean all status of multinet
} else if (res->wakeup_state == WAKENET_CHANNEL_VERIFIED) { // 检测到唤醒词
// play_voice = -1;
afe_handle->disable_wakenet(afe_data); // 关闭唤醒词识别
detect_flag = 1; // 标记已检测到唤醒词
ai_gui_in(); // AI人出现
printf("AFE_FETCH_CHANNEL_VERIFIED, channel index: %d\n", res->trigger_channel_id);
}
if (detect_flag == 1) {
esp_mn_state_t mn_state = multinet->detect(model_data, res->data); // 检测命令词
if (mn_state == ESP_MN_STATE_DETECTING) {
continue;
}
if (mn_state == ESP_MN_STATE_DETECTED) { // 已检测到命令词
esp_mn_results_t *mn_result = multinet->get_results(model_data); // 获取检测词结果
for (int i = 0; i < mn_result->num; i++) { // 打印获取到的命令词
printf("TOP %d, command_id: %d, phrase_id: %d, string:%s prob: %f\n",
i+1, mn_result->command_id[i], mn_result->phrase_id[i], mn_result->string, mn_result->prob[i]);
}
// 根据命令词 执行相应动作
switch (mn_result->command_id[0])
{
case 1: // bo fang yin yue 播放音乐
ai_play();
break;
case 2: // zan ting 暂停
ai_pause();
break;
case 3: // ji xu 继续
ai_resume();
break;
case 4: // shang yi shou 上一首
ai_prev_music();
break;
case 5: // xia yi shou 下一首
ai_next_music();
break;
case 6: // sheng yin da yi dian 声音大一点
ai_volume_up();
break;
case 7: // sheng yin xiao yi dian 声音小一点
ai_volume_down();
break;
default:
break;
}
printf("\n-----------listening-----------\n");
}
if (mn_state == ESP_MN_STATE_TIMEOUT) { // 达到最大检测命令词时间
esp_mn_results_t *mn_result = multinet->get_results(model_data);
printf("timeout, string:%s\n", mn_result->string);
afe_handle->enable_wakenet(afe_data); // 重新打开唤醒词识别
detect_flag = 0; // 清除标记
printf("\n-----------awaits to be waken up-----------\n");
ai_gui_out(); // AI人退出
continue;
}
}
}
if (model_data) {
multinet->destroy(model_data);
model_data = NULL;
}
printf("detect exit\n");
vTaskDelete(NULL);
}
void app_sr_init(void)
{
models = esp_srmodel_init("model"); // 获取模型 名称“model”和分区表中装载模型的名称一致
afe_handle = (esp_afe_sr_iface_t *)&ESP_AFE_SR_HANDLE; // 先配置afe句柄 随后才可以调用afe接口
afe_config_t afe_config = AFE_CONFIG_DEFAULT(); // 配置afe
afe_config.wakenet_model_name = esp_srmodel_filter(models, ESP_WN_PREFIX, NULL); // 配置唤醒模型 必须在create_from_config之前配置
afe_data = afe_handle->create_from_config(&afe_config); // 创建afe_data
ESP_LOGI(TAG, "wakenet:%s", afe_config.wakenet_model_name); // 打印唤醒名称
task_flag = 1;
xTaskCreatePinnedToCore(&detect_Task, "detect", 8 * 1024, (void*)afe_data, 5, NULL, 1);
xTaskCreatePinnedToCore(&feed_Task, "feed", 8 * 1024, (void*)afe_data, 5, NULL, 0);
}

10
main/app_sr.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <stdbool.h>
void app_sr_init(void);

527
main/app_ui.c Normal file
View File

@ -0,0 +1,527 @@
#include "app_ui.h"
#include "audio_player.h"
#include "esp32_s3_szp.h"
#include "file_iterator.h"
#include "string.h"
#include <dirent.h>
static const char *TAG = "app_ui";
static audio_player_config_t player_config = {0};
static uint8_t g_sys_volume = VOLUME_DEFAULT;
static file_iterator_instance_t *file_iterator = NULL;
lv_obj_t *music_list;
lv_obj_t *label_play_pause;
lv_obj_t *btn_play_pause;
lv_obj_t *volume_slider;
// 播放指定序号的音乐
static void play_index(int index)
{
ESP_LOGI(TAG, "play_index(%d)", index);
char filename[128];
int retval = file_iterator_get_full_path_from_index(file_iterator, index, filename, sizeof(filename));
if (retval == 0) {
ESP_LOGE(TAG, "unable to retrieve filename");
return;
}
FILE *fp = fopen(filename, "rb");
if (fp) {
ESP_LOGI(TAG, "Playing '%s'", filename);
audio_player_play(fp);
} else {
ESP_LOGE(TAG, "unable to open index %d, filename '%s'", index, filename);
}
}
// 设置声音处理函数
static esp_err_t _audio_player_mute_fn(AUDIO_PLAYER_MUTE_SETTING setting)
{
esp_err_t ret = ESP_OK;
// 获取当前音量
// uint8_t volume = get_sys_volume();
// 判断是否需要静音
bsp_codec_mute_set(setting == AUDIO_PLAYER_MUTE ? true : false);
// 如果不是静音 设置音量
if (setting == AUDIO_PLAYER_UNMUTE) {
bsp_codec_volume_set(g_sys_volume, NULL);
}
ret = ESP_OK;
return ret;
}
// 播放音乐函数 播放音乐的时候 会不断进入
static esp_err_t _audio_player_write_fn(void *audio_buffer, size_t len, size_t *bytes_written, uint32_t timeout_ms)
{
esp_err_t ret = ESP_OK;
ret = bsp_i2s_write(audio_buffer, len, bytes_written, timeout_ms);
return ret;
}
// 设置采样率 播放的时候进入一次
static esp_err_t _audio_player_std_clock(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch)
{
esp_err_t ret = ESP_OK;
// ret = bsp_speaker_set_fs(rate, bits_cfg, ch);
// ret = bsp_codec_set_fs(rate, bits_cfg, ch);
return ret;
}
// 回调函数 播放器每次动作都会进入
static void _audio_player_callback(audio_player_cb_ctx_t *ctx)
{
ESP_LOGI(TAG, "ctx->audio_event = %d", ctx->audio_event);
switch (ctx->audio_event) {
case AUDIO_PLAYER_CALLBACK_EVENT_IDLE: { // 播放完一首歌 进入这个case
ESP_LOGI(TAG, "AUDIO_PLAYER_REQUEST_IDLE");
// 指向下一首歌
file_iterator_next(file_iterator);
int index = file_iterator_get_index(file_iterator);
ESP_LOGI(TAG, "playing index '%d'", index);
play_index(index);
// 修改当前播放的音乐名称
lvgl_port_lock(0);
lv_dropdown_set_selected(music_list, index);
lv_obj_t *label_title = (lv_obj_t *) music_list->user_data;
lv_label_set_text_static(label_title, file_iterator_get_name_from_index(file_iterator, index));
lvgl_port_unlock();
break;
}
case AUDIO_PLAYER_CALLBACK_EVENT_PLAYING: // 正在播放音乐
ESP_LOGI(TAG, "AUDIO_PLAYER_REQUEST_PLAY");
pa_en(1); // 打开音频功放
break;
case AUDIO_PLAYER_CALLBACK_EVENT_PAUSE: // 正在暂停音乐
ESP_LOGI(TAG, "AUDIO_PLAYER_REQUEST_PAUSE");
pa_en(0); // 关闭音频功放
break;
default:
break;
}
}
// mp3播放器初始化
void mp3_player_init(void)
{
// 获取文件信息
file_iterator = file_iterator_new(SPIFFS_BASE);
assert(file_iterator != NULL);
// 初始化音频播放
player_config.mute_fn = _audio_player_mute_fn;
player_config.write_fn = _audio_player_write_fn;
player_config.clk_set_fn = _audio_player_std_clock;
player_config.priority = 6;
player_config.coreID = 1;
ESP_ERROR_CHECK(audio_player_new(player_config));
ESP_ERROR_CHECK(audio_player_callback_register(_audio_player_callback, NULL));
// 显示界面
music_ui();
}
// 按钮样式相关定义
typedef struct {
lv_style_t style_bg;
lv_style_t style_focus_no_outline;
} button_style_t;
static button_style_t g_btn_styles;
button_style_t *ui_button_styles(void)
{
return &g_btn_styles;
}
// 按钮样式初始化
static void ui_button_style_init(void)
{
/*Init the style for the default state*/
lv_style_init(&g_btn_styles.style_focus_no_outline);
lv_style_set_outline_width(&g_btn_styles.style_focus_no_outline, 0);
lv_style_init(&g_btn_styles.style_bg);
lv_style_set_bg_opa(&g_btn_styles.style_bg, LV_OPA_100);
lv_style_set_bg_color(&g_btn_styles.style_bg, lv_color_make(255, 255, 255));
lv_style_set_shadow_width(&g_btn_styles.style_bg, 0);
}
// 播放暂停按钮 事件处理函数
static void btn_play_pause_cb(lv_event_t *event)
{
lv_obj_t *btn = lv_event_get_target(event);
lv_obj_t *lab = (lv_obj_t *) btn->user_data;
audio_player_state_t state = audio_player_get_state();
printf("state=%d\n", state);
if(state == AUDIO_PLAYER_STATE_IDLE){
lvgl_port_lock(0);
lv_label_set_text_static(lab, LV_SYMBOL_PAUSE);
lvgl_port_unlock();
int index = file_iterator_get_index(file_iterator);
ESP_LOGI(TAG, "playing index '%d'", index);
play_index(index);
}else if (state == AUDIO_PLAYER_STATE_PAUSE) {
lvgl_port_lock(0);
lv_label_set_text_static(lab, LV_SYMBOL_PAUSE);
lvgl_port_unlock();
audio_player_resume();
} else if (state == AUDIO_PLAYER_STATE_PLAYING) {
lvgl_port_lock(0);
lv_label_set_text_static(lab, LV_SYMBOL_PLAY);
lvgl_port_unlock();
audio_player_pause();
}
}
// 上一首 下一首 按键事件处理函数
static void btn_prev_next_cb(lv_event_t *event)
{
bool is_next = (bool) event->user_data;
if (is_next) {
ESP_LOGI(TAG, "btn next");
file_iterator_next(file_iterator);
} else {
ESP_LOGI(TAG, "btn prev");
file_iterator_prev(file_iterator);
}
// 修改当前的音乐名称
int index = file_iterator_get_index(file_iterator);
lvgl_port_lock(0);
lv_dropdown_set_selected(music_list, index);
lv_obj_t *label_title = (lv_obj_t *) music_list->user_data;
lv_label_set_text_static(label_title, file_iterator_get_name_from_index(file_iterator, index));
lvgl_port_unlock();
// 执行音乐事件
audio_player_state_t state = audio_player_get_state();
printf("prev_next_state=%d\n", state);
if (state == AUDIO_PLAYER_STATE_IDLE) {
// Nothing to do
}else if (state == AUDIO_PLAYER_STATE_PAUSE){ // 如果当前正在暂停歌曲
ESP_LOGI(TAG, "playing index '%d'", index);
play_index(index);
audio_player_pause();
} else if (state == AUDIO_PLAYER_STATE_PLAYING) { // 如果当前正在播放歌曲
// 播放歌曲
ESP_LOGI(TAG, "playing index '%d'", index);
play_index(index);
}
}
// 音量调节滑动条 事件处理函数
static void volume_slider_cb(lv_event_t *event)
{
lv_obj_t *slider = lv_event_get_target(event);
int volume = lv_slider_get_value(slider); // 获取slider的值
bsp_codec_volume_set(volume, NULL); // 设置声音大小
g_sys_volume = volume; // 把声音赋值给g_sys_volume保存
ESP_LOGI(TAG, "volume '%d'", volume);
}
// 音乐列表 点击事件处理函数
static void music_list_cb(lv_event_t *event)
{
lv_obj_t *label_title = (lv_obj_t *) music_list->user_data;
uint16_t index = lv_dropdown_get_selected(music_list);
ESP_LOGI(TAG, "switching index to '%d'", index);
file_iterator_set_index(file_iterator, index);
lvgl_port_lock(0);
lv_label_set_text_static(label_title, file_iterator_get_name_from_index(file_iterator, index));
lvgl_port_unlock();
audio_player_state_t state = audio_player_get_state();
if (state == AUDIO_PLAYER_STATE_PAUSE){ // 如果当前正在暂停歌曲
play_index(index);
audio_player_pause();
} else if (state == AUDIO_PLAYER_STATE_PLAYING) { // 如果当前正在播放歌曲
play_index(index);
}
}
// 音乐名称加入列表
static void build_file_list(lv_obj_t *music_list)
{
lv_obj_t *label_title = (lv_obj_t *) music_list->user_data;
lvgl_port_lock(0);
lv_dropdown_clear_options(music_list);
lvgl_port_unlock();
for(size_t i = 0; i<file_iterator->count; i++)
{
const char *file_name = file_iterator_get_name_from_index(file_iterator, i);
if (NULL != file_name) {
lvgl_port_lock(0);
lv_dropdown_add_option(music_list, file_name, i); // 添加音乐名称到列表中
lvgl_port_unlock();
}
}
lvgl_port_lock(0);
lv_dropdown_set_selected(music_list, 0); // 选择列表中的第一个
lv_label_set_text_static(label_title, file_iterator_get_name_from_index(file_iterator, 0)); // 显示list中第一个音乐的名称
lvgl_port_unlock();
}
// 播放器界面初始化
void music_ui(void)
{
lvgl_port_lock(0);
ui_button_style_init();// 初始化按键风格
/* 创建播放暂停控制按键 */
btn_play_pause = lv_btn_create(lv_scr_act());
lv_obj_align(btn_play_pause, LV_ALIGN_CENTER, 0, 40);
lv_obj_set_size(btn_play_pause, 50, 50);
lv_obj_set_style_radius(btn_play_pause, 25, LV_STATE_DEFAULT);
lv_obj_add_flag(btn_play_pause, LV_OBJ_FLAG_CHECKABLE);
lv_obj_add_style(btn_play_pause, &ui_button_styles()->style_focus_no_outline, LV_STATE_FOCUS_KEY);
lv_obj_add_style(btn_play_pause, &ui_button_styles()->style_focus_no_outline, LV_STATE_FOCUSED);
label_play_pause = lv_label_create(btn_play_pause);
lv_label_set_text_static(label_play_pause, LV_SYMBOL_PLAY);
lv_obj_center(label_play_pause);
lv_obj_set_user_data(btn_play_pause, (void *) label_play_pause);
lv_obj_add_event_cb(btn_play_pause, btn_play_pause_cb, LV_EVENT_VALUE_CHANGED, NULL);
/* 创建上一首控制按键 */
lv_obj_t *btn_play_prev = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn_play_prev, 50, 50);
lv_obj_set_style_radius(btn_play_prev, 25, LV_STATE_DEFAULT);
lv_obj_clear_flag(btn_play_prev, LV_OBJ_FLAG_CHECKABLE);
lv_obj_align_to(btn_play_prev, btn_play_pause, LV_ALIGN_OUT_LEFT_MID, -40, 0);
lv_obj_add_style(btn_play_prev, &ui_button_styles()->style_focus_no_outline, LV_STATE_FOCUS_KEY);
lv_obj_add_style(btn_play_prev, &ui_button_styles()->style_focus_no_outline, LV_STATE_FOCUSED);
lv_obj_add_style(btn_play_prev, &ui_button_styles()->style_bg, LV_STATE_FOCUS_KEY);
lv_obj_add_style(btn_play_prev, &ui_button_styles()->style_bg, LV_STATE_FOCUSED);
lv_obj_add_style(btn_play_prev, &ui_button_styles()->style_bg, LV_STATE_DEFAULT);
lv_obj_t *label_prev = lv_label_create(btn_play_prev);
lv_label_set_text_static(label_prev, LV_SYMBOL_PREV);
lv_obj_set_style_text_font(label_prev, &lv_font_montserrat_24, LV_STATE_DEFAULT);
lv_obj_set_style_text_color(label_prev, lv_color_make(0, 0, 0), LV_STATE_DEFAULT);
lv_obj_center(label_prev);
lv_obj_set_user_data(btn_play_prev, (void *) label_prev);
lv_obj_add_event_cb(btn_play_prev, btn_prev_next_cb, LV_EVENT_CLICKED, (void *) false);
/* 创建下一首控制按键 */
lv_obj_t *btn_play_next = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn_play_next, 50, 50);
lv_obj_set_style_radius(btn_play_next, 25, LV_STATE_DEFAULT);
lv_obj_clear_flag(btn_play_next, LV_OBJ_FLAG_CHECKABLE);
lv_obj_align_to(btn_play_next, btn_play_pause, LV_ALIGN_OUT_RIGHT_MID, 40, 0);
lv_obj_add_style(btn_play_next, &ui_button_styles()->style_focus_no_outline, LV_STATE_FOCUS_KEY);
lv_obj_add_style(btn_play_next, &ui_button_styles()->style_focus_no_outline, LV_STATE_FOCUSED);
lv_obj_add_style(btn_play_next, &ui_button_styles()->style_bg, LV_STATE_FOCUS_KEY);
lv_obj_add_style(btn_play_next, &ui_button_styles()->style_bg, LV_STATE_FOCUSED);
lv_obj_add_style(btn_play_next, &ui_button_styles()->style_bg, LV_STATE_DEFAULT);
lv_obj_t *label_next = lv_label_create(btn_play_next);
lv_label_set_text_static(label_next, LV_SYMBOL_NEXT);
lv_obj_set_style_text_font(label_next, &lv_font_montserrat_24, LV_STATE_DEFAULT);
lv_obj_set_style_text_color(label_next, lv_color_make(0, 0, 0), LV_STATE_DEFAULT);
lv_obj_center(label_next);
lv_obj_set_user_data(btn_play_next, (void *) label_next);
lv_obj_add_event_cb(btn_play_next, btn_prev_next_cb, LV_EVENT_CLICKED, (void *) true);
/* 创建声音调节滑动条 */
volume_slider = lv_slider_create(lv_scr_act());
lv_obj_set_size(volume_slider, 200, 10);
lv_obj_set_ext_click_area(volume_slider, 15);
lv_obj_align(volume_slider, LV_ALIGN_BOTTOM_MID, 0, -20);
lv_slider_set_range(volume_slider, 0, 100);
lv_slider_set_value(volume_slider, g_sys_volume, LV_ANIM_ON);
lv_obj_add_event_cb(volume_slider, volume_slider_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_t *lab_vol_min = lv_label_create(lv_scr_act());
lv_label_set_text_static(lab_vol_min, LV_SYMBOL_VOLUME_MID);
lv_obj_set_style_text_font(lab_vol_min, &lv_font_montserrat_20, LV_STATE_DEFAULT);
lv_obj_align_to(lab_vol_min, volume_slider, LV_ALIGN_OUT_LEFT_MID, -10, 0);
lv_obj_t *lab_vol_max = lv_label_create(lv_scr_act());
lv_label_set_text_static(lab_vol_max, LV_SYMBOL_VOLUME_MAX);
lv_obj_set_style_text_font(lab_vol_max, &lv_font_montserrat_20, LV_STATE_DEFAULT);
lv_obj_align_to(lab_vol_max, volume_slider, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
/* 创建音乐标题 */
lv_obj_t *lab_title = lv_label_create(lv_scr_act());
lv_obj_set_user_data(lab_title, (void *) btn_play_pause);
lv_label_set_text_static(lab_title, "Scanning Files...");
lv_obj_set_style_text_font(lab_title, &lv_font_montserrat_32, LV_STATE_DEFAULT);
lv_obj_align(lab_title, LV_ALIGN_TOP_MID, 0, 20);
/* 创建音乐列表 */
music_list = lv_dropdown_create(lv_scr_act());
lv_dropdown_clear_options(music_list);
lv_dropdown_set_options_static(music_list, "Scanning...");
lv_obj_set_style_text_font(music_list, &lv_font_montserrat_20, LV_STATE_ANY);
lv_obj_set_width(music_list, 200);
lv_obj_align_to(music_list, lab_title, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
lv_obj_set_user_data(music_list, (void *) lab_title);
lv_obj_add_event_cb(music_list, music_list_cb, LV_EVENT_VALUE_CHANGED, NULL);
build_file_list(music_list);
lvgl_port_unlock();
}
// AI人动画
LV_IMG_DECLARE(img_bilibili120);
lv_obj_t *gif_start;
// AI人出现在屏幕
void ai_gui_in(void)
{
lvgl_port_lock(0);
gif_start = lv_gif_create(lv_scr_act());
lv_gif_set_src(gif_start, &img_bilibili120);
lv_obj_align(gif_start, LV_ALIGN_CENTER, 0, 0);
lvgl_port_unlock();
}
// AI人退出屏幕
void ai_gui_out(void)
{
lvgl_port_lock(0);
lv_obj_del(gif_start);
lvgl_port_unlock();
}
// AI播放音乐
void ai_play(void)
{
int index = file_iterator_get_index(file_iterator);
ESP_LOGI(TAG, "playing index '%d'", index);
play_index(index);
lvgl_port_lock(0);
lv_label_set_text_static(label_play_pause, LV_SYMBOL_PAUSE); // 显示图标
lv_obj_add_state(btn_play_pause, LV_STATE_CHECKED); // 按键设置为选中状态
lvgl_port_unlock();
}
// AI暂停
void ai_pause(void)
{
audio_player_pause();
lvgl_port_lock(0);
lv_label_set_text_static(label_play_pause, LV_SYMBOL_PLAY); // 显示图标
lv_obj_clear_state(btn_play_pause, LV_STATE_CHECKED); // 清除按键的选中状态
lvgl_port_unlock();
}
// AI继续
void ai_resume(void)
{
audio_player_resume();
lvgl_port_lock(0);
lv_label_set_text_static(label_play_pause, LV_SYMBOL_PAUSE); // 显示图标
lv_obj_add_state(btn_play_pause, LV_STATE_CHECKED); // 按键设置为选中状态
lvgl_port_unlock();
}
// AI上一首
void ai_prev_music(void)
{
// 指向上一首音乐
file_iterator_prev(file_iterator);
// 修改当前的音乐名称
int index = file_iterator_get_index(file_iterator);
lvgl_port_lock(0);
lv_dropdown_set_selected(music_list, index);
lv_obj_t *label_title = (lv_obj_t *) music_list->user_data;
lv_label_set_text_static(label_title, file_iterator_get_name_from_index(file_iterator, index));
lvgl_port_unlock();
// 执行音乐事件
audio_player_state_t state = audio_player_get_state();
printf("prev_next_state=%d\n", state);
if (state == AUDIO_PLAYER_STATE_IDLE) {
// Nothing to do
}else if (state == AUDIO_PLAYER_STATE_PAUSE){ // 如果当前正在暂停歌曲
ESP_LOGI(TAG, "playing index '%d'", index);
play_index(index);
audio_player_pause();
} else if (state == AUDIO_PLAYER_STATE_PLAYING) { // 如果当前正在播放歌曲
// 播放歌曲
ESP_LOGI(TAG, "playing index '%d'", index);
play_index(index);
}
}
// AI下一首
void ai_next_music(void)
{
// 指向上一首音乐
file_iterator_next(file_iterator);
// 修改当前的音乐名称
int index = file_iterator_get_index(file_iterator);
lvgl_port_lock(0);
lv_dropdown_set_selected(music_list, index);
lv_obj_t *label_title = (lv_obj_t *) music_list->user_data;
lv_label_set_text_static(label_title, file_iterator_get_name_from_index(file_iterator, index));
lvgl_port_unlock();
// 执行音乐事件
audio_player_state_t state = audio_player_get_state();
printf("prev_next_state=%d\n", state);
if (state == AUDIO_PLAYER_STATE_IDLE) {
// Nothing to do
}else if (state == AUDIO_PLAYER_STATE_PAUSE){ // 如果当前正在暂停歌曲
ESP_LOGI(TAG, "playing index '%d'", index);
play_index(index);
audio_player_pause();
} else if (state == AUDIO_PLAYER_STATE_PLAYING) { // 如果当前正在播放歌曲
// 播放歌曲
ESP_LOGI(TAG, "playing index '%d'", index);
play_index(index);
}
}
// AI声音大一点
void ai_volume_up(void)
{
if (g_sys_volume < 100){
g_sys_volume = g_sys_volume + 5;
if (g_sys_volume > 100){
g_sys_volume = 100;
}
bsp_codec_volume_set(g_sys_volume, NULL); // 设置声音大小
lvgl_port_lock(0);
lv_slider_set_value(volume_slider, g_sys_volume, LV_ANIM_ON); // 设置slider
lvgl_port_unlock();
}
ESP_LOGI(TAG, "volume '%d'", g_sys_volume);
}
// AI声音小一点
void ai_volume_down(void)
{
if (g_sys_volume > 0){
if (g_sys_volume < 5){
g_sys_volume = 0;
}else{
g_sys_volume = g_sys_volume - 5;
}
bsp_codec_volume_set(g_sys_volume, NULL); // 设置声音大小
lvgl_port_lock(0);
lv_slider_set_value(volume_slider, g_sys_volume, LV_ANIM_ON); // 设置slider
lvgl_port_unlock();
}
ESP_LOGI(TAG, "volume '%d'", g_sys_volume);
}

19
main/app_ui.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
void mp3_player_init(void);
void music_ui(void);
void ai_gui_in(void);
void ai_gui_out(void);
void ai_play(void);
void ai_pause(void);
void ai_resume(void);
void ai_prev_music(void);
void ai_next_music(void);
void ai_volume_up(void);
void ai_volume_down(void);

809
main/esp32_s3_szp.c Normal file
View File

@ -0,0 +1,809 @@
#include <stdio.h>
#include "esp32_s3_szp.h"
static const char *TAG = "esp32_s3_szp";
/******************************************************************************/
/*************************** I2C ↓ *******************************************/
esp_err_t bsp_i2c_init(void)
{
i2c_config_t i2c_conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = BSP_I2C_SDA,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = BSP_I2C_SCL,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = BSP_I2C_FREQ_HZ
};
i2c_param_config(BSP_I2C_NUM, &i2c_conf);
return i2c_driver_install(BSP_I2C_NUM, i2c_conf.mode, 0, 0, 0);
}
/*************************** I2C ↑ *******************************************/
/*******************************************************************************/
/*******************************************************************************/
/*************************** 姿态传感器 QMI8658 ↓ ****************************/
// 读取QMI8658寄存器的值
esp_err_t qmi8658_register_read(uint8_t reg_addr, uint8_t *data, size_t len)
{
return i2c_master_write_read_device(BSP_I2C_NUM, QMI8658_SENSOR_ADDR, &reg_addr, 1, data, len, 1000 / portTICK_PERIOD_MS);
}
// 给QMI8658的寄存器写值
esp_err_t qmi8658_register_write_byte(uint8_t reg_addr, uint8_t data)
{
uint8_t write_buf[2] = {reg_addr, data};
return i2c_master_write_to_device(BSP_I2C_NUM, QMI8658_SENSOR_ADDR, write_buf, sizeof(write_buf), 1000 / portTICK_PERIOD_MS);
}
// 初始化qmi8658
void qmi8658_init(void)
{
uint8_t id = 0; // 芯片的ID号
qmi8658_register_read(QMI8658_WHO_AM_I, &id ,1); // 读芯片的ID号
while (id != 0x05) // 判断读到的ID号是否是0x05
{
vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时1秒
qmi8658_register_read(QMI8658_WHO_AM_I, &id ,1); // 读取ID号
}
ESP_LOGI(TAG, "QMI8658 OK!"); // 打印信息
qmi8658_register_write_byte(QMI8658_RESET, 0xb0); // 复位
vTaskDelay(10 / portTICK_PERIOD_MS); // 延时10ms
qmi8658_register_write_byte(QMI8658_CTRL1, 0x40); // CTRL1 设置地址自动增加
qmi8658_register_write_byte(QMI8658_CTRL7, 0x03); // CTRL7 允许加速度和陀螺仪
qmi8658_register_write_byte(QMI8658_CTRL2, 0x95); // CTRL2 设置ACC 4g 250Hz
qmi8658_register_write_byte(QMI8658_CTRL3, 0xd5); // CTRL3 设置GRY 512dps 250Hz
}
// 读取加速度和陀螺仪寄存器值
void qmi8658_Read_AccAndGry(t_sQMI8658 *p)
{
uint8_t status, data_ready=0;
int16_t buf[6];
qmi8658_register_read(QMI8658_STATUS0, &status, 1); // 读状态寄存器
if (status & 0x03) // 判断加速度和陀螺仪数据是否可读
data_ready = 1;
if (data_ready == 1){ // 如果数据可读
data_ready = 0;
qmi8658_register_read(QMI8658_AX_L, (uint8_t *)buf, 12); // 读加速度和陀螺仪值
p->acc_x = buf[0];
p->acc_y = buf[1];
p->acc_z = buf[2];
p->gyr_x = buf[3];
p->gyr_y = buf[4];
p->gyr_z = buf[5];
}
}
// 获取XYZ轴的倾角值
void qmi8658_fetch_angleFromAcc(t_sQMI8658 *p)
{
float temp;
qmi8658_Read_AccAndGry(p); // 读取加速度和陀螺仪的寄存器值
// 根据寄存器值 计算倾角值 并把弧度转换成角度
temp = (float)p->acc_x / sqrt( ((float)p->acc_y * (float)p->acc_y + (float)p->acc_z * (float)p->acc_z) );
p->AngleX = atan(temp)*57.29578f; // 180/π=57.29578
temp = (float)p->acc_y / sqrt( ((float)p->acc_x * (float)p->acc_x + (float)p->acc_z * (float)p->acc_z) );
p->AngleY = atan(temp)*57.29578f; // 180/π=57.29578
temp = sqrt( ((float)p->acc_x * (float)p->acc_x + (float)p->acc_y * (float)p->acc_y) ) / (float)p->acc_z;
p->AngleZ = atan(temp)*57.29578f; // 180/π=57.29578
}
/*************************** 姿态传感器 QMI8658 ↑ ****************************/
/*******************************************************************************/
/***********************************************************/
/*************** IO扩展芯片 ↓ *************************/
// 读取PCA9557寄存器的值
esp_err_t pca9557_register_read(uint8_t reg_addr, uint8_t *data, size_t len)
{
return i2c_master_write_read_device(BSP_I2C_NUM, PCA9557_SENSOR_ADDR, &reg_addr, 1, data, len, 1000 / portTICK_PERIOD_MS);
}
// 给PCA9557的寄存器写值
esp_err_t pca9557_register_write_byte(uint8_t reg_addr, uint8_t data)
{
uint8_t write_buf[2] = {reg_addr, data};
return i2c_master_write_to_device(BSP_I2C_NUM, PCA9557_SENSOR_ADDR, write_buf, sizeof(write_buf), 1000 / portTICK_PERIOD_MS);
}
// 初始化PCA9557 IO扩展芯片
void pca9557_init(void)
{
// 写入控制引脚默认值 DVP_PWDN=1 PA_EN = 0 LCD_CS = 1
pca9557_register_write_byte(PCA9557_OUTPUT_PORT, 0x05);
// 把PCA9557芯片的IO1 IO1 IO2设置为输出 其它引脚保持默认的输入
pca9557_register_write_byte(PCA9557_CONFIGURATION_PORT, 0xf8);
}
// 设置PCA9557芯片的某个IO引脚输出高低电平
esp_err_t pca9557_set_output_state(uint8_t gpio_bit, uint8_t level)
{
uint8_t data;
esp_err_t res = ESP_FAIL;
pca9557_register_read(PCA9557_OUTPUT_PORT, &data, 1);
res = pca9557_register_write_byte(PCA9557_OUTPUT_PORT, SET_BITS(data, gpio_bit, level));
return res;
}
// 控制 PCA9557_LCD_CS 引脚输出高低电平 参数0输出低电平 参数1输出高电平
void lcd_cs(uint8_t level)
{
pca9557_set_output_state(LCD_CS_GPIO, level);
}
// 控制 PCA9557_PA_EN 引脚输出高低电平 参数0输出低电平 参数1输出高电平
void pa_en(uint8_t level)
{
pca9557_set_output_state(PA_EN_GPIO, level);
}
// 控制 PCA9557_DVP_PWDN 引脚输出高低电平 参数0输出低电平 参数1输出高电平
void dvp_pwdn(uint8_t level)
{
pca9557_set_output_state(DVP_PWDN_GPIO, level);
}
/*************** IO扩展芯片 ↑ *************************/
/***********************************************************/
/***********************************************************/
/**************** LCD显示屏 ↓ *************************/
// 背光PWM初始化
esp_err_t bsp_display_brightness_init(void)
{
// Setup LEDC peripheral for PWM backlight control
const ledc_channel_config_t LCD_backlight_channel = {
.gpio_num = BSP_LCD_BACKLIGHT,
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LCD_LEDC_CH,
.intr_type = LEDC_INTR_DISABLE,
.timer_sel = 0,
.duty = 0,
.hpoint = 0,
.flags.output_invert = true
};
const ledc_timer_config_t LCD_backlight_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.duty_resolution = LEDC_TIMER_10_BIT,
.timer_num = 0,
.freq_hz = 5000,
.clk_cfg = LEDC_AUTO_CLK
};
ESP_ERROR_CHECK(ledc_timer_config(&LCD_backlight_timer));
ESP_ERROR_CHECK(ledc_channel_config(&LCD_backlight_channel));
return ESP_OK;
}
// 背光亮度设置
esp_err_t bsp_display_brightness_set(int brightness_percent)
{
if (brightness_percent > 100) {
brightness_percent = 100;
} else if (brightness_percent < 0) {
brightness_percent = 0;
}
ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness_percent);
// LEDC resolution set to 10bits, thus: 100% = 1023
uint32_t duty_cycle = (1023 * brightness_percent) / 100;
ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH));
return ESP_OK;
}
// 关闭背光
esp_err_t bsp_display_backlight_off(void)
{
return bsp_display_brightness_set(0);
}
// 打开背光 最亮
esp_err_t bsp_display_backlight_on(void)
{
return bsp_display_brightness_set(100);
}
// 定义液晶屏句柄
static esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_io_handle_t io_handle = NULL;
static esp_lcd_touch_handle_t tp; // 触摸屏句柄
static lv_disp_t *disp; // 指向液晶屏
static lv_indev_t *disp_indev = NULL; // 指向触摸屏
// 液晶屏初始化
esp_err_t bsp_display_new(void)
{
esp_err_t ret = ESP_OK;
// 背光初始化
ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed");
// 初始化SPI总线
ESP_LOGD(TAG, "Initialize SPI bus");
const spi_bus_config_t buscfg = {
.sclk_io_num = BSP_LCD_SPI_CLK,
.mosi_io_num = BSP_LCD_SPI_MOSI,
.miso_io_num = GPIO_NUM_NC,
.quadwp_io_num = GPIO_NUM_NC,
.quadhd_io_num = GPIO_NUM_NC,
.max_transfer_sz = BSP_LCD_H_RES * BSP_LCD_V_RES * sizeof(uint16_t),
};
ESP_RETURN_ON_ERROR(spi_bus_initialize(BSP_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI init failed");
// 液晶屏控制IO初始化
ESP_LOGD(TAG, "Install panel IO");
const esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = BSP_LCD_DC,
.cs_gpio_num = BSP_LCD_SPI_CS,
.pclk_hz = BSP_LCD_PIXEL_CLOCK_HZ,
.lcd_cmd_bits = LCD_CMD_BITS,
.lcd_param_bits = LCD_PARAM_BITS,
.spi_mode = 2,
.trans_queue_depth = 10,
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)BSP_LCD_SPI_NUM, &io_config, &io_handle), err, TAG, "New panel IO failed");
// 初始化液晶屏驱动芯片ST7789
ESP_LOGD(TAG, "Install LCD driver");
const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = BSP_LCD_RST,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = BSP_LCD_BITS_PER_PIXEL,
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle), err, TAG, "New panel failed");
esp_lcd_panel_reset(panel_handle); // 液晶屏复位
lcd_cs(0); // 拉低CS引脚
esp_lcd_panel_init(panel_handle); // 初始化配置寄存器
esp_lcd_panel_invert_color(panel_handle, true); // 颜色反转
esp_lcd_panel_swap_xy(panel_handle, true); // 显示翻转
esp_lcd_panel_mirror(panel_handle, true, false); // 镜像
return ret;
err:
if (panel_handle) {
esp_lcd_panel_del(panel_handle);
}
if (io_handle) {
esp_lcd_panel_io_del(io_handle);
}
spi_bus_free(BSP_LCD_SPI_NUM);
return ret;
}
// LCD显示初始化
esp_err_t bsp_lcd_init(void)
{
esp_err_t ret = ESP_OK;
ret = bsp_display_new(); // 液晶屏驱动初始化
lcd_set_color(0x0000); // 设置整屏背景黑色
ret = esp_lcd_panel_disp_on_off(panel_handle, true); // 打开液晶屏显示
ret = bsp_display_backlight_on(); // 打开背光显示
return ret;
}
// 液晶屏初始化+添加LVGL接口
static lv_disp_t *bsp_display_lcd_init(void)
{
/* 初始化液晶屏 */
bsp_display_new(); // 液晶屏驱动初始化
lcd_set_color(0xffff); // 设置整屏背景白色
esp_lcd_panel_disp_on_off(panel_handle, true); // 打开液晶屏显示
/* 液晶屏添加LVGL接口 */
ESP_LOGD(TAG, "Add LCD screen");
const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = io_handle,
.panel_handle = panel_handle,
.buffer_size = BSP_LCD_H_RES * BSP_LCD_DRAW_BUF_HEIGHT, // LVGL缓存大小
.double_buffer = false, // 是否开启双缓存
.hres = BSP_LCD_H_RES, // 液晶屏的宽
.vres = BSP_LCD_V_RES, // 液晶屏的高
.monochrome = false, // 是否单色显示器
/* Rotation的值必须和液晶屏初始化里面设置的 翻转 和 镜像 一样 */
.rotation = {
.swap_xy = true, // 是否翻转
.mirror_x = true, // x方向是否镜像
.mirror_y = false, // y方向是否镜像
},
.flags = {
.buff_dma = false, // 是否使用DMA 注意dma与spiram不能同时为true
.buff_spiram = true, // 是否使用PSRAM 注意dma与spiram不能同时为true
}
};
return lvgl_port_add_disp(&disp_cfg);
}
// 触摸屏初始化
esp_err_t bsp_touch_new(esp_lcd_touch_handle_t *ret_touch)
{
/* Initialize touch */
esp_lcd_touch_config_t tp_cfg = {
.x_max = BSP_LCD_V_RES,
.y_max = BSP_LCD_H_RES,
.rst_gpio_num = GPIO_NUM_NC, // Shared with LCD reset
.int_gpio_num = GPIO_NUM_NC,
.levels = {
.reset = 0,
.interrupt = 0,
},
.flags = {
.swap_xy = 1,
.mirror_x = 1,
.mirror_y = 0,
},
};
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG();
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)BSP_I2C_NUM, &tp_io_config, &tp_io_handle), TAG, "");
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, ret_touch));
return ESP_OK;
}
// 触摸屏初始化+添加LVGL接口
static lv_indev_t *bsp_display_indev_init(lv_disp_t *disp)
{
/* 初始化触摸屏 */
ESP_ERROR_CHECK(bsp_touch_new(&tp));
assert(tp);
/* 添加LVGL接口 */
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = disp,
.handle = tp,
};
return lvgl_port_add_touch(&touch_cfg);
}
// 开发板显示初始化
void bsp_lvgl_start(void)
{
/* 初始化LVGL */
lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
lvgl_port_init(&lvgl_cfg);
/* 初始化液晶屏 并添加LVGL接口 */
disp = bsp_display_lcd_init();
/* 初始化触摸屏 并添加LVGL接口 */
disp_indev = bsp_display_indev_init(disp);
/* 打开液晶屏背光 */
bsp_display_backlight_on();
}
// 显示图片
void lcd_draw_pictrue(int x_start, int y_start, int x_end, int y_end, const unsigned char *gImage)
{
// 分配内存 分配了需要的字节大小 且指定在外部SPIRAM中分配
size_t pixels_byte_size = (x_end - x_start)*(y_end - y_start) * 2;
uint16_t *pixels = (uint16_t *)heap_caps_malloc(pixels_byte_size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
if (NULL == pixels)
{
ESP_LOGE(TAG, "Memory for bitmap is not enough");
return;
}
memcpy(pixels, gImage, pixels_byte_size); // 把图片数据拷贝到内存
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, (uint16_t *)pixels); // 显示整张图片数据
heap_caps_free(pixels); // 释放内存
}
// 设置液晶屏颜色
void lcd_set_color(uint16_t color)
{
// 分配内存 这里分配了液晶屏一行数据需要的大小
uint16_t *buffer = (uint16_t *)heap_caps_malloc(BSP_LCD_H_RES * sizeof(uint16_t), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
if (NULL == buffer)
{
ESP_LOGE(TAG, "Memory for bitmap is not enough");
}
else
{
for (size_t i = 0; i < BSP_LCD_H_RES; i++) // 给缓存中放入颜色数据
{
buffer[i] = color;
}
for (int y = 0; y < 240; y++) // 显示整屏颜色
{
esp_lcd_panel_draw_bitmap(panel_handle, 0, y, 320, y+1, buffer);
}
free(buffer); // 释放内存
}
}
/*************** LCD显示屏 ↑ *************************/
/***********************************************************/
/***********************************************************/
/**************** 摄像头 ↓ ****************************/
#if CAMERA_EN
// 定义lcd显示队列句柄
static QueueHandle_t xQueueLCDFrame = NULL;
// 摄像头硬件初始化
void bsp_camera_init(void)
{
dvp_pwdn(0); // 打开摄像头
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_1; // LEDC通道选择 用于生成XCLK时钟 但是S3不用
config.ledc_timer = LEDC_TIMER_1; // LEDC timer选择 用于生成XCLK时钟 但是S3不用
config.pin_d0 = CAMERA_PIN_D0;
config.pin_d1 = CAMERA_PIN_D1;
config.pin_d2 = CAMERA_PIN_D2;
config.pin_d3 = CAMERA_PIN_D3;
config.pin_d4 = CAMERA_PIN_D4;
config.pin_d5 = CAMERA_PIN_D5;
config.pin_d6 = CAMERA_PIN_D6;
config.pin_d7 = CAMERA_PIN_D7;
config.pin_xclk = CAMERA_PIN_XCLK;
config.pin_pclk = CAMERA_PIN_PCLK;
config.pin_vsync = CAMERA_PIN_VSYNC;
config.pin_href = CAMERA_PIN_HREF;
config.pin_sccb_sda = -1; // 这里写-1 表示使用已经初始化的I2C接口
config.pin_sccb_scl = CAMERA_PIN_SIOC;
config.sccb_i2c_port = 0;
config.pin_pwdn = CAMERA_PIN_PWDN;
config.pin_reset = CAMERA_PIN_RESET;
config.xclk_freq_hz = XCLK_FREQ_HZ;
config.pixel_format = PIXFORMAT_RGB565;
config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 12;
config.fb_count = 2;
config.fb_location = CAMERA_FB_IN_PSRAM;
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
// camera init
esp_err_t err = esp_camera_init(&config); // 配置上面定义的参数
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
return;
}
sensor_t *s = esp_camera_sensor_get(); // 获取摄像头型号
if (s->id.PID == GC0308_PID) {
s->set_hmirror(s, 1); // 这里控制摄像头镜像 写1镜像 写0不镜像
}
}
// lcd处理任务
static void task_process_lcd(void *arg)
{
camera_fb_t *frame = NULL;
while (true)
{
if (xQueueReceive(xQueueLCDFrame, &frame, portMAX_DELAY))
{
esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, frame->width, frame->height, (uint16_t *)frame->buf);
esp_camera_fb_return(frame);
}
}
}
// 摄像头处理任务
static void task_process_camera(void *arg)
{
while (true)
{
camera_fb_t *frame = esp_camera_fb_get();
if (frame)
xQueueSend(xQueueLCDFrame, &frame, portMAX_DELAY);
}
}
// 让摄像头显示到LCD
void app_camera_lcd(void)
{
xQueueLCDFrame = xQueueCreate(2, sizeof(camera_fb_t *));
xTaskCreatePinnedToCore(task_process_camera, "task_process_camera", 3 * 1024, NULL, 5, NULL, 1);
xTaskCreatePinnedToCore(task_process_lcd, "task_process_lcd", 4 * 1024, NULL, 5, NULL, 0);
}
#endif
/******************** 摄像头 ↑ *************************/
/***********************************************************/
/***********************************************************/
/*************** SPIFFS文件系统 ↓ *********************/
esp_err_t bsp_spiffs_mount(void)
{
esp_vfs_spiffs_conf_t conf = {
.base_path = SPIFFS_BASE,
.partition_label = "storage",
.max_files = 5,
.format_if_mount_failed = false,
};
esp_err_t ret_val = esp_vfs_spiffs_register(&conf);
ESP_ERROR_CHECK(ret_val);
size_t total = 0, used = 0;
ret_val = esp_spiffs_info(conf.partition_label, &total, &used);
if (ret_val != ESP_OK) {
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret_val));
} else {
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
}
return ret_val;
}
/*************** SPIFFS文件系统 ↑ *********************/
/**********************************************************/
/***********************************************************/
/********************* 音频 ↓ *************************/
static esp_codec_dev_handle_t play_dev_handle; // speaker句柄
static esp_codec_dev_handle_t record_dev_handle; // microphone句柄
static i2s_chan_handle_t i2s_tx_chan = NULL; // 发送通道
static i2s_chan_handle_t i2s_rx_chan = NULL; // 接收通道
static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface */
// I2S总线初始化
esp_err_t bsp_audio_init(void)
{
esp_err_t ret = ESP_FAIL;
if (i2s_tx_chan && i2s_rx_chan) {
/* Audio was initialized before */
return ESP_OK;
}
/* Setup I2S peripheral */
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(BSP_I2S_NUM, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &i2s_tx_chan, &i2s_rx_chan));
/* Setup I2S channels */
const i2s_std_config_t std_cfg_default = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000), // 采样率16000
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(32, I2S_SLOT_MODE_STEREO), // 32位 2通道
.gpio_cfg = {
.mclk = GPIO_I2S_MCLK,
.bclk = GPIO_I2S_SCLK,
.ws = GPIO_I2S_LRCK,
.dout = GPIO_I2S_DOUT,
.din = GPIO_I2S_SDIN,
},
};
if (i2s_tx_chan != NULL) {
ESP_GOTO_ON_ERROR(i2s_channel_init_std_mode(i2s_tx_chan, &std_cfg_default), err, TAG, "I2S channel initialization failed");
ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_tx_chan), err, TAG, "I2S enabling failed");
}
if (i2s_rx_chan != NULL) {
ESP_GOTO_ON_ERROR(i2s_channel_init_std_mode(i2s_rx_chan, &std_cfg_default), err, TAG, "I2S channel initialization failed");
ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_chan), err, TAG, "I2S enabling failed");
}
audio_codec_i2s_cfg_t i2s_cfg = {
.port = BSP_I2S_NUM,
.rx_handle = i2s_rx_chan,
.tx_handle = i2s_tx_chan,
};
i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg);
if (i2s_data_if == NULL) {
goto err;
}
return ESP_OK;
err:
if (i2s_tx_chan) {
i2s_del_channel(i2s_tx_chan);
}
if (i2s_rx_chan) {
i2s_del_channel(i2s_rx_chan);
}
return ret;
}
// 初始化音频输出芯片
esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void)
{
if (i2s_data_if == NULL) {
/* Configure I2S peripheral and Power Amplifier */
ESP_ERROR_CHECK(bsp_audio_init());
}
assert(i2s_data_if);
const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
audio_codec_i2c_cfg_t i2c_cfg = {
.port = BSP_I2C_NUM,
.addr = ES8311_CODEC_DEFAULT_ADDR,
};
const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
assert(i2c_ctrl_if);
esp_codec_dev_hw_gain_t gain = {
.pa_voltage = 5.0,
.codec_dac_voltage = 3.3,
};
es8311_codec_cfg_t es8311_cfg = {
.ctrl_if = i2c_ctrl_if,
.gpio_if = gpio_if,
.codec_mode = ESP_CODEC_DEV_WORK_MODE_DAC,
.pa_pin = GPIO_PWR_CTRL,
.pa_reverted = false,
.master_mode = false,
.use_mclk = true,
.digital_mic = false,
.invert_mclk = false,
.invert_sclk = false,
.hw_gain = gain,
};
const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg);
assert(es8311_dev);
esp_codec_dev_cfg_t codec_dev_cfg = {
.dev_type = ESP_CODEC_DEV_TYPE_OUT,
.codec_if = es8311_dev,
.data_if = i2s_data_if,
};
return esp_codec_dev_new(&codec_dev_cfg);
}
// 初始化音频输入芯片
esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void)
{
if (i2s_data_if == NULL) {
/* Configure I2S peripheral and Power Amplifier */
ESP_ERROR_CHECK(bsp_audio_init());
}
assert(i2s_data_if);
audio_codec_i2c_cfg_t i2c_cfg = {
.port = BSP_I2C_NUM,
.addr = 0x82,
};
const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
assert(i2c_ctrl_if);
es7210_codec_cfg_t es7210_cfg = {
.ctrl_if = i2c_ctrl_if,
.mic_selected = ES7120_SEL_MIC1 | ES7120_SEL_MIC2 | ES7120_SEL_MIC3 | ES7120_SEL_MIC4,
};
const audio_codec_if_t *es7210_dev = es7210_codec_new(&es7210_cfg);
assert(es7210_dev);
esp_codec_dev_cfg_t codec_es7210_dev_cfg = {
.dev_type = ESP_CODEC_DEV_TYPE_IN,
.codec_if = es7210_dev,
.data_if = i2s_data_if,
};
return esp_codec_dev_new(&codec_es7210_dev_cfg);
}
// 设置采样率
esp_err_t bsp_codec_set_fs(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch)
{
esp_err_t ret = ESP_OK;
esp_codec_dev_sample_info_t fs = {
.sample_rate = rate,
.channel = ch,
.bits_per_sample = bits_cfg,
};
if (play_dev_handle) {
ret = esp_codec_dev_close(play_dev_handle);
}
if (record_dev_handle) {
ret |= esp_codec_dev_close(record_dev_handle);
ret |= esp_codec_dev_set_in_gain(record_dev_handle, CODEC_DEFAULT_ADC_VOLUME);
}
if (play_dev_handle) {
ret |= esp_codec_dev_open(play_dev_handle, &fs);
}
if (record_dev_handle) {
ret |= esp_codec_dev_open(record_dev_handle, &fs);
}
return ret;
}
// 音频芯片初始化
esp_err_t bsp_codec_init(void)
{
play_dev_handle = bsp_audio_codec_speaker_init();
assert((play_dev_handle) && "play_dev_handle not initialized");
record_dev_handle = bsp_audio_codec_microphone_init();
assert((record_dev_handle) && "record_dev_handle not initialized");
bsp_codec_set_fs(CODEC_DEFAULT_SAMPLE_RATE, CODEC_DEFAULT_BIT_WIDTH, CODEC_DEFAULT_CHANNEL);
return ESP_OK;
}
// 播放音乐
esp_err_t bsp_i2s_write(void *audio_buffer, size_t len, size_t *bytes_written, uint32_t timeout_ms)
{
esp_err_t ret = ESP_OK;
ret = esp_codec_dev_write(play_dev_handle, audio_buffer, len);
*bytes_written = len;
return ret;
}
// 设置静音与否
esp_err_t bsp_codec_mute_set(bool enable)
{
esp_err_t ret = ESP_OK;
ret = esp_codec_dev_set_out_mute(play_dev_handle, enable);
return ret;
}
// 设置喇叭音量
esp_err_t bsp_codec_volume_set(int volume, int *volume_set)
{
esp_err_t ret = ESP_OK;
float v = volume;
ret = esp_codec_dev_set_out_vol(play_dev_handle, (int)v);
return ret;
}
int bsp_get_feed_channel(void)
{
return ADC_I2S_CHANNEL;
}
esp_err_t bsp_get_feed_data(bool is_get_raw_channel, int16_t *buffer, int buffer_len)
{
esp_err_t ret = ESP_OK;
int audio_chunksize = buffer_len / (sizeof(int16_t) * ADC_I2S_CHANNEL);
ret = esp_codec_dev_read(record_dev_handle, (void *)buffer, buffer_len);
if (!is_get_raw_channel) {
for (int i = 0; i < audio_chunksize; i++) {
int16_t ref = buffer[4 * i + 0];
buffer[3 * i + 0] = buffer[4 * i + 1];
buffer[3 * i + 1] = buffer[4 * i + 3];
buffer[3 * i + 2] = ref;
}
}
return ret;
}
/********************* 音频 ↑ *************************/
/***********************************************************/

289
main/esp32_s3_szp.h Normal file
View File

@ -0,0 +1,289 @@
#pragma once
#include <string.h>
#include "math.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "driver/i2c.h"
#include "driver/spi_master.h"
#include "driver/ledc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_lcd_types.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_touch_ft5x06.h"
#include "esp_lvgl_port.h"
#include "esp_codec_dev.h"
#include "esp_codec_dev_defaults.h"
#include "driver/spi_master.h"
#include "driver/i2s_std.h"
#include "esp_spiffs.h"
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "driver/sdmmc_host.h"
/******************************************************************************/
/*************************** I2C ↓ *******************************************/
#define BSP_I2C_SDA (GPIO_NUM_1) // SDA引脚
#define BSP_I2C_SCL (GPIO_NUM_2) // SCL引脚
#define BSP_I2C_NUM (0) // I2C外设
#define BSP_I2C_FREQ_HZ 100000 // 100kHz
esp_err_t bsp_i2c_init(void); // 初始化I2C接口
/*************************** I2C ↑ *******************************************/
/*******************************************************************************/
/*******************************************************************************/
/*************************** 姿态传感器 QMI8658 ↓ ****************************/
#define QMI8658_SENSOR_ADDR 0x6A // QMI8658 I2C地址
// QMI8658寄存器地址
enum qmi8658_reg
{
QMI8658_WHO_AM_I,
QMI8658_REVISION_ID,
QMI8658_CTRL1,
QMI8658_CTRL2,
QMI8658_CTRL3,
QMI8658_CTRL4,
QMI8658_CTRL5,
QMI8658_CTRL6,
QMI8658_CTRL7,
QMI8658_CTRL8,
QMI8658_CTRL9,
QMI8658_CATL1_L,
QMI8658_CATL1_H,
QMI8658_CATL2_L,
QMI8658_CATL2_H,
QMI8658_CATL3_L,
QMI8658_CATL3_H,
QMI8658_CATL4_L,
QMI8658_CATL4_H,
QMI8658_FIFO_WTM_TH,
QMI8658_FIFO_CTRL,
QMI8658_FIFO_SMPL_CNT,
QMI8658_FIFO_STATUS,
QMI8658_FIFO_DATA,
QMI8658_STATUSINT = 45,
QMI8658_STATUS0,
QMI8658_STATUS1,
QMI8658_TIMESTAMP_LOW,
QMI8658_TIMESTAMP_MID,
QMI8658_TIMESTAMP_HIGH,
QMI8658_TEMP_L,
QMI8658_TEMP_H,
QMI8658_AX_L,
QMI8658_AX_H,
QMI8658_AY_L,
QMI8658_AY_H,
QMI8658_AZ_L,
QMI8658_AZ_H,
QMI8658_GX_L,
QMI8658_GX_H,
QMI8658_GY_L,
QMI8658_GY_H,
QMI8658_GZ_L,
QMI8658_GZ_H,
QMI8658_COD_STATUS = 70,
QMI8658_dQW_L = 73,
QMI8658_dQW_H,
QMI8658_dQX_L,
QMI8658_dQX_H,
QMI8658_dQY_L,
QMI8658_dQY_H,
QMI8658_dQZ_L,
QMI8658_dQZ_H,
QMI8658_dVX_L,
QMI8658_dVX_H,
QMI8658_dVY_L,
QMI8658_dVY_H,
QMI8658_dVZ_L,
QMI8658_dVZ_H,
QMI8658_TAP_STATUS = 89,
QMI8658_STEP_CNT_LOW,
QMI8658_STEP_CNT_MIDL,
QMI8658_STEP_CNT_HIGH,
QMI8658_RESET = 96
};
// 倾角结构体
typedef struct{
int16_t acc_x;
int16_t acc_y;
int16_t acc_z;
int16_t gyr_x;
int16_t gyr_y;
int16_t gyr_z;
float AngleX;
float AngleY;
float AngleZ;
}t_sQMI8658;
void qmi8658_init(void); // QMI8658初始化
void qmi8658_fetch_angleFromAcc(t_sQMI8658 *p); // 获取倾角
/*************************** 姿态传感器 QMI8658 ↑ ****************************/
/*******************************************************************************/
/******************************************************************************/
/*************************** I2S ↓ **************************************/
/* Example configurations */
#define EXAMPLE_RECV_BUF_SIZE (2400)
#define EXAMPLE_SAMPLE_RATE (16000)
#define EXAMPLE_MCLK_MULTIPLE (384) // If not using 24-bit data width, 256 should be enough
#define EXAMPLE_MCLK_FREQ_HZ (EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE)
#define EXAMPLE_VOICE_VOLUME (70)
/* I2S port and GPIOs */
#define I2S_NUM (0)
#define I2S_MCK_IO (GPIO_NUM_38)
#define I2S_BCK_IO (GPIO_NUM_14)
#define I2S_WS_IO (GPIO_NUM_13)
#define I2S_DO_IO (GPIO_NUM_45)
#define I2S_DI_IO (-1)
/***********************************************************/
/*************** IO扩展芯片 ↓ *************************/
#define PCA9557_INPUT_PORT 0x00
#define PCA9557_OUTPUT_PORT 0x01
#define PCA9557_POLARITY_INVERSION_PORT 0x02
#define PCA9557_CONFIGURATION_PORT 0x03
#define LCD_CS_GPIO BIT(0) // PCA9557_GPIO_NUM_1
#define PA_EN_GPIO BIT(1) // PCA9557_GPIO_NUM_2
#define DVP_PWDN_GPIO BIT(2) // PCA9557_GPIO_NUM_3
#define PCA9557_SENSOR_ADDR 0x19 /*!< Slave address of the MPU9250 sensor */
#define SET_BITS(_m, _s, _v) ((_v) ? (_m)|((_s)) : (_m)&~((_s)))
void pca9557_init(void);
void lcd_cs(uint8_t level);
void pa_en(uint8_t level);
void dvp_pwdn(uint8_t level);
/*************** IO扩展芯片 ↑ *************************/
/***********************************************************/
/***********************************************************/
/**************** LCD显示屏 ↓ *************************/
#define BSP_LCD_PIXEL_CLOCK_HZ (80 * 1000 * 1000)
#define BSP_LCD_SPI_NUM (SPI3_HOST)
#define LCD_CMD_BITS (8)
#define LCD_PARAM_BITS (8)
#define BSP_LCD_BITS_PER_PIXEL (16)
#define LCD_LEDC_CH LEDC_CHANNEL_0
#define BSP_LCD_H_RES (320)
#define BSP_LCD_V_RES (240)
#define BSP_LCD_SPI_MOSI (GPIO_NUM_40)
#define BSP_LCD_SPI_CLK (GPIO_NUM_41)
#define BSP_LCD_SPI_CS (GPIO_NUM_NC)
#define BSP_LCD_DC (GPIO_NUM_39)
#define BSP_LCD_RST (GPIO_NUM_NC)
#define BSP_LCD_BACKLIGHT (GPIO_NUM_42)
#define BSP_LCD_DRAW_BUF_HEIGHT (20)
esp_err_t bsp_display_brightness_init(void);
esp_err_t bsp_display_brightness_set(int brightness_percent);
esp_err_t bsp_display_backlight_off(void);
esp_err_t bsp_display_backlight_on(void);
esp_err_t bsp_lcd_init(void);
void lcd_set_color(uint16_t color);
void lcd_draw_pictrue(int x_start, int y_start, int x_end, int y_end, const unsigned char *gImage);
void bsp_lvgl_start(void);
/*************** LCD显示屏 ↑ *************************/
/***********************************************************/
/***********************************************************/
/**************** 摄像头 ↓ ****************************/
#define CAMERA_EN 0
#if CAMERA
#include "esp_camera.h"
#define CAMERA_PIN_PWDN -1
#define CAMERA_PIN_RESET -1
#define CAMERA_PIN_XCLK 5
#define CAMERA_PIN_SIOD 1
#define CAMERA_PIN_SIOC 2
#define CAMERA_PIN_D7 9
#define CAMERA_PIN_D6 4
#define CAMERA_PIN_D5 6
#define CAMERA_PIN_D4 15
#define CAMERA_PIN_D3 17
#define CAMERA_PIN_D2 8
#define CAMERA_PIN_D1 18
#define CAMERA_PIN_D0 16
#define CAMERA_PIN_VSYNC 3
#define CAMERA_PIN_HREF 46
#define CAMERA_PIN_PCLK 7
#define XCLK_FREQ_HZ 24000000
void bsp_camera_init(void);
void app_camera_lcd(void);
#endif
/******************** 摄像头 ↑ *************************/
/***********************************************************/
/***********************************************************/
/*************** SPIFFS文件系统 ↓ *********************/
#define SPIFFS_BASE "/spiffs"
esp_err_t bsp_spiffs_mount(void);
/*************** SPIFFS文件系统 ↑ *********************/
/**********************************************************/
/***********************************************************/
/********************* 音频 ↓ *************************/
#define ADC_I2S_CHANNEL 4
#define VOLUME_DEFAULT 70
#define CODEC_DEFAULT_SAMPLE_RATE (16000)
#define CODEC_DEFAULT_BIT_WIDTH (32)
#define CODEC_DEFAULT_ADC_VOLUME (24.0)
#define CODEC_DEFAULT_CHANNEL (2)
#define BSP_I2S_NUM I2S_NUM_1
#define GPIO_I2S_LRCK (GPIO_NUM_13)
#define GPIO_I2S_MCLK (GPIO_NUM_38)
#define GPIO_I2S_SCLK (GPIO_NUM_14)
#define GPIO_I2S_SDIN (GPIO_NUM_12)
#define GPIO_I2S_DOUT (GPIO_NUM_45)
#define GPIO_PWR_CTRL (GPIO_NUM_NC)
esp_err_t bsp_codec_init(void);
esp_err_t bsp_i2s_write(void *audio_buffer, size_t len, size_t *bytes_written, uint32_t timeout_ms);
esp_err_t bsp_codec_set_fs(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch);
esp_err_t bsp_speaker_set_fs(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch);
esp_err_t bsp_codec_mute_set(bool enable);
esp_err_t bsp_codec_volume_set(int volume, int *volume_set);
int bsp_get_feed_channel(void);
esp_err_t bsp_get_feed_data(bool is_get_raw_channel, int16_t *buffer, int buffer_len);
/********************* 音频 ↑ *************************/
/***********************************************************/

11
main/idf_component.yml Normal file
View File

@ -0,0 +1,11 @@
## IDF Component Manager Manifest File
dependencies:
# espressif/esp32-camera: "^2.0.10" # 摄像头驱动
lvgl/lvgl: "~8.3.0"
espressif/esp_lvgl_port: "~1.4.0" # LVGL接口
espressif/esp_lcd_touch_ft5x06: "~1.0.6" # 触摸屏驱动
chmorgan/esp-audio-player: "~1.0.7" # 音频播放
chmorgan/esp-file-iterator: "1.0.0" # 获取文件
espressif/esp_codec_dev: "~1.3.0" # 音频驱动
espressif/esp-sr: "~1.6.0" # 语音识别

2910
main/img_bilibili120.c Normal file

File diff suppressed because it is too large Load Diff

18
main/main.c Normal file
View File

@ -0,0 +1,18 @@
#include <stdio.h>
#include "esp32_s3_szp.h"
#include "app_ui.h"
#include "app_sr.h"
void app_main(void)
{
bsp_i2c_init(); // I2C初始化
pca9557_init(); // IO扩展芯片初始化
bsp_lvgl_start(); // 初始化液晶屏lvgl接口
bsp_spiffs_mount(); // SPIFFS文件系统初始化
bsp_codec_init(); // 音频初始化
mp3_player_init(); // MP3播放器初始化
app_sr_init(); // 语音识别初始化
}

7
partitions.csv Normal file
View File

@ -0,0 +1,7 @@
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 24k
phy_init, data, phy, 0xf000, 4k
factory, app, factory, , 3M
storage, data, spiffs, , 3M
model, data, spiffs, , 5168K,
1 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
2 # Name, Type, SubType, Offset, Size, Flags
3 nvs, data, nvs, 0x9000, 24k
4 phy_init, data, phy, 0xf000, 4k
5 factory, app, factory, , 3M
6 storage, data, spiffs, , 3M
7 model, data, spiffs, , 5168K,

21
sdkconfig.defaults Normal file
View File

@ -0,0 +1,21 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
#
CONFIG_IDF_TARGET="esp32s3"
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y
CONFIG_ESP32S3_DATA_CACHE_64KB=y
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
CONFIG_SPIFFS_OBJ_NAME_LEN=128
CONFIG_LV_COLOR_16_SWAP=y
CONFIG_LV_MEM_CUSTOM=y
CONFIG_LV_FONT_MONTSERRAT_20=y
CONFIG_LV_FONT_MONTSERRAT_24=y
CONFIG_LV_FONT_MONTSERRAT_32=y
CONFIG_LV_USE_GIF=y
CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE=y