【LVGL】内存分配管理(与 sct 文件配合管理)

news/2025/11/5 17:37:12/文章来源:https://www.cnblogs.com/Skyrim-sssuuu/p/19194181

引言

LVGL 内存管理,可选默认 lvgl 管理方式,也可选自定义管理方式。

LVGL 内存消耗

这里说的内存管理,就是指“LVGL 要管理的内存”。
这个内存池不能分配的过大,过大则图形缓冲区其他被分配的位置就可能不足;也不能过小,过小则可能分配给某些控件的内存不足。所以需要合理配需要管理的内存池的大小

PixPin_2025-11-05_16-56-23

内存管理文件添加

在工程中选择一个合适的文件夹下添加 malloc.c 和 malloc.h 文件,方便添加管理代码。
由于我使用的 arm 为 stm32h743iit6 型号的单片机,其内存分布如下 sct 文件所示:
其默认未初始化的变量都是放到 AXI SRAM 区的,我们一般也是将 lvgl 需要管理的内存放到这个区域,所以在后面 malloc.c 文件中不需要 _attribute_,直接为默认内存即可。

这个内存管理文件原本是正点原子写来管理整个工程的内存的,但是由于我们已经使用了 sct 静态内存管理的方法,所以这里的内存管理文件仅仅当作 lvgl 的内存管理文件使用了,所以需要初始化的内存区域也只有内部的 SRAM 区,外部 SDRAM 的管理用 sct 文件即可。

PixPin_2025-11-05_17-05-27

malloc.c

