ESP-IDF库开发教程之(4)--FLASH、BootLoader和FreeRTOS

在之前的文章中,我描述了新的esp32芯片,并解释了如何设置开发环境,包括使用一种可选的图形IDE,Eclipse。

在开始开发程序之前,您需要了解框架的三个关键组件:flash,bootloader和FreeRTOS操作系统。但别担心!在本篇文章的最后,您将编写,编译并执行您的第一个程序(即使是“最小化”),并从下一个教程开始,我们将一起处理更复杂的示例。

Flash

正如我在本教程的第一篇文章中所写的,esp32芯片需要一个外部闪存来存储程序,数据,配置参数......

外部存储器通过SPI总线连接到芯片,支持的容量高达16Mb。官方Espressif模块(ESP-WROOM-32)包含由GigaDevice(GD25Q32)制造的4Mb闪存:

esp-flash01

闪存可以存储不同的元素:程序,数据...因此它被分成多个部分(分区)。闪存中的列表分区,大小和位置存储在内存本身(地址0x8000),它被称为分区表。

默认情况下定义了两种分区类型:

  • app (type 0) – partition that contains an application (program)包含应用程序(程序)的分区
  • data (type 1) – partition that contains data 包含数据的分区

在编写新程序时,开发人员可以根据程序的特定需求决定如何组织闪存。 esp-idf框架提供两种预先配置的内存布局,您可以从配置菜单中进行选择:

esp-flash02

您还可以使用make partition_table命令显示项目的分区表:

esp-flash03

I在上面的屏幕截图中,您可以看到对应于最简单配置的分区表布局(单工厂应用程序,无OTA):

  • a data partition (named “nvs“) with a size of 24Kb starting from address 0x9000
  • a data partition (named “phy_init“) with a size of 4Kb starting from address 0xf000
  • an app partition (named “factory“) with a size of 1Mb starting from address 0x10000

Bootloader

为了更好地理解为什么将闪存划分为分区,我必须描述esp32引导加载程序的工作原理,即如何启动应用程序以及如何上载新固件。

在esp32 ROM内存中有一个小程序,名为第一阶段启动加载程序。该程序在芯片的每次复位时执行:它配置对外部闪存的访问,并且如果需要,存储来自串行/ USB端口的新数据(闪存过程)。一旦完成,它将访问闪存(地址为0x1000)并加载并执行第二阶段启动加载程序。

即使esp-idf框架的标准行为是使用此第二阶段启动加载程序,也可以开发一个独立应用程序,该应用程序存储在闪存的地址0x8000处,由第一阶段启动加载程序直接执行。

第二阶段引导加载程序读取地址为0x8000的分区表并搜索应用程序分区。它根据otadata分区的内容决定执行哪个应用程序:如果此分区为空或不存在,则引导加载将执行存储在出厂分区中的应用程序。这允许实施无线(OTA)应用程序更新过程:将新版本的应用程序发送到esp32芯片;该版本存储在新的应用程序分区中。一旦上传完成,分区的ID被保存在otadata中并且芯片被重新启动;引导程序将执行新版本:

esp-flash04

FreeRTOS

esp-idf框架基于FreeRTOS实时操作系统。在像esp32这样的“小型”芯片上工作时谈论操作系统可能听起来很奇怪......但您不必认为FreeRTOS是Windows,Linux或MacOS等操作系统。嵌入式操作系统提供的主要功能(由于其内部调度程序)是多任务处理,即能够并行运行不同任务。我们知道微处理器内核可以一次执行一条指令:由于调度程序在其任务之间快速切换,似乎不同的应用程序同时运行。

esp-flash05

(FreeRTOS中不同的任务状态)

实时操作系统的设计是为了确保任务调度是确定性的:您可以预测其调度程序的行为。 FreeRTOS允许开发人员为每个任务定义优先级:调度程序使用优先级值来定义不同任务的执行模式。

Our first program

我们来编写我们的第一个使用esp-idf框架的程序。这个程序在我的Github仓库中可用,将成为所有下一个教程的基础。

