freeRTOS 低功耗模式 和 空闲任务

2023-05-16

低功耗模式

1. 芯片原本就支持的硬件低功耗

2. freeRTOS提供的软件低功耗,Tickless模式!

当用户将宏定义 configUSE_TICKLESS_IDLE 配置为 1 且系统运行满足以下两个条件时,

系统内核会自动的调用,低功耗宏定义函数 portSUPPRESS_TICKS_AND_SLEEP():
-------------------------------## 当前空闲任务正在运行,所有其它的任务处在挂起状态或者阻塞状态。

## 根据用户配置 configEXPECTED_IDLE_TIME_BEFORE_SLEEP 的大小,

只有当系统可运行于低功耗模式的时钟节拍数大于等于这个参数时,系统才可以进入到低功耗模式。

-------------------------------

#ifndef configEXPECTED_IDLE_TIME_BEFORE_SLEEP
    #define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2
#endif
#if configEXPECTED_IDLE_TIME_BEFORE_SLEEP < 2
    #error configEXPECTED_IDLE_TIME_BEFORE_SLEEP must not be less than 2
#endif
默认定义的大小是 2 个系统时钟节拍,且用户自定义的话,必须大于 2 个系统时钟节拍。 

函数 portSUPPRESS_TICKS_AND_SLEEP 是 FreeRTOS 实现 tickles 模式的关键,此函数被空闲任务调用,

其定义是在 portmacro.h 文件中:

/* Tickless idle/low power functionality. */
#ifndef portSUPPRESS_TICKS_AND_SLEEP
    extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );
    #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime )
#endif

其中函数 vPortSuppressTicksAndSleep 是实际的低功耗执行代码,在 port.c 文件中定义,

