LVGL 动态内存使用 CCMRAM

本文介绍了如何在STM32F407VET6上利用CCMRAM作为LVGL图形库的动态内存空间。通过配置链接文件和修改LVGL配置,实现了64K CCMRAM的有效利用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LVGL 动态内存使用 CCMRAM

使用 STM32F407VET6 开发一个项目,使用了 LVGL,然后关注一下 STM32 的 RAM 信息,官方介绍 STM32F407 的 RAM 可用 RAM 是192K,但这 192 K不是连续的,而是分了三块,其中只有两块是连续的,而因为 STM32F4 没有 MMU 这样的内存管理单元,可以将连续的虚拟内核地址翻译到非连续的物理地址,所以我们使用 STM32F407 的 RAM 时必须要注意地址以及内存空间的大小!

翻芯片手册,看一下具体的 RAM 信息:

20220328103326

可以看到 192K 的 RAM 分为 112K 的 SRAM116K 的 SRAM264K 的 CCMRAM

其中的 SRAM1 和 SRAM2 由内核总线经过总线矩阵后进行连接,而CCMRAM则直接与内核总线

总线矩阵,用于建立内核指令总线和外部设备总线的传输连接,同时具备仲裁、同时访问等功能,防止访问冲突,提高访问效率

SRAM1 和 2 两块内存,地址上是连续的,从 0x20000000 开始,我们默认使用的便是这块内存段

CCMRAM 和上面的内存地址是分开的,他的起始地址是 0x10000000,关于地址可以在手册中看到

20220328104357

因为 CCMRAM 没有经过矩阵总线,直接与内核的数据总线连接,所以只能用于进行数据存取,并且访问操作速度极快,效率很高恰好我使用了 LVGL 一个图形框架,所以我准备将这 64K 的内存使用上,作为 LVGL 的动态内存存储空间,使用的是 ARM-GCC 编译器,简单介绍一下这段内存的使用方法,我的开发环境是 VSCode,然后 STM32 外设代码是由 STM32CubeMX 生成的 Makefile 工程,工程帮我生成好了链接文件 STM32F407VETx_FLASH.ld,里面有如下的代码,这是一段关于内存空间的描述,有 64K 的 CCMRAM

20220328110630

同时有一段代码,指定了 ccmram 的起始地址和结尾地址:_sccmram 和 _eccmram,以及字节对齐方式

  /* CCM-RAM section 
  * 
  * IMPORTANT NOTE! 
  * If initialized variables will be placed in this section,
  * the startup code needs to be modified to copy the init-values.  
  */
  .ccmram :
  {
    . = ALIGN(4);
    _sccmram = .;       /* create a global symbol at ccmram start */
    *(.ccmram)
    *(.ccmram*)
    
    . = ALIGN(4);
    _eccmram = .;       /* create a global symbol at ccmram end */
  } >CCMRAM AT> FLASH

所以我们在自己的代码里面使用 CCMRAM 时,指定使用 CCMRAM 就行,比如定义一个变量

__attribute__((section("CCMRAM"))) int one_dat;

但是注意,我们看他代码生成的注释中有提到,如果要将初始化的变量放到 CCMRAM 里面,我们还需要在启动代码里面加上一段初始化代码,将初始化的变量值,从 Flash 复制到 CCMRAM,和复制到 SRAM 一样,这个我后面会做一篇专门的讲解,这里我因为用来存放动态内存,不需要进行初始化

上面写了如何使用 CCMRAM 了,下面分析一下如何将动态内存和 LVGL 关联起来,进入到 lv_conf.h,找到如下内存管理代码:

20220328114334

将 Size 改成 64K,然后将 LV_MEM_ATTR 修改为 CCMRAM,内存起始地址 LV_MEM_ADR 修改为 0x10000000

在任务中添加一个测试指针,在使用 lvgl 分配一段动态内存:

20220328114655

然后仿真,看一下这个内存测试指针的地址,可以看到这段动态内存指向了 0x10000d44 的地址空间,该空间位于 CCMRAM 上

