diff --git a/samples/modules/lvgl/touch_draw/CMakeLists.txt b/samples/modules/lvgl/touch_draw/CMakeLists.txt new file mode 100644 index 000000000000000..ba5dd780a2b6e1e --- /dev/null +++ b/samples/modules/lvgl/touch_draw/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(lvgl_touch_draw) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/samples/modules/lvgl/touch_draw/README.rst b/samples/modules/lvgl/touch_draw/README.rst new file mode 100644 index 000000000000000..90967fcf77a7002 --- /dev/null +++ b/samples/modules/lvgl/touch_draw/README.rst @@ -0,0 +1,48 @@ +.. zephyr:code-sample:: lvgl-touch-draw + :name: LVGL touch draw demo + :relevant-api: display_interface input_interface + + Allow "drawing" on the screen with use of a touch input + +Overview +******** + +This sample application features drawing functionality based on touch input. +Received touch input will be drawn onto the surface of the screen. +This feature can be generally helpful for testing proper functionality of the +touch interface. + +When the board features a user-button (``sw0``) pushing this button will clear +the screen again. + +Requirements +************ + +This demo requires a board or a shield with a display and touch-input device, +for example: + +- :ref:`m5stack_core2` + +Please make sure (:dtcompatible:`zephyr,lvgl-pointer-input`) is defined in +device tree. + +Memory requirements +=================== +The sample application requires an LVGL memory pool of at least 32 kBytes. + +Building and Running +******************** + +Example building for :ref:`m5stack_core2`: + +.. zephyr-app-commands:: + :zephyr-app: samples/modules/lvgl/touch_draw + :board: m5stack_core2 + :goals: build flash + +References +********** + +.. target-notes:: + +.. _LVGL Web Page: https://lvgl.io/ diff --git a/samples/modules/lvgl/touch_draw/boards/m5stack_core2.conf b/samples/modules/lvgl/touch_draw/boards/m5stack_core2.conf new file mode 100644 index 000000000000000..1ac2b1c55c9b708 --- /dev/null +++ b/samples/modules/lvgl/touch_draw/boards/m5stack_core2.conf @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_MAIN_STACK_SIZE=4096 diff --git a/samples/modules/lvgl/touch_draw/boards/m5stack_stamps3.conf b/samples/modules/lvgl/touch_draw/boards/m5stack_stamps3.conf new file mode 100644 index 000000000000000..1ac2b1c55c9b708 --- /dev/null +++ b/samples/modules/lvgl/touch_draw/boards/m5stack_stamps3.conf @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_MAIN_STACK_SIZE=4096 diff --git a/samples/modules/lvgl/touch_draw/prj.conf b/samples/modules/lvgl/touch_draw/prj.conf new file mode 100644 index 000000000000000..49a6fdfa9111f80 --- /dev/null +++ b/samples/modules/lvgl/touch_draw/prj.conf @@ -0,0 +1,17 @@ +CONFIG_MAIN_STACK_SIZE=2048 + +CONFIG_COMMON_LIBC_MALLOC=y +CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=-1 + +CONFIG_DISPLAY=y +CONFIG_DISPLAY_LOG_LEVEL_ERR=y + +CONFIG_LOG=y +CONFIG_SHELL=y + +CONFIG_INPUT=y + +CONFIG_LVGL=y +CONFIG_LV_Z_MEM_POOL_HEAP_LIB_C=y +CONFIG_LV_USE_LOG=y +CONFIG_LV_FONT_MONTSERRAT_14=y diff --git a/samples/modules/lvgl/touch_draw/sample.yaml b/samples/modules/lvgl/touch_draw/sample.yaml new file mode 100644 index 000000000000000..0f2e797173fc613 --- /dev/null +++ b/samples/modules/lvgl/touch_draw/sample.yaml @@ -0,0 +1,18 @@ +sample: + description: LVGL touch draw demo + name: LVGL touch draw +common: + modules: + - lvgl + harness: none + tags: + - samples + - display + - touch + - gui + - lvgl +tests: + sample.display.lvgl.touch_draw: + filter: dt_chosen_enabled("zephyr,display") and dt_chosen_enabled("zephyr,lvgl-pointer-input") + min_flash: 250 + min_ram: 32 diff --git a/samples/modules/lvgl/touch_draw/src/main.c b/samples/modules/lvgl/touch_draw/src/main.c new file mode 100644 index 000000000000000..ba4aa3356a75912 --- /dev/null +++ b/samples/modules/lvgl/touch_draw/src/main.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2024 Martin Kiepfer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL +#include +LOG_MODULE_REGISTER(app); + +struct pressing_callback_context { + lv_obj_t *canvas; + lv_indev_t *indev; +}; + +#ifdef CONFIG_GPIO +struct button_callback_context { + struct gpio_callback callback; + lv_obj_t *canvas; +}; +static struct gpio_dt_spec button_gpio = GPIO_DT_SPEC_GET_OR(DT_ALIAS(sw0), gpios, {0}); + +static void button_isr_callback(const struct device *port, struct gpio_callback *cb, uint32_t pins) +{ + ARG_UNUSED(port); + ARG_UNUSED(pins); + + struct button_callback_context *ctx = + CONTAINER_OF(cb, struct button_callback_context, callback); + + lv_canvas_fill_bg(ctx->canvas, lv_color_black(), LV_OPA_TRANSP); +} +#endif /* CONFIG_GPIO */ + +static void lv_draw_pressing_callback(lv_event_t *e) +{ + lv_point_t point; + lv_draw_rect_dsc_t dsc; + struct pressing_callback_context *ctx = e->user_data; + + lv_indev_get_point(ctx->indev, &point); + + lv_draw_rect_dsc_init(&dsc); + dsc.bg_color = lv_palette_main(LV_PALETTE_GREY); + dsc.border_width = 0U; + dsc.outline_color = lv_palette_main(LV_PALETTE_GREY); + dsc.outline_width = 3U; + dsc.outline_pad = 0U; + dsc.outline_opa = LV_OPA_COVER; + dsc.radius = 5U; + + lv_canvas_draw_rect(ctx->canvas, point.x, point.y, 10U, 10U, &dsc); +} + +int main(void) +{ + const struct device *display_dev; + const struct device *pointer_dev; + lv_obj_t *touch_label; + struct pressing_callback_context pressing_cb_ctx; +#ifdef CONFIG_GPIO + struct button_callback_context btn_cb_ctx; +#endif + + display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)); + if (!device_is_ready(display_dev)) { + LOG_ERR("Device not ready, aborting test"); + return -ENODEV; + } + + pointer_dev = DEVICE_DT_GET(DT_COMPAT_GET_ANY_STATUS_OKAY(zephyr_lvgl_pointer_input)); + if (!device_is_ready(pointer_dev)) { + LOG_ERR("Device not ready, aborting test"); + return -ENODEV; + } + pressing_cb_ctx.indev = lvgl_input_get_indev(pointer_dev); + + /** + * The required canvas buf size is calculated by + * (lv_img_color_format_get_px_size(cf) * width) / 8 * height) + * 2 bpp are used here to reduce overall memory requirements + **/ + lv_opa_t *mask_map = malloc(LV_VER_RES * LV_HOR_RES * sizeof(lv_opa_t) / 4U); + if (mask_map == NULL) { + LOG_ERR("Allocating canvas layer failed. Please review LVGL mem pool size."); + return -ENOMEM; + } + + /* Create canvas layer for "drawing" touch events */ + pressing_cb_ctx.canvas = lv_canvas_create(lv_scr_act()); + lv_canvas_set_buffer(pressing_cb_ctx.canvas, mask_map, LV_HOR_RES, LV_VER_RES, + LV_IMG_CF_ALPHA_2BIT); + lv_canvas_fill_bg(pressing_cb_ctx.canvas, lv_color_black(), LV_OPA_TRANSP); + lv_obj_center(pressing_cb_ctx.canvas); + lv_obj_add_event_cb(lv_scr_act(), lv_draw_pressing_callback, LV_EVENT_PRESSING, + &pressing_cb_ctx); + + /* Create label with "touch me" text */ + touch_label = lv_label_create(lv_scr_act()); + lv_label_set_text(touch_label, "touch me!"); + lv_obj_align(touch_label, LV_ALIGN_CENTER, 0, 0); + +#ifdef CONFIG_GPIO + if (gpio_is_ready_dt(&button_gpio)) { + int err; + + err = gpio_pin_configure_dt(&button_gpio, GPIO_INPUT); + if (err) { + LOG_ERR("Failed to configure button gpio: %d", err); + return -EIO; + } + + gpio_init_callback(&btn_cb_ctx.callback, button_isr_callback, BIT(button_gpio.pin)); + btn_cb_ctx.canvas = pressing_cb_ctx.canvas; + + err = gpio_add_callback(button_gpio.port, &btn_cb_ctx.callback); + if (err) { + LOG_ERR("Failed to add button callback: %d", err); + return -EIO; + } + + err = gpio_pin_interrupt_configure_dt(&button_gpio, GPIO_INT_EDGE_TO_ACTIVE); + if (err) { + LOG_ERR("Failed to configure button callback: %d", err); + return -EIO; + } + } +#endif /* CONFIG_GPIO */ + + lv_task_handler(); + display_blanking_off(display_dev); + + while (true) { + lv_task_handler(); + k_sleep(K_MSEC(10)); + } +}