modules: lvgl: Move gluecode back to zephyr main repo

Moves back the module specific gluecode into the main repository

Signed-off-by: Fabian Blatz <fabianblatz@gmail.com>
This commit is contained in:
Fabian Blatz 2023-07-28 15:46:31 +02:00 committed by Carles Cufí
parent 81650746f7
commit 2e2163c1bc
22 changed files with 1789 additions and 69 deletions

View file

@ -3164,7 +3164,7 @@ West:
- brgl
- pdgendt
files:
- modules/Kconfig.lvgl
- modules/lvgl/
- tests/lib/gui/lvgl/
labels:
- manifest-lvgl

View file

@ -24,7 +24,7 @@ source "modules/Kconfig.esp32"
source "modules/Kconfig.imx"
source "modules/Kconfig.infineon"
source "modules/Kconfig.libmetal"
source "modules/Kconfig.lvgl"
source "modules/lvgl/Kconfig"
source "modules/Kconfig.mcux"
source "modules/Kconfig.microchip"
source "modules/Kconfig.nuvoton"
@ -105,6 +105,9 @@ comment "THRIFT module not available."
comment "Segger module not available."
depends on !ZEPHYR_SEGGER_MODULE
comment "LVGL module not available."
depends on !ZEPHYR_LVGL_MODULE
# This ensures that symbols are available in Kconfig for dependency checking
# and referencing, while keeping the settings themselves unavailable when the
# modules are not present in the workspace

View file

@ -1,66 +0,0 @@
# Copyright (c) 2022 Huawei Inc.
# SPDX-License-Identifier: Apache-2.0
config LVGL
bool "LVGL GUI library"
help
This option enables the LVGL GUI library.
if LVGL
config LV_DPI_DEF
int
config LV_Z_BITS_PER_PIXEL
int
config LV_Z_DOUBLE_VDB
bool
config LV_Z_FULL_REFRESH
bool
config LV_Z_VBD_CUSTOM_SECTION
bool
config LV_Z_VDB_ALIGN
int
config LV_Z_VDB_SIZE
int
config LV_Z_POINTER_KSCAN
bool
config LV_Z_POINTER_KSCAN_MSGQ_COUNT
int
default 10
config LV_Z_POINTER_KSCAN_SWAP_XY
bool
config LV_Z_POINTER_KSCAN_INVERT_X
bool
config LV_Z_POINTER_KSCAN_INVERT_Y
bool
choice LV_COLOR_DEPTH
default LV_COLOR_DEPTH_16
prompt "Color depth (bits per pixel)"
depends on LVGL
config LV_COLOR_DEPTH_32
bool "32: ARGB8888"
config LV_COLOR_DEPTH_16
bool "16: RGB565"
config LV_COLOR_DEPTH_8
bool "8: RGB232"
config LV_COLOR_DEPTH_1
bool "1: monochrome"
endchoice
config LV_COLOR_16_SWAP
bool
endif

229
modules/lvgl/CMakeLists.txt Normal file
View file

