diff --git a/README.md b/README.md index 0e58b74..184588b 100644 --- a/README.md +++ b/README.md @@ -46,12 +46,12 @@ | 一、认识ESP32 | 1.3 idf.py的基本使用 | ✔ | ✔ | 无 | 2025/1/7 | | 二、IDF基础与工具使用 | 2.1 ESP32工程结构及构建 | ✔ | ✔ | ✔ | 2025/1/9 | | 二、IDF基础与工具使用 | 2.2 ESP-IDF项目配置 | ✔ | ✔ | ✔ | 2025/1/21 | -| 二、IDF基础与工具使用 | 2.3 ESP-IDF自定义组件 | ✔ | | ✔ | | -| 二、IDF基础与工具使用 | 2.4 ESP-IDF组件管理器 | ✔ | | ✔ | | -| 三、FreeRTOS基础 | 3.1 FreeRTOS概述 | | | | | -| 三、FreeRTOS基础 | 3.2 FreeRTOS多任务与任务管理 | | | | | -| 三、FreeRTOS基础 | 3.3 FreeRTOS任务看门狗 | | | | | -| 四、外设学习 | 4.1.1 GPIO入门 | | | | | +| 二、IDF基础与工具使用 | 2.3 ESP-IDF自定义组件 | ✔ | ✔ | ✔ | 2025/1/22 | +| 二、IDF基础与工具使用 | 2.4 ESP-IDF组件管理器 | ✔ | ⏳ | ✔ | | +| 三、FreeRTOS基础 | 3.1 FreeRTOS概述 | ⏳ | | 无 | | +| 三、FreeRTOS基础 | 3.2 FreeRTOS多任务与任务管理 | ⏳ | | | | +| 三、FreeRTOS基础 | 3.3 FreeRTOS任务看门狗 | ⏳ | | | | +| 四、外设学习 | 4.1.1 GPIO入门 | ⏳ | | | | | 四、外设学习 | 4.1.2 UART串口通信 | | | | | | 四、外设学习 | 4.1.3 硬件定时器 | | | | | | 四、外设学习 | 4.1.4 ADC模数转换 | | | | | @@ -89,7 +89,6 @@ 至于LVGL,特殊外设,实战演练部分,会在教程章节单独推荐开发板。 - ## 常见问题及解决方案: 本部分记录搭建环境和开发过程中常见的问题和解决方案,都是本人在开发过程中遇到的,作为经验积累,分享给大家。 @@ -151,7 +150,7 @@ 本节讲解如何创建和管理ESP-IDF的自定义组件,包括组件的目录结构、声明与调用方法,以及组件的复用和共享技巧,帮助读者构建模块化项目。 - 在线文字教程:[ESP-IDF自定义组件.md](docs/02.ESP-IDF基础/2.3-ESP-IDF自定义组件/ESP-IDF自定义组件.md) -- 在线视频教程:[ESP-IDF自定义组件开发详解] +- 在线视频教程:[ESP-IDF自定义组件](https://www.bilibili.com/video/BV182ftYHEox/?spm_id_from=333.1387.upload.video_card.click&vd_source=ef5a0ab0106372751602034cdd9ab98e) - 教程配套代码:[组件示例代码](https://github.com/DuRuofu/ESP32-Guide-Code/tree/master/02.idf_basic/03/blink_component) ## 2.4 ESP-IDF组件管理器 @@ -159,7 +158,7 @@ 本节重点介绍ESP-IDF组件管理器的功能与使用方法,涵盖如何通过组件管理器导入外部库、配置组件依赖,使用官方提供的外部组件。 - 在线文字教程:[ESP-IDF组件管理器.md](docs/02.ESP-IDF基础/2.4-ESP-IDF组件管理器/ESP-IDF组件管理器.md) -- 在线视频教程:[ESP-IDF组件管理器详解] +- 在线视频教程:[ESP-IDF组件管理器详解]() - 教程配套代码:[组件管理器示例代码](https://github.com/DuRuofu/ESP32-Guide-Code/tree/master/02.idf_basic/04/button_blink) --- @@ -168,13 +167,12 @@ > 目标:学习FreeRTOS的核心概念与基本功能,掌握任务管理与调度机制,为实时系统开发打下基础。 -## 3.1 FreeRTOS介绍于引入 +## 3.1 FreeRTOS介绍与引入 本节将介绍FreeRTOS的核心概念,包括实时操作系统的特点、FreeRTOS的基本架构和设计思想,以及其在嵌入式开发中的应用场景,帮助读者快速了解FreeRTOS的基础知识。 > 在线文字教程:[FreeRTOS概述.md](./docs/03.FreeRTOS基础/3.1-FreeRTOS概述/FreeRTOS概述.md) > 在线视频教程:[FreeRTOS概述讲解] -> 教程配套代码:[FreeRTOS初体验示例代码](#) ## 3.2 FreeRTOS多任务与任务管理 @@ -206,7 +204,7 @@ 本节介绍GPIO(通用输入输出)的基本功能,包括输入、输出及中断操作。通过实际案例,学习如何控制外部设备并响应硬件信号。 > 在线文字教程:[GPIO入门](./docs/04.外设学习/4.1-基础外设/4.1.1-GPIO/GPIO入门.md) -> 在线视频教程:[GPIO开发详解] +> 在线视频教程:[GPIO开发详解]() > 教程配套代码:[GPIO示例代码](#) ### 4.1.2 UART diff --git a/docs/02.ESP-IDF基础/2.3-ESP-IDF自定义组件/ESP-IDF自定义组件.md b/docs/02.ESP-IDF基础/2.3-ESP-IDF自定义组件/ESP-IDF自定义组件.md index 9540cd9..88fceab 100644 --- a/docs/02.ESP-IDF基础/2.3-ESP-IDF自定义组件/ESP-IDF自定义组件.md +++ b/docs/02.ESP-IDF基础/2.3-ESP-IDF自定义组件/ESP-IDF自定义组件.md @@ -47,7 +47,6 @@ - led_blink.c - include/ - led_blink.h - ``` 将点灯的函数定义裁剪到 `led_blink.c` @@ -85,14 +84,15 @@ void configure_led(void) #define LED_BLINK_H #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif #include -// Function declarations -void blink_led(int s_led_state); -void configure_led(void); + // Function declarations + void blink_led(uint8_t s_led_state); + void configure_led(void); #ifdef __cplusplus } @@ -100,6 +100,7 @@ void configure_led(void); #endif /* LED_BLINK_H */ + ``` 新建Kconfig文件,移植组件配置,参考[ESP-IDF项目配置](../2.2-ESP-IDF项目配置/ESP-IDF项目配置.md) @@ -180,4 +181,5 @@ void app_main(void) # 参考链接 -1. https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-guides/build-system.html \ No newline at end of file +1. https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-guides/build-system.html +2. https://developer.espressif.com/blog/2024/12/how-to-create-an-esp-idf-component/ \ No newline at end of file diff --git a/docs/02.ESP-IDF基础/2.4-ESP-IDF组件管理器/ESP-IDF组件管理器.md b/docs/02.ESP-IDF基础/2.4-ESP-IDF组件管理器/ESP-IDF组件管理器.md index c390819..c4d7752 100644 --- a/docs/02.ESP-IDF基础/2.4-ESP-IDF组件管理器/ESP-IDF组件管理器.md +++ b/docs/02.ESP-IDF基础/2.4-ESP-IDF组件管理器/ESP-IDF组件管理器.md @@ -3,11 +3,9 @@ ESP-IDF官方对组件管理器的描述是这样的: ->The IDF Component Manager is a tool that allows developers to manage components for the ESP-IDF development framework. The tool is compatible with ESP-IDF versions 4.1 and later, and is included by default starting with ESP-IDF version 4.4. -> - The ESP Component Registry is a central repository for components that can be used with the ESP-IDF. It is hosted at [https://components.espressif.com](https://components.espressif.com/) and provides a convenient way for developers to discover and download components for their projects. -> - With the IDF Component Manager, developers can easily install components from the ESP-IDF Component Registry, streamlining the process of adding new functionality to their projects. +> The IDF Component Manager is a tool that allows developers to easily and reliably add components developed by Espressif and the community to the projects developed with the ESP-IDF framework and create their own components for others to use. +>The [ESP Component Registry](https://components.espressif.com/) is a central repository for components that can be used with the ESP-IDF via the IDF Component Manager. +>With the IDF Component Manager, developers can easily install components from the ESP Component Registry, streamlining the process of adding new functionality to their projects. 说人话就是,世上本没有组件管理器,自定义组件多了,就有了组件库,就有了组件管理器。 diff --git a/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/FreeRTOS概述.md b/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/FreeRTOS概述.md index a77d8c9..e341434 100644 --- a/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/FreeRTOS概述.md +++ b/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/FreeRTOS概述.md @@ -1 +1,76 @@ -FreeRTOS 是一个开源的 RTOS(实时操作系统)内核,它以组件的形式集成到 ESP-IDF 中。因此,所有的 ESP-IDF 应用程序及多种 ESP-IDF 组件都基于 FreeRTOS 编写。FreeRTOS 内核已移植到 ESP 芯片的所有 CPU 架构(即 Xtensa 和 RISC-V)中。 \ No newline at end of file + +FreeRTOS 是一个开源的 RTOS(实时操作系统)内核,它以组件的形式集成到 ESP-IDF 中。因此,所有的 ESP-IDF 应用程序及多种 ESP-IDF 组件都基于 FreeRTOS 编写。 + +## 1.1 RTOS是什么? + +实时操作系统 (RTOS) 是一种体积小巧、确定性强的计算机操作系统。 RTOS 通常用于需要在严格时间限制内对外部事件做出反应的嵌入式系统,如医疗设备和汽车电子控制单元 (ECU)。 通常,此类嵌入式系统中只有一两项功能需要确定性时序,即使嵌入式系统不需要严格的实时反应,使用 RTOS 仍能提供诸多优势。RTOS 通常比通用操作系统体积更小、重量更轻,因此 RTOS 非常适用于 内存、计算和功率受限的设备。 + + +## 1.2 与裸机的对比 + +### 1. 裸机: + +在裸机系统中,所有的操作都是在一个无限的大循环里面实现,支持中断检测。外部中断紧急事件在中断里面标记或者响应,中断服务称为前台,main 函数里面的while(1)无限循环称为后台,按顺序处理业务功能,以及中断标记的可执行的事件。小型的电子产品用的都是裸机系统,而且也能够满足需求。但当程序复杂之后,这样的裸机程序难以阅读和维护。 +![](attachments/20250111224625.png) +### 2. RTOS: + +在实时操作系统中,我们可以把要实现的功能划分为多个任务,每个任务负责实现其中的一部分,每个任务都是一个很简单的程序,通常是一个死循环。 RTOS操作系统的核心内容在于:实时内核。 + +RTOS的内核负责管理所有的任务,内核决定了运行哪个任务,何时停止当前任务切换到其他任务,这个是内核的多任务管理能力。多任务管理给人的感觉就好像芯片有多个CPU,多任务管理实现了CPU资源的最大化利用,多任务管理有助于实现程序的模块化开发,能够实现复杂的实时应用。可剥夺内核顾名思义就是可以剥夺其他任务的CPU使用权,它总是运行就绪任务中的优先级最高的那个任务。 + +加入操作系统后,开发人员不需要关注每个功能模块之间的冲突,重心放在子程序的实现。缺点是整个系统随之带来的额外RAM开销,但对目前的单片机的来影响不大。 + +![](attachments/20250111224634.png) +## 1.3 RTOS多任务处理与并发的实现 + +常规单核处理器一次只能执行一个任务,但多任务操作系统可以快速切换任务, 使所有任务看起来像是同时在执行。 + +![](attachments/20250104213820.png) + + +切换的过程由**任务调度器**实现,任务调度器就是使用相关的调度算法来决定当前需要执行的哪个任务。 + +FreeRTOS一共支持三种任务调度方式: + +- **抢占式调度** :主要是针对优先级不同的任务,每一个任务都有一个任务优先级,优先级高的任务可以抢占低优先级的任务的CPU使用权。 +- **时间片调度** :主要针对相同优先级的任务,当多个任务的优先级相同时,任务调度器会在每个时钟节拍到来的时候切换任务。 +- **协程式调度** :其实就是轮询,当前执行任务将会一直运行,同时高优先级的任务不会抢占低优先级任务。FreeRTOS现在虽然还在支持,但官方已经明确表示不再更新协程式调度。 + +总的来说: + +1. 高优先级任务,优先执行。 +2. 高优先级任务不停止,低优先级任务无法执行。 +3. 被抢占的任务将会进去就绪态。 + +## 1.4 相关概念 + +### 时间片: + +同等优先级任务轮流享有相同的CPU时间(可设置),叫做时间片,在FreeRTOS中,一个时间片等于SysTick中断周期。 +### 队列: + +队列是一种只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。队尾放入数据,对头挤出。先进先出,称为FIFO +### 任务: + +在裸机系统中,系统的主体就是 main 函数里面顺序执行的无限循环,这个无限循环里面 CPU 按照顺序完成各种事情。在多任务系统中,根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为任务。系统中的每一任务都有多种运行状态。系统初始化完成后,创建的任务就可以在系统中竞争一定的资源,由内核进行调度。 + +![](attachments/20250106204735.png) + +任务可以存在于以下状态中: + +- **运行** + 当任务实际执行时,它被称为处于运行状态。任务当前正在使用处理器。 如果运行 RTOS 的处理器只有一个内核, 那么在任何给定时间内都只能有一个任务处于运行状态。 +- **准备就绪** + 准备就绪任务指那些能够执行(它们不处于阻塞或挂起状态), 但目前没有执行的任务, 因为同等或更高优先级的不同任务已经处于运行状态。 +- **阻塞** + 如果任务当前正在等待时间或外部事件,则该任务被认为处于阻塞状态。 例如,如果一个任务调用vTaskDelay(),它将被阻塞(被置于阻塞状态), 直到延迟结束——一个时间事件。 任务也可以通过阻塞来等待队列、信号量、事件组、通知或信号量 事件。处于阻塞状态的任务通常有一个"超时"期, 超时后任务将被超时,并被解除阻塞, 即使该任务所等待的事件没有发生。“阻塞”状态下的任务不使用任何处理时间,不能 被选择进入运行状态。 +- **挂起** + 与“阻塞”状态下的任务一样, “挂起”状态下的任务不能 被选择进入运行状态,但处于挂起状态的任务 没有超时。相反,任务只有在分别通过 vTaskSuspend() 和 xTaskResume() API 调用明确命令时 才会进入或退出挂起状态。 + + +### 临界区: + +临界区就是一段在执行的时候不能被中断的代码段。在多任务操作系统里面,对全局变量的操作不能被打断,不能执行到一半就被其他任务再次操作。一般被打断,原因就是系统调度或外部中断。对临界区的保护控制,归根到底就是对系统中断的使能控制。 +在使用临界区时,关闭中断响应,对部分优先级的中断进行屏蔽,因此临界区不允许运行时间过长。为了对临界区进行控制,就需要使用信号量通信,实现同步或互斥操作。 + + diff --git a/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/attachments/20250104213820.png b/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/attachments/20250104213820.png new file mode 100644 index 0000000..afdf62a Binary files /dev/null and b/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/attachments/20250104213820.png differ diff --git a/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/attachments/20250106204735.png b/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/attachments/20250106204735.png new file mode 100644 index 0000000..af2b1ee Binary files /dev/null and b/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/attachments/20250106204735.png differ diff --git a/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/attachments/20250111224625.png b/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/attachments/20250111224625.png new file mode 100644 index 0000000..7914a49 Binary files /dev/null and b/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/attachments/20250111224625.png differ diff --git a/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/attachments/20250111224634.png b/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/attachments/20250111224634.png new file mode 100644 index 0000000..d5aea8d Binary files /dev/null and b/docs/03.FreeRTOS基础/3.1-FreeRTOS概述/attachments/20250111224634.png differ diff --git a/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/FreeRTOS多任务与任务管理.md b/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/FreeRTOS多任务与任务管理.md index 7d4216f..0e23e2a 100644 --- a/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/FreeRTOS多任务与任务管理.md +++ b/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/FreeRTOS多任务与任务管理.md @@ -1,19 +1,21 @@ - - - - - -注意:与原生 FreeRTOS 不同,在 ESP-IDF 中使用 FreeRTOS 的用户 永远不应调用 `vTaskStartScheduler()` 和 `vTaskEndScheduler()`。相反,ESP-IDF 会自动启动 FreeRTOS。用户必须定义一个 `void app_main(void)` 函数作为用户应用程序的入口点,并在 ESP-IDF 启动时被自动调用。 +注意:与原生 FreeRTOS 不同,在 ESP-IDF 中使用 FreeRTOS 的用户 \永远不应调用 `vTaskStartScheduler()` 和 `vTaskEndScheduler()`。相反,ESP-IDF 会自动启动 FreeRTOS。用户必须定义一个 `void app_main(void)` 函数作为用户应用程序的入口点,并在 ESP-IDF 启动时被自动调用。 通常,用户会从 `app_main` 中启动应用程序的其他任务。`app_main` 函数可以在任何时候返回(应用终止前)。`app_main` 函数由 main 任务调用。 +## 一、FreeRTOS创建任务 -# ESP-IDF中的FreeRTOS的基本使用 +### 1.1 API说明: -## FreeRTOS创建任务 +创建任务涉及三个API: -### 1. xTaskCreate : 动态创建一个任务 +| 函数名 | 功能 | 动态/静态 | 备注 | +| ----------------------------- | -------------- | ----- | --- | +| `xTaskCreate` | 动态创建任务 | 动态 | | +| `xTaskCreateStatic` | 静态创建任务 | 静态 | | +| `xTaskCreateRestrictedStatic` | 静态创建受限任务(权限控制) | 静态 | 很少用 | +下面是对各个API的详细解释: +#### 1. xTaskCreate : 动态创建一个任务 当需要在运行时动态分配内存来创建任务时使用,也就是一般的正常情况。 @@ -39,54 +41,365 @@ static inline BaseType_t xTaskCreate(TaskFunction_t pxTaskCode, - `uxPriority`:任务的优先级(数字越大优先级越高,最低为1)。支持 MPU 的系统中,通过设置 `portPRIVILEGE_BIT` 位可创建特权任务(例如 `(2 | portPRIVILEGE_BIT)` 表示优先级为 2 的特权任务)。 - `pxCreatedTask`:用于存储任务句柄(可选),通过句柄可以引用创建的任务。 +返回值: -## FreeRTOS删除任务 +- `pdPASS`:任务创建成功。 +- `errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY`:任务创建失败(内存不足) + +#### 2. xTaskCreateStatic:静态创建一个任务 + +手动提供任务栈和任务控制块(TCB),避免动态内存分配。 + +**原型:** + +``` c +TaskHandle_t xTaskCreateStatic( + TaskFunction_t pvTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const puxStackBuffer, + StaticTask_t * const pxTaskBuffer +); +``` + +**参数:** + +- `pvTaskCode`:任务函数的入口地址。 +- `pcName`:任务名称(用于调试)。 +- `usStackDepth`:任务栈大小(以字为单位)。 +- `pvParameters`:传递给任务函数的参数。 +- `uxPriority`:任务的优先级。 +- `puxStackBuffer`:指向任务栈缓冲区的指针(由用户提供)。 +- `pxTaskBuffer`:指向任务控制块的缓冲区(由用户提供)。 + +**返回值:** + +- `pdPASS`:任务创建成功。 +- `errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY`:任务创建失败(内存不足) + +#### 3. xTaskCreateRestrictedStatic:为受限任务(restricted task)创建任务 + +用于创建特定权限的任务,一般用于内存保护单元(MPU)配置中,限制任务的访问权限。 + +原型: + +``` c +TaskHandle_t xTaskCreateRestrictedStatic( + const TaskParameters_t * const pxTaskDefinition, + StackType_t * const puxStackBuffer, + StaticTask_t * const pxTaskBuffer +); +``` + +参数说明: + +- `pxTaskDefinition`:任务参数定义结构(包含任务入口、名称、栈大小、优先级等)。 +- `puxStackBuffer`:栈的静态缓冲区。 +- `pxTaskBuffer`:任务控制块的静态缓冲区。 + +返回值:任务句柄。 + +### 1.2 创建任务示例代码: + +``` c +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "main"; + +// 任务函数 +void myTask(void *pvParameters) +{ + for (;;) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + ESP_LOGI(TAG, "myTask"); + } +} + +void app_main(void) +{ + // 创建一个 FreeRTOS 任务 + // 参数说明: + // 1. 任务入口函数:myTask + // 2. 任务名称:"myTask",用于调试时标识任务 + // 3. 任务堆栈大小:2048 字节(适当分配以避免栈溢出) + // 4. 任务参数:NULL(未传递参数) + // 5. 任务优先级:1(优先级较低,空闲任务优先级为 0) + // 6. 任务句柄:NULL(不需要保存任务句柄) + xTaskCreate(myTask, "myTask", 2048, NULL, 1, NULL); +} +``` -## FreeRTOS创建任务传参 +## 二、[FreeRTOS删除任务](https://www.freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/04-API-references/01-Task-creation/00-TaskHandle) + +### 2.1 API说明: + +删除任务涉及一个API: + +| 函数名 | 功能 | 动态/静态 | 备注 | +| ------------- | ---- | ----- | ---------------- | +| `vTaskDelete` | 删除任务 | 动态/静态 | 可以删除别的任务,也可以自我删除 | +下面是对各个API的详细解释: +#### 1. vTaskDelete:删除一个任务 + +当任务完成其功能后,需要释放资源,或当系统需要动态调整任务时使用 +注意:调用后,任务进入删除 状态,但动态分配的内存需要由 FreeRTOS 自动释放。 + +**原型:** + +```c +void vTaskDelete( TaskHandle_t xTask ); +``` + +**参数说明:** + +- xTask:要删除的任务句柄。如果传递 NULL,则删除当前任务。 + +### 2.2 示例代码: + +#### 1. 删除别人 + +```c +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "main"; + +// 任务函数 +void myTask(void *pvParameters) +{ + for (;;) + { + vTaskDelay(500 / portTICK_PERIOD_MS); + ESP_LOGI(TAG, "myTask"); + } +} + +void app_main(void) +{ + // 任务句柄 + TaskHandle_t taskHandle = NULL; + // 创建一个 FreeRTOS 任务 + xTaskCreate(myTask, "myTask", 2048, NULL, 1, &taskHandle); + + vTaskDelay(2000 / portTICK_PERIOD_MS); + + // 删除任务 + if (taskHandle != NULL) + { + vTaskDelete(taskHandle); + } +} + +``` + +#### 2. 自我删除 + +```c +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "main"; + +// 任务函数 +void myTask(void *pvParameters) +{ + vTaskDelay(1000 / portTICK_PERIOD_MS); + ESP_LOGI(TAG, "myTask:1"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + ESP_LOGI(TAG, "myTask:2"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + ESP_LOGI(TAG, "myTask:3"); + + // 删除任务(如果传递 NULL,则删除当前任务) + vTaskDelete(NULL); +} + +void app_main(void) +{ + // 任务句柄 + TaskHandle_t taskHandle = NULL; + // 创建一个 FreeRTOS 任务 + xTaskCreate(myTask, "myTask", 2048, NULL, 1, &taskHandle); +} + +``` -## FreeRTOS任务优先级 +## 三、任务创建时传递参数 -文档:https://www.freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/02-Kernel-features/01-Tasks-and-co-routines/03-Task-priorities +### 2.1 说明: -每个任务均被分配了从 0 到 ( configMAX_PRIORITIES - 1 ) 的优先级,其中 configMAX_PRIORITIES 定义为 FreeRTOSConfig.h。 +在 FreeRTOS 中,任务函数的参数通过创建任务时的 pvParameters 指针传递。pvParameters 是一个 void * 类型的指针,可以传递任意类型的数据(整型、数组、结构体或字符串等)。任务接收到参数后,需要将其强制类型转换为对应的数据类型,以便正确使用。 + +![](attachments/20250110155751.png) +### 2.3.2 示例: + +示例为传递四种参数,分别是整型参数,数组参数,结构体参数,字符串参数 + +```c +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "main"; + +typedef struct +{ + int Int; + int Array[3]; +} MyStruct; + +// 任务函数1:接收整型参数 +void Task_1(void *pvParameters) +{ + int *pInt = (int *)pvParameters; + ESP_LOGI(TAG, "取得整型参数为:%d", *pInt); + vTaskDelete(NULL); +} + +// 任务函数2:接收数组参数 +void Task_2(void *pvParameters) +{ + int *pArray = (int *)pvParameters; + ESP_LOGI(TAG, "取得数组参数为:%d %d %d", *pArray, *(pArray + 1), *(pArray + 2)); + vTaskDelete(NULL); +} + +// 任务函数3:接收结构体参数 +void Task_3(void *pvParameters) +{ + MyStruct *pStruct = (MyStruct *)pvParameters; + ESP_LOGI(TAG, "取得结构体参数为:%d %d %d %d", pStruct->Int, pStruct->Array[0], pStruct->Array[1], pStruct->Array[2]); + vTaskDelete(NULL); +} + +// 任务函数4:接收字符串参数 +void Task_4(void *pvParameters) +{ + char *pChar = (char *)pvParameters; + ESP_LOGI(TAG, "取得字符串参数为:%s", pChar); + vTaskDelete(NULL); +} + +int Parameters_1 = 1; +int Parameters_2[3] = {1, 2, 3}; +MyStruct Parameters_3 = {1, {1, 2, 3}}; +static const char *Parameters_4 = "Hello World!"; + +void app_main(void) +{ + // 传递整形参数 + xTaskCreate(Task_1, "Task_1", 2048, (void *)&Parameters_1, 1, NULL); + + // 传递数组参数 + xTaskCreate(Task_2, "Task_2", 2048, (void *)&Parameters_2, 1, NULL); + + // 传递结构体参数 + xTaskCreate(Task_3, "Task_3", 3048, (void *)&Parameters_3, 1, NULL); + + // 传递字符串参数(注意这里没有取地址符号&) + xTaskCreate(Task_4, "Task_4", 3048, (void *)Parameters_4, 1, NULL); +} +``` + + +## 四、FreeRTOS任务优先级 + +FreeRTOS 中每个任务都有一个优先级,优先级决定了任务的执行顺序。优先级数值较大的任务具有更高的优先级,会在低优先级任务之前被调度执行。当多个任务具有相同优先级时,调度器会使用时间片轮转机制在这些任务之间分配 CPU 时间。 + +关于任务优先级可参考文档:[任务优先级](https://www.freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/02-Kernel-features/01-Tasks-and-co-routines/03-Task-priorities),文档中提到: + +>每个任务均被分配了从 0 到 ( configMAX_PRIORITIES - 1 ) 的优先级,其中 configMAX_PRIORITIES 定义为 FreeRTOSConfig.h。 在ESP-IDF中configMAX_PRIORITIES的值为25,所以任务优先级为0-24. -使用代码 +如果我们创建任务时设定优先级为25 + ```c xTaskCreate(Task_1, "Task_1", 2048, NULL, 25, &taskHandle); ``` -产生报错如下: +IDF会产生报错如下: ![](attachments/Pasted%20image%2020250106200143.png) -获取任务优先级的函数: +### 4.1 API说明: + +任务优先级涉及以下两个API: + +| 函数名 | 功能 | 备注 | +| ------------------- | ---------- | ---------- | +| `uxTaskPriorityGet` | 获取任务的当前优先级 | 返回任务的优先级 | +| `vTaskPrioritySet` | 设置任务的优先级 | 设置指定任务的优先级 | + +#### 1. uxTaskPriorityGet:获取任务的优先级 + +该函数用于获取指定任务的当前优先级。如果任务句柄为 `NULL`,则返回当前任务的优先级。 +**原型:** ```c -UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ); +UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask ); ``` -修改优先级别的函数: +**参数说明:** +- xTask:任务句柄。如果为 NULL,则返回当前任务的优先级。 + +**返回值**:任务的优先级。 + +#### 2. vTaskPrioritySet:设置任务的优先级 + +该函数用于设置指定任务的优先级。如果任务句柄为 NULL,则设置当前任务的优先级. + +**原型:** + ```c -void vTaskPrioritySet( TaskHandle_t xTask, - UBaseType_t uxNewPriority ); +void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxPriority ); ``` -参数: -- xTask - 正在设置优先级的任务的句柄。空句柄会设置调用任务的优先级。 +**参数说明:** -- uxNewPriority - 将要设置任务的优先级。应断言优先级低于 configMAX_PRIORITIES。 如果 configASSERT未定义,则优先级默认上限为 (configMAX_PRIORITIES - 1)。 +- xTask:任务句柄。如果为 NULL,则设置当前任务的优先级。 +- uxPriority:要设置的优先级值。 -## FreeRTOS任务挂起 +### 4.2 示例代码 -FreeRTOS任务状态:https://www.freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/02-Kernel-features/01-Tasks-and-co-routines/02-Task-states +```c +void app_main(void) +{ + UBaseType_t taskPriority_1 = 0; + UBaseType_t taskPriority_2 = 0; + TaskHandle_t taskHandle_1 = NULL; + TaskHandle_t taskHandle_2 = NULL; + + xTaskCreate(Task_1, "Task_1", 2048, NULL, 12, &taskHandle_1); + taskPriority_1 = uxTaskPriorityGet(taskHandle_1); + ESP_LOGI(TAG, "Task_1 优先级:%d", taskPriority_1); -![](attachments/Pasted%20image%2020250106202323.png) + xTaskCreate(Task_2, "Task_1", 2048, NULL, 12, &taskHandle_2); + taskPriority_2 = uxTaskPriorityGet(taskHandle_2); + ESP_LOGI(TAG, "Task_1 优先级:%d", taskPriority_2); +} +``` + +## 五、FreeRTOS任务挂起 + +FreeRTOS 任务挂起是指暂停任务的执行,直到通过显式恢复操作再次启动任务。挂起操作不会影响任务所占用的资源,仅是暂停任务调度。 + +![](attachments/20250106204735.png) + +任务可以存在于以下状态中: - **运行** 当任务实际执行时,它被称为处于运行状态。任务当前正在使用处理器。 如果运行 RTOS 的处理器只有一个内核, 那么在任何给定时间内都只能有一个任务处于运行状态。 @@ -97,10 +410,200 @@ FreeRTOS任务状态:https://www.freertos.org/zh-cn-cmn-s/Documentation/02-Ker - **挂起** 与“阻塞”状态下的任务一样, “挂起”状态下的任务不能 被选择进入运行状态,但处于挂起状态的任务 没有超时。相反,任务只有在分别通过 vTaskSuspend() 和 xTaskResume() API 调用明确命令时 才会进入或退出挂起状态。 -相关函数: +### 5.1 API说明: + +任务挂起涉及以下两个API: + +| 函数名 | 功能 | 备注 | +| -------------- | ---- | --------- | +| `vTaskSuspend` | 挂起任务 | 将指定任务挂起 | +| `xTaskResume` | 恢复任务 | 恢复指定任务的执行 | + +#### 1. vTaskSuspend:挂起任务 + +`vTaskSuspend()` 用于挂起指定任务,任务被挂起后无法再执行,直到通过 `xTaskResume()` 恢复任务。 + +**原型:** + +```c +void vTaskSuspend(TaskHandle_t xTask); +``` + +**参数说明:** + +- xTask:要挂起的任务句柄。如果传递 NULL,则挂起当前任务。 + +#### 2. xTaskResume:恢复任务 + +用于恢复一个挂起的任务。恢复任务后,任务重新进入准备就绪状态,等待调度器调度。 + +**原型:** + +```c +BaseType_t xTaskResume(TaskHandle_t xTask); +``` + +**参数说明:** +- xTask:要恢复的任务句柄。如果传递 NULL,则恢复当前任务。 + +### 2.5.2 示例代码: + +```c +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" + +static const char *TAG = "main"; + +TaskHandle_t taskHandle_1 = NULL; + +// 任务1:定时打印日志 +void Task_1(void *pvParameters) +{ + while (1) + { + ESP_LOGI(TAG, "任务1正在运行..."); + vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1秒 + } +} + +// 任务2:控制任务1的挂起与恢复 +void Task_2(void *pvParameters) +{ + while (1) + { + ESP_LOGI(TAG, "挂起任务1..."); + vTaskSuspend(taskHandle_1); // 挂起任务1 + vTaskDelay(pdMS_TO_TICKS(5000)); // 延迟5秒 + + ESP_LOGI(TAG, "恢复任务1..."); + vTaskResume(taskHandle_1); // 恢复任务1 + vTaskDelay(pdMS_TO_TICKS(5000)); // 再延迟5秒 + } +} + +void app_main(void) +{ + // 创建任务1 + xTaskCreate(Task_1, "Task_1", 2048, NULL, 5, &taskHandle_1); + // 创建任务2 + xTaskCreate(Task_2, "Task_2", 2048, NULL, 5, NULL); +} +``` + +## 六、 系统任务信息显示 + +FreeRTOS 提供了多种方法来显示和分析任务信息,帮助开发者了解系统运行状况、优化性能以及定位问题。 + +系统任务信息显示主要有以下两个API: + +| 函数名 | 功能 | 备注 | +| ----------------------------- | ----------- | --------- | +| `vTaskList` | 输出任务列表 | 需开启配置才能使用 | +| `uxTaskGetStackHighWaterMark` | 获取任务最小剩余栈空间 | 需开启配置才能使用 | + +### 6.1 API介绍 +#### 2. vTaskList: 输出任务列表 + +可通过 `vTaskList()` 来协助分析操作系统当前 task 状态,以帮助优化内存,帮助定位栈溢出问题,帮助理解和学习操作系统原理相关知识。 + +**原型:** +```c +void vTaskList( char *pcWriteBuffer ); +``` + +**注意:**`configUSE_TRACE_FACILITY`和`configUSE_STATS_FORMATTING_FUNCTIONS`必须在 FreeRTOSConfig.h 中定义为 1,才可使用此函数。 +对于ESP32来说。使用 vTaskList() 前需使能: + +menuconfig -> Component config -> FreeRTOS -> Kernel->`configUSE_TRACE_FACILITY` +menuconfig -> Component config -> FreeRTOS -> Kernel->`configUSE_STATS_FORMATTING_FUNCTIONS` + +如下图: + +![](attachments/20250106231616.png) + + +#### 2. uxTaskGetStackHighWaterMark:获取任务最小剩余栈空间 + +用于获取任务在运行期间的最小剩余栈空间(即栈的高水位标记)。此函数可帮助检测任务是否存在栈溢出的风险。 + +**原型**: + +```c +UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ); +``` + +**参数:** + +- xTask:任务句柄。如果传递 NULL,则返回当前任务的栈高水位标记。 + +**返回值:** 剩余栈空间的字数(单位为字),(但是在ESP-IDF里为字节) +### 示例: + +```c +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "main"; + +void Task_1(void *pvParameters) +{ + for (;;) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + ESP_LOGI(TAG, "Task_1"); + } + vTaskDelete(NULL); +} + +void Task_2(void *pvParameters) +{ + for (;;) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + ESP_LOGI(TAG, "Task_2"); + } + vTaskDelete(NULL); +} + +void app_main(void) +{ + TaskHandle_t taskHandle_1 = NULL; + TaskHandle_t taskHandle_2 = NULL; + + xTaskCreate(Task_1, "Task_1", 2048, NULL, 12, &taskHandle_1); + xTaskCreate(Task_2, "Task_1", 2048, NULL, 12, &taskHandle_2); + + + // 输出任务列表 + static char cBuffer[512]={0}; + vTaskList(cBuffer); + ESP_LOGI(TAG, "任务列表:\n%s", cBuffer); + + while (1) + { + int istack = uxTaskGetStackHighWaterMark(taskHandle_1); + ESP_LOGI(TAG, "Task_1 剩余栈空间:%d", istack); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} + +``` + +效果: + +![](attachments/20250106233327.png) + + +# 参考链接 + +1. https://www.freertos.org/zh-cn-cmn-s +2. https://www.bilibili.com/video/BV1fs4y1G7eu/?spm_id_from=333.999.top_right_bar_window_history.content.click&vd_source=ef5a0ab0106372751602034cdd9ab98e + + -## 任务看门狗定时器 (TWDT) -https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32/api-reference/system/wdts.html -https://www.bilibili.com/video/BV1jF411B7gv/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=ef5a0ab0106372751602034cdd9ab98e \ No newline at end of file diff --git a/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/20250106204735.png b/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/20250106204735.png new file mode 100644 index 0000000..af2b1ee Binary files /dev/null and b/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/20250106204735.png differ diff --git a/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/20250106231616.png b/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/20250106231616.png new file mode 100644 index 0000000..f31d78a Binary files /dev/null and b/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/20250106231616.png differ diff --git a/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/20250106233327.png b/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/20250106233327.png new file mode 100644 index 0000000..c46c0b6 Binary files /dev/null and b/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/20250106233327.png differ diff --git a/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/20250110155751.png b/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/20250110155751.png new file mode 100644 index 0000000..d64030e Binary files /dev/null and b/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/20250110155751.png differ diff --git a/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/Pasted image 20250106202323.png b/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/Pasted image 20250106202323.png deleted file mode 100644 index 6dd300d..0000000 Binary files a/docs/03.FreeRTOS基础/3.2-FreeRTOS多任务与任务管理/attachments/Pasted image 20250106202323.png and /dev/null differ diff --git a/docs/03.FreeRTOS基础/3.3-FreeRTOS任务看门狗/FreeRTOS任务看门狗.md b/docs/03.FreeRTOS基础/3.3-FreeRTOS任务看门狗/FreeRTOS任务看门狗.md index e69de29..b4d65f0 100644 --- a/docs/03.FreeRTOS基础/3.3-FreeRTOS任务看门狗/FreeRTOS任务看门狗.md +++ b/docs/03.FreeRTOS基础/3.3-FreeRTOS任务看门狗/FreeRTOS任务看门狗.md @@ -0,0 +1,4 @@ +## 任务看门狗定时器 (TWDT) + +https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32/api-reference/system/wdts.html +https://www.bilibili.com/video/BV1jF411B7gv/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=ef5a0ab0106372751602034cdd9ab98e \ No newline at end of file