一、裸机与RTOS
我们使用的32板子是裸机,又称前后台系统。裸机有如下缺点:
1、实时性差。只能一步一步执行任务,比如在一个while循环中,要想执行上一个任务,就必须把下面的任务执行完,循环一遍后才能执行这个任务;
2、浪费资源。在程序delay延时的时候会进入空等待,此时CPU不执行其他代码;
3、结构臃肿。实现的功能都放在无限循环中。
但RTOS(Real Time OS),实时操作系统就可以解决这些问题。RTOS有以下特点:
1、分而治之。实现的功能可以划分为多个任务;
2、任务调度。在延时函数时,不会进入空等待,而是让CPU执行其他任务;
3、抢占式。高优先级的任务可以抢占低优先级的任务;
4、任务堆栈。每个任务都有自己的栈空间,用于保存局部变量以及任务的上下文信息。
注意:中断可以打断任意任务;任务可以同等优先级。
二、任务调度
1、任务调度器
使用相关的调度算法来决定当前需要执行的任务
2、调度方式
①抢占式调度:
主要针对优先级不同的任务,每个任务都有一个优先级,优先级高的任务先执行,高优先级的任务可以抢占低优先级的任务;
注意:
高优先级的任务优先执行;
高优先级的任务不停止,低优先级的任务无法执行;
被抢占的任务将进入就绪态
②时间片调度:
主要针对优先级相同的任务,优先级任务相同的时候,任务调度器在每一个系统时钟节拍到的时候切换任务,即每一个任务执行一个滴答定时器中断周期的时间,然后执行别的任务;
注意:
同等优先级任务会轮流执行,时间片流转;
一个时间片大小取决于滴答定时器中断频率;
如果任务被中途打断或阻塞,没有用完的时间片不会再次使用,而是直接跳过。
③协程式调度(了解即可):
一个任务执行完后才能执行另一个任务,且不能被抢占。
三、任务状态
1、FreeRTOS的任务状态
①运行态:即正在执行的任务,STM32中,同一时间只有一个任务处于运行态;
②就绪态:任务已经能够被执行但还未执行;
③阻塞态:任务因延时或等待外部事件发生;
④挂起态:类似暂停,调用vTaskSuspend()进入挂起态,调用vTaskResume()解挂函数解除挂起态。
注意:
①:挂起态解除后会进入就绪态;
②:仅就绪态才可以转变为运行态;
③:其他状态想要运行,必须先转变为就绪态。
2、状态之间的转换
四、状态列表
这四种状态中,除了运行态,其他三种任务状态都有对应的任务状态列表:就绪列表(pxReadyTasksList[x]);阻塞列表(pxDelayedTaskList[x]);挂起列表(xSuspendedTaskList[x])其中x代表任务优先级数值,为0~31。
而任务调度器总是在所有处于就绪列表的任务中,选择优先级最高的任务来执行,如果优先级相同,就使用时间片调度,“同时”执行同一优先级的任务。
五、任务实现
在使用 FreeRTOS 的过程中,我们要使用函数 xTaskCreate()或 xTaskCreateStatic()来创建任 务,这两个函数的第一个参数 pxTaskCode,就是这个任务的任务函数。什么是任务函数?任务函数就是完成本任务工作的函数。
FreeRTOS 官方给出的任务函数模板如下:
void vATaskFunction(void *pvParameters) (1)
{for( ; ; ) (2){--任务应用程序-- (3)vTaskDelay(); (4)}/* 不 能 从 任 务 函 数 中 返 回 或 者 退 出 , 从 任 务 函 数 中 返 回 或 退 出 的 话 就 会 调 用configASSERT(),前提是你定义了 configASSERT()。如果一定要从任务函数中退出的话那一定 要调用函数 vTaskDelete(NULL)来删除此任务。*/vTaskDelete(NULL); (5)
}
(1)、任务函数本质也是函数,所以肯定有任务名什么的,不过这里我们要注意:任务函数 的返回类型一定要为 void 类型,也就是无返回值,而且任务的参数也是 void 指针类型的!任务 函数名可以根据实际情况定义。
(2)、任务的具体执行过程是一个大循环,for(; ; )就代表一个循环,作用和 while(1)一样,笔 者习惯用 while(1)。
(3)、循环里面就是真正的任务代码了,此任务具体要干的活就在这里实现!
(4)、FreeRTOS 的延时函数,此处不一定要用延时函数,其他只要能让 FreeRTOS 发生任务 切换的 API 函数都可以,比如请求信号量、队列等,甚至直接调用任务调度器。只不过最常用 的就是 FreeRTOS 的延时函数。
(5)、任务函数一般不允许跳出循环,如果一定要跳出循环的话在跳出循环以后一定要调用 函数 vTaskDelete(NULL)删除此任务!
(6)、FreeRTOS 的每个任务都有一些属性需要存储,FreeRTOS 把这些属性集合到一起用一个结 构体来表示,这个结构体叫做任务控制块:TCB_t,在使用函数 xTaskCreate()创建任务的时候就 会自动的给每个任务分配一个任务控制块。
六、任务堆栈
FreeRTOS 之所以能正确的恢复一个任务的运行就是因为有任务堆栈在保驾护航,任务调 度器在进行任务切换的时候会将当前任务的现场(CPU 寄存器值等)保存在此任务的任务堆栈中, 等到此任务下次运行的时候就会先用堆栈中保存的值来恢复现场,恢复现场以后任务就会接着 从上次中断的地方开始运行。
xTaskCreate()创建任务(动态方法):任务堆栈就会由函数 xTaskCreate()自动创建;
xTaskCreateStatic()创建任务(静态方法):需自行定义任务堆栈,然 后堆栈首地址作为函数的参数 puxStackBuffer 传递给函数;
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 )
不管是使用函数 xTaskCreate()还是 xTaskCreateStatic()创建任务都需要指定任务堆栈大 小。任务堆栈的数据类型为 StackType_t,StackType_t 本质上是 uint32_t,也就是4字节。