Temo of while.

This commit is contained in:
Colin 2025-12-30 23:53:20 +08:00
parent 4cd1a1cb1e
commit bda0fd6096
14 changed files with 727 additions and 2853 deletions

View File

@ -36,6 +36,10 @@
"stack": "c",
"esp_system.h": "c",
"esp_clk_tree.h": "c",
"esp_vfs_fat.h": "c"
"esp_vfs_fat.h": "c",
"gpio.h": "c",
"string.h": "c",
"i2s_reg.h": "c",
"complex": "c"
}
}

View File

@ -1,2 +1,2 @@
idf_component_register(SRCS "app_sr.c" "esp32_s3_szp.c" "main.c" "app_ui.c" "usb_msc.c"
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")

View File

@ -1,322 +0,0 @@
#include "app_sr.h"
#include <errno.h>
#include "app_ui.h"
#include "audio_player.h"
#include "esp32_s3_szp.h"
#include "esp_afe_sr_iface.h"
#include "esp_afe_sr_models.h"
#include "esp_log.h"
#include "esp_mn_iface.h"
#include "esp_mn_models.h"
#include "esp_mn_speech_commands.h"
#include "esp_process_sdkconfig.h"
#include "esp_wn_iface.h"
#include "esp_wn_models.h"
#include "format_wav.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "model_path.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;
FILE* f1;
FILE* f2;
size_t wav_written1;
size_t wav_written2;
esp_err_t record_start() {
esp_err_t ret = ESP_OK;
const wav_header_t wav_header1 =
WAV_HEADER_PCM_DEFAULT(16000 * 2 * 16 / 8 * 20, 16, 16000, 2);
const wav_header_t wav_header2 =
WAV_HEADER_PCM_DEFAULT(16000 * 1 * 16 / 8 * 20, 16, 16000, 1);
ESP_LOGI(TAG, "Opening file %s", "/sdcard/aaaaa.wav");
f1 = fopen("/sdcard/aaaaa.wav", "w");
ESP_LOGI(TAG, "fopen error: %s", strerror(errno));
ESP_RETURN_ON_FALSE(f1, ESP_FAIL, TAG, "error while opening wav file");
ESP_RETURN_ON_FALSE(fwrite(&wav_header1, sizeof(wav_header_t), 1, f1),
ESP_FAIL, TAG, "error while writing wav header");
f2 = fopen("/sdcard/bbbbb.wav", "w");
ESP_LOGI(TAG, "fopen error: %s", strerror(errno));
ESP_RETURN_ON_FALSE(f2, ESP_FAIL, TAG, "error while opening wav file");
ESP_RETURN_ON_FALSE(fwrite(&wav_header2, sizeof(wav_header_t), 1, f2),
ESP_FAIL, TAG, "error while writing wav header");
wav_written1 = 0;
wav_written2 = 0;
return ESP_OK;
}
esp_err_t record_add1(int16_t* data1, uint32_t size) {
uint32_t byte_rate = 16000 * 2 * 16 / 8;
uint32_t wav_size = byte_rate * 20;
if (wav_written1 < wav_size) {
ESP_RETURN_ON_FALSE(fwrite(data1, size, 1, f1), ESP_FAIL, TAG,
"error while writing samples to wav file");
wav_written1 += size;
return ESP_OK;
} else {
if (wav_written1 < 0xFFFFFFFF) {
ESP_LOGI(TAG, "Recording done 1! Flushing file buffer");
fclose(f1);
wav_written1 = 0xFFFFFFFF;
}
}
return ESP_OK;
}
esp_err_t record_add2(int16_t* data2, uint32_t size) {
uint32_t byte_rate = 16000 * 1 * 16 / 8;
uint32_t wav_size = byte_rate * 20;
if (wav_written2 < wav_size) {
ESP_RETURN_ON_FALSE(fwrite(data2, size, 1, f2), ESP_FAIL, TAG,
"error while writing samples to wav file");
wav_written2 += size;
return ESP_OK;
} else {
if (wav_written2 < 0xFFFFFFFF) {
ESP_LOGI(TAG, "Recording done 2! Flushing file buffer");
fclose(f2);
wav_written2 = 0xFFFFFFFF;
}
}
return ESP_OK;
}
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);
printf("AFE audio_chunksize : %d\n", audio_chunksize);
printf("AFE nch: %d\n", nch);
printf("AFE feed_channel: %d\n", feed_channel);
while (task_flag) {
bsp_get_feed_data(
false, i2s_buff,
audio_chunksize * sizeof(int16_t) * feed_channel); // 获取I2S数据
record_add1(i2s_buff, audio_chunksize * sizeof(int16_t) * 2);
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); // 获取模型输出结果
record_add2(res->data, res->data_size);
// int16_t *data; // the data of audio.
// int data_size; // the size of data. The unit is
// byte. float data_volume; // the volume of input
// audio, the unit is decibel(dB). This value is calculated before agc.
// (note: invalid in vc).
// // if enable wakenet, the window
// length is the receptive fields of
// wakenet(about 1.5s), otherwise is
// the frame length.
// wakenet_state_t wakeup_state; // the value is wakenet_state_t
// int wake_word_index; // if the wake word is detected.
// It will store the wake word index which start from 1. int
// wakenet_model_index; // if there are multiple wakenets,
// this value identifies which model be wakes up. Index start from 1.
// afe_vad_state_t vad_state; // the value is afe_vad_state_t
// int trigger_channel_id; // the channel index of output
// int wake_word_length; // the length of wake word. It's
// unit is the number of samples. int ret_value; //
// the return state of fetch function void* reserved; // reserved for future
// use
if (res->vad_state > 0) {
printf("AFE data_size: %d\n", res->data_size);
printf("AFE data_volume: %f\n", res->data_volume);
printf("AFE vad_state: %d\n", res->vad_state);
printf("AFE ret_value: %d\n", res->ret_value);
}
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; // 标记已检测到唤醒词
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");
continue;
}
}
}
if (model_data) {
multinet->destroy(model_data);
model_data = NULL;
}
printf("detect exit\n");
vTaskDelete(NULL);
}
void app_sr_init(void) {
// 获取模型 名称“model”和分区表中装载模型的名称一致
models = esp_srmodel_init("model");
afe_handle = (esp_afe_sr_iface_t*)&ESP_AFE_SR_HANDLE;
// 先配置afe句柄
// 随后才可以调用afe接口
afe_config_t afe_config = AFE_CONFIG_DEFAULT(); // 配置afe
// 配置唤醒模型 必须在create_from_config之前配置
afe_config.aec_init = true;
afe_config.se_init = true;
afe_config.vad_init = true;
afe_config.wakenet_init = false;
afe_config.voice_communication_init = false;
afe_config.voice_communication_agc_init = true;
afe_config.voice_communication_agc_gain = 15;
afe_config.vad_mode = VAD_MODE_3;
afe_config.wakenet_model_name = NULL;
afe_config.wakenet_model_name_2 = NULL;
afe_config.wakenet_mode = DET_MODE_2CH_90;
afe_config.afe_mode = SR_MODE_LOW_COST;
afe_config.afe_perferred_core = 0;
afe_config.afe_perferred_priority = 5;
afe_config.afe_ringbuf_size = 50;
afe_config.memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM;
afe_config.afe_linear_gain = 4.0;
afe_config.agc_mode = AFE_MN_PEAK_AGC_MODE_2;
afe_config.pcm_config.total_ch_num = 3;
afe_config.pcm_config.mic_num = 2;
afe_config.pcm_config.ref_num = 1;
afe_config.pcm_config.sample_rate = 16000;
afe_config.debug_init = false;
// afe_config.debug_hook = {{AFE_DEBUG_HOOK_MASE_TASK_IN, NULL},
// {AFE_DEBUG_HOOK_FETCH_TASK_IN, NULL}};
afe_config.afe_ns_mode = NS_MODE_SSP;
afe_config.afe_ns_model_name = NULL;
// afe_config.wakenet_model_name =
// esp_srmodel_filter(models, ESP_WN_PREFIX, NULL);
afe_data = afe_handle->create_from_config(&afe_config); // 创建afe_data
// ESP_LOGI(TAG, "wakenet:%s", afe_config.wakenet_model_name); //
// 打印唤醒名称
task_flag = 1;
record_start();
xTaskCreatePinnedToCore(&detect_Task, "detect", 8 * 1024, (void*)afe_data, 5,
NULL, 1);
xTaskCreatePinnedToCore(&feed_Task, "feed", 8 * 1024, (void*)afe_data, 5,
NULL, 0);
while (wav_written2 != 0xFFFFFFFF || wav_written1 != 0xFFFFFFFF) {
vTaskDelay(pdMS_TO_TICKS(10));
}
}

