对于esp32,其开发程序中有且只能有一个app_main函数,该函数是用户程序的入口,这在没有调用FreeRTOS的系统中相当于函数main,但其实在app_main之前,系统还有一段初始化的过程,其大致可以分为以下三个过程:

  1. ROM中的第一级引导加载程序将闪存偏移0x1000的第二级引导加载程序映像加载到RAM(IRAM和DRAM)。
  2. 第二级引导程序从闪存加载分区表和主应用程序映像。主应用程序包含RAM段和通过闪存缓存映射的只读段。
  3. 主应用程序图像执行。此时可以启动第二个CPU和RTOS调度程序。

以下将详细介绍这三个过程

STEP1:

第一阶段引导程序

      系统first-stage bootload启动,对于系统的first-stage bootloader,其主要任务是负责从Flash的地址0X1000开始加载bootloader镜像到RAM中(此工程的bootloader文件由esp-idf中的component 目录下的bootloader\subproject\main\bootloader_start.c可以查看源码),在SoC复位后,PRO CPU将立即开始运行,执行复位向量代码,而APP CPU将保持复位。在启动过程中,PRO CPU执行所有初始化。call_start_cpu0应用程序启动代码功能中的APP CPU复位被取消置位。复位向量代码位于ESP32芯片掩码ROM中的地址0x40000400,不能修改。

    从复位向量调用的启动代码通过检查GPIO_STRAP_REG(gpio_reg.h定义的)引导引脚状态的寄存器来确定引导模式。根据复位原因,发生以下情况:

  1. 从深度睡眠复位:如果值为RTC_CNTL_STORE6_REG非零,并且RTC存储器的CRC值RTC_CNTL_STORE7_REG有效,RTC_CNTL_STORE6_REG则将其用作入口点地址并立即跳转到其中。如果RTC_CNTL_STORE6_REG为零,或RTC_CNTL_STORE7_REG包含无效的CRC,或者一旦调用通过RTC_CNTL_STORE6_REG返回的代码,继续进行启动,就好像是上电复位一样。注意:此时运行自定义代码,提供了一个深度睡眠存根机制。请参阅深度睡眠文档。
  2. 对于上电复位,软件SOC复位和看门狗SOC复位:GPIO_STRAP_REG如果要求UART或SDIO下载模式,请检查寄存器。如果是这种情况,请配置UART或SDIO,并等待下载代码。否则,继续进行启动,就好像是由于软件CPU复位。
  3. 对于软件CPU复位和看门狗CPU复位:根据EFUSE值配置SPI闪存,并尝试从闪存加载代码。这一步在下面的段落中有更详细的描述。如果从闪存加载代码失败,请将BASIC解释器解压缩到RAM中并启动它。请注意,当发生这种情况时,RTC看门狗仍然使能,因此除非解释器接收到任何输入,否则看门狗将在几百毫秒内重置SOC,重复整个过程。如果解释器从UART接收到任何输入,它将禁用看门狗。

    可以看出,第一阶段主要是为了第二阶段做铺垫,应用程序二进制从地址0x1000开始从闪存加载。第一个4kB闪存扇区用于存储安全引导IV和应用程序映像的签名。请检查安全引导文档以获取有关的详细信息。

STEP2:

   在ESP-IDF中,闪存中位于0x1000位置的二进制映像是第二级引导加载程序。ESP-IDF的组件/ bootloader目录中提供了第二阶段引导加载程序源代码。请注意,这种安排并不是ESP32芯片中唯一可能的。可以编写一个功能齐全的应用程序,当闪存到0x1000时,该应用程序将工作,ESP-IDF中使用第二阶段引导加载程序来增加闪存布局的灵活性(使用分区表),并允许发生与闪存加密,安全引导和空中更新(OTA)相关的各种流程。

    当第一阶段引导加载程序完成检查和加载第二阶段引导加载程序时,它跳转到二进制映像头中找到的第二阶段引导加载程序入口点。

    第二阶段引导程序读取在偏移0x8000处找到的分区表。有关更多信息,请参阅分区表文档。引导加载程序找到工厂和OTA分区,并根据在OTA信息分区中找到的数据来决定哪一个进行引导。

    对于所选分区,第二级引导加载程序将映射到IRAM和DRAM的数据和代码段复制到其加载地址。对于在DROM和IROM区域中具有加载地址的部分,Flash MMU配置为提供正确的映射。请注意,第二阶段引导加载程序为PRO和APP CPU配置闪存MMU,但只能为PRO CPU启用闪存MMU。这样做的原因是第二阶段引导程序代码被加载到APP CPU缓存使用的内存区域中。启用APP CPU的缓存的功能被传递给应用程序。一旦加载了代码并且设置了闪存MMU,则第二级引导加载程序将跳转到二进制映像头中的应用程序入口点。

    目前,官方并不支持加载程序添加应用程序定义来自己定义应用程序分区选择逻辑。例如:根据GPIO的状态,可能需要加载不同的应用程序映像。(但据说未来会支持),现在我们能做的是,通过将bootloader组件复制到应用程序目录中并在那里进行必要的更改来定制自己的Bootloader。在这种情况下,ESP-IDF构建系统将编译应用程序目录中的组件而不是ESP-IDF组件目录。

