ESP32上的FreeRTOS学习
ESP32上的FreeRTOS学习(1)
任务基本概念
在任何时刻,只有一个任务得到运行,FreeRTOS调度器决定运行哪个任务。调度器会通过不断的启动、停止每一个任务,从而在宏观上看起来所有的任务都在同时执行。
每个FreeRTOS任务都需要有自己的栈空间。当任务切换时,它的执行环境会被保存到该任务的栈空间中,这样当任务再次运行时,就能从栈中正确的恢复上次的运行环境。
任务状态
FreeRTOS任务分为四种状态:
运行 (Running)
当一个任务正在运行时,那么就说这个任务处于运行态,处于运行态的任务就是当前正在使用处理器的任务。如果使用的是单核处理器的话那么不管在任何时刻永远都只有一个任务处于运行态。就绪 (Ready)
该任务在就绪列表中, 就绪的任务已经具备执行的能力,只等待调度器进行调度,新创建的任务会初始化为就绪态。阻塞 (Blocked)
如果当前任务正在等待某个时序或外部中断,我们就说这个任务处于阻塞状态,该任务不在就绪列表中。包含任务被挂起、任务被延时、任务正在等待信号量、读写队列或者等待读写事件等。挂起 (Suspended)
当任务有较长的时间不允许运行的时候,我们可以挂起任务,这样子调度器就不会管这个任务的任何信息,直到我们调用恢复任务的 API 函数。而任务处于阻塞态的时候,系统还需要判断阻塞态的任务是否超时,是否可以解除阻塞。
任务相关函数
- xTaskCreate()
此函数用来创建一个任务,任务所需的 RAM 会自动的从 FreeRTOS 的堆中分配,因此必须提供内存管理文件,默认我们使用heap_4.c 这个内存管理文件,而且宏 configSUPPORT_DYNAMIC_ALLOCATION 必须为 1。1
2
3
4
5
6
7// 创建任务
xTaskCreate(TaskFunction_t pvTaskCode, // 任务函数
const char *pcName, // 任务名称
uint32_t usStackDepth, // 任务堆栈大小
void *pvParameters, // 传给任务参数
UBaseType_t uxPriority, // 优先级
TaskHandle_t *pvCreatedTask); // 任务句柄
返回值
- pdPASS: 任务创建成功
- errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY: 任务创建失败
2.xTaskCreateStatic()
使用此函数创建的任务所需的RAM由用户进行分配,函数返回值就是任务句柄。
1 | xTaskCreateStatic(TaskFunction_t pvTaskCode, // 任务函数 |
3.vTaskDelete()
任务被删除以后就不能再使用此任务的句柄。如果此任务是 xTaskCreate() 创建的,那么在此任务被删除以后,此任务之前申请的堆栈和控制块内存会在空闲任务中被释放掉,因此当调用函数 vTaskDelete() 删除任务以后必须给空闲任务一定的运行时间。用户分配给任务的内存则需要用户自行释放掉。
1 | vTaskDelete(TaskHandle_t xTaskToDelete); // 任务句柄 |
4.xTaskCreatePinnedToCore()
该函数和 xTaskCreate() 大致相同,只是多了最后一个参数选择任务分配的核心。ESP32是双核的,包含了Protocal_CPU(CPU0)和Application_CPU(CPU1)。这两个核心实际上是相同的,共享相同的内存,并且允许任务在两个核心之间交替运行。
xTaskCreatePinnedToCore(TaskFunction_t pvTaskCode, // 任务函数
const char *pcName, // 任务名称
uint32_t usStackDepth, // 任务堆栈大小
void *pvParameters, // 传给任务参数
UBaseType_t uxPriority, // 优先级
TaskHandle_t *pvCreatedTask,// 任务句柄
BaseType_t xCoreID); // 分配核心
ESP32主函数任务
ESP32主函数也是一个任务,任务名称是loopBack,优先级是1,运行在核心1上。xTaskCreate() 函数创建的任务是由CPU决定分配到哪的,所以该任务有可能分配到核心0也可能分配到核心1。但是ESP32的两个核心是完全独立的。所以建议将任务的初始化函数放到相应的任务函数里,而不是放到主函数。