View File

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

View File

@ -1,515 +0,0 @@
#include "app_ui.h"
#include <dirent.h>
#include "audio_player.h"
#include "esp32_s3_szp.h"
#include "file_iterator.h"
#include "string.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();
}
lv_obj_t* gif_start;
// 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);
}

View File

@ -1,13 +0,0 @@
#pragma once
void mp3_player_init(void);
void music_ui(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);

View File

@ -1,790 +0,0 @@
#include "esp32_s3_szp.h"
#include <stdio.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;
}
/********************* 音频 ↑ *************************/
/***********************************************************/

View File

@ -1,282 +0,0 @@
#pragma once
#include <string.h>
#include "driver/i2c.h"
#include "driver/i2s_std.h"
#include "driver/ledc.h"
#include "driver/sdmmc_host.h"
#include "driver/spi_master.h"
#include "esp_check.h"
#include "esp_codec_dev.h"
#include "esp_codec_dev_defaults.h"
#include "esp_err.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_touch_ft5x06.h"
#include "esp_lcd_types.h"
#include "esp_log.h"
#include "esp_lvgl_port.h"
#include "esp_spiffs.h"
#include "esp_vfs_fat.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "math.h"
#include "sdmmc_cmd.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 (90.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);
/********************* 音频 ↑ *************************/
/***********************************************************/

View File

@ -1,78 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Header structure for WAV file with only one data chunk
*
* @note See this for reference: http://soundfile.sapp.org/doc/WaveFormat/
*
* @note Assignment to variables in this struct directly is only possible for
* little endian architectures (including Xtensa & RISC-V)
*/
typedef struct {
struct {
char chunk_id[4]; /*!< Contains the letters "RIFF" in ASCII form */
uint32_t chunk_size; /*!< This is the size of the rest of the chunk
following this number */
char chunk_format[4]; /*!< Contains the letters "WAVE" */
} descriptor_chunk; /*!< Canonical WAVE format starts with the RIFF header */
struct {
char subchunk_id[4]; /*!< Contains the letters "fmt " */
uint32_t subchunk_size; /*!< This is the size of the rest of the Subchunk
which follows this number */
uint16_t audio_format; /*!< PCM = 1, values other than 1 indicate some form
of compression */
uint16_t num_of_channels; /*!< Mono = 1, Stereo = 2, etc. */
uint32_t sample_rate; /*!< 8000, 44100, etc. */
uint32_t byte_rate; /*!< ==SampleRate * NumChannels * BitsPerSample s/ 8 */
uint16_t block_align; /*!< ==NumChannels * BitsPerSample / 8 */
uint16_t bits_per_sample; /*!< 8 bits = 8, 16 bits = 16, etc. */
} fmt_chunk; /*!< The "fmt " subchunk describes the sound data's format */
struct {
char subchunk_id[4]; /*!< Contains the letters "data" */
uint32_t
subchunk_size; /*!< ==NumSamples * NumChannels * BitsPerSample / 8 */
int16_t data[0]; /*!< Holds raw audio data */
} data_chunk; /*!< The "data" subchunk contains the size of the data and the
actual sound */
} wav_header_t;
/**
* @brief Default header for PCM format WAV files
*
*/
#define WAV_HEADER_PCM_DEFAULT(wav_sample_size, wav_sample_bits, \
wav_sample_rate, wav_channel_num) \
{ \
.descriptor_chunk = {.chunk_id = {'R', 'I', 'F', 'F'}, \
.chunk_size = \
(wav_sample_size) + sizeof(wav_header_t) - 8, \
.chunk_format = {'W', 'A', 'V', 'E'}}, \
.fmt_chunk = {.subchunk_id = {'f', 'm', 't', ' '}, \
.subchunk_size = 16, /* 16 for PCM */ \
.audio_format = 1, /* 1 for PCM */ \
.num_of_channels = (wav_channel_num), \
.sample_rate = (wav_sample_rate), \
.byte_rate = (wav_sample_bits) * (wav_sample_rate) * \
(wav_channel_num) / 8, \
.block_align = (wav_sample_bits) * (wav_channel_num) / 8, \
.bits_per_sample = (wav_sample_bits)}, \
.data_chunk = { \
.subchunk_id = {'d', 'a', 't', 'a'}, \
.subchunk_size = (wav_sample_size) \
} \
}
#ifdef __cplusplus
}
#endif

View File

@ -3,15 +3,6 @@ dependencies:
espressif/esp_tinyusb:
version: "^2.0.0"
espressif/es7210: "^1.0.0"
# 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" # 语音识别
idf:
version: ">=5.4.0"

View File

