系列文章目录
文章目录
一、任务的创建与删除
1、什么是任务
在FreeRTOS中,任务就是一个函数,例如
void TaskFunction(void * param)
{
//
while (1) //任务函数通常实现为无限循环
{
printf("Task");
}
}
2、创建任务
创建任务有两种方法:动态创建任务、静态创建任务,于是有两个创建任务函数:
1、动态创建任务函数
// 动态创建任务
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, //函数指针,任务函数
const char * const pcName, //任务的名字
const configSTACK_DEPTH_TYPE usStackDepth,//栈大小,单位为 word ;例如10,表示40个字节
void * const pvParameters,//调用任务函数时传入的参数
UBaseType_t uxPriority, //任务的优先级
TaskHandle_t * const pxCreatedTask ); //任务句柄,用这个句柄来操作这个任务
//返回值:创建任务成功返回 pdPASS; 宏定义为 1
// 失败返回 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; 宏定义为 -1
// 失败的原因为内存不足
在这个函数的内部创建了任务结构体 TCB_t类型,TCB表示任务控制块,_t表示结构体。
在使用 xTaskCreate 函数创建任务时,在函数内部使用 malloc 从堆里分配出结构体。
2、静态创建任务函数
静态创建任务函数:xTaskCreateStatic
由图中可看到,在使用这个函数创建任务必须先定义宏 configSUPPORT_STATIC_ALLOCATION 为 1 ,再实现 vApplicationGetIdleTaskMemory 函数。才能创建静态任务。
创建任务函数:xTaskCreateStatic的参数有有 7 个,其中前五个参数的含义与创建动态任务函数的前 5 个参数的含义一样,第 6 个参数为栈,即自己定义的数组。第 7 个参数为TCB结构体。
静态创建任务函数xTaskCreateStatic 的返回值为该任务的句柄,可使用这个句柄来操作此任务
3、创建任务及删除任务
// 动态创建任务
void Task1Function(void * param)
{
while (1)
{
printf("Task1");
}
}
void Task2Function(void * param)
{
int i = 0;
while (1)
{
printf("Task2");
if(i++ == 100) //循环100次删除任务1
vTaskDelete(xHandleTask1); // 往vTaskDelete();里传入任务的句柄,即删除任务
if(i == 200)
{
vTaskDelete(NULL); //传入NULL 表示任务自杀,自杀的任务由空闲任务清理
}
}
}
int main( void )
{
TaskHandle_t xHandleTask1;
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello, world!\r\n");
xTaskCreate(Task1Function, "Task1", 100, NULL, 0, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 0, NULL);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
// 静态创建任务
// 1、需要在 FreeRTOSConfig.h 中进行宏定义 configSUPPORT_STATIC_ALLOCATION 为 1
// 2、需要实现 vApplicationGetIdleTaskMemory 函数
// 1、需要在 FreeRTOSConfig.h 中进行宏定义
#define configSUPPORT_STATIC_ALLOCATION 1 //写在 FreeRTOSConfig.h 文件中
// 2、需要实现 vApplicationGetIdleTaskMemory 函数
StackType_t xTask3Stack[100]; //定义栈,及大小。来存储此任务的数据
StaticTask_t xTask3TCB; //定义 TCB 结构体
StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
* ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
* ppxIdleTaskStackBuffer = xIdleTaskStack;
* pulIdleTaskStackSize = 100;
}
int main( void )
{
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello, world!\r\n");
xTaskCreateStatic(Task3Function, "Task3", 100, NULL, 1, xTask3Stack, &xTask3TCB);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
二、 任务优先级
在FreeRTOS中,优先级的取值范围是:0~(configMAX_PRIORITIES – 1),数值越大优先级越高。高优先级的、可运行的任务,马上就能执行。相同优先级的、可运行的任务,轮流执行。
轮流执行即你执行一会,我执行一会;执行一会为 Tick
三、任务状态
在FreeRTOS中,任务分为 4 中状态:
- 阻塞状态(Blocked) :当任务等待某个事件或信号的时候处于此状态
- 暂停状态(Suspended):当任务被vTaskSuspend()函数禁止运行的时候处于此状态
- 就绪状态(Ready):准备好了,随时可以运行
- 运行状态(Runing):当前正在运行的任务
四、Delay函数
- vTaskDelay:至少等待指定个数的Tick Interrupt才能变为就绪状态
- vTaskDelayUntil:等待到指定的绝对时刻,才能变为就绪状态
void vTaskDelay( const TickType_t xTicksToDelay ); /* xTicksToDelay: 等待多少Tick */
BaseType_t vTaskDelayUntil( TickType_t * const pxPreviousWakeTime,//pxPreviousWakeTime: 上一次被唤醒的时间
const TickType_t xTimeIncrement );//xTimeIncrement: 要阻塞到(pxPreviousWakeTime + xTimeIncrement),单位都是Tick Count
vTaskDelay( N ); //表示上图,函数运行时间不时间一致,延时时间一致。为 N 个Tick
vTaskDelay( pdMS_TO_TICKS( 50UL ) ); // 调用 pdMS_TO_TICKS(); 函数,将 Tick单位 改为 ms 单位,,,延时 50ms
TickType_t tStart = xTaskGetTickCount(); // 记录任务的启动时间
vTaskDelayUntil(&tStart , 20); //上图 t1-t2 = t2-t1
五、空闲函数及钩子函数
空闲任务的作用是:释放被删除的任务的内存。
在使用 vTaskStartScheduler() 函数来创建、启动调度器时,这个函数内部会创建空闲任务:空闲任务的优先级为 0 ,它不能阻碍用户任务的运行。
使用 vTaskDelete() 来删除任务时,要确保空闲任务有机会执行,否则就无法释放被删除任务的内存。
可以添加一个空闲任务的钩子函数,空闲任务的循环没执行一次,就会调用一次钩子函数。钩子函数不能导致空闲任务进入阻塞状态、暂停状态。
钩子函数
1、可以执行一些低优先级的、后台的、需要连续执行的函数;
2、 可以测量系统的空闲时间:空闲任务能被执行就意味着所有的高优先级任务都停止了,所以测量空闲任务占据的时间,就可以算出处理器占用率;
3、可以让系统进入省电模式:空闲任务能被执行就意味着没有重要的事情要做,当然可以进入省电模式了。
vApplicationIdleHook(); 钩子函数
使用之前需要在 FreeRTOSConfig.h 中将宏定义 configUSE_IDLE_HOOK 的宏改为 1 ;然后就可以实现这个函数了。
六、任务调度
本专栏内容均是学习韦东山老师讲课,学习笔记方便以后复习复盘。
附上韦东山老师百问网官网:https://www.100ask.net/index