参数xExpectedIdleTime 就是系统可以处于低功耗模式的系统时钟节拍数。

  1 #if configUSE_TICKLESS_IDLE == 1
  2 
  3     __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
  4     {
  5     uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL;
  6     TickType_t xModifiableIdleTime;
  7 
  8         /* Make sure the SysTick reload value does not overflow the counter. */
            确保滴答定时器的reload值不会溢出,也就是不能超过滴答定时器最大计数值。
9 if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) 【1】见后 10 { 11 xExpectedIdleTime = xMaximumPossibleSuppressedTicks; 12 } 13 14 /* Stop the SysTick momentarily. The time the SysTick is stopped for 15 is accounted for as best it can be, but using the tickless mode will 16 inevitably result in some tiny drift of the time maintained by the 17 kernel with respect to calendar time. */
停止滴答定时器 18 portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; 19 20 /* Calculate the reload value required to wait xExpectedIdleTime 21 tick periods. -1 is used because this code will execute part way 22 through one of the tick periods. */ 23 根据参数xExpectIdleTime来计算滴答定时器的重载值,进入低功耗之后,计时由滴答定时器计算。
ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG(寄存器)
+ ( ulTimerCountsForOneTick(一个节拍多少个时钟) * ( xExpectedIdleTime - 1UL ) );
24 if( ulReloadValue > ulStoppedTimerCompensation ) 【2】补偿时间,见后 25 { 26 ulReloadValue -= ulStoppedTimerCompensation; 27 } 28 29 /* Enter a critical section but don't use the taskENTER_CRITICAL() 30 method as that will mask interrupts that should exit sleep mode. */ 31 __disable_irq(); 【3】设置PRIMASK关闭中断 32 __dsb( portSY_FULL_READ_WRITE ); 33 __isb( portSY_FULL_READ_WRITE ); 34 35 /* If a context switch is pending or a task is waiting for the scheduler 36 to be unsuspended then abandon the low power entry. */
确认是否可以进入低功耗模式 37 if( eTaskConfirmSleepModeStatus() == eAbortSleep ) 【4】函数见后 38 { 39 /* Restart from whatever is left in the count register to complete 40 this tick period. */
不能进入低功耗模式,重启滴答定时器,恢复滴答运行 41 portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; 42 43 /* Restart SysTick. */ 44 portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; 45 46 /* Reset the reload register to the value required for normal tick 47 periods. */ 48 portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; 49 50 /* Re-enable interrupts - see comments above __disable_irq() call 51 above. */ 52 __enable_irq(); 恢复中断设置 53 } 54 else 55 {
可以进入低功耗模式,设置滴答定时器 56 /* Set the new reload value. */ 57 portNVIC_SYSTICK_LOAD_REG = ulReloadValue; 刚刚在【2】处算的时间值,赋给滴答定时器 58 59 /* Clear the SysTick count flag and set the count value back to 60 zero. */ 61 portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; 62 63 /* Restart SysTick. */ 64 portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; 65 66 /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can 67 set its parameter to 0 to indicate that its implementation contains 68 its own wait for interrupt or wait for event instruction, and so wfi 69 should not be executed again. However, the original expected idle 70 time variable must remain unmodified, so a copy is taken. */ 71 xModifiableIdleTime = xExpectedIdleTime; 72 configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); 【5】见后 73 if( xModifiableIdleTime > 0 ) 74 { 75 __dsb( portSY_FULL_READ_WRITE ); 76 __wfi(); 使用__WFI指令,进入睡眠模式。http://www.keil.com/support/man/docs/armcc/armcc_chr1359125004400.htm 77 __isb( portSY_FULL_READ_WRITE ); 78 }

当代码执行到这里,说明已经退出了低功耗模式!!! 79 configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); 【5】见后 80 81 /* Stop SysTick. Again, the time the SysTick is stopped for is 82 accounted for as best it can be, but using the tickless mode will 83 inevitably result in some tiny drift of the time maintained by the 84 kernel with respect to calendar time. */
停止滴答定时器 85 ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG; 读取滴答定时器控制和状态寄存器 86 portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT ); 87 88 /* Re-enable interrupts - see comments above __disable_irq() call 89 above. */ 90 __enable_irq(); 91
判断导致退出低功耗的是,外部中断,还是滴答定时器计时时间到了 92 if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) 不同的唤醒方式,对应的“系统时间补偿值”(单位是时钟节拍)是不同的。 93 { 94 uint32_t ulCalculatedLoadValue; 95 96 /* The tick interrupt has already executed, and the SysTick 97 count reloaded with ulReloadValue. Reset the 98 portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick 99 period. */ 100 ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); 101 102 /* Don't allow a tiny value, or values that have somehow 103 underflowed because the post sleep hook did something 104 that took too long. */ 105 if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) 106 { 107 ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); 108 } 109 110 portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; 111 112 /* The tick interrupt handler will already have pended the tick 113 processing in the kernel. As the pending tick will be 114 processed as soon as this function exits, the tick value 115 maintained by the tick is stepped forward by one less than the 116 time spent waiting. */ 117 ulCompleteTickPeriods = xExpectedIdleTime - 1UL; 118 } 119 else 外部中断唤醒的,需要进行时间补偿 120 { 121 /* Something other than the tick interrupt ended the sleep. 122 Work out how long the sleep lasted rounded to complete tick 123 periods (not the ulReload value which accounted for part 124 ticks). */ 125 ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; 126 127 /* How many complete tick periods passed while the processor 128 was waiting? */ 129 ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; 130 131 /* The reload value is set to whatever fraction of a single tick 132 period remains. */ 133 portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; 134 } 135 136 /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG 137 again, then set portNVIC_SYSTICK_LOAD_REG back to its standard 138 value. The critical section is used to ensure the tick interrupt 139 can only execute once in the case that the reload register is near 140 zero. */
重新启动滴答定时器,重载值设置为正常值。 141 portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; 142 portENTER_CRITICAL(); 143 { 144 portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; 145 vTaskStepTick( ulCompleteTickPeriods ); 【6】给系统时钟节拍进行补偿,函数见后 146 portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; 147 } 148 portEXIT_CRITICAL(); 149 } 150 } 151 152 #endif /* #if configUSE_TICKLESS_IDLE */

【1】参数 xExpectedIdleTime 表示处理器将要在低功耗模式运行的时长(单位为时钟节拍数)

这个时间会使用滴答定时器来计时,

但是滴答定时器的计数寄存器是 24 位的,因此这个时间值不能超过滴答定时器的最大计数值。

xMaximumPossibleSuppressedTicks 是个静态全局变量,此变量会在函数 vPortSetupTimerInterrupt()中被重新赋值,代码如下:

ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );    多少个时钟计时是一个节拍
xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;

经过计算 xMaximumPossibleSuppressedTicks=0xFFFF_FF/(180000000/1000)93,因此可以得出进入低功耗模式的最大时长为 93 个时钟节拍,

注意!这个值要根据自己所使用的平台以及FreeRTOS 的实际配置情况来计算。

【2】从滴答定时器停止运行,到把统计得到的低功耗模式运行时间补偿给 FreeRTOS系统时钟,也是需要时间的,这期间也是有程序在运行的。

这段程序运行的时间我们要留出来,具体的时间没法去统计,因为平台不同、编译器的代码优化水平不同导致了程序的执行时间也不同。

这里只能大概的留出一个时间值,这个时间值由变量 ulStoppedTimerCompensation 来确定,这是一个全局变量

此变量也会在函数 vPortSetupTimerInterrupt()中被重新赋值,代码如下:

#define portMISSED_COUNTS_FACTOR ( 45UL )

ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );

由上面的公式可以得出: ulStoppedTimerCompensation=45/(180000000/180000000)=45

如果要修改这个时间值的话直接修改宏 portMISSED_COUNTS_FACTOR 即可。