@ -1,421 +1,290 @@
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include "app_sr.h"
#include "app_ui.h"
#include "driver/i2c.h"
#include "driver/i2s_std.h"
#include "driver/i2s_tdm.h"
#include "driver/sdmmc_host.h"
#include "es7210.h"
#include "esp32_s3_szp.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_check.h"
#include "esp_netif_sntp.h"
#include "esp_pm.h"
#include "esp_private/esp_clk.h"
#include "esp_sleep.h"
#include "esp_sntp.h"
#include "esp_system.h"
#include "esp_vfs_fat.h"
#include "esp_wifi.h"
#include "format_wav.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
#include "sdmmc_cmd.h"
/* I2C port and GPIOs */
#define I2C_NUM (0)
#define I2C_SDA_IO (1)
#define I2C_SCL_IO (2)
/* I2S port and GPIOs */
#define I2S_NUM (0)
#define I2S_MCK_IO (38)
#define I2S_BCK_IO (14)
#define I2S_WS_IO (13)
#define I2S_DI_IO (12)
/* SD card GPIOs */
#define SD_CMD_IO (48)
#define SD_CLK_IO (47)
#define SD_DAT0_IO (21)
/* I2S configurations */
#define I2S_TDM_FORMAT (ES7210_I2S_FMT_I2S)
#define I2S_CHAN_NUM (2)
#define I2S_SAMPLE_RATE (16000)
#define I2S_MCLK_MULTIPLE (I2S_MCLK_MULTIPLE_256)
#define I2S_SAMPLE_BITS (I2S_DATA_BIT_WIDTH_16BIT)
#define I2S_TDM_SLOT_MASK (I2S_TDM_SLOT0 | I2S_TDM_SLOT1)
/* ES7210 configurations */
#define ES7210_I2C_ADDR (0x41)
#define ES7210_I2C_CLK (100000)
#define ES7210_MIC_GAIN (ES7210_MIC_GAIN_30DB)
#define ES7210_MIC_BIAS (ES7210_MIC_BIAS_2V87)
#define ES7210_ADC_VOLUME (0)
/* SD card & recording configurations */
#define RECORD_TIME_SEC (20)
#define SD_MOUNT_POINT "/sdcard"
static const char* TAG = "main";
static char* record_file_name = NULL;
static i2s_chan_handle_t es7210_i2s_init(void) {
i2s_chan_handle_t i2s_rx_chan = NULL; // 定义接收通道句柄
ESP_LOGI(TAG, "Create I2S receive channel");
i2s_chan_config_t i2s_rx_conf = I2S_CHANNEL_DEFAULT_CONFIG(
I2S_NUM_AUTO, I2S_ROLE_MASTER); // 配置接收通道
ESP_ERROR_CHECK(
i2s_new_channel(&i2s_rx_conf, NULL, &i2s_rx_chan)); // 创建i2s通道
ESP_LOGI(TAG, "Configure I2S receive channel to TDM mode");
// 定义接收通道为I2S TDM模式 并配置
i2s_tdm_config_t i2s_tdm_rx_conf = {
.slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(
I2S_SAMPLE_BITS, I2S_SLOT_MODE_STEREO, I2S_TDM_SLOT_MASK),
.clk_cfg = {.clk_src = I2S_CLK_SRC_DEFAULT,
.sample_rate_hz = I2S_SAMPLE_RATE,
.mclk_multiple = I2S_MCLK_MULTIPLE},
.gpio_cfg = {.mclk = I2S_MCK_IO,
.bclk = I2S_BCK_IO,
.ws = I2S_WS_IO,
.dout = -1, // ES7210 only has ADC capability
.din = I2S_DI_IO},
};
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(
i2s_rx_chan, &i2s_tdm_rx_conf)); // 初始化I2S通道为TDM模式
return i2s_rx_chan;
}
sdmmc_card_t* mount_sdcard(void) {
sdmmc_card_t* sdmmc_card = NULL;
ESP_LOGI(TAG, "Mounting SD card");
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
.allocation_unit_size = 8 * 1024};
ESP_LOGI(TAG, "Initializing SD card");
ESP_LOGI(TAG, "Using SDMMC peripheral");
sdmmc_host_t sdmmc_host = SDMMC_HOST_DEFAULT(); // SDMMC主机接口配置
sdmmc_slot_config_t slot_config =
SDMMC_SLOT_CONFIG_DEFAULT(); // SDMMC插槽配置
slot_config.width = 1; // 设置为1线SD模式
slot_config.clk = SD_CLK_IO;
slot_config.cmd = SD_CMD_IO;
slot_config.d0 = SD_DAT0_IO;
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; // 打开内部上拉电阻
ESP_LOGI(TAG, "Mounting filesystem");
esp_err_t ret;
while (1) {
ret = esp_vfs_fat_sdmmc_mount(SD_MOUNT_POINT, &sdmmc_host, &slot_config,
&mount_config, &sdmmc_card);
if (ret == ESP_OK) {
break;
} else if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount filesystem.");
} else {
ESP_LOGE(TAG, "Failed to initialize the card (%s). ",
esp_err_to_name(ret));
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
ESP_LOGI(
TAG, "Card size: %lluMB, speed: %dMHz",
(((uint64_t)sdmmc_card->csd.capacity) * sdmmc_card->csd.sector_size) >>
20,
sdmmc_card->max_freq_khz / 1000);
return sdmmc_card;
}
static void es7210_codec_init(void) {
// 创建es7210器件句柄
es7210_dev_handle_t es7210_handle = NULL;
es7210_i2c_config_t es7210_i2c_conf = {.i2c_port = I2C_NUM,
.i2c_addr = ES7210_I2C_ADDR};
ESP_ERROR_CHECK(es7210_new_codec(&es7210_i2c_conf, &es7210_handle));
// 初始化es7210芯片
ESP_LOGI(TAG, "Configure ES7210 codec parameters");
es7210_codec_config_t codec_conf = {
.i2s_format = I2S_TDM_FORMAT,
.mclk_ratio = I2S_MCLK_MULTIPLE,
.sample_rate_hz = I2S_SAMPLE_RATE,
.bit_width = (es7210_i2s_bits_t)I2S_SAMPLE_BITS,
.mic_bias = ES7210_MIC_BIAS,
.mic_gain = ES7210_MIC_GAIN,
.flags.tdm_enable = true};
ESP_ERROR_CHECK(es7210_config_codec(es7210_handle, &codec_conf));
ESP_ERROR_CHECK(es7210_config_volume(es7210_handle, ES7210_ADC_VOLUME));
}
// wifi事件组
static EventGroupHandle_t s_wifi_event_group = NULL;
// wifi事件
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
#define WIFI_START_BIT BIT2
#define WIFI_GET_SNTP_BIT BIT3
// wifi最大重连次数
#define EXAMPLE_ESP_MAXIMUM_RETRY 3
typedef struct {
char wifi_ssid[32]; // 获取wifi名称
char wifi_password[64]; // 获取wifi密码
char back_flag; // 是否退出
} wifi_account_t;
static void wifi_connect() {
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
assert(esp_netif_create_default_wifi_sta());
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// ESP_ERROR_CHECK(esp_event_handler_instance_register(
// WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id));
// ESP_ERROR_CHECK(esp_event_handler_instance_register(
// IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL,
// &instance_got_ip));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
wifi_account_t wifi_account;
strcpy(wifi_account.wifi_ssid, "Liang");
strcpy(wifi_account.wifi_password, "wifi1234");
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", wifi_account.wifi_ssid,
wifi_account.wifi_password);
wifi_account.back_flag = 0; // 正常连接
wifi_config_t wifi_config = {
.sta =
{
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
.sae_h2e_identifier = "",
},
};
strcpy((char*)wifi_config.sta.ssid, wifi_account.wifi_ssid);
strcpy((char*)wifi_config.sta.password, wifi_account.wifi_password);
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
esp_wifi_connect();
}
time_t now;
struct tm timeinfo;
static void get_time_task() {
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("ntp.aliyun.com");
esp_netif_sntp_init(&config);
// wait for time to be set
int retry = 0;
// const int retry_count = 6;
while (esp_netif_sntp_sync_wait(2000 / portTICK_PERIOD_MS) ==
ESP_ERR_TIMEOUT) {
ESP_LOGI(TAG, "Waiting for system time to be set... (%d)", retry++);
}
esp_netif_sntp_deinit();
// 设置时区
setenv("TZ", "CST-8", 1);
tzset();
// 获取系统时间
time(&now);
localtime_r(&now, &timeinfo);
ESP_LOGI(TAG, "%d年%02d月%02d日", timeinfo.tm_year + 1900,
timeinfo.tm_mon + 1, timeinfo.tm_mday);
ESP_LOGI(TAG, "%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min,
timeinfo.tm_sec);
}
static char* get_file_name_from_time(void) {
get_time_task();
strftime(record_file_name, 48, "/sdcard/Record_%Y%m%d%H%M%S.wav", &timeinfo);
return record_file_name;
}
static esp_err_t record_wav(i2s_chan_handle_t i2s_rx_chan) {
ESP_RETURN_ON_FALSE(i2s_rx_chan, ESP_FAIL, TAG,
"invalid i2s channel handle pointer");
esp_err_t ret = ESP_OK;
uint32_t byte_rate = I2S_SAMPLE_RATE * I2S_CHAN_NUM * I2S_SAMPLE_BITS / 8;
uint32_t wav_size = byte_rate * RECORD_TIME_SEC;
const wav_header_t wav_header = WAV_HEADER_PCM_DEFAULT(
wav_size, I2S_SAMPLE_BITS, I2S_SAMPLE_RATE, I2S_CHAN_NUM);
get_file_name_from_time();
ESP_LOGI(TAG, "Opening file %s", record_file_name);
FILE* f = fopen(record_file_name, "w");
ESP_LOGI(TAG, "fopen error: %s", strerror(errno));
ESP_RETURN_ON_FALSE(f, ESP_FAIL, TAG, "error while opening wav file");
ESP_GOTO_ON_FALSE(fwrite(&wav_header, sizeof(wav_header_t), 1, f), ESP_FAIL,
err, TAG, "error while writing wav header");
/* Start recording */
size_t wav_written = 0;
static int16_t i2s_readraw_buff[4096];
ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_chan), err, TAG,
"error while starting i2s rx channel");
while (wav_written < wav_size) {
if (wav_written % byte_rate < sizeof(i2s_readraw_buff)) {
ESP_LOGI(TAG, "Recording: %" PRIu32 "/%ds", wav_written / byte_rate + 1,
RECORD_TIME_SEC);
}
size_t bytes_read = 0;
/* Read RAW samples from ES7210 */
ESP_GOTO_ON_ERROR(i2s_channel_read(i2s_rx_chan, i2s_readraw_buff,
sizeof(i2s_readraw_buff), &bytes_read,
pdMS_TO_TICKS(1000)),
err, TAG, "error while reading samples from i2s");
/* Write the samples to the WAV file */
ESP_GOTO_ON_FALSE(fwrite(i2s_readraw_buff, bytes_read, 1, f), ESP_FAIL, err,
TAG, "error while writing samples to wav file");
wav_written += bytes_read;
}
err:
i2s_channel_disable(i2s_rx_chan);
ESP_LOGI(TAG, "Recording done! Flushing file buffer");
fclose(f);
return ret;
}
void init_msc(sdmmc_card_t* card);
void config_power_manager() {
// 自动降频至8MHz 双核都空闲时进入 Light Sleep
esp_err_t ret;
esp_pm_config_esp32_t pm_config = {
.max_freq_mhz = 240, .min_freq_mhz = 8, .light_sleep_enable = true};
ret = esp_pm_configure(&pm_config);
if (ret != ESP_OK) {
ESP_LOGE("PM", "Power management configuration failed (0x%x)", ret);
}
uint32_t cpu_freq = esp_clk_cpu_freq();
printf("当前CPU频率: %lu MHz\n", cpu_freq / 1000000);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
// light_sleep_off
// deep sleep 5
// 240 44.8
// 160 38.4
// 80 32.9
// 8 18.8
}
void taskOnCore0(void* pvParam) {
while (1) {
uint32_t cpu_freq = esp_clk_cpu_freq();
printf("taskOnCore0 当前CPU频率: %lu MHz\n", cpu_freq / 1000000);
vTaskDelay(1000 / portTICK_PERIOD_MS);
// esp_sleep_enable_timer_wakeup(1 * 1000000);
// esp_light_sleep_start();
}
}
void taskOnCore1(void* pvParam) {
while (1) {
uint32_t cpu_freq = esp_clk_cpu_freq();
printf("taskOnCore1 当前CPU频率: %lu MHz\n", cpu_freq / 1000000);
vTaskDelay(1000 / portTICK_PERIOD_MS);
// esp_sleep_enable_timer_wakeup(1 * 1000000);
// esp_light_sleep_start();
}
}
void app_main(void) {
esp_err_t ret;
ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
sdmmc_card_t* sdmmc_card = mount_sdcard();
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(); // 语音识别初始化
// esp_deep_sleep_start();
// esp_light_sleep_start();
i2s_chan_handle_t i2s_rx_chan = es7210_i2s_init();
es7210_codec_init();
wifi_connect();
// esp_wifi_stop();
// esp_wifi_set_mode(WIFI_MODE_NULL);
// esp_wifi_deinit();
// esp_bt_controller_disable();
// esp_bt_controller_deinit();
// esp_bluedroid_disable();
// esp_bluedroid_deinit();
// esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
// 主动休眠
// esp_deep_sleep_start();
// esp_light_sleep_start();
// config_power_manager();
// xTaskCreatePinnedToCore(taskOnCore0, // 任务函数
// "Core0_Task", // 任务名称
// 4096, // 堆栈大小
// NULL, // 参数
// 1, // 优先级
// NULL, // 任务句柄
// 0 // 绑定到核心0
// );
// xTaskCreatePinnedToCore(taskOnCore1, // 任务函数
// "Core1_Task", // 任务名称
// 4096, // 堆栈大小
// NULL, // 参数
// 1, // 优先级
// NULL, // 任务句柄
// 1 // 绑定到核心0
// );
record_file_name = malloc(64);
for (int i = 0; i < 2; i++) {
esp_err_t err = record_wav(i2s_rx_chan);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Audio success recorded into %s.", record_file_name);
} else {
ESP_LOGE(TAG, "Record failed, %s .", record_file_name);
}
}
init_msc(sdmmc_card);
esp_vfs_fat_sdcard_unmount(SD_MOUNT_POINT, sdmmc_card);
while (1) {
vTaskDelay(pdMS_TO_TICKS(10));
}
}
#include <stdio.h>
#include <string.h>
#include "driver/gpio.h"
#include "driver/i2s_std.h"
#include "driver/i2s_types.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_pm.h"
#include "esp_private/esp_clk.h"
#include "esp_sleep.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "nvs_flash.h"
// 配置参数
#define SAMPLE_RATE 16000
#define I2S_PORT I2S_NUM_0
#define DMA_BUF_COUNT 8
#define DMA_BUF_LEN 1024
#define SAMPLE_BITS I2S_DATA_BIT_WIDTH_16BIT
// 引脚定义
#define I2S_BCK_PIN 14
#define I2S_WS_PIN 13
#define I2S_DATA_PIN 12
#define LED_PIN 48 // 用于指示中断触发
// 全局变量
static const char* TAG = "I2S_DMA_ISR";
static i2s_chan_handle_t rx_chan = NULL;
static QueueHandle_t i2s_event_queue = NULL;
static SemaphoreHandle_t data_ready_sem = NULL;
// 中断服务程序的数据处理任务句柄
static TaskHandle_t i2s_dma_task_handle = NULL;
// 自定义数据结构用于队列传递
typedef struct {
void* buffer;
size_t size;
} i2s_queue_data_t;
// 初始化GPIO
static void gpio_init(void) {
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << LED_PIN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&io_conf);
gpio_set_level(LED_PIN, 0);
}
// I2S数据接收任务 - 从DMA缓冲区读取数据并放入队列
static void i2s_rx_task(void* arg) {
size_t bytes_read = 0;
int16_t* data_buffer = NULL;
// 预分配缓冲区以避免在循环中重复分配
data_buffer =
(int16_t*)heap_caps_malloc(DMA_BUF_LEN * sizeof(int16_t), MALLOC_CAP_DMA);
if (!data_buffer) {
ESP_LOGE(TAG, "无法分配I2S数据缓冲区");
vTaskDelete(NULL);
return;
}
ESP_LOGI(TAG, "I2S接收任务开始运行");
while (1) {
// 从I2S通道读取数据 - 使用 short timeout to avoid blocking indefinitely
esp_err_t ret = ESP_OK;
ret = i2s_channel_read(rx_chan, data_buffer, DMA_BUF_LEN * sizeof(int16_t),
&bytes_read, pdMS_TO_TICKS(1));
if (ret == ESP_OK && bytes_read > 0) {
// 将数据发送到队列进行进一步处理
// i2s_queue_data_t queue_item = {
// .buffer = malloc(bytes_read), // 为处理任务分配新的缓冲区
// .size = bytes_read};
// if (queue_item.buffer) {
// memcpy(queue_item.buffer, data_buffer, bytes_read);
// // 发送数据到队列
// if (xQueueSend(i2s_event_queue, &queue_item, portMAX_DELAY) !=
// pdTRUE) {
// // 队列满,释放缓冲区
// ESP_LOGW(TAG, "I2S队列满丢弃数据");
// free(queue_item.buffer);
// } else {
// // 通知数据处理任务有新数据
// xSemaphoreGive(data_ready_sem);
// }
// } else {
// ESP_LOGE(TAG, "无法分配内存来复制I2S数据");
// }
ESP_LOGI(TAG, "处理 %d 字节的I2S数据", bytes_read);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
esp_sleep_enable_timer_wakeup(50 * 1000);
esp_light_sleep_start();
// 点亮LED指示数据处理
// gpio_set_level(LED_PIN, 1);
// vTaskDelay(pdMS_TO_TICKS(5)); // 短暂点亮LED
// gpio_set_level(LED_PIN, 0);
} else if (ret != ESP_OK && ret != ESP_ERR_TIMEOUT) {
ESP_LOGE(TAG, "I2S读取错误: %s", esp_err_to_name(ret));
}
// ESP_ERR_TIMEOUT is expected when no data is available
}
// 释放DMA缓冲区理论上不会到达这里但为了完整性
if (data_buffer) {
heap_caps_free(data_buffer);
}
vTaskDelete(NULL);
}
// I2S数据处理任务 - 仅处理队列中的数据不主动读取I2S
static void i2s_process_task(void* arg) {
i2s_queue_data_t queue_data;
ESP_LOGI(TAG, "I2S数据处理任务开始运行");
while (1) {
// 等待数据就绪信号,使用超时避免无限等待
if (xSemaphoreTake(data_ready_sem, pdMS_TO_TICKS(1000)) == pdTRUE) {
// 从队列中获取数据
while (xQueueReceive(i2s_event_queue, &queue_data, 0) == pdTRUE) {
// 处理接收到的数据
ESP_LOGI(TAG, "处理 %d 字节的I2S数据", queue_data.size);
// 点亮LED指示数据处理
gpio_set_level(LED_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(5)); // 短暂点亮LED
gpio_set_level(LED_PIN, 0);
// 在这里可以添加对音频数据的处理逻辑
// 例如:音频分析、滤波、存储等
// 释放数据缓冲区
if (queue_data.buffer) {
free(queue_data.buffer);
}
}
} else {
// 超时未收到信号,可以执行其他操作或简单地继续循环
// 这样可以避免任务无限期阻塞
// 可以在这里添加其他处理逻辑 if needed
}
}
}
// 初始化I2S
static esp_err_t i2s_init(void) {
// 创建I2S事件队列
// i2s_event_queue = xQueueCreate(DMA_BUF_COUNT, sizeof(i2s_queue_data_t));
// if (!i2s_event_queue) {
// ESP_LOGE(TAG, "创建I2S事件队列失败");
// return ESP_FAIL;
// }
// 创建数据就绪信号量
// data_ready_sem = xSemaphoreCreateBinary();
// if (!data_ready_sem) {
// ESP_LOGE(TAG, "创建数据就绪信号量失败");
// return ESP_FAIL;
// }
// 配置I2S通道
i2s_chan_config_t chan_cfg =
I2S_CHANNEL_DEFAULT_CONFIG(I2S_PORT, I2S_ROLE_MASTER);
chan_cfg.dma_desc_num = DMA_BUF_COUNT;
chan_cfg.dma_frame_num = DMA_BUF_LEN;
chan_cfg.auto_clear = true;
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_chan));
// 配置STD模式
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg =
I2S_STD_MSB_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_MONO),
.gpio_cfg = {.mclk = I2S_GPIO_UNUSED,
.bclk = I2S_BCK_PIN,
.ws = I2S_WS_PIN,
.dout = I2S_GPIO_UNUSED,
.din = I2S_DATA_PIN,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
}}};
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
// 启用通道
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
ESP_LOGI(TAG, "I2S初始化完成使用DMA");
return ESP_OK;
}
// 主处理任务 - 从队列中获取并处理数据
void config_power_manager() {
// 自动降频至8MHz 双核都空闲时进入 Light Sleep
esp_err_t ret;
esp_pm_config_esp32_t pm_config = {
.max_freq_mhz = 240, .min_freq_mhz = 8, .light_sleep_enable = true};
ret = esp_pm_configure(&pm_config);
if (ret != ESP_OK) {
ESP_LOGE("PM", "Power management configuration failed (0x%x)", ret);
}
uint32_t cpu_freq = esp_clk_cpu_freq();
printf("当前CPU频率: %lu MHz\n", cpu_freq / 1000000);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
// light_sleep_off
// deep sleep 5
// 240 44.8
// 160 38.4
// 80 32.9
// 8 18.8
}
// 主函数
void app_main(void) {
// 初始化NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "启动I2S DMA接收示例");
// 初始化GPIO
gpio_init();
config_power_manager();
// 初始化I2S
if (i2s_init() != ESP_OK) {
ESP_LOGE(TAG, "I2S初始化失败");
return;
}
// 创建I2S接收任务
TaskHandle_t rx_task_handle = NULL;
BaseType_t task_ret = xTaskCreate(i2s_rx_task, "i2s_rx_task", 4096, NULL,
tskIDLE_PRIORITY + 2, &rx_task_handle);
if (task_ret != pdTRUE) {
ESP_LOGE(TAG, "创建I2S接收任务失败");
return;
}
// 创建I2S数据处理任务
// task_ret = xTaskCreate(i2s_process_task, "i2s_process_task", 4096, NULL,
// tskIDLE_PRIORITY + 1, &i2s_dma_task_handle);
// if (task_ret != pdTRUE) {
// ESP_LOGE(TAG, "创建I2S数据处理任务失败");
// return;
// }
ESP_LOGI(TAG, "系统启动完成I2S使用DMA处理数据");
// 主循环可以执行其他任务
// while (1) {
// // 这里可以执行其他非I2S相关的任务
// vTaskDelay(pdMS_TO_TICKS(1000));
// }
}
// 应用程序结束时的清理函数
void app_cleanup(void) {
if (rx_chan) {
i2s_channel_disable(rx_chan);
i2s_del_channel(rx_chan);
rx_chan = NULL;
}
}

