第三章 ESP32-S3基础知识
在本章中,我们将深入探索ESP32-S3这款备受瞩目的微控制器。我们将详细阐述其定义、核心资源、功能应用,以及如何选择适合您项目的ESP32-S3型号。通过本章的学习,您将全面了解ESP32-S3,为您的物联网项目选择合适的硬件平台奠定坚实基础。
本章分为如下几个小节:
3.1 为什么选择ESP32-S3
3.2初识ESP32-S3
3.3 ESP32-S3资源简介
3.4 S3系列型号对比
3.5 ESP32-S3功能概述
3.6 ESP32-S3启动例程
3.1 为什么选择ESP32-S3
在研发之初,作者也对比过乐鑫官方推出的几款MCU系列,经过它们各自的功能及应用场景来分析,最终作者选择S系列的S3型号。
下面,作者比较一下乐鑫推出的芯片有哪些特点:
表3.1.1 乐鑫各系列MCU硬件区别
在上述表格中,我们可以看到乐鑫推出的各系列MCU在硬件方面存在一些差异。下面我将继续分析这些差异及其对应用场景的影响
1,在内核数量方面:S系列和ESP32系列支持单核和双核处理器,而C系列和H系列仅支持单核处理器。这意味着S系列和ESP32系列在处理多任务和高强度计算方面具有更强的性能。对于需要高效能、多任务处理的应用场景,如复杂算法处理、大数据分析等,S系列和ESP32系列可能更合适。
2,在时钟频率方面,S系列和ESP32系列的时钟频率范围为80~240MHz,而C系列和H系列的时钟频率分别为120MHz和96MHz。较高的时钟频率意味着更快的处理速度和更高的性能。对于需要高速处理的应用场景,如实时信号处理、高速数据采集等,S系列和ESP32系列可能更合适。
3,在引出编程IO方面,S系列和ESP32系列的引出编程IO数量较多,而C系列和H系列的引出编程IO数量较少。这表明S系列和ESP32系列在编程接口的多样性和灵活性方面具有优势。对于需要连接多种外设和传感器的应用场景,S系列和ESP32系列可能更合适。
4,在神经网络加速方面,只有S系列支持神经网络加速功能。这意味着选择S系列可以更好地满足深度学习、图像识别等应用场景的需求。对于需要加速神经网络运算的应用场景,如智能家居控制、智能安防等,S系列可能更合适。
5,在通信协议方面,所有系列都支持2.4G Wi-Fi和蓝牙(BLE),这意味着它们在无线通信方面具有良好的兼容性。 6,在存储器方面,各系列MCU的SRAM和ROM大小有所不同。较大的存储器可以提供更多的程序运行空间和数据存储空间,以满足更复杂的应用需求。对于需要处理大量数据和运行复杂程序的应用场景,如物联网网关、智能仪表等,S系列和ESP32系列可能更合适。
综上所述,乐鑫推出的各系列MCU在硬件方面各有特点,选择哪个系列取决于具体的应用场景和需求。对于需要高性能、多核处理和神经网络加速的应用场景,S系列可能是更好的选择;而对于简单的物联网应用场景,C系列或H系列可能更合适。
正点原子选择S系列的S3型号作为开发板的核心芯片,是为了读者提供更好的学习资源和开发体验,帮助读者更好地掌握物联网和嵌入式开发的相关技术。 另外,乐鑫科技还提供了一个在线选型工具(https://products.espressif.com/#/product-selector?language=zh),名为ESP Product Selector。它可以帮助用户全面了解乐鑫产品与方案、提高产品选型和开发效率,如下图所示。
图3.1.1 乐鑫在线选型工具
上图①显示了筛选工具的选择,左边是产品选型,右边是产品对比。上图②表示产品选型的功能筛选,主要根据客户的需求来选择,例如工作温度、单/双核、是否具备天线等条件,来选择自己心仪的芯片/模组或者开发板。上图③表示功能筛选之后的结果选择,例如芯片/模组或者满足条件的开发板。最后,上图④表示筛选的结果,如果筛选结果是芯片/模组,那么它就会显示符合筛选的芯片型号或者模组。
3.2 初识ESP32-S3
ESP32-S3是一款由乐鑫公司开发的物联网芯片,它具有一些非常独特的功能和特点。下图为芯片的功能框图。
图3.2.1 ESP32-S3 功能框图
结合《esp32-s3_datasheet_cn.pdf》数据手册和上图的内容,简单归纳5个内容。
1,架构和性能:ESP32-S3采用Xtensa® LX7 CPU,这是一个哈佛结构的双核系统。它具有独立的指令总线和数据总线,所有的内部存储器、外部存储器以及外设都分布在这两条总线上。这种架构使得CPU可以同时读取指令和数据,从而提高了处理速度。
2,存储:ESP32-S3具有丰富的存储空间。它内部有384 KB的内部ROM,512 KB的内部SRAM,以及8 KB的RTC快速存储器和8 KB的RTC慢速存储器。此外,它还支持最大1 GB的片外FLASH和最大1 GB的片外RAM。
3,外设:ESP32-S3具有许多外设,总计有45个模块/外设。其中11个具有GDMA(Generic DMA)功能,可以用来进行数据块的传输,减轻CPU的负担,提高整体性能。
4,通信:ESP32-S3同时支持WIFI和蓝牙功能,应用领域贯穿移动设备、可穿戴电子设备、智能家居等。在2.4GHz频带支持20MHz和40MHz频宽。 5,向量指令:ESP32-S3增加了用于加速神经网络计算和信号处理等工作的向量指令。这些向量指令可以大大提高芯片在AI方面的计算速度和效率。
ESP32-S3是一款功能强大、性能丰富的物联网芯片,适用于各种物联网应用场景。以上信息仅供参考,如需了解更多信息,请访问乐鑫公司官网查询相关资料。
3.3 ESP32-S3资源简介
下面来看看ESP32-S3具体的内部资源,如下表所示。
ESP32-S3资源 |
内核 | Xtensa® LX7 CPU | 系统定时器 | 1 | UART | 3 |
主频 | 240MHz | 定时器组 | 2 | RNG | 1 |
ROM | 384KB | LEDC | 1 | I2C | 2 |
SRAM | 512KB | RMT | 1 | I2S | 2 |
编程IO | 45GPIO | PCNT | 1 | SPI | 4(0、1禁用) |
工作电压 | 3.3 | TWAI | 1 | RGB | 1 |
Wi-Fi/BLE | 1/1 | USB OTG | 1 | SD/MMC | 1 |
表4.3.1 ESP32-S3内部资源表
由表可知,ESP32内部资源还是非常丰富的,本书将针对这些资源进行详细的使用介绍,并提供丰富的例程,供大家参考学习,相信经过本书的学习,您会对ESP32-S3系列芯片有一个全面的了解和掌握。
关于ESP32-S3内部资源的详细介绍,请大家参考“光盘àA 盘à7,硬件资料à2,芯片资料àesp32-s3_technical_reference_manual_cn.pdf”,该文档即《ESP32-S3的技术手册》,里面有 ESP32-S3详细的资源说明和相关性能参数。
3.4 S3系列型号对比
乐鑫S3系列型号包括ESP32-S3、ESP32-S3R2、ESP32-S3R8和ESP32-S3FN8等。这些型号在硬件配置、功能和应用场景方面略有不同。不同型号的MCU都有不同的应用场景,下面我们来看一下这些型号的命名规则,如下图所示。
图3.4.1 ESP32-S3系列芯片命名规则
从上图可以看到,F表示内置FLASH;H/N表示FLASH温度(H:高温,N:常温);x表示内置FLASH大小(MB);R表示内置PSRAM;x表示内置PSRAM大小(MB);V表示仅支持外部1.8v spi flash。为了让读者更清晰了解ESP32-S3命名规则,这里作者以ESP32-S3FH4R2这一款芯片为例,绘画一副清晰的命名示意图,如下图所示。
图3.4.2 ESP32-S3FH4R2命名解析
根据上述两张图的分析,我们可以了解到乐鑫S3系列的命名规则和特点。除了S3系列的芯片之外,乐鑫还推出了S3系列的模组,它是S3系列芯片的简易系统。
乐鑫S3系列模组是基于S3系列芯片的子系统,它已经设计好了外围电路,简化了开发过程,让开发者可以更快速地使用S3系列芯片进行开发。通过使用S3系列模组,开发者可以更容易地实现特定功能,缩短开发周期,提高开发效率。 乐鑫推出了ESP32-S3-WROOM-1和ESP32-S3-WROOM-1U两款通用型Wi-Fi+低功耗蓝牙MCU模组,如下图所示,它们搭载ESP32-S3系列芯片。除具有丰富的外设接口外,模组还拥有强大的神经网络运算能力和信号处理能力,适用于AIoT领域的多种应用场景,例如唤醒词检测和语音命令识别、人脸检测和识别、智能家居、智能家电、智能控制面板、智能扬声器等。
图3.4.3 ESP32-S3-WROOM-1和ESP32-S3-WROOM-1U的功能框图
从上图可知,ESP32-S3-WROOM-1采用PCB板载天线,而ESP32-S3-WROOM-1U采用连接器连接外部天线。两款模组均有多种芯片型号可供选择,具体见下表所示: 模组型号 | 内置芯片 | 外置FLASH | 内置PSRAM |
ESP32-S3-WROOM-1-N4 | ESP32-S3 | 4 | 0 |
ESP32-S3-WROOM-1-N8 | ESP32-S3 | 8 | 0 |
ESP32-S3-WROOM-1-N16 | ESP32-S3 | 16 | 0 |
ESP32-S3-WROOM-1-H4 | ESP32-S3 | 4 | 0 |
ESP32-S3-WROOM-1-N4R2 | ESP32-S3R2 | 4 | 2(Quad SPI) |
ESP32-S3-WROOM-1-N8R2 | ESP32-S3R2 | 8 | 2(Quad SPI) |
ESP32-S3-WROOM-1-N16R2 | ESP32-S3R2 | 16 | 2(Quad SPI) |
ESP32-S3-WROOM-1-N4R8 | ESP32-S3R8 | 4 | 8(Octal SPI) |
ESP32-S3-WROOM-1-N8R8 | ESP32-S3R8 | 8 | 8(Octal SPI) |
ESP32-S3-WROOM-1-N16R8 | ESP32-S3R8 | 16 | 8(Octal SPI) |
ESP32-S3-WROOM-1U-N4 | ESP32-S3 | 4 | 0 |
ESP32-S3-WROOM-1U -N8 | ESP32-S3 | 8 | 0 |
ESP32-S3-WROOM-1U -N16 | ESP32-S3 | 16 | 0 |
ESP32-S3-WROOM-1U -H4 | ESP32-S3 | 4 | 0 |
ESP32-S3-WROOM-1U -N4R2 | ESP32-S3R2 | 4 | 2(Quad SPI) |
ESP32-S3-WROOM-1U -N8R2 | ESP32-S3R2 | 8 | 2(Quad SPI) |
ESP32-S3-WROOM-1U -N16R2 | ESP32-S3R2 | 16 | 2(Quad SPI) |
ESP32-S3-WROOM-1U -N4R8 | ESP32-S3R8 | 4 | 8(Octal SPI) |
ESP32-S3-WROOM-1U -N8R8 | ESP32-S3R8 | 8 | 8(Octal SPI) |
ESP32-S3-WROOM-1U -N16R8 | ESP32-S3R8 | 16 | 8(Octal SPI) |
表3.4.1 通用型模组的命名
根据上表,可以看出这两款模组的主控芯片是ESP32-S3和ESP32-S3Rx,它们都属于乐鑫的ESP32-S3系列芯片。之前作者已经详细讲解了ESP32-S3系列芯片的命令规则,可以得出这两款通用模组都是外接Flash存储器,并且内置有PSRAM(主控芯片ESP32-S3没有内置PSRAM)。下面我们以ESP32-S3-WROOM-1-N16R8模组为例,来讲解模组的命名规则,如下图所示。
图3.4.3 模组的命令规则
通过了解模组内置的主控芯片类型,开发者可以更好地理解该模组的功能和特点,并根据需要进行相应的开发和应用。正点原子ESP32-S3开发板是以ESP32-S3-WROOM-1-N16R8模组作为主控,它可以提供稳定的控制系统和高效的数据处理能力,同时引出的IO可以满足各种应用需求。
3.5 ESP32-S3功能概述
3.5.1 系统和存储器
ESP32-S3采用哈佛结构Xtensa® LX7 CPU构成双核系统。所有的内部存储器、外部存储器以及外设都分布在CPU 的总线上。
以下是ESP32-S3的主要特性:
1,地址空间:ESP32-S3 拥有丰富的地址空间,包括内部存储器指令地址空间、内部存储器数据地址空间、外设地址空间、外部存储器指令虚地址空间、外部存储器数据虚地址空间、内部DMA地址空间和外部DMA地址空间。这些地址空间为芯片的各个部分提供了独立的存储空间。
2,内部存储器:ESP32-S3内部存储器包括384 KB的内部ROM、512 KB的内部SRAM、8 KB的RTC快速存储器和 8 KB 的 RTC 慢速存储器。这些存储器为芯片提供了存储和读取数据的能力。
3,外部存储器:ESP32-S3支持最大1 GB的片外flash和最大1 GB 的片外 RAM。这些外部存储器可以用来存储大量的程序代码和数据,以满足复杂应用的需求。
4,外设空间:ESP32-S3总计有45个模块/外设,这些外设为芯片提供了丰富的输入输出接口和特殊功能。
5,GDMA(Generic DMA):ESP32-S3具有11个具有GDMA功能的模块/外设,这些 GDMA 外设可以用来进行数据块的传输,从而减轻CPU的负担,提高整体性能。
下图是ESP32-S3地址空间映射结构图,阐述了内部存储器地址空间映射、外部存储器地址空间映射和模块/外设地址映射的系统结构图,以及GDMA与各部分的联系示意图。
图3.5.1 系统结构与地址映射结构
上图中,灰色背景的地址空间不可用,红色五角星表示对应存储器和外设可以被协调器访问。由于ESP32-S3系统是由两个哈佛结构Xtensa® LX7 CPU构成,这两个CPU能够访问的地址空间范围是完全一致的。上图中,地址0x40000000以下部分属于数据总线的地址范围;地址0x40000000~4FFFFFFF部分位指令总线的地址范围,其他是数据总线与指令总线的地址范围,即内部存储器、外部存储器和外设等映射的内存地址。
CPU的数据总线与指令总线都为小端序(将多字节数据的低位放在较小的地址处,高位放在较大的地址处)。CPU可以通过数据总线进行单字节、双字节、4字节、16字节的数据访问。CPU也可以通过指令总线进行数据访问,但必须是4字节对齐方式;非对齐数据访问会导致CPU工作异常。CPU的工作如下:
① 通过数据总线与指令总线直接访问内部存储器。
② 通过Cache直接访问映射到地址空间的外部存储器。
③ 通过数据总线直接访问模块/外设。
系统中部分内部存储器与部分外部存储器既可以被数据总线访问也可以被指令总线访问,这种情况下,CPU可以通过多个地址访问到同一目标。
3.5.1.1 内部存储器
图3.5.1中的①、②、④和⑥部分组成ESP32-S3内部存储器。
上图①:Internal ROM(384KB)是只读存储器、不可编程,用来存放系统底层的固件(程序指令和一些只读数据)。
上图②:Internal SRAM(512 KB)是易失性存储器,可以快速响应CPU的访问请求,通常只需一个CPU时钟周期。其中,SRAM的一部分可以被配置成外部存储器访问的缓存(Cache),但这种情况下无法被CPU访问;另外,某些部分只可以被CPU的指令总线访问;某些部分只可以被CPU的数据总线访问;还有某些部分既可被CPU的指令总线访问,也可被CPU的数据总线访问。
上图④和⑥:RTC Memory(16 KB)RTC 存储器以静态RAM(SRAM)方式实现,因此也是易失性存储器。但是,在deep sleep模式下,存放在RTC存储器中的数据不会丢失。其中, RTC FAST memory(8 KB)只可以被 CPU 访问,不可以被协处理器访问,通常用来存放一些在 Deep Sleep 模式下仍需保持的程序指令和数据。而RTC SLOW memory (8KB)既可以被CPU访问,又可以被协处理器访问,因此通常用来存放一些CPU和协处理器需要共享的程序指令和数据。
注意:所有的内部存储器都接受权限管理。只有获取到访问内部存储器的访问权限,才可以执行正常的访问操作,CPU访问内部存储器时才可以被响应。关于权限管理的更多信息,请参考《esp32-s3_technical_reference_manual_cn.pdf》章节 15 权限控制(PMS)。
3.5.1.2 外部存储器
图3.5.1中的⑦、⑧和⑨可见。CPU借助高速缓存(Cache)来访问外部存储器。 Cache 将根据内存管理单元(MMU)中的信息把 CPU指令总线或数据总线的地址变换为访问片外flash与片外RAM的实地址。经过变换的实地址所组成的实地址空间最大支持1 GB 的片外flash与1 GB的片外RAM。前面我们讨论知道,ESP32-S3采用双核共享ICache 和DCache 结构,以便当CPU的指令总线和数据总线同时发起请求时,也可以迅速响应。当双核同时访问ICache 时,系统会做以下判断,如下图所示。
图3.5.1.2.1 Cache系统框图
当两个核的指令总线同时访问ICache/DCache时,由仲裁器决定谁先获得访问ICache/DCache的权限。当Cache缺失(处理器所要访问的存储块不在高速缓存中的现象)时,Cache控制器会向外部存储器发起请求,当ICache和DCache同时发起外部存储器请求时,由仲裁器决定谁先获得外部存储器的使用权。
①:ICache的缓存大小可配置为16 KB或32KB,块大小可以配置为16B或32B,当ICache缓存大小配置为32KB时禁用16B块大小模式。
②:DCache的缓存大小可配置为32 KB或64 KB,块大小可以配置为16B、32B 或64B,当DCache缓存大小配置为64 KB 时禁用16B 块大小模式。
返回到图4.5.1,外部存储器通过高速缓存(Cache),ESP32-S3一次最多可以同时访问32MB的指令总线地址空间和32MB的数据总线地址空间。32 MB的指令总线地址空间,通过指令缓存(ICache)以64 KB为单位映射到片外flash或片外RAM,支持 4 字节对齐的读访问或取指访问,而32 MB的数据总线地址空间,是通过数据缓存(DCache)以64 KB为单位映射到片外RAM,支持单字节、双字节、4字节、16字节的读写访问。这部分地址空间也可以用作只读数据空间,映射到片外 flash 或片外RAM。
下表列出了访问外部存储器时CPU的数据总线和指令总线与Cache的对应关系。
| | | |
低位地址 | 高位地址 |
数据 | 0x3C000000 | 0x3DFFFFFF | 32 | DCache |
指令 | 0x42000000 | 0x43FFFFFF | 32 | ICache |
表3.5.1.2.1 外部存储器地址映射
同样,想要操作外部存储器的读写,需获取访问权限,CPU访问外部存储器时才能被响应。关于权限管理的更多信息,请参考《esp32-s3_technical_reference_manual_cn.pdf》15权限控制(PMS)。
3.5.1.3 模块/外设
图3.5.1中的⑤就是模块/外设地址空间地址,CPU就是通过该空间地址来访问模块/外设的。下表是模块/外设地址空间的各段地址与其能访问到的模块/外设映射关系。
| | | |
低位地址 | 高位地址 |
UART0 | 0x60000000 | 0x60000FFF | 4 | UART0地址 |
保留 | 0x60001000 | 0x60001FFF | | |
SPI控制器1 | 0x60002000 | 0x60002FFF | 4 | SPI1地址 |
SPI控制器2 | 0x60003000 | 0x60003FFF | 4 | SPI2地址 |
GPIO | 0x60004000 | 0x60004FFF | 4 | GPIO地址 |
保留 | 0x60005000 | 0x60006FFF | | |
eFuse控制器 | 0x60007000 | 0x60007FFF | 4 | eFuse地址 |
低功耗管理 | 0x60008000 | 0x60008FFF | 4 | 低功耗地址 |
IO MUX | 0x60009000 | 0x60009FFF | 4 | IO MUX地址 |
保留 | 0x6000A000 | 0x6000EFFF | | |
其他外设的地址,请参考《esp32-s3_technical_reference_manual_cn.pdf》技术手册表4-3 |
World 控制器 | 0x600D0000 | 0x600D0FFF | 4 | World地址 |
表3.5.1.3.1 模块/外设地址空间映射部分表
从上表可以得知,要操作外设的寄存器,首先需要知道该外设的首地址。然后,我们可以使用一些底层的编程语言,如C语言或汇编语言,来编写程序以设置外设寄存器的值,从而控制外设的行为。例如,通过设置GPIO寄存器的值,我们可以控制某个LED灯的亮灭;同样地,设置UART寄存器的值可以用来发送和接收数据。
与内部存储器和外部存储器访问类似,CPU要想访问某一个模块/外设,需要先获取该模块/外设的访问权限,否则访问将不会被响应。关于权限管理的更多信息,请参考《esp32-s3_technical_reference_manual_cn.pdf》章节15权限控制(PMS)。
3.5.1.4 通用GDMA控制器
通用直接存储访问(General Direct Memory Access, GDMA)用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输软件可以在无需任何CPU操作的情况下通过GDMA快速搬移数据,从而降低了CPU的工作负载,提高了效率。ESP32-S3的GDMA控制器采用AHB总线架构,以字节为单位进行数据传输,支持软件编程控制传输数据量,支持链表传输,同时支持访问内部RAM时的INCR burst传输。其能够访问的最大内部RAM地址空间为480 KB,最大外部RAM地址空间为32 MB。该控制器包含5个接收通道和5个发送通道,每个通道都可以访问内部和外部RAM,并且支持可配置的外设选择。最后,GDMA控制器采用固定优先级及轮询仲裁机制来管理通道间的传输。
正如前文所述,GDMA共有10个独立的通道,其中包括5个接收通道和5个发送通道。这10个通道被支持GDMA功能的外设所共享,也就是说用户可以将通道分配给任何支持GDMA功能的外设。这些外设包括SPI2、SPI3、UHCI0、I2S0、I2S1、LCD/CAM、AES、SHA、ADC和RMT等。根据图3.3.1的③所示的连接关系再一次验证了,这些外设都可以使用GDMA传输数据。此外,每个GDMA通道都具备访问内部RAM或外部RAM的能力,这使得ESP32-S3在处理复杂的数据传输任务时具有显著优势。
下图是GDMA功能模块和GDMA通道示意图。
图3.5.1.4.1 具有GDMA功能的模块和GDMA通道示意图
从上图可知,每一个外设都可以使用任意一条通道进行数据传输。然而,这些通道分为不同的类型。当使用GDMA接收数据时,可以选择任意的RXn通道(n:0~4);相反,当使用GDMA发送数据时,则需要选择任务的TXn通道(n:0~4)。这种通道的分类和选择方式使得数据传输更加高效和灵活。
ESP32-S3中有11个外设/模块可以和GDMA联合工作,如下图所示。其中的 11 根竖线依次对应这11个具有GDMA功能的外设/模块,横线表示GDMA的某一个通道(可以是任意一个通道),竖线与横线的交点表示对应外设/模块可以访问GDMA的某一个通道。同一行上有多个交点则表示这几个外设/模块不可以同时开启GDMA功能。
图3.5.1.4.2 具有GDMA功能的外设
具有GDMA功能的模块/外设通过GDMA可以访问任何GDMA可以访问到的存储器。更多关于GDMA的信息,请参考《esp32-s3_technical_reference_manual_cn.pdf》章节 3 通用 DMA 控制器(GDMA)。
与前面小节一样,当使用GDMA访问任何存储器时,都需要获取对应的访问权限,否则访问将会失败。
3.5.2 IO MUX和GPIO交换矩阵
ESP32-S3 芯片有45个物理通用输入输出管脚(GPIO Pin)。每个管脚都可用作一个通用输入输出,或连接一个内部外设信号。利用GPIO交换矩阵、IO MUX(IO复用选择器)和RTC IO MUX(RTC复用选择器),可配置外设模块的输入信号来源于任何的GPIO管脚,并且外设模块的输出信号也可连接到任意GPIO管脚。这些模块共同组成了芯片的输入输出控制。值得注意的是,这45个物理GPIO管脚的编号为0~21、26~48。这些管脚即可作为输入有可作为输出管脚。正如前文所述,正点原子选择ESP32-S3-WROOM-1-N16R8模组作为主控,但由于该模组只有36个实际引脚的物理GPIO管脚。这是因为该模组的Flash和PSRAM使用了八线SPI即Octal SPI模式,这些模式共占用了12个GPIO管脚。而且,该模组还将IO35、IO36、IO37引出,所以最终的管脚数量为45-12+3,即36个GPIO管脚。
下图是从《esp32-s3_datasheet_cn.pdf》数据手册截取下来的,主要描述Flash和PSRAM使用八线SPI模式下的管脚。
图3.5.2.1 芯片与封装内flash/PSRAM的管脚对应关系
需要注意的是,正点原子ESP32-S3开发板的原理图并没有使用IO35-IO37号管脚,所以不存在共用Falsh和PSRAM管脚。
下面我们来看一下这个模组的实物图和引脚分布图,如下图所示。
图3.5.2.2 ESP32-S3-WROOM-1实物图和引脚分布图
从上图可以得知,左边的图片是该模组的3D实物图,而右边的图片是该模组的管脚分布图。虽然这些管脚是无序的,但它们都可以被复用为其他功能(除个别功能外),例如SPI、串口、IIC等协议。这是ESP32相比其他MCU的优势之一,它具有更多的可复用管脚,可以支持更多的外设和协议。
接下来,我们来看一下模组管脚默认复用管脚和管脚功能释义,如下表所示。
管脚 名称 | | | |
GND | 1 | P | 接地 |
3V3 | 2 | P | 供电 |
| | | |
IO4 | 4 | I/O/T | RTC_GPIO4\GPIO4\TOUCH4\ADC1_CH3 |
IO5 | 5 | I/O/T | RTC_GPIO5\GPIO5\TOUCH5\ADC1_CH4 |
IO6 | 6 | I/O/T | RTC_GPIO6\GPIO6\TOUCH6\ADC1_CH5 |
IO7 | 7 | I/O/T | RTC_GPIO7\GPIO7\TOUCH7\ADC1_CH6 |
IO15 | 8 | I/O/T | RTC_GPIO15\GPIO15\U0RTS\ADC2_CH4\XTAL_32K_P |
IO16 | 9 | I/O/T | RTC_GPIO16\GPIO16\U0CTS\ADC2_CH5\XTAL_32K_N |
IO17 | 10 | I/O/T | RTC_GPIO17\GPIO17\U1TXD\ADC2_CH6 |
IO18 | 11 | I/O/T | RTC_GPIO18\GPIO18\U1RXD\ADC2_CH7\CLK_OUT3 |
IO8 | 12 | I/O/T | RTC_GPIO8\GPIO8\TOUCH8\ADC1_CH7\SUBSPICS1 |
IO19 | 13 | I/O/T | RTC_GPIO19\GPIO19\U1RTS\ADC2_CH8\CLK_OUT2\USB_D- |
IO20 | 14 | I/O/T | RTC_GPIO20\GPIO20\U1CTS\ADC2_CH9\CLK_OUT1\USB_D+ |
IO3 | 15 | I/O/T | RTC_GPIO3\GPIO3\TOUCH3\ADC1_CH2 |
IO46 | 16 | I/O/T | GPIO46 |
IO9 | 17 | I/O/T | RTC_GPIO9,GPIO9,TOUCH9,ADC1_CH8,FSPIHD,SUBSPIHD |
IO10 | 18 | I/O/T | RTC_GPIO10,GPIO10,TOUCH10,ADC1_CH9,FSPICS0,FSPIIO4 |
IO11 | 19 | I/O/T | RTC_GPIO11,GPIO11,TOUCH11,ADC2_CH0,FSPID,FSPIIO5 |
IO12 | 20 | I/O/T | RTC_GPIO12,GPIO12,TOUCH12,ADC2_CH1,FSPICLK,FSPIIO6 |
IO13 | 21 | I/O/T | RTC_GPIO13,GPIO13,TOUCH13,ADC2_CH2,FSPIQ,FSPIIO7 |
IO14 | 22 | I/O/T | RTC_GPIO14,GPIO14,TOUCH14,ADC2_CH3,FSPIWP,FSPIDQS |
IO21 | 23 | I/O/T | RTC_GPIO21,GPIO21 |
IO47 | 24 | I/O/T | SPICLK_P_DIFF,GPIO47,SUBSPICLK_P_DIFF |
IO48 | 25 | I/O/T | SPICLK_N_DIFF\GPIO48\SUBSPICLK_N_DIFF |
IO45 | 26 | I/O/T | GPIO45 |
IO0 | 27 | I/O/T | RTC_GPIO0\GPIO0 |
IO35 | 28 | I/O/T | SPIIO6\GPIO35\FSPID\SUBSPID |
IO36 | 29 | I/O/T | SPIIO7\GPIO36\FSPICLK\SUBSPICLK |
IO37 | 30 | I/O/T | SPIDQS\GPIO37\FSPIQ\SUBSPIQ |
IO38 | 31 | I/O/T | GPIO38\FSPIWP\SUBSPIWP |
IO39 | 32 | I/O/T | MTCK\GPIO39\CLK_OUT3\SUBSPICS1 |
IO40 | 33 | I/O/T | MTDO\GPIO40\CLK_OUT2 |
IO41 | 34 | I/O/T | MTDI\GPIO41\CLK_OUT1 |
IO42 | 35 | I/O/T | MTMS\GPIO42 |
RXD0 | 36 | I/O/T | U0RXD\GPIO44\CLK_OUT2 |
TXD0 | 37 | I/O/T | U0TXD\GPIO43\CLK_OUT1 |
IO2 | 38 | I/O/T | RTC_GPIO2\GPIO2\TOUCH2\ADC1_CH1 |
IO1 | 39 | I/O/T | RTC_GPIO1\GPIO1\TOUCH1\ADC1_CH0 |
GND | 40 | P | 接地 |
EPAD | 41 | P | 接地 |
表3.5.2.1 管脚定义
上表是ESP32-S3-WROOM-1-N16R8模组的管脚定义,下面作者根据这个表格来讲解GPIO交互矩阵及IO MUX复用的知识。
下图为GPIO交换矩阵、IO MUX和RTC IO MUX将信号引入外设和引出至管脚的具体过程。
图3.5.2.3 IO MUX、RTC IO MUX和GPIO交换矩阵结构框图
首先,作者说明一下上图带有颜色线条和标签(Ⅰ、Ⅱ、Ⅲ、Ⅳ)的作用,红色线条表示输出方向;紫色线条代表RTC IO管脚的输出方向;黄色线条代表RTC IO管脚输入方向;标签代表输入/输出分支的节点。
从上图可知,ESP32-S3管脚具有预设功能,即每个IO管脚直接连接至一组特定的片上外设。在运动时,可通过IO MUX和IO矩阵配置连接管脚外设。从上表4.5.2.1可知,有些IO管脚预设了RTC和模拟功能,有些IO管脚预设了SPI、IIC等功能。
上图右边两个“Pin X supplied by VDD3P3_CPU/RTC”框图为芯片焊盘 (PAD) 的内部结构,即芯片逻辑与GPIO管脚之间的电气接口。ESP32-S3的45个GPIO管脚均采用此结构,如下图所示。
图3.5.2.4 焊盘内部结构
上图中的IE表示输入使能;OE表示输出使能;WPU表示内部弱上拉;WPD表示内部弱下拉,它们实现了芯片封装内晶片与GPIO管脚之间的物理连接。
一、“Pin X supplied by VDD3P3_CPU”芯片焊盘输入流程(图4.5.1.5.3中的绿色线条)
从上图3.5.2.3可知,输入信号通过两条通道(Ⅳ处)到达输入信号终端。第一条通道(①)无需经过GPIO SYNC模块的同步处理,而是通过IO_MUX_n_REGIO寄存器(该寄存器的IO_MUX_MCU_SEL位作用为信号选择IO MUX功能,为0选择Function 0,为1选择Function 1(GPIO),Function功能请看《esp32-s3_technical_reference_manual_cn.pdf》章节6.12 IO MUX管脚功能列表)配置进入GPIO交换矩阵,然后输入信号进入旁路GPIO交换矩阵(GPIO_SIMy_IN_SET)。另一方面,另一条通道经过GPIO SYNC模块的同步处理,然后将信号时钟同步APB总线时钟,随后进入GPIO交换矩阵。在这个交换矩阵中,通道的开通是由寄存器GPIO_FUNCy_IN_SEL_CFG_REG进行配置的。这个寄存器的描述如下。 图3.5.2.5 GPIO_FUNCy_IN_SEL_CFG_REG描述
从上图可知,GPIO_FUNCy_IN_SEL(其中y为GPIO的管脚号)是外设输入信号控制位。如果GPIO_FUNCy_IN_SEL的值为0x38,则输入信号被视为高电平;如果GPIO_FUNCy_IN_SEL的值为0x3C,则输入信号被视为低电平。GPIO_FUNCy_IN_INV_SEL(其中y为GPIO的管脚号)是反转输入值的控制位。如果输入是高电平,经过反转操作后变为低电平;否则,保持高电平。GPIO_SIMy_IN_SET(其中y为GPIO的管脚号)是旁路GPIO交换矩阵,它的作用是提高高频数字信号的特性。如果GPIO_SIMy_IN_SET的值为1,则选择GPIO交换矩阵作为输入;否则,选择IO MUX作为输入,最终信号到达输入信号终端。
二、“Pin X supplied by VDD3P3_CPU”芯片焊盘输出流程(图4.5.1.5.3中的红色线条)
从上图3.5.2.3可知,输出信号也是分为两个通道传输(Ⅰ处),如果输出信号是普通的GPIO输出,则该信号经过GPIO矩阵,再由该矩阵输出到IO MUX,再到输出管脚,这个流程由GPIO_FUNCy_OUT_SEL_CFG_REG寄存器配置,如下所示:
图3.5.2.6 GPIO_FUNCy_OUT_SEL_CFG_REG描述
从上图可知,GPIO_FUNCx_OUT_SEL(其中x为GPIO的管脚号)是外设输出信号控制位。当GPIO0管脚输出信号时,该值为0。GPIO_FUNCx_OUT _INV_SEL(其中x为GPIO的管脚号)是反转输出值的控制位。如果输出是高电平,经过反转操作后变为低电平;否则,保持高电平。然后通过IO_MUX_n_REGIO寄存器(该寄存器的IO_MUX_MCU_SEL位的作用是信号选择IO MUX功能,为0选择Function 0,为1选择Function 1(GPIO)。有关Function的详细信息,请参阅《esp32-s3_technical_reference_manual_cn.pdf》第6.12节中的IO MUX管脚功能列表)配置,信号最终到达输出管脚。
另一条通道是复用功能输出的通道。该通道由输出信号的起始端到IO MUX复用电路,然后IO_MUX_n_REGIO寄存器的IO_MUX_MCU_SEL位不为1(GPIO模式),为复用功能,最后经过Ⅱ处输出到输出端(GPIO和RTC IO)。
三、“Pin X supplied by VDD3P3_RTC”芯片焊盘输入流程(图4.5.1.5.3中的黄色线条)
根据表3.5.2.1和图3.5.2.3所示,ESP32-S3中有22个GPIO管脚具有低功耗(RTC)性能和模拟功能,由RTC子系统控制。这些功能不使用 IO MUX 和GPIO交换矩阵,而是使用RTC IO MUX将22个RTC输入输出信号引入RTC子系统。当这些管脚被配置为RTC GPIO管脚,作为输出管脚时仍然能够在芯片处于Deep-sleep模式下保持输出电平值或者作为输入管脚使用时可以将芯片从Deep-sleep中唤醒。
如果它们被用作普通输入,则输入流程与“Pin X supplied by VDD3P3_CPU”的输入流程相同。如果它们作为RTC复用功能,则输入信号会进入RTC IO MUX复用电路,并最终到达RTC GPIO矩阵。
3.5.3 复位与时钟
3.5.3.1 ESP32-S3复位等级
ESP32-S3提供四种级别的复位方式,分别是CPU复位、内核复位、系统复位和芯片复位。除芯片复位外其它复位方式不影响片上内存存储的数据。下图展示了整个芯片系统的结构以及四种复位等级。
图3.5.3.1.1 四种复位等级
CPU复位:只复位CPUx内核,这里的CPUx代表CPU0和CPU1.复位释放后,程序将从CPUx Reset Vector开始执行。
内核复位:复位除了RTC以外的数字系统,包括CPU0、CPU1、外设、WiFi、Bluetooth® LE及数字GPIO。
系统复位:复位包括RTC在内的整个数字系统。
芯片复位:复位整个芯片。
上述任意复位源产生时,CPU0和CPU1均将立刻复位。复位释放后,CPU0和CPU1可分别通过读取寄存器RTC_CNTL_RESET_CAUSE_PROCPU和RTC_CNTL_RESET_CAUSE_APPCPU获取复位源。这两个寄存器记录的复位源除了复位级别为CPU复位的复位源分别对应自身的CPUx以外,其余的复位源保持一致。下表列出了从上述两个寄存器中可能读出的复位源。
复位编码 | 复位源 | 复位等级 | 描述 |
0x01 | 芯片复位 | 芯片复位 | - |
0x0F | 欠压系统复位 | 系统复位或芯片复位 | 欠压检测器触发的系统复位 |
0x10 | RWDT 系统复位 | 系统复位 | 见技术手册章节13 |
0x12 | Super Watchdog 复位 | 系统复位 | 见技术手册章节13 |
0x13 | GLITCH 复位 | 系统复位 | 见技术手册章节24 |
| | | 配置RTC_CNTL_SW_SYS_RST寄存器触发 |
0x05 | Deep-sleep 复位 | 内核复位 | 见技术手册章节10 |
0x07 | MWDT0 内核复位 | 内核复位 | 见技术手册章节13 |
0x08 | MWDT1 内核复位 | 内核复位 | 见技术手册章节13 |
0x09 | RWDT 内核复位 | 内核复位 | 见技术手册章节13 |
0x14 | eFuse 复位 | 内核复位 | eFuse CRC校验错误触发复位 |
0x15 | USB (UART) 复位 | 内核复位 | 见技术手册章节33 |
0x16 | USB (JTAG) 复位 | 内核复位 | 见技术手册章节33 |
0x0B | MWDT0 CPUx 复位 | CPU 复位 | 见技术手册章节13 |
| | | 配置RTC_CNTL_SW_PROCPU_RST寄存器触发 |
0x0D | RWDT CPUx 复位 | CPU 复位 | 见技术手册章节13 |
0x11 | MWDT1 CPUx 复位 | CPU 复位 | 见技术手册章节13 |
表3.5.3.1.1 相关复位的复位源
上表描述了不同的复位对应的复位源,在ESP32-S3上电复位时,它的复位源为芯片复位,如下信息所示:
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x1 (POWERON),boot:0xb (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3810,len:0x17c0
load:0x403c9700,len:0xd7c
load:0x403cc700,len:0x300c
entry 0x403c992c
从上述内容可以看到,rst为0x01(复位编码),根据上表的对应关系,可得芯片上电时的复位源为芯片复位。
3.5.3.2 系统时钟
ESP32-S3的时钟主要来源于振荡器(oscillator,OSC)、 RC振荡电路和PLL时钟生成电路。上述时钟源产生的时钟经时钟分频器或时钟选择器等时钟模块的处理,使得大部分功能模块可以根据不同功耗和性能需求来获取及选择对应频率的工作时钟。下图为ESP32-S3系统时钟结构。
图3.5.3.2.1 ESP32-S3时钟树
从上图可知,ESP32-S3时钟频率,可划分为:
(1),高性能时钟,主要为CPU和数字外设提供工作时钟。
①:PLL_CLK:320MHz或者480MHz内部PLL时钟
②:XTAL_CLK:40MHz外部晶振时钟
(2),低功耗时钟,主要为RTC模块以及部分处于低功耗模式的外设提供工作时钟。
①:XTAL32K_CLK:32kHz外部晶振时钟
②:RC_FAST_CLK:内置快速RC振荡器时钟,频率可调节(通常为17.5MHz)
③:RC_FAST_DIV_CLK:内置快速RC振荡器分频时钟(RC_FAST_CLK/256)
④:RC_SLOW_CLK:内置慢速RC振荡器,频率可调节(通常为136 kHz)
从上图红色线条所示,CPU_CLK代表CPU的主时钟。在CPU最高效的工作模式下,主频可以达到240MHz。主频频率是由寄存器SYSTEM_SOC_CLK_SEL(SEL_0:选择SOC时钟源)、SYSTEM_PLL_FREQ_SEL(SEL_2:选择 PLL 时钟频率)和SYSTEM_CPUPERIOD_SEL(SEL_3:选择 CPU 时钟频率)共同确定的,具体如下表所示。
时钟源 | SEL_0 | SEL_2 | SEL_3 | CPU时钟频率 |
| | | | CPU_CLK = XTAL_CLK/(SYSTEM_PRE_DIV_CNT + 1) |
| | | | CPU_CLK = PLL_CLK/6 CPU_CLK 频率为 80 MHz。 |
| | | | CPU_CLK = PLL_CLK/3 CPU_CLK 频率为 160 MHz |
| | | | CPU_CLK = PLL_CLK/2 CPU_CLK 频率为 240 MHz |
| | | | CPU_CLK = PLL_CLK/4 CPU_CLK 频率为 80 MHz |
| | | | CPU_CLK = PLL_CLK/2 CPU_CLK 频率为 160 MHz |
| | | | CPU_CLK = RC_FAST_CLK/(SYSTEM_PRE_DIV_CNT + 1) |
表3.5.3.2.1 CPU_CLK时钟频率配置
从上表可以得知,如果用户想要将ESP32-S3的主频设置为240MHz,那么我们应该选择PLL_CLK作为输入源,然后通过二分频得到240MHz的时钟频率。
外设、WiFi、BLUE、RTC等时钟配置及选择源,请读者参考《esp32-s3_technical_reference_manual_cn.pdf》技术手册章节7 复位和时钟。
3.5.4 芯片Boot控制
在上电复位、 RTC看门狗复位、欠压复位、模拟超级看门狗(analog super watchdog)复位、晶振时钟毛刺检测复位过程中,硬件将采样 Strapping 管脚电平存储到锁存器中,并一直保持到芯片掉电或关闭。GPIO0、GPIO3、GPIO45和GPIO46锁存的状态可以通过软件从寄存器GPIO_STRAPPING中读取。GPIO0、GPIO45和GPIO46默认连接内部上拉/下拉。如果这些管脚没有外部连接或者连接的外部线路处于高阻抗状态,内部弱上拉/下拉将决定这几个管脚输入电平的默认值,如下表所示。
功能 | Strapping 管脚 | 默认配置 |
芯片启动模式 | GPIO0 和 GPIO46 | 上拉 |
VDD_SPI 电压 | GPIO45 | 下拉 |
ROM 代码日志打印 | GPIO46 | 下拉 |
JTAG 信号源 | GPIO3 | 浮空 |
表3.5.4.1 Strapping 管脚默认配置
GPIO0、GPIO45和GPIO46在芯片复位时连接芯片内部的弱上拉/下拉电阻。如果strapping管脚没有外部连接或者连接的外部线路处于高阻抗状态,这些电阻将决定strappin管脚的默认值。所有strapping管脚都有锁存器。系统复位时,锁存器采样并存储相应strapping管脚的值,一直保持到芯片掉电或关闭。锁存器的状态无法用其他方式更改。因此,strapping管脚的值在芯片工作时一直可读取,并可在芯片复位后作为普通IO管脚使用。
① 芯片启动模式控制
复位释放后,GPIO0和GPIO46共同决定启动模式。详见下表。
启动模式 | GPIO0 | GPIO46 |
默认配值 | 1 | 0 |
SPI BOOT | 1 | 任意值 |
Download Boot | 0 | 0 |
无效组合 | 0 | 1 |
表3.5.4.2 芯片启动模式控制
正常情况下,ESP32启动模式为“SPI BOOT”,当我们按下开发板的BOOT按键时才能进入“Download Boot”模式启动。
② VDD_SPI 电压控制
ESP32-S3系列芯片所需的VDD_SPI电压请参考《esp32-s3_datasheet_cn.pdf》数据手册的1.2型号对比表格,如下图所示。
图3.5.4.1 芯片型号对比
这个表格下定义了每个芯片型号VDD_SPI电压。由于正点原子ESP32S3开发板的模组选择的是ESP32-S3-WROOM-1-N16R8,而它的主控芯片为ESP32R8,所以根据上图的内容,我们会发现ESP32R8芯片的VDD_SPI电压为3.3V。接着我们来看一下GPIO45号管脚的定义,如下图所示。
图3.5.4.2 VDD_SPI电压控制
从上图可以看到,电压有两种控制方式,具体取决于EFUSE_VDD_SPI_FORCE的值。如果这个值为0,那么VDD_SPI电压取决于GPIO45的电平值。如果GPIO45的电平值为0,VDD_SPI电压为3.3V;否则为1.8V。相反,如果EFUSE_VDD_SPI_FORCE 为 1,VDD_SPI电压取决于 eFuse(表示 flash 电压调节器是否短接至VDD_RTC_IO)。如果eFuse为0,VDD_SPI电压值为1.8V;否则为 3.3V。
③ ROM日记打印控制
系统启动过程中, ROM代码日志可打印至UART和USB串口/JTAG控制器。我们可通过配置寄存器和eFuse可分别关闭UART和USB串口/JTAG控制器的ROM代码日志打印功能。详细信息请参考《ESP32-S3技术参考手册》 ->章节芯片Boot控制。
④ JTAG 信号源控制
在系统启动早期阶段,GPIO3可用于控制JTAG信号源。该管脚没有内部上下拉电阻,strapping的值必须由不处于高阻抗状态的外部电路控制。如图所示,GPIO3与EFUSE_DIS_PAD_JTAG、EFUSE_DIS_USB_JTAG和EFUSE_STRAP_JTAG_SEL共同控制 JTAG 信号源。
图3.5.4.3 JTAG信号源控制
注意:ESP32-S3系统中有一块4-Kbit的eFuse,其中存储着参数内容。相关内容请看《esp32-s3_technical_reference_manual_cn.pdf》技术参考手册-> 章节 eFuse 控制器。
3.5.5 中断矩阵
ESP32-S3的中断矩阵将任意外部中断源单独分配到双核CPU的任意外部中断上,以便在外部设备中断信号产生后,能够及时通知CPU0或CPU1进行处理。外部中断源必须经过中断矩阵分配至CPU0/CPU1外部中断,主要是因为ESP32-S3具有99个外部中断源,但每个CPU只有32个中断。通过使用中断矩阵,可以根据应用需求将一个外部中断源映射到多个CPU0中断或CPU1中断。实际上,CPU0和CPU1的外部中断只有26个,剩下的6个中断均为内部中断。
下图是双核中断矩阵结构。
图3.5.5.1 中断矩阵结构图
这种设计使得ESP32S3能够适应不同的应用需求,提供更大的灵活性和控制力。在硬件配置上,用户需要确保中断矩阵的正确配置,以便能够正确地接收和处理外部中断。同时,用户也需要通过编程方式,根据实际需求对中断矩阵进行适当的配置和操作。
当某个外部中断源满足触发条件时(例如GPIO引脚信号状态发生变化),该中断信号将被送入中断矩阵进行处理。中断矩阵将根据中断信号的特性,将其映射到一个特定的CPU外部中断上。当CPU接收到这个外部中断信号时,会执行与该中断相关联的ISR函数。
总的来说,ESP32S3的中断矩阵是一种高效的中断处理机制,它能够将多个外部中断源映射到两个CPU的外部中断上进行处理,并能够查询外部中断源当前的中断状态。
3.6 ESP32-S3启动流程
本文将会介绍ESP32-S3从上电到运行app_main函数中间所经历的步骤(即启动流程)。从宏观上,该启动流程可分为如下3个步骤。
①:一级引导程序,它被固化在ESP32-S3内部的ROM中,它会从flash的0x00处地址加载二级引导程序至RAM中。
②:二级引导程序从flash中加载分区表和主程序镜像至内存中,主程序中包含了RAM段和通过flash高速缓存映射的只读段。
③:应用程序启动阶段运行,这时第二个CPU和freeRTOS的调度器启动,最后进入app_main函数执行用户代码。
下面作者根据IDF库相关的代码来讲解这三个引导流程,如下:
一、一级引导程序
该部分程序是直接存储在ESP32-S3内部ROM中,所以普通开发者无法直接查看,它主要是做一些前期的准备工作(复位向量代码),然后从flash 0x00偏移地址中读取二级引导程序文件头中的配置信息,并使用这些信息来加载剩余的二级引导程序。
二、二级引导程序
该程序是可以查看且可被修改,在搭建ESP-IDF环境完成后,可在esp-idf\components\bootloader/subproject/main/路径下找到bootloader_start.c文件,此文件就是二级引导程序启动处。首先我们克隆ESP-IDF库,克隆过程如下所示。
图3.6.1 克隆ESP-IDF库
克隆完成后,使用VSCode打开ESP-IDF库,接着找到bootloader_start.c,如下图所示。
图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下定义的)。