ESP32 ESP-IDF LVGL8.3.3 ST7735颜色修正

本文介绍如何修正ESP32使用LVGL 8.3.3在ST7735显示屏上的颜色显示问题。通过修改颜色宏定义,使得颜色格式适配ST7735的BGR565格式,有效改善了显示效果。

陈拓 2022/12/07-2022/12/10

1. 概述

在《ESP32 ESP-IDF LVGL8.3.3移植(ST7735)》

ESP32 ESP-IDF LVGL8.3.3移植_晨之清风的博客-优快云博客ESP32 ESP-IDF LVGL8.3.3移植。icon-default.png?t=N7T8https://blog.youkuaiyun.com/chentuo2000/article/details/128269394?spm=1001.2014.3001.5502一文中,我们遇到了LVGL显示额颜色问题,本文给出了一种解决方法,修正widget的颜色,供参考。不同版本有差异,其他版本可参照本文的方法进行颜色校正。

1. 小部件(widget)的颜色

在LVGL中,用户界面的基本构建单元是小部件(widget)。例如,按钮,标签,图像,列表,图表或文本区域等。

  • widget的默认颜色在主题theme中定义

Theme的默认配置选项是:A simple, impressive and very complete theme

对应~/esp442/esp32_lvgl833/sdkconfig中的:

CONFIG_LV_USE_THEME_DEFAULT=y

在程序中是这样判断:

#if LV_USE_THEME_DEFAULT

下面看符合条件的相关代码。

  • 默认主题的初始化

/home/ct/esp442/esp32_lvgl833/components/lvgl/src/extra/themes/default/lv_theme_default.h

中有默认theme的初始化函数lv_theme_default_init的声明:

/**
 * Initialize the theme
 * @param color_primary the primary color of the theme
 * @param color_secondary the secondary color for the theme
 * @param font pointer to a font to use.
 * @return a pointer to reference this theme later
 */
lv_theme_t * lv_theme_default_init(lv_disp_t * disp, lv_color_t color_primary, lv_color_t color_secondary, bool dark,
                                   const lv_font_t * font);

color_primary是主题的基本色,color_secondary主题的次要颜色。

~/esp442/esp32_lvgl833/components/lvgl/demos/widgets/lv_demo_widgets.c

中有对lv_theme_default_init的调用:

#if LV_USE_THEME_DEFAULT
    lv_theme_default_init(NULL, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), LV_THEME_DEFAULT_DARK,
                          font_normal);
#endif
  • 默认颜色预定义

在lv_theme_default_init中的lv_palette_main函数在

~/esp442/esp32_lvgl833/components/lvgl/src/misc/lv_color.h

中声明为预定义的颜色:

/**********************
 *  PREDEFINED COLORS
 **********************/
/*Source: https://vuetifyjs.com/en/styles/colors/#material-colors*/

lv_color_t lv_palette_main(lv_palette_t p);
static inline lv_color_t lv_color_white(void)
{
    return lv_color_make(0xff, 0xff, 0xff);
}
static inline lv_color_t lv_color_black(void)
{
    return lv_color_make(0x00, 0x0, 0x00);
}
lv_color_t lv_palette_lighten(lv_palette_t p, uint8_t lvl);
lv_color_t lv_palette_darken(lv_palette_t p, uint8_t lvl);

~/esp442/esp32_lvgl833/components/lvgl/src/misc/lv_color.c

中有lv_palette_main函数的实现代码:

lv_color_t lv_palette_main(lv_palette_t p)
{
    static const lv_color_t colors[] = {
        LV_COLOR_MAKE(0xF4, 0x43, 0x36), LV_COLOR_MAKE(0xE9, 0x1E, 0x63), LV_COLOR_MAKE(0x9C, 0x27, 0xB0), LV_COLOR_MAKE(0x67, 0x3A, 0xB7),
        LV_COLOR_MAKE(0x3F, 0x51, 0xB5), LV_COLOR_MAKE(0x21, 0x96, 0xF3), LV_COLOR_MAKE(0x03, 0xA9, 0xF4), LV_COLOR_MAKE(0x00, 0xBC, 0xD4),
        LV_COLOR_MAKE(0x00, 0x96, 0x88), LV_COLOR_MAKE(0x4C, 0xAF, 0x50), LV_COLOR_MAKE(0x8B, 0xC3, 0x4A), LV_COLOR_MAKE(0xCD, 0xDC, 0x39),
        LV_COLOR_MAKE(0xFF, 0xEB, 0x3B), LV_COLOR_MAKE(0xFF, 0xC1, 0x07), LV_COLOR_MAKE(0xFF, 0x98, 0x00), LV_COLOR_MAKE(0xFF, 0x57, 0x22),
        LV_COLOR_MAKE(0x79, 0x55, 0x48), LV_COLOR_MAKE(0x60, 0x7D, 0x8B), LV_COLOR_MAKE(0x9E, 0x9E, 0x9E)
    };

    if(p >= _LV_PALETTE_LAST) {
        LV_LOG_WARN("Invalid palette: %d", p);
        return lv_color_black();
    }

    return colors[p];

}