421
main/main.c.bacl Normal file
View File

@ -0,0 +1,421 @@
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include "app_sr.h"
#include "app_ui.h"
#include "driver/i2c.h"
#include "driver/i2s_std.h"
#include "driver/i2s_tdm.h"
#include "driver/sdmmc_host.h"
#include "es7210.h"
#include "esp32_s3_szp.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_check.h"
#include "esp_netif_sntp.h"
#include "esp_pm.h"
#include "esp_private/esp_clk.h"
#include "esp_sleep.h"
#include "esp_sntp.h"
#include "esp_system.h"
#include "esp_vfs_fat.h"
#include "esp_wifi.h"
#include "format_wav.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
#include "sdmmc_cmd.h"
/* I2C port and GPIOs */
#define I2C_NUM (0)
#define I2C_SDA_IO (1)
#define I2C_SCL_IO (2)
/* I2S port and GPIOs */
#define I2S_NUM (0)
#define I2S_MCK_IO (38)
#define I2S_BCK_IO (14)
#define I2S_WS_IO (13)
#define I2S_DI_IO (12)
/* SD card GPIOs */
#define SD_CMD_IO (48)
#define SD_CLK_IO (47)
#define SD_DAT0_IO (21)
/* I2S configurations */
#define I2S_TDM_FORMAT (ES7210_I2S_FMT_I2S)
#define I2S_CHAN_NUM (2)
#define I2S_SAMPLE_RATE (16000)
#define I2S_MCLK_MULTIPLE (I2S_MCLK_MULTIPLE_256)
#define I2S_SAMPLE_BITS (I2S_DATA_BIT_WIDTH_16BIT)
#define I2S_TDM_SLOT_MASK (I2S_TDM_SLOT0 | I2S_TDM_SLOT1)
/* ES7210 configurations */
#define ES7210_I2C_ADDR (0x41)
#define ES7210_I2C_CLK (100000)
#define ES7210_MIC_GAIN (ES7210_MIC_GAIN_30DB)
#define ES7210_MIC_BIAS (ES7210_MIC_BIAS_2V87)
#define ES7210_ADC_VOLUME (0)
/* SD card & recording configurations */
#define RECORD_TIME_SEC (20)
#define SD_MOUNT_POINT "/sdcard"
static const char* TAG = "main";
static char* record_file_name = NULL;
static i2s_chan_handle_t es7210_i2s_init(void) {
i2s_chan_handle_t i2s_rx_chan = NULL; // 定义接收通道句柄
ESP_LOGI(TAG, "Create I2S receive channel");
i2s_chan_config_t i2s_rx_conf = I2S_CHANNEL_DEFAULT_CONFIG(
I2S_NUM_AUTO, I2S_ROLE_MASTER); // 配置接收通道
ESP_ERROR_CHECK(
i2s_new_channel(&i2s_rx_conf, NULL, &i2s_rx_chan)); // 创建i2s通道
ESP_LOGI(TAG, "Configure I2S receive channel to TDM mode");
// 定义接收通道为I2S TDM模式 并配置
i2s_tdm_config_t i2s_tdm_rx_conf = {
.slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(
I2S_SAMPLE_BITS, I2S_SLOT_MODE_STEREO, I2S_TDM_SLOT_MASK),
.clk_cfg = {.clk_src = I2S_CLK_SRC_DEFAULT,
.sample_rate_hz = I2S_SAMPLE_RATE,
.mclk_multiple = I2S_MCLK_MULTIPLE},
.gpio_cfg = {.mclk = I2S_MCK_IO,
.bclk = I2S_BCK_IO,
.ws = I2S_WS_IO,
.dout = -1, // ES7210 only has ADC capability
.din = I2S_DI_IO},
};
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(
i2s_rx_chan, &i2s_tdm_rx_conf)); // 初始化I2S通道为TDM模式
return i2s_rx_chan;
}
sdmmc_card_t* mount_sdcard(void) {
sdmmc_card_t* sdmmc_card = NULL;
ESP_LOGI(TAG, "Mounting SD card");
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
.allocation_unit_size = 8 * 1024};
ESP_LOGI(TAG, "Initializing SD card");
ESP_LOGI(TAG, "Using SDMMC peripheral");
sdmmc_host_t sdmmc_host = SDMMC_HOST_DEFAULT(); // SDMMC主机接口配置
sdmmc_slot_config_t slot_config =
SDMMC_SLOT_CONFIG_DEFAULT(); // SDMMC插槽配置
slot_config.width = 1; // 设置为1线SD模式
slot_config.clk = SD_CLK_IO;
slot_config.cmd = SD_CMD_IO;
slot_config.d0 = SD_DAT0_IO;
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; // 打开内部上拉电阻
ESP_LOGI(TAG, "Mounting filesystem");
esp_err_t ret;
while (1) {
ret = esp_vfs_fat_sdmmc_mount(SD_MOUNT_POINT, &sdmmc_host, &slot_config,
&mount_config, &sdmmc_card);
if (ret == ESP_OK) {
break;
} else if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount filesystem.");
} else {
ESP_LOGE(TAG, "Failed to initialize the card (%s). ",
esp_err_to_name(ret));
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
ESP_LOGI(
TAG, "Card size: %lluMB, speed: %dMHz",
(((uint64_t)sdmmc_card->csd.capacity) * sdmmc_card->csd.sector_size) >>
20,
sdmmc_card->max_freq_khz / 1000);
return sdmmc_card;
}
static void es7210_codec_init(void) {
// 创建es7210器件句柄
es7210_dev_handle_t es7210_handle = NULL;
es7210_i2c_config_t es7210_i2c_conf = {.i2c_port = I2C_NUM,
.i2c_addr = ES7210_I2C_ADDR};
ESP_ERROR_CHECK(es7210_new_codec(&es7210_i2c_conf, &es7210_handle));
// 初始化es7210芯片
ESP_LOGI(TAG, "Configure ES7210 codec parameters");
es7210_codec_config_t codec_conf = {
.i2s_format = I2S_TDM_FORMAT,
.mclk_ratio = I2S_MCLK_MULTIPLE,
.sample_rate_hz = I2S_SAMPLE_RATE,
.bit_width = (es7210_i2s_bits_t)I2S_SAMPLE_BITS,
.mic_bias = ES7210_MIC_BIAS,
.mic_gain = ES7210_MIC_GAIN,
.flags.tdm_enable = true};
ESP_ERROR_CHECK(es7210_config_codec(es7210_handle, &codec_conf));
ESP_ERROR_CHECK(es7210_config_volume(es7210_handle, ES7210_ADC_VOLUME));
}
// wifi事件组
static EventGroupHandle_t s_wifi_event_group = NULL;
// wifi事件
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
#define WIFI_START_BIT BIT2
#define WIFI_GET_SNTP_BIT BIT3
// wifi最大重连次数
#define EXAMPLE_ESP_MAXIMUM_RETRY 3
typedef struct {
char wifi_ssid[32]; // 获取wifi名称
char wifi_password[64]; // 获取wifi密码
char back_flag; // 是否退出
} wifi_account_t;
static void wifi_connect() {
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
assert(esp_netif_create_default_wifi_sta());
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// ESP_ERROR_CHECK(esp_event_handler_instance_register(
// WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id));
// ESP_ERROR_CHECK(esp_event_handler_instance_register(
// IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL,
// &instance_got_ip));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
wifi_account_t wifi_account;
strcpy(wifi_account.wifi_ssid, "Liang");
strcpy(wifi_account.wifi_password, "wifi1234");
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", wifi_account.wifi_ssid,
wifi_account.wifi_password);
wifi_account.back_flag = 0; // 正常连接
wifi_config_t wifi_config = {
.sta =
{
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
.sae_h2e_identifier = "",
},
};
strcpy((char*)wifi_config.sta.ssid, wifi_account.wifi_ssid);
strcpy((char*)wifi_config.sta.password, wifi_account.wifi_password);
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
esp_wifi_connect();
}
time_t now;
struct tm timeinfo;
static void get_time_task() {
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("ntp.aliyun.com");
esp_netif_sntp_init(&config);
// wait for time to be set
int retry = 0;
// const int retry_count = 6;
while (esp_netif_sntp_sync_wait(2000 / portTICK_PERIOD_MS) ==
ESP_ERR_TIMEOUT) {
ESP_LOGI(TAG, "Waiting for system time to be set... (%d)", retry++);
}
esp_netif_sntp_deinit();
// 设置时区
setenv("TZ", "CST-8", 1);
tzset();
// 获取系统时间
time(&now);
localtime_r(&now, &timeinfo);
ESP_LOGI(TAG, "%d年%02d月%02d日", timeinfo.tm_year + 1900,
timeinfo.tm_mon + 1, timeinfo.tm_mday);
ESP_LOGI(TAG, "%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min,
timeinfo.tm_sec);
}
static char* get_file_name_from_time(void) {
get_time_task();
strftime(record_file_name, 48, "/sdcard/Record_%Y%m%d%H%M%S.wav", &timeinfo);
return record_file_name;
}
static esp_err_t record_wav(i2s_chan_handle_t i2s_rx_chan) {
ESP_RETURN_ON_FALSE(i2s_rx_chan, ESP_FAIL, TAG,
"invalid i2s channel handle pointer");
esp_err_t ret = ESP_OK;
uint32_t byte_rate = I2S_SAMPLE_RATE * I2S_CHAN_NUM * I2S_SAMPLE_BITS / 8;
uint32_t wav_size = byte_rate * RECORD_TIME_SEC;
const wav_header_t wav_header = WAV_HEADER_PCM_DEFAULT(
wav_size, I2S_SAMPLE_BITS, I2S_SAMPLE_RATE, I2S_CHAN_NUM);
get_file_name_from_time();
ESP_LOGI(TAG, "Opening file %s", record_file_name);
FILE* f = fopen(record_file_name, "w");
ESP_LOGI(TAG, "fopen error: %s", strerror(errno));
ESP_RETURN_ON_FALSE(f, ESP_FAIL, TAG, "error while opening wav file");
ESP_GOTO_ON_FALSE(fwrite(&wav_header, sizeof(wav_header_t), 1, f), ESP_FAIL,
err, TAG, "error while writing wav header");
/* Start recording */
size_t wav_written = 0;
static int16_t i2s_readraw_buff[4096];
ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_chan), err, TAG,
"error while starting i2s rx channel");
while (wav_written < wav_size) {
if (wav_written % byte_rate < sizeof(i2s_readraw_buff)) {
ESP_LOGI(TAG, "Recording: %" PRIu32 "/%ds", wav_written / byte_rate + 1,
RECORD_TIME_SEC);
}
size_t bytes_read = 0;
/* Read RAW samples from ES7210 */
ESP_GOTO_ON_ERROR(i2s_channel_read(i2s_rx_chan, i2s_readraw_buff,
sizeof(i2s_readraw_buff), &bytes_read,
pdMS_TO_TICKS(1000)),
err, TAG, "error while reading samples from i2s");
/* Write the samples to the WAV file */
ESP_GOTO_ON_FALSE(fwrite(i2s_readraw_buff, bytes_read, 1, f), ESP_FAIL, err,
TAG, "error while writing samples to wav file");
wav_written += bytes_read;
}
err:
i2s_channel_disable(i2s_rx_chan);
ESP_LOGI(TAG, "Recording done! Flushing file buffer");
fclose(f);
return ret;
}
void init_msc(sdmmc_card_t* card);
void config_power_manager() {
// 自动降频至8MHz 双核都空闲时进入 Light Sleep
esp_err_t ret;
esp_pm_config_esp32_t pm_config = {
.max_freq_mhz = 240, .min_freq_mhz = 8, .light_sleep_enable = true};
ret = esp_pm_configure(&pm_config);
if (ret != ESP_OK) {
ESP_LOGE("PM", "Power management configuration failed (0x%x)", ret);
}
uint32_t cpu_freq = esp_clk_cpu_freq();
printf("当前CPU频率: %lu MHz\n", cpu_freq / 1000000);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
// light_sleep_off
// deep sleep 5
// 240 44.8
// 160 38.4
// 80 32.9
// 8 18.8
}
void taskOnCore0(void* pvParam) {
while (1) {
uint32_t cpu_freq = esp_clk_cpu_freq();
printf("taskOnCore0 当前CPU频率: %lu MHz\n", cpu_freq / 1000000);
vTaskDelay(1000 / portTICK_PERIOD_MS);
// esp_sleep_enable_timer_wakeup(1 * 1000000);
// esp_light_sleep_start();
}
}
void taskOnCore1(void* pvParam) {
while (1) {
uint32_t cpu_freq = esp_clk_cpu_freq();
printf("taskOnCore1 当前CPU频率: %lu MHz\n", cpu_freq / 1000000);
vTaskDelay(1000 / portTICK_PERIOD_MS);
// esp_sleep_enable_timer_wakeup(1 * 1000000);
// esp_light_sleep_start();
}
}
void app_main(void) {
esp_err_t ret;
ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
sdmmc_card_t* sdmmc_card = mount_sdcard();
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(); // 语音识别初始化
// esp_deep_sleep_start();
// esp_light_sleep_start();
i2s_chan_handle_t i2s_rx_chan = es7210_i2s_init();
es7210_codec_init();
wifi_connect();
// esp_wifi_stop();
// esp_wifi_set_mode(WIFI_MODE_NULL);
// esp_wifi_deinit();
// esp_bt_controller_disable();
// esp_bt_controller_deinit();
// esp_bluedroid_disable();
// esp_bluedroid_deinit();
// esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
// 主动休眠
// esp_deep_sleep_start();
// esp_light_sleep_start();
// config_power_manager();
// xTaskCreatePinnedToCore(taskOnCore0, // 任务函数
// "Core0_Task", // 任务名称
// 4096, // 堆栈大小
// NULL, // 参数
// 1, // 优先级
// NULL, // 任务句柄
// 0 // 绑定到核心0
// );
// xTaskCreatePinnedToCore(taskOnCore1, // 任务函数
// "Core1_Task", // 任务名称
// 4096, // 堆栈大小
// NULL, // 参数
// 1, // 优先级
// NULL, // 任务句柄
// 1 // 绑定到核心0
// );
record_file_name = malloc(64);
for (int i = 0; i < 2; i++) {
esp_err_t err = record_wav(i2s_rx_chan);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Audio success recorded into %s.", record_file_name);
} else {
ESP_LOGE(TAG, "Record failed, %s .", record_file_name);
}
}
init_msc(sdmmc_card);
esp_vfs_fat_sdcard_unmount(SD_MOUNT_POINT, sdmmc_card);
while (1) {
vTaskDelay(pdMS_TO_TICKS(10));
}
}

