> 文档中心 > ESP32-C3 应用程序的启动流程

ESP32-C3 应用程序的启动流程

网上说明一大堆,基本是官网文档复制没有额外解释!对于ESP32-C3的 risc-v 内核,是我选择他的原因之一,了解芯片上电后的启动流程,有利于我们更加深入理解芯片。

目录

  • 前言
  • 一、应用程序启动阶段
    • 1.1 app_main.c
    • 1.2 port_common.c
    • 1.3 port.c
    • 1.4 startup.c
    • 1.5 startup_internal.h
    • 1.6 cpu_start.c
    • 1.7 esp32c3.project.ld.in
  • 二、二级引导程序
  • 结语

前言

对于ARM内核的STM32的启动流程,我以前的博文详细分析过,搞懂了STM32的启动流程对于芯片的使用和理解来说就会更上一个等级。现在我们新接触的 risc-v 内核的ESP32-C3,如果能够搞明白他的启动流程,就能更深的理解 ESP32-C3。

在写文章之前也看了很多网上的文章,然后官方的说明也看过了,网上绝大多数都是官网文档复制一遍,这倒没什么,毕竟官网权威,问题是,复制一遍过来没有做过多的解释,没有额外的分析,还写个原创,我真是XX了。当然不排除我没看到的好的文章,也正是因为基本没有看到更加详细的分析,我决定要自己写一篇记录,一来当做自己记录,二来底层有些地方目前来说我确实不是理解透彻,肯定有不到位的地方,希望大家多提意见,能让文章更加完善。

按照惯例,我把能找到的文章和官方的资料都看了好多遍,官方提到的3大步骤:
ESP32-C3 应用程序的启动流程

对于上述提到的3大步骤,最简单的清晰的是第三步,第二步也有源码,倒是可以查看试着分析一下,但是第一步确实,只是知道这么回事,具体的实现方式因为程序是在ESP32-C3 的内部 ROM 中,确实找不到可以分析的源码和资料。

芯片的启动流程,大多离不开启动文件和链接文件。ESP32-C3应该也是这样,本来按照理解,应该找到底层的启动文件和连接文件开始按照步骤分析,但是查看了一会底层代码,因为对底层架构深入了解得还不够,没有找到= =! 所以想着怎么办?

不能按照从最开始到结束的顺序来,那么就按照从结束到开始的顺序来!我们从app_main 函数反过来一层一层网前看,看看经过了一些什么处理,程序才执行到app_main 函数的!

注意,文章以倒叙的方式说明~ ~

本文是基于 VScode 插件的工程结构来说明(Ubuntu环境),环境搭建见下面博文:

ESP32-C3 VScode开发环境搭建(基于乐鑫官方ESP-IDF——Windows和Ubuntu双环境)

一、应用程序启动阶段

1.1 app_main.c

我们从app_main.c的主函数app_main中,我们直接通过转到定义看看上一层:
在这里插入图片描述

1.2 port_common.c

app_main 往上找的文件是 port_common.c,路径如下:
ESP32-C3 应用程序的启动流程

是哪一个任务调用了app_main呢,我们直接往上就能看到,
main_task调用了app_main
main_task这个任务就在esp_startup_start_app_common函数中创建:
在这里插入图片描述
知道了esp_startup_start_app_common 函数创建了main_task这个任务,
那么这个函数在哪边有调用呢?我们继续往上看,
找到esp_startup_start_app函数中调用了esp_startup_start_app_common
调用完成以后就开启了 FreeRTOS 任务调度。

在这里插入图片描述
对应的,我们看一下启动LOG:
在这里插入图片描述

1.3 port.c

我们又进入了一个新的文件 port.c,路径如下:
ESP32-C3 应用程序的启动流程
接着上面的,从esp_startup_start_app函数往上找,
又找到一个start_cpu0_default函数,如图:
在这里插入图片描述

1.4 startup.c

又找到一个新文件startup.c,路径如下:
ESP32-C3 应用程序的启动流程
start_cpu0_default

startup.c 文件的 start_cpu0_default函数,其中进行了很多关键步骤,具体可以自行查看代码:
在这里插入图片描述
对应的,我们看一下启动LOG:
在这里插入图片描述