【3】三个中断屏蔽寄存:

PRIMASK禁止除NMI和HardFault以外的所有异常和中断。

使用CPS(修改寄存器状态指令)

CPSIE  I    清除PRIMASK(使能中断)

CPSID  I    设置PRIMASK(禁止中断)

FAULTMASK 连HardFault也屏蔽了。【只有NMI】

CPSIE   F    清除指令,FAULTmask会在异常退出时自动清零。

CPSID   F

BASEPRI 用于屏蔽优先级 大于等于 某个阈值的中断。(M3内核优先级大则低,freRTOS大则高)

MOV    R0,       #0x60

MSR     BASEPRI,   R0

## 程序中写的__disable_irq()这个函数找不到定义的地方,在网上搜到人家直接是MDK的内部指令:(链接失效的话,直接上MDK官方搜__disable_irq intrinsic 即可)

http://www.keil.com/support/man/docs/armcc/armcc_chr1359124995648.htm

在以上代码中的含义:

在执行 WFI 前设置寄存器 PRIMASK 的话处理器可以由中断唤醒,但是不会处理这些中断,

退出低功耗模式以后,通过清除寄存器 PRIMASK 来使 ISR 得到执行,其实就是利用PRIMASK 来延迟 ISR 的执行。 

【4】返回eAbortSleep就不能进入低功耗模式了。

注意区别:

PendingReadyList 任务进入就绪状态,但是没有放入readylist链表。这种情况发生在调度器被停止时,有些任务进入到ready状态,这时就将任务加入到xPendingReadyList,等待调度器开始时,从新进行一次调度。 

SuspendTaskList 是任务调用"挂起任务"的API,导致被挂起的任务列表。

 1 #if( configUSE_TICKLESS_IDLE != 0 )
 2 
 3     eSleepModeStatus eTaskConfirmSleepModeStatus( void )
 4     {
 5     /* The idle task exists in addition to the application tasks. */
 6     const UBaseType_t uxNonApplicationTasks = 1;
 7     eSleepModeStatus eReturn = eStandardSleep;
 8 
 9         if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0 )  是否有就绪任务
10         {
11             /* A task was made ready while the scheduler was suspended. */
12             eReturn = eAbortSleep;
13         }
14         else if( xYieldPending != pdFALSE )  是否产生了调度请求
15         {
16             /* A yield was pended while the scheduler was suspended. */
17             eReturn = eAbortSleep;
18         }
19         else
20         {
21             /* If all the tasks are in the suspended list (which might mean they
22             have an infinite block time rather than actually being suspended)
23             then it is safe to turn all clocks off and just wait for external
24             interrupts. */
25             if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == ( uxCurrentNumberOfTasks - uxNonApplicationTasks ) )  只有一个IdleTask活着
26             {
27                 eReturn = eNoTasksWaitingTimeout;
28             }
29             else
30             {
31                 mtCOVERAGE_TEST_MARKER();
32             }
33         }
34 
35         return eReturn;
36     }
37 
38 #endif /* configUSE_TICKLESS_IDLE */

【5】宏 configPRE_SLEEP_PROCESSING ()configPOST_SLEEP_PROCESSING()
在真正的低功耗设计中不仅仅是将处理器设置到低功耗模式就行了,还需要做一些其他的处理,比如:

● 将处理器降低到合适的频率,因为频率越低功耗越小,甚至可以在进入低功耗模式以后关闭系统时钟。
● 修改时钟源,晶振的功耗肯定比处理器内部的时钟源高,进入低功耗模式以后可以切换到内部时钟源,比如 STM32 的内部 RC 振荡器。
● 关闭其他外设时钟,比如 IO 口的时钟。
● 关闭板子上其他功能模块电源,这个需要在产品硬件设计的时候就要处理好,比如可以通过 MOS 管来控制某个模块电源的开关,在处理器进入低功耗模式之前关闭这些模块的电源。

FreeRTOS 为我们提供了一个宏来完成这些操作,它就是 configPRE_SLEEP_PROCESSING(),这个宏的具体实现内容需要用户去编写。

如果在进入低功耗模式之前我们降低了处理器频率、关闭了某些外设时钟等的话,那在退出低功耗模式以后就需要恢复处理器频率、重新打开外设时钟等,

这个操作在宏configPOST_SLEEP_PROCESSING()中完成,同样的这个宏的具体内容也需要用户去编写。

这两个宏会被函数 vPortSuppressTicksAndSleep()调用,我们可以在 FreeRTOSConfig.h 定义这两个宏,如下:

