文章目录
前言
前面讲述了任务创建和任务删除
但是系统还缺少了最主要的任务调度,状态,优先级等。这章讲优先级
提示:以下是本篇文章正文内容,下面案例可供参考
一、任务状态

举例:打游戏
创建:打开,运行游戏
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 是基于优先级的抢占式调度器,核心原则是:始终运行就绪态中优先级最高的任务。