点击查看代码
#include "malloc.h"/* 内存池(32字节对齐) */
static __ALIGNED(32) uint8_t mem1base[MEM1_MAX_SIZE]; /* 内部SRAM内存池 */
// static __ALIGNED(32) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((section(".RAM_SDRAM"))); /* 外部SDRAM内存池 *//* 内存管理表 */
static MT_TYPE mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; /* 内部SRAM内存池MAP */
// static MT_TYPE mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((section(".RAM_SDRAM"))); /* 外部SRAM内存池MAP *//* 内存管理参数 */
const uint32_t memtblsize[SRAMBANK] = {MEM1_ALLOC_TABLE_SIZE,// MEM2_ALLOC_TABLE_SIZE
}; /* 内存表大小 */
const uint32_t memblksize[SRAMBANK] = {MEM1_BLOCK_SIZE,// MEM2_BLOCK_SIZE
}; /* 内存分块大小 */
const uint32_t memsize[SRAMBANK] = {MEM1_MAX_SIZE,// MEM2_MAX_SIZE
}; /* 内存总大小 *//* 内存管理控制器 */
struct _m_mallco_dev mallco_dev ={my_mem_init,    /* 内存初始化 */my_mem_perused, /* 内存使用率 *//* 内存池 */mem1base,// mem2base,/* 内存管理状态表 */mem1mapbase,// mem2mapbase,/* 内存管理未就绪 */0,// 0,
};/*** @brief       复制内存* @param       *des : 目的地址* @param       *src : 源地址* @param       n    : 需要复制的内存长度(字节为单位)* @retval      无*/
void my_mem_copy(void *des, void *src, uint32_t n)
{uint8_t *xdes = des;uint8_t *xsrc = src;while (n--)*xdes++ = *xsrc++;
}/*** @brief       设置内存值* @param       *s    : 内存首地址* @param       c     : 要设置的值* @param       count : 需要设置的内存大小(字节为单位)* @retval      无*/
void my_mem_set(void *s, uint8_t c, uint32_t count)
{uint8_t *xs = s;while (count--)*xs++ = c;
}/*** @brief       内存管理初始化* @param       memx : 所属内存块* @retval      无*/
void my_mem_init(uint8_t memx)
{my_mem_set(mallco_dev.memmap[memx], 0, memtblsize[memx] * 4); /* 内存状态表数据清零 */mallco_dev.memrdy[memx] = 1;                                  /* 内存管理初始化OK */
}/*** @brief       获取内存使用率* @param       memx : 所属内存块* @retval      使用率(扩大了10倍,0~1000,代表0.0%~100.0%)*/
uint16_t my_mem_perused(uint8_t memx)
{uint32_t used = 0;uint32_t i;for (i = 0; i < memtblsize[memx]; i++){if (mallco_dev.memmap[memx][i])used++;}return (used * 1000) / (memtblsize[memx]);
}/*** @brief       内存分配(内部调用)* @param       memx : 所属内存块* @param       size : 要分配的内存大小(字节)* @retval      内存偏移地址*   @arg       0 ~ 0xFFFFFFFE : 有效的内存偏移地址*   @arg       0xFFFFFFFF     : 无效的内存偏移地址*/
uint32_t my_mem_malloc(uint8_t memx, uint32_t size)
{signed long offset = 0;uint32_t nmemb;     /* 需要的内存块数 */uint32_t cmemb = 0; /* 连续空内存块数 */uint32_t i;if (!mallco_dev.memrdy[memx]){mallco_dev.init(memx); /* 未初始化,先执行初始化 */}if (size == 0)return 0XFFFFFFFF;           /* 不需要分配 */nmemb = size / memblksize[memx]; /* 获取需要分配的连续内存块数 */if (size % memblksize[memx])nmemb++;for (offset = memtblsize[memx] - 1; offset >= 0; offset--) /* 搜索整个内存控制区 */{if (!mallco_dev.memmap[memx][offset]){cmemb++; /* 连续空内存块数增加 */}else{cmemb = 0; /* 连续内存块清零 */}if (cmemb == nmemb) /* 找到了连续nmemb个空内存块 */{for (i = 0; i < nmemb; i++) /* 标注内存块非空  */{mallco_dev.memmap[memx][offset + i] = nmemb;}return (offset * memblksize[memx]); /* 返回偏移地址  */}}return 0XFFFFFFFF; /* 未找到符合分配条件的内存块 */
}/*** @brief       释放内存(内部调用)* @param       memx   : 所属内存块* @param       offset : 内存地址偏移* @retval      释放结果*   @arg       0, 释放成功;*   @arg       1, 释放失败;*   @arg       2, 超区域了(失败);*/
uint8_t my_mem_free(uint8_t memx, uint32_t offset)
{int i;if (!mallco_dev.memrdy[memx]) /* 未初始化,先执行初始化 */{mallco_dev.init(memx);return 1; /* 未初始化 */}if (offset < memsize[memx]) /* 偏移在内存池内. */{int index = offset / memblksize[memx];      /* 偏移所在内存块号码 */int nmemb = mallco_dev.memmap[memx][index]; /* 内存块数量 */for (i = 0; i < nmemb; i++) /* 内存块清零 */{mallco_dev.memmap[memx][index + i] = 0;}return 0;}else{return 2; /* 偏移超区了. */}
}/*** @brief       释放内存(外部调用)* @param       memx : 所属内存块* @param       ptr  : 内存首地址* @retval      无*/
void myfree(uint8_t memx, void *ptr)
{uint32_t offset;if (ptr == NULL)return; /* 地址为0. */offset = (uint32_t)ptr - (uint32_t)mallco_dev.membase[memx];my_mem_free(memx, offset); /* 释放内存 */
}/*** @brief       分配内存(外部调用)* @param       memx : 所属内存块* @param       size : 要分配的内存大小(字节)* @retval      分配到的内存首地址.*/
void *mymalloc(uint8_t memx, uint32_t size)
{uint32_t offset;offset = my_mem_malloc(memx, size);if (offset == 0xFFFFFFFF) /* 申请出错 */{return NULL; /* 返回空(0) */}else /* 申请没问题, 返回首地址 */{return (void *)((uint32_t)mallco_dev.membase[memx] + offset);}
}/*** @brief       重新分配内存(外部调用)* @param       memx : 所属内存块* @param       *ptr : 旧内存首地址* @param       size : 要分配的内存大小(字节)* @retval      新分配到的内存首地址.*/
void *myrealloc(uint8_t memx, void *ptr, uint32_t size)
{uint32_t offset;offset = my_mem_malloc(memx, size);if (offset == 0xFFFFFFFF) /* 申请出错 */{return NULL; /* 返回空(0) */}else /* 申请没问题, 返回首地址 */{my_mem_copy((void *)((uint32_t)mallco_dev.membase[memx] + offset), ptr, size); /* 拷贝旧内存内容到新内存 */myfree(memx, ptr);                                                             /* 释放旧内存 */return (void *)((uint32_t)mallco_dev.membase[memx] + offset);                  /* 返回新内存首地址 */}
}/*** @brief       分配内存(外部调用)* @param       size : 要分配的内存大小(字节)* @retval      分配到的内存首地址.*/
void *lv_mymalloc(uint32_t size)
{return (void *)mymalloc(SRAM, size);
}/*** @brief       释放内存(外部调用)* @param       ptr  : 内存首地址* @retval      无*/
void lv_myfree(void *ptr)
{myfree(SRAM, ptr);
}/*** @brief       重新分配内存(外部调用)* @param       *ptr : 旧内存首地址* @param       size : 要分配的内存大小(字节)* @retval      新分配到的内存首地址.*/
void *lv_myrealloc(void *ptr, uint32_t size)
{return (void *)myrealloc(SRAM, ptr, size);
}