@ -0,0 +1,229 @@
# Copyright (c) 2019 jan.van_winkel@dxplore.eu
# Copyright (c) 2020 Teslabs Engineering S.L.
# Copyright (c) 2023 Fabian Blatz <fabianblatz@gmail.com>
#
# SPDX-License-Identifier: Apache-2.0
if(CONFIG_LVGL)
set(ZEPHYR_CURRENT_LIBRARY lvgl)
set(LVGL_DIR ${ZEPHYR_LVGL_MODULE_DIR})
zephyr_interface_library_named(LVGL)
zephyr_library()
zephyr_include_directories(${LVGL_DIR}/src/)
zephyr_include_directories(.)
zephyr_compile_definitions(LV_CONF_INCLUDE_SIMPLE=1)
zephyr_compile_definitions(LV_CONF_PATH=${CMAKE_CURRENT_SOURCE_DIR}/lv_conf.h)
zephyr_library_sources(
${LVGL_DIR}/src/core/lv_disp.c
${LVGL_DIR}/src/core/lv_event.c
${LVGL_DIR}/src/core/lv_group.c
${LVGL_DIR}/src/core/lv_indev.c
${LVGL_DIR}/src/core/lv_indev_scroll.c
${LVGL_DIR}/src/core/lv_obj.c
${LVGL_DIR}/src/core/lv_obj_class.c
${LVGL_DIR}/src/core/lv_obj_draw.c
${LVGL_DIR}/src/core/lv_obj_pos.c
${LVGL_DIR}/src/core/lv_obj_scroll.c
${LVGL_DIR}/src/core/lv_obj_style.c
${LVGL_DIR}/src/core/lv_obj_style_gen.c
${LVGL_DIR}/src/core/lv_obj_tree.c
${LVGL_DIR}/src/core/lv_refr.c
${LVGL_DIR}/src/core/lv_theme.c
${LVGL_DIR}/src/draw/arm2d/lv_gpu_arm2d.c
${LVGL_DIR}/src/draw/lv_draw.c
${LVGL_DIR}/src/draw/lv_draw_arc.c
${LVGL_DIR}/src/draw/lv_draw_img.c
${LVGL_DIR}/src/draw/lv_draw_label.c
${LVGL_DIR}/src/draw/lv_draw_layer.c
${LVGL_DIR}/src/draw/lv_draw_line.c
${LVGL_DIR}/src/draw/lv_draw_mask.c
${LVGL_DIR}/src/draw/lv_draw_rect.c
${LVGL_DIR}/src/draw/lv_draw_transform.c
${LVGL_DIR}/src/draw/lv_draw_triangle.c
${LVGL_DIR}/src/draw/lv_img_buf.c
${LVGL_DIR}/src/draw/lv_img_cache.c
${LVGL_DIR}/src/draw/lv_img_decoder.c
${LVGL_DIR}/src/draw/nxp/pxp/lv_draw_pxp_blend.c
${LVGL_DIR}/src/draw/nxp/pxp/lv_gpu_nxp_pxp.c
${LVGL_DIR}/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.c
${LVGL_DIR}/src/draw/nxp/vglite/lv_draw_vglite_arc.c
${LVGL_DIR}/src/draw/nxp/vglite/lv_draw_vglite_blend.c
${LVGL_DIR}/src/draw/nxp/vglite/lv_draw_vglite_rect.c
${LVGL_DIR}/src/draw/nxp/vglite/lv_draw_vglite.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_arc.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_bg.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_composite.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_img.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_label.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_layer.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_line.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_mask.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_polygon.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_rect.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_stack_blur.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_texture_cache.c
${LVGL_DIR}/src/draw/sdl/lv_draw_sdl_utils.c
${LVGL_DIR}/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.c
${LVGL_DIR}/src/draw/sw/lv_draw_sw.c
${LVGL_DIR}/src/draw/sw/lv_draw_sw_arc.c
${LVGL_DIR}/src/draw/sw/lv_draw_sw_blend.c
${LVGL_DIR}/src/draw/sw/lv_draw_sw_dither.c
${LVGL_DIR}/src/draw/sw/lv_draw_sw_gradient.c
${LVGL_DIR}/src/draw/sw/lv_draw_sw_img.c
${LVGL_DIR}/src/draw/sw/lv_draw_sw_layer.c
${LVGL_DIR}/src/draw/sw/lv_draw_sw_letter.c
${LVGL_DIR}/src/draw/sw/lv_draw_sw_line.c
${LVGL_DIR}/src/draw/sw/lv_draw_sw_polygon.c
${LVGL_DIR}/src/draw/sw/lv_draw_sw_rect.c
${LVGL_DIR}/src/draw/sw/lv_draw_sw_transform.c
${LVGL_DIR}/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.c
${LVGL_DIR}/src/extra/layouts/flex/lv_flex.c
${LVGL_DIR}/src/extra/layouts/grid/lv_grid.c
${LVGL_DIR}/src/extra/libs/bmp/lv_bmp.c
${LVGL_DIR}/src/extra/libs/ffmpeg/lv_ffmpeg.c
${LVGL_DIR}/src/extra/libs/freetype/lv_freetype.c
${LVGL_DIR}/src/extra/libs/fsdrv/lv_fs_fatfs.c
${LVGL_DIR}/src/extra/libs/fsdrv/lv_fs_posix.c
${LVGL_DIR}/src/extra/libs/fsdrv/lv_fs_stdio.c
${LVGL_DIR}/src/extra/libs/fsdrv/lv_fs_win32.c
${LVGL_DIR}/src/extra/libs/gif/gifdec.c
${LVGL_DIR}/src/extra/libs/gif/lv_gif.c
${LVGL_DIR}/src/extra/libs/png/lodepng.c
${LVGL_DIR}/src/extra/libs/png/lv_png.c
${LVGL_DIR}/src/extra/libs/qrcode/lv_qrcode.c
${LVGL_DIR}/src/extra/libs/qrcode/qrcodegen.c
${LVGL_DIR}/src/extra/libs/rlottie/lv_rlottie.c
${LVGL_DIR}/src/extra/libs/sjpg/lv_sjpg.c
${LVGL_DIR}/src/extra/libs/sjpg/tjpgd.c
${LVGL_DIR}/src/extra/lv_extra.c
${LVGL_DIR}/src/extra/others/fragment/lv_fragment.c
${LVGL_DIR}/src/extra/others/fragment/lv_fragment_manager.c
${LVGL_DIR}/src/extra/others/gridnav/lv_gridnav.c
${LVGL_DIR}/src/extra/others/ime/lv_ime_pinyin.c
${LVGL_DIR}/src/extra/others/imgfont/lv_imgfont.c
${LVGL_DIR}/src/extra/others/monkey/lv_monkey.c
${LVGL_DIR}/src/extra/others/msg/lv_msg.c
${LVGL_DIR}/src/extra/others/snapshot/lv_snapshot.c
${LVGL_DIR}/src/extra/themes/basic/lv_theme_basic.c
${LVGL_DIR}/src/extra/themes/default/lv_theme_default.c
${LVGL_DIR}/src/extra/themes/mono/lv_theme_mono.c
${LVGL_DIR}/src/extra/widgets/animimg/lv_animimg.c
${LVGL_DIR}/src/extra/widgets/calendar/lv_calendar.c
${LVGL_DIR}/src/extra/widgets/calendar/lv_calendar_header_arrow.c
${LVGL_DIR}/src/extra/widgets/calendar/lv_calendar_header_dropdown.c
${LVGL_DIR}/src/extra/widgets/chart/lv_chart.c
${LVGL_DIR}/src/extra/widgets/colorwheel/lv_colorwheel.c
${LVGL_DIR}/src/extra/widgets/imgbtn/lv_imgbtn.c
${LVGL_DIR}/src/extra/widgets/keyboard/lv_keyboard.c
${LVGL_DIR}/src/extra/widgets/led/lv_led.c
${LVGL_DIR}/src/extra/widgets/list/lv_list.c
${LVGL_DIR}/src/extra/widgets/menu/lv_menu.c
${LVGL_DIR}/src/extra/widgets/meter/lv_meter.c
${LVGL_DIR}/src/extra/widgets/msgbox/lv_msgbox.c
${LVGL_DIR}/src/extra/widgets/span/lv_span.c
${LVGL_DIR}/src/extra/widgets/spinbox/lv_spinbox.c
${LVGL_DIR}/src/extra/widgets/spinner/lv_spinner.c
${LVGL_DIR}/src/extra/widgets/tabview/lv_tabview.c
${LVGL_DIR}/src/extra/widgets/tileview/lv_tileview.c
${LVGL_DIR}/src/extra/widgets/win/lv_win.c
${LVGL_DIR}/src/font/lv_font.c
${LVGL_DIR}/src/font/lv_font_dejavu_16_persian_hebrew.c
${LVGL_DIR}/src/font/lv_font_fmt_txt.c
${LVGL_DIR}/src/font/lv_font_loader.c
${LVGL_DIR}/src/font/lv_font_montserrat_10.c
${LVGL_DIR}/src/font/lv_font_montserrat_12.c
${LVGL_DIR}/src/font/lv_font_montserrat_12_subpx.c
${LVGL_DIR}/src/font/lv_font_montserrat_14.c
${LVGL_DIR}/src/font/lv_font_montserrat_16.c
${LVGL_DIR}/src/font/lv_font_montserrat_18.c
${LVGL_DIR}/src/font/lv_font_montserrat_20.c
${LVGL_DIR}/src/font/lv_font_montserrat_22.c
${LVGL_DIR}/src/font/lv_font_montserrat_24.c
${LVGL_DIR}/src/font/lv_font_montserrat_26.c
${LVGL_DIR}/src/font/lv_font_montserrat_28.c
${LVGL_DIR}/src/font/lv_font_montserrat_28_compressed.c
${LVGL_DIR}/src/font/lv_font_montserrat_30.c
${LVGL_DIR}/src/font/lv_font_montserrat_32.c
${LVGL_DIR}/src/font/lv_font_montserrat_34.c
${LVGL_DIR}/src/font/lv_font_montserrat_36.c
${LVGL_DIR}/src/font/lv_font_montserrat_38.c
${LVGL_DIR}/src/font/lv_font_montserrat_40.c
${LVGL_DIR}/src/font/lv_font_montserrat_42.c
${LVGL_DIR}/src/font/lv_font_montserrat_44.c
${LVGL_DIR}/src/font/lv_font_montserrat_46.c
${LVGL_DIR}/src/font/lv_font_montserrat_48.c
${LVGL_DIR}/src/font/lv_font_montserrat_8.c
${LVGL_DIR}/src/font/lv_font_simsun_16_cjk.c
${LVGL_DIR}/src/font/lv_font_unscii_16.c
${LVGL_DIR}/src/font/lv_font_unscii_8.c
${LVGL_DIR}/src/hal/lv_hal_disp.c
${LVGL_DIR}/src/hal/lv_hal_indev.c
${LVGL_DIR}/src/hal/lv_hal_tick.c
${LVGL_DIR}/src/misc/lv_anim.c
${LVGL_DIR}/src/misc/lv_anim_timeline.c
${LVGL_DIR}/src/misc/lv_area.c
${LVGL_DIR}/src/misc/lv_async.c
${LVGL_DIR}/src/misc/lv_bidi.c
${LVGL_DIR}/src/misc/lv_color.c
${LVGL_DIR}/src/misc/lv_fs.c
${LVGL_DIR}/src/misc/lv_gc.c
${LVGL_DIR}/src/misc/lv_ll.c
${LVGL_DIR}/src/misc/lv_log.c
${LVGL_DIR}/src/misc/lv_lru.c
${LVGL_DIR}/src/misc/lv_math.c
${LVGL_DIR}/src/misc/lv_mem.c
${LVGL_DIR}/src/misc/lv_printf.c
${LVGL_DIR}/src/misc/lv_style.c
${LVGL_DIR}/src/misc/lv_style_gen.c
${LVGL_DIR}/src/misc/lv_templ.c
${LVGL_DIR}/src/misc/lv_timer.c
${LVGL_DIR}/src/misc/lv_tlsf.c
${LVGL_DIR}/src/misc/lv_txt.c
${LVGL_DIR}/src/misc/lv_txt_ap.c
${LVGL_DIR}/src/misc/lv_utils.c
${LVGL_DIR}/src/widgets/lv_arc.c
${LVGL_DIR}/src/widgets/lv_bar.c
${LVGL_DIR}/src/widgets/lv_btn.c
${LVGL_DIR}/src/widgets/lv_btnmatrix.c
${LVGL_DIR}/src/widgets/lv_canvas.c
${LVGL_DIR}/src/widgets/lv_checkbox.c
${LVGL_DIR}/src/widgets/lv_dropdown.c
${LVGL_DIR}/src/widgets/lv_img.c
${LVGL_DIR}/src/widgets/lv_label.c
${LVGL_DIR}/src/widgets/lv_line.c
${LVGL_DIR}/src/widgets/lv_objx_templ.c
${LVGL_DIR}/src/widgets/lv_roller.c
${LVGL_DIR}/src/widgets/lv_slider.c
${LVGL_DIR}/src/widgets/lv_switch.c
${LVGL_DIR}/src/widgets/lv_table.c
${LVGL_DIR}/src/widgets/lv_textarea.c
lvgl.c
lvgl_display.c
lvgl_display_mono.c
lvgl_display_16bit.c
lvgl_display_24bit.c
lvgl_display_32bit.c
lvgl_display_mono.c
)
zephyr_library_sources_ifdef(CONFIG_LV_Z_USE_FILESYSTEM lvgl_fs.c)
zephyr_library_sources_ifdef(CONFIG_LV_Z_MEM_POOL_SYS_HEAP lvgl_mem.c)
zephyr_library_sources_ifdef(CONFIG_LV_Z_SHELL lvgl_shell.c)
zephyr_library_link_libraries(LVGL)
target_link_libraries(LVGL INTERFACE zephyr_interface)
endif()

