介绍
ESP32 微控制器是一款高度集成且功能丰富的设备,它在物联网 (IoT) 和嵌入式系统应用中扮演着重要角色。这款微控制器的一个显著特性是其能够在内部闪存中存储多个固件镜像,并能够在这些镜像之间灵活切换。这项功能为开发者提供了极大的便利,无论是用于测试不同版本的固件、同时运行多个应用程序 ,还是为了维护一个备份固件以备不时之需。
本文旨在深入探讨如何利用 ESP32 的图形引导加载程序在多个存储于闪存中的固件镜像之间进行切换。该引导程序作为一个主应用程序,赋予用户选择并执行不同固件镜像的能力。此外,我们还将展示每个应用程序如何能够重新切换回引导程序,以便于进行进一步的固件选择或系统维护。
工作方式
图形引导加载程序提供了一个用户友好的图形界面,允许用户从菜单中选择并启动特定的应用程序。用户通过图形菜单选择一个应用程序时,引导加载程序会识别并选择与该应用程序关联的特定闪存分区。随后,设备将执行重启操作,引导加载程序切换到新选择的应用程序。在新应用程序启动的过程中,会运行一段特定的代码,该代码负责将设备的引导分区重新指向包含图形引导加载程序的原始分区。这一关键步骤确保了在下一次设备重启时,引导加载程序能够重新成为启动固件,从而允许用户再次进行应用程序的选择。整个固件切换过程采用了 OTA 机制,但所有应用程序固件均已预先存储在设备的闪存中。
分区表
要启用 ESP32 微控制器上的多个固件映像,我们需要一个自定义分区表。以下是文件 partitions.csv 的示例,该文件可容纳引导加载程序和五个 OTA 分区,专为 ESP32-S3-BOX-3 和 M5Stack-CoreS3 中常用的 16 MB 闪存而设计:
# Name, Type, Subtype, Offset, Size, Flags
nvs, data, nvs, 0x9000, 24K,
otadata, data, ota, , 8K,
phy_init, data, phy, , 4K,
factory, app, factory, , 2M,
ota_0, app, ota_0, , 2816K,
ota_1, app, ota_1, , 2816K,
ota_2, app, ota_2, , 2816K,
ota_3, app, ota_3, , 2816K,
ota_4, app, ota_4, , 2816K,
此分区表需要在您的项目中定义,并且必须在 sdkconfig 文件中启用以下选项:
CONFIG_PARTITION_TABLE_CUSTOM=y
设置图形引导程序
使用预建固件快速入门
示例应用程序在 Releases 中以二进制固件的形式提供。
它可以从地址 0x0 烧录到设备。
esptool.py --chip esp32s3 write_flash 0x0000 graphical_bootloader_esp32-s3-box-3.bin
您还可以使用 Wokwi 在 Web 浏览器中运行应用程序的模拟。
克隆仓库
如果您想构建自己的版本,请按照本章中的说明进行操作。
从克隆项目仓库开始:
git clone https://github.com/georgik/esp32-graphical-bootloader.git
cd esp32-graphical-bootloader
选择目标板
使用 BUILD_BOARD 变量设置适当的板。例如,要配置 ESP32-S3-BOX-3,请使用:
cmake -DBUILD_BOARD=esp-box-3 -Daction=build_all_apps -P Bootloader.cmake
其他受支持的板列表可以在 boards 目录中找到。
将二进制文件合并为单个固件
上述构建完成后,将二进制文件合并为单个固件:
esptool.py --chip esp32s3 merge_bin -o build.esp-box-3/combined.bin --flash_mode dio --flash_size 16MB \
0x0 build.esp-box-3/bootloader/bootloader.bin \
0x8000 build.esp-box-3/partition_table/partition-table.bin \
0xf000 build.esp-box-3/ota_data_initial.bin \
0x20000 build.esp-box-3/esp32-graphical-bootloader.bin \
0x220000 apps/tic_tac_toe/build.esp-box-3/tic_tac_toe.bin \
0x4E0000 apps/wifi_list/build.esp-box-3/wifi_list.bin \
0x7A0000 apps/calculator/build.esp-box-3/calculator.bin \
0xA60000 apps/synth_piano/build.esp-box-3/synth_piano.bin \
0xD20000 apps/game_of_life/build.esp-box-3/game_of_life.bin
将合并后的二进制文件烧录到 ESP32 上
最后,将合并后的二进制文件烧录到 ESP32 上:
esptool.py --chip esp32s3 write_flash 0x0 build.esp-box-3/combined.bin
如何使用引导程序
烧录完成后,ESP32 将启动进入图形引导加载程序。该引导程序允许您选择要运行的应用程序。用户界面十分直观,您可以直接浏览存储在 OTA 分区中的不同应用程序。
创建自定义应用程序
以下步骤将解释如何为 ESP32-S3-BOX-3 创建与图形引导加载程序配合使用的自定义 ESP-IDF 应用程序。

idf.py create-project hello_app
cd hello_app
idf.py set-target esp32s3
idf.py add-dependency "espressif/esp-box-3^1.2.0"
idf.py add-dependency "espressif/esp_codec_dev==1.1.0"
cp ../calculator/sdkconfig.defaults .
cp ../calculator/sdkconfig.defaults.esp-box-3 .
切换到存储在 OTA 分区中的应用程序
引导程序中的以下代码片段显示了如何切换到另一个应用程序。这对于管理存储在不同 OTA 分区中的多个应用程序十分有效:
// For button 1, next_partition will not change, thus pointing to 'ota_0'
if (next_partition && esp_ota_set_boot_partition(next_partition) == ESP_OK) {
printf("Setting boot partition to %s\\n", next_partition->label);
esp_restart(); // Restart to boot from the new partition
} else {
printf("Failed to set boot partition\\n");
}
返回工厂分区中的原始应用程序
每个应用程序都可以包含一个切换回原始应用程序(图形引导加载程序)的机制。以下是其中一个子应用程序的示例功能:
#include "esp_ota_ops.h"
void reset_to_factory_app() {
// Get the partition structure for the factory partition
const esp_partition_t *factory_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
if (factory_partition != NULL) {
if (esp_ota_set_boot_partition(factory_partition) == ESP_OK) {
printf("Set boot partition to factory, restarting now.\\n");
} else {
printf("Failed to set boot partition to factory.\\n");
}
} else {
printf("Factory partition not found.\\n");
}
fflush(stdout);
}
该函数可以在应用程序开始时调用此功能,以确保设备在发生崩溃时能够恢复到出厂固件(引导程序)。完成此操作后,任何重置都将启动到原始固件中。
在您应用程序的 CMakeLists.txt 中,请确保包含所需的依赖项:
idf_component_register(SRCS "hello_app.c"
INCLUDE_DIRS "."
REQUIRES app_update)
应用代码
更新 main/hello_app.c 的代码,以切换到出厂应用程序并在屏幕上显示信息。
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "lvgl.h"
#include "bsp/esp-bsp.h"
#include "esp_ota_ops.h"
void reset_to_factory_app() {
// Get the partition structure for the factory partition
const esp_partition_t *factory_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
if (factory_partition != NULL) {
if (esp_ota_set_boot_partition(factory_partition) == ESP_OK) {
printf("Set boot partition to factory, restarting now.\\n");
} else {
printf("Failed to set boot partition to factory.\\n");
}
} else {
printf("Factory partition not found.\\n");
}
fflush(stdout);
}
void app_main(void) {
reset_to_factory_app();
// Initialize the BSP
bsp_i2c_init();
bsp_display_start();
// Create a label and set its text
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello ESP32-S3-BOX-3");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
// Start the display backlight
bsp_display_backlight_on();
printf("Hello ESP32-S3-BOX-3\n");
// Handle LVGL tasks
while (1) {
lv_task_handler();
vTaskDelay(pdMS_TO_TICKS(10));
}
}
刷新应用程序
您可以将应用程序直接构建并烧录到 OTA 分区。
idf.py @../../boards/esp-box-3.cfg build
esptool.py --chip esp32s3 write_flash 0x220000 build.esp-box-3/hello_app.bin
更改应用程序图标
图形引导加载程序中显示的图标是图形加载引导程序固件的一部分,并非应用程序的一部分。图标存储在 resources/images 中。
您只需要替换其中一个现有的 png 文件。
如果您想使用您自己的图标名称,请更新引导加载程序 mainmain/bootloader_ui.c:
LV_IMG_DECLARE(icon_hello_app)
并将您的图标在 main/CMakeLists.txt 中进行注册。IDF 构建过程会将该图标转换为正确的格式。
lvgl_port_create_c_image("../resources/images/icon_hello_app.png" "images/gen/" "ARGB8888" "NONE")
更改图标后,您需要重建并烧录主应用程序:
idf.py @boards/esp-box-3 fullclean
idf.py @boards/esp-box-3 app-flash
相关链接:
结论
ESP32 的图形引导加载程序为在单一设备上管理多个应用程序提供了一种优越的方式。通过利用 OTA 分区,您可以轻松地存储和切换不同的应用程序。无论您是希望尝试不同项目的创客,还是需要多个应用环境的专业人士,这款引导程序都简化了这一过程。
1307

被折叠的 条评论
为什么被折叠?



