FreeRTOS(1)----动态与静态创建任务,删除任务

一.静态创建任务

1.创建空闲任务

空闲任务:是由启用freertos任务调度器时内核自动生成的,确保系统内至少存在一个任务正在运行。

空闲任务的优先级永远是最低的,确保不会影响到当前系统其他的任务的运行,空闲任务一直处于就绪态,或者运行态两种之一。当系统的其它任务转为“就绪态”,则会“抢占”空闲任务进行执行。

空闲任务在删除任务函数结束之后,会自动释放掉删除任务分配的内存空间。解释:当其他的运行的任务执行到vTaskDelay();这个函数的时候,空闲任务就可以切进来,释放掉删除任务分配的内存空间了。

当存在和空闲任务都为0优先级的任务,空闲任务会礼让,让别的0级任务进行执行。

static StackType_t  IdleTaskStack[configMINIMAL_STACK_SIZE];        /* 空闲任务任务堆栈 */
static StaticTask_t IdleTaskTCB;                                    /* 空闲任务控制块 */


/**
 * @brief       获取空闲任务地任务堆栈和任务控制块内存,因为本例程使用的
                静态内存,因此空闲任务的任务堆栈和任务控制块的内存就应该
                有用户来提供,FreeRTOS提供了接口函数vApplicationGetIdleTaskMemory()
                实现此函数即可。
 * @param       ppxIdleTaskTCBBuffer:任务控制块内存
                ppxIdleTaskStackBuffer:任务堆栈内存
                pulIdleTaskStackSize:任务堆栈大小
 * @retval      无
 */
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, 
                                   StackType_t  **ppxIdleTaskStackBuffer, 
                                   uint32_t     *pulIdleTaskStackSize)
{
    *ppxIdleTaskTCBBuffer   = &IdleTaskTCB;
    *ppxIdleTaskStackBuffer = IdleTaskStack;
    *pulIdleTaskStackSize   = configMINIMAL_STACK_SIZE;
}

2.系统设置

需要在 FreeRTOSconfig.h文件中 

#define configSUPPORT_STATIC_ALLOCATION                 1                       
/* 1: 支持静态申请内存, 默认: 0 */

3.创建任务

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO 1                   /* 任务优先级 */
#define START_STK_SIZE  128                 /* 任务堆栈大小 */
StackType_t StartTaskStack[START_STK_SIZE]; /* 任务堆栈 */
StaticTask_t            StartTaskTCB;       /* 任务控制块 */
TaskHandle_t            StartTask_Handler;  /* 任务句柄 */
void start_task(void *pvParameters);        /* 任务函数 */

任务优先级:在freertos里面优先级有五种0-4,但一般选择都是分组4,总共为2^ 4-1,因为这样不用去考虑子优先级,方便了很多,只需要通过抢占优先级的大小直接判断进行任务的抢占,freertos任务的优先级数值越小,任务的优先级越低。

任务堆栈:分配给当前任务的运行空间,因为是静态分配所以由用户自行设置,每个任务的堆栈都是自行分离的,数据也是隔离的,当新建一个freertos任务的时候,将分配空间的首地址传入到TCB,当任务删除的时候,堆栈也会被空闲任务释放掉。堆栈大小需要根据程序任务的实际情况进行分配,当出现堆栈溢出的时候,会触发堆栈溢出监测机制,系统会进行异常处理,存储相关的信息进行调试。

任务控制块(TCB):里面存放该任务对应的所有信息(堆栈指针,任务名称,任务形参等),任务控制块相当于是任务的身份证。当产生中断的时候,TCB会保存当时中断,寄存器以及指针等各种信息,当中断结束,再次执行任务的时候,会直接从TCB里面存储的信息直接开始执行。

任务句柄:这是一个二级指针,指向的是对应创建的TCB的首地址,如果是操作自己,则传入的可以是NULL。

任务函数:必须是一个无返回值且可以无限循环的函数。

