Update LVGL to v9.1.0
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
=============================
|
||||
Espressif (ESP32 chip series)
|
||||
=============================
|
||||
|
||||
LVGL can be used and configured as a standard `ESP-IDF <https://github.com/espressif/esp-idf>`__ component.
|
||||
|
||||
More information about ESP-IDF build system can be found `here <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html>`__.
|
||||
|
||||
|
||||
LVGL demo project for ESP32
|
||||
---------------------------
|
||||
|
||||
We've created `lv_port_esp32 <https://github.com/lvgl/lv_port_esp32>`__,
|
||||
a project using ESP-IDF and LVGL to show one of the demos from
|
||||
`demos <https://github.com/lvgl/lvgl/demos>`__. You can configure the
|
||||
project to use one of the many supported display controllers and targets
|
||||
(chips).
|
||||
|
||||
See `lvgl_esp32_drivers <https://github.com/lvgl/lvgl_esp32_drivers>`__
|
||||
repository for a complete list of supported display and indev (touch)
|
||||
controllers and targets.
|
||||
|
||||
|
||||
Using LVGL in your ESP-IDF project
|
||||
----------------------------------
|
||||
|
||||
Prerequisites
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
- ESP-IDF v4.1 and above
|
||||
- ESP evaluation board with a display
|
||||
|
||||
|
||||
Obtaining LVGL
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
**Option 1:** git submodule
|
||||
|
||||
Simply clone LVGL into your ``project_root/components`` directory and it
|
||||
will be automatically integrated into the project. If the project is a
|
||||
git repository you can include LVGL as a git submodule:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
git submodule add https://github.com/lvgl/lvgl.git components/lvgl
|
||||
|
||||
The above command will clone LVGL's main repository into the
|
||||
``components/lvgl`` directory. LVGL includes a ``CMakeLists.txt`` file
|
||||
that sets some configuration options so you can use LVGL right away.
|
||||
|
||||
**Option 2:** IDF Component Manager
|
||||
|
||||
LVGL is also distributed through `IDF Component Manager <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html>`__.
|
||||
It allows users to seamlessly integrate `LVGL component <https://components.espressif.com/component/lvgl/lvgl>`__ into
|
||||
their project with following command:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
idf.py add-dependency lvgl/lvgl>=8.*
|
||||
|
||||
During next project build, LVGL component will be fetched from the
|
||||
component registry and added to project build.
|
||||
|
||||
|
||||
Configuration
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
When you are ready to configure LVGL, launch the configuration menu with
|
||||
``idf.py menuconfig`` in your project root directory, go to
|
||||
``Component config`` and then ``LVGL configuration``.
|
||||
|
||||
|
||||
Using lvgl_esp32_drivers in ESP-IDF project
|
||||
-------------------------------------------
|
||||
|
||||
You can also add ``lvgl_esp32_drivers`` as a "component". This component
|
||||
should be located inside a directory named "components" in your project
|
||||
root directory.
|
||||
|
||||
When your project is a git repository you can include
|
||||
``lvgl_esp32_drivers`` as a git submodule:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
git submodule add https://github.com/lvgl/lvgl_esp32_drivers.git components/lvgl_esp32_drivers
|
||||
@@ -0,0 +1,10 @@
|
||||
============
|
||||
Chip vendors
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
nxp
|
||||
stm32
|
||||
espressif
|
||||
@@ -0,0 +1,398 @@
|
||||
===
|
||||
NXP
|
||||
===
|
||||
|
||||
NXP has integrated LVGL into the MCUXpresso SDK packages for several of our
|
||||
microcontrollers as an optional software component, allowing easy evaluation and
|
||||
migration into your product design. LVGL is a free and open-source embedded
|
||||
graphic library with features that enable you need to create embedded GUIs with
|
||||
intuitive graphical elements, beautiful visual effects and a low memory
|
||||
footprint. The complete graphic framework includes a variety of widgets for you
|
||||
to use in the creation of your GUI, and supports more advanced functions such as
|
||||
animations and anti-aliasing.
|
||||
|
||||
LVGL enables graphics in our free GUI Guider UI tool. It's available for use
|
||||
with NXP’s general purpose and crossover microcontrollers, providing developers
|
||||
with a tool for creating complete, high quality GUI applications with LVGL.
|
||||
|
||||
Creating new project with LVGL
|
||||
------------------------------
|
||||
|
||||
`Download an SDK for a supported board <https://www.nxp.com/design/software/embedded-software/littlevgl-open-source-graphics-library:LITTLEVGL-OPEN-SOURCE-GRAPHICS-LIBRARY?&tid=vanLITTLEVGL-OPEN-SOURCE-GRAPHICS-LIBRARY>`__
|
||||
today and get started with your next GUI application. It comes fully configured
|
||||
with LVGL (and with PXP/VGLite support if the modules are present), no
|
||||
additional integration work is required.
|
||||
|
||||
HW acceleration for NXP iMX RT platforms
|
||||
----------------------------------------
|
||||
|
||||
Depending on the RT platform used, the acceleration can be done by NXP PXP
|
||||
(PiXel Pipeline) and/or the Verisilicon GPU through an API named VGLite. Each
|
||||
accelerator has its own context that allows them to be used individually as well
|
||||
simultaneously (in LVGL multithreading mode).
|
||||
|
||||
PXP accelerator
|
||||
~~~~~~~~~~~~~~~
|
||||
Basic configuration:
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Select NXP PXP engine in "lv_conf.h": Set :c:macro:`LV_USE_DRAW_PXP` to `1`.
|
||||
- Enable PXP asserts in "lv_conf.h": Set :c:macro: `LV_USE_PXP_ASSERT` to `1`.
|
||||
There are few PXP assertions that can stop the program execution in case the
|
||||
c:macro: `LV_ASSERT_HANDLER` is set to `while(1);` (Halt by default). Else,
|
||||
there will be logged just an error message via `LV_LOG_ERROR`.
|
||||
- If :c:macro:`SDK_OS_FREE_RTOS` symbol is defined, FreeRTOS implementation
|
||||
will be used, otherwise bare metal code will be included.
|
||||
|
||||
Basic initialization:
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
PXP draw initialization is done automatically in :cpp:func:`lv_init()` once the
|
||||
PXP is enabled, no user code is required:
|
||||
|
||||
.. code:: c
|
||||
|
||||
#if LV_USE_DRAW_PXP
|
||||
lv_draw_pxp_init();
|
||||
#endif
|
||||
|
||||
During PXP initialization, a new draw unit `lv_draw_pxp_unit_t` will be created
|
||||
with the additional callbacks:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_draw_pxp_unit_t * draw_pxp_unit = lv_draw_create_unit(sizeof(lv_draw_pxp_unit_t));
|
||||
draw_pxp_unit->base_unit.evaluate_cb = _pxp_evaluate;
|
||||
draw_pxp_unit->base_unit.dispatch_cb = _pxp_dispatch;
|
||||
draw_pxp_unit->base_unit.delete_cb = _pxp_delete;
|
||||
|
||||
and an addition thread `_pxp_render_thread_cb()` will be spawned in order to
|
||||
handle the supported draw tasks.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#if LV_USE_OS
|
||||
lv_thread_init(&draw_pxp_unit->thread, LV_THREAD_PRIO_HIGH, _pxp_render_thread_cb, 2 * 1024, draw_pxp_unit);
|
||||
#endif
|
||||
|
||||
If `LV_USE_OS` is not defined, then no additional draw thread will be created
|
||||
and the PXP drawing task will get executed on the same LVGL main thread.
|
||||
|
||||
`_pxp_evaluate()` will get called after each task is being created and will
|
||||
analyze if the task is supported by PXP or not. If it is supported, then an
|
||||
preferred score and the draw unit id will be set to the task. An `score` equal
|
||||
to `100` is the default CPU score. Smaller score means that PXP is capable of
|
||||
drawing it faster.
|
||||
|
||||
`_pxp_dispatch()` is the PXP dispatcher callback, it will take a ready to draw
|
||||
task (having the `DRAW_UNIT_ID_PXP` set) and will pass the task to the PXP draw
|
||||
unit for processing.
|
||||
|
||||
`_pxp_delete()` will cleanup the PXP draw unit.
|
||||
|
||||
Features supported:
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Several drawing features in LVGL can be offloaded to the PXP engine. The CPU is
|
||||
available for other operations while the PXP is running. RTOS is required to
|
||||
block the LVGL drawing thread and switch to another task or suspend the CPU for
|
||||
power savings.
|
||||
|
||||
Supported draw tasks are available in "src/draw/nxp/pxp/lv_draw_pxp.c":
|
||||
|
||||
.. code:: c
|
||||
|
||||
switch(t->type) {
|
||||
case LV_DRAW_TASK_TYPE_FILL:
|
||||
lv_draw_pxp_fill(draw_unit, t->draw_dsc, &t->area);
|
||||
break;
|
||||
case LV_DRAW_TASK_TYPE_IMAGE:
|
||||
lv_draw_pxp_img(draw_unit, t->draw_dsc, &t->area);
|
||||
break;
|
||||
case LV_DRAW_TASK_TYPE_LAYER:
|
||||
lv_draw_pxp_layer(draw_unit, t->draw_dsc, &t->area);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Additional, the screen rotation can be handled by the PXP:
|
||||
|
||||
.. code::c
|
||||
|
||||
void lv_draw_pxp_rotate(const void * src_buf, void * dest_buf, int32_t src_width, int32_t src_height,
|
||||
int32_t src_stride, int32_t dest_stride, lv_display_rotation_t rotation,
|
||||
lv_color_format_t cf);
|
||||
|
||||
- Fill area with color (w/o radius, w/o gradient) + optional opacity.
|
||||
- Blit source image RGB565/ARGB888/XRGB8888 over destination.
|
||||
RGB565/RGB888/ARGB888/XRGB8888 + optional opacity.
|
||||
- Recolor source image RGB565.
|
||||
- Scale and rotate (90, 180, 270 degree) source image RGB565.
|
||||
- Blending layers (w/ same supported formats as blitting).
|
||||
- Rotate screen (90, 180, 270 degree).
|
||||
|
||||
Known limitations:
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- PXP can only rotate at 90x angles.
|
||||
- Rotation is not supported for images unaligned to blocks of 16x16 pixels. PXP
|
||||
is set to process 16x16 blocks to optimize the system for memory bandwidth and
|
||||
image processing time. The output engine essentially truncates any output
|
||||
pixels after the desired number of pixels has been written. When rotating a
|
||||
source image and the output is not divisible by the block size, the incorrect
|
||||
pixels could be truncated and the final output image can look shifted.
|
||||
- Recolor or transformation for images w/ opacity or alpha channel can't be
|
||||
obtained in a single PXP pipeline configuration. Two or multiple steps would
|
||||
be required.
|
||||
- Buffer address must be aligned to 64 bytes: set :c:macro:`LV_DRAW_BUF_ALIGN`
|
||||
to `64` in "lv_conf.h".
|
||||
No stride alignment is required: set :c:macro:`LV_DRAW_BUF_STRIDE_ALIGN` to
|
||||
`1` in "lv_conf.h".
|
||||
|
||||
Project setup:
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
- Add PXP related source files (and corresponding headers if available) to
|
||||
project:
|
||||
|
||||
- "src/draw/nxp/pxp/lv_draw_buf_pxp.c": draw buffer callbacks
|
||||
- "src/draw/nxp/pxp/lv_draw_pxp_fill.c": fill area
|
||||
- "src/draw/nxp/pxp/lv_draw_pxp_img.c": blit image (w/ optional recolor or
|
||||
transformation)
|
||||
- "src/draw/nxp/pxp/lv_draw_pxp_layer.c": layer blending
|
||||
- "src/draw/nxp/pxp/lv_draw_pxp.c": draw unit initialization
|
||||
- "src/draw/nxp/pxp/lv_pxp_cfg.c": init, deinit, run/wait PXP device
|
||||
- "src/draw/nxp/pxp/lv_pxp_osa.c": OS abstraction (FreeRTOS or bare metal)
|
||||
- "src/draw/nxp/pxp/lv_pxp_utils.c": function helpers
|
||||
|
||||
- PXP related code depends on two drivers provided by MCU SDK. These drivers
|
||||
need to be added to project:
|
||||
|
||||
- fsl_pxp.c: PXP driver
|
||||
- fsl_cache.c: CPU cache handling functions
|
||||
|
||||
PXP default configuration:
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Implementation depends on multiple OS-specific functions. The struct
|
||||
:cpp:struct:`pxp_cfg_t` with callback pointers is used as a parameter for the
|
||||
:cpp:func:`lv_pxp_init()` function. Default implementation for FreeRTOS and
|
||||
bare metal is provided in lv_pxp_osa.c.
|
||||
|
||||
- :cpp:func:`pxp_interrupt_init()`: Initialize PXP interrupt (HW setup,
|
||||
OS setup)
|
||||
- :cpp:func:`pxp_interrupt_deinit()`: Deinitialize PXP interrupt (HW setup,
|
||||
OS setup)
|
||||
- :cpp:func:`pxp_run()`: Start PXP job. Use OS-specific mechanism to block
|
||||
drawing thread.
|
||||
- :cpp:func:`pxp_wait()`: Wait for PXP completion.
|
||||
|
||||
VGLite accelerator
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Extra drawing features in LVGL can be handled by the VGLite engine. The
|
||||
CPU is available for other operations while the VGLite is running. An
|
||||
RTOS is required to block the LVGL drawing thread and switch to another
|
||||
task or suspend the CPU for power savings.
|
||||
|
||||
Basic configuration:
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Select NXP VGLite engine in "lv_conf.h": Set :c:macro:`LV_USE_DRAW_VGLITE` to
|
||||
`1`. :c:macro:`SDK_OS_FREE_RTOS` symbol needs to be defined so that FreeRTOS
|
||||
driver osal implementation will be enabled.
|
||||
- Enable VGLite asserts in "lv_conf.h": Set :c:macro: `LV_USE_VGLITE_ASSERT` to
|
||||
`1`.
|
||||
VGLite assertions will verify the driver API status code and in any error, it
|
||||
can stop the program execution in case the c:macro: `LV_ASSERT_HANDLER` is set
|
||||
to `while(1);` (Halt by default). Else, there will be logged just an error
|
||||
message via `LV_LOG_ERROR`.
|
||||
|
||||
Basic initialization:
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Initialize VGLite GPU before calling :cpp:func:`lv_init()` by specifying the
|
||||
width/height of tessellation window. The default values for tesselation width
|
||||
and height, and command buffer size are in the SDK file "vglite_support.h".
|
||||
|
||||
.. code:: c
|
||||
|
||||
#if LV_USE_GPU_NXP_VG_LITE
|
||||
#include "vg_lite.h"
|
||||
#include "vglite_support.h"
|
||||
#endif
|
||||
...
|
||||
#if LV_USE_DRAW_VGLITE
|
||||
if(vg_lite_init(DEFAULT_VG_LITE_TW_WIDTH, DEFAULT_VG_LITE_TW_HEIGHT) != VG_LITE_SUCCESS)
|
||||
{
|
||||
PRINTF("VGLite init error. STOP.");
|
||||
vg_lite_close();
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
if (vg_lite_set_command_buffer_size(VG_LITE_COMMAND_BUFFER_SIZE) != VG_LITE_SUCCESS)
|
||||
{
|
||||
PRINTF("VGLite set command buffer. STOP.");
|
||||
vg_lite_close();
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
#endif
|
||||
|
||||
VGLite draw initialization is done automatically in :cpp:func:`lv_init()` once
|
||||
the VGLite is enabled, no user code is required:
|
||||
|
||||
.. code:: c
|
||||
|
||||
#if LV_USE_DRAW_VGLITE
|
||||
lv_draw_vglite_init();
|
||||
#endif
|
||||
|
||||
During VGLite initialization, a new draw unit `lv_draw_vglite_unit_t` will be
|
||||
created with the additional callbacks:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_draw_vglite_unit_t * draw_vglite_unit = lv_draw_create_unit(sizeof(lv_draw_vglite_unit_t));
|
||||
draw_vglite_unit->base_unit.evaluate_cb = _vglite_evaluate;
|
||||
draw_vglite_unit->base_unit.dispatch_cb = _vglite_dispatch;
|
||||
draw_vglite_unit->base_unit.delete_cb = _vglite_delete;
|
||||
|
||||
and an addition thread `_vglite_render_thread_cb()` will be spawned in order to
|
||||
handle the supported draw tasks.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#if LV_USE_OS
|
||||
lv_thread_init(&draw_vglite_unit->thread, LV_THREAD_PRIO_HIGH, _vglite_render_thread_cb, 2 * 1024, draw_vglite_unit);
|
||||
#endif
|
||||
|
||||
If `LV_USE_OS` is not defined, then no additional draw thread will be created
|
||||
and the VGLite drawing task will get executed on the same LVGL main thread.
|
||||
|
||||
`_vglite_evaluate()` will get called after each task is being created and will
|
||||
analyze if the task is supported by VGLite or not. If it is supported, then an
|
||||
preferred score and the draw unit id will be set to the task. An `score` equal
|
||||
to `100` is the default CPU score. Smaller score means that VGLite is capable of
|
||||
drawing it faster.
|
||||
|
||||
`_vglite_dispatch()` is the VGLite dispatcher callback, it will take a ready to
|
||||
draw task (having the `DRAW_UNIT_ID_VGLITE` set) and will pass the task to the
|
||||
VGLite draw unit for processing.
|
||||
|
||||
`_vglite_delete()` will cleanup the VGLite draw unit.
|
||||
|
||||
Advanced configuration:
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Enable VGLite blit split in "lv_conf.h":
|
||||
Set :c:macro: `LV_USE_VGLITE_BLIT_SPLIT` to `1`.
|
||||
Enabling the blit split workaround will mitigate any quality degradation issue
|
||||
on screen's dimension > 352 pixels.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#define VGLITE_BLIT_SPLIT_THR 352
|
||||
|
||||
- By default, the blit split threshold is set to 352. Blits with width or height
|
||||
higher than this value will be done in multiple steps. Value must be multiple
|
||||
of stride alignment in px. For most color formats, the alignment is 16px
|
||||
(except the index formats). Transformation will not be supported once with
|
||||
the blit split.
|
||||
|
||||
- Enable VGLite draw task synchronously in "lv_conf.h":
|
||||
Set :c:macro: `LV_USE_VGLITE_DRAW_ASYNC` to `1`.
|
||||
Multiple draw tasks can be queued and flushed them once to the GPU based on
|
||||
the GPU idle status. If GPU is busy, the task will be queued, and the VGLite
|
||||
dispatcher will ask for a new available task. If GPU is idle, the queue with
|
||||
any pending tasks will be flushed to the GPU. The completion status of draw
|
||||
task will be sent to the main LVGL thread asynchronously.
|
||||
|
||||
Features supported:
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Several drawing features in LVGL can be offloaded to the VGLite engine. The CPU
|
||||
is available for other operations while the GPU is running. RTOS is required to
|
||||
block the LVGL drawing thread and switch to another task or suspend the CPU for
|
||||
power savings.
|
||||
|
||||
Supported draw tasks are available in "src/draw/nxp/pxp/lv_draw_vglite.c":
|
||||
|
||||
.. code:: c
|
||||
|
||||
switch(t->type) {
|
||||
case LV_DRAW_TASK_TYPE_LABEL:
|
||||
lv_draw_vglite_label(draw_unit, t->draw_dsc, &t->area);
|
||||
break;
|
||||
case LV_DRAW_TASK_TYPE_FILL:
|
||||
lv_draw_vglite_fill(draw_unit, t->draw_dsc, &t->area);
|
||||
break;
|
||||
case LV_DRAW_TASK_TYPE_BORDER:
|
||||
lv_draw_vglite_border(draw_unit, t->draw_dsc, &t->area);
|
||||
break;
|
||||
case LV_DRAW_TASK_TYPE_IMAGE:
|
||||
lv_draw_vglite_img(draw_unit, t->draw_dsc, &t->area);
|
||||
break;
|
||||
case LV_DRAW_TASK_TYPE_ARC:
|
||||
lv_draw_vglite_arc(draw_unit, t->draw_dsc, &t->area);
|
||||
break;
|
||||
case LV_DRAW_TASK_TYPE_LINE:
|
||||
lv_draw_vglite_line(draw_unit, t->draw_dsc);
|
||||
break;
|
||||
case LV_DRAW_TASK_TYPE_LAYER:
|
||||
lv_draw_vglite_layer(draw_unit, t->draw_dsc, &t->area);
|
||||
break;
|
||||
case LV_DRAW_TASK_TYPE_TRIANGLE:
|
||||
lv_draw_vglite_triangle(draw_unit, t->draw_dsc);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
All the below opration can be done in addition with optional opacity.
|
||||
- Fill area with color (w/ radius or gradient).
|
||||
- Blit source image (any format from `_vglite_src_cf_supported()`) over
|
||||
destination (any format from `_vglite_dest_cf_supported()`).
|
||||
- Recolor source image.
|
||||
- Scale and rotate (any decimal degree) source image.
|
||||
- Blending layers (w/ same supported formats as blitting).
|
||||
- Draw letters (blit bitmap letters - raster font).
|
||||
- Draw full borders (LV_BORDER_SIDE_FULL).
|
||||
- Draw arcs (w/ rounded edges).
|
||||
- Draw lines (w/ dash or rounded edges).
|
||||
- Draw triangles with color (w/ gradient).
|
||||
|
||||
Known limitations:
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Source image alignment: The byte alignment requirement for a pixel depends on
|
||||
the specific pixel format. Both buffer address and buffer stride must be
|
||||
aligned. As general rule, the alignment is set to 16 pixels. This makes the
|
||||
buffer address alignment to be 32 bytes for RGB565 and 64 bytes for ARGB8888.
|
||||
- For pixel engine (PE) destination, the alignment should be 64 bytes for all
|
||||
tiled (4x4) buffer layouts. The pixel engine has no additional alignment
|
||||
requirement for linear buffer layouts (:c:macro:`VG_LITE_LINEAR`).
|
||||
|
||||
Project setup:
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
- Add VGLite related source files (and corresponding headers if available) to
|
||||
project:
|
||||
|
||||
- "src/draw/nxp/vglite/lv_draw_buf_vglite.c": draw buffer callbacks
|
||||
- "src/draw/nxp/vglite/lv_draw_vglite_arc.c": draw arc
|
||||
- "src/draw/nxp/vglite/lv_draw_vglite_border.c": draw border
|
||||
- "src/draw/nxp/vglite/lv_draw_vglite_fill.c": fill area
|
||||
- "src/draw/nxp/vglite/lv_draw_vglite_img.c": blit image (w/ optional
|
||||
recolor or transformation)
|
||||
- "src/draw/nxp/vglite/lv_draw_vglite_label.c": draw label
|
||||
- "src/draw/nxp/vglite/lv_draw_vglite_layer.c": layer blending
|
||||
- "src/draw/nxp/vglite/lv_draw_vglite_line.c": draw line
|
||||
- "src/draw/nxp/vglite/lv_draw_vglite_triangle.c": draw triangle
|
||||
- "src/draw/nxp/vglite/lv_draw_vglite.c": draw unit initialization
|
||||
- "src/draw/nxp/vglite/lv_vglite_buf.c": init/get vglite buffer
|
||||
- "src/draw/nxp/vglite/lv_vglite_matrix.c": set vglite matrix
|
||||
- "src/draw/nxp/vglite/lv_vglite_path.c": create vglite path data
|
||||
- "src/draw/nxp/vglite/lv_vglite_utils.c": function helpers
|
||||
@@ -0,0 +1,259 @@
|
||||
=====
|
||||
STM32
|
||||
=====
|
||||
|
||||
LVGL Can be added to `STM32CubeIDE <https://www.st.com/en/development-tools/stm32cubeide.html>`__
|
||||
in a similar fashion to any other Eclipse-based IDE.
|
||||
|
||||
Including LVGL in a Project
|
||||
---------------------------
|
||||
|
||||
- Create or open a project in STM32CubeIDE.
|
||||
- Copy the entire LVGL folder to *[project_folder]/Drivers/lvgl*.
|
||||
- In the STM32CubeIDE **Project Explorer** pane: right click on the
|
||||
LVGL folder that you copied (you may need to refresh the view first
|
||||
before it will appear), and select **Add/remove include path…**. If
|
||||
this doesn't appear, or doesn't work, you can review your project
|
||||
include paths under the **Project** -> **Properties** menu, and then
|
||||
navigating to **C/C++ Build** -> **Settings** -> **Include paths**, and
|
||||
ensuring that the LVGL directory is listed.
|
||||
|
||||
Now that the source files are included in your project, follow the
|
||||
instructions for `Porting <https://docs.lvgl.io/master/porting/project.html>`__ your
|
||||
project to create the ``lv_conf.h`` file, and initialise the display.
|
||||
|
||||
Bare Metal Example
|
||||
------------------
|
||||
|
||||
A minimal example using STM32CubeIDE, and HAL. \* When setting up
|
||||
**Pinout and Configuration** using the **Device Configuration Tool**,
|
||||
select **System Core** -> **SYS** and ensure that **Timebase Source** is
|
||||
set to **SysTick**. \* Configure any other peripherals (including the
|
||||
LCD panel), and initialise them in *main.c*. \* ``#include "lvgl.h"`` in
|
||||
the *main.c* file. \* Create some frame buffer(s) as global variables:
|
||||
|
||||
.. code:: c
|
||||
|
||||
//Frame buffers
|
||||
/*Static or global buffer(s). The second buffer is optional*/
|
||||
static lv_color_t buf_1[BUFF_SIZE]; //TODO: Chose a buffer size. DISPLAY_WIDTH * 10 is one suggestion.
|
||||
static lv_color_t buf_2[BUFF_SIZE];
|
||||
|
||||
- In your ``main()`` function, after initialising your CPU,
|
||||
peripherals, and LCD panel, call :cpp:func:`lv_init` to initialise LVGL.
|
||||
You can then create the display driver using
|
||||
:cpp:func:`lv_display_create`, and register the frame buffers using
|
||||
:cpp:func:`lv_display_set_buffers`.
|
||||
|
||||
.. code:: c
|
||||
|
||||
//Initialise LVGL UI library
|
||||
lv_init();
|
||||
|
||||
lv_display_t * disp = lv_display_create(WIDTH, HEIGHT); /*Basic initialization with horizontal and vertical resolution in pixels*/
|
||||
lv_display_set_flush_cb(disp, my_flush_cb); /*Set a flush callback to draw to the display*/
|
||||
lv_display_set_buffers(disp, buf_1, buf_2, sizeof(buf_1), LV_DISPLAY_RENDER_MODE_PARTIAL); /*Set an initialized buffer*/
|
||||
|
||||
- Create some dummy objects to test the output:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Change the active screen's background color
|
||||
lv_obj_set_style_bg_color(lv_screen_active(), lv_color_hex(0x003a57), LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(lv_screen_active(), lv_color_hex(0xffffff), LV_PART_MAIN);
|
||||
|
||||
/*Create a spinner*/
|
||||
lv_obj_t * spinner = lv_spinner_create(lv_screen_active(), 1000, 60);
|
||||
lv_obj_set_size(spinner, 64, 64);
|
||||
lv_obj_align(spinner, LV_ALIGN_BOTTOM_MID, 0, 0);
|
||||
|
||||
- Add a call to :cpp:func:`lv_timer_handler` inside your ``while(1)`` loop:
|
||||
|
||||
.. code:: c
|
||||
|
||||
/* Infinite loop */
|
||||
while (1)
|
||||
{
|
||||
lv_timer_handler();
|
||||
HAL_Delay(5);
|
||||
}
|
||||
|
||||
- Add a call to :cpp:func:`lv_tick_inc` inside the :cpp:func:`SysTick_Handler`
|
||||
function. Open the *stm32xxxx_it.c* file (the name will depend on
|
||||
your specific MCU), and update the :cpp:func:`SysTick_Handler` function:
|
||||
|
||||
.. code:: c
|
||||
|
||||
void SysTick_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN SysTick_IRQn 0 */
|
||||
|
||||
HAL_SYSTICK_IRQHandler();
|
||||
lv_tick_inc(1);
|
||||
#ifdef USE_RTOS_SYSTICK
|
||||
osSystickHandler();
|
||||
#endif
|
||||
|
||||
/* USER CODE END SysTick_IRQn 0 */
|
||||
HAL_IncTick();
|
||||
/* USER CODE BEGIN SysTick_IRQn 1 */
|
||||
|
||||
/* USER CODE END SysTick_IRQn 1 */
|
||||
}
|
||||
|
||||
- Finally, write the callback function, ``my_flush_cb``, which will
|
||||
send the display buffer to your LCD panel. Below is one example, but
|
||||
it will vary depending on your setup.
|
||||
|
||||
.. code:: c
|
||||
|
||||
void my_flush_cb(lv_display_t * disp, const lv_area_t * area, lv_color_t * color_p)
|
||||
{
|
||||
//Set the drawing region
|
||||
set_draw_window(area->x1, area->y1, area->x2, area->y2);
|
||||
|
||||
int height = area->y2 - area->y1 + 1;
|
||||
int width = area->x2 - area->x1 + 1;
|
||||
|
||||
//We will do the SPI write manually here for speed
|
||||
HAL_GPIO_WritePin(DC_PORT, DC_PIN, GPIO_PIN_SET);
|
||||
//CS low to begin data
|
||||
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET);
|
||||
|
||||
//Write colour to each pixel
|
||||
for (int i = 0; i < width * height; i++) {
|
||||
uint16_t color_full = (color_p->red << 11) | (color_p->green << 5) | (color_p->blue);
|
||||
parallel_write(color_full);
|
||||
|
||||
color_p++;
|
||||
}
|
||||
|
||||
//Return CS to high
|
||||
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);
|
||||
|
||||
/* IMPORTANT!!!
|
||||
* Inform the graphics library that you are ready with the flushing*/
|
||||
lv_display_flush_ready(disp);
|
||||
}
|
||||
|
||||
FreeRTOS Example
|
||||
----------------
|
||||
|
||||
A minimal example using STM32CubeIDE, HAL, and CMSISv1 (FreeRTOS). *Note
|
||||
that we have not used Mutexes in this example, however LVGL is* **NOT**
|
||||
*thread safe and so Mutexes should be used*. See: :ref:`os_interrupt`
|
||||
\* ``#include "lvgl.h"`` \* Create your frame buffer(s) as global
|
||||
variables:
|
||||
|
||||
.. code:: c
|
||||
|
||||
//Frame buffers
|
||||
/*Static or global buffer(s). The second buffer is optional*/
|
||||
static lv_color_t buf_1[BUFF_SIZE]; //TODO: Declare your own BUFF_SIZE appropriate to your system.
|
||||
static lv_color_t buf_2[BUFF_SIZE];
|
||||
|
||||
- In your ``main`` function, after your peripherals (SPI, GPIOs, LCD
|
||||
etc) have been initialised, initialise LVGL using :cpp:func:`lv_init`,
|
||||
create a new display driver using :cpp:func:`lv_display_create`, and
|
||||
register the frame buffers using :cpp:func:`lv_display_set_buffers`.
|
||||
|
||||
.. code:: c
|
||||
|
||||
//Initialise LVGL UI library
|
||||
lv_init();
|
||||
lv_display_t *display = lv_display_create(WIDTH, HEIGHT); /*Create the display*/
|
||||
lv_display_set_flush_cb(display, my_flush_cb); /*Set a flush callback to draw to the display*/
|
||||
|
||||
// Register the touch controller with LVGL - Not included here for brevity.
|
||||
|
||||
- Create some dummy objects to test the output:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Change the active screen's background color
|
||||
lv_obj_set_style_bg_color(lv_screen_active(), lv_color_hex(0x003a57), LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(lv_screen_active(), lv_color_hex(0xffffff), LV_PART_MAIN);
|
||||
|
||||
/*Create a spinner*/
|
||||
lv_obj_t * spinner = lv_spinner_create(lv_screen_active(), 1000, 60);
|
||||
lv_obj_set_size(spinner, 64, 64);
|
||||
lv_obj_align(spinner, LV_ALIGN_BOTTOM_MID, 0, 0);
|
||||
|
||||
- Create two threads to call :cpp:func:`lv_timer_handler`, and
|
||||
:cpp:func:`lv_tick_inc`.You will need two ``osThreadId`` handles for
|
||||
CMSISv1. These don't strictly have to be globally accessible in this
|
||||
case, however STM32Cube code generation does by default. If you are
|
||||
using CMSIS and STM32Cube code generation it should look something
|
||||
like this:
|
||||
|
||||
.. code:: c
|
||||
|
||||
//Thread Handles
|
||||
osThreadId lvgl_tickHandle;
|
||||
osThreadId lvgl_timerHandle;
|
||||
|
||||
/* definition and creation of lvgl_tick */
|
||||
osThreadDef(lvgl_tick, LVGLTick, osPriorityNormal, 0, 1024);
|
||||
lvgl_tickHandle = osThreadCreate(osThread(lvgl_tick), NULL);
|
||||
|
||||
//LVGL update timer
|
||||
osThreadDef(lvgl_timer, LVGLTimer, osPriorityNormal, 0, 1024);
|
||||
lvgl_timerHandle = osThreadCreate(osThread(lvgl_timer), NULL);
|
||||
|
||||
- And create the thread functions:
|
||||
|
||||
.. code:: c
|
||||
|
||||
/* LVGL timer for tasks. */
|
||||
void LVGLTimer(void const * argument)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
lv_timer_handler();
|
||||
osDelay(20);
|
||||
}
|
||||
}
|
||||
/* LVGL tick source */
|
||||
void LVGLTick(void const * argument)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
lv_tick_inc(10);
|
||||
osDelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
- Finally, create the ``my_flush_cb`` function to output the frame
|
||||
buffer to your LCD. The specifics of this function will vary
|
||||
depending on which MCU features you are using. Below is an example
|
||||
for a typical MCU interface.
|
||||
|
||||
.. code:: c
|
||||
|
||||
void my_flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map);
|
||||
{
|
||||
uint16_t * color_p = (uint16_t *)px_map;
|
||||
|
||||
//Set the drawing region
|
||||
set_draw_window(area->x1, area->y1, area->x2, area->y2);
|
||||
|
||||
int height = area->y2 - area->y1 + 1;
|
||||
int width = area->x2 - area->x1 + 1;
|
||||
|
||||
//Begin SPI Write for DATA
|
||||
HAL_GPIO_WritePin(DC_PORT, DC_PIN, GPIO_PIN_SET);
|
||||
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET);
|
||||
|
||||
//Write colour to each pixel
|
||||
for (int i = 0; i < width * height; i++) {
|
||||
parallel_write(color_p);
|
||||
color_p++;
|
||||
}
|
||||
|
||||
//Return CS to high
|
||||
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);
|
||||
|
||||
/* IMPORTANT!!!
|
||||
* Inform the graphics library that you are ready with the flushing*/
|
||||
lv_display_flush_ready(display);
|
||||
}
|
||||
Reference in New Issue
Block a user