FreeRTOS笔记【一】 任务的创建(动态方法和静态方法)

一、任务创建和删除API函数

函数 描述
xTaskCreate() 使用动态的方法创建一个任务
xTaskCreateStatic() 使用静态的方法创建一个任务
xTaskCreateRestricted() 创建一个使用MPU进行限制的任务,相关内存使用动态内存分配
vTaskDelete() 删除一个任务

二、动态创建任务

2.1 宏定义

使用 xTaskCreate()  函数是在 FreeRTOS 中创建任务的一种方法,使用该函数所需的 RAM 会自动从 FreeRTOS的堆中自动分配。因此需要开启 FreeRTOSConfig.h 中的一个宏定义为1,就可以创建任务了。

#define configSUPPORT_DYNAMIC_ALLOCATION        1     //支持动态内存申请

2.2 函数原型

xTaskCreate() 函数原型如下:

BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
                        const char * const pcName,
                        const uint16_t usStackDepth,
                        void * const pvParameters,
                        UBaseType_t uxPriority,
                        TaskHandle_t * const pxCreatedTask )
参数 描述
pxTaskCode 任务函数
pcName 任务名字,一般用于追踪和调试,任务名字长度不能超过configMAX_TASK_NAME_LEN
usStackDepth 任务堆栈大小,注意实际申请到的堆栈是usStackDepth的4倍。其中空闲任务的任务堆栈大小为configMINIMAL_STACK_SIZE
pvParameters 传递给任务函数的参数
uxPriotiry 任务优先级,范围0~ configMAX_PRIORITIES-1
pxCreatedTask 任务句柄,任务创建成功以后会返回此任务的任务句柄,这个句柄其实就是任务的任务堆栈。此参数就用来保存这个任务句柄。其他API函数可能会使用到这个句柄。
返回值 描述
pdPASS 任务创建成功。
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY 任务创建失败,因为堆内存不足! 

2.3 示例代码

TaskHandle_t Task1_Handler;         //任务句柄
void task_1(void *pvParameters);    //任务函数

TaskHandle_t Task2_Handler;         //任务句柄
void task_2(void *pvParameters);    //任务函数

int main(void)
{
    HAL_Init();                     //初始化HAL库   
    Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz
	delay_init(180);                //初始化延时函数
    LED_Init();                     //初始化LED 
    KEY_Init();
    uart_init(115200);              //初始化串口
    //创建task1任务
    xTaskCreate((TaskFunction_t )task_1,     	  //任务函数
                (const char*    )"task_1",   	  //任务名称
                (uint16_t       )50,              //任务堆栈大小
                (void*          )NULL,		      //传递给任务函数的参数
                (UBaseType_t    )2,	              //任务优先级
                (TaskHandle_t*  )&Task1_Handler); //任务句柄        
    //创建task2任务
    xTaskCreate((TaskFunction_t )task_2,     
                (const char*    )"task_2",   
                (uint16_t       )50, 
                (void*          )NULL,
                (UBaseType_t    )2,
                (TaskHandle_t*  )&Task2_Handler);
    vTaskStartScheduler();          //开启任务调度
}

//task_1任务函数 
void task_1(void *pvParameters)
{
    while(1)
        printf("1");
}   

//task_2任务函数
void task_2(void *pvParameters)
{
    while(1)
        printf("2");
}

 效果:

 

对于 FreeRTOS 来说,同等优先级的任务会在时间片反复切换。在上述代码中,task1 和 task2 的优先级都是2,所以每一个时间片上就会切换一次任务,导致这样的效果。

  2.4 FreeRTOS 内部实现

在图上三个是我们需要编写的,下三步是 FreeRTOS 替我们完成的。

2.5 TCB 任务控制块

本篇暂时不解析其他的流程,我们来详解 TCP 任务控制块。他是一个结构体,可以看成是任务的身份证,每一个任务都有自己任务控制块,结构体成员保存了任务的特征,任务、优先级、任务状态等。

typedef struct tskTaskControlBlock             
    {
        // 这里栈顶指针必须位于TCB第一项是为了便于上下文切换操作,详见xPortPendSVHandler中任务切换的操作。
        volatile StackType_t    *pxTopOfStack;    
        // 表示任务状态,不同的状态会挂接在不同的状态链表下
        ListItem_t            xStateListItem;    
        // 事件链表项,会挂接到不同事件链表下
        ListItem_t            xEventListItem;        
        // 任务优先级,数值越大优先级越高
        UBaseType_t            uxPriority;            
        // 指向堆栈起始位置,这只是单纯的一个分配空间的地址,可以用来检测堆栈是否溢出
        StackType_t            *pxStack;            
        // 任务名
        char                pcTaskName[ configMAX_TASK_NAME_LEN ];
        //...省略一些条件编译的成员
} tskTCB;

三、静态创建任务函数

3.1 宏定义

使用函数 xTaskCreateStatic() 来创建任务,也就是静态方法,任务的堆栈、任务控制块就需要由用户来指定了。使用静态方法创建任务的时候需要将宏configSUPPORT_STATIC_ALLOCATION设置为1,在文件FreeRTOSConfig.h中设置。