void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           /* 进入临界区 */
    /* 创建任务1 */
    Task1Task_Handler = xTaskCreateStatic((TaskFunction_t   )task1,         /* 任务函数 */
                                          (const char*      )"task1",       /* 任务名称 */
                                          (uint32_t         )TASK1_STK_SIZE,/* 任务堆栈大小 */
                                          (void*            )NULL,          /* 传递给任务函数的参数 */
                                          (UBaseType_t      )TASK1_PRIO,    /* 任务优先级 */
                                          (StackType_t*     )Task1TaskStack,/* 任务堆栈 */
                                          (StaticTask_t*    )&Task1TaskTCB);/* 任务控制块 */
    /* 创建任务2 */
    Task2Task_Handler = xTaskCreateStatic((TaskFunction_t   )task2,         /* 任务函数 */
                                          (const char*      )"task2",       /* 任务名称 */
                                          (uint32_t         )TASK2_STK_SIZE,/* 任务堆栈大小 */
                                          (void*            )NULL,          /* 传递给任务函数的参数 */
                                          (UBaseType_t      )TASK2_PRIO,    /* 任务优先级 */
                                          (StackType_t*     )Task2TaskStack,/* 任务堆栈 */
                                          (StaticTask_t*    )&Task2TaskTCB);/* 任务控制块 */
    /* 创建任务3 */
    Task3Task_Handler = xTaskCreateStatic((TaskFunction_t   )task3,         /* 任务函数 */
                                          (const char*      )"task3",       /* 任务名称 */
                                          (uint32_t         )TASK3_STK_SIZE,/* 任务堆栈大小 */
                                          (void*            )NULL,          /* 传递给任务函数的参数 */
                                          (UBaseType_t      )TASK3_PRIO,    /* 任务优先级 */
                                          (StackType_t*     )Task3TaskStack,/* 任务堆栈 */
                                          (StaticTask_t*    )&Task3TaskTCB);/* 任务控制块 */
    vTaskDelete(StartTask_Handler); /* 删除开始任务 */
    taskEXIT_CRITICAL();            /* 退出临界区 */
}

4.任务调度 

vTaskStartScheduler();

freertos无非是四种状态,运行态,就绪态,阻塞态,挂起态

运行态:正在执行的任务,stm32一次只能执行一个任务,因为只有一个cpu。

就绪态:任务可以被执行,准备好被执行,但当前CPU在执行别的任务,此任务还未被执行。

阻塞态:任务因为延时或者中断,导致进入阻塞状态。

挂起态:暂停当前函数的运行。要调用函数vTaskSuspend()进入挂起状态,调用vTaskResume()进行解挂,转入就绪态。

freertos的两种调度方式:

·抢占式调度:高优先级的任务可以抢占低优先级的任务,被抢占的任务会进入就绪态

·时间片调度:当多个任务的优先级相同的时候,任务调度器会根据系统时钟节拍进行切换任务,每个时间片执行一个任务。当时间片结束时切换任务,但当运行任务的期间,该任务被中断或者延时进入阻塞状态,则会直接执行下一个任务。

5.临界区

必须完整运行,不可以被打断的代码。每次有且仅有一个进程进入到临界区,当任务进入后,别的进程需要等待该进程出临界区才可以继续进入一个,进入临界区的进程必须在有限的时间退出,当进程不可以进入自己的临界区,需要让出CPU,避免出现"忙等"的现象。

二.动态创建任务

1.系统配置

#define configSUPPORT_STATIC_ALLOCATION                 0                       /* 1: 支持静态申请内存, 默认: 0 */
#define configSUPPORT_DYNAMIC_ALLOCATION                1                       /* 1: 支持动态申请内存, 默认: 1 */

区别:因为是动态创建任务,所以堆栈空间是由freertos内核分配堆栈空间的,所以只需要设置任务句柄去指向TCB任务控制块的首地址。

 xTaskCreate((TaskFunction_t )task1,                 /* 任务函数 */
 (const char*    )"task1",               /* 任务名称 */
 (uint16_t       )TASK1_STK_SIZE,        /* 任务堆栈大小 */
 (void*          )NULL,                  /* 传入给任务函数的参数 */
 (UBaseType_t    )TASK1_PRIO,            /* 任务优先级 */
 (TaskHandle_t*  )&Task1Task_Handler);   /* 任务句柄 */

其余配置和静态分配任务一样。

三.删除任务

#define INCLUDE_vTaskDelete                             1                       /* 删除任务 */

删除任务只需要删除对应任务的任务句柄,上文中提到TCB是一个一级指针且分配了内存空间,里面存放了每个任务的指针位置,寄存器等所有信息。任务句柄是二级指针,指向TCB首地址。所以通过任务句柄可以找到对应的任务。

vTaskDelete(task)

当你需要在任务中删除别的任务,直接task == 对应任务的任务句柄,删除自己本身,task == NULL,当执行vTaskDelete(),删除的是任务本身,释放对应的堆栈空间,需要在执行空闲任务中释放。

注意的是:不管删除的任务处于什么状态,删除函数执行后,会直接从队列中消失。

猜你喜欢

转载自blog.csdn.net/weixin_63032791/article/details/130675692