/********************************************************************************/
/* FreeRTOS 与低功耗管理相关配置 */
/********************************************************************************/
extern void PreSleepProcessing(uint32_t ulExpectedIdleTime);
extern void PostSleepProcessing(uint32_t ulExpectedIdleTime);
//进入低功耗模式前要做的处理
#define configPRE_SLEEP_PROCESSING PreSleepProcessing
//退出低功耗模式后要做的处理
#define configPOST_SLEEP_PROCESSING PostSleepProcessing

函数 PreSleepProcessing()和 PostSleepProcessing()可以在任意一个 C 文件中编写,例子:

//进入低功耗模式前需要处理的事情
//ulExpectedIdleTime:低功耗模式运行时间
void PreSleepProcessing(uint32_t ulExpectedIdleTime)
{
  //关闭某些低功耗模式下不使用的外设时钟
  __HAL_RCC_GPIOB_CLK_DISABLE(); (1)
  __HAL_RCC_GPIOC_CLK_DISABLE();
  __HAL_RCC_GPIOD_CLK_DISABLE();
  __HAL_RCC_GPIOE_CLK_DISABLE();
  __HAL_RCC_GPIOF_CLK_DISABLE();
  __HAL_RCC_GPIOG_CLK_DISABLE();
  __HAL_RCC_GPIOH_CLK_DISABLE();
}

void PostSleepProcessing(uint32_t ulExpectedIdleTime) {   //退出低功耗模式以后打开那些被关闭的外设时钟   __HAL_RCC_GPIOB_CLK_ENABLE(); (2)   __HAL_RCC_GPIOC_CLK_ENABLE();   __HAL_RCC_GPIOD_CLK_ENABLE();   __HAL_RCC_GPIOE_CLK_ENABLE();   __HAL_RCC_GPIOF_CLK_ENABLE();   __HAL_RCC_GPIOG_CLK_ENABLE();   __HAL_RCC_GPIOH_CLK_ENABLE(); } (1)、进入低功耗模式以后关闭那些低功耗模式中不用的外设时钟,USART1 和 GPIOA 的时钟没有关闭。 (2)、退出低功耗模式以后需要打开函数 PreSleepProcessing()中关闭的那些外设的时钟。

【6】给系统时钟节拍,加个补偿值。

    void vTaskStepTick( const TickType_t xTicksToJump )
    {
        /* Correct the tick count value after a period during which the tick
        was suppressed.  Note this does *not* call the tick hook function for
        each stepped tick. */
        configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime );
        xTickCount += xTicksToJump;
        traceINCREASE_TICK_COUNT( xTicksToJump );
    }

======================================================================

======================================================================

空闲任务

优先级最低,

空闲任务有一个重要的职责:

如果某个任务要调用函数 vTaskDelete()删除自身,

那么这个任务的任务控制块 TCB 和 任务堆栈 等这些由 FreeRTOS 系统自动分配的内存,需要在空闲任务中释放掉。

空闲任务的创建:启动任务调度器的时候自动创建。

