该部分在上份代码基础上修改得来,代码下载链接:
https://wwzr.lanzout.com/iPEbq1l75bri 密码:1ffe
该代码尽量做到最简,不添加多余的、不规范的代码。
内容主要包括三个部分:任务创建、任务删除、两种delay函数
最终实现效果:通过三种任务创建方式,创建三个任务task1、task2、task3,每个任务在OLED上显示各自的字符串,运行几秒后删除任务。
动态内存分配方式创建任务、静态内存分配方式创建任务、带有任务参数方式创建任务。
函数说明:
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);
}
}
函数说明:
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);
}
}
示例:
// 定义任务句柄
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);
}
}
实例:
在三个任务中添加用户代码,让OLED分别显示各任务的cnt++。由于三个任务共用了OLED,此处先用全局变量来实现一个简陋的互斥操作,这种操作是不安全的,后续会进行改进。
运行数秒后删除任务。
函数说明:
void vTaskDelete( TaskHandle_t xTaskToDelete );
示例:
// 杀自己
vTaskDelete(NULL)
// 杀别人
vTaskDelete(任务句柄)
实例:
在默认任务中,每隔3秒删除一个任务,最终OLED数值全部静止。
有两个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 自加一次。