首先,我们必须包含一些库:

esp-flash07

stdio.h库(Standard I / O)用于输入/输出函数(printf ...),两个freertos头文件用于定义和执行不同的任务。

每个程序都从app_main()开始执行:

esp-flash08

在app_main()中,使用xTaskCreate方法创建一个新任务。此方法需要以下参数:指向包含任务代码的函数(&loop_task)的指针,任务名称(“loop_task”),要分配给任务的堆栈内存大小(以字为单位) ,任务的附加参数(NULL),任务的优先级(5)和指针 - 可选 - 来检索任务的处理程序(NULL)。

一旦创建任务,FreeRTOS调度程序将根据不同的任务和优先级执行任务。

该任务具有以下代码:

esp-flash09

这是一个连续的循环,每秒打印一个句子Hello World!“。 vTaskDelay方法将任务暂停指定的刻度数。 portTICK_RATE_MS常量定义一个刻度的持续时间(以毫秒为单位);如果你因此将1000分为这个常数,你会在一秒钟内得到滴答的数量。

Additional files

为了能够编译你的项目,你需要添加一些额外的文件:

  • a Makefile in the main folder of your project that contains the name of the project and an include for the main makefile of the framework:项目主文件夹中的Makefile,其中包含项目的名称和框架的主makefile的include:

esp-flash10

  • a component.mk file – it can be empty – in the folder where the source code is saved into to tell the compiler to process the files in that folder:一个component.mk文件 - 它可以是空的 - 在保存源代码的文件夹中告诉编译器处理该文件夹中的文件:

esp-flash11

Test

当程序准备就绪时,您可以按照上一篇文章中的说明编译并加载到主板上:

make
make flash

如果一切正常,当用串行仿真器连接到电路板时,应该看到:

esp-flash12

