宏观上,该启动流程可以分为如下 3 个步骤:
- 一级引导程序 被固化在了 ESP32 内部的 ROM 中,它会从 flash 的 0x1000 偏移地址处加载二级引导程序至 RAM (IRAM & DRAM) 中。
- 二级引导程序 从 flash 中加载分区表和主程序镜像至内存中,主程序中包含了 RAM 段和通过 flash 高速缓存映射的只读段。
- 应用程序启动阶段 运行,这时第二个 CPU 和 RTOS 的调度器启动。
函数调用流程
(esp_system)call_start_cpu0
-> SYS_STARTUP_FN
-> g_startup_fn
-> start_cpu0
-> start_cpu0_default
-> (freertos)esp_startup_start_app
-> esp_startup_start_app_common
-> main_task
-> app_main
->
应用程序启动包含了从应用程序开始执行到
app_main
函数在主任务内部运行前的所有过程。可分为三个阶段:
- 硬件和基本 C 语言运行环境的端口初始化。
- 软件服务和 FreeRTOS 的系统初始化。
- 运行主任务并调用
app_main
。
通常不需要了解 ESP-IDF 应用程序初始化的所有阶段。如果需要仅从应用程序开发人员的角度了解初始化,请注重 运行主任务。
在所有其他组件都初始化后,主任务会被创建,
FreeRTOS
调度器开始运行。
做完一些初始化任务后(需要启动调度器),主任务在固件中运行应用程序提供的函数
app_main
。
运行
app_main
的主任务有一个固定的 RTOS 优先级(比最小值高)和一个 可配置的堆栈大小。
主任务的内核亲和性也是可以配置的,请参考 CONFIG_ESP_MAIN_TASK_AFFINITY。
与普通的
FreeRTOS
任务(或嵌入式 C 的main
函数)不同,app_main
任务可以返回。如果app_main
函数返回,那么主任务将会被删除。系统将继续运行其他的RTOS
任务。因此可以将app_main
实现为一个创建其他应用任务然后返回的函数,或主应用任务本身。