61
modules/lvgl/Kconfig Normal file
View file

@ -0,0 +1,61 @@
# Copyright (c) 2023 Fabian Blatz <fabianblatz@gmail.com>
# SPDX-License-Identifier: Apache-2.0
config ZEPHYR_LVGL_MODULE
bool
config LVGL
bool "LVGL support"
help
This option enables the LVGL graphics library.
if LVGL
config LV_USE_MONKEY
bool
config LV_DPI_DEF
int
config LV_CONF_SKIP
bool
default n
config APP_LINK_WITH_LVGL
bool "Link 'app' with LVGL"
default y
help
Add LVGL header files to the 'app' include path. It may be
disabled if the include paths for LVGL are causing aliasing
issues for 'app'.
config LV_Z_USE_FILESYSTEM
bool "LVGL file system support"
depends on FILE_SYSTEM
default y if FILE_SYSTEM
help
Enable LittlevGL file system
choice LV_COLOR_DEPTH
default LV_COLOR_DEPTH_16
prompt "Color depth (bits per pixel)"
config LV_COLOR_DEPTH_32
bool "32: ARGB8888"
config LV_COLOR_DEPTH_16
bool "16: RGB565"
config LV_COLOR_DEPTH_8
bool "8: RGB232"
config LV_COLOR_DEPTH_1
bool "1: monochrome"
endchoice
config LV_COLOR_16_SWAP
bool
rsource "Kconfig.memory"
rsource "Kconfig.input"
rsource "Kconfig.shell"
endif

View file

@ -0,0 +1,41 @@
# Copyright (c) 2018-2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
# Copyright (c) 2020 Teslabs Engineering S.L.
# SPDX-License-Identifier: Apache-2.0
menu "Input device settings"
config LV_Z_POINTER_KSCAN
bool "Keyboard scan pointer input"
depends on KSCAN
help
Enable keyboard scan pointer input
if LV_Z_POINTER_KSCAN
config LV_Z_POINTER_KSCAN_MSGQ_COUNT
int "Keyboard scan message queue count maximum"
default 10
help
Maximum number of items in the keyboard scan message queue.
config LV_Z_POINTER_KSCAN_SWAP_XY
bool "Swap keyboard scan X,Y axes"
help
Swap keyboard scan X,Y axes. This option can be used to align keyboard
scan coordinates with the display.
config LV_Z_POINTER_KSCAN_INVERT_X
bool "Invert keyboard scan X axis"
help
Invert keyboard scan X axis. This option can be used to align keyboard
scan coordinates with the display.
config LV_Z_POINTER_KSCAN_INVERT_Y
bool "Invert keyboard scan Y axis"
help
Invert keyboard scan Y axis. This option can be used to align keyboard
scan coordinates with the display.
endif # LV_Z_POINTER_KSCAN
endmenu

119
modules/lvgl/Kconfig.memory Normal file
View file

@ -0,0 +1,119 @@
# Copyright (c) 2018-2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
# Copyright (c) 2020 Teslabs Engineering S.L.
# SPDX-License-Identifier: Apache-2.0
menu "Memory manager settings"
config LV_Z_BITS_PER_PIXEL
int "Bits per pixel"
default 32
range 1 32
depends on LV_Z_BUFFER_ALLOC_STATIC
help
Number of bits per pixel.
choice LV_Z_MEMORY_POOL
prompt "Memory pool"
default LV_Z_MEM_POOL_SYS_HEAP
help
Memory pool to use for lvgl allocated objects
config LV_Z_MEM_POOL_HEAP_LIB_C
bool "C library Heap"
depends on !MINIMAL_LIBC || (MINIMAL_LIBC_MALLOC_ARENA_SIZE != 0)
help
Use C library malloc and free to allocate objects on the C library heap
config LV_Z_MEM_POOL_SYS_HEAP
bool "User space lvgl pool"
help
Use a dedicated memory pool from a private sys heap.
endchoice
if LV_Z_MEM_POOL_SYS_HEAP
config LV_Z_MEM_POOL_MIN_SIZE
int "Minimum memory pool block size"
default 16
help
Size of the smallest block in the memory pool in bytes
config LV_Z_MEM_POOL_MAX_SIZE
int "Maximum memory pool block size"
default 2048
help
Size of the largest block in the memory pool in bytes
config LV_Z_MEM_POOL_NUMBER_BLOCKS
int "Number of max size blocks in memory pool"
default 1
help
Number of maximum sized blocks in the memory pool.
endif
config LV_Z_VDB_SIZE
int "Rendering buffer size"
default 10
range 1 100
help
Size of the buffer used for rendering screen content as a percentage
of total display size.
config LV_Z_DOUBLE_VDB
bool "Use two rendering buffers"
help
Use two buffers to render and flush data in parallel
config LV_Z_FULL_REFRESH
bool "Force full refresh mode"
help
Force full refresh of display on update. When combined with
LV_Z_VDB_SIZE, this setting can improve performance for display
controllers that require a framebuffer to be present in system memory,
since the controller can render the LVGL framebuffer directly
config LV_Z_VDB_ALIGN
int "Rending buffer alignment"
default 4
depends on LV_Z_BUFFER_ALLOC_STATIC
help
Rendering buffer alignment. Depending on chosen color depth,
buffer may be accessed as a uint8_t *, uint16_t *, or uint32_t *,
so buffer must be aligned to prevent unaligned memory access
config LV_Z_VBD_CUSTOM_SECTION
bool "Link rendering buffers to custom section"
depends on LV_Z_BUFFER_ALLOC_STATIC
help
Place LVGL rendering buffers in custom section, with tag ".lvgl_buf".
This can be used by custom linker scripts to relocate the LVGL
rendering buffers to a custom location, such as tightly coupled or
external memory.
choice LV_Z_RENDERING_BUFFER_ALLOCATION
prompt "Rendering Buffer Allocation"
default LV_Z_BUFFER_ALLOC_STATIC
help
Type of allocation that should be used for allocating rendering buffers
config LV_Z_BUFFER_ALLOC_STATIC
bool "Static"
help
Rendering buffers are statically allocated based on the following
configuration parameters:
* Horizontal screen resolution
* Vertical screen resolution
* Rendering buffer size
* Bytes per pixel
config LV_Z_BUFFER_ALLOC_DYNAMIC
bool "Dynamic"
help
Rendering buffers are dynamically allocated based on the actual
display parameters
endchoice
endmenu

View file

@ -0,0 +1,19 @@
# Copyright (c) 2023 Fabian Blatz <fabianblatz@gmail.com>
# SPDX-License-Identifier: Apache-2.0
config LV_Z_SHELL
bool "LVGL Shell"
depends on SHELL
help
Enable LVGL shell for testing.
if LV_Z_SHELL
config LV_Z_MAX_MONKEY_COUNT
int "Maximum number of monkeys"
default 4
depends on LV_USE_MONKEY
help
Number of monkey instances that can exist in parallel
endif