malloc.h

点击查看代码
#ifndef __MALLOC_H
#define __MALLOC_H#include "headers.h"#ifndef NULL
#define NULL 0
#endif/* 定义三个内存池 */
#define SRAM 0  /* 内部内存池 */
#define SDRAM 1 /* CCM内存池(此部分SRAM仅仅CPU可以访问!!!) *//* 定义内存管理表类型,当外扩SDRAM的时候,必须使用uint32_t类型,否则可以定义成uint16_t,以节省内存占用 */
#define MT_TYPE uint32_t#define SRAMBANK 1 /* 定义支持的SRAM块数. *//* mem1内存参数设定.mem1完全处于内部SRAM里面. */
#define MEM1_BLOCK_SIZE 64                                    /* 内存块大小为 64 字节 */
#define MEM1_MAX_SIZE 200 * 1024                              /* 最大管理内存 512K */
#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE / MEM1_BLOCK_SIZE /* 内存表大小 */// /* mem2内存参数设定.mem2处于CCM,用于管理CCM(特别注意,这部分SRAM,仅CPU可以访问!!) */
// #define MEM2_BLOCK_SIZE 64                                    /* 内存块大小为 64 字节 */
// #define MEM2_MAX_SIZE 60 * 1024                               /* 最大管理内存 32MB */
// #define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE / MEM2_BLOCK_SIZE /* 内存表大小 *//* 内存管理控制器 */
struct _m_mallco_dev
{void (*init)(uint8_t);        /* 初始化 */uint16_t (*perused)(uint8_t); /* 内存使用率 */uint8_t *membase[SRAMBANK];   /* 内存池 管理SRAMBANK个区域的内存 */uint32_t *memmap[SRAMBANK];   /* 内存管理状态表 */uint8_t memrdy[SRAMBANK];     /* 内存管理是否就绪 */
};extern struct _m_mallco_dev mallco_dev; /* 在mallco.c里面定义 */void my_mem_set(void *s, uint8_t c, uint32_t count); /* 设置内存 */
void my_mem_copy(void *des, void *src, uint32_t n);  /* 复制内存 */
void my_mem_init(uint8_t memx);                      /* 内存管理初始化函数(外/内部调用) */
uint32_t my_mem_malloc(uint8_t memx, uint32_t size); /* 内存分配(内部调用) */
uint8_t my_mem_free(uint8_t memx, uint32_t offset);  /* 内存释放(内部调用) */
uint16_t my_mem_perused(uint8_t memx);               /* 获得内存使用率(外/内部调用)  *//* 用户调用函数 */
void myfree(uint8_t memx, void *ptr);                    /* 内存释放(外部调用) */
void *mymalloc(uint8_t memx, uint32_t size);             /* 内存分配(外部调用) */
void *myrealloc(uint8_t memx, void *ptr, uint32_t size); /* 重新分配内存(外部调用) */void *lv_mymalloc(uint32_t size);
void lv_myfree(void *ptr);
void *lv_myrealloc(void *ptr, uint32_t size);
#endif

lv_conf.h 修改

找到MEMORY SETTINGS位置,

  1. LV_MEMCUSTOM宏定义修改为 1,即指定自定义内存分配管理方式
  2. 将下图红框中的 malloc、free、realloc 修改成 malloc.c 文件中的 lv_mymalloc、lv_myfree、lv_myrealloc,并且将应用的头文件改为"malloc.h"

PixPin_2025-11-05_17-21-53

测试

我这里测试的是 music 的 demo,是一个较大的 demo,我们可以通过修改 malloc.h 文件中的内存分配位置(下图所示),来管理分配给 lvgl 管理的内存,将其改为不同大小可以看到不同效果,那么就是管理成功了(由于此 demo 较大,则需要分配较大的内存,例如 200K 才能正常运行)。
PixPin_2025-11-05_17-25-16

main.c 调用

在 main 函数中调用 my_mem_init(SRAM);初始化即可(非必要)。