#define configSUPPORT_STATIC_ALLOCATION 1 //静态内存 

3.2 函数原型

xTaskCreateStatic() 函数原型如下:

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 ) 
参数 描述
pxTaskCode 任务函数
pcName 任务名字,一般用于追踪和调试,任务名字长度不能超过
usStackDepth 任务堆栈大小,由于本函数是静态方法创建任务,所以任务堆栈由用户给出,一般是个数组,此参数就是这个数组的大小
pvParameters 传递给任务函数的参数
uxPriotiry 任务优先级,范围0~ configMAX_PRIORITIES-1
puxStackBuffer 任务堆栈,一般为数组,数组类型要为StackType_t类型
pxTaskBuffer 任务控制块
返回值 描述
NULL 任务创建失败,puxStackBuffer或pxTaskBuffer为NULL的时候会导致这个错误的发生
其他值 任务创建成功,返回任务的任务句柄。

对比 xTaskCreate() 来说, xTaskCreateStatic() 需要多自定添加 任务堆栈 (puxStackBuffer) 任务控制块 (pxTaskBuffer),也就是说在在静态任务中 TCB 结构体是需要自己定义的。而且在 xTaskCreateStatic() 中,任务句柄并不是作为参数填入,而是返回值。

3.3 定义两个接口函数

开启静态内存的同时需要实现两个函数:(使用静态内存分配任务堆栈和任务控制块内存)

vApplicationGetIdleTaskMemory():空闲任务堆栈函数。实现该函数是为了给内核提供空闲任务关于空闲任务控制块和空闲任务堆栈的相关信息。

vApplicationGetTimerTaskMemory():定时器任务堆栈函数。实现该函数是为了给内核创建定时器任务时提供定时器任务控制块和定时器任务堆栈的相关信息。

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

//获取定时器服务任务的任务堆栈和任务控制块内存
//ppxTimerTaskTCBBuffer:任务控制块内存
//ppxTimerTaskStackBuffer:任务堆栈内存
//pulTimerTaskStackSize:任务堆栈大小
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, 
									StackType_t **ppxTimerTaskStackBuffer, 
									uint32_t *pulTimerTaskStackSize)
{
	*ppxTimerTaskTCBBuffer=&TimerTaskTCB;
	*ppxTimerTaskStackBuffer=TimerTaskStack;
	*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;
}

3.4 示例代码

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

//定时器服务任务堆栈
static StackType_t TimerTaskStack[configTIMER_TASK_STACK_DEPTH];
//定时器服务任务控制块
static StaticTask_t TimerTaskTCB;

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

//获取定时器服务任务的任务堆栈和任务控制块内存
//ppxTimerTaskTCBBuffer:任务控制块内存
//ppxTimerTaskStackBuffer:任务堆栈内存
//pulTimerTaskStackSize:任务堆栈大小
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, 
									StackType_t **ppxTimerTaskStackBuffer, 
									uint32_t *pulTimerTaskStackSize)
{
	*ppxTimerTaskTCBBuffer=&TimerTaskTCB;
	*ppxTimerTaskStackBuffer=TimerTaskStack;
	*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;
}

TaskHandle_t Task1_Handler;         //任务句柄
void task_1(void *pvParameters);
StackType_t Task1TaskStack[128];    //任务堆栈
StaticTask_t Task1TaskTCB;          //任务控制块  

TaskHandle_t Task2_Handler;         //任务句柄
void task_2(void *pvParameters);
StackType_t Task2TaskStack[128];    //任务堆栈
StaticTask_t Task2TaskTCB;          //任务控制块

int main(void)
{
    HAL_Init();                     //初始化HAL库   
    Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz
	delay_init(180);                //初始化延时函数
    LED_Init();                     //初始化LED 
    KEY_Init();
    uart_init(115200);              //初始化串口
    //创建开始任务
     taskENTER_CRITICAL();           //进入临界区
    //创建task1任务
    Task1_Handler = xTaskCreateStatic((TaskFunction_t )task_1,     	
                (const char*    )"task_1",   	
                (uint16_t       )50, 
                (void*          )NULL,				
                (UBaseType_t    )2,	
                (StackType_t*   )Task1TaskStack,	
				(StaticTask_t*  )&Task1TaskTCB);   
    //创建task2任务
    Task2_Handler = xTaskCreateStatic((TaskFunction_t )task_2,     
                (const char*    )"task_2",   
                (uint16_t       )50, 
                (void*          )NULL,
                (UBaseType_t    )2,
                (StackType_t*   )Task2TaskStack,	
				(StaticTask_t*  )&Task2TaskTCB);        
    taskEXIT_CRITICAL();            //退出临界区
                
    vTaskStartScheduler();          //开启任务调度
}


//LED0任务函数 
void task_1(void *pvParameters)
{
    while(1)
    {
        printf("1");
    }
}   

//LED1任务函数
void task_2(void *pvParameters)
{
    while(1)
    {
        printf("2");
    }
}

3.5 实现流程

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>