文章作者:二土电子
关注文末公众号获取其他资料和工程文件!
期待大家一起学习交流!
这四种任务状态是可以互相转换的。任务创建完成之后,处于就绪态。如果任务调度器调度了处于就绪态的任务,此时任务状态变成了就绪态。当然,运行态也可以变成就绪态。比如此时A任务正在运行,此时有一个比A任务优先级高的B任务处于就绪态。这时任务调度器会将A任务的CPU使用权转交给B任务,此时A任务从运行态转入就绪态。
由就绪态到挂起态,可以直接通过函数vTaskSuspend()实现。运行态和阻塞态也可以调用函数vTaskSuspend()进入挂起态。解挂时,调用vTaskResume()函数即可。
如果处于运行态的任务调用了一些可以阻塞的API函数,比如等待状态量,等待消息队列等,处于运行态的任务就会转到阻塞态。
任务优先级高的任务,会先被执行。在FreeRTOS中,任务优先级可选范围在0~configMAX_PRIORITIER - 1,同一优先级下,可以有多个任务。值得注意的是,在FreeRTOS中,数字越大,优先级越大。
FreeRTOS官方给出了一个任务函数的模板
void vATaskFunction(void *pvParameters)
{
for( ; ; ) // 死循环,可以用while(1)
{
// 任务应用程序
vTaskDelay(); // 引起任务调度
}
}
一般情况下,我们不会从任务中跳出。如果需要跳出,一定要将任务删除。删除任务可以调用下面的函数
vTaskDelete(NULL); // 删除任务
需要注意的是,FreeRTOS的任务函数必须是void类型。
FreeRTOS 把任务属性集合到一起,用一个结构体来表示,这个结构体就是任务控制块——TCB_t(Task Control Block)。所以总结来说,任务控制块就是描述任务属性的结构体。任务控制块可以在“task.c”文件中找到。
上面介绍过,当正在执行任务A,需要切换到任务B时,会将当前任务的现场(CPU寄存器值等)保存到任务的任务堆栈中。等到任务A再次运行时,根据从任务A的任务堆栈中存储的值来恢复现场。
创建任务的时候需要给任务指定堆栈。如果使用函数 xTaskCreate()创建任务(动态创建),任务堆栈就会由函数 xTaskCreate()自动创建。如果使用函数 xTaskCreateStatic()创建任务(静态方法),需要程序员自行定义任务堆栈,然后堆栈首地址作为函数的参数 puxStackBuffer 传递给函数。
不管是使用函数xTaskCreatel()还是xTaskCreateStatic0创建任务,都需要指定任务堆栈大小。任务堆栈的数据类型为 StackType_t, Stackype_t 本质上是 uint32 t,在 portmacro.h 中有定义
#define portSTACK_TYPE uint32_t
typedef portSTACK_TYPE StackType_t;
由此可见,任务堆栈StackType_t的变量类型是u32,无符号整型。
比如之前在移植测试程序中创建LED程序时,定义任务堆栈
//任务堆栈大小
#define LED0_STK_SIZE 50
这里的“50”并不是指该任务的任务堆栈大小为50个字节。而是指该任务的任务堆栈大小是50个StackType_t。一个StackType_t是四个字节。因此,该任务的任务堆栈大小应该是4 * 50 = 200 字节。
任务堆栈需要根据任务的实际大小分配,如果任务所需要的内存比较大,需要调整任务堆栈大小。如果出现任务卡死不运行的情况,大多数情况是因为任务堆栈分配太小。
但是如果任务卡住,将其堆栈增大到很大,依旧卡住,那应该是其他问题。