《ESP-IDF入门指南》----->应用程序的启动流程(app_main如何被调用的)

app_main如何被调用的


ESP32 从上电到运行 app_main 函数中间所经历的步骤(即启动流程)分为三个步骤.

  1. 一级引导程序 被固化在了 ESP32 内部的 ROM 中,它会从 flash 的 0x1000 偏移地址处加载二级引导程序至 RAM (IRAM & DRAM) 中。
  2. 二级引导程序 从 flash 中加载分区表和主程序镜像至内存中,主程序中包含了 RAM 段和通过 flash 高速缓存映射的只读段。
  3. 应用程序启动阶段 ,这时第二个 CPU 和 RTOS 的调度器启动。

一级引导程序

Soc复位后,PRO CPU会立即执行,执行复位向量代码,APP CPU 仍然保持复位状态。在启动过程中,PRO CPU 会执行所有的初始化操作。APP CPU的复位状态在执行call_start_cpu0函数后失效。

复位向量调用的启动代码会根据 GPIO_STRAP_REG 寄存器的值来确定 ESP32 的启动模式,根据不同的复位原因,程序会执行如下操作:

  1. 从深度睡眠模式复位

    1. 如果 RTC_CNTL_STORE6_REG 寄存器的值非零,且 RTC_CNTL_STORE7_REG 寄存器中的 RTC 内存的 CRC 校验值有效,那么程序会使用 RTC_CNTL_STORE6_REG 寄存器的值作为入口地址,并立即跳转到该地址运行。
    2. 如果 RTC_CNTL_STORE6_REG 的值为零,或 RTC_CNTL_STORE7_REG 中的 CRC 校验值无效,又或通过 RTC_CNTL_STORE6_REG 调用的代码返回,那么则像上电复位一样继续启动。
  2. 上电复位、软件 SoC 复位、看门狗 SoC 复位

    检查 GPIO_STRAP_REG 寄存器,判断是否请求自定义启动模式,如 UART 下载模式。如果是,ROM 会执行此自定义加载模式,否则会像软件 CPU 复位一样继续启动。

  3. 软件 CPU 复位、看门狗 CPU 复位

    根据 EFUSE 中的值配置 SPI flash,然后尝试从 flash 中加载代码。

注:

  1. SoC:(System on Chips) 称为系统级芯片,或者片上系统。意指它是一个产品,是一个有专用目标的集成电路,其中包含完整系统并有嵌入软件的全部内容。
  2. PRO CPU:ESP32是双核CPU结构。两个CPU可以称为(CPU0和CPU1)或者(APP CPU和PRO CPU)。
  3. GPIO_STRAP_REG:保存着bootstrap引脚的电平状态。(ESP32技术手册)
  4. RTC_CNTL_STORE6_REG :32 位通用保留寄存器。(读/写)

二级引导程序

在 ESP-IDF 中,存放在 flash 的 0x1000 偏移地址处的二进制镜像就是二级引导程序。二级引导程序的源码可以在 ESP-IDF 的 [components/bootloader]目录下找到。ESP-IDF 使用二级引导程序可以增加 flash 分区的灵活性(使用分区表),并且方便实现 flash 加密,安全引导和空中升级 (OTA) 等功能。

当一级引导程序校验并加载完二级引导程序后,它会从二进制镜像的头部找到二级引导程序的入口点,并跳转过去运行。

二级引导程序默认从 flash 的 0x8000 偏移地址处[可配置的值]读取分区表。请参考 [分区表] 获取详细信息。引导程序会寻找工厂分区和 OTA 应用程序分区。如果在分区表中找到了 OTA 应用程序分区,引导程序将查询 otadata 分区以确定应引导哪个分区。更多信息请参考 [空中升级 (OTA)]。

关于 ESP-IDF 引导程序可用的配置选项,请参考 [引导加载程序 (Bootloader)]。

