FreeRTOS 学习:(十一)任务创建和删除 相关的 API 函数

上一篇下一篇
字和字节


任务创建和删除 相关的 API 函数

任务堆栈大小是以 字(word) 为单位

此类 API 函数主要有三个:

API 函数描述
xTaskCreate()动态方式创建任务
xTaskCreateStatic()静态方式创建任务
vTaskDelete()删除任务
  • 动态方式创建任务

    任务的任务控制块(TCB)以及任务栈空间所需的内存,均由 FreeRTOS 自动从 FreeRTOS 管理的堆中分配

    这个自动分配不是说连内存大小都是自动设置的,内存大小仍然要手动输入,只是说这个分配的时机和方式是动态的。

    可以理解为 任务栈的地址是由 FreeRTOS 自动分配的,而栈的 大小是由开发者通过 uxStackDepth 参数手动指定的

  • 静态方式创建任务

    任务的任务控制块(TCB)以及任务栈空间所需的内存,需用户分配提供,在编译时就分配好,通常通过全局数组或静态变量实现。

1)动态创建任务函数,常用(⭐️)

函数 xTaskCreate() 位于 tasks.c 文件中,其输入参数有 6 个,函数具体定义如下(不同版本的代码不太一样),含注释版:

#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )  // 判断是否支持动态内存分配
...
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,  // 任务函数
                        const char * const pcName,   // 任务名称
                        const configSTACK_DEPTH_TYPE uxStackDepth,  // 任务栈大小
                        void * const pvParameters,  // 传递给任务的参数
                        UBaseType_t uxPriority,  // 任务优先级
                        TaskHandle_t * const pxCreatedTask )  // 输出参数:任务句柄

{
    TCB_t * pxNewTCB;  // 任务控制块指针
    BaseType_t xReturn;  // 返回值,用于返回任务创建的结果

    traceENTER_xTaskCreate(pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask);
    // 调试追踪:记录进入 xTaskCreate() 函数时的相关信息

    pxNewTCB = prvCreateTask(pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask);
    // 调用 prvCreateTask() 创建任务,返回任务控制块指针 pxNewTCB

    if( pxNewTCB != NULL )  // 如果任务控制块创建成功
    {
        #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) )
        {
            /* 如果系统支持多核且启用了任务核心亲和性设置
               在此设置任务的亲和性,指定任务在哪个核心上运行 */
            pxNewTCB->uxCoreAffinityMask = configTASK_DEFAULT_CORE_AFFINITY;
        }
        #endif

        prvAddNewTaskToReadyList( pxNewTCB );  // 将新任务添加到就绪任务列表,等待调度
        xReturn = pdPASS;  // 设置返回值为 pdPASS,表示任务创建成功
    }
    else  // 如果任务控制块创建失败
    {
        xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;  // 设置返回值为内存分配失败错误码
    }

    traceRETURN_xTaskCreate( xReturn );  // 调试追踪:记录函数返回时的相关信息

    return xReturn;  // 返回任务创建结果
}
...
#endif

其中主要的工作都是用子函数 prvCreateTask() 实现的,其函数源码为:

