物联网操作系统低功耗管理源码分析

 

void vTaskStartScheduler( void )
		xReturn = xTaskCreate(	prvIdleTask,
								"IDLE", configMINIMAL_STACK_SIZE,
								( void * ) NULL,
								( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
								&xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
	}
/*任务删除自身*/
static void prvCheckTasksWaitingTermination( void )
{

	/** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/

	#if ( INCLUDE_vTaskDelete == 1 )
	{
		BaseType_t xListIsEmpty;

		/* 
			遍历将要删除的任务
			uxDeletedTasksWaitingCleanUp 在vTaskDelete进行加1处理
		*/
		while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U )
		{
			//挂起了调度器
			vTaskSuspendAll();
			{
				//读取删除任务自身列表里任务状态是否为空
				xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination );
			}
			//开启调度器
			( void ) xTaskResumeAll();
			
			if( xListIsEmpty == pdFALSE )
			{
				//删除任务
				TCB_t *pxTCB;
				//进入临界段
				taskENTER_CRITICAL();
				{
					/*
						1、获取任务控制块
						2、从任务列表项移除任务
						3、任务总计数减一
						4、等待删除计数减一
					*/
					pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) );
					( void ) uxListRemove( &( pxTCB->xStateListItem ) );
					--uxCurrentNumberOfTasks;
					--uxDeletedTasksWaitingCleanUp;
				}
				//退出临界段
				taskEXIT_CRITICAL();
				//释放了任务控制块
				prvDeleteTCB( pxTCB );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
	}
	#endif /* INCLUDE_vTaskDelete */
}
	/*
		获取最小系统时间片
	*/
	static TickType_t prvGetExpectedIdleTime( void )
	{
	TickType_t xReturn;
	UBaseType_t uxHigherPriorityReadyTasks = pdFALSE;

		/* */
		#if( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 )
		{
			//就绪态的任务优先级高于空闲
			if( uxTopReadyPriority > tskIDLE_PRIORITY )
			{
				uxHigherPriorityReadyTasks = pdTRUE;
			}
		}
		#else
		{
			const UBaseType_t uxLeastSignificantBit = ( UBaseType_t ) 0x01;

			/* 就绪态的任务优先级高于空闲 */
			if( uxTopReadyPriority > uxLeastSignificantBit )
			{
				uxHigherPriorityReadyTasks = pdTRUE;
			}
		}
		#endif
		//当前任务优先级高于空闲任务
		if( pxCurrentTCB->uxPriority > tskIDLE_PRIORITY )
		{
			xReturn = 0;
		}
		//与空闲任务优先级相同的其他任务处于就绪态
		else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 )
		{
			xReturn = 0;
		}
		//高优先级任务处于就绪态
		else if( uxHigherPriorityReadyTasks != pdFALSE )
		{
			/* There are tasks in the Ready state that have a priority above the
			idle priority.  This path can only be reached if
			configUSE_PREEMPTION is 0. */
			xReturn = 0;
		}
		else
		{
			//系统解锁时间-系统tick计数值== 就是当前系统的最小时间片
			xReturn = xNextTaskUnblockTime - xTickCount;
		}

		return xReturn;
	}
	
