目录
测试环境如下
stm32F103C8T6
MDK keil5
stm32cube + FreeRTOS
基础要求
- 如何构建C项目,包括不同的编译和链接阶段。
- 堆栈和堆是什么?
- 标准的C库malloc()和free()功能。
FreeRTOS 不使用标准库的malloc和free
- 它们并不总是在小型嵌入式系统上可用。
- 它们的实现可以相对较大,占用了宝贵的代码空间。
- 它们很少是线程安全的。
- 它们不是确定性的;执行函数所需的时间将因调用而不同。
- 他们可能遭受碎裂。
- 它们会使链接器配置复杂化。
- 如果允许堆空间成长为其他变量使用的内存,它们可能是难以调试错误的根源。
FreeRTOS 使用自己内存管理
- FreeRTOS使用内存时 调用pvPortMalloc().函数
- 释放内存时 调用 vPortFree().
FreeRTOS提供5个内存案例
Heap_1详解
实现了一个非常基本的pv Port Malloc()版本,而不实现v Port Free()。 从未删除任务或其他内核对象的应用程序有可能使用heap_1。
pvPort Malloc()版本
void * pvPortMalloc( size_t xWantedSize )
{
void * pvReturn = NULL;
static uint8_t * pucAlignedHeap = NULL;
/* Ensure that blocks are always aligned to the required number of bytes. */
#if ( portBYTE_ALIGNMENT != 1 )
{
if( xWantedSize & portBYTE_ALIGNMENT_MASK )
{
/* Byte alignment required. */
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
}
#endif
vTaskSuspendAll();
{
if( pucAlignedHeap == NULL )
{
/* Ensure the heap starts on a correctly aligned boundary. */
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) & ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
}
/* Check there is enough room left for the allocation. */
if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&
( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) ) /* Check for overflow. */
{
/* Return the next free byte then increment the index past this
* block. */
pvReturn = pucAlignedHeap + xNextFreeByte;
xNextFreeByte += xWantedSize;
}
traceMALLOC( pvReturn, xWantedSize );
}
( void ) xTaskResumeAll();
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
}
#endif
return pvReturn;
}
/*-----------------------------------------------------------*/
vPortFree()
void vPortFree( void * pv )
{
/* Memory cannot be freed using this scheme. See heap_2.c, heap_3.c and
* heap_4.c for alternative implementations, and the memory management pages of
* https://www.FreeRTOS.org for more information. */
( void ) pv;
/* Force an assert as it is invalid to call this function. */
configASSERT( pv == NULL );
}
/*-----------------------------------------------------------*/
每个创建的任务都需要一个任务控制块(TCB)和一个从堆中分配的堆栈。 图5演示了heap_1如何在创建任务时细分简单数组。
- 在创建任何任务之前,A显示数组-整个数组是免费的。
- B显示创建一个任务后的数组
- C显示创建了三个任务之后的数组
Heap_2 详解
Heap_2适用于重复创建和删除任务的应用程序,条件是分配给创建任务的堆栈大小不会改变。
- a显示创建了三个任务之后的数组。 一个大的自由块仍然位于数组的顶部。
- B其中一个任务被删除后,B显示数组。 数组顶部的大自由块仍然存在。 现在还有两个较小的自由块,它们以前被分配给TCB和删除任务的堆栈。
- C显示创建另一个任务之后的情况。 创建任务导致了两个对pv端口Malloc()的调用,一个用于分配新的TCB,另一个用于分配任务堆栈。 任务是使用xTaskCreate()API函数创建的,即
-
每个TCB的大小完全相同,因此最佳拟合算法先前分配给删除任务的TCB的RAM块被重用来分配新任务的TCB。
总结: Heap_2不是确定性的,但比malloc()和自由()的大多数标准库实现更快。
Heap_3 详解
Heap_3.c使用标准库malloc()和free()函数,因此堆的大小由linker配置定义,configTOTAL_HEAP_SIZE设置没有影响。
Heap_3通过暂时暂停Free RTOS调度程序,使malloc()和自由()线程安全。 线程安全和调度程序暂停都是第7章资源管理中涉及的主题。
加了分界区
Heap_4 详解
Heap_4使用第一拟合算法来分配内存。 与heap_2不同的是,heap_4将相邻的自由块内存组合成一个更大的块,从而最大限度地减少了内存碎片的风险。
使用场景:
并使其适合于反复分配和释放不同大小的RAM块的应用程序。
- a显示创建了三个任务之后的数组。 一个大的自由块仍然位于数组的顶部。
- B其中一个任务被删除后,B显示数组。 数组顶部的大自由块仍然存在。 还有一个自由块,其中TCB和堆栈的 已删除的任务先前已分配。 请注意,与演示heap_2不同的是,当TCB被删除时释放的内存和当堆栈被删除时释放的内存不作为两个单独的自由块保留,而是组合在一起创建一个更大的单个自由块。
- C创建队列子在原来的基础上C显示创建自由RTOS队列之后的情况。 队列是使用xQueue Create()API函数创建的,这在4.3节中描述。xQueue Create()调用pv端口Malloc()来分配队列使用的RAM。 当heap_4使用第一个FIT算法时,PV端口Malloc()将从第一个足够大以容纳队列的空闲RAM块分配RAM,在图7中,当任务被删除时,RAM被释放。 但是,队列不会消耗空闲块中的所有RAM,因此该块被分割成两个,并且未使用的部分仍然可用于将来对PV端口Malloc()的调用。
- D显示了PV端口Malloc()直接从应用程序代码调用后的情况,而不是通过调用FreeRTOSAPI函数间接调用。 用户分配的块足够小,适合于第一个空闲块,即分配给队列的内存和分配给以下TCB的内存之间的块。 删除任务时释放的内存现在已被分成三个单独的块;第一个块保存队列,第二个块保存用户分配的内存,第三个块保持空闲。
- E。 显示队列被删除后的情况,它自动释放分配给删除队列的内存。 现在用户分配块的两边都有空闲内存
- F F显示用户分配的内存也被释放后的情况。 用户分配的块使用的内存已经与两侧的空闲内存相结合,以创建一个更大的单个空闲块。
Heap_5 详解 好像是将RAM分配给堆
使用内存分配方案
vPortDefineHeapRegions寄存器
typedef struct HeapRegion
{
/* The start address of a block of memory that will be part of the heap.*/
uint8_t *pucStartAddress;
/* The size of the block of memory in byte. */
size_t xSizeInBytes;
} HeapRegion_t;
- 指向HeapRegion_t结构数组开始的指针。 数组中的每个结构都描述了当使用heap_5时将成为堆的一部分的内存区域的起始地址和长度。
显示了一组HeapRegion_t结构,它们一起描述了RAM的三个块。
xPortGetFreeHeapSize() API Function
获取内存大小
内存方案3是无法使用该函数的
x端口获取自由堆大小()API函数返回函数调用时堆中的自由字节数。 可用于优化堆大小。 例如,如果x端口获取自由堆大小()在创建了所有内核对象之后返回2000,那么configTOTAL_HEAP_SIZE的值可以减少2000。
xPortGetMinimumEverFreeHeapSize() API Function
函数返回自Free RTOS应用程序开始执行以来堆中存在的最小未分配字节数。
内存方案4和5才能使用。
v Port Malloc()返回NULL
如果调用pv Port Malloc()返回NULL,则可以将所有示例堆分配方案配置为调用钩子(或回调)函数
如果configUSE_MALLOC_FAILED_HOOK在Free RTOS Config.h中设置为1,那么应用程序必须提供一个malloc失败的钩子函数,该函数具有清单10所示的名称和原型。 该函数可以适合应用程序的任何方式实现。