View File

@ -1,416 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* DESCRIPTION:
* This example contains code to make ESP32 based device recognizable by
* USB-hosts as a USB Mass Storage Device. It either allows the embedded
* application i.e. example to access the partition or Host PC accesses the
* partition over USB MSC. They can't be allowed to access the partition at the
* same time. For different scenarios and behaviour, Refer to README of this
* example.
*/
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#include "driver/gpio.h"
#include "esp_check.h"
#include "esp_console.h"
#include "esp_partition.h"
#include "sdkconfig.h"
#include "tinyusb.h"
#include "tinyusb_default_config.h"
#include "tinyusb_msc.h"
/*
* We warn if a secondary serial console is enabled. A secondary serial console
* is always output-only and hence not very useful for interactive console
* applications. If you encounter this warning, consider disabling the secondary
* serial console in menuconfig unless you know what you are doing.
*/
#if SOC_USB_SERIAL_JTAG_SUPPORTED
#if !CONFIG_ESP_CONSOLE_SECONDARY_NONE
#warning \
"A secondary serial console is not useful when using the console component. Please disable it in menuconfig."
#endif
#endif
static const char* TAG = "msc";
static esp_console_repl_t* repl = NULL;
/* Storage global variables */
tinyusb_msc_storage_handle_t storage_hdl = NULL;
tinyusb_msc_mount_point_t mp;
static SemaphoreHandle_t _wait_console_smp = NULL;
/* TinyUSB descriptors
********************************************************************* */
#define EPNUM_MSC 1
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
enum { ITF_NUM_MSC = 0, ITF_NUM_TOTAL };
enum {
EDPT_CTRL_OUT = 0x00,
EDPT_CTRL_IN = 0x80,
EDPT_MSC_OUT = 0x01,
EDPT_MSC_IN = 0x81,
};
static tusb_desc_device_t descriptor_config = {
.bLength = sizeof(descriptor_config),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x303A, // This is Espressif VID. This needs to be changed
// according to Users / Customers
.idProduct = 0x4002,
.bcdDevice = 0x100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01};
static uint8_t const msc_fs_configuration_desc[] = {
// Config number, interface count, string index, total length, attribute,
// power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 64),
};
#if (TUD_OPT_HIGH_SPEED)
static const tusb_desc_device_qualifier_t device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0};
static uint8_t const msc_hs_configuration_desc[] = {
// Config number, interface count, string index, total length, attribute,
// power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 512),
};
#endif // TUD_OPT_HIGH_SPEED
static char const* string_desc_arr[] = {
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456", // 3: Serials
"Example MSC", // 4. MSC
};
/*********************************************************************** TinyUSB
* descriptors*/
#define BASE_PATH "/sdcard" // base path to mount the partition
#define PROMPT_STR CONFIG_IDF_TARGET
static int console_unmount(int argc, char** argv);
static int console_read(int argc, char** argv);
static int console_write(int argc, char** argv);
static int console_size(int argc, char** argv);
static int console_status(int argc, char** argv);
static int console_exit(int argc, char** argv);
const esp_console_cmd_t cmds[] = {
{
.command = "read",
.help = "read BASE_PATH/README.MD and print its contents",
.hint = NULL,
.func = &console_read,
},
{
.command = "write",
.help = "create file BASE_PATH/README.MD if it does not exist",
.hint = NULL,
.func = &console_write,
},
{
.command = "size",
.help = "show storage size and sector size",
.hint = NULL,
.func = &console_size,
},
{
.command = "expose",
.help = "Expose Storage to Host",
.hint = NULL,
.func = &console_unmount,
},
{
.command = "status",
.help = "Status of storage exposure over USB",
.hint = NULL,
.func = &console_status,
},
{
.command = "exit",
.help = "exit from application",
.hint = NULL,
.func = &console_exit,
}};
// Set mount point to the application and list files in BASE_PATH by filesystem
// API
static void mount(void) {
ESP_LOGI(TAG, "Mount storage...");
ESP_ERROR_CHECK(tinyusb_msc_set_storage_mount_point(
storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_APP));
// List all the files in this directory
ESP_LOGI(TAG, "\nls command output:");
struct dirent* d;
DIR* dh = opendir(BASE_PATH);
if (!dh) {
if (errno == ENOENT) {
// If the directory is not found
ESP_LOGE(TAG, "Directory doesn't exist %s", BASE_PATH);
} else {
// If the directory is not readable then throw error and exit
ESP_LOGE(TAG, "Unable to read directory %s", BASE_PATH);
}
return;
}
// While the next entry is not readable we will print directory files
while ((d = readdir(dh)) != NULL) {
printf("%s\n", d->d_name);
}
return;
}
// unmount storage
static int console_unmount(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG, "Storage is already exposed");
return -1;
}
ESP_LOGI(TAG, "Unmount storage...");
ESP_ERROR_CHECK(tinyusb_msc_set_storage_mount_point(
storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_USB));
return 0;
}
// read BASE_PATH/README.MD and print its contents
static int console_read(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG,
"Storage exposed over USB. Application can't read from storage.");
return -1;
}
ESP_LOGD(TAG, "read from storage:");
const char* filename = BASE_PATH "/README.MD";
FILE* ptr = fopen(filename, "r");
if (ptr == NULL) {
ESP_LOGE(TAG, "Filename not present - %s", filename);
return -1;
}
char buf[1024];
while (fgets(buf, 1000, ptr) != NULL) {
printf("%s", buf);
}
fclose(ptr);
return 0;
}
// create file BASE_PATH/README.MD if it does not exist
static int console_write(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG,
"storage exposed over USB. Application can't write to storage.");
return -1;
}
ESP_LOGD(TAG, "write to storage:");
const char* filename = BASE_PATH "/README.MD";
FILE* fd = fopen(filename, "r");
if (!fd) {
ESP_LOGW(TAG, "README.MD doesn't exist yet, creating");
fd = fopen(filename, "w");
fprintf(fd,
"Mass Storage Devices are one of the most common USB devices. It "
"use Mass Storage Class (MSC) that allow access to their internal "
"data storage.\n");
fprintf(fd,
"In this example, ESP chip will be recognised by host (PC) as Mass "
"Storage Device.\n");
fprintf(fd,
"Upon connection to USB host (PC), the example application will "
"initialize the storage module and then the storage will be seen "
"as removable device on PC.\n");
fclose(fd);
}
return 0;
}
// Show storage size and sector size
static int console_size(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG, "storage exposed over USB. Application can't access storage");
return -1;
}
uint32_t sec_count;
uint32_t sec_size;
ESP_ERROR_CHECK(tinyusb_msc_get_storage_sector_size(storage_hdl, &sec_size));
ESP_ERROR_CHECK(tinyusb_msc_get_storage_capacity(storage_hdl, &sec_count));
// Calculate size in MB or KB
uint64_t total_bytes = (uint64_t)sec_size * sec_count;
if (total_bytes >= (1024 * 1024)) {
uint64_t total_mb = total_bytes / (1024 * 1024);
printf("Storage Capacity %lluMB\n", total_mb);
} else {
uint64_t total_kb = total_bytes / 1024;
printf("Storage Capacity %lluKB\n", total_kb);
}
return 0;
}
// Show storage status
static int console_status(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
printf("storage exposed over USB: %s\n",
(mp == TINYUSB_MSC_STORAGE_MOUNT_USB) ? "Yes" : "No");
return 0;
}
// Exit from application
static int console_exit(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_delete_storage(storage_hdl));
ESP_ERROR_CHECK(tinyusb_driver_uninstall());
xSemaphoreGive(_wait_console_smp);
printf("Application Exit\n");
return 0;
}
/**
* @brief Storage mount changed callback
*
* @param handle Storage handle
* @param event Event information
* @param arg User argument, provided during callback registration
*/
static void storage_mount_changed_cb(tinyusb_msc_storage_handle_t handle,
tinyusb_msc_event_t* event, void* arg) {
switch (event->id) {
case TINYUSB_MSC_EVENT_MOUNT_START:
// Verify that all the files are closed before unmounting
break;
case TINYUSB_MSC_EVENT_MOUNT_COMPLETE:
ESP_LOGI(
TAG, "Storage mounted to application: %s",
(event->mount_point == TINYUSB_MSC_STORAGE_MOUNT_APP) ? "Yes" : "No");
break;
case TINYUSB_MSC_EVENT_MOUNT_FAILED:
case TINYUSB_MSC_EVENT_FORMAT_REQUIRED:
ESP_LOGE(TAG, "Storage mount failed or format required");
break;
default:
break;
}
}
void init_msc(sdmmc_card_t* card) {
ESP_LOGI(TAG, "Initializing storage...");
_wait_console_smp = xSemaphoreCreateBinary();
if (_wait_console_smp == NULL) {
ESP_LOGE(TAG, "Failed to create semaphore");
return;
}
// Initial mount point to APP
tinyusb_msc_storage_config_t storage_cfg = {
.mount_point = TINYUSB_MSC_STORAGE_MOUNT_USB,
.fat_fs =
{
.base_path = NULL, // Use default base path
.config.max_files = 5, // Maximum number of files that can be
// opened simultaneously
.format_flags = 0, // No special format flags
},
};
storage_cfg.medium.card = card;
ESP_ERROR_CHECK(tinyusb_msc_new_storage_sdmmc(&storage_cfg, &storage_hdl));
ESP_ERROR_CHECK(
tinyusb_msc_set_storage_callback(storage_mount_changed_cb, NULL));
// Re-mount to the APP
mount();
ESP_LOGI(TAG, "USB MSC initialization");
tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
tusb_cfg.descriptor.device = &descriptor_config;
tusb_cfg.descriptor.full_speed_config = msc_fs_configuration_desc;
tusb_cfg.descriptor.string = string_desc_arr;
tusb_cfg.descriptor.string_count =
sizeof(string_desc_arr) / sizeof(string_desc_arr[0]);
#if (TUD_OPT_HIGH_SPEED)
tusb_cfg.descriptor.high_speed_config = msc_hs_configuration_desc;
tusb_cfg.descriptor.qualifier = &device_qualifier;
#endif // TUD_OPT_HIGH_SPEED
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
ESP_LOGI(TAG, "USB MSC initialization DONE");
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG, "storage exposed over USB. Application can't access.");
}
#if 1
// Init console based on menuconfig settings
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
repl_config.prompt = PROMPT_STR ">";
repl_config.max_cmdline_length = 64;
esp_console_dev_uart_config_t hw_config =
ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));
for (int count = 0; count < sizeof(cmds) / sizeof(esp_console_cmd_t);
count++) {
ESP_ERROR_CHECK(esp_console_cmd_register(&cmds[count]));
}
ESP_ERROR_CHECK(esp_console_start_repl(repl));
xSemaphoreTake(_wait_console_smp, portMAX_DELAY);
ESP_ERROR_CHECK(esp_console_stop_repl(repl));
vSemaphoreDelete(_wait_console_smp);
#endif
}

View File

@ -49,6 +49,16 @@ CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=2 # 空闲等待时间单位tick
CONFIG_ESP_SYSTEM_PM_IDLE_SLEEP_MODE=1 # 0:无睡眠 1:Light Sleep
CONFIG_FREERTOS_UNICORE=n
CONFIG_FREERTOS_HZ=1000
CONFIG_I2S_ISR_IRAM_SAFE=y
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
CONFIG_ESP32S3_INSTRUCTION_CACHE_16KB=y
CONFIG_ESP32S3_DATA_CACHE_32KB=y
CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y
CONFIG_ESP32S3_DATA_CACHE_LINE_32B=y
#
# ESP Speech Recognition