static TCB_t * prvCreateTask( TaskFunction_t pxTaskCode,
                               const char * const pcName,
                               const configSTACK_DEPTH_TYPE uxStackDepth,
                               void * const pvParameters,
                               UBaseType_t uxPriority,
                               TaskHandle_t * const pxCreatedTask )
{
    TCB_t * pxNewTCB;  // 任务控制块(TCB)指针,用于存储新创建任务的控制信息

    /* 如果栈是从低地址到高地址生长,首先分配栈,然后分配 TCB。 
       如果栈是从高地址到低地址生长,先分配 TCB,再分配栈空间。 */
    #if ( portSTACK_GROWTH > 0 )  // 判断栈是否从低地址生长(portSTACK_GROWTH > 0 时,栈从低地址生长)
    {
        /* 分配 TCB 内存空间 */
        pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

        if( pxNewTCB != NULL )  // 如果 TCB 内存分配成功
        {
            /* 初始化 TCB 内存为 0 */
            ( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );

            /* 分配栈空间:分配的栈大小为 uxStackDepth 个 StackType_t 类型的空间 */
            pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );

            if( pxNewTCB->pxStack == NULL )  // 如果栈空间分配失败
            {
                /* 如果栈内存分配失败,释放已经分配的 TCB 内存 */
                vPortFree( pxNewTCB );
                pxNewTCB = NULL;  // 设置 TCB 为 NULL,表示创建失败
            }
        }
    }
    #else /* portSTACK_GROWTH < 0 */
    {
        StackType_t * pxStack;

        /* 如果栈是从高地址生长,首先分配栈空间 */
        pxStack = pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) );

        if( pxStack != NULL )  // 如果栈内存分配成功
        {
            /* 分配 TCB 内存空间 */
            pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

            if( pxNewTCB != NULL )  // 如果 TCB 内存分配成功
            {
                /* 初始化 TCB 内存为 0 */
                ( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );

                /* 将栈空间地址保存在 TCB 中 */
                pxNewTCB->pxStack = pxStack;
            }
            else  // 如果 TCB 内存分配失败
            {
                /* 释放之前分配的栈空间 */
                vPortFreeStack( pxStack );
            }
        }
        else  // 如果栈内存分配失败
        {
            pxNewTCB = NULL;  // 设置 TCB 为 NULL,表示任务创建失败
        }
    }
    #endif /* portSTACK_GROWTH */

    if( pxNewTCB != NULL )  // 如果 TCB 和栈都分配成功
    {
        #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
        {
            /* 如果支持静态和动态任务分配,标记任务为动态分配 */
            pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
        }
        #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */

        /* 初始化任务:设置任务的函数、名称、优先级、参数等 */
        prvInitialiseNewTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
    }

    /* 返回新创建的 TCB。如果任务创建失败,返回 NULL */
    return pxNewTCB;
}

想要完全搞懂函数内部的逻辑,还是非常难的,可以先学会使用,再深挖内部细节。

① 输入参数介绍

TaskFunction_t 					pxTaskCode, 	/* 指向任务函数的指针 */
const char * const 				pcName,			/* 任务名字,最大长度configMAX_TASK_NAME_LEN, 默认16 */
const configSTACK_DEPTH_TYPE 	uxStackDepth,	/* 任务堆栈大小,注意字(word)为单位 */
void * const 					pvParameters,	/* 传递给任务函数的参数, 用不到, 一般为NULL */
UBaseType_t 					uxPriority,		/* 任务优先级,范围:0 ~ configMAX_PRIORITIES-1 */
TaskHandle_t * const 			pxCreatedTask	/* 任务句柄,指向任务的任务控制块 */

② 返回值

返回值描述
pdPASS任务创建成功
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY任务创建失败

其实就是 1 和 -1 ,只不过是经过重定义的宏罢了。

pdPASS这个宏的命名以 “pd” 开头,是 FreeRTOS 中一种命名约定的一部分,用于表示 Portable (跨平台) Data Types(可移植数据类型)。

③ 调用步骤与函数体内容解析

调用步骤(此函数创建的任务会立刻进入就绪态,由任务调度器调度运行):

  1. 将宏 configSUPPORT_DYNAMIC_ALLOCATION 配置为 1 ;
  2. 定义函数入口参数;
  3. 编写任务函数。

函数体内部实现过程

  1. 申请 堆栈内存 和 任务控制块内存;
  2. TCB 结构体成员赋值;
  3. 添加新任务到就绪列表中。

2)静态创建任务函数

静态创建任务要比动态创建复杂的多…而且实际应用中用的少,但是也要掌握

函数 xTaskCreateStatic() 位于 tasks.c 文件中,其输入参数有 7 个,函数具体定义如下:

