ESP32上电到app_main()的过程梳理

news2024/11/20 6:27:39

前言

(1)如果有嵌入式企业需要招聘校园大使,湖南区域的日常实习,任何区域的暑假Linux驱动实习岗位,可C站直接私聊,或者邮件:zhangyixu02@gmail.com,此消息至2025年1月1日前均有效
(2)因为实习,需要使用ESP32S3进行开发,而乐鑫官方的esp32_s3_box.c并不是完全符合我们的要求,所以我被安排了解一下esp32的启动流程,然后根据乐鑫的esp32_s3_box.c文件编写一个适配我们自己产品的板级支持包。接下来我根据乐鑫官方文档以及结合自己的理解,梳理一下启动流程。
(3)乐鑫的启动文件个人认为还是有点意思的,因为在我的认知里面,启动文件一般都是采用的汇编代码编写的。但是乐鑫的这个启动文件却是采用的C语言编写(还是有一小部分是用的汇编),理解起来还是方便许多。

ESP32的应用程序

(1)ESP32的启动流程分为三段:一级引导程序,二级引导程序,应用程序启动阶段。

一级引导程序

(1)一级引导程序:这一部分程序直接存储进入ESP32的ROM中了,所以普通开发者无法直接查看,主要是做一些前期的准备工作。
(2)个人认为,唯一需要了解的是,在这一阶段会判断是否请求自定义启动模式,如 UART 下载模式。这也一定程度说明了,为什么进入下载模式,需要先按住BOOT,然后再按下松开复位键,最后松开BOOT的原因了。
(3)因为怕有人无法理解上面加粗部分,我再详细介绍一下。因为芯片内部的操作是非常迅速的,如果你先按下复位键,再按下BOOT,这个时候一级启动引导程序早就完成了。就无法进入你想要的下载模式。
(4)关于芯片启动的控制介绍如下,原文在ESP32 技术规格书的2.6.1章节

在这里插入图片描述

二级引导程序

(1)二级引导程序:这个程序是可查看并且可被修改的,在安装ESP-IDF 的时候,指定的Enter_ESP-IDF_container_directory路径下esp-idf\components\bootloader路径中找到源代码,这一部分应该主要是用于修改bootloader的能做如下配置:
<1>内部模块的最小化初始配置;
<2>如果配置了 flash 加密 和/或 Secure,则对其进行初始化。
<3>根据分区表和 ota_data(如果存在)选择需要引导的应用程序 (app) 分区;
<4>将此应用程序镜像加载到 RAM(IRAM 和 DRAM)中,最后把控制权转交给此应用程序。
(2)利用对bootloader的修改配置,能够实现OTA空中升级程序
<1>OTA 升级机制可以让设备在固件正常运行时根据接收数据(如通过 Wi-Fi 或蓝牙)进行自我更新。
<2>在嵌入式开发过程中,对于绝大多数人来说,单片机的程序烧录都是直接利用一根数据线进行更新程序的。如果是大型的企业开发,SOC一般都是集成在了大型设备里面,我们如果要更新程序,总不可能把每次更新都将整个设备拆除,拿个数据线插上去更新程序,再重新装上吧。因此,有了OTA空中升级机制之后,我们就可以像手机那样,连接上网络,下载更新固件程序,然后就可以更新了。

应用程序启动阶段

(1)ESP32的应用程序启动阶段又分为三个阶段:
<1>硬件和基本 C 语言运行环境的端口初始化。
<2>软件服务和 FreeRTOS 的系统初始化。
<3>运行主任务并调用 app_main

端口初始化

(1)在执行完二级引导程序之后,他会跳转到指定的Enter_ESP-IDF_container_directory路径下esp-idf\components\esp_system\cpu_start.c 中。
(2)在cpu_start.c文件中找到call_start_cpu0 ()函数。这个函数由二级引导加载程序执行,并且从不返回。因此你看不到是哪个函数调用了他,他是从汇编的最底层直接调用的。
(4)在这个函数中会初始化基本的 C 运行环境 (“CRT”),并对 SoC 的内部硬件进行了初始配置。
(5)执行完call_start_cpu0 ()就会找到Enter_ESP-IDF_container_directory路径下esp-idf\components\esp_system\startup.c 文件,找到的“系统层”初始化函数 start_cpu0()。其他内核也将完成端口层的初始化,并调用同一文件中的 start_other_cores()
<1>不过这里的start_cpu0()函数可能是以start_cpu0_default()形式体现出来,因为在startup.c 的第110行有如下代码,表示弱关联。
<2>start_other_cores()这个函数,我也没有找到,个人猜测可能是do_secondary_init()。因为从注释和函数名上来看,do_secondary_init()就是对第二个内核的初始化。

void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))) __attribute__((noreturn));