大部分功能已经在“ 开启关闭调度器、挂起恢复调度器、vTaskStepTick ”这章节解释过了。这里看IdleTask的创建部分就行。

  1 void vTaskStartScheduler( void )
  2 {
  3 BaseType_t xReturn;
  4 
  5     
  6     /* Add the idle task at the lowest priority. */
  7     #if( configSUPPORT_STATIC_ALLOCATION == 1 )   静态方式创建IdleTask
  8     {
  9         StaticTask_t *pxIdleTaskTCBBuffer = NULL;
 10         StackType_t *pxIdleTaskStackBuffer = NULL;
 11         uint32_t ulIdleTaskStackSize;
 12 
 13         /* The Idle task is created using user provided RAM - obtain the
 14         address of the RAM then create the idle task. */
 15         vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );    这几个空间由用户来定义
 16         xIdleTaskHandle = xTaskCreateStatic(    prvIdleTask,
 17                                                 "IDLE",
 18                                                 ulIdleTaskStackSize,
 19                                                 ( void * ) NULL,
 20                                                 ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
 21                                                 pxIdleTaskStackBuffer,
 22                                                 pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
 23 
 24         if( xIdleTaskHandle != NULL )
 25         {
 26             xReturn = pdPASS;
 27         }
 28         else
 29         {
 30             xReturn = pdFAIL;
 31         }
 32     }
 33     #else   动态方式创建IdleTask
 34     {
 35         /* The Idle task is being created using dynamically allocated RAM. */
 36         xReturn = xTaskCreate(    prvIdleTask,
 37                                 "IDLE", configMINIMAL_STACK_SIZE,    堆栈大小可以改~
 38                                 ( void * ) NULL,
 39                                 ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
 40                                 &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
 41     }
 42     #endif /* configSUPPORT_STATIC_ALLOCATION */
 43 


/* 下边的不属于IdleTask方面的 */
44 #if ( configUSE_TIMERS == 1 ) 【略】 45 { 46 if( xReturn == pdPASS ) 47 { 48 xReturn = xTimerCreateTimerTask(); 49 } 50 else 51 { 52 mtCOVERAGE_TEST_MARKER(); 53 } 54 } 55 #endif /* configUSE_TIMERS */ 56 57 if( xReturn == pdPASS ) 58 { 59 /* Interrupts are turned off here, to ensure a tick does not occur 60 before or during the call to xPortStartScheduler(). The stacks of 61 the created tasks contain a status word with interrupts switched on 62 so interrupts will automatically get re-enabled when the first task 63 starts to run. */ 64 portDISABLE_INTERRUPTS(); 65 66 #if ( configUSE_NEWLIB_REENTRANT == 1 ) 67 { 68 /* Switch Newlib's _impure_ptr variable to point to the _reent 69 structure specific to the task that will run first. */ 70 _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); 71 } 72 #endif /* configUSE_NEWLIB_REENTRANT */ 73 74 xNextTaskUnblockTime = portMAX_DELAY; 75 xSchedulerRunning = pdTRUE; 76 xTickCount = ( TickType_t ) 0U; 77 78 /* If configGENERATE_RUN_TIME_STATS is defined then the following 79 macro must be defined to configure the timer/counter used to generate 80 the run time counter time base. */ 81 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); 82 83 /* Setting up the timer tick is hardware specific and thus in the 84 portable interface. */ 85 if( xPortStartScheduler() != pdFALSE ) 86 { 87 /* Should not reach here as if the scheduler is running the 88 function will not return. */ 89 } 90 else 91 { 92 /* Should only reach here if a task calls xTaskEndScheduler(). */ 93 } 94 } 95 else 96 { 97 /* This line will only be reached if the kernel could not be started, 98 because there was not enough FreeRTOS heap to create the idle task 99 or the timer task. */ 100 configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ); 101 } 102 103 /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0, 104 meaning xIdleTaskHandle is not used anywhere else. */ 105 ( void ) xIdleTaskHandle; 106 }

IdleTask任务函数:

  1 /*
  2  * -----------------------------------------------------------
  3  * The Idle task.
  4  * ----------------------------------------------------------
  5  *
  6  * The portTASK_FUNCTION() macro is used to allow port/compiler specific
  7  * language extensions.  The equivalent prototype for this function is:
  8  *
  9  * void prvIdleTask( void *pvParameters );
 10  *
 11  */
 12 static portTASK_FUNCTION( prvIdleTask, pvParameters )
 13 {
 14     /* Stop warnings. */
 15     ( void ) pvParameters;
 16 
 17     /** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE
 18     SCHEDULER IS STARTED. **/
 19 
 20     for( ;; )
 21     {
 22         /* See if any tasks have deleted themselves - if so then the idle task
 23         is responsible for freeing the deleted task's TCB and stack. */
 24         prvCheckTasksWaitingTermination();  检查是否有任务删除自己,有的话,会添加到xTaskWaitingTermination列表,扫描这个列表,并进行清理工作。
 25 
 26         #if ( configUSE_PREEMPTION == 0 )
 27         {
 28             /* If we are not using preemption we keep forcing a task switch to
 29             see if any other task has become available.  If we are using
 30             preemption we don't need to do this as any task becoming available
 31             will automatically get the processor anyway. */
如果没有使用抢占式内核,就强制执行任务切换,查看是否有其他任务有效。
抢占式内核,只要有优先级高的任务,自动就会抢占,不用这一步。 32 taskYIELD(); 33 } 34 #endif /* configUSE_PREEMPTION */ 35 36 #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) 37 { 38 /* When using preemption tasks of equal priority will be 39 timesliced. If a task that is sharing the idle priority is ready 40 to run then the idle task should yield before the end of the 41 timeslice. 42 如果使用抢占式内核,并且使能时间片调度,当有任务和空闲任务共享一个优先级的时,此任务就绪,空闲任务就应该放弃本时间片
将本时间片剩余的时间让给这个就绪任务。
43 A critical region is not required here as we are just reading from 44 the list, and an occasional incorrect value will not matter. If 45 the ready list at the idle priority contains more than one task 46 then a task other than the idle task is ready to execute. */ 47 if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 ) 48 { 49 taskYIELD(); 检查空闲任务优先级的就绪任务列表,不为空,则进行任务切换。 50 } 51 else 52 { 53 mtCOVERAGE_TEST_MARKER(); 54 } 55 } 56 #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */ 57 58 #if ( configUSE_IDLE_HOOK == 1 ) 59 { 60 extern void vApplicationIdleHook( void ); 61 62 /* Call the user defined function from within the idle task. This 63 allows the application designer to add background functionality 64 without the overhead of a separate task. 65 NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, 66 CALL A FUNCTION THAT MIGHT BLOCK. */ 67 vApplicationIdleHook(); 执行空闲任务钩子函数,钩子函数不能使用任何可以引起阻塞的API. 68 } 69 #endif /* configUSE_IDLE_HOOK */ 70 71 /* This conditional compilation should use inequality to 0, not equality 72 to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when 73 user defined low power mode implementations require 74 configUSE_TICKLESS_IDLE to be set to a value other than 1. */
如果使能了Tickless模式,就执行相关处理代码
 75         #if ( configUSE_TICKLESS_IDLE != 0 )      使能了Tickless模式
 76         {
 77         TickType_t xExpectedIdleTime;
 78 
 79             /* It is not desirable to suspend then resume the scheduler on
 80             each iteration of the idle task.  Therefore, a preliminary
 81             test of the expected idle time is performed without the
 82             scheduler suspended.  The result here is not necessarily
 83             valid. */
 84             xExpectedIdleTime = prvGetExpectedIdleTime();  获取处理器进入低功耗模式的时长,变量ExceptionidleTime单位是时钟节拍数。
 85 
 86             if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) ExcptIdleTime要大于这个宏,原因在后边再说。⭐
 87             {
 88                 vTaskSuspendAll();  相当于临界段代码保护功能。
 89                 {
 90                     /* Now the scheduler is suspended, the expected idle
 91                     time can be sampled again, and this time its value can
 92                     be used. */
调度器已被挂起,重新采集一次时间值,这次的时间值可以使用。 93 configASSERT( xNextTaskUnblockTime >= xTickCount ); 94 xExpectedIdleTime = prvGetExpectedIdleTime(); 重新获取,这次可以直接用于Suppress_Tick_And_Sleep. 95 96 if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) 97 { 98 traceLOW_POWER_IDLE_BEGIN(); 99 portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ); 调用宏,进入低功耗模式。 100 traceLOW_POWER_IDLE_END(); 101 } 102 else 103 { 104 mtCOVERAGE_TEST_MARKER(); 105 } 106 } 107 ( void ) xTaskResumeAll(); 恢复任务调度器 108 } 109 else 110 { 111 mtCOVERAGE_TEST_MARKER(); 112 } 113 } 114 #endif /* configUSE_TICKLESS_IDLE */ 115 } 116 }

留白

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

freeRTOS 低功耗模式 和 空闲任务 的相关文章

  • STM32 USB学习笔记6

    主机环境 xff1a Windows 7 SP1 开发环境 xff1a MDK5 14 目标板 xff1a STM32F103C8T6 开发库 xff1a STM32F1Cube库和STM32 USB Device Library 现在来分
  • Invalid bound statement (not found)

    目录 一 遇到的问题 二 分析思路 1 映射文件 2 测试类 三 解决方案 一 遇到的问题 前几日 xff0c 有个工作不久的同事找我帮他解决一个 Mybatis 的问题 他写了一个增删改查 xff0c 但是在启动程序的时候报错 xff1a
  • ThinkPHP6 解决小程序调用接口返回错误是网页的尴尬

    背景 早在开始了解ThinkPHP时就一直记得一段话 xff1a 在一开始无知的我以为出现错误后能在调试阶段优雅的了解错误信息 xff0c 但结果大家试一下便知道 xff0c 十分尴尬 尤其是当在小程序里请求api xff0c 在过程中发生
  • 大数据技术Canal总结和详细案例

    0 Canal介绍 Canal 是用 Java 开发的基于数据库增量日志解析 xff0c 提供增量数据订阅 amp 消费的中间件 目前Canal 主要支持了 MySQL 的 Binlog 解析 xff0c 解析完成后才利用 Canal Cl
  • T507 Ubuntu18.04 LXDE桌面汉化

    本文硬件平台采用飞凌T507开发板 xff0c 主要讲解Ubuntu图形桌面LXDE如何修改为中文界面 xff0c 本文使用的思路和方法仅供参考使用 xff0c 其它arm开发板虽然芯片不同 xff0c 但思路和方法有很多的共性 xff0c
  • 工作站和台式机的区别

    转自 xff1a 微点阅读 xff08 www weidianyuedu com xff09 微点阅读 范文大全 免费学习网站 工作站电脑非常高配 xff0c 那么它和台式机有什么区别呢 下面由小编给你做出详细的工作站和台式机区别介绍 希望
  • 分布式锁-简单入门

    状态不是很好 xff0c 记一下以前学过的分布式锁吧 样例简介 不谈大概念 xff0c 就是简单入门以及使用 为什么要用分布式锁呢 xff1f 假设我需要一个定时操作 xff0c 每天在某个点 xff0c 我要处理一批数据 xff0c 要先
  • 深度神经网络的成功应用,深度神经网络发展历程

    深度学习与神经网络有什么区别 深度学习与神经网络关系2017 01 10最近开始学习深度学习 xff0c 基本上都是zouxy09博主的文章 xff0c 写的蛮好 xff0c 很全面 xff0c 也会根据自己的思路 xff0c 做下删减 x
  • Ubuntu22.04启用root账户 2208120941

    Ubuntu22 04启用root账户 2208120941 Ubuntu是有root账户的 只是没有密码 所以无法切换 所以启用的方法是 管理员账户用 sudo passwd root 给 root 设置密码 span class tok
  • WARNING: CLOCK SKEW DETECTED. YOUR BUILD MAY BE INCOMPLETE

    make时遇到这个问题几次了 xff0c 就记录一下吧 WARNING CLOCK SKEW DETECTED YOUR BUILD MAY BE INCOMPLETE 警告 xff1a 检测到时钟偏移 您的构建可能不完整 xff08 来自
  • ROS学习(五):构建系统

    ROS的构建系统默认使用CMake xff08 Cross Platform Make xff09 xff0c 其构建环境在功能 包目录中的CMakeLists txt文件中描述 在ROS中 xff0c CMake被修改为适合于ROS的 c
  • ROS学习(七):三维可视化工具(RViz)

    RViz是ROS的三维可视化工具 它的主要目的是以三维方式显示ROS消息 xff0c 可以将 数据进行可视化表达 例如 xff0c 可以无需编程就能表达激光测距仪 xff08 LRF xff09 传感器中的传感 器到障碍物的距离 xff0c
  • 你想拥有开挂的人生吗?

    此前 xff0c 时任百度首席科学家的吴恩达 xff0c 在接受机器之心视频专访时曾提及 xff0c 我想对所有还在考虑自己的职业生涯的年轻人说一句 xff0c 我知道当你很年轻的时候 xff0c 有时候你无法确定该追求怎样的事业 我认为我
  • 【数据处理】R语言--data.table包使用总结

    data table包使用总结 R中的data table包提供了一个data frame的高级版本 xff0c 让你的程序做数据整型的运算速度大大的增加 data table已经在金融 xff0c 基因工程学等领域大放光彩 他尤其适合那些
  • R语言--异常值检测

    自编函数 boxplot 原理 span class hljs title outlier span span class hljs type IQR span lt function x multiple 61 span class hl
  • Python时间序列案例分析实战--奶牛产奶量预测

    Python时间序列实践之奶牛产奶量预测 CSDN xff1a http blog csdn net kicilove article 数据及完整示例代码下载 xff08 https github com zhaohuicici Time
  • Vue 之v-for中获取index(element)

    可以通过事件 xff0c 把item和index传给函数 xff0c 这样就能获取到idenx了 xff0c 这样做的好处是我们定位一个数组元素的时候重名的时候用indexof就会获取到并不一定是我们想要的 xff0c 来看element官
  • jQuery获取更改标签内容、操作标签属性:html()、text()、val()、attr()、prop()

    1 jQuery操作标签里的内容 三种方式 xff1a html xff08 xff09 text xff08 xff09 val xff08 xff09 xff1b html xff08 xff09 xff1a 获取标签里所有的内容 xf
  • AttributeError: '_io.TextIOWrapper' object has no attribute 'xreadlines'

    CSDN xff1a http blog csdn net kicilove article github xff1a https github com zhaohuicici tab 61 repositories 问题 xff1a At
  • 数据科学家应知必会的6种常见概率分布

    前言一常见数据类型 离散型数据连续型数据 二分布类型 伯努利分布Bernoulli Distribution均匀分布Uniform Distribution二项分布Binomial Distribution正态分布Normal Distri

随机推荐

  • Python 面向对象(初级篇)

    转载地址 Python3 面向对象 Python从设计之初就已经是一门面向对象的语言 xff0c 正因为如此 xff0c 在Python中创建一个类和对象是很容易的 本章节我们将详细介绍Python的面向对象编程 如果你以前没有接触过面向对
  • 在hdfs中创建文件夹,出现权限问题

    报错 xff1a hdmkdir Permission denied user 61 WODAS access 61 WRITE inode 61 user yimr yimr supergroup drwxr xr x 尝试1 xff1a
  • pip安装库报错:Could not find a version that satisfies the requirement gesim (from versions

    使用word2vec需要安装gesim库 xff0c 传统方法 pip span class hljs keyword install span gesim 报错 xff1a Could not find a version that sa
  • Spark学习: Spark-Scala-IntelliJ编译Jar包流程

    Spark Scala IntelliJ编译可执行Jar包流程 注意 xff1a 非Maven环境下 xff0c 本篇文章接住上一篇文章使用IntelliJ创建Spark项目 第1步 xff1a 点击File project structu
  • Spark2 Linear Regression线性回归

    本文使用SparkMllib搭建简单的线性回归例子 数据下载地址 package SparkMlibCourse3 import org span class hljs preprocessor apache span span class
  • 惯性测量单元(IMU)系列介绍(1)——加速度计简介

    引言 惯性测量单元 xff08 Inertial Measurement Unit xff0c IMU xff09 主要用来检测和测量加速度 倾斜 冲击 振动 旋转和多自由度运动 通常是指使用加速度计和陀螺仪来测量物体单轴 双轴或三轴姿态角
  • github 添加wiki

    亲们支持我的新博客哦 61 61 地址 xff08 以后更新会尽量在新博客更新 xff0c 欢迎大家访问加入我的后宫w xff09 xff08 欢迎加群一起玩耍 xff1a 575180837 xff09 平时都是写readMe和docs的
  • 2011

    2011 Problem Description The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits G
  • 数据结构与算法1:用JavaScript 实现 栈 结构

    栈 xff08 stack xff09 又名堆栈 xff0c 数据特点 xff1a 后进先出 由于其数据形式特点 xff0c 故用 数组搭建 常用方法有 xff1a push 入栈pop 出栈peek 查看栈顶元素isEmpty 判断是否为
  • 【ROS实践入门(八)ROS使用USB视觉传感器相机】

    ROS实践入门 xff08 八 xff09 ROS使用USB视觉传感器相机 本内容经官网实例以及一些参考书总结而成 xff0c 欢迎留言评论交流 xff5e 联系方式 xff1a ziyuanw foxmail com 换成 64 ROS对
  • 【ROS实践入门(五)自定义节点功能包使用与消息传递】

    ROS实践学习入门系列 xff08 五 xff09 自定义节点功能包使用与消息传递 本内容经官网实例以及一些参考书总结而成 xff0c 欢迎留言评论交流 xff5e 联系方式 xff1a ziyuanw foxmail com 换成 64
  • FreeRTOS学习笔记(二)——FreeRTOS中的任务管理

    目录 FreeRTOS的任务创建和删除创建动态任务创建静态任务和空闲任务FreeRTOS的任务删除 FreeRTOS中的任务状态常用函数 FreeRTOS的延时函数 FreeRTOS的任务创建和删除 创建动态任务 BaseType t sp
  • Docker 产品手册之 Docker Hub - 通过 GitHub 实现自动构建

    原文地址 如果你已经将 Docker Hub 链接到了你的 GitHub 账户 xff0c 直接跳到第五小节 创建自动构建 1 将 Docker Hub 链接到 GitHub 账户 注意 xff1a 因为 Docker Hub 需要设置 G
  • 微信自动回复(python)

    前言 xff1a 自从微信禁止网页版登陆之后 xff0c itchat 库也就都不能用了 xff0c 新号不能再登录网页版 xff0c 而且itchat 库在headers里面只放了UserAgent xff0c 其他字段都没有放 所以在你
  • 国际贸易词汇术语大搜罗

    价格术语 world international market price 国际市场价格 FOB free on board 离岸价 C amp F cost and freight 成本加运费价 CIF cost insurance an
  • 【保姆级教程】Docker服务在双架构(X86和ARM)编译统一实践

    在现代计算机系统中 xff0c X86和ARM64是两种常见的处理器架构 为了满足不同架构的需求 xff0c Docker镜像也需要支持双架构编包形式 本文将介绍Docker镜像双架构编包统一的实践 一 Docker镜像编包 在Docker
  • 玩Raspberrypi走过的坑

    树莓派挺有用 xff0c 基本上linux所有的功能都能用上 xff0c 比如开发个人脸识别 xff0c 搭建一个AVR开发环境 xff0c 都相当的不错 从18年到现在一年多 xff0c 也走过不少的坑 xff0c 希望分享一下自己的经验
  • 小米吉姆尼自动驾驶改造

    最近买了个小米吉姆尼 xff0c 恰好有个wifi图传 xff0c 准备做个无人驾驶 xff0c 通过图像处理实现自动驾驶 图传回到PC处理图像目前已经实现 xff0c 这个比较简单 现在存在的问题如何给吉姆尼发送信号 考虑方式有两种 xf
  • 化骨龙 GPS M80Pro 拆解

    最近玩穿越机 xff0c 用GPS的时候一不小心锡渣把9V和5V短路 xff0c 直接把化骨龙GPS M80Pro烧了血泪教训 这个GPS还是挺精致的 xff0c 于是乎拆解一下分享给大家 拆开来看其实并不复杂 xff0c 核心是一颗Ubl
  • freeRTOS 低功耗模式 和 空闲任务

    低功耗模式 1 芯片原本就支持的硬件低功耗 2 freeRTOS提供的软件低功耗 xff0c Tickless模式 xff01 当用户将宏定义 configUSE TICKLESS IDLE 配置为 1 且系统运行满足以下两个条件时 xff