50
modules/lvgl/lv_conf.h Normal file
View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2020 Jan Van Winkel <jan.van_winkel@dxplore.eu>
* Copyright (c) 2020 Teslabs Engineering S.L.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_MODULES_LVGL_LV_CONF_H_
#define ZEPHYR_MODULES_LVGL_LV_CONF_H_
/* Memory manager settings */
#define LV_MEMCPY_MEMSET_STD 1
#if defined(CONFIG_LV_Z_MEM_POOL_HEAP_LIB_C)
#define LV_MEM_CUSTOM_INCLUDE "stdlib.h"
#define LV_MEM_CUSTOM_ALLOC malloc
#define LV_MEM_CUSTOM_REALLOC realloc
#define LV_MEM_CUSTOM_FREE free
#else
#define LV_MEM_CUSTOM_INCLUDE "lvgl_mem.h"
#define LV_MEM_CUSTOM_ALLOC lvgl_malloc
#define LV_MEM_CUSTOM_REALLOC lvgl_realloc
#define LV_MEM_CUSTOM_FREE lvgl_free
#endif
/* HAL settings */
#define LV_TICK_CUSTOM 1
#define LV_TICK_CUSTOM_INCLUDE <zephyr/kernel.h>
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (k_uptime_get_32())
/* Misc settings */
#define LV_SPRINTF_CUSTOM 1
#define LV_SPRINTF_INCLUDE "stdio.h"
#define lv_snprintf snprintf
#define lv_vsnprintf vsnprintf
/*
* Needed because of a workaround for a GCC bug,
* see https://github.com/lvgl/lvgl/issues/3078
*/
#define LV_CONF_SUPPRESS_DEFINE_CHECK 1
#endif /* ZEPHYR_MODULES_LVGL_LV_CONF_H_ */

385
modules/lvgl/lvgl.c Normal file
View file

