关于esp32的系统初始化启动过程及设计学习方法
对于esp32,其开发程序中有且只能有一个app_main函数,该函数是用户程序的入口,这在没有调用FreeRTOS的系统中相当于函数main,但其实在app_main之前,系统还有一段初始化的过程,其大致可以分为以下三个过程:
- ROM中的第一级引导加载程序将闪存偏移0x1000的第二级引导加载程序映像加载到RAM(IRAM和DRAM)。
- 第二级引导程序从闪存加载分区表和主应用程序映像。主应用程序包含RAM段和通过闪存缓存映射的只读段。
- 主应用程序图像执行。此时可以启动第二个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定义的)
引导引脚状态的寄存器来确定引导模式。根据复位原因,发生以下情况:
- 从深度睡眠复位:如果值为
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
返回的代码,继续进行启动,就好像是上电复位一样。注意:此时运行自定义代码,提供了一个深度睡眠存根机制。请参阅深度睡眠文档。 - 对于上电复位,软件SOC复位和看门狗SOC复位:
GPIO_STRAP_REG
如果要求UART或SDIO下载模式,请检查寄存器。如果是这种情况,请配置UART或SDIO,并等待下载代码。否则,继续进行启动,就好像是由于软件CPU复位。 - 对于软件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_cpu0
和start_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的系统初始化启动过程及设计学习方法的更多相关文章
- 探索 Linux 系统的启动过程
引言 之所以想到写这些东西,那是因为我确实想让大家也和我一样,把 Linux 桌面系统打造成真真正正日常使用的工具,而不是安装之后试用几把再删掉.我是真的在日常生活和工作中都使用 Linux,比如在 ...
- Linux系统的启动过程
Linux 系统启动过程 Linux系统的启动过程可以分为5个阶段: BIOS自检 内核的引导. 运行init. 系统初始化. 用户登录系统. BIOS自检: BIOS是英文"Basic I ...
- Linux基础-6.系统的启动过程
Linux启动时我们会看到许多启动信息 Linux系统的启动过程并不是大家想象中的那么复杂,其过程可以分为5个阶段: 内核的引导 运行init 系统初始化 建立终端 用户登录系统 init程序的类型: ...
- 001---Linux系统的启动过程
Linux系统的启动过程 按下电源 开机自检(BIOS):检查cpu.内存.硬盘是否有问题,找到启动盘. MBR引导(master boot record):主引导记录,读取存储设备的512bytes ...
- Linux系统在启动过程中mbr主引导程序被破坏的解决方案
首先,mbr主引导程序被破坏是指系统在启动过程中,磁头找不到/boot分区(windows的启动分区在c盘). 1)下面我们模拟主引导分区被破坏的情况:(在启动分区划分446M的存储大小) 2)重启( ...
- Linux:系统的启动过程
Linux系统的启动过程 过程 通电-> BIOS-> LILO/GRUB-> Kernel Boot-> init->rc.sysinit init->rc -& ...
- 详解linux系统的启动过程及系统初始化
一.linux系统的启动流程 关于linux系统的启动流程我们可以按步进行划分为如下: POST加电自检 -->BIOS(Boot Sequence)-->加载对应引导上的MBR(boot ...
- linux系统的启动过程简要分析
接触linux系统运维已经好几年了,常常被问到linux系统启动流程问题,刚好今天有空来梳理下这个过程:一般来说,所有的操作系统的启动流程基本就是: 总的来说,linux系统启动流程可以简单总结为以下 ...
- Linux系统在启动过程中grub引导文件丢失的解决方法
在/boot/grub2目录下有一个grub.cfg文件:该文件主要是用来自动地引导系统启动内核程序和系统的初始化程序. 问题一:当系统在启动的情况下,我们不小心删除/boot/grub2/grub. ...
随机推荐
- laravel中消息通知功能
以laravel5.5为例子,这个功能laravel自带的有: 1.生成表文件的migration文件,再migrate一下在数据库里生成表.命令为:php artisan notifications ...
- 开源项目初涉(C++自我学习开始)
版权声明:本文为博主原创文章,未经博主允许不得转载. https://i.cnblogs.com/EditPosts.aspx?postid=8428885 临近2018农历新年,我还在上班,哈哈. ...
- ApacheTraffic Server 使用ssd 以及裸盘
使用裸设备后可以使用ATS自身的文件子系统,可以获得更好的IO性能,也是官方推荐的方式.下面为例 删除分区,不使用操作系统自带分区 `fdisk -l /dev/sde` 修改相关设备权限并创新相关设 ...
- C#与Excel的交互示例
//这里加添加一个Excel对象的包装器.就是添加一个引用 using System; using System.Drawing; using System.Collections; using Sy ...
- Mybatis中DAO层接口没有写实现类,Mapper中的方法和DAO接口方法是怎么绑定到一起的,其内部是怎么实现的
其实也就是通过接口名与mapper的id绑定在一起(即相同),通过SQL去写实现类,返回数据.
- 尚硅谷springboot学习30-docker安装mysql示例
docker pull mysql 错误的启动示例 错误日志:需要设置密码 正确的启动 但还不能直接使用,因为没有做端口映射,外界无法连接 可用的启动 连接成功 几个高级的操作 指定配置文件 dock ...
- ==、equals和hashCode小结
1.== ==是关系操作符,对于基本类型(byte,short,char,int,long,float,double,boolean),比较的是值是否相等:对于对象,比较的是对象的引用(也即栈内存中的 ...
- 更改mysql 和jinkins目录
更改MySQL数据目录 1.修改my.cnf,注销原datadir,增加新的数据目录 #datadir=/var/lib/mysqldatadir=/mysql-data/mysql 2.修改启动脚本 ...
- Linux磁盘配额
Step1:修改fstab文件,增加磁盘限额用户和用户组信息 # /etc/fstab# Created by anaconda on Sat Dec 29 04:48:18 2018## Acces ...
- 四、Vmware虚拟机三种网络模式详解
转载自: http://note.youdao.com/share/web/file.html?id=236896997b6ffbaa8e0d92eacd13abbf&type=note 1. ...