Update LVGL to v9.1.0

This commit is contained in:
jacqueline
2024-06-12 17:54:40 +10:00
parent 611176ed66
commit 64bd9053a2
2244 changed files with 278442 additions and 118864 deletions
@@ -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
+10
View File
@@ -0,0 +1,10 @@
============
Chip vendors
============
.. toctree::
:maxdepth: 2
nxp
stm32
espressif
+398
View File
@@ -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 NXPs 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
+259
View File
@@ -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);
}