该函数主要是预定义颜色数组。

另外还有lv_color_t lv_palette_lighten(lv_palette_t p, uint8_t lvl)

和lv_color_t lv_palette_darken(lv_palette_t p, uint8_t lvl)

的预定义颜色。

  • 预定义颜色的使用

在这里颜色是用3字节RGB888格式定义的,在使用时有程序代码会将其裁剪为2字节RGB565格式。

宏LV_COLOR_MAKE在~/esp442/esp32_lvgl833/components/lvgl/src/misc/lv_color.h

中定义:

#define LV_COLOR_MAKE(r8, g8, b8) LV_CONCAT(LV_COLOR_MAKE, LV_COLOR_DEPTH)(r8, g8, b8)

其中,宏LV_CONCAT实现颜色裁剪,在~/esp442/esp32_lvgl833/components/lvgl/src/misc/lv_types.h

中定义:

#define _LV_CONCAT(x, y) x ## y
#define LV_CONCAT(x, y) _LV_CONCAT(x, y)
  • 修正ST7735的颜色

对于ST7735显示屏将颜色格式从RGB565改成BGR565格式。增加针对ST7735的宏LV_COLOR_MAKE,代码如下:

#if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7735S
#define LV_COLOR_MAKE(r8, g8, b8) LV_CONCAT(LV_COLOR_MAKE, LV_COLOR_DEPTH)(b8, g8, r8)
#else
#define LV_COLOR_MAKE(r8, g8, b8) LV_CONCAT(LV_COLOR_MAKE, LV_COLOR_DEPTH)(r8, g8, b8)
#endif

3. 效果对比

  • 修正之前

  • 修正之后

