/** * @file fpv_mem.c * @brief FPV 内存管理初始化模块 * @details 本文件负责在系统启动时初始化用户空间的多个专用内存堆: * - av_psram_heap: 从片外 PSRAM 中分配,专用于音视频大数据缓冲 * - av_heap: 从片内 SRAM 中分配,专用于音视频快速处理 * 采用内存池(MPOOL_ALLOC)模式,在编译期确定各堆大小, * 运行时从系统堆中切分出专用区域,以减少内存碎片并提高分配效率。 */ #include "sys_config.h" // 包含系统配置宏定义,如 MPOOL_ALLOC、AV_PSRAM_HEAP 等 #include "basic_include.h" // 包含基础类型定义(uint32 等)和基础宏 #include "osal/string.h" // 包含操作系统抽象层字符串/打印接口(os_printf 等) #include "lib/heap/av_psram_heap.h" // 包含音视频 PSRAM 堆的初始化接口声明 #include "lib/heap/av_heap.h" // 包含音视频 SRAM 堆的初始化接口声明 // 通用用户空间内存初始化 // 通用用户空间内存初始化入口函数 /** * @brief 用户空间内存堆初始化 * @details 在系统启动阶段调用,依次完成以下工作: * 1. 若启用了 PSRAM 音视频堆(AV_PSRAM_HEAP),从片外 PSRAM 分配一大块连续内存, * 作为音视频专用的 psram 堆,支持大容量缓冲(如视频帧缓冲区)。 * 2. 若启用了 SRAM 音视频堆(AV_HEAP),从片内 SRAM 分配一块内存, * 作为音视频专用的 sram 堆,利用 SRAM 的高速访问特性进行音视频编解码处理。 * 为使 av_heap 尽量位于 SRAM 高地址区域(避免与系统堆碎片混合), * 先申请一大块占位空间,再释放该空间。 * @param 无参数 * @return 无返回值 */ void user_heap_init() { _os_printf("\r\n"); // 打印空行,作为内存初始化信息输出的起始分隔 os_printf("--------------------------------------------------------------------\r\n"); // 打印分隔线,便于在串口日志中识别内存初始化区域 /* * ==================== 第一部分:PSRAM 音视频堆初始化 ==================== * 条件编译:仅当同时定义了以下三个宏时才编译此段代码: * MPOOL_ALLOC - 启用内存池分配模式 * AV_PSRAM_HEAP - 启用音视频 PSRAM 堆 * PSRAM_HEAP - 系统支持 PSRAM 堆 * PSRAM(Pseudo-SRAM)是片外扩展内存,容量大但速度比片内 SRAM 慢, * 适合存放视频帧数据等大块缓冲区。 */ #if defined(MPOOL_ALLOC) && defined(AV_PSRAM_HEAP) && defined(PSRAM_HEAP) { // 设置堆的属性标志: // SYSHEAP_FLAGS_MEM_ALIGN_32 - 所有分配的内存按 32 字节对齐, // 适配 DMA 传输和 SIMD 指令的对其要求 // SYSHEAP_FLAGS_MEM_LEAK_TRACE - 启用内存泄漏追踪, // 在调试阶段记录每次分配/释放,便于排查泄漏 uint32 flags = SYSHEAP_FLAGS_MEM_ALIGN_32 | SYSHEAP_FLAGS_MEM_LEAK_TRACE; // 从 PSRAM 系统堆中分配一块大小为 CONFIG_PSRAM_AVHEAP_SIZE 的连续内存, // 用于后续初始化为音视频专用的 PSRAM 堆 void *av_psram_buf = os_malloc_psram(CONFIG_PSRAM_AVHEAP_SIZE); // 保存分配到的 PSRAM 内存块大小,供后续初始化函数和日志打印使用 uint32_t av_psram_size = CONFIG_PSRAM_AVHEAP_SIZE; // 打印 PSRAM 音视频堆的地址范围和大小信息, // 方便开发者在串口日志中确认内存分配是否正确 os_printf("| CPU0 AV_PSRAM_HEAP : %p ~ %p, Size:%-8d |\r\n", av_psram_buf, av_psram_buf+av_psram_size, av_psram_size); // 检查 PSRAM 内存是否分配成功,只有成功时才进行堆初始化 if (av_psram_buf) { // 使用分配到的 PSRAM 内存块和指定的标志位,初始化音视频 PSRAM 堆, // 初始化后可通过 av_psram_malloc() 等接口从此堆中分配内存 av_psram_heap_init((void *) av_psram_buf, av_psram_size, flags); } } #endif /* * ==================== 第二部分:SRAM 音视频堆初始化 ==================== * 条件编译:仅当同时定义了以下两个宏时才编译此段代码: * MPOOL_ALLOC - 启用内存池分配模式 * AV_HEAP - 启用音视频 SRAM 堆 * SRAM 是片内高速内存,容量小但访问速度快, * 适合存放音视频编解码过程中的临时数据结构和控制信息。 * * 关键技巧:为了让 av_heap 尽量位于 SRAM 高地址区域,避免与系统运行时 * 动态分配的碎片化内存混在一起,这里采用"占位-释放"策略: * 1. 先查询 SRAM 系统堆剩余总空间 * 2. 如果剩余空间远大于所需 av_heap 大小,先申请一大块"占位"空间 * 3. 然后再申请 av_heap 所需的空间(此时 av_heap 就被推到了高地址) * 4. 最后释放占位空间,归还给系统堆 */ #if defined(MPOOL_ALLOC) && defined(AV_HEAP) { // 设置 SRAM 音视频堆的属性标志: // SYSHEAP_FLAGS_MEM_ALIGN_32 - 32 字节对齐,满足 DMA 和缓存行对齐要求 // 注意:此处未启用 SYSHEAP_FLAGS_MEM_LEAK_TRACE,因为 SRAM 堆用于高频 // 临时分配,启用追踪会显著影响性能 uint32 flags = SYSHEAP_FLAGS_MEM_ALIGN_32; // 占位空间的指针,初始为 NULL,稍后根据剩余空间情况决定是否分配 void *rev_mem_head = NULL; // 获取配置中定义的音视频 SRAM 堆大小(字节数) uint32_t sram_size = CONFIG_AVHEAP_SIZE; //计算SRAM_HEAP剩余空间 // 查询当前 SRAM 系统堆(sram_heap)中剩余可用的总字节数, // 用于判断是否有足够空间分配 av_heap 以及是否需要占位 uint32_t total_mem_remain_size = sysheap_freesize(&sram_heap); // 如果剩余空间大于 av_heap 所需大小 + 1024 字节(预留安全余量), // 则先分配占位空间,将 av_heap 推向高地址区域 if(total_mem_remain_size > sram_size + 1024) { //申请空间,尽量将av_sram空间放到最后 // 计算占位空间大小 = 剩余总量 - av_heap所需 - 1024(安全余量), // 申请这块空间后,后续 av_heap 的分配就会被挤到 SRAM 的高地址端 rev_mem_head = (void*)os_malloc(total_mem_remain_size - sram_size - 1024); } // 正式分配音视频 SRAM 堆所需的内存块, // 由于前面可能已有占位分配,此块内存将位于 SRAM 较高地址区域 uint8_t *sram_buf = (uint8_t*)os_malloc(sram_size); // 打印 SRAM 音视频堆的地址范围和大小信息 os_printf("| CPU0 AV_HEAP : %p ~ %p, Size:%-8d |\r\n", sram_buf, sram_buf+sram_size, sram_size); // 检查 SRAM 内存是否分配成功,只有成功时才进行堆初始化 if(sram_buf) { // 使用分配到的 SRAM 内存块和指定的标志位,初始化音视频 SRAM 堆, // 初始化后可通过 av_malloc() 等接口从此堆中分配高速内存 av_heap_init((void *) sram_buf, sram_size, flags); } // 如果之前分配了占位空间,现在释放它,归还给 SRAM 系统堆, // 这样系统堆仍可使用这块低地址空间,而 av_heap 已固定在高地址 if(rev_mem_head) { os_free(rev_mem_head); } } #endif os_printf("--------------------------------------------------------------------\r\n"); // 打印分隔线,标记内存初始化信息输出结束 }