17、TickLess低功耗模式
17.1 TickLess低功耗模式简介
在STM32中,一般支持三种低功耗模式,分别为睡眠模式、停止模式和待机模式,而在FreeRTOS中使用的则主要是睡眠模式,进入睡眠模式可以使用两个指令,WFI和WFE指令,而不同的模式唤醒睡眠模式的方法也不同,如下表所示:
TickLess低功耗模式本质就是通过调用指令WFI实现睡眠模式
为了能够降低功耗,又不影响系统运行,应该怎么办?
可以在本该空闲任务执行的期间,让MCU 进入相应的低功耗模式;当其他任务准备运行的时候,唤醒MCU退出低功耗模式
17.2 TickLess模式相关配置项
configUSE_TICKLESS_IDLE
此宏用于使能低功耗 Tickless 模式
configEXPECTED_IDLE_TIME_BEFORE_SLEEP
此宏用于定义系统进入相应低功耗模式的最短时长
configPRE_SLEEP_PROCESSING(x)
此宏用于定义需要在系统进入低功耗模式前执行的事务,如:进入低功耗前关闭外设时钟,以达到降低功耗的目的
configPOSR_SLEEP_PROCESSING(x)
此宏用于定义需要在系统退出低功耗模式后执行的事务,如:退出低功耗后开启之前关闭的外设时钟,以使系统能够正常运行
17.3 TickLess低功耗模式编程实战
freertos_demo.c:
/**
****************************************************************************************************
* @file freertos.c
* @author 正点原子团队(ALIENTEK)
* @version V1.4
* @date 2022-01-04
* @brief FreeRTOS 移植实验
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 精英F103开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
****************************************************************************************************
*/
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
#include "stm32f1xx_hal.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_STACK_SIZE 128
#define START_TASK_PRIORITY 1
TaskHandle_t Start_Task_Handler;
void start_task( void * pvParameters );
/* Task1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_STACK_SIZE 128
#define TASK1_PRIORITY 2
TaskHandle_t Task1_Handler;
void task1( void * pvParameters );
/* Task2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_STACK_SIZE 128
#define TASK2_PRIORITY 3
TaskHandle_t Task2_Handler;
void task2( void * pvParameters );
/******************************************************************************************************/
QueueHandle_t Semphoer_Handler;
/*进入低功耗前所需要执行的操作*/
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();
}
/*
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
Semphoer_Handler = xSemaphoreCreateBinary();
if(Semphoer_Handler != NULL)
{
printf("二值信号量创建成功!\r\n");
}
else
{
printf("二值信号量创建失败!\r\n");
}
xTaskCreate(( TaskFunction_t ) start_task,
(char * ) "start_task",
(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIORITY,
(TaskHandle_t * ) &Start_Task_Handler );
vTaskStartScheduler();//开启任务调度器
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
/*创建任务1*/
xTaskCreate(( TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,
(void * ) NULL,
( UBaseType_t ) TASK1_PRIORITY,
(TaskHandle_t * ) &Task1_Handler );
/*创建任务2*/
xTaskCreate(( TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,
(void * ) NULL,
( UBaseType_t ) TASK2_PRIORITY,
(TaskHandle_t * ) &Task2_Handler );
vTaskDelete(NULL);//删除开始任务
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/*任务一,释放二值信号量*/
void task1( void * pvParameters )
{
uint8_t Key = 0;
BaseType_t Err;
while(1)
{
Key = key_scan(0);
if( Key == KEY0_PRES )
{
if(Semphoer_Handler != NULL)
{
Err = xSemaphoreGive( Semphoer_Handler );
if(Err == pdPASS)
{
printf("二值信号量释放成功!\r\n");
}
else
{
printf("二值信号量释放失败!\r\n");
}
}
}
vTaskDelay(10);
}
}
/*任务二,获取二值信号量*/
void task2( void * pvParameters )
{
BaseType_t Err;
while(1)
{
Err = xSemaphoreTake(Semphoer_Handler,portMAX_DELAY);
if(Err == pdTRUE)
{
printf("二值信号量获取成功!\r\n");
}
else
{
printf("二值信号量获取失败!\r\n");
}
}
}
freertos_Config.h:
将以下三行代码加入
#include "freertos_demo.h"
#define configPRE_SLEEP_PROCESSING( x ) PRE_SLEEP_PROCESSING();
#define configPOST_SLEEP_PROCESSING( x ) POST_SLEEP_PROCESSING();
18、FreeRTOS内存管理
18.1 FreeRTOS内存管理简介
除了 FreeRTOS 提供的动态内存管理方法,标准的 C 库也提供了函数 malloc()和函数 free()来实现动态地申请和释放内存 。
疑问:为啥不用标准的 C 库自带的内存管理算法?
因为标准 C 库的动态内存管理方法有如下几个缺点:
1、占用大量的代码空间 不适合用在资源紧缺的嵌入式系统中
2、没有线程安全的相关机制
3、运行有不确定性,每次调用这些函数时花费的时间可能都不相同
4、内存碎片化等
因此,FreeRTOS提供了多种动态内存管理的算法,可针对不同的嵌入式系统
FreeRTOS提供了五种动态内存管理算法,如下:
接下来依次讲解这五种算法的功能特点:
heap_1:
heap_1只实现了pvPortMalloc,没有实现vPortFree;也就是说,它只能申请内存,无法释放内存!
heap_1的实现最为简单,管理的内存堆是一个数组,在申请内存的时候, heap_1 内存管理算法只是简单地从数组中分出合适大小的内存,内存堆数组的定义如下所示 :
heap_2:
相比于 heap_1 内存管理算法, heap_2 内存管理算法使用最适应算法,并且支持释放内存;
heap_2 内存管理算法并不能将相邻的空闲内存块合并成一个大的空闲内存块;因此 heap_2 内存管理算法不可避免地会产生内存碎片;
最适应算法:申请满足所需使用的,最小的内存。
什么叫内存碎片?
由于多次申请和释放内存,但释放的内存无法与相邻的空闲内存合并,就导致某些需要大内存的任务无法被分配。
heap_3:
使用C库的malloc和free。
heap_4:
heap_4 内存管理算法使用了首次适应算法,也支持内存的申请与释放,并且能够将空闲且相邻的内存进行合并,从而减少内存碎片的现象。
heap_5:
heap_5 内存管理算法是在 heap_4 内存管理算法的基础上实现的,但是 heap_5 内存管理算法在 heap_4 内存管理算法的基础上实现了管理多个非连续内存区域的能力
18.2 内存管理相关API函数简介
void * pvPortMalloc( size_t xWantedSize );
该函数只有一个形参,即申请的内存大小,以字节为单位。有一个返回值,当申请内存成功时,返回一个指向已分配大小的内存的指针,当申请内存失败时,返回NULL。
void vPortFree( void * pv );
该函数只有一个形参,为一个指针,指向一个要释放内存的内存块。
size_t xPortGetFreeHeapSize( void );
该函数无形参,只有一个返回值,返回当前剩余的空间内存大小。
18.3 内存管理编程实战
/**
****************************************************************************************************
* @file freertos.c
* @author 正点原子团队(ALIENTEK)
* @version V1.4
* @date 2022-01-04
* @brief FreeRTOS 移植实验
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 精英F103开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
****************************************************************************************************
*/
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_STACK_SIZE 128
#define START_TASK_PRIORITY 1
TaskHandle_t Start_Task_Handler;
void start_task( void * pvParameters );
/* Task1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_STACK_SIZE 128
#define TASK1_PRIORITY 2
TaskHandle_t Task1_Handler;
void task1( void * pvParameters );
/******************************************************************************************************/
QueueHandle_t Semphoer_Handler;
/*
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
Semphoer_Handler = xSemaphoreCreateBinary();
if(Semphoer_Handler != NULL)
{
printf("二值信号量创建成功!\r\n");
}
else
{
printf("二值信号量创建失败!\r\n");
}
xTaskCreate(( TaskFunction_t ) start_task,
(char * ) "start_task",
(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIORITY,
(TaskHandle_t * ) &Start_Task_Handler );
vTaskStartScheduler();//开启任务调度器
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
/*创建任务1*/
xTaskCreate(( TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,
(void * ) NULL,
( UBaseType_t ) TASK1_PRIORITY,
(TaskHandle_t * ) &Task1_Handler );
vTaskDelete(NULL);//删除开始任务
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/*任务一,申请内存以及释放内存*/
void task1( void * pvParameters )
{
uint8_t Key = 0,t = 0;
uint8_t *Buf = NULL;
while(1)
{
Key = key_scan(0);
if( Key == KEY0_PRES )
{
Buf = pvPortMalloc( 30 ); /*申请内存*/
if(Buf != NULL)
{
printf("内存申请成功!\r\n");
}
else
{
printf("内存申请失败!\r\n");
}
}
else if( Key == KEY1_PRES )
{
if(Buf != NULL)
{
vPortFree(Buf); /*释放内存*/
printf("释放内存!\r\n");
}
}
if(t++ >= 50)
{
t = 0;
printf("剩余的空闲内存大小为:%d\r\n",xPortGetFreeHeapSize());
}
vTaskDelay(10);
}
}