From 700d79e9f9eaab2b38f1b75d1c44e9be0b27ec4e Mon Sep 17 00:00:00 2001 From: zhouli <1932489836@qq.com> Date: Sun, 12 Apr 2026 19:39:40 +0800 Subject: [PATCH] refactor(lua): refine display and heap module APIs Rename the display lifecycle API from create_screen/destroy_screen to init/deinit. Fix display HAL teardown so callbacks and synchronization primitives are released on deinit and can be registered again on the next init. Trim stack sizes, enable periodic task stack logging, and remove obsolete Lua GC/coroutine demos. --- .../fatfs_image/lua/coroutine_demo.lua | 47 ------------------- .../fatfs_image/lua/display_demo.lua | 8 ++-- .../basic_demo/fatfs_image/lua/gc_demo.lua | 33 ------------- .../fatfs_image/lua/gc_userdata_demo.lua | 39 --------------- .../fatfs_image/lua/lcd_touch_paint.lua | 8 ++-- .../fatfs_image/lua/random_demo_stress.lua | 2 +- application/basic_demo/main/basic_demo_cli.c | 2 +- application/basic_demo/main/main.c | 4 +- .../claw_capabilities/cap_time/src/cap_time.c | 2 +- .../skills/lua_module_display.md | 24 +++++----- .../lua_module_display/src/display_hal.c | 42 ++++++++++++++++- .../src/lua_module_display.c | 16 +++---- .../lua_module_esp_heap/lua_module_esp_heap.c | 11 ++--- .../skills/lua_module_esp_heap.md | 4 +- 14 files changed, 79 insertions(+), 163 deletions(-) delete mode 100644 application/basic_demo/fatfs_image/lua/coroutine_demo.lua delete mode 100644 application/basic_demo/fatfs_image/lua/gc_demo.lua delete mode 100644 application/basic_demo/fatfs_image/lua/gc_userdata_demo.lua diff --git a/application/basic_demo/fatfs_image/lua/coroutine_demo.lua b/application/basic_demo/fatfs_image/lua/coroutine_demo.lua deleted file mode 100644 index ec0464f..0000000 --- a/application/basic_demo/fatfs_image/lua/coroutine_demo.lua +++ /dev/null @@ -1,47 +0,0 @@ -local delay = require("delay") - -print("[coroutine_demo] start") - -local co = coroutine.create(function(name) - print("[coroutine_demo] worker enter: " .. tostring(name)) - - for step = 1, 3 do - print(string.format("[coroutine_demo] worker step=%d before yield", step)) - coroutine.yield("yield@" .. tostring(step)) - print(string.format("[coroutine_demo] worker step=%d after resume", step)) - delay.delay_ms(100) - end - - return "done:" .. tostring(name) -end) - -print("[coroutine_demo] initial status: " .. coroutine.status(co)) - -local ok, value = coroutine.resume(co, "demo-task") -print(string.format("[coroutine_demo] resume #1 ok=%s value=%s status=%s", - tostring(ok), tostring(value), coroutine.status(co))) - -ok, value = coroutine.resume(co) -print(string.format("[coroutine_demo] resume #2 ok=%s value=%s status=%s", - tostring(ok), tostring(value), coroutine.status(co))) - -ok, value = coroutine.resume(co) -print(string.format("[coroutine_demo] resume #3 ok=%s value=%s status=%s", - tostring(ok), tostring(value), coroutine.status(co))) - -ok, value = coroutine.resume(co) -print(string.format("[coroutine_demo] resume #4 ok=%s value=%s status=%s", - tostring(ok), tostring(value), coroutine.status(co))) - -local wrapped = coroutine.wrap(function() - for i = 1, 2 do - coroutine.yield("wrap@" .. tostring(i)) - end - return "wrap-done" -end) - -print("[coroutine_demo] wrap call #1: " .. tostring(wrapped())) -print("[coroutine_demo] wrap call #2: " .. tostring(wrapped())) -print("[coroutine_demo] wrap call #3: " .. tostring(wrapped())) - -print("[coroutine_demo] done") diff --git a/application/basic_demo/fatfs_image/lua/display_demo.lua b/application/basic_demo/fatfs_image/lua/display_demo.lua index 786b8da..066d08a 100644 --- a/application/basic_demo/fatfs_image/lua/display_demo.lua +++ b/application/basic_demo/fatfs_image/lua/display_demo.lua @@ -34,9 +34,9 @@ if not panel_handle then return end -local ok, err = pcall(display.create_screen, panel_handle, io_handle, width, height) +local ok, err = pcall(display.init, panel_handle, io_handle, width, height) if not ok then - print("[display_demo] ERROR: create_screen failed: " .. tostring(err)) + print("[display_demo] ERROR: init failed: " .. tostring(err)) return end @@ -45,7 +45,7 @@ local screen_created = true local function cleanup() if screen_created then pcall(display.end_frame) - pcall(display.destroy_screen) + pcall(display.deinit) screen_created = false end end @@ -54,7 +54,7 @@ width = display.width() height = display.height() if width <= 0 or height <= 0 then - print("[display_demo] ERROR: invalid display size after create_screen") + print("[display_demo] ERROR: invalid display size after init") cleanup() return end diff --git a/application/basic_demo/fatfs_image/lua/gc_demo.lua b/application/basic_demo/fatfs_image/lua/gc_demo.lua deleted file mode 100644 index 4da00aa..0000000 --- a/application/basic_demo/fatfs_image/lua/gc_demo.lua +++ /dev/null @@ -1,33 +0,0 @@ -local function kb() - return collectgarbage("count") -end - -local function print_kb(label) - print(string.format("[gc_demo] %s: %.2f KB", label, kb())) -end - -print("[gc_demo] start") -print_kb("initial") - -local blocks = {} -for i = 1, 400 do - local t = {} - for j = 1, 64 do - t[j] = string.format("block=%d item=%d", i, j) - end - blocks[i] = t -end - -print_kb("after allocation") - -blocks = nil -print_kb("after release refs") - -local step_done = collectgarbage("step", 64) -print(string.format("[gc_demo] step result: %s", tostring(step_done))) -print_kb("after one step") - -collectgarbage("collect") -print_kb("after full collect") - -print("[gc_demo] done") diff --git a/application/basic_demo/fatfs_image/lua/gc_userdata_demo.lua b/application/basic_demo/fatfs_image/lua/gc_userdata_demo.lua deleted file mode 100644 index bf68614..0000000 --- a/application/basic_demo/fatfs_image/lua/gc_userdata_demo.lua +++ /dev/null @@ -1,39 +0,0 @@ -local btn = require("button") - -local gpio_num = (args and args.gpio_num) or 0 -local rounds = (args and args.rounds) or 12 - -print(string.format( - "[gc_userdata_demo] start gpio=%d rounds=%d", - gpio_num, - rounds -)) - -local function make_and_drop_button(index) - local handle, err = btn.new(gpio_num, 0) - if not handle then - error(string.format("button.new failed at round %d: %s", index, tostring(err))) - end - - print(string.format("[gc_userdata_demo] round=%d created handle=%s", index, tostring(handle))) - - -- Drop the only Lua reference on purpose. The userdata __gc should release - -- the native button handle so repeated creation does not exhaust BTN_MAX_HANDLES. - handle = nil - collectgarbage("collect") - collectgarbage("collect") -end - -for i = 1, rounds do - make_and_drop_button(i) -end - -local final_handle, final_err = btn.new(gpio_num, 0) -if not final_handle then - error("[gc_userdata_demo] final create failed after GC cycling: " .. tostring(final_err)) -end - -print("[gc_userdata_demo] final create succeeded after repeated GC") -btn.close(final_handle) -collectgarbage("collect") -print("[gc_userdata_demo] done") diff --git a/application/basic_demo/fatfs_image/lua/lcd_touch_paint.lua b/application/basic_demo/fatfs_image/lua/lcd_touch_paint.lua index a20f04d..b243e91 100644 --- a/application/basic_demo/fatfs_image/lua/lcd_touch_paint.lua +++ b/application/basic_demo/fatfs_image/lua/lcd_touch_paint.lua @@ -9,9 +9,9 @@ if not panel_handle then return end -local ok, err = pcall(display.create_screen, panel_handle, io_handle, width, height) +local ok, err = pcall(display.init, panel_handle, io_handle, width, height) if not ok then - print("[lcd_touch_paint] ERROR: create_screen failed: " .. tostring(err)) + print("[lcd_touch_paint] ERROR: init failed: " .. tostring(err)) return end @@ -20,7 +20,7 @@ local screen_created = true local function cleanup() if screen_created then pcall(display.end_frame) - pcall(display.destroy_screen) + pcall(display.deinit) screen_created = false end end @@ -29,7 +29,7 @@ width = display.width() height = display.height() if width <= 0 or height <= 0 then - print("[lcd_touch_paint] ERROR: invalid display size after create_screen") + print("[lcd_touch_paint] ERROR: invalid display size after init") cleanup() return end diff --git a/application/basic_demo/fatfs_image/lua/random_demo_stress.lua b/application/basic_demo/fatfs_image/lua/random_demo_stress.lua index bcf8ace..c460496 100644 --- a/application/basic_demo/fatfs_image/lua/random_demo_stress.lua +++ b/application/basic_demo/fatfs_image/lua/random_demo_stress.lua @@ -2,7 +2,7 @@ local delay = require("delay") local esp_heap = require("esp_heap") local LUA_ROOT = "/fatfs/data/lua" -local caps = esp_heap.caps() +local caps = esp_heap.caps local iterations = (args and args.iterations) or 20 local pause_ms = (args and args.pause_ms) or 200 diff --git a/application/basic_demo/main/basic_demo_cli.c b/application/basic_demo/main/basic_demo_cli.c index 246d773..9fbcf32 100644 --- a/application/basic_demo/main/basic_demo_cli.c +++ b/application/basic_demo/main/basic_demo_cli.c @@ -668,7 +668,7 @@ esp_err_t basic_demo_cli_start(void) ESP_LOGI(TAG, "Starting console REPL"); repl_config.prompt = "basic_demo> "; - repl_config.task_stack_size = 8192; + repl_config.task_stack_size = 4096; repl_config.max_cmdline_length = 512; #if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM diff --git a/application/basic_demo/main/main.c b/application/basic_demo/main/main.c index a355e7a..0f761b1 100644 --- a/application/basic_demo/main/main.c +++ b/application/basic_demo/main/main.c @@ -97,7 +97,7 @@ static void memory_monitor_task(void *arg) size_t psram_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); ESP_LOGI(TAG, "Memory: internal_free=%u bytes, internal_min_free=%u bytes, psram_free=%u bytes", (unsigned)internal_free, (unsigned)internal_min, (unsigned)psram_free); - // print_task_stack_info(); + print_task_stack_info(); } } @@ -131,6 +131,6 @@ void app_main(void) #if BASIC_DEMO_ENABLE_MEM_LOG /* Start memory monitor: print internal free, min free, PSRAM free every 20s */ - xTaskCreate(memory_monitor_task, "mem_mon", 8192, NULL, 1, NULL); + xTaskCreate(memory_monitor_task, "mem_mon", 4096, NULL, 1, NULL); #endif } diff --git a/components/claw_capabilities/cap_time/src/cap_time.c b/components/claw_capabilities/cap_time/src/cap_time.c index 3c77710..9bd1f0a 100644 --- a/components/claw_capabilities/cap_time/src/cap_time.c +++ b/components/claw_capabilities/cap_time/src/cap_time.c @@ -28,7 +28,7 @@ static const char *TAG = "cap_time"; #define CAP_TIME_MIN_VALID_EPOCH 1704067200 #define CAP_TIME_DEFAULT_DISCONNECTED_RETRY_MS 5000 #define CAP_TIME_DEFAULT_SYNC_RETRY_MS 30000 -#define CAP_TIME_SYNC_TASK_STACK_SIZE 6144 +#define CAP_TIME_SYNC_TASK_STACK_SIZE 4096 #define CAP_TIME_SYNC_TASK_PRIORITY 5 static const char *s_month_names[] = { diff --git a/components/lua_modules/lua_module_display/skills/lua_module_display.md b/components/lua_modules/lua_module_display/skills/lua_module_display.md index 3f67c72..edfa076 100644 --- a/components/lua_modules/lua_module_display/skills/lua_module_display.md +++ b/components/lua_modules/lua_module_display/skills/lua_module_display.md @@ -3,7 +3,7 @@ This skill describes how to correctly use `display` when writing Lua scripts. `display` is a low-level drawing module. It can: -- Create and destroy the LCD drawing context +- Initialize and deinitialize the LCD drawing context - Draw text, lines, rectangles, circles, arcs, ellipses, triangles, and round rectangles - Draw raw RGB565 bitmaps - Draw JPEG and PNG images from memory or files @@ -20,10 +20,10 @@ local display = require("display") local panel_handle, io_handle, width, height = board_manager.get_display_lcd_params("display_lcd") -display.create_screen(panel_handle, io_handle, width, height) +display.init(panel_handle, io_handle, width, height) ``` -After `display.create_screen(...)` succeeds: +After `display.init(...)` succeeds: - `display.width()` returns the current screen width - `display.height()` returns the current screen height - Most drawing APIs can be used @@ -32,7 +32,7 @@ When finished: ```lua pcall(display.end_frame) -pcall(display.destroy_screen) +pcall(display.deinit) ``` ## Important rules @@ -47,9 +47,9 @@ pcall(display.destroy_screen) ## Screen lifecycle -### `display.create_screen(panel_handle, io_handle, lcd_width, lcd_height)` +### `display.init(panel_handle, io_handle, lcd_width, lcd_height)` -Creates the drawing context. +Initializes the drawing context. - `panel_handle`: lightuserdata, usually from `board_manager.get_display_lcd_params(...)` - `io_handle`: lightuserdata @@ -58,9 +58,9 @@ Creates the drawing context. - Returns `true` on success - Raises a Lua error on failure -### `display.destroy_screen()` +### `display.deinit()` -Destroys the drawing context. +Deinitializes the drawing context. - Returns `true` on success - Raises a Lua error on failure @@ -390,12 +390,12 @@ Returns: For normal screen rendering: 1. Use `board_manager.get_display_lcd_params("display_lcd")` -2. Call `display.create_screen(...)` +2. Call `display.init(...)` 3. Call `display.begin_frame(...)` 4. Draw text, shapes, or images 5. Call `display.present()` or `display.present_rect(...)` 6. Call `display.end_frame()` -7. Call `display.destroy_screen()` before exit +7. Call `display.deinit()` before exit ## Example @@ -406,7 +406,7 @@ local display = require("display") local panel_handle, io_handle, width, height = bm.get_display_lcd_params("display_lcd") -display.create_screen(panel_handle, io_handle, width, height) +display.init(panel_handle, io_handle, width, height) display.begin_frame({ clear = true, r = 12, g = 18, b = 28 }) @@ -429,5 +429,5 @@ display.draw_text_aligned(0, display.height() - 24, display.width(), 20, "frame display.present() display.end_frame() -display.destroy_screen() +display.deinit() ``` diff --git a/components/lua_modules/lua_module_display/src/display_hal.c b/components/lua_modules/lua_module_display/src/display_hal.c index e841d01..f6936b5 100644 --- a/components/lua_modules/lua_module_display/src/display_hal.c +++ b/components/lua_modules/lua_module_display/src/display_hal.c @@ -57,6 +57,7 @@ typedef struct { static display_hal_state_t s_state; static void display_hal_clear_clip_locked(void); +static esp_err_t display_hal_clear_io_callbacks_locked(void); static bool display_hal_flush_done_isr(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx); @@ -151,6 +152,8 @@ fail: esp_err_t display_hal_destroy(void) { esp_err_t ret = display_hal_lock(); + SemaphoreHandle_t flush_done_to_delete = NULL; + SemaphoreHandle_t lock_to_delete = NULL; if (ret != ESP_OK) { return ret; @@ -164,6 +167,14 @@ esp_err_t display_hal_destroy(void) } } + if (s_state.display_callbacks_registered) { + ret = display_hal_clear_io_callbacks_locked(); + if (ret != ESP_OK) { + display_hal_unlock(); + return ret; + } + } + for (size_t i = 0; i < DISPLAY_HAL_FRAMEBUFFER_COUNT_MAX; ++i) { heap_caps_free(s_state.framebuffers[i]); s_state.framebuffers[i] = NULL; @@ -173,8 +184,12 @@ esp_err_t display_hal_destroy(void) s_state.painter = NULL; } + flush_done_to_delete = s_state.display_flush_done; + lock_to_delete = s_state.lock; + s_state.panel = NULL; s_state.io = NULL; + s_state.display_callbacks_registered = false; s_state.width = 0; s_state.height = 0; s_state.framebuffer_bytes = 0; @@ -190,8 +205,18 @@ esp_err_t display_hal_destroy(void) s_state.clip_y = 0; s_state.clip_width = 0; s_state.clip_height = 0; + s_state.display_flush_done = NULL; + s_state.lock = NULL; - display_hal_unlock(); + if (lock_to_delete) { + xSemaphoreGive(lock_to_delete); + } + if (flush_done_to_delete) { + vSemaphoreDelete(flush_done_to_delete); + } + if (lock_to_delete) { + vSemaphoreDelete(lock_to_delete); + } return ESP_OK; } @@ -204,6 +229,21 @@ static void display_hal_clear_clip_locked(void) s_state.clip_height = s_state.height; } +static esp_err_t display_hal_clear_io_callbacks_locked(void) +{ + const esp_lcd_panel_io_callbacks_t callbacks = {0}; + + if (!s_state.io) { + s_state.display_callbacks_registered = false; + return ESP_OK; + } + + ESP_RETURN_ON_ERROR(esp_lcd_panel_io_register_event_callbacks(s_state.io, &callbacks, NULL), + TAG, "clear flush callback failed"); + s_state.display_callbacks_registered = false; + return ESP_OK; +} + static uint16_t *display_hal_get_draw_framebuffer_locked(void) { if (s_state.framebuffer_count == 0) { diff --git a/components/lua_modules/lua_module_display/src/lua_module_display.c b/components/lua_modules/lua_module_display/src/lua_module_display.c index 58173d1..7236f5b 100644 --- a/components/lua_modules/lua_module_display/src/lua_module_display.c +++ b/components/lua_modules/lua_module_display/src/lua_module_display.c @@ -84,7 +84,7 @@ static void *lua_display_check_lightuserdata_arg(lua_State *L, int index, const * Screen lifecycle * ---------------------------------------------------------------------- */ -static int lua_display_create_screen(lua_State *L) +static int lua_display_init(lua_State *L) { esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)lua_display_check_lightuserdata_arg( @@ -97,19 +97,19 @@ static int lua_display_create_screen(lua_State *L) esp_err_t err = display_hal_create(panel_handle, io_handle, lcd_width, lcd_height); if (err != ESP_OK) { - return luaL_error(L, "display create_screen failed: %s", esp_err_to_name(err)); + return luaL_error(L, "display init failed: %s", esp_err_to_name(err)); } lua_pushboolean(L, 1); return 1; } -static int lua_display_destroy_screen(lua_State *L) +static int lua_display_deinit(lua_State *L) { (void)L; esp_err_t err = display_hal_destroy(); if (err != ESP_OK) { - return luaL_error(L, "display destroy_screen failed: %s", esp_err_to_name(err)); + return luaL_error(L, "display deinit failed: %s", esp_err_to_name(err)); } lua_pushboolean(L, 1); @@ -1184,10 +1184,10 @@ int luaopen_display(lua_State *L) { lua_newtable(L); - lua_pushcfunction(L, lua_display_create_screen); - lua_setfield(L, -2, "create_screen"); - lua_pushcfunction(L, lua_display_destroy_screen); - lua_setfield(L, -2, "destroy_screen"); + lua_pushcfunction(L, lua_display_init); + lua_setfield(L, -2, "init"); + lua_pushcfunction(L, lua_display_deinit); + lua_setfield(L, -2, "deinit"); lua_pushcfunction(L, lua_display_width); lua_setfield(L, -2, "width"); diff --git a/components/lua_modules/lua_module_esp_heap/lua_module_esp_heap.c b/components/lua_modules/lua_module_esp_heap/lua_module_esp_heap.c index af3b39a..434978e 100644 --- a/components/lua_modules/lua_module_esp_heap/lua_module_esp_heap.c +++ b/components/lua_modules/lua_module_esp_heap/lua_module_esp_heap.c @@ -58,13 +58,6 @@ static void lua_module_esp_heap_push_caps_constants(lua_State *L) lua_setfield(L, -2, "RETENTION"); } -static int lua_module_esp_heap_caps(lua_State *L) -{ - lua_newtable(L); - lua_module_esp_heap_push_caps_constants(L); - return 1; -} - static int lua_module_esp_heap_get_info(lua_State *L) { lua_Integer caps_value = luaL_optinteger(L, 1, MALLOC_CAP_DEFAULT); @@ -173,7 +166,6 @@ static int lua_module_esp_heap_get_current_task(lua_State *L) int luaopen_esp_heap(lua_State *L) { static const luaL_Reg funcs[] = { - {"caps", lua_module_esp_heap_caps}, {"get_info", lua_module_esp_heap_get_info}, {"get_task_watermarks", lua_module_esp_heap_get_task_watermarks}, {"get_current_task", lua_module_esp_heap_get_current_task}, @@ -182,6 +174,9 @@ int luaopen_esp_heap(lua_State *L) lua_newtable(L); luaL_setfuncs(L, funcs, 0); + lua_newtable(L); + lua_module_esp_heap_push_caps_constants(L); + lua_setfield(L, -2, "caps"); return 1; } diff --git a/components/lua_modules/lua_module_esp_heap/skills/lua_module_esp_heap.md b/components/lua_modules/lua_module_esp_heap/skills/lua_module_esp_heap.md index 127094a..228f5e8 100644 --- a/components/lua_modules/lua_module_esp_heap/skills/lua_module_esp_heap.md +++ b/components/lua_modules/lua_module_esp_heap/skills/lua_module_esp_heap.md @@ -4,7 +4,7 @@ This skill describes how to correctly use esp_heap when writing Lua scripts. ## How to call - Import it with `local esp_heap = require("esp_heap")` -- Call `local caps = esp_heap.caps()` to get capability flags such as `caps.DEFAULT`, `caps.INTERNAL`, and `caps.SPIRAM` +- Read `local caps = esp_heap.caps` to get capability flags such as `caps.DEFAULT`, `caps.INTERNAL`, and `caps.SPIRAM` - Call `esp_heap.get_info(caps)` to read heap statistics such as `free_size`, `allocated_size`, and `largest_free_block` - Call `esp_heap.get_task_watermarks()` to inspect stack high-water marks for tasks - Call `esp_heap.get_current_task()` to inspect the current task state @@ -12,7 +12,7 @@ This skill describes how to correctly use esp_heap when writing Lua scripts. ## Example ```lua local esp_heap = require("esp_heap") -local caps = esp_heap.caps() +local caps = esp_heap.caps local info = esp_heap.get_info(caps.DEFAULT) print(info.free_size, info.largest_free_block)