本帖最后由 SighingWind 于 2022-7-9 22:26 编辑
这些天在学习STemWin,用的是原子的探索者F407,移植到rt-thread 3.1.3 nano下,跟着原子哥的例程进行学习。
到了后面字体和图像这块,用到了动态内存分配,记得rt-thread也有自己的内存管理函数,于是就仔细研究了一下,现把一些体会写出来,希望高手指正。
rt-thread的片内单块的SRAM的堆管理很简单,是在board.c文件中建立一个数组,把这个数组作为堆空间
#define RT_HEAP_SIZE 1024
static uint32_t rt_heap[RT_HEAP_SIZE]; // heap default size: 4K(1024 * 4)
而我看到rt-thread文档中提到了可以使用memheap方式把多个地址不连续的内存粘合起来作为一个堆进行使用,这个相当具有吸引力。但文档里的介绍很简单,只是一带而过,也没有例程。
根据文档的介绍,在rtconfig.h中加入以下两个开关
#define RT_USING_MEMHEAP #define RT_USING_MEMHEAP_AS_HEAP
RT_USING_MEMHEAP是必须要定义的,定义之后,可以使用rt_memheap_init()函数, 函数原型: rt_err_t rt_memheap_init(struct rt_memheap *memheap, const char *name, void *start_addr, rt_size_t size)
我在board.c中为每个堆定义一个静态全局struct rt_memheap变量,作为其参数之一。 static struct rt_memheap inner_heap; static struct rt_memheap ex_heap; static struct rt_memheap cmm_heap;
然后是名称,地址与大小,地址不可以与系统堆冲突之后进行初始化,返回值res并非必须,只是为了测试方便。 res = rt_memheap_init(&inner_heap, "IRAMHeap", (void*)(0x20000000 + 0x000052d0), 0x00020000 - 0x000052d0); res = rt_memheap_init(&ex_heap, "ERAMHeap", (void*)(0x68000000 + 512*1024), 512*1024); res = rt_memheap_init(&cmm_heap, "CMMHeap", (void*)(0x10000000 + 0x00002000), 0x00010000 - 0x00002000);
初始化的地址信息来自于MAP文件,作为测试,我从CMM区域定义了一个静态数组 (需要在设置里勾选IRAM2,不过如果不勾选,不会报错,而且不会对堆的初始化产生影响。只是MAP文件里不会出现相关的信息,而且我估计如果真要使用这个数组,可能就会报错) static uint8_t CMM_Array[0x2000] __attribute__((at(0x10000000)));
相关MAP文件内容: CMMRAM:定义了一个静态数组 Execution Region RW_IRAM2 (Exec base: 0x10000000, Load base: 0x0804c7b0, Size: 0x00002000, Max: 0x00010000, ABSOLUTE)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object 0x10000000 - 0x00002000 Zero RW 2640 .ARM.__AT_0x10000000 board.o
内部RAM: Execution Region RW_IRAM1 (Exec base: 0x20000000, Load base: 0x0804c2b8, Size: 0x000052d0, Max: 0x00020000, ABSOLUTE, COMPRESSED[0x000004f8])
外部RAM:为emWin分配了512K Load Region LR$$.ARM.__AT_0x68000000 (Base: 0x68000000, Size: 0x00000000, Max: 0x00080000, ABSOLUTE)
Execution Region ER$$.ARM.__AT_0x68000000 (Exec base: 0x68000000, Load base: 0x68000000, Size: 0x00080000, Max: 0x00080000, ABSOLUTE, UNINIT)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x68000000 - 0x00080000 Zero RW 2753 .ARM.__AT_0x68000000 guiconf.o
这样之后就可以直接使用rt_malloc, rt_free进行内存的分配了,rt-thread自己动态建立的各种对象也会不受影响,在我的测试中,只保留了4K的默认堆空间,而分配的任务栈使用了10k,并没有问题。后来甚至在board.c有关系统堆的代码全部注释掉都没有问题。 //#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP) //#define RT_HEAP_SIZE 1024 //static uint32_t rt_heap[RT_HEAP_SIZE]; // heap default size: 4K(1024 * 4)
//RT_WEAK void *rt_heap_begin_get(void) //{ // return rt_heap; //}
//RT_WEAK void *rt_heap_end_get(void) //{ // return rt_heap + RT_HEAP_SIZE; //} //#endif 。。。 //#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP) // rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get()); //#endif
但是,如果只是只是定义了RT_USING_MEMHEAP,而不定义RT_USING_MEMHEAP_AS_HEAP,则无法使用统一的rt-malloc和rt-free,这两个函数只能用于rt_heap[RT_HEAP_SIZE]所定义的系统堆,而且rt_thread动态创建的各对象也只能在这上面创建。而各单独的memheap则必须使用rt_memheap_alloc(struct rt_memheap *heap, rt_size_t size),rt_memheap_free(void *ptr)等系列函数。
虽然可以统一使用rt_malloc进行分配,但并不意味这这些内存块真正连成了一块,能分配的最大内存块只是各组成内存块中最大的一块。另外,分配的优先顺序还没有探究,不过如果要在指定的内存块中进行分配,还是可以使用rt_memheap_alloc()等函数进行分配。
如果需要使用rt_memheap_alloc()等函数,显然,其用到的struct rt_memheap参数必须是全局的,而不是之前的静态的。
另外发现,rt_memheap_init函数并不检查地址的准确性,所以,地址的准确性必须自己来确定。
测试线程(基于rt-thread文档):
void mem_thread_entry(void* parameters)
{
uint32_t i;
char* ptr = RT_NULL;
for (i = 0; ;i++)
{
// ptr = (void*)rt_malloc(1<<i);
ptr = (void*)rt_memheap_alloc(&cmm_heap, 1<<i);
if (ptr != RT_NULL)
{
rt_kprintf("get memory: %d bytes\r\n", (1<<i));
// rt_free(ptr);
rt_memheap_free(ptr);
rt_kprintf("free memory: %d bytes\r\n", (1<<i));
ptr = RT_NULL;
}
else
{
rt_kprintf("try to get %d bytes memory failed!\r\n", (1<<i));
return;
}
}
}
|