#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
...
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
                               const char * const pcName,
                               const configSTACK_DEPTH_TYPE uxStackDepth,
                               void * const pvParameters,
                               UBaseType_t uxPriority,
                               StackType_t * const puxStackBuffer,
                               StaticTask_t * const pxTaskBuffer )
{
    TaskHandle_t xReturn = NULL;
    TCB_t * pxNewTCB;

    traceENTER_xTaskCreateStatic( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, 		
                                  puxStackBuffer, pxTaskBuffer );

    pxNewTCB = prvCreateStaticTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, 
                                    puxStackBuffer, pxTaskBuffer, &xReturn );

    if( pxNewTCB != NULL )
    {
        #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) )
        {
            /* Set the task's affinity before scheduling it. */
            pxNewTCB->uxCoreAffinityMask = configTASK_DEFAULT_CORE_AFFINITY;
        }
        #endif

        prvAddNewTaskToReadyList( pxNewTCB );
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    traceRETURN_xTaskCreateStatic( xReturn );

    return xReturn;
}
...
#endif

① 输入参数介绍

TaskFunction_t			pxTaskCode,			/* 指向任务函数的指针 */
const char * const		pcName,				/* 任务函数名 */
const uint32_t			ulStackDepth, 		/* 任务堆栈大小注意字(word)为单位 */
void * const			pvParameters, 		/* 传递的任务函数参数,用不到, 一般为NULL*/
UBaseType_t				uxPriority, 		/* 任务优先级 */
StackType_t * const		puxStackBuffer, 	/* 任务堆栈,一般为数组,由用户分配 */
StaticTask_t * const	pxTaskBuffer		/* 任务控制块指针,由用户分配 */

② 返回值

返回值描述
NULL用户没有提供相应的内存,任务创建失败
有效的任务句柄任务创建成功

③ 调用步骤与函数体内容解析

调用步骤(此函数创建的任务会立刻进入就绪态,由任务调度器调度运行):

  1. 需将宏 configSUPPORT_STATIC_ALLOCATION 配置为 1

  2. 定义 “空闲任务&软件定时器任务” 的任务堆栈及 TCB

    后面会解释为什么要有这一步以及第三步

  3. 实现两个接口函数

    • (必要)vApplicationGetIdleTaskMemory() 空闲任务内存赋值(传入第 2 步定义的空闲任务结构体)
    • (可选)vApplicationGetTimerTaskMemory() 软件定时器内存赋值(传入第 2 步定义的定时器任务结构体)
  4. 定义函数入口参数

  5. 编写任务函数

函数体内部实现过程

  1. TCB 结构体成员赋值
  2. 添加新任务到就绪列表中

3)任务删除函数(⭐️)

void vTaskDelete(TaskHandle_t xTaskToDelete)

由于函数体太复杂,这里就不贴出了,输入参数如下,没有返回值:

输入参数描述
xTaskToDelete待删除任务的任务句柄(指针,指向TCB)

【删除的结果】:

  • 用于删除已被创建的任务,被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除。

  • 被删除的任务,其状态会保留最后一刻的状态。

  • 被删除的任务的任务句柄并不会自动变成 NULL ,任务句柄本身不会被直接修改或重置,因此我们需要手动将任务句柄设置为 NULL,以便标识该任务已经被删除。

【注意】:

  1. 当传入的参数为 NULL ,则代表删除任务自身(当前正在运行的任务),立即执行,但内存 不会 立即被释放(任务自杀)。
  2. 在任务 1 的任务函数体中调用此函数,传入任务 2 的任务句柄,就可以删除任务 2 ,也是立即执行,内存 立即被释放。
  3. FreeRTOS中,在任务自杀情况下,空闲任务会自动负责释放自杀任务被系统分配的内存(动态创建的任务),但是由用户在任务删除前申请的内存(静态创建的任务), 则需要由用户在任务被删除前提前释放,否则将导致内存泄露。

① 调用与函数体内容解析

调用:

  1. 使用删除任务函数,需将宏 INCLUDE_vTaskDelete 配置为 1

  2. 入口参数输入需要删除的任务句柄(NULL代表删除本身)。

函数体内部实现过程:

  1. 获取所要删除任务的控制块
    • 通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身。
  2. 将被删除任务,移除所在列表
    • 将该任务在所在列表中移除,包括:就绪、阻塞、挂起、事件等列表。
  3. 判断所需要删除的任务
    • 删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行。
    • 删除其他任务,释放内存,任务数量就减少了。
  4. 更新下个任务的阻塞时间
    • 更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务 。

4)实验示例

示例代码你可以直接写在 main.c 文件中,也可以新建一个 freertos_demo.c 文件,专门用来存放。

