ESP32-C3 上优化该眼睛渲染示例的思路和方法,帮助减少 RAM 占用并提升可用内存空间。主要思路是:

下面给出一些在 ESP32-C3 上优化该眼睛渲染示例的思路和方法,帮助减少 RAM 占用并提升可用内存空间。主要思路是:

  1. 将大数组常量放在 Flash 中,而不占用宝贵的内部 RAM
  2. 缩减或分批处理需要动态分配的大块内存
  3. 剔除不必要的功能或数据
  4. 适当压缩或裁剪贴图

以下是一些具体可行的方向和操作示例:


1. 将大数组/查表存储在 Flash(const 修饰)

目前代码中有很多大的查表或贴图数据(如 sclera_xxx, iris_xxx, polar_xxx, upper_xxx, lower_xxx 等),如果只是只读数据,可以用以下几种方式让编译器将它们放到 Flash(.rodata 段),而不是在启动时拷贝到 RAM:

  • 在数组声明时使用 const 关键字,并确保没有对其进行写操作:

    // eyes_data.h 等
    // 原先可能是:
    // uint16_t sclera_default[SCLERA_SIZE] = { ... };
    //
    // 改为:
    const uint16_t sclera_default[SCLERA_SIZE] = { ... } __attribute__((aligned(4)));
    
  • 如果需要进一步确保数据不会挪到 IRAM,可显式使用宏 ROM_ATTR(ESP-IDF 提供)或 __attribute__((section(".rodata")))

    // 常量数据放在 .rodata 段
    const uint16_t sclera_default[SCLERA_SIZE] ROM_ATTR = { ... };
    
  • 对于一些辅助查表(例如 ease 数组),同理声明为 static const uint8_t ease[] ROM_ATTR = { ... };,以免它占用宝贵的 DRAM。

  • 注意:如果你的数据非常大,检查一下链接脚本是否有分段大小限制;但一般只要是 const 并且不进行写操作,编译器会自动将其放进 Flash,而不会耗费 RAM。

这样做可以显著减少 ESP32-C3 在启动时的静态 RAM 占用。


2. 减少或拆分一次性分配的大块内存

在函数 drawEye() 中,出现了如下批量绘制的逻辑:

#define LINES_PER_BATCH  20

// 分配双缓冲区
lineBuf[0] = (uint16_t*)malloc(LINES_PER_BATCH * SCREEN_WIDTH * sizeof(uint16_t));
lineBuf[1] = (uint16_t*)malloc(LINES_PER_BATCH * SCREEN_WIDTH * sizeof(uint16_t));
  • 问题:一次性分配了 2 块缓存,每块大小约为:
    [
    20 \ (\text{行}) \times 240 \ (\text{宽}) \times 2 \ (\text{字节/像素}) = 9600 \ \text{字节/块}
    ]
    两块就是近 19 KB,对 ESP32-C3 来说较大。

  • 改进

    • 减少 LINES_PER_BATCH,例如调到 85、甚至 1(单行刷新),可以显著降低一次性分配的内存。但同时要接受渲染效率的下降。
    • 只用一个缓冲区:如果帧率需求不高,可以只用 lineBuf[0],每次处理一批行后立即发送到 LCD,然后继续下一批行,不一定非要双缓冲。这样可再减少一半的内存消耗。
    • 如果你的显示驱动支持 DMA 传输,而且必须用双缓冲,可以保留双缓冲但继续减小 LINES_PER_BATCH

示例——改为 单缓冲 + 行刷

#define LINES_PER_BATCH 1 // 每次只刷 1 行

uint16_t* lineBuf = (uint16_t*)malloc(LINES_PER_BATCH * SCREEN_WIDTH * sizeof(uint16_t));
if (lineBuf == NULL) {
    ESP_LOGE(TAG, "drawEye() no enough memory for lineBuf");
    return;
}

