图3.6.2 bootloader_start.c文件路径
在这个文件下,找到call_start_cpu0函数,此函数是bootloader程序,如下是bootloader程序的部分代码。
/*
ROM引导加载程序完成从闪存加载第二阶段引导加载程序之后到达这里
*/
void __attribute__((noreturn)) call_start_cpu0(void)
{
if (bootloader_before_init) {
bootloader_before_init();
}
/* 1. 硬件初始化:清楚bss段、开启cache、复位mmc等操作
bootloader_support/src/esp32s3/bootloader_esp32s3.c */
if (bootloader_init() != ESP_OK) {
bootloader_reset();
}
if (bootloader_after_init) {
bootloader_after_init();
}
/* 2. 选择启动分区的数量:加载分区表,选择boot分区 */
bootloader_state_t bs = {0};
int boot_index = select_partition_number(&bs);
if (boot_index == INVALID_INDEX){
bootloader_reset();
}
/* 3. 加载应用程序映像并启动
bootloader_support/src/esp32s3/bootloader_utility.c */
bootloader_utility_load_boot_image(&bs, boot_index);
}
ESP-IDF使用二级引导程序可以增加FLASH分区的灵活性(使用分区表),并且方便实现FLASH加密,安全引导和空中升级(OTA)等功能。主要的作用是从flash的0x8000处加载分区表(请看在线ESP32-IDF编程指南分区表章节)。根据分区表运行应用程序。
三、三级引导程序
应用程序的入口是在esp-idf/components/esp_system/port/路径下的cpu_star.c文件,在此文件下找到call_start_cpu0函数(端口层初始化函数)。这个函数由二级引导加载程序执行,并且从不返回。因此你看不到是哪个函数调用了它,它是从汇编的最底层直接调用的。
这个函数会初始化基本的C运行环境(“CRT”),并对SOC的内部硬件进行了初始配置。执行call_start_cpu0函数完成之后,在components\esp_system\startup.c文件下调用start_cpu0(在110行中,弱关联start_cpu0_default函数)系统层初始化函数,如下start_cpu0_default函数的部分代码。
static void start_cpu0_default(void)
{
ESP_EARLY_LOGI(TAG, "Pro cpu start user code");
/* 获取CPU时钟 */
int cpu_freq = esp_clk_cpu_freq();
ESP_EARLY_LOGI(TAG, "cpu freq: %d Hz", cpu_freq);
/* 初始化核心组件和服务 */
do_core_init();
/* 执行构造函数 */
do_global_ctors();
/* 执行其他组件的init函数 */
do_secondary_init();
/* 开启APP程序 */
esp_startup_start_app();
while (1);
}
到了这里,就完成了二级程序引导,并调用esp_startup_start_app函数进入三级引导程序,该函数的源码如下:
/* components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c */
/* 开启APP程序 */
void esp_startup_start_app(void)
{ /* 省略部分代码 */
/* 新建main任务函数 */
esp_startup_start_app_common();
/* 开启FreeRTOS任务调度 */
vTaskStartScheduler();
}
/* components/freertos/FreeRTOS-Kernel/portable/port_common.c */
/* 新建main任务函数 */
void esp_startup_start_app_common(void)
{
/* 省略部分代码 */
/* 创建main任务 */
portBASE_TYPE res = xTaskCreatePinnedToCore(&main_task, "main",
ESP_TASK_MAIN_STACK, NULL,
ESP_TASK_MAIN_PRIO, NULL, ESP_TASK_MAIN_CORE);
assert(res == pdTRUE);
(void)res;
}
/* main任务函数 */
static void main_task(void* args)
{ /* 省略部分代码 */
/* 执行app_main函数 */
app_main();
vTaskDelete(NULL);
}
从上述源码可知,首先在esp_startup_start_app_common函数调用FreeRTOS API创建main任务,然后开启freeRTOS任务调度器,最后在main任务下调用app_main函数(此函数在创建工程时,在main.c下定义的)。