一、简介
ESP-IDF(Espressif IoT Development Framework)是乐鑫为其 ESP32、ESP32-S 系列、ESP32-C 系列等芯片提供的官方物联网开发框架,基于 FreeRTOS 操作系统,支持 C、C++ 和 Lua 等语言,用于快速开发物联网、智能家居、工业控制等领域的嵌入式应用。
(1)主要特点:
- 硬件抽象层:提供 Wi-Fi、蓝牙、以太网、SPI、I2C、UART 等外设的驱动和 API。
- 丰富组件:包含文件系统(FAT、SPIFFS)、网络协议栈(TCP/IP、MQTT、HTTP)、安全框架(SSL/TLS)等。
- 工具链支持:集成 CMake 构建系统、idf.py 命令行工具,支持一键编译、烧录、调试。
- 跨平台:支持 Windows、Linux、macOS 开发环境。
- 示例与文档:提供大量官方示例和详细文档,便于快速上手。
(2)基本开发流程:
- 环境搭建:安装 ESP-IDF 工具链(包含编译器、调试工具等)。
- 项目创建:基于示例模板或自定义创建项目。
- 配置:通过
idf.py menuconfig
配置项目参数(如 Wi-Fi、串口波特率等)。 - 开发:编写代码实现业务逻辑。
- 编译与烧录:使用
idf.py build
编译,idf.py flash
烧录到设备。 - 调试:通过
idf.py monitor
查看串口输出或调试信息。
(3)典型应用场景:
- 低功耗物联网设备(如温湿度传感器)
- 智能家居控制(如智能开关、灯光控制)
- 工业自动化数据采集
- 无线通信中继设备
ESP-IDF 适合需要高性能、低功耗、丰富通信接口的嵌入式系统开发,尤其在物联网领域应用广泛。
二、创建工程
1.新建文件夹ESP32_Example
2. 使用命令idf.py build初始化该工程
注:CMakeLists.txt是帮助链接编译的文件
3.对于 ESP - IDF 等开发框架中component
文件夹的功能,用于明确该目录在项目结构里承担存放特定代码(第三方组件、用户程序驱动代码 )的角色 。使用命令idf.py create-component + 文件名字
4.在ESP32_Example下新建一个文件夹components,把所有组件放在components下
5.设置目标芯片
使用查询命令idf.py --list-targets查看有哪些芯片类型
idf.py set-target <芯片类型>
成功设置
6.输入命令idf.py menuconfig启动文本界面进行详细配置
7.设置SPI Flash速度模式,默认(DIO)即可
按下ESC退出
7.使用命令idf.py -p COM8 flash下载代码,这里只是检测是否能正常下载
8.编写测试函数
9.测试函数编译
10.测试函数下载
11.测试函数监控,使用命令idf.py monitor
可以看到了成功打印了hello,然后按下ctrl + ] 退出监控器。
12.清除编译文件
idf.py fullclean
三、使用VSCode+ESP-IDF插件编写代码
1.使用VSCode打开工程文件夹ESP32_Example
2.配置工程
选择下载程序方式、接口和目标芯片
3.扳手按钮编译
4.闪电按钮烧写
5.电视按钮启用监控
初步测试没问题。
6.最下方的芯片选择后的齿轮按钮进行SDK的详细配置
flash
ESP32-S3-N16R8 中, “N16” 代表的是16MB 的 Flash,“R8” 表示8MB 的 PSRAM。所以这款芯片的 Flash 大小是 16MB。
Partition Table
选择自定义分区表,分区表自定义名称:partitions_tabel_16MB.csv
PSRAM
使能外部SRAM,主要调整为八线模式和80MHz
CPU Frequency
FreeRTOS
一、GDB Stub(调试相关)
GDB Stub 是 FreeRTOS 中用于支持 GDB 调试器的组件,允许通过 GDB 查看任务状态、断点调试等。
- Enable listing FreeRTOS tasks through GDB Stub
启用后,GDB 调试器可以列出当前所有 FreeRTOS 任务的信息(如任务名、状态、优先级等),方便调试时监控任务运行状态。 - Maximum number of tasks supported by GDB Stub: 32
GDB Stub 最多支持同时显示 32 个任务的信息(超过则无法通过 GDB 完整列出)。
二、FreeRTOS Kernel(内核核心配置)
1. 内核基础
- Run the Amazon SMP FreeRTOS kernel instead (FEATURE UNDER DEVELOPMENT)
启用后将使用亚马逊的 SMP(对称多处理)版本 FreeRTOS 内核(实验性功能),支持多核心同时运行任务(适用于 ESP32S3 等双核芯片)。默认不启用时,使用标准单核心调度的 FreeRTOS 内核。 - Run FreeRTOS only on first core
限制 FreeRTOS 仅在芯片的第一个核心(Core 0)上运行,第二个核心(若有,如 ESP32S3 的 Core 1)可用于其他裸机程序或独立任务。 - configTICK_RATE_HZ: 1000
FreeRTOS 系统时钟节拍频率,单位为 Hz。此处设置为 1000,即每秒产生 1000 个时钟节拍(每个节拍间隔 1ms),用于任务调度、延时(如vTaskDelay()
)等时间相关操作。节拍频率越高,时间精度越高,但内核开销略增。
2. 栈溢出检测
- configCHECK_FOR_STACK_OVERFLOW: Check using canary bytes (Method 2)
启用栈溢出检测功能,采用「金丝雀字节(canary bytes)」方式(方法 2):在任务栈的末尾放置特殊标记字节(金丝雀值),每次任务切换时检查标记是否被改写,若改写则判定为栈溢出(会触发栈溢出钩子函数)。这是一种常用的内存越界检测机制。
3. 任务与内存相关
- configNUM_THREAD_LOCAL_STORAGE_POINTERS: 1
每个任务可分配的「线程本地存储指针(TLS)」数量。TLS 用于存储任务私有数据(如任务专属的变量),此处最多支持 1 个指针。 - configMINIMAL_STACK_SIZE (Idle task stack size): 1536
空闲任务(Idle Task)的栈大小(单位:字节)。空闲任务是 FreeRTOS 自动创建的最低优先级任务,用于系统空闲时运行,栈大小需根据系统最小需求配置(此处 1536 字节适用于多数嵌入式场景)。 - configMAX_TASK_NAME_LEN: 16
任务名称的最大长度(包含终止符\0
),超过则会被截断。例如,任务名最长为 15 个可见字符 + 1 个终止符。 - configENABLE_BACKWARD_COMPATIBILITY
启用后兼容旧版本 FreeRTOS 的 API 或宏定义(如旧版中的函数参数、结构体成员),避免因版本升级导致的兼容性问题。
4. 定时器功能
- configUSE_TIMERS
启用 FreeRTOS 软件定时器功能,允许创建周期性或一次性定时器(通过xTimerCreate()
等 API)。 - configTIMER_SERVICE_TASK_NAME: Tmr Svc
定时器服务任务的名称(默认Tmr Svc
),该任务负责处理所有软件定时器的触发逻辑。 - configTIMER_SERVICE_TASK_CORE_AFFINITY: No affinity
定时器服务任务的核心绑定(适用于多核芯片),「No affinity」表示不固定核心,由内核自动调度到任意核心。 - configTIMER_TASK_PRIORITY: 1
定时器服务任务的优先级(数值越大优先级越高),此处为 1(较低优先级,避免抢占关键任务)。 - configTIMER_TASK_STACK_DEPTH: 2048
定时器服务任务的栈大小(2048 字节),需根据定时器回调函数的复杂度调整(若回调函数复杂,需增大栈大小)。 - configTIMER_QUEUE_LENGTH: 10
定时器命令队列的长度,用于存储待处理的定时器操作(如启动、停止定时器),超过 10 个则需等待队列空闲。
5. 队列与通知
- configQUEUE_REGISTRY_SIZE: 0
队列注册表的大小,用于将队列 / 信号量与名称关联(通过vQueueAddToRegistry()
),方便调试时识别队列。0 表示禁用该功能。 - configTASK_NOTIFICATION_ARRAY_ENTRIES: 1
每个任务的「任务通知数组」长度。任务通知是一种轻量级的通信机制(替代信号量 / 队列),此处最多支持 1 个通知条目(每个任务可接收 1 种通知)。
6. 调试与统计
- configUSE_TRACE_FACILITY
启用跟踪功能,支持 FreeRTOS 的跟踪宏(如vTaskList()
列出任务状态、vTaskGetRunTimeStats()
统计任务运行时间),用于系统性能分析。 - configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES
启用链表数据完整性检查,在 FreeRTOS 内部链表(如任务就绪链表、阻塞链表)的节点中添加校验字节,检测链表是否被意外篡改(如内存越界导致的链表损坏)。 - configGENERATE_RUN_TIME_STATS
启用任务运行时间统计功能,可通过vTaskGetRunTimeStats()
获取每个任务的运行时间占比(需配合硬件定时器实现)。 - configUSE_APPLICATION_TASK_TAG
允许为任务设置「标签(tag)」,通过vTaskSetApplicationTaskTag()
关联一个整数或指针,用于标识任务(如区分任务类型、归属模块)。
三、Port(端口配置,与硬件相关)
针对特定硬件平台(如 ESP32S3 的 Xtensa 架构)的 FreeRTOS 移植配置。
- Wrap task functions
对任务函数进行包装(如添加进入 / 退出钩子),通常用于调试或跟踪任务的创建 / 销毁。 - Enable stack overflow debug watchpoint
启用栈溢出调试断点(依赖硬件 watchpoint 功能),当栈指针超出栈范围时触发调试中断,方便定位栈溢出问题。 - Enable thread local storage pointers deletion callbacks
启用线程本地存储(TLS)指针的删除回调,当任务销毁时自动调用 TLS 指针的清理函数(释放资源)。 - Enable task pre-deletion hook
启用任务删除前的钩子函数(vTaskPreDeleteHook()
),在任务被删除前执行自定义逻辑(如释放任务持有的资源)。 - Enable static task clean up hook (DEPRECATED)
启用静态任务的清理钩子(已过时),用于静态创建的任务(xTaskCreateStatic()
)销毁时的资源清理。 - Check that mutex semaphore is given by owner task
检查互斥锁(mutex)的释放操作是否由持有锁的任务执行(避免其他任务释放不属于自己的锁,导致死锁)。 - ISR stack size: 1536
中断服务程序(ISR)的栈大小(1536 字节),用于处理中断时的临时数据存储(栈不足会导致系统崩溃)。 - Enable backtrace from interrupt to task context
启用从中断上下文到任务上下文的回溯功能,当发生中断时,可追踪中断前正在运行的任务调用栈(方便调试中断相关问题)。 - Use float in Level 1 ISR
允许在 Level 1 中断服务程序中使用浮点数(需硬件支持浮点运算,且会增加中断处理开销)。 - Tick timer source (Xtensa Only): SYSTIMER 0 (level 1)
选择 FreeRTOS 系统节拍(tick)的定时器源(仅 Xtensa 架构),此处使用 SYSTIMER 0(系统定时器 0),且为 Level 1 中断(较高优先级,确保节拍计时准确)。 - Place FreeRTOS functions into Flash
将 FreeRTOS 函数存储在 Flash 中(而非 RAM),节省 RAM 空间(适用于 RAM 有限的嵌入式设备)。 - *Tests compliance with Vanilla FreeRTOS port _CRITICAL calls
检查与「标准 FreeRTOS 端口」中临界区函数(如taskENTER_CRITICAL()
)的兼容性,确保临界区操作符合官方规范。
四、Extra(额外配置)
- Allow external memory as an argument to xTaskCreateStatic (READ HELP)
允许将外部内存(如 SPI RAM)作为参数传递给xTaskCreateStatic()
(静态创建任务的 API),即任务的栈和控制块可存储在外部内存中(需确保外部内存的访问速度和稳定性)。
有如下更改:
100对应的节拍周期是10ms,1000对应的节拍周期是1ms
7.保存SDK配置后,ctrl+shift+p打开分区表编辑器
这是 ESP-IDF Partition Table Editor(ESP-IDF 分区表编辑器) 的界面,用于可视化配置 ESP32/ESP32-S3 等设备的 Flash 分区表。以下逐行解释核心内容:
1. 界面标题与功能说明
ESP-IDF Partition Table Editor
标题:明确这是 ESP-IDF 框架的分区表可视化工具。Partition Editor can help you to easily edit, build & flash partition table through GUI...
功能说明:无需手动编辑 CSV 文件,可通过图形界面(GUI)轻松编辑、构建和烧录分区表。
2. 操作按钮(右上角)
Select Flash Method
选择烧录方式:指定如何将分区表烧录到设备(如串口、JTAG 等)。Build
构建:根据当前配置生成分区表二进制文件(.bin
)。Flash
烧录:将生成的分区表二进制文件烧录到设备的 Flash 中。
3. 分区表配置区域
表格中每行对应一个 Flash 分区,包含以下字段:
字段 1:Name
(分区名称)
nvs
:Non-Volatile Storage(非易失性存储)分区,用于保存设备配置、键值对数据(如 WiFi 密码、自定义参数)。phy_init
:PHY(物理层)初始化数据分区,存储 WiFi / 蓝牙射频的校准参数。factory
:工厂应用分区,存储默认的固件镜像(设备首次启动或恢复出厂设置时运行的程序)。vfs
:Virtual File System(虚拟文件系统)分区,类型为fat
(FAT 文件系统),可用于存储文件(如配置文件、日志)。storage
:自定义存储分区,类型为spiffs
(SPIFFS 文件系统),适合嵌入式设备的轻量级文件存储。
字段 2:Type
(分区类型)
data
:数据分区(存储配置、文件系统等非程序数据)。app
:应用分区(存储固件程序,可被 Bootloader 加载运行)。
字段 3:Sub Type
(子类型)
nvs
:对应nvs
分区,标识该数据分区用于 NVS。phy
:对应phy_init
分区,标识该数据分区用于 PHY 初始化。factory
:对应factory
分区,标识该应用分区为工厂固件。fat
:对应vfs
分区,标识该数据分区使用 FAT 文件系统。spiffs
:对应storage
分区,标识该数据分区使用 SPIFFS 文件系统。
字段 4:Offset
(分区起始偏移)
- 如
0x9000
、0xF000
等,表示该分区在 Flash 中的起始地址(相对于 Flash 起始位置)。 - 需注意分区之间不能重叠,且需符合 Flash 擦除块(erase block)对齐要求。
字段 5:Size
(分区大小)
- 如
0x6000
(24KB)、0x1F0000
(2,048KB = 2MB)等,表示该分区占用的 Flash 空间大小。
字段 6:Encrypted
(加密)
- 复选框:启用后,该分区数据会被加密(需配合 ESP-IDF 的安全机制,如 Secure Boot、Flash 加密)。
字段 7:×
(删除按钮)
- 点击可删除对应分区的配置。
4. 关键分区的作用
nvs
:必选分区,用于保存设备运行时的配置(掉电不丢失)。phy_init
:必选分区(若使用 WiFi / 蓝牙),存储射频校准参数,确保无线通信稳定。factory
:必选分区,存储默认固件,设备启动时 Bootloader 会优先加载此分区的程序。vfs
(FAT):可选分区,适合存储文件(如文本、二进制数据),支持电脑直接挂载访问。storage
(SPIFFS):可选分区,轻量级文件系统,适合嵌入式设备(无需复杂的文件系统支持)。
5. 实际应用场景
- 分区表的作用:将 Flash 划分为多个逻辑区域,分别存储程序、配置、文件系统等,让不同功能的数据互不干扰。
- 修改注意事项:
- 调整分区大小或偏移时,需确保 Flash 总容量足够(如 ESP32-S3-N16R8 的 Flash 是 16MB)。
- 若新增文件系统分区(如
vfs
、storage
),需在代码中初始化对应的文件系统(如esp_vfs_fat_mount()
或esp_spiffs_mount()
)。 - 烧录新分区表后,若覆盖了原有数据分区(如
nvs
),可能导致配置丢失,需谨慎操作。
简单说,这个界面让你可视化管理 ESP32 设备的 Flash 分区,像分硬盘区一样把 Flash 划给固件、配置、文件系统等不同功能使用,避免手动写 CSV 配置的麻烦~
分区如下:
8.编译烧写
头文件报红问题:
验证了程序正常,但是这里头文件报红是因为VSCode里找不到组件文件,需要配置一下路径,这个组件的位置其实是在ESP-IDF里带的组件源码
方法一:
(1)可以在文件夹.vscode下手动添加c_cpp_properties.json,或者点击VSCode的右下角的WIN32点击JSON可以得到自动生成的c_cpp_properties.json文件
(2)"D:/Espressif/frameworks/esp-idf-v5.4/components"你需要将你安装的ESP-IDF里的组件路径导入VSCode的json配置文件里,然后如果还有波浪线需要使用VSCode的自带的快速修复自动添加路径。
方法二:
按下ctrl+shift+P,输入Add VS Code Configuration Folder,然后VSCode会把我们自动配置路径,以及更好地完善组件文件与用户文件地内联关系,可以自己去看看,了解一下。
结果:
11.bsp_led和main的CMakeLists分别需要补充依赖
bsp_led
# idf_component_register(SRCS "bsp_led.c"
# INCLUDE_DIRS "include")
idf_component_register(
SRCS "bsp_led.c" # 合并两个源文件
INCLUDE_DIRS "include" # 合并头文件目录
REQUIRES driver # 声明依赖 driver 组件
)
main
idf_component_register(SRCS "ESP32_Example.c"
INCLUDE_DIRS "."
REQUIRES bsp_led )
10.编写点灯测试代码
bsp_led.c
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "bsp_led.h"
void bsp_led_init(void)
{
// 设置GPIO1为输出
gpio_set_direction(GPIO_NUM_1, GPIO_MODE_OUTPUT);
// // GPIO配置结构体
// gpio_config_t io_conf = {
// .pin_bit_mask = (1ULL<<GPIO_NUM_1), // 选择GPIO1
// .mode = GPIO_MODE_OUTPUT, // 设置为输出模式
// .pull_up_en = GPIO_PULLUP_ENABLE, // 启用上拉
// .pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用下拉
// .intr_type = GPIO_INTR_DISABLE // 禁用中断
// };
// // 配置GPIO
// esp_err_t ret = gpio_config(&io_conf);
// if (ret != ESP_OK) {
// printf("GPIO配置失败\n");
// }
}
void bsp_led_toggle(void)
{
// 切换GPIO1的状态
gpio_set_level(GPIO_NUM_1, !gpio_get_level(GPIO_NUM_1));
//vTaskDelay(pdMS_TO_TICKS(1000)); // 延时1秒
}
main.c(ESP32_Example.c)
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// #include "nvs_flash.h"
#include "esp_system.h"
#include "esp_chip_info.h"
// #include "esp_psram.h"
// #include "esp_flash.h"
#include "bsp_led.h" /* 包含 LED 头文件 */
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
// esp_err_t ret;
// uint32_t flash_size;
// esp_chip_info_t chip_info; /* 定义芯片信息结构体变量 */
// ret = nvs_flash_init(); /* 初始化 NVS */
// if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
// {
// ESP_ERROR_CHECK(nvs_flash_erase());
// ret = nvs_flash_init();
// }
// esp_flash_get_size(NULL, &flash_size); /* 获取 FLASH 大小 */
// esp_chip_info(&chip_info);
// printf("内核: cup 数量%d\n",chip_info.cores); /* 获取 CPU 内核数并显示 */
// /* 获取 FLASH 大小并显示 */
// printf("FLASH size:%ld MB flash\n",flash_size / (1024 * 1024));
// /* 获取 PARAM 大小并显示 */
// printf("PSRAM size: %d bytes\n", esp_psram_get_size());
bsp_led_init(); /* 初始化 LED */
while(1)
{
printf("Hello-ESP32\r\n");
bsp_led_toggle(); /* 切换 LED 状态 */
vTaskDelay(1000);
}
}
参考链接:
【正点原子】手把手教你学ESP32S3快速入门
ESP32的ESP-IDF在VScode工程下,头文件标红警告、报错、无法跳转