对于选定的分区,二级引导程序将从 flash 逐段读取二进制镜像:

  • 对于在内部 [IRAM(指令 RAM)]或 [DRAM(数据 RAM)] 中具有加载地址的段,将把数据从 flash 复制到它们的加载地址处。
  • 对于一些加载地址位于 [DROM(数据存储在 flash 中)]或 [IROM(代码从 flash 中运行)]区域的段,通过配置 flash MMU,可为从 flash 到加载地址提供正确的映射。

请注意,二级引导程序同时为 PRO CPU 和 APP CPU 配置 flash MMU,但仅使能 PRO CPU 的 flash MMU。原因是二级引导程序代码已加载到 APP CPU 的高速缓存使用的内存区域中。因此使能 APP CPU 高速缓存的任务就交给了应用程序。

一旦处理完所有段(即加载了代码并设置了 flash MMU),二级引导程序将验证应用程序的完整性,并从二进制镜像文件的头部寻找入口地址,然后跳转到该地址处运行。

注:


应用程序启动阶段

应用程序启动包含了从应用程序开始执行到 app_main 函数在主任务内部运行前的所有过程。可分为三个阶段:

  • 硬件和基本 C 语言运行环境的端口初始化。
  • 软件服务和 FreeRTOS 的系统初始化。
  • 运行主任务并调用 app_main

注: 通常不需要了解 ESP-IDF 应用程序初始化的所有阶段。如果需要仅从应用程序开发人员的角度了解初始化,请跳至 [运行主任务]。

  1. 运行主任务:

    在所有其他组件都初始化后,主任务会被创建,FreeRTOS 调度器开始运行。

    做完一些初始化任务后(需要启动调度器),主任务在固件中运行应用程序提供的函数 app_main

    运行 app_main 的主任务有一个固定的 RTOS 优先级(比最小值高)和一个 可配置的堆栈大小。

    与普通的 FreeRTOS 任务(或嵌入式 C 的 main 函数)不同,app_main 任务可以返回。如果app_main 函数返回,那么主任务将会被删除。系统将继续运行其他的 RTOS 任务。因此可以将 app_main 实现为一个创建其他应用任务然后返回的函数,或主应用任务本身。


代码层面调用

文字终归枯燥无味,接下来,我们在代码层面来了解下app_main是如何一步步被调用的。

一级引导程序 中,了解到APP CPU会调用call_start_cpu0函数。

call_start_cpu0函数的结尾处调用SYS_STARTUP_FNSYS_STARTUP_FN是一个宏定义。具体内容是((*g_startup_fn[(esp_cpu_get_core_id())])()),该数组是在startup.c中被赋值。

void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))) __attribute__((noreturn));


const sys_startup_fn_t g_startup_fn[SOC_CPU_CORES_NUM] = { [0] = start_cpu0,
#if SOC_CPU_CORES_NUM > 1
    [1 ... SOC_CPU_CORES_NUM - 1] = start_cpu_other_cores
#endif
};

接下来,继续查看start_cpu0_default 函数的源代码,该函数在结尾处会调用esp_startup_start_app函数。

esp_startup_start_app函数里面,使用xTaskCreatePinnedToCore函数创建main_task,

    BaseType_t res = xTaskCreatePinnedToCore(main_task, "main",
                                             ESP_TASK_MAIN_STACK, NULL,
                                             ESP_TASK_MAIN_PRIO,NULL,ESP_TASK_MAIN_CORE);

而在main_task任务中则会调用app_main函数.到此为止,我们就找到了app_main的调用关系.

注:call_start_cpu0: esp-idf\v5.0.6\esp-idf\components\esp_system\port\cpu_start.c

start_cpu0 g_startup_fn: esp-idf\v5.0.6\esp-idf\components\esp_system\startup.c

esp_startup_start_app: esp-idf\v5.0.6\esp-idf\components\freertos\app_startup.c

  • 33
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不知名小白11

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

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

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

打赏作者

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

抵扣说明:

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

余额充值