优先级抢占式调度
先上图
任务的四个状态:
运行:在就绪态里优先级最高的任务
阻塞:等待触发条件(时间,中断或其他任务触发),触发后进入就绪态
就绪:就绪后,由优先级决定是否运行
挂起:在运行,阻塞和就绪态时都可以将任务挂起,挂起后系统不会去管这些任务
系统永远让最高优先级运行,当不止一个任务是最高优先级时,系统让这些任务轮流执行,执行的时间都一样,而这个时间的确定是由configTICK_RATE_HZ设置
例如
configTICK_RATE_HZ = 10Hz; //那么TICK就是100ms
TICK周期的精度依赖于系统的时钟频率
空闲任务
当所有任务都阻塞时,没任务运行了咋办?系统会创建一个空闲任务(优先级最低),来保证一直有一个任务在运行,优先级最低保证了其他任务就绪后能进入运行态,
空闲任务钩子函数(hook)
void vApplicationIdleHook( void );
通过FreeRTOSConfig.h 中的配置常量configUSE_IDLE_HOOK 定义为1,启用函数
它在空闲任务中循环执行,可以在函数内添加计时来查看系统的空闲时间,判断系统利用率,或者将系统设置为低功耗,可以根据你自己的需要去设置,但是有条件的:
它不能被阻塞和挂起
在任务被vTaskDelete() API删除后,空闲任务要去回收资源,所以要求它能快速返回
例:
/* 定义一个变量去计数*/
unsigned long ulIdleCycleCount = 0UL;
/* 空闲钩子函数必须命名为vApplicationIdleHook(),无参数也无返回值 */
void vApplicationIdleHook( void )
{
ulIdleCycleCount++;
}
延时函数
vTaskDelay() API
void vTaskDelay( portTickType xTicksToDelay );
//xTicksToDelay :就是TICK周期
利用这个延时来让任务进入阻塞,让出运行位置,来执行其他任务,这个延时时间段是从调用vTaskDelay()开始到切出阻塞态的时间;相对延时
借用大佬图片
vTaskDelayUntil() API
void vTaskDelayUntil( portTickType * pxPreviousWakeTime, portTickType xTimeIncrement );
也是阻塞延时,但时间段不一样,任务开始到执行到下一次任务执行的时间
这样任务就可以按固定周期执行,在任务中执行中断,任务的执行时间也不会变,增加的中断处理时间,延时时间就减小,整体的时间还是没变
借用大佬图片
vTaskPrioritySet() API
void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority );
// xTaskHandle pxTask:任务句柄
//unsigned portBASE_TYPE uxNewPriority :优先级
//任务修改自己本身的优先级时,句柄为NULL
在调度器启动后去更改任务的优先级
uxTaskPriorityGet() API
unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask );
查询任务的优先级
vTaskDelete() API
void vTaskDelete( xTaskHandle pxTaskToDelete );
//xTaskHandle pxTaskToDelete:句柄
//删自己还是NULL
任务创建
创建两个简单的任务
int main( void )
{
xTaskCreate( Task1, //看作函数名就好
"Task 1", //任务名
1000, //设定任务的栈空间
NULL, //传入函数的指针(void*),这里没用
2, //优先级
NULL ); //句柄,这里没用
xTaskCreate( Task2, "Task 2", 1000, NULL, 1, NULL ); //0的优先级最低
vTaskStartScheduler(); //开启调度器
while(1);
}
void task1(void *pvParameters) //LED0亮灭
{
while(1)
{
LED0=~LED0;
vTaskDelay(500);
}
}
//LED1 任务函数
void task2(void *pvParameters) //LED1亮灭
{
while(1)
{
LED1=0;
vTaskDelay(200);
LED1=1;
vTaskDelay(800);
}
}