文章目录
前言
前面讲述了任务创建和任务删除
但是系统还缺少了最主要的任务调度,状态,优先级等。这章讲优先级
提示:以下是本篇文章正文内容,下面案例可供参考
一、任务状态
举例:打游戏
创建:打开,运行游戏
Running :进入对局,正在进行游玩的对局
Ready :匹配中,等待进入对局/掉线了等待重连。
Blocked :服务器维护,暂时玩不了。等待服务器恢复才能继续游玩
所以,Blocked 下一状态是Ready
Suspended :玩不下去游戏,上厕所,打电话,睡着了等等。但是后台还挂着。恢复了继续玩。同上,下一状态是Ready
Deleted:关闭游戏

示例代码
c
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
uint8_t dev, data;
int len;
int status;
TaskHandle_t xSoundTaskHandle = NULL;
BaseType_t ret;
LCD_Init();
LCD_Clear();
IRReceiver_Init();
LCD_PrintString(0,0,"Waiting music");
while (1)
{
/* 读取红外遥控器,创建播放任务或者删除播放任务 */
if(0==IRReceiver_Read(&dev,&data))
{
if(data==0xa8)
{
extern void PlayMusic(void *params);
if(xSoundTaskHandle==NULL)
{
LCD_ClearLine(0,0);
LCD_PrintString(0,0,"Create music");
ret = xTaskCreate(PlayMusic, "SoundTask", 128, NULL, osPriorityNormal, &xSoundTaskHandle);
status=1;
}
else
{
if(status)
{
/*暂停或恢复*/
LCD_ClearLine(0,0);
LCD_PrintString(0,0,"Suspend music");
vTaskSuspend(xSoundTaskHandle);
PassiveBuzzer_Control(0);
status=0;
}
else
{
LCD_ClearLine(0,0);
LCD_PrintString(0,0,"Resume music");
vTaskResume(xSoundTaskHandle);
status=1;
}
}
}
else if(data==0xa2)
{
if(xSoundTaskHandle!=NULL)
{
LCD_ClearLine(0,0);
LCD_PrintString(0,0,"Delete music");
vTaskDelete(xSoundTaskHandle);
PassiveBuzzer_Control(0);
xSoundTaskHandle=NULL;
}
}
}
}
/* USER CODE END StartDefaultTask */
}
二、任务调度
调度,肯定就是任务之间的切换,包括延时等
FreeRTOS 是基于优先级的抢占式调度器,核心原则是:始终运行就绪态中优先级最高的任务。
- 调度策略
抢占式调度:高优先级任务就绪时,会立即抢占低优先级任务的处理器资源(低优先级任务被暂停,进入就绪态)。
时间片调度(同优先级任务):若多个任务优先级相同且均为就绪态,调度器会按时间片(由 configTICK_RATE_HZ 定义的系统节拍周期)轮流调度它们,实现 "并发" 效果。 - 调度触发时机
系统节拍中断:由定时器产生的周期性中断(SysTick),是最常见的调度触发点(如任务延时到期、时间片轮转)。
任务状态变化:如高优先级任务从阻塞态转为就绪态、任务主动调用 taskYIELD() 放弃处理器等。
中断服务程序(ISR):ISR 中释放资源(如信号量)可能导致高优先级任务就绪,此时需调用 portYIELD_FROM_ISR() 触发调度。
1.函数说明
代码如下(示例):
- 任务创建与删除
xTaskCreate() 创建一个新的任务并将其添加到就绪列表(动态内存分配,依赖 configSUPPORT_DYNAMIC_ALLOCATION 配置)。参数包括任务函数、任务名、栈大小、参数、优先级、任务句柄(输出)。
xTaskCreateStatic() 静态创建任务(需手动指定栈和任务控制块内存,不依赖动态内存,依赖 configSUPPORT_STATIC_ALLOCATION)。
vTaskDelete() 删除指定任务,被删除的任务将从就绪 / 阻塞 / 挂起列表中移除,其资源(若动态分配)会在空闲任务中释放。 - 任务阻塞与延时
vTaskDelay() 让当前任务进入阻塞状态一段指定时间(以系统节拍 tick 为单位),时间到后自动进入就绪态。注意:延时是 "相对时间",从调用时刻开始计算。
vTaskDelayUntil() 让任务按照 "绝对时间" 周期性阻塞(适合需要固定周期执行的任务),确保任务执行间隔稳定,不受调度延迟影响。
xTaskAbortDelay() 强制唤醒一个因 vTaskDelay()、vTaskDelayUntil() 或阻塞在信号量 / 队列等上的任务,使其立即进入就绪态。 - 任务优先级管理
vTaskPrioritySet() 动态修改指定任务的优先级。
uxTaskPriorityGet() 获取指定任务的当前优先级。
vTaskPriorityInherit() / vTaskPriorityDisinherit() 用于互斥锁(xSemaphoreCreateMutex())的优先级继承机制,防止优先级反转(自动提升低优先级任务的优先级)。 - 任务挂起 / 恢复的扩展
vTaskSuspendAll() 挂起所有任务(包括调度器),仅允许当前任务运行,直到调用 xTaskResumeAll() 恢复。注意:此函数不挂起中断,且期间不能调用可能引起上下文切换的函数(如 vTaskDelay())。
xTaskResumeAll() 恢复被 vTaskSuspendAll() 挂起的所有任务和调度器,返回值指示恢复期间是否有任务需要运行(pdTRUE 表示需要上下文切换)。
xTaskResumeFromISR() 在中断服务程序(ISR)中恢复被挂起的任务,返回值为 pdTRUE 时需触发上下文切换(通过 portYIELD_FROM_ISR())。 - 任务状态查询
eTaskGetState() 获取指定任务的当前状态(如运行、就绪、阻塞、挂起等,返回 eTaskState 枚举值)。
uxTaskGetNumberOfTasks() 获取当前系统中存在的任务总数(包括所有状态的任务)。
vTaskList() 生成任务状态列表字符串(包含任务名、状态、优先级、栈剩余空间等信息),需配置 configUSE_TRACE_FACILITY 为 1。 - 调度器控制
vTaskStartScheduler() 启动 FreeRTOS 调度器,开始任务调度(通常在 main() 中初始化所有任务后调用)。
vTaskEndScheduler() 停止调度器并释放所有资源(仅部分平台支持,如 x86,嵌入式平台较少使用)。 - 任务切换触发
taskYIELD() 强制当前任务放弃 CPU 使用权,触发一次上下文切换(让就绪态中最高优先级的任务运行)。
portYIELD_FROM_ISR() 在中断服务程序中触发上下文切换(需配合 xTaskResumeFromISR() 等函数的返回值使用)。
这些函数共同构成了 FreeRTOS 的任务调度机制,使用时需结合具体场景(如是否在中断中、是否需要动态内存、任务优先级设计等),并确保配置项(FreeRTOSConfig.h)与函数功能匹配。
2.任务调度
前面讲了,删除任务必须自杀或者他杀;
c
void Led_Test(void)
{
int i;
Led_Init();
for(i=0;i<10;i++)
{
Led_Control(LED_GREEN, 1);
mdelay(500);
Led_Control(LED_GREEN, 0);
mdelay(500);
}
vTaskDelete(NULL);//不自杀或者他杀,系统崩溃
}
任务他杀后,"收尸"(释放TCB,回收栈)工作由执行他杀任务清除。自杀后,由空闲任务进行"收尸"(释放TCB,回收栈), 若一直没有空闲任务进行"收尸",则栈被使用完,系统崩溃
编程习惯可以尽量避免这种情况;
1,事件驱动
2,延时函数不要用死循环
c
void Led_Test(void)
{
int i;
Led_Init();
for(i=0;i<10;i++)
{
Led_Control(LED_GREEN, 1);
//mdelay(500);
vTaskDelay(500);
Led_Control(LED_GREEN, 0);
//mdelay(500);
vTaskDelay(500);
}
vTaskDelete(NULL);//不自杀或者他杀,系统崩溃
}
自杀后,由空闲任务进行"收尸"(释放TCB,回收栈)
调度器函数中会自己创建空闲任务
3.延时函数区别
vTaskDelay()让当前任务从就绪态进入阻塞状态一段指定时间
使用此函数,系统任务切换延时时间近乎于指定时间
vTaskDelayUntil()让任务按照 "绝对时间" 周期性阻塞,确保任务执行间隔稳定,不受调度延迟影响。
vTaskDelayUntil(&PreTime,500);
系统任务切换时间间隔不确定,为了前面的if部分,使延时总体为500ms
c
void LcdPrintTask(void *params)
{
struct TaskPrintInfo *pInfo = params;
uint32_t cnt = 0;
int len;
BaseType_t PreTime;
uint64_t t1,t2;
PreTime=xTaskGetTickCount();
while (1)
{
/* 打印信息 */
if (g_LCDCanUse)
{
g_LCDCanUse = 0;
len = LCD_PrintString(pInfo->x, pInfo->y, pInfo->name);
len += LCD_PrintString(len, pInfo->y, ":");
LCD_PrintSignedVal(len, pInfo->y, cnt++);
g_LCDCanUse = 1;
mdelay(cnt & 0x3);
}
t1=system_get_ns();
//vTaskDelay(500);
vTaskDelayUntil(&PreTime,500);
t2=system_get_ns();
LCD_ClearLine(pInfo->x, pInfo->y+2);
LCD_PrintSignedVal(pInfo->x, pInfo->y+2,t2-t1);
}
}
总结
FreeRTOS 通过优先级抢占和时间片调度实现高效的任务管理,任务状态的切换由事件(如延时、信号量)和调度器共同控制。理解这些机制是使用 FreeRTOS 进行多任务编程的基础。
FreeRTOS 是基于优先级的抢占式调度器,核心原则是:始终运行就绪态中优先级最高的任务。