任务的创建和删除本质就是调用FreeRTOS的API函数。
动态创建任务:任务的控制块以及任务的栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配。——由FreeRTOS自动实现分配内存。
静态创建任务:任务的任务控制块以及任务的栈空间所需的内存,需用户分配提供。
BaseType_t xTaskCreate
(
TaskFunction_t pxTaskCode,//执行任务函数的指针
const char* const pcName;//任务名字,最大长度configMAX_TASK_NAME_LEN,默认16
const configsSTACK_DEPTH_TYPE usStackDepth;//任务堆栈大小,注意字为单位
void *const pvParameter; //传递给任务函数的参数
UBaseType_t uxPriority;//任务优先级,范围:0~configMAX_PRIORITIES-1
TaskHandle_t *const pxCreatedTask; //任务句柄,就是任务的控制块
)
//返回值:pdPASS-任务创建成功
//errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:任务创建失败
使用动态创建函数xTaskCreate()创建的任务会立刻进入就绪态,由任务调度器调度运行。
动态创建任务函数内部实现
任务控制块结构体成员介绍
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; //任务栈栈顶,必须为TCB的第一个成员,与任务切换,任务上下文保存以及任务恢复有关
ListItem_t xStateListItem; //任务状态列表项
ListItem_t xEventListItem; //任务事件列表项
UBaseType_t uxPriority; //任务优先级,数值越大,优先级越大
StackType_t *pxStack; //任务栈起始地址
char pxTaskName[configMAX_TASK_NAME_LEN];//任务名字
}tskTCB;
任务栈栈顶,在任务切换时的任务上下文保存、任务恢复息息相关
每个任务都有属于自己的任务控制块
TaskHandle_t xTaskCreateStatic
(
TaskFunction_t pxTaskCode; //指向任务函数的指针
const char * const pcName; //任务函数名
const uint32_t ulStackDepth;//任务堆栈大小,注意字为单位
void *const pvParameters; //传递的任务函数参数
UBaseType_t uxPriority;//任务优先级
StackType_t *const puxStackBuffer;//任务堆栈,一般为数组,由用户分配
StaticTask_t *const pxTaskBuffer;//任务控制块指针,用户分配
)
//返回值NULL:用户没有提供相应的内存,任务创建失败
//其他值:任务句柄,任务创建成功
静态创建内部实现
void vTaskDelete(TaskHandle_t xTaskToDelete)
xTaskToDelete待删除任务的句柄
被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除。
当传入的参数为NULL,则表示删除任务自身(当前正在运行的任务)
空闲任务会负责释放被删除任务中由系统分配的内存(只针对动态创建的任务,vTaskDelete(NULL))
由用户在任务删除前申请的内存,需要由用户在任务被删除前提前释放,否则将导致内存泄露。
内部实现过程
#define configSUPPORT_DYNAMIC_ALLOCATION 1 //支持动态内存申请
#define START_TASK_PRIO 1
#define START_STK_SIZE 128 //定义任务堆栈大小
TaskHandle_t StartTask_Handler;
void start_task(void *pvParameter);
#define LED0_TASK_PRIO 2
#define LED0_STK_SIZE 50 //定义任务堆栈大小
TaskHandle_t LED0Task_Handler;
void led0_task(void *pvParameter);
#define LED1_TASK_PRIO 2
#define LED1_STK_SIZE 50 //定义任务堆栈大小
TaskHandle_t LED1Task_Handler;
void led1_task(void *pvParameter);
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组为4
delay_init();
uart_init(115200);
LED_Init();
//创建开始任务
xTaskCreate((TaskFunction_t)start_task,
(const char*)"start_task",
(uint16_t)START_STK_SIZE,
(void*)NULL,
(UBaseType_t)START_TASK_PRIO,//
(TaskHandler_t *)&StartTask_Handler);
vTaskStartScheduler();
}
void start_task(void *pvParameter)
{
taskENTER_CRITICAL();//进入临界区,关闭中断,避免被中断,切换任务
//创建LED0任务
xTaskCreate((TaskFunction_t)led0_task,
(const char*)"led0_task",
(uint16_t)LED0_STK_SIZE,
(void*)NULL,
(UBaseType_t)LED0_TASK_PRIO,//
(TaskHandler_t *)&LED0Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t)led1_task,
(const char*)"led1_task",
(uint16_t)LED1_STK_SIZE,
(void *)NULL,
(UBaseType_t)LED1_TASK_PRIO,
(TaskHandle_t*)&LED1Task_Handler);
vTaskDelete(StartTask_Handler);
taskEXTI_CRITICAL();
}
void led0_task(void *pvParameter)
{
while(1)
{
LED0=~LED0;
vTaskDelay(500);
}
}
void led1_task(void *pvParameter)
{
while(1)
{
LED1=0;
vTaskDelay(500);
LED1=1;
vTaskDelay(500);
}
}
void task3(void *pvParameters)
{
uint8_t key = 0;
while(1)
{
printf("task3正在运行");
key = key_scan(0);
if(key == KEY0_PRES)
{
if(task1_handler != NULL)
{
printf("删除task1\r\n");
vTaskDelete(task1_handler);
task1_handler = NULL;
}
vTaskDelay(10);
}
}
}
在32位的计算机中,一个字等于四字节。
INCLUDE_uxTaskGetStackHighWaterMark:获取任务堆栈历史剩余最小值,如果大很多,我们可以将任务改小。
portSTACK_GROWTH:栈的生长方向,STM32是高地址向低地址生长的,也就是向下生长的。堆是从低地址向高地址生长(向上生长)
动态创建任务其内部实现
#define tskSTACK_FILL_BYTE ( 0xa5U )
( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
把分配的堆栈内存全部赋值为a5,当值不是a5时,就表示被用了。
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
获取栈顶地址
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
任务优先级最大,赋值为31
#if ( configUSE_MUTEXES == 1 )
{
pxNewTCB->uxBasePriority = uxPriority;
pxNewTCB->uxMutexesHeld = 0;
}
互斥量有优先级继承问题,所以先将基优先级保存下来。
vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
初始化列表项