Temo of while.
This commit is contained in:
parent
4cd1a1cb1e
commit
bda0fd6096
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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 ".")
|
||||
322
main/app_sr.c
322
main/app_sr.c
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void app_sr_init(void);
|
||||
515
main/app_ui.c
515
main/app_ui.c
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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,
|
||||
®_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,
|
||||
®_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;
|
||||
}
|
||||
|
||||
/********************* 音频 ↑ *************************/
|
||||
/***********************************************************/
|
||||
|
|
@ -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);
|
||||
|
||||
/********************* 音频 ↑ *************************/
|
||||
/***********************************************************/
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
711
main/main.c
711
main/main.c
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
416
main/usb_msc.c
416
main/usb_msc.c
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue