diff --git a/code/esp32c3_espidf/main/CMakeLists.txt b/code/esp32c3_espidf/main/CMakeLists.txt index 20cbed4..6287e94 100644 --- a/code/esp32c3_espidf/main/CMakeLists.txt +++ b/code/esp32c3_espidf/main/CMakeLists.txt @@ -7,6 +7,21 @@ idf_component_register( INCLUDE_DIRS "." REQUIRES lvgl + spiffs + PRIV_REQUIRES ) +set(SPIFFS_PARTITION_NAME "storage") # 如果分区名是 "storage" +# 设置 SPIFFS 根目录路径 +set(SPIFFS_IMAGE_DIR "${CMAKE_SOURCE_DIR}/spiffs_image") +# 确保目录存在 +if(NOT EXISTS ${SPIFFS_IMAGE_DIR}) + file(MAKE_DIRECTORY ${SPIFFS_IMAGE_DIR}) +endif() +# 添加 SPIFFS 镜像生成 +spiffs_create_partition_image( + ${SPIFFS_PARTITION_NAME} # 分区名称 + ${SPIFFS_IMAGE_DIR} # 源文件目录 + FLASH_IN_PROJECT # 生成 flash 目标 +) \ No newline at end of file diff --git a/code/esp32c3_espidf/main/hello_world_main.c b/code/esp32c3_espidf/main/hello_world_main.c index b38da98..6516826 100644 --- a/code/esp32c3_espidf/main/hello_world_main.c +++ b/code/esp32c3_espidf/main/hello_world_main.c @@ -18,9 +18,18 @@ #include "esp_chip_info.h" #include "esp_flash.h" #include "esp_system.h" - +#include "esp_spiffs.h" #include "esp_log.h" +#include +#include +#include +#include // 关键:包含 DIR 相关定义 +#include // 错误码定义 +#include // 时间相关函数 + +#include "spiffs.h" + #include "spi.h" #include "lcd.h" @@ -33,18 +42,6 @@ static const char *TAG = "SYS"; -// TimerHandle_t periodic_timer = NULL; - -// 定时器回调函数(在定时器服务任务中执行) -// void periodic_timer_callback(TimerHandle_t xTimer) -// { -// // printf("每秒执行一次的函数\n"); -// // 在这里执行你的周期性任务 -// // 注意:此回调函数应尽快返回,避免阻塞定时器服务任务 - -// lcd_one_second_task(); -// } - // 自定义 tick 获取函数 static uint32_t custom_tick_get(void) { @@ -52,74 +49,60 @@ static uint32_t custom_tick_get(void) return (uint32_t)(esp_timer_get_time() / 1000); } -/** - * Basic example to create a "Hello world" label - */ -// static void btn_event_cb(lv_event_t * e) -// { -// lv_event_code_t code = lv_event_get_code(e); -// lv_obj_t * btn = lv_event_get_target(e); -// if(code == LV_EVENT_CLICKED) { -// static uint8_t cnt = 0; -// cnt++; -// /*Get the first child of the button which is the label and change its text*/ -// lv_obj_t * label = lv_obj_get_child(btn, 0); -// lv_label_set_text_fmt(label, "Button: %d", cnt); -// } -// } -// /** -// * Create a button with a label and react on click event. -// */ -// void lv_example_get_started_2(void) -// { -// lv_obj_t * btn = lv_button_create(lv_screen_active()); /*Add a button the current screen*/ -// lv_obj_set_pos(btn, 10, 10); /*Set its position*/ -// lv_obj_set_size(btn, 120, 50); /*Set its size*/ -// lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL); /*Assign a callback to the button*/ - -// lv_obj_t * label = lv_label_create(btn); /*Add a label to the button*/ -// lv_label_set_text(label, "Button"); /*Set the labels text*/ -// lv_obj_center(label); -// } void app_main(void) { - ESP_LOGI(TAG,"Hello world!"); + ESP_LOGI(TAG, "Hello world!"); /* Print chip information */ esp_chip_info_t chip_info; uint32_t flash_size; esp_chip_info(&chip_info); - ESP_LOGI(TAG,"This is %s chip with %d CPU core(s), %s%s%s%s, ", - CONFIG_IDF_TARGET, - chip_info.cores, - (chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "", - (chip_info.features & CHIP_FEATURE_BT) ? "BT" : "", - (chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "", - (chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : ""); + ESP_LOGI(TAG, "This is %s chip with %d CPU core(s), %s%s%s%s, ", + CONFIG_IDF_TARGET, + chip_info.cores, + (chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "", + (chip_info.features & CHIP_FEATURE_BT) ? "BT" : "", + (chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "", + (chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : ""); unsigned major_rev = chip_info.revision / 100; unsigned minor_rev = chip_info.revision % 100; - ESP_LOGI(TAG,"silicon revision v%d.%d, ", major_rev, minor_rev); + ESP_LOGI(TAG, "silicon revision v%d.%d, ", major_rev, minor_rev); if (esp_flash_get_size(NULL, &flash_size) != ESP_OK) { - ESP_LOGI(TAG,"Get flash size failed"); + ESP_LOGI(TAG, "Get flash size failed"); return; } - ESP_LOGI(TAG,"%" PRIu32 "MB %s flash", flash_size / (uint32_t)(1024 * 1024), - (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); + ESP_LOGI(TAG, "%" PRIu32 "MB %s flash", flash_size / (uint32_t)(1024 * 1024), + (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); - ESP_LOGI(TAG,"Minimum free heap size: %" PRIu32 " bytes", esp_get_minimum_free_heap_size()); + ESP_LOGI(TAG, "Minimum free heap size: %" PRIu32 " bytes", esp_get_minimum_free_heap_size()); - ESP_LOGI(TAG,"sizeof(int) ==%d", sizeof(int)); - ESP_LOGI(TAG,"LVGL version: %s", lv_version_info()); - ESP_LOGI(TAG,"LVGL memory size: %u bytes", LV_MEM_SIZE); - ESP_LOGI(TAG,"LVGL color depth: %d bits", LV_COLOR_DEPTH); + ESP_LOGI(TAG, "sizeof(int) ==%d", sizeof(int)); + ESP_LOGI(TAG, "LVGL version: %s", lv_version_info()); + ESP_LOGI(TAG, "LVGL memory size: %u bytes", LV_MEM_SIZE); + ESP_LOGI(TAG, "LVGL color depth: %d bits", LV_COLOR_DEPTH); + + // 1. 初始化 SPIFFS + const char *spiffs_base_path = "/spiffs"; + esp_err_t ret = spiffs_init(spiffs_base_path); + + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "SPIFFS 初始化失败"); + } + else + { + ESP_LOGI(TAG, "SPIFFS 初始化OK"); + + list_spiffs_files_safe(spiffs_base_path); + } spi_init(); lcd_init(); @@ -129,7 +112,7 @@ void app_main(void) lv_port_disp_init(); lv_example_get_started_1(); - + while (1) { diff --git a/code/esp32c3_espidf/main/lv_apps/helloworld/lv_helloworld.c b/code/esp32c3_espidf/main/lv_apps/helloworld/lv_helloworld.c index c280022..256f383 100644 --- a/code/esp32c3_espidf/main/lv_apps/helloworld/lv_helloworld.c +++ b/code/esp32c3_espidf/main/lv_apps/helloworld/lv_helloworld.c @@ -1,4 +1,7 @@ #include "lv_helloworld.h" +#include "lv_load_font_from_spiffs.h" + +static const char *TAG = "DEMO1"; LV_FONT_DECLARE(my_cn_font); @@ -7,12 +10,21 @@ LV_FONT_DECLARE(my_cn_font); */ void lv_example_get_started_1(void) { + + // 1. 加载中文字体 + lv_font_t* cn_font_16 = load_chinese_font_from_spiffs("/spiffs/cn_font.bin"); + if (cn_font_16 == NULL) { + ESP_LOGE(TAG, "无法加载中文字体,使用默认字体"); + cn_font_16 = &my_cn_font; // 回退到默认字体 + } + + /*Change the active screen's background color*/ lv_obj_set_style_bg_color(lv_screen_active(), lv_color_hex(0x003a57), LV_PART_MAIN); static lv_style_t style_label; lv_style_init(&style_label); - lv_style_set_text_font(&style_label, &my_cn_font); // 关键:设置字体 + //lv_style_set_text_font(&style_label, &cn_font_16); // 关键:设置字体 /*Create a white label, set its text and align it to the center*/ lv_obj_t *label = lv_label_create(lv_screen_active()); diff --git a/code/esp32c3_espidf/main/lv_apps/helloworld/lv_helloworld.h b/code/esp32c3_espidf/main/lv_apps/helloworld/lv_helloworld.h index 56b1653..f5104a4 100644 --- a/code/esp32c3_espidf/main/lv_apps/helloworld/lv_helloworld.h +++ b/code/esp32c3_espidf/main/lv_apps/helloworld/lv_helloworld.h @@ -2,6 +2,7 @@ #define LV_HELLOWORLD_H #include "lvgl.h" +#include "esp_log.h" void lv_example_get_started_1(void); diff --git a/code/esp32c3_espidf/main/lv_load_font_from_spiffs.c b/code/esp32c3_espidf/main/lv_load_font_from_spiffs.c new file mode 100644 index 0000000..fee4adc --- /dev/null +++ b/code/esp32c3_espidf/main/lv_load_font_from_spiffs.c @@ -0,0 +1,44 @@ +#include "lv_load_font_from_spiffs.h" + +static const char *TAG = "LV_LOAD_FONT"; + +/** + * @brief 从SPIFFS加载中文字体 + * @param font_path 字体文件路径,如"/spiffs/cn_font.bin" + * @return lv_font_t* 成功返回字体指针,失败返回NULL + */ +lv_font_t* load_chinese_font_from_spiffs(const char* font_path) +{ + ESP_LOGI(TAG, "尝试加载字体: %s", font_path); + + // 检查文件是否存在 + FILE* f = fopen(font_path, "rb"); + if (f == NULL) { + ESP_LOGE(TAG, "字体文件不存在: %s", font_path); + return NULL; + } + fclose(f); + + // LVGL V8/V9通用方法:使用lv_binfont_create + lv_font_t* font = lv_binfont_create(font_path); + + if (font == NULL) { + ESP_LOGE(TAG, "字体加载失败: %s", font_path); + return NULL; + } + + ESP_LOGI(TAG, "字体加载成功: %s", font_path); + return font; +} + +/** + * @brief 释放字体资源 + * @param font 要释放的字体指针 + */ +void free_chinese_font(lv_font_t* font) +{ + if (font != NULL) { + lv_binfont_destroy(font); + ESP_LOGI(TAG, "字体资源已释放"); + } +} \ No newline at end of file diff --git a/code/esp32c3_espidf/main/lv_load_font_from_spiffs.h b/code/esp32c3_espidf/main/lv_load_font_from_spiffs.h new file mode 100644 index 0000000..6ca0a65 --- /dev/null +++ b/code/esp32c3_espidf/main/lv_load_font_from_spiffs.h @@ -0,0 +1,30 @@ +#ifndef LV_LOAD_FONT_FROM_SPIFFS_H +#define LV_LOAD_FONT_FROM_SPIFFS_H + +/********************* + * INCLUDES + *********************/ +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#include +#include +#include "sdkconfig.h" +#include +#include +#include +#include // 关键:包含 DIR 相关定义 +#include // 错误码定义 +#include // 时间相关函数 + +#include "esp_system.h" +#include "esp_spiffs.h" +#include "esp_log.h" + +lv_font_t* load_chinese_font_from_spiffs(const char* font_path); +void free_chinese_font(lv_font_t* font); + +#endif \ No newline at end of file diff --git a/code/esp32c3_espidf/main/spiffs.c b/code/esp32c3_espidf/main/spiffs.c new file mode 100644 index 0000000..26cf16a --- /dev/null +++ b/code/esp32c3_espidf/main/spiffs.c @@ -0,0 +1,138 @@ +#include "spiffs.h" + +static const char *TAG = "SPIFFS"; + +/** + * @brief 初始化并挂载 SPIFFS 文件系统 + * @param base_path 挂载点路径(如 "/spiffs") + * @return esp_err_t ESP_OK 成功,其他失败 + */ +esp_err_t spiffs_init(const char *base_path) +{ + ESP_LOGI(TAG, "正在初始化 SPIFFS..."); + + esp_vfs_spiffs_conf_t conf = { + .base_path = base_path, + .partition_label = NULL, // 使用第一个找到的 SPIFFS 分区 + .max_files = 10, // 最大打开文件数 + .format_if_mount_failed = true // 如果挂载失败则格式化 + }; + + // 挂载 SPIFFS 分区 + esp_err_t ret = esp_vfs_spiffs_register(&conf); + if (ret != ESP_OK) + { + if (ret == ESP_FAIL) + { + ESP_LOGE(TAG, "挂载 SPIFFS 失败"); + } + else if (ret == ESP_ERR_NOT_FOUND) + { + ESP_LOGE(TAG, "未找到 SPIFFS 分区"); + } + else + { + ESP_LOGE(TAG, "SPIFFS 初始化失败 (%s)", esp_err_to_name(ret)); + } + return ret; + } + + ESP_LOGI(TAG, "SPIFFS 挂载成功,挂载点: %s", base_path); + + // 获取分区信息 + size_t total = 0, used = 0; + ret = esp_spiffs_info(NULL, &total, &used); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "获取 SPIFFS 分区信息失败 (%s)", esp_err_to_name(ret)); + } + else + { + ESP_LOGI(TAG, "分区大小: 总共 %d KB, 已用 %d KB, 可用 %d KB", + total / 1024, used / 1024, (total - used) / 1024); + } + + return ESP_OK; +} + +/** + * @brief 列出 SPIFFS 文件(安全版本) + */ +void list_spiffs_files_safe(const char *base_path) +{ + ESP_LOGI(TAG, "Listing files in %s:", base_path); + + DIR *dir = opendir(base_path); + if (dir == NULL) { + ESP_LOGE(TAG, "Failed to open directory %s: %s", + base_path, strerror(errno)); + return; + } + + struct dirent *entry; + int count = 0; + + while ((entry = readdir(dir)) != NULL) { + // 跳过 "." 和 ".." + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + // 方法1:使用固定大小缓冲区(512字节足够) + char full_path[512]; + int ret = snprintf(full_path, sizeof(full_path), "%s/%s", base_path, entry->d_name); + + if (ret < 0) { + ESP_LOGW(TAG, "Error formatting path for: %s", entry->d_name); + continue; + } + + if ((size_t)ret >= sizeof(full_path)) { + ESP_LOGW(TAG, "Path truncated for: %s", entry->d_name); + // 继续处理,但路径可能不完整 + } + + // 方法2:使用动态分配(如果文件名可能很长) + // size_t path_len = strlen(base_path) + 1 + strlen(entry->d_name) + 1; + // char *full_path = malloc(path_len); + // if (full_path) { + // snprintf(full_path, path_len, "%s/%s", base_path, entry->d_name); + // // ... 使用 full_path + // free(full_path); + // } + + struct stat entry_stat; + if (stat(full_path, &entry_stat) == -1) { + ESP_LOGW(TAG, " %s - cannot stat: %s", + entry->d_name, strerror(errno)); + continue; + } + + if (S_ISDIR(entry_stat.st_mode)) { + ESP_LOGI(TAG, " [DIR] %-32s", entry->d_name); + } else { + const char *unit = "B"; + double size = (double)entry_stat.st_size; + + if (size >= 1024.0 * 1024.0) { + size /= 1024.0 * 1024.0; + unit = "MB"; + } else if (size >= 1024.0) { + size /= 1024.0; + unit = "KB"; + } + + ESP_LOGI(TAG, " [FILE] %-32s %8.2f %s", + entry->d_name, size, unit); + } + count++; + } + + closedir(dir); + + if (count == 0) { + ESP_LOGI(TAG, " No files found"); + } else { + ESP_LOGI(TAG, "Total: %d items", count); + } +} \ No newline at end of file diff --git a/code/esp32c3_espidf/main/spiffs.h b/code/esp32c3_espidf/main/spiffs.h new file mode 100644 index 0000000..e579146 --- /dev/null +++ b/code/esp32c3_espidf/main/spiffs.h @@ -0,0 +1,21 @@ +#ifndef SPIFFS_H +#define SPIFFS_H + +#include +#include +#include "sdkconfig.h" +#include +#include +#include +#include // 关键:包含 DIR 相关定义 +#include // 错误码定义 +#include // 时间相关函数 + +#include "esp_system.h" +#include "esp_spiffs.h" +#include "esp_log.h" + +esp_err_t spiffs_init(const char *base_path); +void list_spiffs_files_safe(const char *base_path); + +#endif \ No newline at end of file diff --git a/code/esp32c3_espidf/partitions.csv b/code/esp32c3_espidf/partitions.csv index c18b602..358be4f 100644 --- a/code/esp32c3_espidf/partitions.csv +++ b/code/esp32c3_espidf/partitions.csv @@ -3,4 +3,4 @@ nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x1D0000, -storage, data, spiffs, , 0xF0000, \ No newline at end of file +storage, data, spiffs, , 0x200000, \ No newline at end of file diff --git a/code/font/cn_font.bin b/code/font/cn_font.bin new file mode 100644 index 0000000..73b5af4 Binary files /dev/null and b/code/font/cn_font.bin differ diff --git a/code/font/make_font_bin.cmd b/code/font/make_font_bin.cmd new file mode 100644 index 0000000..63c410a --- /dev/null +++ b/code/font/make_font_bin.cmd @@ -0,0 +1 @@ +lv_font_conv --font HarmonyOS_Sans_SC_Regular.ttf --size 16 --bpp 1 --range 0x20-0x7F --range 0x3000-0x303F --range 0xFF00-0xFFEF --range 0x4E00-0x9FFF --format bin --output cn_font.bin \ No newline at end of file