参考文献:官方资料
FreeRTOS类似于UCOS,关于前面的基础就不所了,直接看如何将系统运用到ESP32中去。
提示:以下是本篇文章正文内容
FreeRTOS最基本的功能就是任务管理:创建和删除任务
函数原型
static inline IRAM_ATTR BaseType_t xTaskCreate(
TaskFunction_t pvTaskCode, // 任务函数
const char * const pcName,//任务名称,没什么用
const uint32_t usStackDepth,//任务堆栈大小
void * const pvParameters,//传递给 任务函数的参数
UBaseType_t uxPriority,//任务优先级
TaskHandle_t * const pvCreatedTask//任务句柄
)
{
return xTaskCreatePinnedToCore( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask, tskNO_AFFINITY );
}
此函数用来创建一个任务,任务需要RAM来保存与任务有关的状态信息(任务控制块),任务也需要一定的RAM来作为任务堆栈。所需的RAM会自动从FreeRTOS的堆中分配。
此函数和xTaskCreate()功能一样,但是需要的RAM需要用户来提供
此函数和xTaskCreate()功能一样,但是此函数要求所使用的MCU有MPU(内存保护单元)
函数原型
void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION;
使用任务句柄删除指定的任务,若删除任务本身传入NULL即可
用于删除创建的任务
注:
1.从RTOS 实时内核管理中移除任务,要删除的任务将从就绪,封锁,挂起,事件列表中移除
2.任务被删除后就不复存在,也不会再进入运行态, 空闲任务负责释放内核分配给已删除任务的内存
3.只有内核为任务分配的内存空间才会在任务被删除后由空闲任务自动回收,任务自己占用的内存或资源需要由应用程序自己显式地释放
eg:创建三个任务,并在串口打印出来
#include
// 任务1
#define TASK1_TASK_PRIO 1 // 任务优先级
#define TASK1_STK_SIZE 1024 // 任务堆栈大小
TaskHandle_t Tasks1_TaskHandle; // 任务句柄
void task1(void *pvParameters); //任务函数
// 任务2
#define TASK2_TASK_PRIO 1 // 任务优先级
#define TASK2_STK_SIZE 1024 // 任务堆栈大小
TaskHandle_t Tasks2_TaskHandle; // 任务句柄
void task2(void *pvParameters); //任务函数
// 任务3
#define TASK3_TASK_PRIO 1 // 任务优先级
#define TASK3_STK_SIZE 1024 // 任务堆栈大小
TaskHandle_t Tasks3_TaskHandle; // 任务句柄
void task3(void *pvParameters); //任务函数
void setup()
{
Serial.begin(115200);
// 创建任务
xTaskCreate(task1, "task1_task",TASK1_STK_SIZE,NULL,TASK1_TASK_PRIO,NULL);
xTaskCreate(task2, "task2_task",TASK2_STK_SIZE,NULL,TASK2_TASK_PRIO,NULL);
xTaskCreate(task3, "task3_task",TASK3_STK_SIZE,NULL,TASK3_TASK_PRIO,NULL);
vTaskStartScheduler(); //启动调度
}
void loop()
{
}
void task1(void *pvParameters)
{
while(true)
{
Serial.println("task1 runing........");
vTaskDelay(100/portTICK_PERIOD_MS); //等待1s
}
}
void task2(void *pvParameters)
{
while(true)
{
Serial.println("task2 runing........");
vTaskDelay(100/portTICK_PERIOD_MS); //等待1s
}
}
void task3(void *pvParameters)
{
while(true)
{
Serial.println("task3 runing........");
vTaskDelay(100/portTICK_PERIOD_MS); //等待1s
}
}
使用xTaskCreate()创建任务之后,该函数的第一个参数就是pxTaskCode,也就是我们定义任务函数的函数名,任务函数就是实现任务工作的函数,模板如下
void vATaskFunction(void *pvParameters)
{
for(;;)
{
--任务应用程序--
vTaskDelay();
}
vTaskDelete(NULL);
}
1.任务函数的本质也是函数,函数名根据情况而定,但是函数的返回类型必须是void类型,函数的参数也必须是viod指针类型
2.任务具体的执行过程是一个大循环,for(;;)或者while(true)都可以
3.在循环里面实现我们真正要做的事情的程序
4.调用vTaskDelay(),任务从运行态进入阻塞态,转而去执行其他任务,这里一定不用使用delay, 一般只要能让任务发送调度就可以,比如信号量,队列,任务调度器都可
5.任务函数一般不允许跳出循环
每个任务都有一些属性要求存储,任务控制块就是实现的该功能的,在使用xTaskCreak()创建任务的时候,会自动的给每个任务分配一个TCB,
函数原型
void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION;
在UCOSIII延时函数有三种模式:相对模式,周期模式和绝对模式,
在FreeRTOS中,vTaskDelay()就是相对延时函数,而vTaskDelayUnitl()就是绝对延时函数。
参数xTicksToDelay 是以心跳周期为单位,每个‘1’代表15ms,延时的时间一般大于0,否则直接使用**任务调度函数portYIELD()**进行任务调度(注:延时函数里面调用了任务调度的函数)
如果看过UCOS源码的都知道,调用了该延时函数,任务就相当于挂起,会被从就绪列表中删除,然后会被添加到时基列表,当延时时间到就会重新添加到就绪列表
实时操作系统的两大列表就是时基列表和就绪列表
有兴趣的大佬可以看这篇UCOS的介绍:UCOSiii源码阅读
函数原型
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION;
延时的绝对时间,应用于按照一定频率运行的任务
该处使用的url网络请求的数据。
简单说一下,xTickCount就是该系统的系统时钟节拍计数器,每个定时器中断中xTickCount就会加一,实现函数如下:
BaseType_t xTaskIncrementTick( void ) PRIVILEGED_FUNCTION;
判断任务延时时间结束与否,也是在这里完成的
函数原型
void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION;
用于将某个任务设置为挂起状态,进入挂起状态后,只有使用vTaskResume和xTaskResumeFromISR才会进入运行态
如果传入参数为NULL,表示挂起任务本身,可以传入其他任务句柄挂起其他任务,xTaskGetHandle()可根据任务名称获取某个任务的任务句柄
函数原型
void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION;
将一个任务从挂起状态恢复到就绪态,只有挂起的任务次才可以使用该函数
函数原型
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION;
用于在中断服务函数中恢复一个任务
提示:这里对文章进行总结: