文章目录
- 任务创建
- 任务的内容
- 推荐阅读
任务创建
prvIdleTask
任务,是由任务调度函数vTaskStartScheduler
创建的,任务优先级0,任务堆栈深度由配置选项configMINIMAL_STACK_SIZE
定义。
void vTaskStartScheduler(void)
{/* 其他代码*//* Add the idle task at the lowest priority. */xReturn = xTaskCreate(prvIdleTask,configIDLE_TASK_NAME,configMINIMAL_STACK_SIZE,(void *) NULL,portPRIVILEGE_BIT, &xIdleTaskHandle); /* 其他代码*/
}
任务的内容
这里贴上源码,然后对源码做解析。
static portTASK_FUNCTION(prvIdleTask, pvParameters)
{/* Stop warnings. */(void) pvParameters;/** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE* SCHEDULER IS STARTED. **//* In case a task that has a secure context deletes itself, in which case* the idle task is responsible for deleting the task's secure context, if* any. */portALLOCATE_SECURE_CONTEXT(configMINIMAL_SECURE_STACK_SIZE);for(; ;){/* See if any tasks have deleted themselves - if so then the idle task* is responsible for freeing the deleted task's TCB and stack. */prvCheckTasksWaitingTermination();#if (configUSE_PREEMPTION == 0){/* If we are not using preemption we keep forcing a task switch to* see if any other task has become available. If we are using* preemption we don't need to do this as any task becoming available* will automatically get the processor anyway. */taskYIELD();}#endif /* configUSE_PREEMPTION */#if ((configUSE_PREEMPTION == 1) && (configIDLE_SHOULD_YIELD == 1)){/* When using preemption tasks of equal priority will be* timesliced. If a task that is sharing the idle priority is ready* to run then the idle task should yield before the end of the* timeslice.** A critical region is not required here as we are just reading from* the list, and an occasional incorrect value will not matter. If* the ready list at the idle priority contains more than one task* then a task other than the idle task is ready to execute. */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);/* Call the user defined function from within the idle task. This* allows the application designer to add background functionality* without the overhead of a separate task.* NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,* CALL A FUNCTION THAT MIGHT BLOCK. */vApplicationIdleHook();}#endif /* configUSE_IDLE_HOOK *//* This conditional compilation should use inequality to 0, not equality* to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when* user defined low power mode implementations require* configUSE_TICKLESS_IDLE to be set to a value other than 1. */#if (configUSE_TICKLESS_IDLE != 0){TickType_t xExpectedIdleTime;/* It is not desirable to suspend then resume the scheduler on* each iteration of the idle task. Therefore, a preliminary* test of the expected idle time is performed without the* scheduler suspended. The result here is not necessarily* valid. */xExpectedIdleTime = prvGetExpectedIdleTime();if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP){vTaskSuspendAll();{/* Now the scheduler is suspended, the expected idle* time can be sampled again, and this time its value can* be used. */configASSERT(xNextTaskUnblockTime >= xTickCount);xExpectedIdleTime = prvGetExpectedIdleTime();/* Define the following macro to set xExpectedIdleTime to 0* if the application does not want* portSUPPRESS_TICKS_AND_SLEEP() to be called. */configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING(xExpectedIdleTime);if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP){traceLOW_POWER_IDLE_BEGIN();portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime);traceLOW_POWER_IDLE_END();}else{mtCOVERAGE_TEST_MARKER();}}(void) xTaskResumeAll();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_TICKLESS_IDLE */}
}
-
portTASK_FUNCTION
是一个宏定义,专门用来定义任务函数,如下图。
-
portALLOCATE_SECURE_CONTEXT(configMINIMAL_SECURE_STACK_SIZE);
portALLOCATE_SECURE_CONTEXT
是一个用于在 FreeRTOS 中分配安全上下文的函数,一般用在使用了TrustZone 技术的系统中。通过分配和初始化安全堆栈,它为非安全任务提供了必要的支持,以便在需要时与安全区域进行交互。TrustZone技术是由ARM公司开发的一种硬件安全技术,旨在通过将处理器和系统资源分为安全和非安全两部分来增强系统的安全性。它广泛应用于需要高安全性的嵌入式系统中,如智能手机、物联网设备和支付系统。
-
prvCheckTasksWaitingTermination();
检查是否有需要释放内存的被删除任务,当有任务调用函数vTaskDelete()
删除自身的话,此任务就会添加到列表xTasksWaitingTermination
中。函数prvCheckTasksWaitingTermination()
会检查列表xTasksWaitingTermination
是否为空,如果不为空的话就依次将列表中所有任务对应的内存释放掉(任务控制块 TCB 和任务堆栈的内存)。 -
使用抢占式内核并且 configIDLE_SHOULD_YIELD 为 1,说明空闲任务需要让出时间片给同优先级的其他就绪任务。
-
检查优先级为
tskIDLE_PRIORITY
(空闲任务优先级)的就绪任务列表是否为空,如果不为空的话就调用函数taskYIELD()
进行一次任务切换。 -
如果使能了空闲任务钩子函数的话就执行这个钩子函数,空闲任务钩子函数的函数名为
vApplicationIdleHook()
,这个函数需要用户自行编写!在编写这个这个钩子函数的时候一定不能调用任何可以阻塞空闲任务的 API 函数。通常,使用这个空闲钩子函数设置CPU进入低功耗模式。 -
剩下的都是Tickless模式的代码
configUSE_TICKLESS_IDLE
不为 0,说明使能了 FreeRTOS 的低功耗 Tickless 模式。一般而言,在RTOS中为了降低功耗。运行空闲任务时需要进入低功耗模式,在处理应用层任务时需要将处理器从低功耗模式中唤醒。一般会在空闲任务的钩子函数中执行低功耗相关处理,比如设置处理器进入低功耗模式、关闭其他外设时钟、降低系统主频等。
但FreeRTOS中的时钟是由滴答定时器(一般频率为1000Hz)来提供的,这会导致当处理器进入低功耗模式时会被滴答定时器中断频繁唤醒,导致低功耗模式作用大大降低。因此引入了Tickless模式——当处理器进入空闲任务周期后,就关闭滴答定时器中断,只有当其他中断发生或者其他任务需要处理的时候,处理器才会被从低功耗模式中唤醒。
但这将面临两个问题: 其一是关闭系统节拍中断会导致系统节拍计数器停止,系统时钟就会停止。 FreeRTOS 的系统时钟是依赖于系统节拍中断(滴答定时器中断)的,如果关闭了系统节拍中断的话就会导致系统时钟停止运行,这是绝对不允许的!解决方法为–记录下系统节拍中断的关闭时间,当系统节拍中断再次开启运行的时候补上这段时间,因此这段时间也称为补偿时间。这时候就需要另外一个定时器来记录这段补偿时间,而F103是继续采用滴答定时器来完成这个功能。
其二是当处理器进入睡眠模式之后,除了及时响应中断外,还需要响应应用层的就绪任务,但是中断可以唤醒睡眠模式,应用层任务却不能主动将处理器从睡眠中唤醒。为了解决上述问题,采用记录处理器在进入低功耗模式之前的待运行任务阻塞时间(也称为低功耗持续时间),FreeRTOS中提供了
prvGetExpectedIdleTime()
函数来获取这段时间。
推荐阅读
空闲任务与钩子函数