/*
	空闲任务

*/
static portTASK_FUNCTION( prvIdleTask, pvParameters )
{
	/* Stop warnings. */
	( void ) pvParameters;

	/** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE
	SCHEDULER IS STARTED. **/

	for( ;; )
	{
		/*检查任务删除自身处理 */
		prvCheckTasksWaitingTermination();
		/*判断调度器工作模式是否开启了优先级抢占模式*/
		#if ( configUSE_PREEMPTION == 0 )
		{ 
			/* 
				1、触发了上下文切换
				2、让调度器判断是否有其他任务,处于了就绪态,然后进行调度
			*/
			taskYIELD();
		}
		#endif /* configUSE_PREEMPTION */
		//调度器使能抢占式
		#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )
		{
			/*
				1、和空闲任务处于同一优先级的任务,处于就绪态
				2、进行上下文切换
				3、高于空闲任务优先级的任务,有调度器进行处理

			*/
			if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 )
			{
				taskYIELD();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */
		/*
			钩子函数,主要让用户自己填充代码
		*/
		#if ( configUSE_IDLE_HOOK == 1 )
		{
			extern void vApplicationIdleHook( void );

			/* 用户自己实现,比如检测外部信息 */
			vApplicationIdleHook();
		}
		#endif /* configUSE_IDLE_HOOK */

		/* 低功耗处理功能 */
		#if ( configUSE_TICKLESS_IDLE != 0 )
		{
		TickType_t xExpectedIdleTime;

			/* 获取系统的最小时间片 */
			xExpectedIdleTime = prvGetExpectedIdleTime();
			//判断是否大于休眠空闲处理的最小间隔=2tick
			if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
			{
				//挂起调度器
				vTaskSuspendAll();
				{
					/* 又一次获取获取系统的最小时间片*/
					xExpectedIdleTime = prvGetExpectedIdleTime();
					//再次判断
					if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
					{
						//进入了休眠处理,传入系统的最小时间片
						portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				//恢复调度器
				( void ) xTaskResumeAll();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configUSE_TICKLESS_IDLE */
	}
}



 

 

portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
//这个函数,是需要用户自己实现,但是STM32FreeRTOS已经帮我们实现
extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );
	#define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime )
#endif
/*
	低功耗实际处理函数
	1、传入系统的最小时间片

*/
__weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
	{
	uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL;
	TickType_t xModifiableIdleTime;

		/* 判断
			系统最小时间片是否大于systick的最大装载周期  单位都tick
		*/
		if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
		{
			//系统最小时间片=systick最大装载周期
			//如果获取系统最小时间片很大,但是systick休眠周期的最大值就是最大装载值
			//为什么这样设计????  
				1、systick定时器受限制(定时周期)
				2、保证systick精度问题
			xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
		}

		/* 关闭systick定时器 */
		portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT;

		/*
			systick重载值= 当前的systick计数值+单次系统tick装载值*(系统最小时间片-1)
			
		*/
		ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
		//装载值是否大于补偿周期 之后减去补偿周期
		//最终计算出,systick重载值
		if( ulReloadValue > ulStoppedTimerCompensation )
		{
			ulReloadValue -= ulStoppedTimerCompensation;
		}

		/* 关闭中断 
		
			关闭所有中断 和 进入临界段不一样 
			虽然关闭了中断,但是可以唤醒CPU,不进行中断处理
		
		*/
		__disable_irq();
		__dsb( portSY_FULL_READ_WRITE );
		__isb( portSY_FULL_READ_WRITE );

		/* 是否有其他任务,进入了就绪态 */
		if( eTaskConfirmSleepModeStatus() == eAbortSleep )
		{
			//终止休眠
			/* 当前的systick计数值,放到systick装载寄存器中 */
			portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG;

			/* 启动systick */
			portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;

			/* 重新赋值装载寄存器值为一个系统的tick周期. */
			portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;

			/* 开启中断 */
			__enable_irq();
		}
		else
		{
			/* 装载休眠systick装载值 */
			portNVIC_SYSTICK_LOAD_REG = ulReloadValue;

			/* 清除systick当前计数值 */
			portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;

			/* 启动systick定时器*/
			portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;

			/*  */
			xModifiableIdleTime = xExpectedIdleTime;
			//这个就是给我用户提供的接口,让我自己实现休眠处理,其实就是进一步降低功耗
			configPRE_SLEEP_PROCESSING( &xModifiableIdleTime );
			if( xModifiableIdleTime > 0 )
			{
				//让CPU休眠
				__dsb( portSY_FULL_READ_WRITE );
				__wfi();
				__isb( portSY_FULL_READ_WRITE );
			}
			//退出处理
			configPOST_SLEEP_PROCESSING( &xExpectedIdleTime );

			/* 停止systick定时器 */
			ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG;
			portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT );

			/* 使能中断 */
			__enable_irq();
			//判断是否为systick唤醒的
			if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )
			{
				uint32_t ulCalculatedLoadValue;

				/*systick恢复值= 单个tick周期值- (休眠装载值-当前systick计数值)*/
				ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );

				/* 
					这是一个保护处理
					1、装载值很小,就赋值为1个tick周期
					2、装载很大,也赋值为1个tick周期
					
				*/
				if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )
				{
					ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );
				}
				//装载恢复systick装载值
				portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue;

				/* 休眠周期的补偿值,单位为tick  也就是1ms单位  */
				ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
			}
			else
			{
				/* 休眠运行装载值= 休眠装载值-当前systick计数值)*/
				ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;

				/* 休眠运行周期,单位为tick值 */
				ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;

				//装载恢复systick装载值
				portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;
			}

			/* 清楚systick计数值*/
			portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
			portENTER_CRITICAL();
			{
				/*
					1、使能了systick
					2、补偿系统的tick周期值,也是说,tick运行了多长时间(tick值)
						为什么这样做?
						在调度器恢复的时候,会根据tick值,进行遍历的,保证实时性
					3、恢复systick周期为1个tick值
				*/
				portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
				vTaskStepTick( ulCompleteTickPeriods );
				portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
			}
			portEXIT_CRITICAL();
		}
	}

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值