我们还想看 start_cpu0_default函数从哪里调用,但是这时候已经无法跳转了,这时候我们依然在startup.c文件中搜索,可以找到下面一句话:
ESP32-C3 应用程序的启动流程
这句话的意思就是使得 start_cpu0_default函数弱连接到start_cpu0函数,如果没有额外的声明,start_cpu0_default函数等价于start_cpu0函数。(有错误请指出)

继续看看与 start_cpu0函数 有关的程序,g_startup_fn好像是一个数组?如图:
在这里插入图片描述
尝试着对 g_startup_fn 使用转到定义,我们进入了另外一个新的文件,可以找到一个宏定义SYS_STARTUP_FN()
在这里插入图片描述

1.5 startup_internal.h

老样子先看文件路径如下:
ESP32-C3 应用程序的启动流程

这是一个头文件,所以这里面肯定都是函数的声明和宏定义,我们在这个文件中需要关注的地方就是一个宏定义#define SYS_STARTUP_FN() ((*g_startup_fn[(cpu_hal_get_core_id())])())
ESP32-C3 应用程序的启动流程
我们接下来要找的就是这个宏定义SYS_STARTUP_FN()在哪里调用了。

因为是头文件,找函数调用就复杂一点,但是问题不大,如下图:
在这里插入图片描述
点进去确实发现是我所需要查找的文件,如果找不到,可以在目录下面搜索,先小目录,还是找不到,再用上层目录。 这也是看底层源码的基本方法。

1.6 cpu_start.c

老样子先看文件路径如下:
ESP32-C3 应用程序的启动流程
cpu_start.c文件中,可以看到确实有上面头文件中宏定义SYS_STARTUP_FN()的调用,里面主要就是几个启动cpu的函数,
其中是call_start_cpu0函数调用了这个宏定义:
在这里插入图片描述
函数的内容是很多的,这里我们就不一一分析。但是到了这里,我们再结合官方的说明文档再看一遍:
在这里插入图片描述
这样子去理解,感觉官方说得好简单,实际上都是层层调用。

分析到这里,我们都已经进入入口函数了,那么程序是如何调用。按照以前的理解,这个入口函数应该会在一个连接脚本里面使用 ENTRY调用,就像ENTRY(call_start_cpu0);

实际上,如果去文件夹搜索可以找到很多文件中有ENTRY(call_start_cpu0);,即便我们值选择与 ESP32-C3 有关的,也有不同的地方,那么到底是哪个呢?我们还得继续顺藤摸瓜!

1.7 esp32c3.project.ld.in

在组件的esp32c3目录下面搜索call_start_cpu0,找到一个使用了ENTRY(call_start_cpu0);的链接文件:(有错误请指出)
在这里插入图片描述
这个和我们的STM32中分析中的类似,就是进入到入口函数执行:
在这里插入图片描述

文件路径:
ESP32-C3 应用程序的启动流程

我们还得往下简单看看二级引导程序。

二、二级引导程序

二级引导程序,基于现在我的理解,还不足以能够完全的讲清楚,官方提到源码在 ESP-IDF 的 components/bootloader 目录下,我也简单的看了一下,在目录中找到比较“可能”的程序:
在这里插入图片描述
在这个bootloader_start.c文件中,正如官方的说明一样,ESP-IDF 使用二级引导程序可以增加 Flash 分区的灵活性(使用分区表)等一些功能:
在这里插入图片描述
继续查看了一些代码(具体的就是在bootloader_start.c文件相关联的地方查找),确定bootloader_start.c就是二级引导程序的代码,根据LOG输出判断的 = =!
在这里插入图片描述
在bootloader 目录下也有 ESP32-C3 的链接文件:
在这里插入图片描述
在其中也有ENTRY(call_start_cpu0);的调用,这个应该是和二级引导程序中bootloader_start.c中对应的链接文件(有问题清指出)。
在这里插入图片描述

结语

文章在前面C语言启动的部分倒是还算满意,到底层以后,对陌生的 risc-v 内核和架构还是处于模糊的状态,希望小伙伴多多给些意见。

文章整体只能算是代码的流程解析,并没有分析函数的功能,这是我不满意的地方,冰冻三尺非一日之寒,加油!
文章会随着博主的理解深入保持更新,希望以后能有时间从内存,从架构好好的完善一下文章!