4.1)动态创建任务+任务删除

① 实验目的: 学会 xTaskCreate()vTaskDelete() 的使用。

② 实验设计: 将设计四个任务:start_task、task1、task2、task3。

四个任务的功能如下:

  • start_task:用来创建其他的三个任务(执行完之后就要删除,避免重复创建任务);
  • task1:实现 LED0 每 500ms 闪烁一次;
  • task2:实现 LED1 灭 200ms ,再亮 800 ms,循环;
  • task3:判断按键 KEY0 是否按下,按下则删掉 task1 (注意观察是不是立即删除的)。

【注意】这里就不要使用自己定义的延时函数了,要用自带的延时函数,自己的延时函数就是在那里死等得了。

③ main.c 代码:

需要先将 FreeRTOSConfig.h 文件中的相关宏置 1 :

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

main.c 代码:

#include "sys.h" 
#include "delay.h" 
#include "usart.h" 
#include "led.h" 
#include "key.h"
#include "FreeRTOS.h" 
#include "task.h"

// -----------------------------------------------------------------------------------

#define START_TASK_PRIO  1           //任务优先级 
#define START_STK_SIZE   128         //任务堆栈大小  
TaskHandle_t StartTask_Handler;      //任务句柄 
void start_task(void *pvParameters); //任务函数 
 
#define LED0_TASK_PRIO   2           //任务优先级 
#define LED0_STK_SIZE    50          //任务堆栈大小  
TaskHandle_t LED0Task_Handler;       //任务句柄 
void led0_task(void *p_arg);         //任务函数 
 
#define LED1_TASK_PRIO   3           //任务优先级 
#define LED1_STK_SIZE    50          //任务堆栈大小 
TaskHandle_t LED1Task_Handler;       //任务句柄 
void led1_task(void *p_arg);         //任务函数 

#define KEY_TASK_PRIO   4            //任务优先级 
#define KEY_STK_SIZE    50           //任务堆栈大小 
TaskHandle_t KEYTask_Handler;        //任务句柄 
void KEY_task(void *p_arg);          //任务函数 

// -----------------------------------------------------------------------------------

int main(void) 
{ 
    HAL_Init();
    delay_init(72);       //延时函数初始化    
    usart_init(115200);   //初始化串口 
    LED_Init();           //初始化LED 
    KEY_Init();           //按键初始化
    
 
    //创建开始任务 
    xTaskCreate((TaskFunction_t  )start_task,           //任务函数 
                (const char*     )"start_task",         //任务名称 
                (uint16_t        )START_STK_SIZE,       //任务堆栈大小 
                (void*           )NULL,                 //传递给任务函数的参数 
                (UBaseType_t     )START_TASK_PRIO,      //任务优先级 
                (TaskHandle_t*   )&StartTask_Handler);  //任务句柄               
    vTaskStartScheduler();                              //开启任务调度器 
} 

// -----------------------------------------------------------------------------------

//开始任务任务函数
void start_task(void *pvParameters) 
{ 
    //创建LED0任务 
    xTaskCreate((TaskFunction_t  )led0_task,       
                (const char*     )"led0_task",     
                (uint16_t        )LED0_STK_SIZE,  
                (void*           )NULL,     
                (UBaseType_t     )LED0_TASK_PRIO,  
                (TaskHandle_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);         
    //创建KEY任务
    xTaskCreate((TaskFunction_t  )KEY_task,       
                (const char*     )"key_task",     
                (uint16_t        )KEY_STK_SIZE,  
                (void*           )NULL,     
                (UBaseType_t     )KEY_TASK_PRIO,  
                (TaskHandle_t*   )&KEYTask_Handler);
    
    vTaskDelete(StartTask_Handler);   //删除开始任务 
} 
 

//LED0任务函数, LED0每隔500ms翻转一次
void led0_task(void *pvParameters) 
{ 
    while(1) 
    { 
        printf("task1\r\n");
        HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); 
        vTaskDelay(500); 
    } 
}    
 

//LED1任务函数, LED1以不同时间亮灭
void led1_task(void *pvParameters) 
{ 
    while(1) 
    { 
        printf("task2\r\n");
        HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET); 
        vTaskDelay(200); 
        HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET); 
        vTaskDelay(800); 
    } 
} 


