【FreeRTOS操作系统教程】第28章 FreeRTOS动态内存管理

标签:
fatfsfreertosstemwinstm32usb |
分类: FreeRTOS |
第28章
FreeRTOS 动态内存管理
本章节为大家讲解FreeRTOS动态内存管理,动态内存管理是FreeRTOS非常重要的一项功能,前面章节讲解的任务创建、信号量、消息队列、事件标志组、互斥信号量、软件定时器组等需要的RAM空间都是通过动态内存管理从FreeRTOSConfig.h文件定义的heap空间中申请的。
本章节整理的部分内容整理自官网地址:http://www.freertos.org/a00111.html。
本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及F429。
28.1 动态内存管理介绍
28.2 动态内存和静态内存比较
28.3 动态内存API函数
28.4 实验例程说明
28.5
28.1 动态内存管理介绍
FreeRTOS支持5种动态内存管理方案,分别通过文件heap_1,heap_2,heap_3,heap_4和heap_5实现,这5个文件在FreeRTOS软件包中的路径是:FreeRTOS\Source\portable\MemMang。用户创建的FreeRTOS工程项目仅需要5种方式中的一种。
下面将这5种动态内存管理方式分别进行讲解。
28.1.1 动态内存管理方式一heap_1
heap_1动态内存管理方式是五种动态内存管理方式中最简单的,这种方式的动态内存管理一旦申请了相应内存后,是不允许被释放的。尽管如此,这种方式的动态内存管理还是满足大部分嵌入式应用的,因为这种嵌入式应用在系统启动阶段就完成了任务创建、事件标志组、信号量、消息队列等资源的创建,而且这些资源是整个嵌入式应用过程中一直要使用的,所以也就不需要删除,即释放内存。FreeRTOS的动态内存大小在FreeRTOSConfig.h文件中进行了定义:
#define
configTOTAL_HEAP_SIZE
用户通过函数xPortGetFreeHeapSize就能获得FreeRTOS动态内存的剩余,进而可以根据剩余情况优化动态内存的大小。heap_1方式的动态内存管理有以下特点:
1、项目应用不需要删除任务、信号量、消息队列等已经创建的资源。
2、具有时间确定性,即申请动态内存的时间是固定的并且不会产生内存碎片。
3、确切的说这是一种静态内存分配,因为申请的内存是不允许被释放掉的。
28.1.2 动态内存管理方式二heap_2
与heap_1动态内存管理方式不同,heap_2动态内存管理利用了最适应算法,并且支持内存释放。但是heap_2不支持内存碎片整理,动态内存管理方式四heap_4支持内存碎片整理。FreeRTOS的动态内存大小在FreeRTOSConfig.h文件中进行了定义:
#define
configTOTAL_HEAP_SIZE
用户通过函数xPortGetFreeHeapSize就能获得FreeRTOS动态内存的剩余,但是不提供动态内存是如何被分配成各个小内存块的信息。另外,就是用户可以根据剩余情况优化动态内存的大小。heap_2方式的动态内存管理有以下特点:
1、不考虑内存碎片的情况下,这种方式支持重复的任务、信号量、事件标志组、软件定时器等内部资源的创建和删除。
2、如果用户申请和释放的动态内存大小是随机的,不建议采用这种动态内存管理方式,比如:
3、如果用户随机的创建和删除任务、消息队列、事件标志组、信号量等内部资源也容易出现内存碎片。
4、heap_2方式实现的动态内存申请不具有时间确定性,但是比C库中的malloc函数效率要高。
大部分需要动态内存申请和释放的小型实时系统项目可以使用heap_2。如果需要内存碎片的回收机制可以使用heap_4。
28.1.3动态内存管理方式三heap_3
这种方式实现的动态内存管理是对编译器提供的malloc和free函数进行了封装,保证是线程安全的。
heap_3方式的动态内存管理有以下特点:
1、需要编译器提供malloc和free函数。
2、不具有时间确定性,即申请动态内存的时间不是固定的。
3、增加RTOS内核的代码量。
另外要特别注意一点,这种方式的动态内存申请和释放不是用的FreeRTOSConfig.h文件中定义的heap空间大小,而是用的编译器设置的heap空间大小或者说STM32启动代码中设置的heap空间大小,比如MDK版本的STM32F103工程中heap大小就是在这里进行的定义:
28.1.4动态内存管理方式四heap_4
与heap_2动态内存管理方式不同,heap_4动态内存管理利用了最适应算法,且支持内存碎片的回收并将其整理为一个大的内存块。FreeRTOS的动态内存大小在FreeRTOSConfig.h文件中进行了定义:
#define
configTOTAL_HEAP_SIZE
heap_4同时支持将动态内存设置在指定的RAM空间位置。
用户通过函数xPortGetFreeHeapSize就能获得FreeRTOS动态内存的剩余,但是不提供动态内存是如何被分配成各个小内存块的信息。使用函数xPortGetMinimumEverFreeH
1、可以用于需要重复的创建和删任务、信号量、事件标志组、软件定时器等内部资源的场合。
2、随机的调用pvPortMalloc() 和 vPortFree(),且每次申请的大小都不同,也不会像heap_2那样产生很多的内存碎片。
3、不具有时间确定性,即申请动态内存的时间不是确定的,但是比C库中的malloc函数要高效。
heap_4比较实用,本教程配套的所有例子都是用的这种方式的动态内存管理,用户的代码也可以直接调用函数pvPortMalloc() 和 vPortFree()进行动态内存的申请和释放。
28.1.5动态内存管理方式五heap_5
有时候我们希望FreeRTOSConfig.h文件中定义的heap空间可以采用不连续的内存区,比如我们希望可以将其定义在内部SRAM一部分,外部SRAM一部分,此时我们就可以采用heap_5动态内存管理方式。另外,heap_5动态内存管理是在heap_4的基础上实现的。
heap_5动态内存管理是通过函数vPortDefineHeapRegions进行初始化的,也就是说用户在创建任务FreeRTOS的内部资源前要优先级调用这个函数vPortDefineHeapRegions,否则是无法通过函数pvPortMalloc申请到动态内存的。
函数vPortDefineHeapRegions定义不同段的内存空间采用了下面这种结构体:
typedef struct HeapRegion
{
} HeapRegion_t;
比如下面定义了两个内存块:
const HeapRegion_t xHeapRegions[] =
{
};
vPortDefineHeapRegions( xHeapRegions );
定义的时候要注意两个问题,一个是内存段结束时要定义NULL。另一个是内存段的地址是从低地址到高地址排列。
用户通过函数xPortGetFreeHeapSize就能获得FreeRTOS动态内存的剩余,但是不提供动态内存是如何被分配成各个小内存块的信息。使用函数xPortGetMinimumEverFreeH
28.1.6五种动态内存方式总结
五种动态内存管理方式简单总结如下,实际项目中,用户根据需要选择合适的:
(1)heap_1:五种方式里面最简单的,但是申请的内存不允许释放。
(2)heap_2:支持动态内存的申请和释放,但是不支持内存碎片的处理,并将其合并成一个大的内存块。
(3)heap_3:将编译器自带的malloc和free函数进行简单的封装,以支持线程安全,即支持多任务调用。
(4)heap_4:支持动态内存的申请和释放,支持内存碎片处理,支持将动态内存设置在个固定的地址。
(5)heap_5:在heap_4的基础上支持将动态内存设置在不连续的区域上。
28.2动态内存和静态内存比较
静态内存方式是从FreeRTOS的V9.0.0版本才开始有的,而我们本次教程使用的版本是V8.2.3。所以静态内存方式我们暂时不做讲解,等FreeRTOS教程版本升级时再做讲解。关于静态内存方式和动态内存方式的优缺点可以看官方的此贴说明:
http://www.freertos.org/Static_Vs_Dynamic_Memory_Allocation.html
(制作此教程的时候,官方的FreeRTOS V9.0.0正式版本还没有发布,所以采用的是当前最新的V8.2.3)
28.3动态内存API函数
动态内存的API函数在官方的在线版手册上面没有列出,其实使用也比较简单,类似C库的malloc和free函数,具体使用参看下面的实例说明。
28.4实验例程说明
28.4.1STM32F103开发板实验
配套例子:
V4-334_FreeRTOS实验_动态内存管理
实验目的:
1.
2.
实验内容:
1.
2.
3.
FreeRTOS的配置:
FreeRTOSConfig.h文件中的配置如下:
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#endif
#define
configUSE_PREEMPTION
#define
configUSE_IDLE_HOOK
#define
configUSE_TICK_HOOK
#define
configCPU_CLOCK_HZ
#define
configTICK_RATE_HZ
#define
configMAX_PRIORITIES
#define
configMINIMAL_STACK_SIZE
#define
configTOTAL_HEAP_SIZE
#define
configMAX_TASK_NAME_LEN
#define
configUSE_TRACE_FACILITY
#define
configUSE_16_BIT_TICKS
#define
configIDLE_SHOULD_YIELD
#define
configGENERATE_RUN_TIME_STATS
#define
configUSE_STATS_FORMATTING_FUNCTIONS
#define
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
#define
portGET_RUN_TIME_COUNTER_VALUE()
//#define
portALT_GET_RUN_TIME_COUNTER_VALUE
#define
configUSE_CO_ROUTINES
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
#define
INCLUDE_vTaskPrioritySet
#define
INCLUDE_uxTaskPriorityGet
#define
INCLUDE_vTaskDelete
#define
INCLUDE_vTaskCleanUpResources
#define
INCLUDE_vTaskSuspend
#define
INCLUDE_vTaskDelayUntil
#define
INCLUDE_vTaskDelay
#ifdef __NVIC_PRIO_BITS
#else
#endif
#define
configLIBRARY_LOWEST_INTERRUPT_PRIORITY
#define
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
几个重要选项说明:
u
使能抢占式调度器
u
系统主频72MHz。
u
系统时钟节拍1KHz,即1ms。
u
定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
u
定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
u
定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
l
l
l
更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
#define
tskBLOCKED_CHAR
#define
tskREADY_CHAR
#define
tskDELETED_CHAR
#define
tskSUSPENDED_CHAR
程序设计:
u
vTaskUserIF任务
vTaskLED任务
vTaskMsgPro任务 :2048字节
vTaskStart任务
任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
#define
configTOTAL_HEAP_SIZE
u
u
int main(void)
{
}
u
硬件外设的初始化是在bsp.c文件实现:
void bsp_Init(void)
{
}
u
static void AppTaskCreate (void)
{
}
u
static void AppObjCreate (void)
{
}
u
static void vTaskTaskUserIF(void *pvParameters)
{
}
static void vTaskLED(void *pvParameters)
{
}
static void vTaskMsgPro(void *pvParameters)
{
}
static void vTaskStart(void *pvParameters)
{
}