for (uint16_t screenY = 0; screenY < SCREEN_HEIGHT; screenY += LINES_PER_BATCH) {
    // 填充 lineBuf[0 ... SCREEN_WIDTH-1]
    // ...
    esp_lcd_safe_draw_bitmap(lcd_panel,
                             0,
                             screenY,
                             SCREEN_WIDTH,
                             screenY + 1, // 只绘制这一行
                             lineBuf);
}

free(lineBuf);

虽然速度会比大批量刷屏慢一些,但内存占用可以减少到 240×2=480 字节(忽略少许循环变量开销),极大节省了 DRAM。


3. 移除不必要的数据/功能

  • 如果你的应用实际只使用 1~2 种虹膜或巩膜,不必把所有 eyes_data_*.h 都编译进来。可以通过 #if / #ifdef 判断,只保留必要资源。
  • 有些场景下,upper_xxxlower_xxx 这些眼睑图片大小也不小,如果某些外观不需要眨眼或不需要复杂眼睑贴图,可考虑用更小分辨率的盖板或简化算法。
  • 如果不使用自动眨眼、跟踪等功能,可注释掉对应的结构体、全局变量、或者把 blink 逻辑放在 #ifdef BLINK_FEATURE 中进行编译控制。
  • 依赖队列 file_queue、任务间通信等,如果暂时用不到也可以精简掉。

4. 对贴图进行压缩或降分辨率

目前的 SCLERA_WIDTH=375SCLERA_HEIGHT=375,而实际显示是 240×240。如果在外部资源中可行,建议将原图降分辨率到与屏幕等大或稍大即可,比如 256×256 或者直接 240×240。这样能显著缩减巩膜数组和虹膜数组的大小。

同时,若能接受一定失真或多次实时解码,也可以把这些数组改为 RLE、Run-length、或其他轻量级压缩方式存放到 Flash,运行时用小的解码缓冲区进行动态解压再渲染。但实现逻辑会复杂一些。


5. 其它可选优化

  1. ease[] 等查表数据改为放在 Flash

    static const uint8_t ease[] ROM_ATTR = { /* ... */ };
    
  2. 在 JPEG/GIF 解码时,使用更小的中间缓冲

    • 某些 JPEG 解码库支持“区域解码”或回调模式,可以边解码边输出到 LCD,免得在内存里维护整张图片。
    • 如果仅需要 240×240 的显示,也可以限制最大解码尺寸,减小解码时所需缓冲。
  3. 启用 ESP-IDF 的内存堆统计和分析

    • menuconfig 里打开 Heap Memory Debugging 等特性,可以帮助定位最大内存占用点,针对性优化。
  4. 减小任务栈

    • 如果默认任务栈较大(比如 4KB 或更多),在 menuconfig 里也可以酌情减小,但要确保不会出现栈溢出。
  5. 充分利用 CONFIG_ESP_SYSTEM_PSRAM_SUPPORT(若有)

    • 如果你的硬件使用了支持 PSRAM 的 ESP32(非C3),可使用 heap_caps_malloc(MALLOC_CAP_SPIRAM, size) 在 PSRAM 分配大缓冲。
    • 但 ESP32-C3 大多数模块没有 PSRAM,所以这一条可能不适用。

在这里插入图片描述

总结

  • 将大数组声明为 const 并放到 Flash:这是最关键的一点,可最大限度缩小静态 RAM 占用。
  • 减小或拆分动态分配缓冲:使用单行刷屏或更小的 LINES_PER_BATCH
  • 减少/精简不需要的资源:只保留真正用到的贴图或功能。
  • 根据需求对图像做降分辨率或轻量压缩:减少静态数据体积。

按照以上思路改进后,基本可以在仅 400 KB ~ 512 KB(具体看芯片和分区)的可用内存空间内运行出较流畅的眼睛动画。希望这些建议能帮助你在 ESP32-C3 上节省内存、稳定运行。祝开发顺利!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值