//KEY任务函数, 判断按键 KEY0 是否按下,按下则删掉 task1 
void KEY_task(void *pvParameters) 
{ 
    while(1) 
    { 
        printf("task3\r\n");
        if(KEY_Scan(0)==1)
        {
            if (LED0Task_Handler != NULL) 
            {
                vTaskDelete(LED0Task_Handler);
                LED0Task_Handler = NULL;
            }
        }
        vTaskDelay(10);  // 之前没加这行代码, 导致task1和task2就一开始运行了一次, 然后就一直运行task3, LED后面一直是灭的
    } 
} 

④ 实验结果:

动态任务创建实验结果

⑤ 执行流程:

  1. start_task 创建 task1 后,task1 打断 start_task 并立即执行,此时 start_task 一直是就绪态;

  2. 直到 task1 延时进入阻塞(延时时间长),start_task 转为运行态继续创建 task2 ;

  3. start_task 创建 task2 后,task2 打断 start_task 并立即执行,此时 start_task 又处于就绪态;

  4. 直到 task2 延时进入阻塞(延时时间长),此时 task1 仍阻塞,所以 start_task 转为运行态继续创建 task3 ;

  5. start_task 创建 task3 后,task3 打断 start_task 并立即执行,此时 start_task 又处于就绪态;

  6. 直到 task3 延时进入阻塞,此时 task1 和 task2 仍处于阻塞状态, start_task 又处于就绪态;

  7. start_task 执行任务自杀,结束了自己的使命,然后退出临界区;

  8. 下面就是三个任务根据抢占式调度机制执行了。

相信大家看完执行流程也就能知道:为什么先运行的是任务优先级最低的 task1 了。

⑥ 为什么按键再次按下会卡住:

有些同学按键再次按下的时候,好像卡住了,那是因为重复删除任务会触发内存管理报错,替换如下代码就没事了:

if(KEY_Scan(0)==1)
{
    if (LED0Task_Handler != NULL) 
    {
        vTaskDelete(LED0Task_Handler);
        LED0Task_Handler = NULL;
    }
}

⑦ 执行流程的简化:

如果想要等三个任务全部创建好之后,再执行任务调度,那么就在 start_task 的任务函数体的头和尾加上临界区的相关代码:

//开始任务任务函数
void start_task(void *pvParameters) 
{ 
    taskENTER_CRITICAL();           //进入临界区 ----
    
    //创建LED0任务 
    xTaskCreate((TaskFunction_t  )led0_task,       
                (const char*     )"led0_task",     
                (uint16_t        )LED0_STK_SIZE,  
                (void*           )NULL,     
                (UBaseType_t     )LED0_TASK_PRIO,  
                (TaskHandle_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);         
    //创建KEY任务
    xTaskCreate((TaskFunction_t  )KEY_task,       
                (const char*     )"key_task",     
                (uint16_t        )KEY_STK_SIZE,  
                (void*           )NULL,     
                (UBaseType_t     )KEY_TASK_PRIO,  
                (TaskHandle_t*   )&KEYTask_Handler);
    
    vTaskDelete(StartTask_Handler);   //删除开始任务 
                
    taskEXIT_CRITICAL();            //退出临界区 ----
} 

这样的话,那就是等三个任务创建好之后,start_task 自杀,然后退出临界区,三个任务执行抢占式调度,task3 先运行。

4.2)静态创建任务+任务删除

① 实验目的: 学会 xTaskCreateStatic()vTaskDelete() 的使用。

② 实验设计: 将设计四个任务:start_task、task1、task2、task3 。

四个任务的功能如下:

  • start_task:用来创建其他的三个任务(执行完之后就要删除,避免重复创建任务);
  • task1:实现 LED0 每 500ms 闪烁一次;
  • task2:实现 LED1 灭 200ms ,再亮 800 ms,循环;
  • task3:判断按键 KEY0 是否按下,按下则删掉 task1 (注意观察是不是立即删除的)。

【注意】这里就不要使用自己定义的延时函数了,要用自带的延时函数,自己的延时函数就是在那里死等得了。

③ 相关代码:

