目录
1.Tickless低功耗模式
1.1 stm32低功耗模式
睡眠模式
停止模式
待机模式
这里我们主要使用的是睡眠模式
进入睡眠模式:
WFI 指令:__WFI
WFE 指令:__WFE
退出睡眠模式:
任何中断或事件都可以唤醒睡眠模式
1.2 Tickless模式
Tickless低功耗模式的本质是通过调用指令 WFI 实现睡眠模式!
任务运行时间统计实验中,可以看出,在整个系统的运行过程中,其实大部分时间是在执行空闲任务的
为了可以降低功耗,又不影响系统运行,该如何做?
可以在本该空闲任务执行的期间,让MCU 进入相应的低功耗模式;当其他任务准备运行的时候,唤醒MCU退出低功耗模式
难点:
1、进入低功耗之后,多久唤醒?也就是下一个要运行的任务如何被准确唤醒
2、任何中断均可唤醒MCU,若滴答定时器频繁中断则会影响低功耗的效果?
将滴答定时器的中断周期修改为低功耗运行时间
退出低功耗后,需补上系统时钟节拍数
不过,FreeRTOS 的低功耗 Tickless 模式机制已经帮我们处理好了这些难点,只需调用即可
在空闲任务中,处理低功耗 Tickless 机制的源码
/*在空闲任务的函数中操作*/
static portTASK_FUNCTION( prvIdleTask, pvParameters )
{
/* 低功耗 Tickless 模式无关代码,代码省略 */
...
...
/* 此宏用于启用 FreeRTOS 低功耗 Tickless 模式 */
#if ( configUSE_TICKLESS_IDLE!= 0 )
{
TickType_t xExpectedIdleTime;
/* 计算进入相应低功耗模式的时长
* 本次计算的结果并不一定准确,
* 因为可能会收到任务调度器的影响
*/
xExpectedIdleTime = prvGetExpectedIdleTime();
/* 如果时长大于 configEXPECTED_IDLE_TIME_BEFORE_SLEEP,才进入相应的低功耗模式 */
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
/* 挂起任务调度器 */
vTaskSuspendAll();
{
configASSERT( xNextTaskUnblockTime >= xTickCount );
/* 重新计算进入相应低功耗模式的时长
* 此时任务调度器已经被挂起,
* 因此本次的计算结果就是 MCU 进入相应低功耗模式的时长
*/
xExpectedIdleTime = prvGetExpectedIdleTime();
/* 如果不希望进入低功耗模式,
* 可以定义此宏将 xExpectedIdleTime 设置为 0
*/
configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime );
/* 如果时长大于 configEXPECTED_IDLE_TIME_BEFORE_SLEEP,
* 才进入相应的低功耗模式
*/
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
/* 用于调试,不用理会 */
traceLOW_POWER_IDLE_BEGIN();
/* 此宏就是用来让 MCU 进入相应的低功耗模式的
* 传入 MCU 需要进入相应低功耗模式的时长
*/
portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
/* 用于调试,不用理会 */
traceLOW_POWER_IDLE_END();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 恢复任务调度器 */
( void ) xTaskResumeAll();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
/* 低功耗 Tickless 模式无关代码,代码省略 */
}
3.Tickless模式相关配置项
configUSE_TICKLESS_IDLE:此宏用于使能低功耗 Tickless 模式
configEXPECTED_IDLE_TIME_BEFORE_SLEEP:此宏用于定义系统进入相应低功耗模式的最短时长
configPRE_SLEEP_PROCESSING(x):此宏用于定义需要在系统进入低功耗模式前执行的事务,如:进入低功耗前关闭外设时钟,以达到降低功耗的目的
configPOST_SLEEP_PROCESSING(x):此宏用于定义需要在系统退出低功耗模式后执行的事务,如:退出低功耗后开启之前关闭的外设时钟,以使系统能够正常运行
注意:configPRE_SLEEP_PROCESSING(x)、configPOST_SLEEP_PROCESSING(x)一般默认没有定义,需要我们人为定义该函数
4.低功耗Tickless模式实验
实验设计:将在原先二值信号量的课堂源码中,加入低功耗模式,最后对比这个两个实验的功耗结果,观察Tickless 模式对于降低功耗是否有用
freertos.c:
/*进入低功耗前需要执行的操作*/
void PRE_SLEEP_PROCESSING(void)
{
__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();
}
/*退出低功耗前需要执行的操作*/
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();
}
...
其他代码省略
...
/*start_task 创建task1和task2任务*/
void start_task(void *pvParameters)
{
/*创建二值信号量*/
Semhore_Handle = xSemaphoreCreateBinary();
if(Semhore_Handle == NULL)
{
printf("二值信号量创建失败\r\n");
}else
{
printf("二值信号量创建成功\r\n");
}
taskENTER_CRITICAL(); /*进入临界区*/
xTaskCreate( (TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
xTaskCreate( (TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handler );
vTaskDelete(start_task_handler);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/*task1 释放二值信号量*/
void task1(void *pvParameters)
{
uint8_t Key = 0;
uint8_t err = 0;
while(1)
{
Key = key_scan(0);
if(Key == KEY2_PRES)
{
if(Semhore_Handle != NULL)
{
err = xSemaphoreGive(Semhore_Handle);
if(err == pdPASS)
{
printf("二值信号量释放成功\r\n");
}else
{
printf("二值信号释放失败\r\n");
}
}
}
}
}
/*task2 获取二值信号量*/
void task2(void *pvParameters)
{
uint8_t err = 0;
uint8_t i = 0;
while(1)
{
err = xSemaphoreTake(Semhore_Handle, portMAX_DELAY);
if(err == pdTRUE)
{
printf("获取信号量成功%d\r\n",++i);
}
}
}
FreeRTOSConfig.h:
#include "freertos_demo.h"
#define configPRE_SLEEP_PROCESSING( x ) PRE_SLEEP_PROCESSING()
#define configPOST_SLEEP_PROCESSING( x ) POST_SLEEP_PROCESSING()
该实验出现的错误:
将:
#define configPRE_SLEEP_PROCESSING( x ) PRE_SLEEP_PROCESSING()
#define configPOST_SLEEP_PROCESSING( x ) POST_SLEEP_PROCESSING()
写成:
#define configPRE_SLEEP_PROCESSING( x ) PRE_SLEEP_PROCESSING( void )
#define configPOST_SLEEP_PROCESSING( x ) POST_SLEEP_PROCESSING( void )
这不符合宏定义的规范