系统初始化

(1)主要的系统初始化函数是 start_cpu0()。默认情况下,这个函数与 start_cpu0_default() 函数弱链接。这意味着可以覆盖这个函数,增加一些额外的初始化步骤。

void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))) __attribute__((noreturn));

(2)做完一些初始化步骤之后,start_cpu0_default() 函数就会调用esp_startup_start_app()准备进入主任务了。

运行主任务

(1)esp_startup_start_app()先会检查看门狗的配置环境,CPU0的初始化。最终调用xTaskCreatePinnedToCore()这个宏在FreeRTOS上创建一个主任务main_task()任务,名字叫做main(),无传入参数,任务优先级为1。这个任务不会停止。
(2)main_task()这个函数实现如下,其实对app_main()函数进行了再一次的封装,然后执行vTaskDelete(NULL)进行任务自杀。(这个是FreeRTOS的知识点)
(3)这很好的揭示了,为什么用户使用ESP32是在app_main()函数里面进行编程。

static void main_task(void* args)
{
    app_main();
    vTaskDelete(NULL);
}

(4)如果你阅读了ESP32BOX的源代码,你就会发现,app_main()这个程序没有死循环。我们都知道,一个嵌入式程序是不能停止的,一定要进行死循环,否则会出现一些问题。
(5)如果你有这样的疑惑,那么就可以看一下系统初始化部分所讲解的start_cpu0_default()函数。在这个函数里面的最后一样,执行完esp_startup_start_app()之后,就是执行while (1)进入死循环了。最后根据你的操作,进入FreeRTOS的任务调度环节。

参考文章

(1)乐鑫官方文档—应用程序的启动流程;
(2)ESP32 技术规格书;
(3)乐鑫官方文档—引导加载程序 (Bootloader);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1062262.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【单片机】16-LCD1602和12864和LCD9648显示器

1.LCD显示器相关背景 1.LCD简介 &#xff08;1&#xff09;显示器&#xff0c;常见显示器&#xff1a;电视&#xff0c;电脑 &#xff08;2&#xff09;LCD&#xff08;Liquid Crystal Display&#xff09;&#xff0c;液晶显示器&#xff0c;原理介绍 &#xff08;3&#xff…

哈希表的总结

今天刷了力扣的第一题&#xff08;1. 两数之和 - 力扣&#xff08;LeetCode&#xff09;&#xff09;&#xff0c;是一道用暴力解法就可以完成的题目&#xff08;两个for循环&#xff09;,但是官方解答给出了用哈希表的解法&#xff0c;用空间换时间&#xff0c;时间复杂度从O(…

Jmeter排查正则表达式提取器未生效问题

今天在使用Jmeter的时候遇到一个很简单的问题&#xff0c;使用正则表达式提取token一直未生效&#xff0c;原因是正则表达式中多了一个空格。虽然问题很简单&#xff0c;但是觉得排查问题的方法很普适&#xff0c;所以记录下&#xff0c;也希望能够给遇到问题的大家一个参考。 …

蓝桥杯每日一题2023.10.5

3420. 括号序列 - AcWing题库 题目描述 题目分析 对于这一我们需要有前缀知识完全背包 完全背包的朴素写法&#xff1a; #include<bits/stdc.h> using namespace std; const int N 1010; int n, m, v[N], w[N], f[N][N]; int main() {cin >> n >> m;fo…

MySQL数据库入门到精通——进阶篇(3)

黑马程序员 MySQL数据库入门到精通——进阶篇&#xff08;3&#xff09; 1. 锁1.1 锁-介绍1.2 锁-全局锁1.3 锁-表级锁1.3.1 表级锁-表锁1.3.2 表级锁元数据锁( meta data lock&#xff0c;MDL)1.3.3 表级锁-意向锁1.3.4 表级锁意向锁测试 1.4 锁-行级锁1.4.1 行级锁-行锁1.4.2…

计算机网络 (中科大郑烇老师)笔记(一)概论

目录 0 引言1 什么是Internet&#xff1f;1.1 网络、计算机网络、互联网1.2 什么是Internet&#xff1f;&#xff1a;从服务角度看 2 什么是协议&#xff1f;3 网络的结构&#xff08;子系统&#xff09;3.1 网络边缘3.2 网络核心&#xff1a;分组交换、线路交换3.3 接入网、物…

【13】c++设计模式——>工厂模式

简单工厂模式的弊端 简单工厂模式虽然简单&#xff0c;但是违反了设计模式中的开放封闭原则&#xff0c;即工厂类在数据增加时需要被修改&#xff0c;而我们在设计时对于已经设计好的类需要避免修改的操作&#xff0c;而选用扩展的方式。 工厂模式设计 简单工厂模式只有一个…