1、需要先将 FreeRTOSConfig.h 文件中的相关宏置 1

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

2、编写空闲任务内存赋值函数 vApplicationGetIdleTaskMemory() ,这个函数先照着写,后面会详细解释用法:

StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];  // 空闲任务内存分配
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,
                                   StackType_t ** ppxIdleTaskStackBuffer,
                                   uint32_t * pulIdleTaskStackSize)
{
	* ppxIdleTaskTCBBuffer = &idle_task_tcb;
    * ppxIdleTaskStackBuffer = idle_task_stack;
    * pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; // configMINIMAL_STACK_SIZE 被系统定义了
}

有些同学可能会遇到这个错误:

..\User\main.c(36): error:  #147-D: declaration is incompatible with "void vApplicationGetIdleTaskMemory(StaticTask_t **, StackType_t **, uint16_t *)"  (declared at line 2001 of "..\Middlewares\FreeRTOS\include\task.h")

这个错误的意思是:由于版本差异,你定义的 vApplicationGetIdleTaskMemory() 的参数类型与 FreeRTOS 官方声明不一致(尤其是最后一个 uint32_t* 应为 uint16_t*)。

3、编写软件定时器任务内存赋值函数 vApplicationGetTimerTaskMemory() ,这个函数先照着写,后面会详细解释用法:

StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
void vApplicationGetTimerTaskMemory(StaticTask_t ** ppxTimerTaskTCBBuffer,
                                    StackType_t ** ppxTimerTaskStackBuffer,
                                    uint32_t * pulTimerTaskStackSize)
{
	* ppxTimerTaskTCBBuffer = &timer_task_tcb;
	* ppxTimerTaskStackBuffer = timer_task_stack;
    * pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;   
}

4、main.c 代码:

#include "sys.h" 
#include "delay.h" 
#include "usart.h" 
#include "led.h" 
#include "key.h"
#include "FreeRTOS.h" 
#include "task.h"

// -----------------------------------------------------------------------------------

#define START_TASK_PRIO  1           //任务优先级 
#define START_STK_SIZE   128         //任务堆栈大小  
TaskHandle_t StartTask_Handler;      //任务句柄 
StackType_t start_task_stack[START_STK_SIZE]; // 任务堆栈
StaticTask_t start_task_tcb;         //任务控制块
void start_task(void *pvParameters); //任务函数 
 
#define LED0_TASK_PRIO   2           //任务优先级 
#define LED0_STK_SIZE    50          //任务堆栈大小  
TaskHandle_t LED0Task_Handler;       //任务句柄 
StackType_t LED0_task_stack[LED0_STK_SIZE]; // 任务堆栈
StaticTask_t led0_task_tcb;          //任务控制块
void led0_task(void *p_arg);         //任务函数 
 
#define LED1_TASK_PRIO   3           //任务优先级 
#define LED1_STK_SIZE    50          //任务堆栈大小 
TaskHandle_t LED1Task_Handler;       //任务句柄 
StackType_t LED1_task_stack[LED1_STK_SIZE]; // 任务堆栈
StaticTask_t led1_task_tcb;          //任务控制块
void led1_task(void *p_arg);         //任务函数 

#define KEY_TASK_PRIO   4            //任务优先级 
#define KEY_STK_SIZE    50           //任务堆栈大小 
TaskHandle_t KEYTask_Handler;        //任务句柄 
StackType_t KEY_task_stack[KEY_STK_SIZE]; // 任务堆栈
StaticTask_t key_task_tcb;          //任务控制块
void KEY_task(void *p_arg);          //任务函数 

// -----------------------------------------------------------------------------------

// 定义静态方式下, 空闲任务内存分配函数
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];  // 空闲任务内存分配
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,
                                   StackType_t ** ppxIdleTaskStackBuffer,
                                   uint16_t * pulIdleTaskStackSize)
{
    * ppxIdleTaskTCBBuffer = &idle_task_tcb;
    * ppxIdleTaskStackBuffer = idle_task_stack;
    * pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; // configMINIMAL_STACK_SIZE 被系统定义了
}