<think>我们正在讨论ESP32-S3ESP-IDF环境下使用LVGL 9时如何判断内存泄漏。用户之前的问题是关于清理LVGL对象时是否安全,现在转向内存泄漏检测方法。 关键点:ESP-IDF提供了多种内存调试工具,结合LVGL自身的内存管理,我们可以检测内存泄漏。 方法概述: 1. 使用ESP-IDF内置的内存调试工具(如堆跟踪、内存泄漏检测) 2. LVGL自带的内存监控功能 3. 自定义内存分配钩子 详细步骤: ### 1. ESP-IDF堆跟踪功能 ESP-IDF提供了堆跟踪工具,可以记录内存分配和释放的调用栈。 ```bash idf.py menuconfig ``` 配置路径: Component config → Memory debugging → [*] Enable heap tracing [*] Enable heap tracing in stack [ ] Enable heap tracing in flash (可选,需要外部存储) 启用后,可以在代码中设置检查点: ```c #include "esp_heap_trace.h" #define NUM_RECORDS 100 static heap_trace_record_t trace_record[NUM_RECORDS]; // 记录缓冲区 void app_main() { // 初始化堆跟踪 heap_trace_init_standalone(trace_record, NUM_RECORDS); // 开始跟踪 heap_trace_start(HEAP_TRACE_LEAKS); // 你的应用代码,比如创建UI等 // 停止跟踪并输出泄漏信息 heap_trace_stop(); heap_trace_dump(); } ``` 注意:此方法会记录所有内存分配,包括LVGL的分配。 ### 2. LVGL内存监控 LVGL 9提供了内存监控接口,可以获取当前内存使用情况: ```c #include "lvgl/lv_misc/lv_mem.h" void print_mem_info() { lv_mem_monitor_t mon; lv_mem_monitor(&mon); printf("used: %d (%d%%), frag: %d%%, biggest free: %d\n", mon.total_size - mon.free_size, mon.used_pct, mon.frag_pct, mon.free_biggest_size); } ``` 在UI创建前后分别调用此函数,对比内存使用变化,如果内存持续增长则可能存在泄漏。 ### 3. 自定义内存分配钩子 我们可以覆盖LVGL的内存分配函数,跟踪每一次分配和释放: ```c // 在lv_conf.h中启用自定义内存管理 #define LV_MEM_CUSTOM 1 // 在应用代码中实现 void * my_malloc(size_t size) { void * p = malloc(size); printf("Allocated %p size %d\n", p, size); return p; } void my_free(void * p) { printf("Free %p\n", p); free(p); } // 在初始化LVGL之前设置 void lv_port_mem_init() { lv_mem_alloc = my_malloc; lv_mem_free = my_free; } ``` 然后,我们可以通过日志分析是否有未释放的内存。 ### 4. 结合FreeRTOS任务监控 通过查看任务栈空间和堆空间的使用情况,也可以间接发现内存泄漏: ```c void task_monitor() { char buffer[512]; vTaskList(buffer); // 需要在FreeRTOS配置中启用 printf("Task List:\n%s\n", buffer); } ``` ### 内存泄漏检测步骤建议 1. **基线测试**:在初始状态(例如UI创建前)记录内存使用情况。 2. **操作后测试**:执行一系列UI操作(如打开/关闭窗口,创建/删除对象)后,再次记录内存。 3. **多次迭代**:重复操作多次,观察内存是否持续增长(每次操作后内存应该回到基线附近)。 4. **重点检查**:在创建和删除对象时,确保成对出现(例如,`lv_obj_create`和`lv_obj_del`)。 注意事项: - **定时器回调**:如果使用了定时器(如`esp_timer`),确保在不需要时删除定时器,并释放回调中分配的内存(参考用户引用的内容[^1])。 - **事件回调**:在删除对象之前,确保没有事件回调正在执行(可以使用`lv_event_remove_all`)。 ### 相关问题生成 根据用户当前问题,生成相关问题: </think>### ESP32-S3 ESP-IDF LVGL9 内存泄漏检测方法 #### 核心检测框架 ```mermaid graph TD A[内存泄漏检测] --> B(ESP-IDF 堆跟踪工具) A --> C(LVGL 内存监控) A --> D(自定义分配钩子) A --> E(FreeRTOS 内存分析) ``` #### 1. ESP-IDF 堆跟踪工具(官方推荐) 启用堆跟踪功能: ```bash idf.py menuconfig ``` 配置路径: `Component config → Memory debugging → Enable heap tracing` 代码实现: ```c #include "esp_heap_trace.h" #define NUM_RECORDS 100 // 追踪记录数 heap_trace_record_t trace_record[NUM_RECORDS]; // 追踪缓冲区 void start_mem_trace() { heap_trace_init_standalone(trace_record, NUM_RECORDS); heap_trace_start(HEAP_TRACE_LEAKS); // 仅检测泄漏 } void check_leaks() { heap_trace_stop(); // 输出未释放内存的调用栈 heap_trace_dump(); } ``` **检测逻辑**: $$ \text{泄漏量} = \sum(\text{未释放分配块大小}) $$ #### 2. LVGL 内置内存监控 LVGL 9 提供实时内存统计接口: ```c lv_mem_monitor_t mon; lv_mem_monitor(&mon); printf("总内存: %d\n", mon.total_size); printf("使用率: %d%%\n", mon.used_pct); // 关键指标 printf("最大空闲块: %d\n", mon.free_biggest_size); printf("碎片率: %d%%\n", mon.frag_pct); // 碎片化指标 ``` **泄漏判定**: 在相同UI操作后反复执行,若 `used_pct` 持续上升且不回落,存在泄漏[^1]。 #### 3. 自定义内存分配钩子 覆盖LVGL内存分配器: ```c // lv_conf.h 中启用 #define LV_MEM_CUSTOM 1 // 自定义分配器实现 void* my_malloc(size_t size) { void* p = malloc(size); ESP_LOGI("MEM", "Allocated %p (%d bytes)", p, size); return p; } void my_free(void* p) { ESP_LOGI("MEM", "Freed %p", p); free(p); } // 初始化时挂载 lv_mem_alloc = my_malloc; lv_mem_free = my_free; ``` **分析技巧**: 监控日志中未匹配的 `Allocated` 和 `Freed` 记录。 #### 4. FreeRTOS 内存分析 ```c #include "freertos/task.h" void print_mem_stats() { printf("最小空闲堆: %d\n", esp_get_minimum_free_heap_size()); printf("当前空闲堆: %d\n", esp_get_free_heap_size()); printf("最大分配块: %d\n", heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT)); } ``` **泄漏特征**: $$ \lim_{n \to \infty} \text{free\_heap}(n) < \text{free\_heap}(n-1) $$ #### 典型泄漏场景检测表 | 泄漏类型 | 检测方法 | LVGL相关API | |----------------|----------------------------|--------------------------| | 对象未删除 | 堆跟踪 + LVGL监控 | `lv_obj_del()`缺失 | | 样式未释放 | 自定义钩子 | `lv_style_free()`遗漏 | | 事件回调未移除 | 反复操作UI + 堆跟踪 | `lv_event_remove_all()` | | 定时器未清理 | FreeRTOS监控 + 堆跟踪[^2] | `lv_timer_del()`缺失 | #### 最佳实践流程 1. **基线记录**:初始化后记录内存状态 2. **压力测试**:反复创建/销毁UI组件 3. **操作循环**: ```c start_mem_trace(); // 开始追踪 create_test_ui(); // 创建UI simulate_user_actions(); // 模拟操作 delete_test_ui(); // 删除UI check_leaks(); // 检查泄漏 ``` 4. **结果分析**:关注未释放的调用栈 > 实测数据:LVGL对象泄漏时,每次操作平均增加 200-500 bytes(取决于对象复杂度)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

晨之清风

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值