空闲任务中的低功耗Tickless处理:
在整个系统运行得过程中,其中大部分时间都是在执行空闲任务的,空闲任务之所以执行,因为在系统中的其他任务处于阻塞或者被挂起时才会执行,因此可以将空闲任务的执行时间转换成低功耗模式,在其他任务解除阻塞而准备运行的时候让MCU退出相应的低功耗模式。难点在于,如何计算MCU进入低功耗模式的时间。
在Freertos中已经解决了这个问题,源码:
#if ( configUSE_TICKLESS_IDLE != 0 )
{
TickType_t xExpectedIdleTime;
/* 计算进入低功耗的时长,不一定准确,会受到任务调度器的影响 */
xExpectedIdleTime = prvGetExpectedIdleTime();
/*如果计算的是时长大于两个时钟节拍,则进去低功耗模式 */
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
vTaskSuspendAll();
{
configASSERT( xNextTaskUnblockTime >= xTickCount );
/* 重新计算进入低功耗的时长 */
xExpectedIdleTime = prvGetExpectedIdleTime();
/* 如果不想进入低功耗模式,将此宏置0. */
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();
}
}
从以上代码可以看出,首先判断进入低功耗模式的时长,当大于两个时钟节拍的时候,进入低功耗模式,进入之后在计算低功耗时长。
低功耗函数:
configPRE_SLEEP_PROCESSING(x)
此函数用于进入低功耗模式前需要执行的事务,如关闭外设时钟等
configPOSR_SLEEP_PROCESSING(x)
在系统退出相应低功耗模式后执行的事务,如开启外设时钟
void freertos_demo(void)
{
xTaskCreate((TaskFunction_t )start_task, /* 任务函数 */
(const char* )"start_task", /* 任务名称 */
(uint16_t )START_STK_SIZE, /* 任务堆栈大小 */
(void* )NULL, /* 传入给任务函数的参数 */
(UBaseType_t )START_TASK_PRIO, /* 任务优先级 */
(TaskHandle_t* )&StartTask_Handler); /* 任务句柄 */
vTaskStartScheduler();
}
/**
* @brief start_task
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建任务1 */
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1Task_Handler);
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/**
* @brief task1
* @param pvParameters : 传入参数(未用到)
* @retval 无
*/
void task1(void *pvParameters)
{
while(1)
{
LED0(1);
delay_ms(3000); /* CPU忙延时,期间不会进入低功耗模式 */
LED0(0);
vTaskDelay(3000); /* 阻塞延时,期间会进入低功耗模式 */
}
}
void PRE_SLEEP_PROCESSING()
{
/* 关闭部分外设时钟,仅作演示 */
__HAL_RCC_GPIOA_CLK_DISABLE();
__HAL_RCC_GPIOB_CLK_DISABLE();
__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();
}
/**
* @brief POST_SLEEP_PROCESSING
* @param 无
* @retval 无
*/
void POST_SLEEP_PROCESSING(void)
{
/* 重新打开部分外设时钟,仅作演示 */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__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();
}