STEP3:

    主函数镜像开始执行,即main_task,(ESP-IDF应用程序入口点是call_start_cpu0,可在components/esp32/cpu_start.c中找到。)这个功能的两个主要作用是启用堆分配器并使APP CPU跳到其入口点call_start_cpu1。PRO CPU上的代码设置APP CPU的入口点,取消置位APP CPU复位,并等待由APP CPU上运行的代码设置的全局标志,表示已启动。一旦完成,PRO CPU跳转到start_cpu0功能,并且APP CPU将跳转到start_cpu1功能。

    这两个start_cpu0start_cpu的功能并不是不可修改的,start_cpu0根据所做的选择启用或初始化组件默认实现menuconfig,你可以通过查看omponents/esp32/cpu_start.c观察最新的执行步骤列表,不过值得注意的是,此阶段将调用应用程序中存在的所有C++全局构造函数。一旦所有基本组件都被初始化,则创建主任务,并启动FreeRTOS调度程序。对于esp32我们知道的是,他是一个双核cpu,在这个过程中,当PRO CPU在start_cpu0功能中进行初始化时,APP CPU会自动start_cpu1运行功能,等待在PRO CPU上启动调度程序。一旦在PRO CPU上启动了调度程序,APP CPU上的代码也启动了调度程序。

    main_task,的任务是可以配置主任务堆栈大小和优先级menuconfig。当然我们可以使用此任务进行初始的应用程序特定设置,例如启动其他任务。应用程序还可以使用事件循环和其他通用活动的主要任务。但是需要注意的是如果app_main函数返回,main_task将被删除。

下面将源码中的main_task贴出来

static void main_task(void* args)
{
// Now that the application is about to start, disable boot watchdogs
REG_CLR_BIT(TIMG_WDTCONFIG0_REG(), TIMG_WDT_FLASHBOOT_MOD_EN_S);
REG_CLR_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN);
#if !CONFIG_FREERTOS_UNICORE
// Wait for FreeRTOS initialization to finish on APP CPU, before replacing its startup stack
while (port_xSchedulerRunning[] == ) {
;
}
#endif
//Enable allocation in region where the startup stacks were located.
heap_caps_enable_nonos_stack_heaps();
app_main();
vTaskDelete(NULL); }

可以看到,其主要任务是关闭看门狗,并调用app_main函数(即用户二次开发时的主函数入口),因为main_task中没有循环,在app_main执行后,vTaskDelet函数就将这个任务删除了。

  下面说一下,关于esp32的开发,在开发这种Wi-Fi cpu时 。因为非arm芯片,官方也没有提供详细的寄存器配置文档,虽然寄存器和引脚功能都在esp-idf中以英文注释给出来了,但是esp开头的api基本都是以链接库方式提供的,你是看不到它的实现方式,所以比较合理快捷的开发方式,由于官方给出的esp-idf中自带FreeRTOS操作系统,且官方文档中给出来相关函数的API手册中,已经将相关应用需要用到的API函数接口给出的比较详细了,且esp-idf中对于相关的应用给出了大量的示例(esp-idf/examples),所以对于esp32学习来说,我觉得比较好的学习方法是:

   对于没有嵌入式操作系统基础的人来说,最好是先学习下FreeRTOS的操作系统,重点学习相关队列及任务调度部分的应用,如果有过操作系统的学习和应用,但是使用的并不是FreeRTOS而是ucos之类的操作系统,那么建议看下FreeRTOS的API手册就可以开始进行开发了。对于esp32部分,这里建议学习下官方提供的例程,学习自己需要用到功能部分的API的调用和使用方法,并以此学习相关功能的应用方法,(这一部分将是我后面文章的重点部分)最终完成产品的相关设计。

