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();
}
}