天地无用 - 修改朋友圈的定位: 高德地图 + 爱思助手

1&#xff0c;电脑上打开高德地图网页版 高德地图 (amap.com) 2&#xff0c;网页最下一栏&#xff0c;点击“开放平台” 高德开放平台 | 高德地图API (amap.com) 3&#xff0c;在新网页中&#xff0c;需要登录高德账户才能操作。 可以使用手机号和验证码登录。 4&#xff0c…

探秘前后端开发世界:猫头虎带你穿梭编程的繁忙街区,解锁全栈之路

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

香蕉叶病害数据集

1.数据集 第一个文件夹为数据增强&#xff08;旋转平移裁剪等操作&#xff09;后的数据集 第二个文件夹为原始数据集 2.原始数据集 Cordana文件夹&#xff08;162张照片&#xff09; healthy文件夹&#xff08;129张&#xff09; Pestalotiopsis文件夹&#xff08;173张照片&…

用Python实现一个电影订票系统!

一、整体结构图 二、代码分解 2.1 infos.py 一部电影的详细信息适合用 字典 结构来存储&#xff0c;我们可以给字典里添加多个键值对来保存电影的名称、座位表和宣传时用的字符画&#xff0c;比如电影《泰坦尼克号》的详细信息就可以按下面的形式保存到字典 titanic 中&#…

Tomcat在CentOS上的安装部署

目录 1. Tomcat简介 2. 安装 2.1 安装JDK环境 2.1.1 下载JDK软件 2.1.2 登陆Linux系统&#xff0c;切换到root用户 2.1.3 通过FinalShell&#xff0c;上传下载好的JDK安装包 2.1.4 创建文件夹&#xff0c;用来部署JDK&#xff0c;将JDK和Tomcat都安装部署到&#…

QGIS文章四——对遥感影像进行土地类型分类

关于土地类型分类&#xff0c;按照性质、用途、利用现状有不同的分类标准。 一、按照国家土地性质分类标准&#xff0c;一般分五类:商业用地、综合用地、住宅用地、工业用地和其他用地。 二、按照用途进行土地分类&#xff1a;可以分为农用地、建设用地和未利用土地&#xff0c…

vue、vuex状态管理、vuex的核心概念state状态

每一个 Vuex 应用的核心就是 store&#xff08;仓库&#xff09;。“store”基本上就是一个容器&#xff0c;它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同&#xff1a; Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候&…

超详细DeepLabv3 介绍与使用指南 – 使用 PyTorch 推理

DeepLab 模型首次在 ICLR 14 中首次亮相,是一系列旨在解决语义分割问题的深度学习架构。经过多年的迭代改进,谷歌研究人员的同一个团队在 17 年底发布了广受欢迎的“DeepLabv3”。当时,DeepLabv3 在 Pascal VOC 2012 测试集上实现了最先进的 (SOTA) 性能,在著名的 Cityscap…

Day-06 基于 Docker 安装 Nginx 镜像

1.去官方公有仓库查询nginx镜像 docker search nginx 2.拉取该镜像 docker pull nginx 3. 启动镜像&#xff0c;使用nginx服务&#xff0c;代理本机8080端口(测试是不是好使) docker run -d -p 8080:80 --name nginx-8080 nginx docker ps curl 127.0.0.1:8080

归并排序含非递归版

目录 1.归并排序的原理 2.实现归并排序 2.1框架 2.2区间问题和后序遍历 2.3归并并拷贝 2.4归并排序代码 2.5测试 3.非递归实现归并排序 3.1初次实现 3.2测试 3.3修改 3.4修改测试 1.归并排序的原理 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治…

UG\NX CAM二次开发 获取当前加工导航器选中的对象数量和tag UF_UI_ONT_ask_selected_nodes

文章作者:代工 来源网站:NX CAM二次开发专栏 简介: UG\NX CAM二次开发 获取当前加工导航器选中的对象数量和tag UF_UI_ONT_ask_selected_nodes 效果: 代码: void MyClass::do_it() {//获取当前加工导航器选中的对象数量和TAGint count = 0;tag_t* objects = NULL…

教育类《中学政史地》收稿方向-投稿邮箱

教育类《中学政史地》收稿方向-投稿邮箱 《中学政史地》收稿方向&#xff1a;中学政治、历史、地理类稿件 《中学政史地》创办于1987年&#xff0c;是我国唯一一份集中学政治、历史、地理三门学科为一体的综合性月刊。每月两期&#xff0c;分初中版和高中版。以服务学生、服务…

Docker安装ActiveMQ

ActiveMQ简介 官网地址&#xff1a;https://activemq.apache.org/ 简介&#xff1a; ActiveMQ 是Apache出品&#xff0c;最流行的&#xff0c;能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,…