@ -0,0 +1,385 @@
/*
* Copyright (c) 2018-2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <lvgl.h>
#include "lvgl_display.h"
#ifdef CONFIG_LV_Z_USE_FILESYSTEM
#include "lvgl_fs.h"
#endif
#ifdef CONFIG_LV_Z_POINTER_KSCAN
#include <zephyr/drivers/kscan.h>
#endif
#include LV_MEM_CUSTOM_INCLUDE
#define LOG_LEVEL CONFIG_LV_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(lvgl);
static lv_disp_drv_t disp_drv;
struct lvgl_disp_data disp_data = {
.blanking_on = false,
};
#ifdef CONFIG_LV_Z_POINTER_KSCAN
static lv_indev_drv_t indev_drv;
#endif /* CONFIG_LV_Z_POINTER_KSCAN */
#define DISPLAY_NODE DT_CHOSEN(zephyr_display)
#define KSCAN_NODE DT_CHOSEN(zephyr_keyboard_scan)
#ifdef CONFIG_LV_Z_BUFFER_ALLOC_STATIC
static lv_disp_draw_buf_t disp_buf;
#define DISPLAY_WIDTH DT_PROP(DISPLAY_NODE, width)
#define DISPLAY_HEIGHT DT_PROP(DISPLAY_NODE, height)
#define BUFFER_SIZE \
(CONFIG_LV_Z_BITS_PER_PIXEL * \
((CONFIG_LV_Z_VDB_SIZE * DISPLAY_WIDTH * DISPLAY_HEIGHT) / 100) / 8)
#define NBR_PIXELS_IN_BUFFER (BUFFER_SIZE * 8 / CONFIG_LV_Z_BITS_PER_PIXEL)
/* NOTE: depending on chosen color depth buffer may be accessed using uint8_t *,
* uint16_t * or uint32_t *, therefore buffer needs to be aligned accordingly to
* prevent unaligned memory accesses.
*/
static uint8_t buf0[BUFFER_SIZE]
#ifdef CONFIG_LV_Z_VBD_CUSTOM_SECTION
Z_GENERIC_SECTION(.lvgl_buf)
#endif
__aligned(CONFIG_LV_Z_VDB_ALIGN);
#ifdef CONFIG_LV_Z_DOUBLE_VDB
static uint8_t buf1[BUFFER_SIZE]
#ifdef CONFIG_LV_Z_VBD_CUSTOM_SECTION
Z_GENERIC_SECTION(.lvgl_buf)
#endif
__aligned(CONFIG_LV_Z_VDB_ALIGN);
#endif /* CONFIG_LV_Z_DOUBLE_VDB */
#endif /* CONFIG_LV_Z_BUFFER_ALLOC_STATIC */
#if CONFIG_LV_LOG_LEVEL != 0
/*
* In LVGLv8 the signature of the logging callback has changes and it no longer
* takes the log level as an integer argument. Instead, the log level is now
* already part of the buffer passed to the logging callback. It's not optimal
* but we need to live with it and parse the buffer manually to determine the
* level and then truncate the string we actually pass to the logging framework.
*/
static void lvgl_log(const char *buf)
{
/*
* This is ugly and should be done in a loop or something but as it
* turned out, Z_LOG()'s first argument (that specifies the log level)
* cannot be an l-value...
*
* We also assume lvgl is sane and always supplies the level string.
*/
switch (buf[1]) {
case 'E':
LOG_ERR("%s", buf + strlen("[Error] "));
break;
case 'W':
LOG_WRN("%s", buf + strlen("Warn] "));
break;
case 'I':
LOG_INF("%s", buf + strlen("[Info] "));
break;
case 'T':
LOG_DBG("%s", buf + strlen("[Trace] "));
break;
}
}
#endif
#ifdef CONFIG_LV_Z_BUFFER_ALLOC_STATIC
static int lvgl_allocate_rendering_buffers(lv_disp_drv_t *disp_driver)
{
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_driver->user_data;
int err = 0;
if (data->cap.x_resolution <= DISPLAY_WIDTH) {
disp_driver->hor_res = data->cap.x_resolution;
} else {
LOG_ERR("Horizontal resolution is larger than maximum");
err = -ENOTSUP;
}
if (data->cap.y_resolution <= DISPLAY_HEIGHT) {
disp_driver->ver_res = data->cap.y_resolution;
} else {
LOG_ERR("Vertical resolution is larger than maximum");
err = -ENOTSUP;
}
disp_driver->draw_buf = &disp_buf;
#ifdef CONFIG_LV_Z_DOUBLE_VDB
lv_disp_draw_buf_init(disp_driver->draw_buf, &buf0, &buf1, NBR_PIXELS_IN_BUFFER);
#else
lv_disp_draw_buf_init(disp_driver->draw_buf, &buf0, NULL, NBR_PIXELS_IN_BUFFER);
#endif /* CONFIG_LV_Z_DOUBLE_VDB */
return err;
}
#else
static int lvgl_allocate_rendering_buffers(lv_disp_drv_t *disp_driver)
{
void *buf0 = NULL;
void *buf1 = NULL;
uint16_t buf_nbr_pixels;
uint32_t buf_size;
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_driver->user_data;
disp_driver->hor_res = data->cap.x_resolution;
disp_driver->ver_res = data->cap.y_resolution;
buf_nbr_pixels = (CONFIG_LV_Z_VDB_SIZE * disp_driver->hor_res * disp_driver->ver_res) / 100;
/* one horizontal line is the minimum buffer requirement for lvgl */
if (buf_nbr_pixels < disp_driver->hor_res) {
buf_nbr_pixels = disp_driver->hor_res;
}
switch (data->cap.current_pixel_format) {
case PIXEL_FORMAT_ARGB_8888:
buf_size = 4 * buf_nbr_pixels;
break;
case PIXEL_FORMAT_RGB_888:
buf_size = 3 * buf_nbr_pixels;
break;
case PIXEL_FORMAT_RGB_565:
buf_size = 2 * buf_nbr_pixels;
break;
case PIXEL_FORMAT_MONO01:
case PIXEL_FORMAT_MONO10:
buf_size = buf_nbr_pixels / 8;
buf_size += (buf_nbr_pixels % 8) == 0 ? 0 : 1;
break;
default:
return -ENOTSUP;
}
buf0 = LV_MEM_CUSTOM_ALLOC(buf_size);
if (buf0 == NULL) {
LOG_ERR("Failed to allocate memory for rendering buffer");
return -ENOMEM;
}
#ifdef CONFIG_LV_Z_DOUBLE_VDB
buf1 = LV_MEM_CUSTOM_ALLOC(buf_size);
if (buf1 == NULL) {
LV_MEM_CUSTOM_FREE(buf0);
LOG_ERR("Failed to allocate memory for rendering buffer");
return -ENOMEM;
}
#endif
disp_driver->draw_buf = LV_MEM_CUSTOM_ALLOC(sizeof(lv_disp_draw_buf_t));
if (disp_driver->draw_buf == NULL) {
LV_MEM_CUSTOM_FREE(buf0);
LV_MEM_CUSTOM_FREE(buf1);
LOG_ERR("Failed to allocate memory to store rendering buffers");
return -ENOMEM;
}
lv_disp_draw_buf_init(disp_driver->draw_buf, buf0, buf1, buf_nbr_pixels);
return 0;
}
#endif /* CONFIG_LV_Z_BUFFER_ALLOC_STATIC */
#ifdef CONFIG_LV_Z_POINTER_KSCAN
K_MSGQ_DEFINE(kscan_msgq, sizeof(lv_indev_data_t), CONFIG_LV_Z_POINTER_KSCAN_MSGQ_COUNT, 4);
static void lvgl_pointer_kscan_callback(const struct device *dev, uint32_t row, uint32_t col,
bool pressed)
{
lv_indev_data_t data = {
.point.x = col,
.point.y = row,
.state = pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL,
};
if (k_msgq_put(&kscan_msgq, &data, K_NO_WAIT) != 0) {
LOG_DBG("Could not put input data into queue");
}
}
static void lvgl_pointer_kscan_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
lv_disp_t *disp;
struct lvgl_disp_data *disp_data;
struct display_capabilities *cap;
lv_indev_data_t curr;
static lv_indev_data_t prev = {
.point.x = 0,
.point.y = 0,
.state = LV_INDEV_STATE_REL,
};
if (k_msgq_get(&kscan_msgq, &curr, K_NO_WAIT) != 0) {
goto set_and_release;
}
prev = curr;
disp = lv_disp_get_default();
disp_data = disp->driver->user_data;
cap = &disp_data->cap;
/* adjust kscan coordinates */
if (IS_ENABLED(CONFIG_LV_Z_POINTER_KSCAN_SWAP_XY)) {
lv_coord_t x;
x = prev.point.x;
prev.point.x = prev.point.y;
prev.point.y = x;
}
if (IS_ENABLED(CONFIG_LV_Z_POINTER_KSCAN_INVERT_X)) {
if (cap->current_orientation == DISPLAY_ORIENTATION_NORMAL ||
cap->current_orientation == DISPLAY_ORIENTATION_ROTATED_180) {
prev.point.x = cap->x_resolution - prev.point.x;
} else {
prev.point.x = cap->y_resolution - prev.point.x;
}
}
if (IS_ENABLED(CONFIG_LV_Z_POINTER_KSCAN_INVERT_Y)) {
if (cap->current_orientation == DISPLAY_ORIENTATION_NORMAL ||
cap->current_orientation == DISPLAY_ORIENTATION_ROTATED_180) {
prev.point.y = cap->y_resolution - prev.point.y;
} else {
prev.point.y = cap->x_resolution - prev.point.y;
}
}
/* rotate touch point to match display rotation */
if (cap->current_orientation == DISPLAY_ORIENTATION_ROTATED_90) {
lv_coord_t x;
x = prev.point.x;
prev.point.x = prev.point.y;
prev.point.y = cap->y_resolution - x;
} else if (cap->current_orientation == DISPLAY_ORIENTATION_ROTATED_180) {
prev.point.x = cap->x_resolution - prev.point.x;
prev.point.y = cap->y_resolution - prev.point.y;
} else if (cap->current_orientation == DISPLAY_ORIENTATION_ROTATED_270) {
lv_coord_t x;
x = prev.point.x;
prev.point.x = cap->x_resolution - prev.point.y;
prev.point.y = x;
}
/* filter readings within display */
if (prev.point.x <= 0) {
prev.point.x = 0;
} else if (prev.point.x >= cap->x_resolution) {
prev.point.x = cap->x_resolution - 1;
}
if (prev.point.y <= 0) {
prev.point.y = 0;
} else if (prev.point.y >= cap->y_resolution) {
prev.point.y = cap->y_resolution - 1;
}
set_and_release:
*data = prev;
data->continue_reading = k_msgq_num_used_get(&kscan_msgq) > 0;
}
static int lvgl_pointer_kscan_init(void)
{
const struct device *kscan_dev = DEVICE_DT_GET(KSCAN_NODE);
if (!device_is_ready(kscan_dev)) {
LOG_ERR("Keyboard scan device not ready.");
return -ENODEV;
}
if (kscan_config(kscan_dev, lvgl_pointer_kscan_callback) < 0) {
LOG_ERR("Could not configure keyboard scan device.");
return -ENODEV;
}
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = lvgl_pointer_kscan_read;
if (lv_indev_drv_register(&indev_drv) == NULL) {
LOG_ERR("Failed to register input device.");
return -EPERM;
}
kscan_enable_callback(kscan_dev);
return 0;
}
#endif /* CONFIG_LV_Z_POINTER_KSCAN */
static int lvgl_init(void)
{
const struct device *display_dev = DEVICE_DT_GET(DISPLAY_NODE);
int err = 0;
if (!device_is_ready(display_dev)) {
LOG_ERR("Display device not ready.");
return -ENODEV;
}
#if CONFIG_LV_LOG_LEVEL != 0
lv_log_register_print_cb(lvgl_log);
#endif
lv_init();
#ifdef CONFIG_LV_Z_USE_FILESYSTEM
lvgl_fs_init();
#endif
disp_data.display_dev = display_dev;
display_get_capabilities(display_dev, &disp_data.cap);
lv_disp_drv_init(&disp_drv);
disp_drv.user_data = (void *)&disp_data;
#ifdef CONFIG_LV_Z_FULL_REFRESH
disp_drv.full_refresh = 1;
#endif
err = lvgl_allocate_rendering_buffers(&disp_drv);
if (err != 0) {
return err;
}
if (set_lvgl_rendering_cb(&disp_drv) != 0) {
LOG_ERR("Display not supported.");
return -ENOTSUP;
}
if (lv_disp_drv_register(&disp_drv) == NULL) {
LOG_ERR("Failed to register display device.");
return -EPERM;
}
#ifdef CONFIG_LV_Z_POINTER_KSCAN
lvgl_pointer_kscan_init();
#endif /* CONFIG_LV_Z_POINTER_KSCAN */
return 0;
}
SYS_INIT(lvgl_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include "lvgl_display.h"
int set_lvgl_rendering_cb(lv_disp_drv_t *disp_drv)
{
int err = 0;
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_drv->user_data;
switch (data->cap.current_pixel_format) {
case PIXEL_FORMAT_ARGB_8888:
disp_drv->flush_cb = lvgl_flush_cb_32bit;
disp_drv->rounder_cb = NULL;
#ifdef CONFIG_LV_COLOR_DEPTH_32
disp_drv->set_px_cb = NULL;
#else
disp_drv->set_px_cb = lvgl_set_px_cb_32bit;
#endif
break;
case PIXEL_FORMAT_RGB_888:
disp_drv->flush_cb = lvgl_flush_cb_24bit;
disp_drv->rounder_cb = NULL;
disp_drv->set_px_cb = lvgl_set_px_cb_24bit;
break;
case PIXEL_FORMAT_RGB_565:
case PIXEL_FORMAT_BGR_565:
disp_drv->flush_cb = lvgl_flush_cb_16bit;
disp_drv->rounder_cb = NULL;
#ifdef CONFIG_LV_COLOR_DEPTH_16
disp_drv->set_px_cb = NULL;
#else
disp_drv->set_px_cb = lvgl_set_px_cb_16bit;
#endif
break;
case PIXEL_FORMAT_MONO01:
case PIXEL_FORMAT_MONO10:
disp_drv->flush_cb = lvgl_flush_cb_mono;
disp_drv->rounder_cb = lvgl_rounder_cb_mono;
disp_drv->set_px_cb = lvgl_set_px_cb_mono;
break;
default:
disp_drv->flush_cb = NULL;
disp_drv->rounder_cb = NULL;
disp_drv->set_px_cb = NULL;
err = -ENOTSUP;
break;
}
return err;
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_MODULES_LVGL_DISPLAY_H_
#define ZEPHYR_MODULES_LVGL_DISPLAY_H_
#include <zephyr/drivers/display.h>
#include <lvgl.h>
#ifdef __cplusplus
extern "C" {
#endif
struct lvgl_disp_data {
const struct device *display_dev;
struct display_capabilities cap;
bool blanking_on;
};
void lvgl_flush_cb_mono(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
void lvgl_flush_cb_16bit(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
void lvgl_flush_cb_24bit(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
void lvgl_flush_cb_32bit(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
void lvgl_set_px_cb_mono(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x,
lv_coord_t y, lv_color_t color, lv_opa_t opa);
void lvgl_set_px_cb_16bit(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x,
lv_coord_t y, lv_color_t color, lv_opa_t opa);
void lvgl_set_px_cb_24bit(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x,
lv_coord_t y, lv_color_t color, lv_opa_t opa);
void lvgl_set_px_cb_32bit(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x,
lv_coord_t y, lv_color_t color, lv_opa_t opa);
void lvgl_rounder_cb_mono(lv_disp_drv_t *disp_drv, lv_area_t *area);
int set_lvgl_rendering_cb(lv_disp_drv_t *disp_drv);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_MODULES_LVGL_DISPLAY_H_ */

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <lvgl.h>
#include "lvgl_display.h"
void lvgl_flush_cb_16bit(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_drv->user_data;
uint16_t w = area->x2 - area->x1 + 1;
uint16_t h = area->y2 - area->y1 + 1;
struct display_buffer_descriptor desc;
desc.buf_size = w * 2U * h;
desc.width = w;
desc.pitch = w;
desc.height = h;
display_write(data->display_dev, area->x1, area->y1, &desc, (void *)color_p);
lv_disp_flush_ready(disp_drv);
}
#ifndef CONFIG_LV_COLOR_DEPTH_16
void lvgl_set_px_cb_16bit(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x,
lv_coord_t y, lv_color_t color, lv_opa_t opa)
{
uint16_t *buf_xy = (uint16_t *)(buf + x * 2U + y * 2U * buf_w);
*buf_xy = lv_color_to16(color);
}
#endif

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <lvgl.h>
#include "lvgl_display.h"
void lvgl_flush_cb_24bit(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_drv->user_data;
uint16_t w = area->x2 - area->x1 + 1;
uint16_t h = area->y2 - area->y1 + 1;
struct display_buffer_descriptor desc;
desc.buf_size = w * 3U * h;
desc.width = w;
desc.pitch = w;
desc.height = h;
display_write(data->display_dev, area->x1, area->y1, &desc, (void *)color_p);
lv_disp_flush_ready(disp_drv);
}
void lvgl_set_px_cb_24bit(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x,
lv_coord_t y, lv_color_t color, lv_opa_t opa)
{
uint8_t *buf_xy = buf + x * 3U + y * 3U * buf_w;
lv_color32_t converted_color;
#ifdef CONFIG_LV_COLOR_DEPTH_32
if (opa != LV_OPA_COVER) {
lv_color_t mix_color;
mix_color.ch.red = *buf_xy;
mix_color.ch.green = *(buf_xy + 1);
mix_color.ch.blue = *(buf_xy + 2);
color = lv_color_mix(color, mix_color, opa);
}
#endif
converted_color.full = lv_color_to32(color);
*buf_xy = converted_color.ch.red;
*(buf_xy + 1) = converted_color.ch.green;
*(buf_xy + 2) = converted_color.ch.blue;
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <lvgl.h>
#include "lvgl_display.h"
void lvgl_flush_cb_32bit(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_drv->user_data;
uint16_t w = area->x2 - area->x1 + 1;
uint16_t h = area->y2 - area->y1 + 1;
struct display_buffer_descriptor desc;
desc.buf_size = w * 4U * h;
desc.width = w;
desc.pitch = w;
desc.height = h;
display_write(data->display_dev, area->x1, area->y1, &desc, (void *)color_p);
lv_disp_flush_ready(disp_drv);
}
#ifndef CONFIG_LV_COLOR_DEPTH_32
void lvgl_set_px_cb_32bit(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x,
lv_coord_t y, lv_color_t color, lv_opa_t opa)
{
uint32_t *buf_xy = (uint32_t *)(buf + x * 4U + y * 4U * buf_w);
if (opa == LV_OPA_COVER) {
/* Do not mix if not required */
*buf_xy = lv_color_to32(color);
} else {
lv_color_t bg_color = *((lv_color_t *)buf_xy);
*buf_xy = lv_color_to32(lv_color_mix(color, bg_color, opa));
}
}
#endif

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <lvgl.h>
#include "lvgl_display.h"
void lvgl_flush_cb_mono(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
uint16_t w = area->x2 - area->x1 + 1;
uint16_t h = area->y2 - area->y1 + 1;
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_drv->user_data;
const struct device *display_dev = data->display_dev;
struct display_buffer_descriptor desc;
const bool is_epd = data->cap.screen_info & SCREEN_INFO_EPD;
const bool is_last = lv_disp_flush_is_last(disp_drv);
if (is_epd && !data->blanking_on && !is_last) {
/*
* Turn on display blanking when using an EPD
* display. This prevents updates and the associated
* flicker if the screen is rendered in multiple
* steps.
*/
display_blanking_on(display_dev);
data->blanking_on = true;
}
desc.buf_size = (w * h) / 8U;
desc.width = w;
desc.pitch = w;
desc.height = h;
display_write(display_dev, area->x1, area->y1, &desc, (void *)color_p);
if (data->cap.screen_info & SCREEN_INFO_DOUBLE_BUFFER) {
display_write(display_dev, area->x1, area->y1, &desc, (void *)color_p);
}
if (is_epd && is_last && data->blanking_on) {
/*
* The entire screen has now been rendered. Update the
* display by disabling blanking.
*/
display_blanking_off(display_dev);
data->blanking_on = false;
}
lv_disp_flush_ready(disp_drv);
}
void lvgl_set_px_cb_mono(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x,
lv_coord_t y, lv_color_t color, lv_opa_t opa)
{
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_drv->user_data;
uint8_t *buf_xy;
uint8_t bit;
if (data->cap.screen_info & SCREEN_INFO_MONO_VTILED) {
buf_xy = buf + x + y / 8 * buf_w;
if (data->cap.screen_info & SCREEN_INFO_MONO_MSB_FIRST) {
bit = 7 - y % 8;
} else {
bit = y % 8;
}
} else {
buf_xy = buf + x / 8 + y * buf_w / 8;
if (data->cap.screen_info & SCREEN_INFO_MONO_MSB_FIRST) {
bit = 7 - x % 8;
} else {
bit = x % 8;
}
}
if (data->cap.current_pixel_format == PIXEL_FORMAT_MONO10) {
if (color.full == 0) {
*buf_xy &= ~BIT(bit);
} else {
*buf_xy |= BIT(bit);
}
} else {
if (color.full == 0) {
*buf_xy |= BIT(bit);
} else {
*buf_xy &= ~BIT(bit);
}
}
}
void lvgl_rounder_cb_mono(lv_disp_drv_t *disp_drv, lv_area_t *area)
{
struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_drv->user_data;
if (data->cap.screen_info & SCREEN_INFO_X_ALIGNMENT_WIDTH) {
area->x1 = 0;
area->x2 = data->cap.x_resolution - 1;
} else {
if (data->cap.screen_info & SCREEN_INFO_MONO_VTILED) {
area->y1 &= ~0x7;
area->y2 |= 0x7;
} else {
area->x1 &= ~0x7;
area->x2 |= 0x7;
}
}
}

226
modules/lvgl/lvgl_fs.c Normal file
View file

@ -0,0 +1,226 @@
/*
* Copyright (c) 2018-2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <lvgl.h>
#include <zephyr/kernel.h>
#include <zephyr/fs/fs.h>
#include <stdlib.h>
#include "lvgl_fs.h"
static bool lvgl_fs_ready(struct _lv_fs_drv_t *drv)
{
return true;
}
static lv_fs_res_t errno_to_lv_fs_res(int err)
{
switch (err) {
case 0:
return LV_FS_RES_OK;
case -EIO:
/*Low level hardware error*/
return LV_FS_RES_HW_ERR;
case -EBADF:
/*Error in the file system structure */
return LV_FS_RES_FS_ERR;
case -ENOENT:
/*Driver, file or directory is not exists*/
return LV_FS_RES_NOT_EX;
case -EFBIG:
/*Disk full*/
return LV_FS_RES_FULL;
case -EACCES:
/*Access denied. Check 'fs_open' modes and write protect*/
return LV_FS_RES_DENIED;
case -EBUSY:
/*The file system now can't handle it, try later*/
return LV_FS_RES_BUSY;
case -ENOMEM:
/*Not enough memory for an internal operation*/
return LV_FS_RES_OUT_OF_MEM;
case -EINVAL:
/*Invalid parameter among arguments*/
return LV_FS_RES_INV_PARAM;
default:
return LV_FS_RES_UNKNOWN;
}
}
static void *lvgl_fs_open(struct _lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode)
{
int err;
int zmode = FS_O_CREATE;
void *file;
/* LVGL is passing absolute paths without the root slash add it back
* by decrementing the path pointer.
*/
path--;
zmode |= (mode & LV_FS_MODE_WR) ? FS_O_WRITE : 0;
zmode |= (mode & LV_FS_MODE_RD) ? FS_O_READ : 0;
file = malloc(sizeof(struct fs_file_t));
if (!file) {
return NULL;
}
fs_file_t_init((struct fs_file_t *)file);
err = fs_open((struct fs_file_t *)file, path, zmode);
if (err) {
return NULL;
}
return file;
}
static lv_fs_res_t lvgl_fs_close(struct _lv_fs_drv_t *drv, void *file)
{
int err;
err = fs_close((struct fs_file_t *)file);
return errno_to_lv_fs_res(err);
}
static lv_fs_res_t lvgl_fs_read(struct _lv_fs_drv_t *drv, void *file, void *buf, uint32_t btr,
uint32_t *br)
{
int err;
err = fs_read((struct fs_file_t *)file, buf, btr);
if (err > 0) {
if (br != NULL) {
*br = err;
}
err = 0;
} else if (br != NULL) {
*br = 0U;
}
return errno_to_lv_fs_res(err);
}
static lv_fs_res_t lvgl_fs_write(struct _lv_fs_drv_t *drv, void *file, const void *buf,
uint32_t btw, uint32_t *bw)
{
int err;
err = fs_write((struct fs_file_t *)file, buf, btw);
if (err == btw) {
if (bw != NULL) {
*bw = btw;
}
err = 0;
} else if (err < 0) {
if (bw != NULL) {
*bw = 0U;
}
} else {
if (bw != NULL) {
*bw = err;
}
err = -EFBIG;
}
return errno_to_lv_fs_res(err);
}
static lv_fs_res_t lvgl_fs_seek(struct _lv_fs_drv_t *drv, void *file, uint32_t pos,
lv_fs_whence_t whence)
{
int err, fs_whence;
switch (whence) {
case LV_FS_SEEK_END:
fs_whence = FS_SEEK_END;
break;
case LV_FS_SEEK_CUR:
fs_whence = FS_SEEK_CUR;
break;
case LV_FS_SEEK_SET:
default:
fs_whence = FS_SEEK_SET;
break;
}
err = fs_seek((struct fs_file_t *)file, pos, fs_whence);
return errno_to_lv_fs_res(err);
}
static lv_fs_res_t lvgl_fs_tell(struct _lv_fs_drv_t *drv, void *file, uint32_t *pos_p)
{
*pos_p = fs_tell((struct fs_file_t *)file);
return LV_FS_RES_OK;
}
static void *lvgl_fs_dir_open(struct _lv_fs_drv_t *drv, const char *path)
{
void *dir;
int err;
/* LVGL is passing absolute paths without the root slash add it back
* by decrementing the path pointer.
*/
path--;
dir = malloc(sizeof(struct fs_dir_t));
if (!dir) {
return NULL;
}
fs_dir_t_init((struct fs_dir_t *)dir);
err = fs_opendir((struct fs_dir_t *)dir, path);
if (err) {
return NULL;
}
return dir;
}
static lv_fs_res_t lvgl_fs_dir_read(struct _lv_fs_drv_t *drv, void *dir, char *fn)
{
/* LVGL expects a string as return parameter but the format of the
* string is not documented.
*/
return LV_FS_RES_NOT_IMP;
}
static lv_fs_res_t lvgl_fs_dir_close(struct _lv_fs_drv_t *drv, void *dir)
{
int err;
err = fs_closedir((struct fs_dir_t *)dir);
free(dir);
return errno_to_lv_fs_res(err);
}
static lv_fs_drv_t fs_drv;
void lvgl_fs_init(void)
{
lv_fs_drv_init(&fs_drv);
/* LVGL uses letter based mount points, just pass the root slash as a
* letter. Note that LVGL will remove the drive letter, or in this case
* the root slash, from the path passed via the FS callbacks.
* Zephyr FS API assumes this slash is present so we will need to add
* it back.
*/
fs_drv.letter = '/';
fs_drv.ready_cb = lvgl_fs_ready;
fs_drv.open_cb = lvgl_fs_open;
fs_drv.close_cb = lvgl_fs_close;
fs_drv.read_cb = lvgl_fs_read;
fs_drv.write_cb = lvgl_fs_write;
fs_drv.seek_cb = lvgl_fs_seek;
fs_drv.tell_cb = lvgl_fs_tell;
fs_drv.dir_open_cb = lvgl_fs_dir_open;
fs_drv.dir_read_cb = lvgl_fs_dir_read;
fs_drv.dir_close_cb = lvgl_fs_dir_close;
lv_fs_drv_register(&fs_drv);
}

20
modules/lvgl/lvgl_fs.h Normal file
View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2018 Jan Van Winkel <jan.van_winkel@dxplore.eu>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_MODULES_LVGL_FS_H_
#define ZEPHYR_MODULES_LVGL_FS_H_
#ifdef __cplusplus
extern "C" {
#endif
void lvgl_fs_init(void);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_MODULES_LVGL_FS_H_ */

67
modules/lvgl/lvgl_mem.c Normal file
View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2018 Jan Van Winkel <jan.van_winkel@dxplore.eu>
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "lvgl_mem.h"
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include <zephyr/sys/sys_heap.h>
#define HEAP_BYTES (CONFIG_LV_Z_MEM_POOL_MAX_SIZE * CONFIG_LV_Z_MEM_POOL_NUMBER_BLOCKS)
static char lvgl_heap_mem[HEAP_BYTES] __aligned(8);
static struct sys_heap lvgl_heap;
static struct k_spinlock lvgl_heap_lock;
void *lvgl_malloc(size_t size)
{
k_spinlock_key_t key;
void *ret;
key = k_spin_lock(&lvgl_heap_lock);
ret = sys_heap_alloc(&lvgl_heap, size);
k_spin_unlock(&lvgl_heap_lock, key);
return ret;
}
void *lvgl_realloc(void *ptr, size_t size)
{
k_spinlock_key_t key;
void *ret;
key = k_spin_lock(&lvgl_heap_lock);
ret = sys_heap_realloc(&lvgl_heap, ptr, size);
k_spin_unlock(&lvgl_heap_lock, key);
return ret;
}
void lvgl_free(void *ptr)
{
k_spinlock_key_t key;
key = k_spin_lock(&lvgl_heap_lock);
sys_heap_free(&lvgl_heap, ptr);
k_spin_unlock(&lvgl_heap_lock, key);
}
void lvgl_print_heap_info(bool dump_chunks)
{
k_spinlock_key_t key;
key = k_spin_lock(&lvgl_heap_lock);
sys_heap_print_info(&lvgl_heap, dump_chunks);
k_spin_unlock(&lvgl_heap_lock, key);
}
static int lvgl_heap_init(void)
{
sys_heap_init(&lvgl_heap, &lvgl_heap_mem[0], HEAP_BYTES);
return 0;
}
SYS_INIT(lvgl_heap_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

29
modules/lvgl/lvgl_mem.h Normal file
View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018 Jan Van Winkel <jan.van_winkel@dxplore.eu>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_MODULES_LVGL_MEM_H_
#define ZEPHYR_MODULES_LVGL_MEM_H_
#include <stdlib.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
void *lvgl_malloc(size_t size);
void *lvgl_realloc(void *ptr, size_t size);
void lvgl_free(void *ptr);
void lvgl_print_heap_info(bool dump_chunks);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_MODULES_LVGL_MEM_H_ */

204
modules/lvgl/lvgl_shell.c Normal file
View file

@ -0,0 +1,204 @@
/*
* Copyright (c) 2023 Fabian Blatz <fabianblatz@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/shell/shell.h>
#include <lvgl.h>
#include <autoconf.h>
#ifdef CONFIG_LV_Z_MEM_POOL_SYS_HEAP
#include "lvgl_mem.h"
#endif
#ifdef CONFIG_LV_USE_MONKEY
static lv_monkey_t *lvgl_monkeys[CONFIG_LV_Z_MAX_MONKEY_COUNT];
static const char *lvgl_monkey_indev_as_string(lv_monkey_t *monkey)
{
lv_indev_t *input_device;
input_device = lv_monkey_get_indev(monkey);
if (!input_device || !input_device->driver) {
return "unknown";
}
switch (input_device->driver->type) {
case LV_INDEV_TYPE_POINTER:
return "pointer";
case LV_INDEV_TYPE_KEYPAD:
return "keypad";
case LV_INDEV_TYPE_BUTTON:
return "button";
case LV_INDEV_TYPE_ENCODER:
return "encoder";
default:
return "unknown";
}
}
static int lvgl_monkey_indev_from_string(const char *str, lv_indev_type_t *input_device)
{
if (strcmp(str, "pointer") == 0) {
*input_device = LV_INDEV_TYPE_POINTER;
} else if (strcmp(str, "keypad") == 0) {
*input_device = LV_INDEV_TYPE_KEYPAD;
} else if (strcmp(str, "button") == 0) {
*input_device = LV_INDEV_TYPE_BUTTON;
} else if (strcmp(str, "encoder") == 0) {
*input_device = LV_INDEV_TYPE_ENCODER;
} else {
return -EINVAL;
}
return 0;
}
static void dump_monkey_info(const struct shell *sh)
{
shell_print(sh, "id device active");
for (size_t i = 0; i < CONFIG_LV_Z_MAX_MONKEY_COUNT; i++) {
if (lvgl_monkeys[i] != NULL) {
shell_print(sh, "%-4u %-9s %-3s", i,
lvgl_monkey_indev_as_string(lvgl_monkeys[i]),
lv_monkey_get_enable(lvgl_monkeys[i]) ? "yes" : "no");
}
}
}
static int cmd_lvgl_monkey(const struct shell *sh, size_t argc, char *argv[])
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
dump_monkey_info(sh);
shell_print(sh, "");
shell_help(sh);
return SHELL_CMD_HELP_PRINTED;
}
static int cmd_lvgl_monkey_create(const struct shell *sh, size_t argc, char *argv[])
{
bool created_monkey = false;
lv_monkey_config_t default_config;
lv_monkey_config_init(&default_config);
if (argc == 2) {
if (lvgl_monkey_indev_from_string(argv[1], &default_config.type) < 0) {
shell_error(sh, "Invalid monkey input device %s", argv[1]);
shell_help(sh);
return SHELL_CMD_HELP_PRINTED;
}
}
for (size_t i = 0; i < CONFIG_LV_Z_MAX_MONKEY_COUNT; i++) {
if (lvgl_monkeys[i] == NULL) {
lvgl_monkeys[i] = lv_monkey_create(&default_config);
lv_monkey_set_enable(lvgl_monkeys[i], true);
created_monkey = true;
break;
}
}
if (!created_monkey) {
shell_error(sh, "Error creating monkey instance");
return -ENOSPC;
}
dump_monkey_info(sh);
return 0;
}
static int cmd_lvgl_monkey_set(const struct shell *sh, size_t argc, char *argv[])
{
int index;
index = atoi(argv[1]);
if (index < 0 || index >= CONFIG_LV_Z_MAX_MONKEY_COUNT || lvgl_monkeys[index] == NULL) {
shell_error(sh, "Invalid monkey index");
return -ENOEXEC;
}
if (strcmp(argv[2], "active") == 0) {
lv_monkey_set_enable(lvgl_monkeys[index], true);
} else if (strcmp(argv[2], "inactive") == 0) {
lv_monkey_set_enable(lvgl_monkeys[index], false);
} else {
shell_error(sh, "Invalid monkey state %s", argv[2]);
shell_help(sh);
return SHELL_CMD_HELP_PRINTED;
}
dump_monkey_info(sh);
return 0;
}
#endif /* CONFIG_LV_USE_MONKEY */
static int cmd_lvgl_stats(const struct shell *sh, size_t argc, char *argv[])
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
shell_help(sh);
return SHELL_CMD_HELP_PRINTED;
}
static int cmd_lvgl_stats_memory(const struct shell *sh, size_t argc, char *argv[])
{
#ifdef CONFIG_LV_Z_MEM_POOL_SYS_HEAP
bool dump_chunks = false;
if (argc == 2) {
if (strcmp(argv[1], "-c") == 0) {
dump_chunks = true;
} else {
shell_error(sh, "unsupported option %s", argv[1]);
shell_help(sh);
return SHELL_CMD_HELP_PRINTED;
}
}
lvgl_print_heap_info(dump_chunks);
return 0;
#else
ARG_UNUSED(argc);
ARG_UNUSED(argv);
shell_error(sh, "Set CONFIG_LV_Z_MEM_POOL_SYS_HEAP to enable memory statistics support.");
return -ENOTSUP;
#endif
}
SHELL_STATIC_SUBCMD_SET_CREATE(lvgl_cmd_stats,
SHELL_CMD_ARG(memory, NULL,
"Show LVGL memory statistics\n"
"Usage: lvgl stats memory [-c]\n"
"-c dump chunk information",
cmd_lvgl_stats_memory, 1, 1),
SHELL_SUBCMD_SET_END);
#ifdef CONFIG_LV_USE_MONKEY
SHELL_STATIC_SUBCMD_SET_CREATE(
lvgl_cmd_monkey,
SHELL_CMD_ARG(create, NULL,
"Create a new monkey instance (default: pointer)\n"
"Usage: lvgl monkey create [pointer|keypad|button|encoder]",
cmd_lvgl_monkey_create, 1, 1),
SHELL_CMD_ARG(set, NULL,
"Activate/deactive a monkey instance\n"
"Usage: lvgl monkey set <index> <active|inactive>\n",
cmd_lvgl_monkey_set, 3, 0),
SHELL_SUBCMD_SET_END);
#endif /* CONFIG_LV_USE_MONKEY */
SHELL_STATIC_SUBCMD_SET_CREATE(
lvgl_cmds, SHELL_CMD(stats, &lvgl_cmd_stats, "Show LVGL statistics", cmd_lvgl_stats),
#ifdef CONFIG_LV_USE_MONKEY
SHELL_CMD(monkey, &lvgl_cmd_monkey, "LVGL monkey testing", cmd_lvgl_monkey),
#endif /* CONFIG_LV_USE_MONKEY */
SHELL_SUBCMD_SET_END);
SHELL_CMD_REGISTER(lvgl, &lvgl_cmds, "LVGL shell commands", NULL);

View file

@ -270,7 +270,7 @@ manifest:
revision: ce57712f3e426bbbb13acaec97b45369f716f43a
path: modules/lib/loramac-node
- name: lvgl
revision: 5da257f782a8f9c6e265bdc60ebc2a93fdee24de
revision: 8a6a2d1d29d17d1e4bdc94c243c146a39d635fdd
path: modules/lib/gui/lvgl
- name: lz4
revision: 8e303c264fc21c2116dc612658003a22e933124d