关于esp32的系统初始化启动过程及设计学习方法的更多相关文章

  1. 探索 Linux 系统的启动过程

    引言 之所以想到写这些东西,那是因为我确实想让大家也和我一样,把 Linux 桌面系统打造成真真正正日常使用的工具,而不是安装之后试用几把再删掉.我是真的在日常生活和工作中都使用 Linux,比如在 ...

  2. Linux系统的启动过程

    Linux 系统启动过程 Linux系统的启动过程可以分为5个阶段: BIOS自检 内核的引导. 运行init. 系统初始化. 用户登录系统. BIOS自检: BIOS是英文"Basic I ...

  3. Linux基础-6.系统的启动过程

    Linux启动时我们会看到许多启动信息 Linux系统的启动过程并不是大家想象中的那么复杂,其过程可以分为5个阶段: 内核的引导 运行init 系统初始化 建立终端 用户登录系统 init程序的类型: ...

  4. 001---Linux系统的启动过程

    Linux系统的启动过程 按下电源 开机自检(BIOS):检查cpu.内存.硬盘是否有问题,找到启动盘. MBR引导(master boot record):主引导记录,读取存储设备的512bytes ...

  5. Linux系统在启动过程中mbr主引导程序被破坏的解决方案

    首先,mbr主引导程序被破坏是指系统在启动过程中,磁头找不到/boot分区(windows的启动分区在c盘). 1)下面我们模拟主引导分区被破坏的情况:(在启动分区划分446M的存储大小) 2)重启( ...

  6. Linux:系统的启动过程

    Linux系统的启动过程 过程 通电-> BIOS-> LILO/GRUB-> Kernel Boot-> init->rc.sysinit init->rc -& ...

  7. 详解linux系统的启动过程及系统初始化

    一.linux系统的启动流程 关于linux系统的启动流程我们可以按步进行划分为如下: POST加电自检 -->BIOS(Boot Sequence)-->加载对应引导上的MBR(boot ...

  8. linux系统的启动过程简要分析

    接触linux系统运维已经好几年了,常常被问到linux系统启动流程问题,刚好今天有空来梳理下这个过程:一般来说,所有的操作系统的启动流程基本就是: 总的来说,linux系统启动流程可以简单总结为以下 ...

  9. Linux系统在启动过程中grub引导文件丢失的解决方法

    在/boot/grub2目录下有一个grub.cfg文件:该文件主要是用来自动地引导系统启动内核程序和系统的初始化程序. 问题一:当系统在启动的情况下,我们不小心删除/boot/grub2/grub. ...

随机推荐

  1. vue中.sync 修饰符

    一直以来,都不太明白.sync的用法,归根结底原因在于,没有仔细阅读“.sync修饰符”. 正好,最近在拿一个项目练手,然后使用了elment-ui,然后在用到dialog的时候,属性visible是 ...

  2. docker命令相关

    进入容器 容器已经启动 docker exec -it ece7b58a2a04 /bin/sh 容器未启动 docker run -it zzzzz/edas:v1 sh 检查容器 docker i ...

  3. FTP管理常用命令

    #新增用户liuhui,指定群组为groupa,附加群组为groupb,家目录为/ftp/groupbuseradd -g groupa -G groupb -d /ftp/groupb linhui ...

  4. c#操作excel方式三:使用Microsoft.Office.Interop.Excel.dll读取Excel文件

    1.引用Microsoft.Office.Interop.Excel.dll 2.引用命名空间.使用别名 using System.Reflection; using Excel = Microsof ...

  5. block引用外部变量原理

    block在赋值时才会生成对应的block结构体实例(结构体数据结构在编译时已经生成),赋值时会扫一遍里面引用的外部变量(嵌套block中的外部变量也算,只不过嵌套block中的外部变量会被内外两个b ...

  6. Linux的Namespace与Cgroups介绍

    Namespace 的概念 Linux Namespace 是kernel 的一个功能,它可以隔离一系列系统的资源,比如PID(Process ID),User ID, Network等等.一般看到这 ...

  7. 使用Powershell实现计算机名称及IP地址修改

    我的第一篇博客分享,写这个代码的用途是在公司Ghost完系统之后去修改本地计算机名称及IP 地址,用Powershell实现. 1. 代码第一部分,检查Powershell是否已管理员权限执行,如果不 ...

  8. (转)PWA(Progressive Web App)渐进式Web应用程序

    PWA 编辑 讨论 PWA(Progressive Web App)是一种理念,使用多种技术来增强web app的功能,可以让网站的体验变得更好,能够模拟一些原生功能,比如通知推送.在移动端利用标准化 ...

  9. Datatable的操作方法

    DataTable操作相关内容: 对DataTable 的一些操作在dataTable中最容易想到的是用for循环来操作,但事实不到万不得已是不会用for循环的,因为效率一般不高. 1. 取行-取行一 ...

  10. xhprof 安装详解

    准备工作1.xhprof不支持php7,需要php7以下版本2.php扩展模块xhprof下载地址: http://pecl.php.net/get/xhprof-0.9.4.tgz xhprof安装 ...