// 定义静态方式下, 软件定时器任务内存分配函数
StaticTask_t timer_task_tcb;
StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH];
void vApplicationGetTimerTaskMemory(StaticTask_t ** ppxTimerTaskTCBBuffer,
                                    StackType_t ** ppxTimerTaskStackBuffer,
                                    uint16_t * pulTimerTaskStackSize)
{
    * ppxTimerTaskTCBBuffer = &timer_task_tcb;
    * ppxTimerTaskStackBuffer = timer_task_stack;
    * pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;   
}

// -----------------------------------------------------------------------------------

int main(void) 
{ 
    HAL_Init();
    delay_init(72);       //延时函数初始化    
    usart_init(115200);   //初始化串口 
    LED_Init();           //初始化LED 
    KEY_Init();           //按键初始化
    
 
    //创建开始任务 
    StartTask_Handler = xTaskCreateStatic((TaskFunction_t        ) start_task,
                                          (const char *          ) "start_task",
                                          (configSTACK_DEPTH_TYPE) START_STK_SIZE,
                                          (void *                ) NULL,
                                          (UBaseType_t           ) START_TASK_PRIO,
                                          (StackType_t *         ) start_task_stack,
                                          (StaticTask_t *        ) &start_task_tcb );
          
    vTaskStartScheduler();                              //开启任务调度器 
} 

// -----------------------------------------------------------------------------------

//开始任务任务函数
void start_task(void *pvParameters) 
{ 
    taskENTER_CRITICAL();           //进入临界区 
    //创建LED0任务 
    LED0Task_Handler = xTaskCreateStatic((TaskFunction_t        ) led0_task,
                                         (const char *          ) "led0_task",
                                         (configSTACK_DEPTH_TYPE) LED0_STK_SIZE,
                                         (void *                ) NULL,
                                         (UBaseType_t           ) LED0_TASK_PRIO,
                                         (StackType_t *         ) LED0_task_stack,
                                         (StaticTask_t *        ) &led0_task_tcb ); 
    //创建LED1任务 
    LED1Task_Handler = xTaskCreateStatic((TaskFunction_t        ) led1_task,
                                         (const char *          ) "led1_task",
                                         (configSTACK_DEPTH_TYPE) LED1_STK_SIZE,
                                         (void *                ) NULL,
                                         (UBaseType_t           ) LED1_TASK_PRIO,
                                         (StackType_t *         ) LED1_task_stack,
                                         (StaticTask_t *        ) &led1_task_tcb ); 
    //创建KEY任务
    KEYTask_Handler = xTaskCreateStatic((TaskFunction_t        ) KEY_task,
                                        (const char *          ) "key_task",
                                        (configSTACK_DEPTH_TYPE) KEY_STK_SIZE,
                                        (void *                ) NULL,
                                        (UBaseType_t           ) KEY_TASK_PRIO,
                                        (StackType_t *         ) KEY_task_stack,
                                        (StaticTask_t *        ) &key_task_tcb ); 
                      
    vTaskDelete(StartTask_Handler);   //删除开始任务 
                
    taskEXIT_CRITICAL();            //退出临界区 
} 
 

//LED0任务函数, LED0每隔500ms翻转一次
void led0_task(void *pvParameters) 
{ 
    while(1) 
    { 
        printf("task1\r\n");
        HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); 
        vTaskDelay(500); 
    } 
}    
 

//LED1任务函数, LED1以不同时间亮灭
void led1_task(void *pvParameters) 
{ 
    while(1) 
    { 
        printf("task2\r\n");
        HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET); 
        vTaskDelay(200); 
        HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET); 
        vTaskDelay(800); 
    } 
} 


//KEY任务函数, 判断按键 KEY0 是否按下,按下则删掉 task1 
void KEY_task(void *pvParameters) 
{ 
    while(1) 
    { 
        printf("task3\r\n");
        if(KEY_Scan(0)==1)
        {
            if (LED0Task_Handler != NULL) 
            {
                vTaskDelete(LED0Task_Handler);
                LED0Task_Handler = NULL;
            }
        }
        vTaskDelay(10);  // 之前没加这行代码, 导致task1和task2就一开始运行了一次, 然后就一直运行task3, LED后面一直是灭的
    } 
} 

效果和动态的一样。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值