3.4 优先级与tick
3.4.1 优先级
优先级的取值范围是 0 ~ configMAX_PRIORITIES - 1,越大优先级越高
FreeRTOS会确保最高优先级的任务马上执行,对于同级的轮流执行。
3.4.2 tick和延迟函数
FreeRTOS使用定时器产生固定间隔中断,叫tick。假设t1、t2、t3三个时刻发生中断,则称两次中断之间的实践为time slice(时间片)。
常用的延时函数有两种,mdelay(time)和vTaskDelay(tick_period),前者用延时时间作为输入,后者用几个tick作为输入,也可以用vTaskDelay(pdMS_TO_TICKS(time))实现输入延时时间。
- mdelay(t)通过CPU执行无意义指令空耗时间,会阻塞其他任务执行
- vTaskDelay(n)是FreeRTOS提供的,调用后任务会立即放弃CPU控制权,进入阻塞状态,延时结束后会被任务调度器唤醒并变得可运行。
- 但是vTaskDelay(n)所延迟的时间并不是完全精准的,可以用vTaskDelayUntil(&Pre, n)等待到指定的绝对时刻任务才进入就绪状态。&Pre是上一次被唤醒的时间,n是要等待的tick数。常用做法为:
pretime = xTaskGetTickCount();
while(1)
{
func();
vTaskDelayUntil(&pretime, 15);
}
为什么xTaskGetTickCount写在while外面也能起作用呢,因为调用vTaskDelayUntil(&pretime, 15)时,pretime会自动更新上一次的唤醒时间
3.5 任务状态
3.5.1 阻塞状态(Block)
在日常生活的例子中,母亲在电脑前跟同事沟通时,如果同事一直没回复,那么母亲的
工作就被卡住了、被堵住了、处于阻塞状态(Blocked)。重点在于:母亲在等待。
在实际产品中,我们不会让一个任务一直运行,而是使用"事件驱动"的方法让它运行:
任务要等待某个事件,事件发生后它才能运行;在等待事件过程中,它不消耗 CPU 资源,这个任务就处于阻塞状态。
阻塞状态下的任务可以等待两种类型的事件:
- 时间:固定的时长或特定的时刻
- 同步事件:别的事件传来的信号
3.5.2 暂停状态(Suspended)
可以通过任务句柄暂停任务
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
// 输入别的任务句柄可以暂停别人
//输入NULL可以暂停自己
退出暂停状态只能让别人干
vTaskResume( TaskHandle_t xTaskToSuspend );
3.5.3 就绪状态(Ready)
可以跑了就等一声令下
3.6 空闲任务
空闲任务的作用:给删除的任务释放内存。
此外,
调度器必须能找到一个可以运行的任务,当使用vTaskStartScheduler()函数来创建、启动调度器时,这个函数内部会创建空闲任务。(像默认构造函数?)
空闲任务优先级为0,永远不会阻塞。
添加一个空闲任务的钩子函数(空闲函数的循环每执行一次,就调用一次钩子函数)可以达到以下目的:
- 执行低优先级,后台的,需要连续执行的函数
- 测量系统空闲时间
- 进入省电模式
示例:让LED闪烁任务挂起和继续
任务描述:初始时,遥控器按play键创建任务,以后再按play键则在挂起和继续任务中切换,按power键删除任务:
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
void StartDefaultTask(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
}
void StartDefaultTask(void *argument)
{
ColorLED_Set(0);
LCD_Init();
LCD_Clear();
IRReceiver_Init();
TaskHandle_t xColorLedHandle=NULL;
LCD_PrintString(0,0,"waiting control");
int running;
for(;;)
{
uint8_t dev,data;
//读取红外遥控器
if (!IRReceiver_Read(&dev, &data))
{
if(data == 0xa8) //按下play
{
if(xColorLedHandle == NULL){
LCD_ClearLine(0, 0);
LCD_PrintString(0, 0, "running Task");
vTaskDelay(2);
xTaskCreate(ColorLED_Test, "cled", 128, NULL, osPriorityNormal, &xColorLedHandle);
running = 1;
}
else{
if(running) // 如果正在运行,则让其挂起
{
LCD_ClearLine(0, 0);
LCD_PrintString(0, 0, "Task Suspended");
vTaskSuspend(xColorLedHandle);
ColorLED_Set(0);
running = 0;
}
else{ // 如果没在运行,则让其继续
LCD_ClearLine(0, 0);
LCD_PrintString(0, 0, "Task Resume");
vTaskResume(xColorLedHandle);
running = 1;
}
}
}
else if(data == 0xa2) //按下power
{
if(xColorLedHandle != NULL){
LCD_ClearLine(0, 0);
LCD_PrintString(0, 0, "Task deleted");
vTaskDelete(xColorLedHandle);
ColorLED_Set(0);
xColorLedHandle = NULL;
}
}
}
}
}