点击查看代码
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MPU Configuration--------------------------------------------------------*/MPU_Config();/* Enable the CPU Cache *//* Enable I-Cache---------------------------------------------------------*/SCB_EnableICache();/* Enable D-Cache---------------------------------------------------------*/SCB_EnableDCache();/* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_FMC_Init();MX_LTDC_Init();MX_DMA2D_Init();MX_TIM1_Init();/* USER CODE BEGIN 2 *//****************** 初始化打印 ******************/Set_Current_USART(USART1_IDX);   // 设置当前使用的USART为USART1printf("SDRAM 初始化通过!\r\n"); // 打印SDRAM初始化成功信息/****************** 初始化操作 ******************/delay_init(480); // 初始化延时函数,参数为系统时钟频率MHzmy_mem_init(SRAM);//  lcd_init();      // 初始化LCD显示控制器//  gtxxxx_init();   // 初始化触摸屏控制器 GTXXXX/* lvgl */btim_timx_int_init(2400 - 1, 100 - 1); // 基本定时器TIMX定时中断初始化,1ms中断一次lv_init();                             // 初始化LVGL库lv_port_disp_init();                   // 初始化LVGL显示端口lv_port_indev_init();                  // 初始化LVGL输入设备端口// lv_obj_t *switch_obj = lv_switch_create(lv_scr_act()); // 创建一个开关对象// lv_obj_set_size(switch_obj, 120, 60);                  // 设置开关对象的大小为120x60像素// lv_obj_align(switch_obj, LV_ALIGN_CENTER, 0, 0);       // 将开关对象对齐到屏幕中心/* demo */// lv_demo_stress();lv_demo_music();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){// lcd_test();// gtxxxx_scan(); // 触摸屏扫描测试,带坐标返回/* lvgl */delay_ms(5);lv_timer_handler(); /* LVGL任务处理 *//* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

MEM1_MAX_SIZE 修改为 40 * 1024 效果

可以看到,就只有白屏,因为仅仅初始化了 lcd 后,给 lvgl 后续的图片即控件什么的分配的内存不足,就只能显示白屏了。
4a5fcc7dc50d2e3479f7aa6abe229a31

MEM1_MAX_SIZE 修改为 200 * 1024 效果

200KB 分配的内存刚好,正常运行。
40d1d865361493de3721d2e8029d37d7

MEM1_MAX_SIZE 修改为 500 * 1024 效果

某些缓存内存不足,直接在初始化前就进入了内存错误中断,只能看到黑屏,甚至没有背光。
074f99b22ad85d996979945cf69fa6b4

博客导航

博客导航

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/957005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

焊接效率翻倍!焊台工具的性价比黑马!正点原子T300智能焊台160W 大功率 + 四芯兼容!

焊接效率翻倍!焊台工具的性价比黑马!正点原子T300智能焊台160W 大功率 + 四芯兼容! 电子工程师的工作台前,永远缺一台「趁手」的焊台:功率不足焊不动大焊点,换芯麻烦适配性差,温控不准烧穿 PCB 板 — 这些痛点,…

实现 json path 来评估函数式解析器的损耗

目的 之前就考虑评估过 函数式解析器 在dotnet这些面向对象语言上有着一些损耗,虽然表意性很强,与ABNF范式结合使用,维护性大大提升 不过由于性能考虑(以及之前认为也或许没有太多机会实现解析器),就没打算继续深…

2025年无线充电方案厂家新排行榜,稳定无线充电方案公司推荐

2025年智慧科技产业高速迭代,无线充电方案及机器人无线充电方案、稳定无线充电方案等细分领域技术,已成为智能家居、智慧出行、智慧物流、智能制造等场景的核心基础设施,其传输效率、安全稳定性、场景适配性直接决定…

2025年荔枝型PC颗粒板定做厂家权威推荐榜单:钻石型PC颗粒板/耐力板车库出入口/耐力板车棚源头厂家精选

在建筑采光与装饰材料领域,荔枝型PC颗粒板凭借其独特的表面纹理、优异的抗冲击性和耐候性,已成为高端车棚、雨棚及装饰立面的优选材料。行业数据显示,2025年国内PC板材市场规模预计突破200亿元,其中荔枝型等特殊表…

rust学习(六)控制流

学习完了函数,接下来学习控制流。rust的控制流包括if、else-if、else。循环语句包括loop、while、for等 老样子,我们利用caogo新建一个项目cargo new branches cd branchesif 如果我们学习过其他编程语言,if的判断条…

2025 年 11 月衬四氟反应釜,化工反应釜,夹套反应釜厂家最新推荐,聚焦资质、案例、售后的五家机构深度解读!

引言 在化工、医药等行业生产中,衬四氟反应釜、化工反应釜、夹套反应釜作为关键设备,其质量与性能对生产效率、产品品质及安全生产意义重大。为帮助企业精准挑选优质厂家,行业协会联合专业测评机构开展了专项测评,…

2025年升降舞台机械厂家权威推荐榜单:移动舞台机械/舞台机械方案/异形舞台机械源头厂家精选