20220328114804

<think>我们正在解决LVGLSTM32F407上链接时由于内存不足导致的错误,具体错误信息为: Error L6406E: No space in execution regions with .ANY selector matching lv_port_disp.o(.bss) L6407E: Sections could not fit 这个错误通常是因为链接器在分配内存时,发现RAM(尤其是.bss段)空间不足。STM32F407的RAM资源有限(192KB SRAM),而LVGL(Light and Versatile Graphics Library)是一个图形库,可能会消耗较多的内存。 ### 解决步骤 #### 1. 分析内存使用情况 - 首先,使用链接器生成的.map文件(通常在工程目录的Output文件夹中)查看内存的详细分配情况。重点关注.bss段(未初始化的全局变量和静态变量)和.data段(已初始化的全局变量和静态变量)的大小。 - 在.map文件中,查找`lv_port_disp.o`模块的.bss段大小,以及其他大内存消耗模块。 #### 2. 优化LVGL配置 LVGL提供了丰富的配置选项,可以通过修改`lv_conf.h`文件来减少内存消耗: - **降低显示分辨率**:如果屏幕分辨率较高,会需要更大的显存(frame buffer)。在`lv_conf.h`中,可以设置`LV_HOR_RES_MAX`和`LV_VER_RES_MAX`为实际屏幕分辨率,不要设置过大。 - **减少颜色深度**:将`LV_COLOR_DEPTH`设置为16(RGB565)而不是32(ARGB8888),可以显著减少显存需求。 - **使用单帧缓冲**:如果使用双缓冲(`LV_DISP_DOUBLE_BUFFER`),则显存需求会翻倍。可以尝试改为单缓冲(设置为0),但可能会引起闪烁。另外,可以考虑使用部分刷新(`LV_DISP_USE_REFRESH_AREAS`)来减少刷新区域。 - **减少内存池大小**:调整`LV_MEM_SIZE`(LVGL动态内存池大小)和`LV_LAYER_MAX_MEM_SIZE`(图层最大内存)等参数,确保它们不过大。 - **减少对象数量**:通过`LV_OBJ_MAX_NUM`限制同时存在的对象数量,但注意这可能影响功能。 #### 3. 优化链接脚本 检查STM32的链接脚本(.ld文件),确保RAM区域划分合理。通常,STM32F407的RAM分为多个块(如112KB的0x20000000和16KB的0x2001C000以及64KB的CCM RAM)。我们可以将部分数据(如显存)分配到CCM RAM(如果可用)中,因为CCM RAM通常只被内核访问,速度较快,且独立于主RAM。 例如,在链接脚本中,可以这样分配: ```ld MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 112K CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K ... } SECTIONS { ... .framebuffer (NOLOAD) : { *(.framebuffer) } >CCMRAM } ``` 然后在代码中,将显存分配到CCM RAM: ```c // 在定义显存时,使用特定的section属性 __attribute__((section(".framebuffer"))) static lv_color_t buf[LV_HOR_RES_MAX * LV_VER_RES_MAX]; ``` #### 4. 减少全局变量 检查代码中是否有不必要的全局变量,特别是大型数组。尽量使用局部变量或动态分配(但注意动态分配也可能导致堆碎片)。 #### 5. 使用外部RAM(如果可用) 如果板子上有外部RAM(如SDRAM),可以通过链接脚本将部分数据(如显存)分配到外部RAM中。但注意外部RAM速度较慢,可能会影响性能。 #### 6. 代码优化 - **静态分配改为动态分配**:对于只在某些函数中使用的大型数组,考虑改为从堆中动态分配,并在使用后释放。 - **使用更小的数据类型**:例如,使用`uint8_t`代替`int`等。 #### 7. 其他注意事项 - 确保编译器优化级别设置合理(如-O2或-Os),以减小代码大小和内存占用。 - 检查是否有多个大数组被同时使用,考虑是否可以复用内存区域。 ### 总结 解决链接错误的关键在于减少RAM使用量,特别是.bss段。通过优化LVGL配置、调整链接脚本分配、减少全局变量等方法,可以释放出足够的RAM空间。 §§相关问题§§ 1. 如何修改STM32的链接脚本以使用CCM RAM? 2. 如何分析.map文件以确定内存占用情况? 3. LVGL的显存(frame buffer)大小如何计算? 4. 除了LVGL配置,还有哪些方法可以减少STM32的内存占用? 5. 如何将变量指定到特定的内存段(如CCM RAM)?</think>### 解决LVGLSTM32F407上内存不足的链接错误 #### 问题分析 错误信息 `L6406E: No space in execution regions` 表明链接器无法为 `.bss` 段中的变量分配足够内存,主要原因是: 1. STM32F407 的 RAM 资源有限(192KB) 2. LVGL 的显存缓冲区(frame buffer)占用过大 3. 全局变量/静态变量过多 4. 链接脚本未优化内存分配 --- ### 解决方案步骤 #### 1. 优化LVGL配置(关键步骤) 修改 `lv_conf.h` 文件: ```c // 降低颜色深度(32位→16位) #define LV_COLOR_DEPTH 16 // 使用单缓冲代替双缓冲 #define LV_DISP_DOUBLE_BUFFER 0 // 减小显存尺寸(根据实际分辨率调整) #define LV_HOR_RES_MAX 320 #define LV_VER_RES_MAX 240 // 缩小内存池 #define LV_MEM_SIZE (32 * 1024) // 32KB ``` #### 2. 修改链接脚本(.ld文件) 在 `STM32F407VGTx_FLASH.ld` 中: ```ld MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K /* 保留64KB给CCM RAM */ CCMRAM (xrw): ORIGIN = 0x10000000, LENGTH = 64K /* 启用CCM RAM */ } SECTIONS { .framebuffer : { *(.frame_buffer) /* 将显存分配到CCM RAM */ } >CCMRAM } ``` #### 3. 将显存分配到CCM RAM 在显示初始化代码中: ```c // 添加section属性指定内存区域 __attribute__((section(".frame_buffer"))) static lv_color_t buf[LV_HOR_RES_MAX * 10]; // 部分缓冲模式 lv_disp_draw_buf_init(&draw_buf, buf, NULL, LV_HOR_RES_MAX * 10); ``` #### 4. 减少全局变量占用 - 检查并移除未使用的全局变量 - 将大型数组改为动态分配(需谨慎管理内存) - 使用 `const` 将常量放入Flash: ```c const uint8_t large_data[] = {...} __attribute__((section(".rodata"))); // 放入Flash ``` #### 5. 启用编译器优化 在IDE(如Keil)中: - 设置优化等级为 `-Os`(空间优化) - 启用链接时优化(LTO) #### 6. 使用部分刷新模式 替代全屏缓冲: ```c // 使用部分缓冲 static lv_disp_draw_buf_t draw_buf; static lv_color_t buf_1[LV_HOR_RES_MAX * 10]; // 10行缓冲 lv_disp_draw_buf_init(&draw_buf, buf_1, NULL, LV_HOR_RES_MAX * 10); ``` --- ### 验证方法 1. 编译后查看.map文件,确认关键段占用: ``` .bss 0x20000000 0x8000 .ccmram 0x10000000 0x2000 /* 显存应在此区域 */ ``` 2. 使用 `arm-none-eabi-size` 工具检查各段大小: ```bash text data bss dec hex filename 123456 7890 34567 166913 28c01 output.elf ``` --- ### 备选方案 如果仍不足: 1. **使用外部RAM**:通过FSMC连接外部SRAM 2. **降低分辨率**:将显示分辨率降至240x135 3. **简化UI**:减少同时显示的控件数量 4. **启用压缩**:使用 `LV_USE_FLEX` 和 `LV_USE_GRID` 减少布局内存 > 注:CCM RAM(64KB)是STM32F4系列独有的核心耦合内存,只能被CPU直接访问(DMA不可用),但访问速度更快,非常适合作为显存缓冲区[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Top嵌入式

投喂个鸡腿

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值