xecuting action: flash Serial port /dev/ttyACM0 Connecting.... Detecting chip type... ESP32-S3 Running ninja in directory /home/jichu/esp32/lvgl_display/build Executing "ninja flash"... [1/1] cd /home/jichu/esp32/lvgl_displa...isplay/build/bootloader/bootloader.bin Bootloader binary size 0x5240 bytes. 0x2dc0 bytes (36%) free. [2/5] Linking CXX executable lvgl_display.elf FAILED: lvgl_display.elf : && /home/jichu/.espressif/tools/xtensa-esp-elf/esp-13.2.0_20230928/xtensa-esp-elf/bin/xtensa-esp32s3-elf-g++ -mlongcalls -Wl,--cref -Wl,--defsym=IDF_TARGET_ESP32S3=0 -Wl,--Map=/home/jichu/esp32/lvgl_display/build/lvgl_display.map -Wl,--no-warn-rwx-segments -fno-rtti -fno-lto -Wl,--gc-sections -Wl,--warn-common -T esp32s3.peripherals.ld -T esp32s3.rom.ld -T esp32s3.rom.api.ld -T esp32s3.rom.libgcc.ld -T esp32s3.rom.newlib.ld -T esp32s3.rom.version.ld -T memory.ld -T sections.ld CMakeFiles/lvgl_display.elf.dir/project_elf_src_esp32s3.c.obj -o lvgl_display.elf -L/home/jichu/esp32/esp-idf/components/soc/esp32s3/ld -L/home/jichu/esp32/esp-idf/components/esp_rom/esp32s3/ld -L/home/jichu/esp32/lvgl_display/build/esp-idf/esp_system/ld -L/home/jichu/esp32/esp-idf/components/esp_phy/lib/esp32s3 -L/home/jichu/esp32/esp-idf/components/esp_coex/lib/esp32s3 -L/home/jichu/esp32/esp-idf/components/esp_wifi/lib/esp32s3 -L/home/jichu/esp32/esp-idf/components/bt/controller/lib_esp32c3_family/esp32s3 esp-idf/xtensa/libxtensa.a esp-idf/esp_ringbuf/libesp_ringbuf.a esp-idf/efuse/libefuse.a esp-idf/esp_mm/libesp_mm.a esp-idf/driver/libdriver.a esp-idf/esp_pm/libesp_pm.a esp-idf/mbedtls/libmbedtls.a esp-idf/esp_bootloader_format/libesp_bootloader_format.a esp-idf/esp_app_format/libesp_app_format.a esp-idf/bootloader_support/libbootloader_support.a esp-idf/esp_partition/libesp_partition.a esp-idf/app_update/libapp_update.a esp-idf/spi_flash/libspi_flash.a esp-idf/pthread/libpthread.a esp-idf/esp_system/libesp_system.a esp-idf/esp_rom/libesp_rom.a esp-idf/hal/libhal.a esp-idf/log/liblog.a esp-idf/heap/libheap.a esp-idf/soc/libsoc.a esp-idf/esp_hw_support/libesp_hw_support.a esp-idf/freertos/libfreertos.a esp-idf/newlib/libnewlib.a esp-idf/cxx/libcxx.a esp-idf/esp_common/libesp_common.a esp-idf/esp_timer/libesp_timer.a esp-idf/app_trace/libapp_trace.a esp-idf/esp_event/libesp_event.a esp-idf/nvs_flash/libnvs_flash.a esp-idf/esp_phy/libesp_phy.a esp-idf/vfs/libvfs.a esp-idf/lwip/liblwip.a esp-idf/esp_netif/libesp_netif.a esp-idf/wpa_supplicant/libwpa_supplicant.a esp-idf/esp_coex/libesp_coex.a esp-idf/esp_wifi/libesp_wifi.a esp-idf/bt/libbt.a esp-idf/unity/libunity.a esp-idf/cmock/libcmock.a esp-idf/console/libconsole.a esp-idf/http_parser/libhttp_parser.a esp-idf/esp-tls/libesp-tls.a esp-idf/esp_adc/libesp_adc.a esp-idf/esp_eth/libesp_eth.a esp-idf/esp_gdbstub/libesp_gdbstub.a esp-idf/esp_hid/libesp_hid.a esp-idf/tcp_transport/libtcp_transport.a esp-idf/esp_http_client/libesp_http_client.a esp-idf/esp_http_server/libesp_http_server.a esp-idf/esp_https_ota/libesp_https_ota.a esp-idf/esp_lcd/libesp_lcd.a esp-idf/protobuf-c/libprotobuf-c.a esp-idf/protocomm/libprotocomm.a esp-idf/esp_local_ctrl/libesp_local_ctrl.a esp-idf/espcoredump/libespcoredump.a esp-idf/wear_levelling/libwear_levelling.a esp-idf/sdmmc/libsdmmc.a esp-idf/fatfs/libfatfs.a esp-idf/json/libjson.a esp-idf/mqtt/libmqtt.a esp-idf/nvs_sec_provider/libnvs_sec_provider.a esp-idf/perfmon/libperfmon.a esp-idf/spiffs/libspiffs.a esp-idf/touch_element/libtouch_element.a esp-idf/usb/libusb.a esp-idf/wifi_p
03-14
<think>好的,我现在要处理用户提供的关于ESP32-S3项目编译失败的日志。首先,我需要仔细阅读日志内容,找出关键错误信息。用户执行了`ninja flash`命令,但在链接阶段出现了问题,具体是`FAILED: lvgl_display.elf`,说明生成可执行文件时失败。 首先,我注意到链接器命令中使用了`xtensa-esp32s3-elf-g++`,这可能涉及到C++的链接问题。常见的失败原因可能包括内存不足、缺失或版本不兼容、符号未定义等。用户的环境是ESP-IDF,可能使用了LVGL,所以需要检查相关依赖是否正确配置。 日志中的错误没有直接显示具体的错误信息,这可能是因为输出被截断或者需要更详细的编译日志。因此,我应该建议用户增加编译的详细程度,例如运行`ninja -v flash`来获取更多信息。此外,检查是否有未定义的引用(undefined reference)或路径问题。 另一个可能性是项目的sdkconfig配置不正确,特别是对于ESP32-S3的配置,例如PSRAM设置、分区表大小等。用户需要确认`sdkconfig`中是否选择了正确的芯片型号,以及LVGL相关配置是否适配ESP32-S3。 内存不足也是一个常见问题,尤其是在链接阶段。Bootloader的大小显示为0x5240字节,剩余36%的空间,这可能没问题,但主应用程序可能超出了可用内存。需要检查分区表是否合理,以及应用程序的代码量内存需求。 还有可能是工具链的问题,比如xtensa-esp32s3-elf-g++的版本是否与ESP-IDF版本兼容。用户使用的ESP-IDF版本是否支持所选的LVGL版本?需要确认版本兼容性,并尝试重新安装或更新工具链。 此外,检查CMakeLists.txt组件依赖是否正确,特别是LVGL组件是否被正确包含,路径是否正确。如果用户自定义了组件,可能存在路径错误或编译选项冲突。 最后,建议用户清理构建目录(删除build文件夹)并重新编译,有时候旧的构建文件会导致链接错误。如果问题依旧,查看更详细的日志或尝试简化项目以隔离问题。</think>您在尝试通过 `ninja flash` 命令烧录 ESP32-S3 固件时遇到了链接阶段的编译错误。以下是问题分析解决步骤: --- ### **错误定位** 日志显示关键错误: ```bash FAILED: lvgl_display.elf : && /home/jichu/.espressif/tools/.../xtensa-esp32s3-elf-g++ ... (链接命令) ``` 此错误表明在生成最终可执行文件 `lvgl_display.elf` 时,链接器 (`xtensa-esp32s3-elf-g++`) 失败。 --- ### **可能原因** 1. **内存不足** - Bootloader 剩余空间:$$ 0x5240 \text{ bytes ()} - 0x2dc0 \text{ bytes (剩余)} = 0x2480 \text{ bytes (已用)} $$ - 主程序 (`lvgl_display.elf`) 可能超出芯片 Flash 或 RAM 限制。 2. **依赖缺失或冲突** - 检查是否缺少 LVGL 相关(如 `lvgl/liblvgl.a`)。 - 确保所有组件(如 Wi-Fi、SPIFFS)在 `sdkconfig` 中正确配置。 3. **符号未定义 (Undefined Reference)** - 常见于未实现函数、头文件未包含或未链接。 4. **工具链版本不兼容** - ESP-IDF 版本与 LVGL 组件或第三方存在兼容性问题。 --- ### **解决步骤** #### 1. **启用详细日志** 重新编译并输出详细错误信息: ```bash ninja -v flash | tee build.log ``` 检查 `build.log` 中具体的未定义符号或路径错误。 --- #### 2. **检查内存配置** - **分区表**:确认 `partitions.csv` 中 `app` 分区大小是否足够。 - **sdkconfig**:通过 `idf.py menuconfig` 检查: - **Component config → LVGL → Memory settings**(调整 LVGL 缓冲区大小)。 - **Serial flasher config → Flash Size** 是否匹配硬件。 --- #### 3. **验证依赖关系** - **组件路径**:确保 LVGL 组件路径正确(`CMakeLists.txt` 中 `EXTRA_COMPONENT_DIRS`)。 - **链接**:在 `CMakeLists.txt` 中添加缺失: ```cmake target_link_libraries(${PROJECT_NAME} PRIVATE lvgl) ``` --- #### 4. **清理并重新编译** 清除旧构建文件: ```bash rm -rf build idf.py build ``` --- #### 5. **缩小问题范围** - **最小化代码**:注释掉 LVGL 初始化代码,逐步排查功能模块。 - **更新工具链**:重新安装 ESP-IDF 或更新至稳定版本(如 v5.1)。 --- ### **扩展建议** - **使用官方例程**:从 `esp-idf/examples/peripherals/lcd/lvgl` 测试基础功能。 - **内存分析工具**:通过 `idf.py size-components` 查看各组件内存占用。 如果仍有问题,请提供 `build.log` 中的具体错误信息,以便进一步诊断!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.Lanson

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值