STM32FreeRtos入门(四)——任务状态和调度

文章目录


前言

前面讲述了任务创建和任务删除

但是系统还缺少了最主要的任务调度,状态,优先级等。这章讲优先级


提示:以下是本篇文章正文内容,下面案例可供参考

一、任务状态


举例:打游戏

创建:打开,运行游戏

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

  1. 调度策略
    抢占式调度:高优先级任务就绪时,会立即抢占低优先级任务的处理器资源(低优先级任务被暂停,进入就绪态)。
    时间片调度(同优先级任务):若多个任务优先级相同且均为就绪态,调度器会按时间片(由 configTICK_RATE_HZ 定义的系统节拍周期)轮流调度它们,实现 "并发" 效果。
  2. 调度触发时机
    系统节拍中断:由定时器产生的周期性中断(SysTick),是最常见的调度触发点(如任务延时到期、时间片轮转)。
    任务状态变化:如高优先级任务从阻塞态转为就绪态、任务主动调用 taskYIELD() 放弃处理器等。
    中断服务程序(ISR):ISR 中释放资源(如信号量)可能导致高优先级任务就绪,此时需调用 portYIELD_FROM_ISR() 触发调度。

1.函数说明

代码如下(示例):

  1. 任务创建与删除
    xTaskCreate() 创建一个新的任务并将其添加到就绪列表(动态内存分配,依赖 configSUPPORT_DYNAMIC_ALLOCATION 配置)。参数包括任务函数、任务名、栈大小、参数、优先级、任务句柄(输出)。
    xTaskCreateStatic() 静态创建任务(需手动指定栈和任务控制块内存,不依赖动态内存,依赖 configSUPPORT_STATIC_ALLOCATION)。
    vTaskDelete() 删除指定任务,被删除的任务将从就绪 / 阻塞 / 挂起列表中移除,其资源(若动态分配)会在空闲任务中释放。
  2. 任务阻塞与延时
    vTaskDelay() 让当前任务进入阻塞状态一段指定时间(以系统节拍 tick 为单位),时间到后自动进入就绪态。注意:延时是 "相对时间",从调用时刻开始计算。
    vTaskDelayUntil() 让任务按照 "绝对时间" 周期性阻塞(适合需要固定周期执行的任务),确保任务执行间隔稳定,不受调度延迟影响。
    xTaskAbortDelay() 强制唤醒一个因 vTaskDelay()、vTaskDelayUntil() 或阻塞在信号量 / 队列等上的任务,使其立即进入就绪态。
  3. 任务优先级管理
    vTaskPrioritySet() 动态修改指定任务的优先级。
    uxTaskPriorityGet() 获取指定任务的当前优先级。
    vTaskPriorityInherit() / vTaskPriorityDisinherit() 用于互斥锁(xSemaphoreCreateMutex())的优先级继承机制,防止优先级反转(自动提升低优先级任务的优先级)。
  4. 任务挂起 / 恢复的扩展
    vTaskSuspendAll() 挂起所有任务(包括调度器),仅允许当前任务运行,直到调用 xTaskResumeAll() 恢复。注意:此函数不挂起中断,且期间不能调用可能引起上下文切换的函数(如 vTaskDelay())。
    xTaskResumeAll() 恢复被 vTaskSuspendAll() 挂起的所有任务和调度器,返回值指示恢复期间是否有任务需要运行(pdTRUE 表示需要上下文切换)。
    xTaskResumeFromISR() 在中断服务程序(ISR)中恢复被挂起的任务,返回值为 pdTRUE 时需触发上下文切换(通过 portYIELD_FROM_ISR())。
  5. 任务状态查询
    eTaskGetState() 获取指定任务的当前状态(如运行、就绪、阻塞、挂起等,返回 eTaskState 枚举值)。
    uxTaskGetNumberOfTasks() 获取当前系统中存在的任务总数(包括所有状态的任务)。
    vTaskList() 生成任务状态列表字符串(包含任务名、状态、优先级、栈剩余空间等信息),需配置 configUSE_TRACE_FACILITY 为 1。
  6. 调度器控制
    vTaskStartScheduler() 启动 FreeRTOS 调度器,开始任务调度(通常在 main() 中初始化所有任务后调用)。
    vTaskEndScheduler() 停止调度器并释放所有资源(仅部分平台支持,如 x86,嵌入式平台较少使用)。
  7. 任务切换触发
    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 是基于优先级的抢占式调度器,核心原则是:始终运行就绪态中优先级最高的任务。

相关推荐
充哥单片机设计7 小时前
【STM32项目开源】基于STM32的智能天然气火灾监控
stm32·单片机·嵌入式硬件
充哥单片机设计7 小时前
【STM32项目开源】基于STM32的智能仓库火灾检测系统
stm32·单片机·嵌入式硬件
就叫飞六吧9 小时前
普中stm32大Dap烧录流程
stm32
A9better9 小时前
嵌入式开发学习日志38——stm32之看门狗
stm32·嵌入式硬件·学习
小莞尔10 小时前
【51单片机】【protues仿真】基于51单片机智能路灯控制系统
c语言·stm32·单片机·嵌入式硬件·51单片机
辰哥单片机设计19 小时前
TT直流减速电机(STM32)
stm32
A9better19 小时前
嵌入式开发学习日志36——stm32之USART串口通信前述
stm32·单片机·嵌入式硬件·学习
思诺学长20 小时前
BMS(电池管理系统)的主要功能和架构简述
单片机·嵌入式硬件
czhaii20 小时前
全局不关总中断的 RTOS / CosyOS-II for STCAI MCU
单片机