随着文化演艺产业与公共空间建设的持续发展,升降舞台机械作为现代演艺场所、会议中心及多功能厅的核心设备,其技术升级与市场需求显著增长。该类设备以其高承载性、运行平稳性与灵活的智能控制,在剧院、体育馆、电视…

一文读懂激活函数与损失函数的区别

激活函数(Activation Function)与损失函数(Loss Function)在神经网络中扮演着完全不同的角色,它们位于模型的不同位置,服务于不同的目的。 核心区别总结如下:特性激活函数 (Activation Function)损失函数 (Loss…

2025年河北公司注册代理记账服务权威推荐榜单:河北税务咨询/河北会计税务服务商/河北营业执照年检服务精选

随着河北省营商环境的持续优化与企业数量的稳步增长,公司注册与代理记账服务市场需求显著上升。专业服务机构通过标准化流程、专业化团队与数字化工具,为企业提供从初创到成长的全周期财税支持,在提升合规效率、优化…

为运动注入智能:结合 AI、立体视觉与边缘计算

在工业自动化和机器人技术领域,运动控制一直以精度、速度和可靠性为核心。然而,随着自动化系统越来越多地在非结构化、动态环境中运行,例如有人工操作的工厂、库存布局不断变化的仓库、有机体运动频繁的手术室,一个…

工业自动化通信之西门子CPU连接资源

工业自动化通信之西门子CPU连接资源 下面两张图中,分别为1215和1511的CPU链接资源的截图。 以下整理该页面中各个资源的意义,在平时做项目过程中,常常遇到需要西门子PLC与第三方、西门子HMI、博途WinCC、经典…

AI+ Smali 等于 破解插件

升级完idea 之后 发现插件不对劲了 . 重新下载mybatis类插件 就下载了 MybatisCodeHelperPro 免费功能够用 但是每次提示激活 于是 tg中搜了下 发现居然没有.. 简单的试下破解 插件位置 由于我是mac 电脑 在jetbrains…

Oracle性能优化:latch free-SQL memory manager latch等待

我们的文章会在微信公众号IT民工的龙马人生和博客网站( www.htz.pw )同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢! 由于博客中有大量代码,通过页面浏览效果更佳。Oracle性能优…

2025 年 11 月电能质量分析仪厂家权威推荐榜:A类/B类/动态/三相电能质量监测仪、在线监测装置及系统精选

一、电能质量分析仪行业背景与发展趋势随着现代电力系统的复杂化程度不断提高,电能质量问题日益成为影响供电可靠性和用电设备安全的关键因素。在工业自动化、数据中心、新能源并网等关键领域,电压暂降、谐波污染、频…

2025 年 11 月电气防火保护器厂家推荐排行榜,电弧故障保护器/断路器,防火限流保护器,故障电弧探测器,单相/三相保护装置专业选购指南

行业背景与发展趋势电气防火保护领域作为现代建筑安全体系的重要组成部分,正经历着技术革新与标准升级的双重驱动。随着智能电网建设和电气安全法规的完善,电弧故障保护器、防火限流保护器及故障电弧探测器等设备在预…

如何将一个html以host方式运行跑起来

直接贴代码npx http-server . -p 8084 随后你浏览器直接访问 http://127.0.0.1:8084/test.html 就可以了

2025年pc耐力板定制厂家权威推荐榜单:采光耐力板/pc实心板/pc阻燃板源头厂家精选

随着建筑、装饰及工业领域对高性能材料需求的持续增长,PC耐力板以其优异的抗冲击性、良好的透光性及安全环保特性,在采光顶棚、广告灯箱、安全防护及绿色建筑等场景中的应用日益广泛。本文将基于企业技术实力、生产能…

原型污染攻击工具揭秘:Prototype Pollution Gadgets Finder

本文深入探讨了JavaScript原型污染漏洞的检测与利用方法,重点介绍了Doyensec团队开发的Burpsuite插件,该插件能够自动发现和利用服务器端原型污染gadgets,包含对Axios和Nodemailer等流行库的具体攻击向量分析。引言…

2025 年 11 月箱包五金电镀加工,链条电镀加工,饰品电镀加工厂家最新推荐,产能、专利、环保三维数据透视!

引言 当前箱包五金、链条、饰品电镀加工领域需求持续增长,但行业内厂家实力差异显著,为帮助企业精准筛选优质合作伙伴,专业协会联合检测机构开展了专项测评。测评从产能规模、专利技术、环保合规三大核心维度入手,…

IBM 3650M

IMM2.1 interface You can connect to the IMM2.1 interface via a 10/100Mbps Ethernet port located at the rear of the servers. By default the IMM2.1 web interface is configured with a static IP address. T…