【RTOS】快速体验FreeRTOS所有常用API(2)任务管理

发布时间:2024年01月15日

二、任务管理

该部分在上份代码基础上修改得来,代码下载链接:

https://wwzr.lanzout.com/iPEbq1l75bri 密码:1ffe

该代码尽量做到最简,不添加多余的、不规范的代码。

内容主要包括三个部分:任务创建任务删除两种delay函数

最终实现效果:通过三种任务创建方式,创建三个任务task1、task2、task3,每个任务在OLED上显示各自的字符串,运行几秒后删除任务。

img

2.1 任务创建(三种方式)

动态内存分配方式创建任务、静态内存分配方式创建任务、带有任务参数方式创建任务。

1)动态内存分配方式创建任务

函数说明:

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 函数指针, 任务函数
                        const char * const pcName, // 任务的名字
                        const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位为word,10表示40字节
                        void * const pvParameters, // 调用任务函数时传入的参数
                        UBaseType_t uxPriority,    // 优先级
                        TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用它来操作这个任务

示例:

// 定义任务句柄
static TaskHandle_t g_xTask1Handle;

/* 动态内存分配方式创建任务 */
xTaskCreate(vTask1, "Task1", 128, NULL, osPriorityNormal, &g_xTask1Handle);

// 任务函数声明
void vTask1(void *pvParameters);

// 任务函数定义
void vTask1(void *pvParameters)
{
    for(;;)
    {
       vTaskDelay(500);
    }
}
2)静态内存分配方式创建任务

函数说明:

TaskHandle_t xTaskCreateStatic ( 
    TaskFunction_t pxTaskCode,   // 函数指针, 任务函数
    const char * const pcName,   // 任务的名字
    const uint32_t ulStackDepth, // 栈大小,单位为word,10表示40字节
    void * const pvParameters,   // 调用任务函数时传入的参数
    UBaseType_t uxPriority,      // 优先级
    StackType_t * const puxStackBuffer, // 静态分配的栈,就是一个buffer
    StaticTask_t * const pxTaskBuffer // 静态分配的任务结构体的指针,用它来操作这个任务
    );

示例:

// 定义任务句柄
static TaskHandle_t g_xTask2Handle;

// 静态内存分配,定义任务栈和任务控制块(TCB)
static StackType_t g_xTask2Stack[128];
static StaticTask_t g_xTask2TCB;

/* 静态内存分配方式创建任务 */
g_xTask2Handle = xTaskCreateStatic(vTask2, "Task2", 128, NULL, osPriorityNormal, g_xTask2Stack, &g_xTask2TCB);

// 任务函数声明
void vTask2(void *pvParameters);

// 任务函数定义
void vTask2(void *pvParameters)
{
    for(;;)
    {
       vTaskDelay(500);
    }
}
3)带有任务参数方式创建任务

示例:

// 定义任务句柄
static TaskHandle_t g_xTask3Handle;

// 定义任务3的输入参数
static char g_Task3Param = 'A'; 

/* 带有任务参数方式创建任务 */
xTaskCreate(vTask3, "Task3", 128, &g_Task3Param, osPriorityNormal, &g_xTask3Handle);

// 任务函数声明
void vTask3(void *pvParameters);

// 任务函数定义
void vTask3(void *pvParameters)
{
    // 将输入参数转换为正确的类型
    char *param = (char *)pvParameters; 
    for(;;)
    {
       vTaskDelay(500);
    }
}

实例:

img

在三个任务中添加用户代码,让OLED分别显示各任务的cnt++。由于三个任务共用了OLED,此处先用全局变量来实现一个简陋的互斥操作,这种操作是不安全的,后续会进行改进。

img

2.2 任务删除

运行数秒后删除任务。

函数说明:

void vTaskDelete( TaskHandle_t xTaskToDelete );

示例:

// 杀自己
vTaskDelete(NULL)
// 杀别人
vTaskDelete(任务句柄)

实例:

在默认任务中,每隔3秒删除一个任务,最终OLED数值全部静止。

img

2.3 两种delay

有两个Delay函数:

  • vTaskDelay 函数是一种相对延时函数,用于让任务在当前位置暂停执行一段时间。
  • vTaskDelayUntil:函数是一种周期性延时函数,用于让任务按照指定的时间间隔执行

这两个延时函数的选择取决于任务的具体需求。

  • vTaskDelay 适用于相对延时
  • vTaskDelayUntil 更适用于周期性执行的任务

函数说明:

/* xTicksToDelay: 需要延时的时钟节拍数。 */
void vTaskDelay( const TickType_t xTicksToDelay ); 


/* 
    pxPreviousWakeTime:任务上次唤醒的时间,需要用指针传递。
    xTimeIncrement:任务的执行周期,以时钟节拍为单位。
*/
BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,const TickType_t xTimeIncrement );

示例:

// 延时500毫秒
vTaskDelay(500);
// 定义变量记录上次唤醒的时间
TickType_t xLastWakeTime;

// 记录上次唤醒的时间
xLastWakeTime = xTaskGetTickCount();

while (1) {
    // 任务的实际工作
    // ...
    
    // 等待下一个周期
    vTaskDelayUntil(&xLastWakeTime, 500);//500个tick中断、500ms
}

实例:

两种函数的使用感受是很明显的,OLED显示有明显区别。

举个例子,下面代码使用vTaskDelay 时,len = OLED_PrintString(0, 0, "Task1:");这行代码,每次执行的时间间隔是**不确定的,总会有个10ms左右的波动。**无法真正做到让 cnt 500ms 自加一次。

使用vTaskDelayUntil时,len = OLED_PrintString(0, 0, "Task1:");这行代码,每次执行的时间间隔是**确定的,这行代码在周期执行,每500ms执行一次。**是真正做到了让 cnt 500ms 自加一次。

img

文章来源:https://blog.csdn.net/weixin_44029896/article/details/135610980
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。