inite.
This commit is contained in:
commit
8a2b1dd2e9
|
|
@ -0,0 +1,4 @@
|
|||
build
|
||||
managed_components
|
||||
*.lock
|
||||
sdkconfig
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# For more information about build system see
|
||||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(speech_recognition)
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
idf_component_register(SRCS "img_bilibili120.c" "app_sr.c" "esp32_s3_szp.c" "main.c" "app_ui.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
spiffs_create_partition_image(storage ../spiffs FLASH_IN_PROJECT)
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
|
||||
#include "app_sr.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_wn_iface.h"
|
||||
#include "esp_wn_models.h"
|
||||
#include "esp_afe_sr_iface.h"
|
||||
#include "esp_afe_sr_models.h"
|
||||
#include "esp_mn_iface.h"
|
||||
#include "esp_mn_models.h"
|
||||
#include "esp_mn_speech_commands.h"
|
||||
#include "model_path.h"
|
||||
#include "esp32_s3_szp.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_process_sdkconfig.h"
|
||||
#include "audio_player.h"
|
||||
#include "app_ui.h"
|
||||
|
||||
static const char *TAG = "app_sr";
|
||||
|
||||
srmodel_list_t *models = NULL;
|
||||
static esp_afe_sr_iface_t *afe_handle = NULL;
|
||||
static esp_afe_sr_data_t *afe_data = NULL;
|
||||
|
||||
int detect_flag = 0;
|
||||
static volatile int task_flag = 0;
|
||||
|
||||
void feed_Task(void *arg)
|
||||
{
|
||||
esp_afe_sr_data_t *afe_data = arg; // 获取参数
|
||||
int audio_chunksize = afe_handle->get_feed_chunksize(afe_data); // 获取帧长度
|
||||
int nch = afe_handle->get_channel_num(afe_data); // 获取声道数
|
||||
int feed_channel = bsp_get_feed_channel(); // 获取ADC输入通道数
|
||||
assert(nch <= feed_channel);
|
||||
int16_t *i2s_buff = heap_caps_malloc(audio_chunksize * sizeof(int16_t) * feed_channel, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); // 分配获取I2S数据的缓存大小
|
||||
assert(i2s_buff);
|
||||
|
||||
while (task_flag) {
|
||||
bsp_get_feed_data(false, i2s_buff, audio_chunksize * sizeof(int16_t) * feed_channel); // 获取I2S数据
|
||||
|
||||
afe_handle->feed(afe_data, i2s_buff); // 把获取到的I2S数据输入给afe_data
|
||||
}
|
||||
if (i2s_buff) {
|
||||
free(i2s_buff);
|
||||
i2s_buff = NULL;
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void detect_Task(void *arg)
|
||||
{
|
||||
esp_afe_sr_data_t *afe_data = arg; // 接收参数
|
||||
int afe_chunksize = afe_handle->get_fetch_chunksize(afe_data); // 获取fetch帧长度
|
||||
char *mn_name = esp_srmodel_filter(models, ESP_MN_PREFIX, ESP_MN_CHINESE); // 初始化命令词模型
|
||||
printf("multinet:%s\n", mn_name); // 打印命令词模型名称
|
||||
esp_mn_iface_t *multinet = esp_mn_handle_from_name(mn_name);
|
||||
model_iface_data_t *model_data = multinet->create(mn_name, 6000); // 设置唤醒后等待事件 6000代表6000毫秒
|
||||
esp_mn_commands_clear(); // 清除当前的命令词列表
|
||||
esp_mn_commands_add(1, "bo fang yin yue"); // 播放音乐
|
||||
esp_mn_commands_add(2, "zan ting"); // 暂停
|
||||
esp_mn_commands_add(3, "ji xu"); // 继续
|
||||
esp_mn_commands_add(4, "shang yi shou"); // 上一首
|
||||
esp_mn_commands_add(5, "xia yi shou"); // 下一首
|
||||
esp_mn_commands_add(6, "sheng yin da yi dian"); // 声音大一点
|
||||
esp_mn_commands_add(7, "sheng yin xiao yi dian"); // 声音小一点
|
||||
esp_mn_commands_update(); // 更新命令词
|
||||
int mu_chunksize = multinet->get_samp_chunksize(model_data); // 获取samp帧长度
|
||||
assert(mu_chunksize == afe_chunksize);
|
||||
|
||||
// 打印所有的命令
|
||||
multinet->print_active_speech_commands(model_data);
|
||||
printf("------------detect start------------\n");
|
||||
|
||||
while (task_flag) {
|
||||
afe_fetch_result_t* res = afe_handle->fetch(afe_data); // 获取模型输出结果
|
||||
if (!res || res->ret_value == ESP_FAIL) {
|
||||
printf("fetch error!\n");
|
||||
break;
|
||||
}
|
||||
if (res->wakeup_state == WAKENET_DETECTED) {
|
||||
printf("WAKEWORD DETECTED\n");
|
||||
multinet->clean(model_data); // clean all status of multinet
|
||||
} else if (res->wakeup_state == WAKENET_CHANNEL_VERIFIED) { // 检测到唤醒词
|
||||
// play_voice = -1;
|
||||
afe_handle->disable_wakenet(afe_data); // 关闭唤醒词识别
|
||||
detect_flag = 1; // 标记已检测到唤醒词
|
||||
ai_gui_in(); // AI人出现
|
||||
printf("AFE_FETCH_CHANNEL_VERIFIED, channel index: %d\n", res->trigger_channel_id);
|
||||
}
|
||||
|
||||
if (detect_flag == 1) {
|
||||
esp_mn_state_t mn_state = multinet->detect(model_data, res->data); // 检测命令词
|
||||
|
||||
if (mn_state == ESP_MN_STATE_DETECTING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mn_state == ESP_MN_STATE_DETECTED) { // 已检测到命令词
|
||||
esp_mn_results_t *mn_result = multinet->get_results(model_data); // 获取检测词结果
|
||||
for (int i = 0; i < mn_result->num; i++) { // 打印获取到的命令词
|
||||
printf("TOP %d, command_id: %d, phrase_id: %d, string:%s prob: %f\n",
|
||||
i+1, mn_result->command_id[i], mn_result->phrase_id[i], mn_result->string, mn_result->prob[i]);
|
||||
}
|
||||
// 根据命令词 执行相应动作
|
||||
switch (mn_result->command_id[0])
|
||||
{
|
||||
case 1: // bo fang yin yue 播放音乐
|
||||
ai_play();
|
||||
break;
|
||||
case 2: // zan ting 暂停
|
||||
ai_pause();
|
||||
break;
|
||||
case 3: // ji xu 继续
|
||||
ai_resume();
|
||||
break;
|
||||
case 4: // shang yi shou 上一首
|
||||
ai_prev_music();
|
||||
break;
|
||||
case 5: // xia yi shou 下一首
|
||||
ai_next_music();
|
||||
break;
|
||||
case 6: // sheng yin da yi dian 声音大一点
|
||||
ai_volume_up();
|
||||
break;
|
||||
case 7: // sheng yin xiao yi dian 声音小一点
|
||||
ai_volume_down();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
printf("\n-----------listening-----------\n");
|
||||
}
|
||||
|
||||
if (mn_state == ESP_MN_STATE_TIMEOUT) { // 达到最大检测命令词时间
|
||||
esp_mn_results_t *mn_result = multinet->get_results(model_data);
|
||||
printf("timeout, string:%s\n", mn_result->string);
|
||||
afe_handle->enable_wakenet(afe_data); // 重新打开唤醒词识别
|
||||
detect_flag = 0; // 清除标记
|
||||
printf("\n-----------awaits to be waken up-----------\n");
|
||||
ai_gui_out(); // AI人退出
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (model_data) {
|
||||
multinet->destroy(model_data);
|
||||
model_data = NULL;
|
||||
}
|
||||
printf("detect exit\n");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_sr_init(void)
|
||||
{
|
||||
models = esp_srmodel_init("model"); // 获取模型 名称“model”和分区表中装载模型的名称一致
|
||||
|
||||
afe_handle = (esp_afe_sr_iface_t *)&ESP_AFE_SR_HANDLE; // 先配置afe句柄 随后才可以调用afe接口
|
||||
afe_config_t afe_config = AFE_CONFIG_DEFAULT(); // 配置afe
|
||||
|
||||
afe_config.wakenet_model_name = esp_srmodel_filter(models, ESP_WN_PREFIX, NULL); // 配置唤醒模型 必须在create_from_config之前配置
|
||||
afe_data = afe_handle->create_from_config(&afe_config); // 创建afe_data
|
||||
ESP_LOGI(TAG, "wakenet:%s", afe_config.wakenet_model_name); // 打印唤醒名称
|
||||
|
||||
task_flag = 1;
|
||||
xTaskCreatePinnedToCore(&detect_Task, "detect", 8 * 1024, (void*)afe_data, 5, NULL, 1);
|
||||
xTaskCreatePinnedToCore(&feed_Task, "feed", 8 * 1024, (void*)afe_data, 5, NULL, 0);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void app_sr_init(void);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,527 @@
|
|||
#include "app_ui.h"
|
||||
#include "audio_player.h"
|
||||
#include "esp32_s3_szp.h"
|
||||
#include "file_iterator.h"
|
||||
#include "string.h"
|
||||
#include <dirent.h>
|
||||
|
||||
static const char *TAG = "app_ui";
|
||||
|
||||
static audio_player_config_t player_config = {0};
|
||||
static uint8_t g_sys_volume = VOLUME_DEFAULT;
|
||||
static file_iterator_instance_t *file_iterator = NULL;
|
||||
|
||||
lv_obj_t *music_list;
|
||||
lv_obj_t *label_play_pause;
|
||||
lv_obj_t *btn_play_pause;
|
||||
lv_obj_t *volume_slider;
|
||||
|
||||
// 播放指定序号的音乐
|
||||
static void play_index(int index)
|
||||
{
|
||||
ESP_LOGI(TAG, "play_index(%d)", index);
|
||||
|
||||
char filename[128];
|
||||
int retval = file_iterator_get_full_path_from_index(file_iterator, index, filename, sizeof(filename));
|
||||
if (retval == 0) {
|
||||
ESP_LOGE(TAG, "unable to retrieve filename");
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
if (fp) {
|
||||
ESP_LOGI(TAG, "Playing '%s'", filename);
|
||||
audio_player_play(fp);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "unable to open index %d, filename '%s'", index, filename);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置声音处理函数
|
||||
static esp_err_t _audio_player_mute_fn(AUDIO_PLAYER_MUTE_SETTING setting)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
// 获取当前音量
|
||||
// uint8_t volume = get_sys_volume();
|
||||
// 判断是否需要静音
|
||||
bsp_codec_mute_set(setting == AUDIO_PLAYER_MUTE ? true : false);
|
||||
// 如果不是静音 设置音量
|
||||
if (setting == AUDIO_PLAYER_UNMUTE) {
|
||||
bsp_codec_volume_set(g_sys_volume, NULL);
|
||||
}
|
||||
ret = ESP_OK;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 播放音乐函数 播放音乐的时候 会不断进入
|
||||
static esp_err_t _audio_player_write_fn(void *audio_buffer, size_t len, size_t *bytes_written, uint32_t timeout_ms)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
ret = bsp_i2s_write(audio_buffer, len, bytes_written, timeout_ms);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 设置采样率 播放的时候进入一次
|
||||
static esp_err_t _audio_player_std_clock(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
// ret = bsp_speaker_set_fs(rate, bits_cfg, ch);
|
||||
// ret = bsp_codec_set_fs(rate, bits_cfg, ch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 回调函数 播放器每次动作都会进入
|
||||
static void _audio_player_callback(audio_player_cb_ctx_t *ctx)
|
||||
{
|
||||
ESP_LOGI(TAG, "ctx->audio_event = %d", ctx->audio_event);
|
||||
switch (ctx->audio_event) {
|
||||
case AUDIO_PLAYER_CALLBACK_EVENT_IDLE: { // 播放完一首歌 进入这个case
|
||||
ESP_LOGI(TAG, "AUDIO_PLAYER_REQUEST_IDLE");
|
||||
// 指向下一首歌
|
||||
file_iterator_next(file_iterator);
|
||||
int index = file_iterator_get_index(file_iterator);
|
||||
ESP_LOGI(TAG, "playing index '%d'", index);
|
||||
play_index(index);
|
||||
// 修改当前播放的音乐名称
|
||||
lvgl_port_lock(0);
|
||||
lv_dropdown_set_selected(music_list, index);
|
||||
lv_obj_t *label_title = (lv_obj_t *) music_list->user_data;
|
||||
lv_label_set_text_static(label_title, file_iterator_get_name_from_index(file_iterator, index));
|
||||
lvgl_port_unlock();
|
||||
break;
|
||||
}
|
||||
case AUDIO_PLAYER_CALLBACK_EVENT_PLAYING: // 正在播放音乐
|
||||
ESP_LOGI(TAG, "AUDIO_PLAYER_REQUEST_PLAY");
|
||||
pa_en(1); // 打开音频功放
|
||||
break;
|
||||
case AUDIO_PLAYER_CALLBACK_EVENT_PAUSE: // 正在暂停音乐
|
||||
ESP_LOGI(TAG, "AUDIO_PLAYER_REQUEST_PAUSE");
|
||||
pa_en(0); // 关闭音频功放
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// mp3播放器初始化
|
||||
void mp3_player_init(void)
|
||||
{
|
||||
// 获取文件信息
|
||||
file_iterator = file_iterator_new(SPIFFS_BASE);
|
||||
assert(file_iterator != NULL);
|
||||
|
||||
// 初始化音频播放
|
||||
player_config.mute_fn = _audio_player_mute_fn;
|
||||
player_config.write_fn = _audio_player_write_fn;
|
||||
player_config.clk_set_fn = _audio_player_std_clock;
|
||||
player_config.priority = 6;
|
||||
player_config.coreID = 1;
|
||||
|
||||
ESP_ERROR_CHECK(audio_player_new(player_config));
|
||||
ESP_ERROR_CHECK(audio_player_callback_register(_audio_player_callback, NULL));
|
||||
|
||||
// 显示界面
|
||||
music_ui();
|
||||
}
|
||||
|
||||
|
||||
// 按钮样式相关定义
|
||||
typedef struct {
|
||||
lv_style_t style_bg;
|
||||
lv_style_t style_focus_no_outline;
|
||||
} button_style_t;
|
||||
|
||||
static button_style_t g_btn_styles;
|
||||
|
||||
button_style_t *ui_button_styles(void)
|
||||
{
|
||||
return &g_btn_styles;
|
||||
}
|
||||
|
||||
// 按钮样式初始化
|
||||
static void ui_button_style_init(void)
|
||||
{
|
||||
/*Init the style for the default state*/
|
||||
lv_style_init(&g_btn_styles.style_focus_no_outline);
|
||||
lv_style_set_outline_width(&g_btn_styles.style_focus_no_outline, 0);
|
||||
|
||||
lv_style_init(&g_btn_styles.style_bg);
|
||||
lv_style_set_bg_opa(&g_btn_styles.style_bg, LV_OPA_100);
|
||||
lv_style_set_bg_color(&g_btn_styles.style_bg, lv_color_make(255, 255, 255));
|
||||
lv_style_set_shadow_width(&g_btn_styles.style_bg, 0);
|
||||
}
|
||||
|
||||
// 播放暂停按钮 事件处理函数
|
||||
static void btn_play_pause_cb(lv_event_t *event)
|
||||
{
|
||||
lv_obj_t *btn = lv_event_get_target(event);
|
||||
lv_obj_t *lab = (lv_obj_t *) btn->user_data;
|
||||
|
||||
audio_player_state_t state = audio_player_get_state();
|
||||
printf("state=%d\n", state);
|
||||
if(state == AUDIO_PLAYER_STATE_IDLE){
|
||||
lvgl_port_lock(0);
|
||||
lv_label_set_text_static(lab, LV_SYMBOL_PAUSE);
|
||||
lvgl_port_unlock();
|
||||
int index = file_iterator_get_index(file_iterator);
|
||||
ESP_LOGI(TAG, "playing index '%d'", index);
|
||||
play_index(index);
|
||||
}else if (state == AUDIO_PLAYER_STATE_PAUSE) {
|
||||
lvgl_port_lock(0);
|
||||
lv_label_set_text_static(lab, LV_SYMBOL_PAUSE);
|
||||
lvgl_port_unlock();
|
||||
audio_player_resume();
|
||||
} else if (state == AUDIO_PLAYER_STATE_PLAYING) {
|
||||
lvgl_port_lock(0);
|
||||
lv_label_set_text_static(lab, LV_SYMBOL_PLAY);
|
||||
lvgl_port_unlock();
|
||||
audio_player_pause();
|
||||
}
|
||||
}
|
||||
|
||||
// 上一首 下一首 按键事件处理函数
|
||||
static void btn_prev_next_cb(lv_event_t *event)
|
||||
{
|
||||
bool is_next = (bool) event->user_data;
|
||||
|
||||
if (is_next) {
|
||||
ESP_LOGI(TAG, "btn next");
|
||||
file_iterator_next(file_iterator);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "btn prev");
|
||||
file_iterator_prev(file_iterator);
|
||||
}
|
||||
// 修改当前的音乐名称
|
||||
int index = file_iterator_get_index(file_iterator);
|
||||
lvgl_port_lock(0);
|
||||
lv_dropdown_set_selected(music_list, index);
|
||||
lv_obj_t *label_title = (lv_obj_t *) music_list->user_data;
|
||||
lv_label_set_text_static(label_title, file_iterator_get_name_from_index(file_iterator, index));
|
||||
lvgl_port_unlock();
|
||||
// 执行音乐事件
|
||||
audio_player_state_t state = audio_player_get_state();
|
||||
printf("prev_next_state=%d\n", state);
|
||||
if (state == AUDIO_PLAYER_STATE_IDLE) {
|
||||
// Nothing to do
|
||||
}else if (state == AUDIO_PLAYER_STATE_PAUSE){ // 如果当前正在暂停歌曲
|
||||
ESP_LOGI(TAG, "playing index '%d'", index);
|
||||
play_index(index);
|
||||
audio_player_pause();
|
||||
} else if (state == AUDIO_PLAYER_STATE_PLAYING) { // 如果当前正在播放歌曲
|
||||
// 播放歌曲
|
||||
ESP_LOGI(TAG, "playing index '%d'", index);
|
||||
play_index(index);
|
||||
}
|
||||
}
|
||||
|
||||
// 音量调节滑动条 事件处理函数
|
||||
static void volume_slider_cb(lv_event_t *event)
|
||||
{
|
||||
lv_obj_t *slider = lv_event_get_target(event);
|
||||
int volume = lv_slider_get_value(slider); // 获取slider的值
|
||||
bsp_codec_volume_set(volume, NULL); // 设置声音大小
|
||||
g_sys_volume = volume; // 把声音赋值给g_sys_volume保存
|
||||
ESP_LOGI(TAG, "volume '%d'", volume);
|
||||
}
|
||||
|
||||
// 音乐列表 点击事件处理函数
|
||||
static void music_list_cb(lv_event_t *event)
|
||||
{
|
||||
lv_obj_t *label_title = (lv_obj_t *) music_list->user_data;
|
||||
|
||||
uint16_t index = lv_dropdown_get_selected(music_list);
|
||||
ESP_LOGI(TAG, "switching index to '%d'", index);
|
||||
file_iterator_set_index(file_iterator, index);
|
||||
lvgl_port_lock(0);
|
||||
lv_label_set_text_static(label_title, file_iterator_get_name_from_index(file_iterator, index));
|
||||
lvgl_port_unlock();
|
||||
|
||||
audio_player_state_t state = audio_player_get_state();
|
||||
if (state == AUDIO_PLAYER_STATE_PAUSE){ // 如果当前正在暂停歌曲
|
||||
play_index(index);
|
||||
audio_player_pause();
|
||||
} else if (state == AUDIO_PLAYER_STATE_PLAYING) { // 如果当前正在播放歌曲
|
||||
play_index(index);
|
||||
}
|
||||
}
|
||||
|
||||
// 音乐名称加入列表
|
||||
static void build_file_list(lv_obj_t *music_list)
|
||||
{
|
||||
lv_obj_t *label_title = (lv_obj_t *) music_list->user_data;
|
||||
|
||||
lvgl_port_lock(0);
|
||||
lv_dropdown_clear_options(music_list);
|
||||
lvgl_port_unlock();
|
||||
|
||||
for(size_t i = 0; i<file_iterator->count; i++)
|
||||
{
|
||||
const char *file_name = file_iterator_get_name_from_index(file_iterator, i);
|
||||
if (NULL != file_name) {
|
||||
lvgl_port_lock(0);
|
||||
lv_dropdown_add_option(music_list, file_name, i); // 添加音乐名称到列表中
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
}
|
||||
lvgl_port_lock(0);
|
||||
lv_dropdown_set_selected(music_list, 0); // 选择列表中的第一个
|
||||
lv_label_set_text_static(label_title, file_iterator_get_name_from_index(file_iterator, 0)); // 显示list中第一个音乐的名称
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
// 播放器界面初始化
|
||||
void music_ui(void)
|
||||
{
|
||||
lvgl_port_lock(0);
|
||||
|
||||
ui_button_style_init();// 初始化按键风格
|
||||
|
||||
/* 创建播放暂停控制按键 */
|
||||
btn_play_pause = lv_btn_create(lv_scr_act());
|
||||
lv_obj_align(btn_play_pause, LV_ALIGN_CENTER, 0, 40);
|
||||
lv_obj_set_size(btn_play_pause, 50, 50);
|
||||
lv_obj_set_style_radius(btn_play_pause, 25, LV_STATE_DEFAULT);
|
||||
lv_obj_add_flag(btn_play_pause, LV_OBJ_FLAG_CHECKABLE);
|
||||
|
||||
lv_obj_add_style(btn_play_pause, &ui_button_styles()->style_focus_no_outline, LV_STATE_FOCUS_KEY);
|
||||
lv_obj_add_style(btn_play_pause, &ui_button_styles()->style_focus_no_outline, LV_STATE_FOCUSED);
|
||||
|
||||
label_play_pause = lv_label_create(btn_play_pause);
|
||||
lv_label_set_text_static(label_play_pause, LV_SYMBOL_PLAY);
|
||||
lv_obj_center(label_play_pause);
|
||||
|
||||
lv_obj_set_user_data(btn_play_pause, (void *) label_play_pause);
|
||||
lv_obj_add_event_cb(btn_play_pause, btn_play_pause_cb, LV_EVENT_VALUE_CHANGED, NULL);
|
||||
|
||||
/* 创建上一首控制按键 */
|
||||
lv_obj_t *btn_play_prev = lv_btn_create(lv_scr_act());
|
||||
lv_obj_set_size(btn_play_prev, 50, 50);
|
||||
lv_obj_set_style_radius(btn_play_prev, 25, LV_STATE_DEFAULT);
|
||||
lv_obj_clear_flag(btn_play_prev, LV_OBJ_FLAG_CHECKABLE);
|
||||
lv_obj_align_to(btn_play_prev, btn_play_pause, LV_ALIGN_OUT_LEFT_MID, -40, 0);
|
||||
|
||||
lv_obj_add_style(btn_play_prev, &ui_button_styles()->style_focus_no_outline, LV_STATE_FOCUS_KEY);
|
||||
lv_obj_add_style(btn_play_prev, &ui_button_styles()->style_focus_no_outline, LV_STATE_FOCUSED);
|
||||
lv_obj_add_style(btn_play_prev, &ui_button_styles()->style_bg, LV_STATE_FOCUS_KEY);
|
||||
lv_obj_add_style(btn_play_prev, &ui_button_styles()->style_bg, LV_STATE_FOCUSED);
|
||||
lv_obj_add_style(btn_play_prev, &ui_button_styles()->style_bg, LV_STATE_DEFAULT);
|
||||
|
||||
lv_obj_t *label_prev = lv_label_create(btn_play_prev);
|
||||
lv_label_set_text_static(label_prev, LV_SYMBOL_PREV);
|
||||
lv_obj_set_style_text_font(label_prev, &lv_font_montserrat_24, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_text_color(label_prev, lv_color_make(0, 0, 0), LV_STATE_DEFAULT);
|
||||
lv_obj_center(label_prev);
|
||||
lv_obj_set_user_data(btn_play_prev, (void *) label_prev);
|
||||
lv_obj_add_event_cb(btn_play_prev, btn_prev_next_cb, LV_EVENT_CLICKED, (void *) false);
|
||||
|
||||
/* 创建下一首控制按键 */
|
||||
lv_obj_t *btn_play_next = lv_btn_create(lv_scr_act());
|
||||
lv_obj_set_size(btn_play_next, 50, 50);
|
||||
lv_obj_set_style_radius(btn_play_next, 25, LV_STATE_DEFAULT);
|
||||
lv_obj_clear_flag(btn_play_next, LV_OBJ_FLAG_CHECKABLE);
|
||||
lv_obj_align_to(btn_play_next, btn_play_pause, LV_ALIGN_OUT_RIGHT_MID, 40, 0);
|
||||
|
||||
lv_obj_add_style(btn_play_next, &ui_button_styles()->style_focus_no_outline, LV_STATE_FOCUS_KEY);
|
||||
lv_obj_add_style(btn_play_next, &ui_button_styles()->style_focus_no_outline, LV_STATE_FOCUSED);
|
||||
lv_obj_add_style(btn_play_next, &ui_button_styles()->style_bg, LV_STATE_FOCUS_KEY);
|
||||
lv_obj_add_style(btn_play_next, &ui_button_styles()->style_bg, LV_STATE_FOCUSED);
|
||||
lv_obj_add_style(btn_play_next, &ui_button_styles()->style_bg, LV_STATE_DEFAULT);
|
||||
|
||||
lv_obj_t *label_next = lv_label_create(btn_play_next);
|
||||
lv_label_set_text_static(label_next, LV_SYMBOL_NEXT);
|
||||
lv_obj_set_style_text_font(label_next, &lv_font_montserrat_24, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_text_color(label_next, lv_color_make(0, 0, 0), LV_STATE_DEFAULT);
|
||||
lv_obj_center(label_next);
|
||||
lv_obj_set_user_data(btn_play_next, (void *) label_next);
|
||||
lv_obj_add_event_cb(btn_play_next, btn_prev_next_cb, LV_EVENT_CLICKED, (void *) true);
|
||||
|
||||
/* 创建声音调节滑动条 */
|
||||
volume_slider = lv_slider_create(lv_scr_act());
|
||||
lv_obj_set_size(volume_slider, 200, 10);
|
||||
lv_obj_set_ext_click_area(volume_slider, 15);
|
||||
lv_obj_align(volume_slider, LV_ALIGN_BOTTOM_MID, 0, -20);
|
||||
lv_slider_set_range(volume_slider, 0, 100);
|
||||
lv_slider_set_value(volume_slider, g_sys_volume, LV_ANIM_ON);
|
||||
lv_obj_add_event_cb(volume_slider, volume_slider_cb, LV_EVENT_VALUE_CHANGED, NULL);
|
||||
|
||||
lv_obj_t *lab_vol_min = lv_label_create(lv_scr_act());
|
||||
lv_label_set_text_static(lab_vol_min, LV_SYMBOL_VOLUME_MID);
|
||||
lv_obj_set_style_text_font(lab_vol_min, &lv_font_montserrat_20, LV_STATE_DEFAULT);
|
||||
lv_obj_align_to(lab_vol_min, volume_slider, LV_ALIGN_OUT_LEFT_MID, -10, 0);
|
||||
|
||||
lv_obj_t *lab_vol_max = lv_label_create(lv_scr_act());
|
||||
lv_label_set_text_static(lab_vol_max, LV_SYMBOL_VOLUME_MAX);
|
||||
lv_obj_set_style_text_font(lab_vol_max, &lv_font_montserrat_20, LV_STATE_DEFAULT);
|
||||
lv_obj_align_to(lab_vol_max, volume_slider, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
|
||||
|
||||
/* 创建音乐标题 */
|
||||
lv_obj_t *lab_title = lv_label_create(lv_scr_act());
|
||||
lv_obj_set_user_data(lab_title, (void *) btn_play_pause);
|
||||
lv_label_set_text_static(lab_title, "Scanning Files...");
|
||||
lv_obj_set_style_text_font(lab_title, &lv_font_montserrat_32, LV_STATE_DEFAULT);
|
||||
lv_obj_align(lab_title, LV_ALIGN_TOP_MID, 0, 20);
|
||||
|
||||
/* 创建音乐列表 */
|
||||
music_list = lv_dropdown_create(lv_scr_act());
|
||||
lv_dropdown_clear_options(music_list);
|
||||
lv_dropdown_set_options_static(music_list, "Scanning...");
|
||||
lv_obj_set_style_text_font(music_list, &lv_font_montserrat_20, LV_STATE_ANY);
|
||||
lv_obj_set_width(music_list, 200);
|
||||
lv_obj_align_to(music_list, lab_title, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
|
||||
lv_obj_set_user_data(music_list, (void *) lab_title);
|
||||
lv_obj_add_event_cb(music_list, music_list_cb, LV_EVENT_VALUE_CHANGED, NULL);
|
||||
|
||||
build_file_list(music_list);
|
||||
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
|
||||
// AI人动画
|
||||
LV_IMG_DECLARE(img_bilibili120);
|
||||
|
||||
lv_obj_t *gif_start;
|
||||
|
||||
// AI人出现在屏幕
|
||||
void ai_gui_in(void)
|
||||
{
|
||||
lvgl_port_lock(0);
|
||||
gif_start = lv_gif_create(lv_scr_act());
|
||||
lv_gif_set_src(gif_start, &img_bilibili120);
|
||||
lv_obj_align(gif_start, LV_ALIGN_CENTER, 0, 0);
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
// AI人退出屏幕
|
||||
void ai_gui_out(void)
|
||||
{
|
||||
lvgl_port_lock(0);
|
||||
lv_obj_del(gif_start);
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
|
||||
// AI播放音乐
|
||||
void ai_play(void)
|
||||
{
|
||||
int index = file_iterator_get_index(file_iterator);
|
||||
ESP_LOGI(TAG, "playing index '%d'", index);
|
||||
play_index(index);
|
||||
lvgl_port_lock(0);
|
||||
lv_label_set_text_static(label_play_pause, LV_SYMBOL_PAUSE); // 显示图标
|
||||
lv_obj_add_state(btn_play_pause, LV_STATE_CHECKED); // 按键设置为选中状态
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
// AI暂停
|
||||
void ai_pause(void)
|
||||
{
|
||||
audio_player_pause();
|
||||
lvgl_port_lock(0);
|
||||
lv_label_set_text_static(label_play_pause, LV_SYMBOL_PLAY); // 显示图标
|
||||
lv_obj_clear_state(btn_play_pause, LV_STATE_CHECKED); // 清除按键的选中状态
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
// AI继续
|
||||
void ai_resume(void)
|
||||
{
|
||||
audio_player_resume();
|
||||
lvgl_port_lock(0);
|
||||
lv_label_set_text_static(label_play_pause, LV_SYMBOL_PAUSE); // 显示图标
|
||||
lv_obj_add_state(btn_play_pause, LV_STATE_CHECKED); // 按键设置为选中状态
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
// AI上一首
|
||||
void ai_prev_music(void)
|
||||
{
|
||||
// 指向上一首音乐
|
||||
file_iterator_prev(file_iterator);
|
||||
// 修改当前的音乐名称
|
||||
int index = file_iterator_get_index(file_iterator);
|
||||
lvgl_port_lock(0);
|
||||
lv_dropdown_set_selected(music_list, index);
|
||||
lv_obj_t *label_title = (lv_obj_t *) music_list->user_data;
|
||||
lv_label_set_text_static(label_title, file_iterator_get_name_from_index(file_iterator, index));
|
||||
lvgl_port_unlock();
|
||||
// 执行音乐事件
|
||||
audio_player_state_t state = audio_player_get_state();
|
||||
printf("prev_next_state=%d\n", state);
|
||||
if (state == AUDIO_PLAYER_STATE_IDLE) {
|
||||
// Nothing to do
|
||||
}else if (state == AUDIO_PLAYER_STATE_PAUSE){ // 如果当前正在暂停歌曲
|
||||
ESP_LOGI(TAG, "playing index '%d'", index);
|
||||
play_index(index);
|
||||
audio_player_pause();
|
||||
} else if (state == AUDIO_PLAYER_STATE_PLAYING) { // 如果当前正在播放歌曲
|
||||
// 播放歌曲
|
||||
ESP_LOGI(TAG, "playing index '%d'", index);
|
||||
play_index(index);
|
||||
}
|
||||
}
|
||||
|
||||
// AI下一首
|
||||
void ai_next_music(void)
|
||||
{
|
||||
// 指向上一首音乐
|
||||
file_iterator_next(file_iterator);
|
||||
// 修改当前的音乐名称
|
||||
int index = file_iterator_get_index(file_iterator);
|
||||
lvgl_port_lock(0);
|
||||
lv_dropdown_set_selected(music_list, index);
|
||||
lv_obj_t *label_title = (lv_obj_t *) music_list->user_data;
|
||||
lv_label_set_text_static(label_title, file_iterator_get_name_from_index(file_iterator, index));
|
||||
lvgl_port_unlock();
|
||||
// 执行音乐事件
|
||||
audio_player_state_t state = audio_player_get_state();
|
||||
printf("prev_next_state=%d\n", state);
|
||||
if (state == AUDIO_PLAYER_STATE_IDLE) {
|
||||
// Nothing to do
|
||||
}else if (state == AUDIO_PLAYER_STATE_PAUSE){ // 如果当前正在暂停歌曲
|
||||
ESP_LOGI(TAG, "playing index '%d'", index);
|
||||
play_index(index);
|
||||
audio_player_pause();
|
||||
} else if (state == AUDIO_PLAYER_STATE_PLAYING) { // 如果当前正在播放歌曲
|
||||
// 播放歌曲
|
||||
ESP_LOGI(TAG, "playing index '%d'", index);
|
||||
play_index(index);
|
||||
}
|
||||
}
|
||||
|
||||
// AI声音大一点
|
||||
void ai_volume_up(void)
|
||||
{
|
||||
if (g_sys_volume < 100){
|
||||
g_sys_volume = g_sys_volume + 5;
|
||||
if (g_sys_volume > 100){
|
||||
g_sys_volume = 100;
|
||||
}
|
||||
bsp_codec_volume_set(g_sys_volume, NULL); // 设置声音大小
|
||||
lvgl_port_lock(0);
|
||||
lv_slider_set_value(volume_slider, g_sys_volume, LV_ANIM_ON); // 设置slider
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
ESP_LOGI(TAG, "volume '%d'", g_sys_volume);
|
||||
}
|
||||
|
||||
// AI声音小一点
|
||||
void ai_volume_down(void)
|
||||
{
|
||||
if (g_sys_volume > 0){
|
||||
if (g_sys_volume < 5){
|
||||
g_sys_volume = 0;
|
||||
}else{
|
||||
g_sys_volume = g_sys_volume - 5;
|
||||
}
|
||||
bsp_codec_volume_set(g_sys_volume, NULL); // 设置声音大小
|
||||
lvgl_port_lock(0);
|
||||
lv_slider_set_value(volume_slider, g_sys_volume, LV_ANIM_ON); // 设置slider
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
ESP_LOGI(TAG, "volume '%d'", g_sys_volume);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
void mp3_player_init(void);
|
||||
void music_ui(void);
|
||||
|
||||
void ai_gui_in(void);
|
||||
void ai_gui_out(void);
|
||||
|
||||
void ai_play(void);
|
||||
void ai_pause(void);
|
||||
void ai_resume(void);
|
||||
void ai_prev_music(void);
|
||||
void ai_next_music(void);
|
||||
void ai_volume_up(void);
|
||||
void ai_volume_down(void);
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,809 @@
|
|||
#include <stdio.h>
|
||||
#include "esp32_s3_szp.h"
|
||||
|
||||
static const char *TAG = "esp32_s3_szp";
|
||||
|
||||
/******************************************************************************/
|
||||
/*************************** I2C ↓ *******************************************/
|
||||
esp_err_t bsp_i2c_init(void)
|
||||
{
|
||||
i2c_config_t i2c_conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = BSP_I2C_SDA,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = BSP_I2C_SCL,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = BSP_I2C_FREQ_HZ
|
||||
};
|
||||
i2c_param_config(BSP_I2C_NUM, &i2c_conf);
|
||||
|
||||
return i2c_driver_install(BSP_I2C_NUM, i2c_conf.mode, 0, 0, 0);
|
||||
}
|
||||
/*************************** I2C ↑ *******************************************/
|
||||
/*******************************************************************************/
|
||||
|
||||
|
||||
/*******************************************************************************/
|
||||
/*************************** 姿态传感器 QMI8658 ↓ ****************************/
|
||||
|
||||
// 读取QMI8658寄存器的值
|
||||
esp_err_t qmi8658_register_read(uint8_t reg_addr, uint8_t *data, size_t len)
|
||||
{
|
||||
return i2c_master_write_read_device(BSP_I2C_NUM, QMI8658_SENSOR_ADDR, ®_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;
|
||||
}
|
||||
|
||||
/********************* 音频 ↑ *************************/
|
||||
/***********************************************************/
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,289 @@
|
|||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include "math.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_lcd_types.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_touch_ft5x06.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
#include "esp_codec_dev.h"
|
||||
#include "esp_codec_dev_defaults.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/i2s_std.h"
|
||||
|
||||
#include "esp_spiffs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/*************************** I2C ↓ *******************************************/
|
||||
#define BSP_I2C_SDA (GPIO_NUM_1) // SDA引脚
|
||||
#define BSP_I2C_SCL (GPIO_NUM_2) // SCL引脚
|
||||
|
||||
#define BSP_I2C_NUM (0) // I2C外设
|
||||
#define BSP_I2C_FREQ_HZ 100000 // 100kHz
|
||||
|
||||
esp_err_t bsp_i2c_init(void); // 初始化I2C接口
|
||||
/*************************** I2C ↑ *******************************************/
|
||||
/*******************************************************************************/
|
||||
|
||||
|
||||
/*******************************************************************************/
|
||||
/*************************** 姿态传感器 QMI8658 ↓ ****************************/
|
||||
#define QMI8658_SENSOR_ADDR 0x6A // QMI8658 I2C地址
|
||||
|
||||
// QMI8658寄存器地址
|
||||
enum qmi8658_reg
|
||||
{
|
||||
QMI8658_WHO_AM_I,
|
||||
QMI8658_REVISION_ID,
|
||||
QMI8658_CTRL1,
|
||||
QMI8658_CTRL2,
|
||||
QMI8658_CTRL3,
|
||||
QMI8658_CTRL4,
|
||||
QMI8658_CTRL5,
|
||||
QMI8658_CTRL6,
|
||||
QMI8658_CTRL7,
|
||||
QMI8658_CTRL8,
|
||||
QMI8658_CTRL9,
|
||||
QMI8658_CATL1_L,
|
||||
QMI8658_CATL1_H,
|
||||
QMI8658_CATL2_L,
|
||||
QMI8658_CATL2_H,
|
||||
QMI8658_CATL3_L,
|
||||
QMI8658_CATL3_H,
|
||||
QMI8658_CATL4_L,
|
||||
QMI8658_CATL4_H,
|
||||
QMI8658_FIFO_WTM_TH,
|
||||
QMI8658_FIFO_CTRL,
|
||||
QMI8658_FIFO_SMPL_CNT,
|
||||
QMI8658_FIFO_STATUS,
|
||||
QMI8658_FIFO_DATA,
|
||||
QMI8658_STATUSINT = 45,
|
||||
QMI8658_STATUS0,
|
||||
QMI8658_STATUS1,
|
||||
QMI8658_TIMESTAMP_LOW,
|
||||
QMI8658_TIMESTAMP_MID,
|
||||
QMI8658_TIMESTAMP_HIGH,
|
||||
QMI8658_TEMP_L,
|
||||
QMI8658_TEMP_H,
|
||||
QMI8658_AX_L,
|
||||
QMI8658_AX_H,
|
||||
QMI8658_AY_L,
|
||||
QMI8658_AY_H,
|
||||
QMI8658_AZ_L,
|
||||
QMI8658_AZ_H,
|
||||
QMI8658_GX_L,
|
||||
QMI8658_GX_H,
|
||||
QMI8658_GY_L,
|
||||
QMI8658_GY_H,
|
||||
QMI8658_GZ_L,
|
||||
QMI8658_GZ_H,
|
||||
QMI8658_COD_STATUS = 70,
|
||||
QMI8658_dQW_L = 73,
|
||||
QMI8658_dQW_H,
|
||||
QMI8658_dQX_L,
|
||||
QMI8658_dQX_H,
|
||||
QMI8658_dQY_L,
|
||||
QMI8658_dQY_H,
|
||||
QMI8658_dQZ_L,
|
||||
QMI8658_dQZ_H,
|
||||
QMI8658_dVX_L,
|
||||
QMI8658_dVX_H,
|
||||
QMI8658_dVY_L,
|
||||
QMI8658_dVY_H,
|
||||
QMI8658_dVZ_L,
|
||||
QMI8658_dVZ_H,
|
||||
QMI8658_TAP_STATUS = 89,
|
||||
QMI8658_STEP_CNT_LOW,
|
||||
QMI8658_STEP_CNT_MIDL,
|
||||
QMI8658_STEP_CNT_HIGH,
|
||||
QMI8658_RESET = 96
|
||||
};
|
||||
|
||||
// 倾角结构体
|
||||
typedef struct{
|
||||
int16_t acc_x;
|
||||
int16_t acc_y;
|
||||
int16_t acc_z;
|
||||
int16_t gyr_x;
|
||||
int16_t gyr_y;
|
||||
int16_t gyr_z;
|
||||
float AngleX;
|
||||
float AngleY;
|
||||
float AngleZ;
|
||||
}t_sQMI8658;
|
||||
|
||||
void qmi8658_init(void); // QMI8658初始化
|
||||
void qmi8658_fetch_angleFromAcc(t_sQMI8658 *p); // 获取倾角
|
||||
|
||||
/*************************** 姿态传感器 QMI8658 ↑ ****************************/
|
||||
/*******************************************************************************/
|
||||
|
||||
/******************************************************************************/
|
||||
/*************************** I2S ↓ **************************************/
|
||||
|
||||
/* Example configurations */
|
||||
#define EXAMPLE_RECV_BUF_SIZE (2400)
|
||||
#define EXAMPLE_SAMPLE_RATE (16000)
|
||||
#define EXAMPLE_MCLK_MULTIPLE (384) // If not using 24-bit data width, 256 should be enough
|
||||
#define EXAMPLE_MCLK_FREQ_HZ (EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE)
|
||||
#define EXAMPLE_VOICE_VOLUME (70)
|
||||
|
||||
/* I2S port and GPIOs */
|
||||
#define I2S_NUM (0)
|
||||
#define I2S_MCK_IO (GPIO_NUM_38)
|
||||
#define I2S_BCK_IO (GPIO_NUM_14)
|
||||
#define I2S_WS_IO (GPIO_NUM_13)
|
||||
#define I2S_DO_IO (GPIO_NUM_45)
|
||||
#define I2S_DI_IO (-1)
|
||||
|
||||
|
||||
/***********************************************************/
|
||||
/*************** IO扩展芯片 ↓ *************************/
|
||||
#define PCA9557_INPUT_PORT 0x00
|
||||
#define PCA9557_OUTPUT_PORT 0x01
|
||||
#define PCA9557_POLARITY_INVERSION_PORT 0x02
|
||||
#define PCA9557_CONFIGURATION_PORT 0x03
|
||||
|
||||
#define LCD_CS_GPIO BIT(0) // PCA9557_GPIO_NUM_1
|
||||
#define PA_EN_GPIO BIT(1) // PCA9557_GPIO_NUM_2
|
||||
#define DVP_PWDN_GPIO BIT(2) // PCA9557_GPIO_NUM_3
|
||||
|
||||
#define PCA9557_SENSOR_ADDR 0x19 /*!< Slave address of the MPU9250 sensor */
|
||||
|
||||
#define SET_BITS(_m, _s, _v) ((_v) ? (_m)|((_s)) : (_m)&~((_s)))
|
||||
|
||||
void pca9557_init(void);
|
||||
void lcd_cs(uint8_t level);
|
||||
void pa_en(uint8_t level);
|
||||
void dvp_pwdn(uint8_t level);
|
||||
/*************** IO扩展芯片 ↑ *************************/
|
||||
/***********************************************************/
|
||||
|
||||
|
||||
/***********************************************************/
|
||||
/**************** LCD显示屏 ↓ *************************/
|
||||
#define BSP_LCD_PIXEL_CLOCK_HZ (80 * 1000 * 1000)
|
||||
#define BSP_LCD_SPI_NUM (SPI3_HOST)
|
||||
#define LCD_CMD_BITS (8)
|
||||
#define LCD_PARAM_BITS (8)
|
||||
#define BSP_LCD_BITS_PER_PIXEL (16)
|
||||
#define LCD_LEDC_CH LEDC_CHANNEL_0
|
||||
|
||||
#define BSP_LCD_H_RES (320)
|
||||
#define BSP_LCD_V_RES (240)
|
||||
|
||||
#define BSP_LCD_SPI_MOSI (GPIO_NUM_40)
|
||||
#define BSP_LCD_SPI_CLK (GPIO_NUM_41)
|
||||
#define BSP_LCD_SPI_CS (GPIO_NUM_NC)
|
||||
#define BSP_LCD_DC (GPIO_NUM_39)
|
||||
#define BSP_LCD_RST (GPIO_NUM_NC)
|
||||
#define BSP_LCD_BACKLIGHT (GPIO_NUM_42)
|
||||
|
||||
#define BSP_LCD_DRAW_BUF_HEIGHT (20)
|
||||
|
||||
esp_err_t bsp_display_brightness_init(void);
|
||||
esp_err_t bsp_display_brightness_set(int brightness_percent);
|
||||
esp_err_t bsp_display_backlight_off(void);
|
||||
esp_err_t bsp_display_backlight_on(void);
|
||||
esp_err_t bsp_lcd_init(void);
|
||||
void lcd_set_color(uint16_t color);
|
||||
void lcd_draw_pictrue(int x_start, int y_start, int x_end, int y_end, const unsigned char *gImage);
|
||||
void bsp_lvgl_start(void);
|
||||
/*************** LCD显示屏 ↑ *************************/
|
||||
/***********************************************************/
|
||||
|
||||
|
||||
/***********************************************************/
|
||||
/**************** 摄像头 ↓ ****************************/
|
||||
#define CAMERA_EN 0
|
||||
#if CAMERA
|
||||
#include "esp_camera.h"
|
||||
|
||||
#define CAMERA_PIN_PWDN -1
|
||||
#define CAMERA_PIN_RESET -1
|
||||
#define CAMERA_PIN_XCLK 5
|
||||
#define CAMERA_PIN_SIOD 1
|
||||
#define CAMERA_PIN_SIOC 2
|
||||
|
||||
#define CAMERA_PIN_D7 9
|
||||
#define CAMERA_PIN_D6 4
|
||||
#define CAMERA_PIN_D5 6
|
||||
#define CAMERA_PIN_D4 15
|
||||
#define CAMERA_PIN_D3 17
|
||||
#define CAMERA_PIN_D2 8
|
||||
#define CAMERA_PIN_D1 18
|
||||
#define CAMERA_PIN_D0 16
|
||||
#define CAMERA_PIN_VSYNC 3
|
||||
#define CAMERA_PIN_HREF 46
|
||||
#define CAMERA_PIN_PCLK 7
|
||||
|
||||
|
||||
#define XCLK_FREQ_HZ 24000000
|
||||
|
||||
void bsp_camera_init(void);
|
||||
void app_camera_lcd(void);
|
||||
|
||||
#endif
|
||||
/******************** 摄像头 ↑ *************************/
|
||||
/***********************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
/***********************************************************/
|
||||
/*************** SPIFFS文件系统 ↓ *********************/
|
||||
#define SPIFFS_BASE "/spiffs"
|
||||
|
||||
esp_err_t bsp_spiffs_mount(void);
|
||||
|
||||
/*************** SPIFFS文件系统 ↑ *********************/
|
||||
/**********************************************************/
|
||||
|
||||
|
||||
|
||||
/***********************************************************/
|
||||
/********************* 音频 ↓ *************************/
|
||||
#define ADC_I2S_CHANNEL 4
|
||||
|
||||
#define VOLUME_DEFAULT 70
|
||||
|
||||
#define CODEC_DEFAULT_SAMPLE_RATE (16000)
|
||||
#define CODEC_DEFAULT_BIT_WIDTH (32)
|
||||
#define CODEC_DEFAULT_ADC_VOLUME (24.0)
|
||||
#define CODEC_DEFAULT_CHANNEL (2)
|
||||
|
||||
#define BSP_I2S_NUM I2S_NUM_1
|
||||
|
||||
#define GPIO_I2S_LRCK (GPIO_NUM_13)
|
||||
#define GPIO_I2S_MCLK (GPIO_NUM_38)
|
||||
#define GPIO_I2S_SCLK (GPIO_NUM_14)
|
||||
#define GPIO_I2S_SDIN (GPIO_NUM_12)
|
||||
#define GPIO_I2S_DOUT (GPIO_NUM_45)
|
||||
#define GPIO_PWR_CTRL (GPIO_NUM_NC)
|
||||
|
||||
esp_err_t bsp_codec_init(void);
|
||||
esp_err_t bsp_i2s_write(void *audio_buffer, size_t len, size_t *bytes_written, uint32_t timeout_ms);
|
||||
esp_err_t bsp_codec_set_fs(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch);
|
||||
esp_err_t bsp_speaker_set_fs(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch);
|
||||
esp_err_t bsp_codec_mute_set(bool enable);
|
||||
esp_err_t bsp_codec_volume_set(int volume, int *volume_set);
|
||||
|
||||
int bsp_get_feed_channel(void);
|
||||
esp_err_t bsp_get_feed_data(bool is_get_raw_channel, int16_t *buffer, int buffer_len);
|
||||
|
||||
/********************* 音频 ↑ *************************/
|
||||
/***********************************************************/
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
# espressif/esp32-camera: "^2.0.10" # 摄像头驱动
|
||||
lvgl/lvgl: "~8.3.0"
|
||||
espressif/esp_lvgl_port: "~1.4.0" # LVGL接口
|
||||
espressif/esp_lcd_touch_ft5x06: "~1.0.6" # 触摸屏驱动
|
||||
chmorgan/esp-audio-player: "~1.0.7" # 音频播放
|
||||
chmorgan/esp-file-iterator: "1.0.0" # 获取文件
|
||||
espressif/esp_codec_dev: "~1.3.0" # 音频驱动
|
||||
espressif/esp-sr: "~1.6.0" # 语音识别
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,18 @@
|
|||
#include <stdio.h>
|
||||
#include "esp32_s3_szp.h"
|
||||
#include "app_ui.h"
|
||||
#include "app_sr.h"
|
||||
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
bsp_i2c_init(); // I2C初始化
|
||||
pca9557_init(); // IO扩展芯片初始化
|
||||
bsp_lvgl_start(); // 初始化液晶屏lvgl接口
|
||||
|
||||
bsp_spiffs_mount(); // SPIFFS文件系统初始化
|
||||
bsp_codec_init(); // 音频初始化
|
||||
mp3_player_init(); // MP3播放器初始化
|
||||
|
||||
app_sr_init(); // 语音识别初始化
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 24k
|
||||
phy_init, data, phy, 0xf000, 4k
|
||||
factory, app, factory, , 3M
|
||||
storage, data, spiffs, , 3M
|
||||
model, data, spiffs, , 5168K,
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
|
||||
#
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_MODE_OCT=y
|
||||
CONFIG_SPIRAM_SPEED_80M=y
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y
|
||||
CONFIG_ESP32S3_DATA_CACHE_64KB=y
|
||||
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
|
||||
CONFIG_SPIFFS_OBJ_NAME_LEN=128
|
||||
CONFIG_LV_COLOR_16_SWAP=y
|
||||
CONFIG_LV_MEM_CUSTOM=y
|
||||
CONFIG_LV_FONT_MONTSERRAT_20=y
|
||||
CONFIG_LV_FONT_MONTSERRAT_24=y
|
||||
CONFIG_LV_FONT_MONTSERRAT_32=y
|
||||
CONFIG_LV_USE_GIF=y
|
||||
CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE=y
|
||||
Loading…
Reference in New Issue