1=====
2STM32
3=====
4
5LVGL Can be added to `STM32CubeIDE <https://www.st.com/en/development-tools/stm32cubeide.html>`__
6in a similar fashion to any other Eclipse-based IDE.
7
8
9Including LVGL in a Project
10---------------------------
11
12- Create or open a project in STM32CubeIDE.
13- Copy the entire LVGL folder to *[project_folder]/Drivers/lvgl*.
14- In the STM32CubeIDE **Project Explorer** pane: right click on the
15  LVGL folder that you copied (you may need to refresh the view first
16  before it will appear), and select **Add/remove include path…**. If
17  this doesn't appear, or doesn't work, you can review your project
18  include paths under the **Project** -> **Properties** menu, and then
19  navigating to **C/C++ Build** -> **Settings** -> **Include paths**, and
20  ensuring that the LVGL directory is listed.
21
22Now that the source files are included in your project, follow the instructions to
23:ref:`add_lvgl_to_your_project` and to create the ``lv_conf.h`` file, and
24initialise the display.
25
26
27Bare Metal Example
28------------------
29
30A minimal example using STM32CubeIDE, and HAL. \* When setting up
31**Pinout and Configuration** using the **Device Configuration Tool**,
32select **System Core** -> **SYS** and ensure that **Timebase Source** is
33set to **SysTick**. \* Configure any other peripherals (including the
34LCD panel), and initialise them in *main.c*. \* ``#include "lvgl.h"`` in
35the *main.c* file. \* Create some frame buffer(s) as global variables:
36
37.. code-block:: c
38
39   /* Frame buffers
40    * Static or global buffer(s). The second buffer is optional
41    * TODO: Adjust color format and choose buffer size. DISPLAY_WIDTH * 10 is one suggestion. */
42   #define BYTES_PER_PIXEL (LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565)) /* will be 2 for RGB565 */
43   #define BUFF_SIZE (DISPLAY_WIDTH * 10 * BYTES_PER_PIXEL)
44   static uint8_t buf_1[BUFF_SIZE];
45   static uint8_t buf_2[BUFF_SIZE];
46
47- In your ``main()`` function, after initialising your CPU,
48    peripherals, and LCD panel, call :cpp:func:`lv_init` to initialise LVGL.
49    You can then create the display driver using
50    :cpp:func:`lv_display_create`, and register the frame buffers using
51    :cpp:func:`lv_display_set_buffers`.
52
53    .. code-block:: c
54
55        //Initialise LVGL UI library
56        lv_init();
57
58        lv_display_t * disp = lv_display_create(WIDTH, HEIGHT); /* Basic initialization with horizontal and vertical resolution in pixels */
59        lv_display_set_flush_cb(disp, my_flush_cb); /* Set a flush callback to draw to the display */
60        lv_display_set_buffers(disp, buf_1, buf_2, sizeof(buf_1), LV_DISPLAY_RENDER_MODE_PARTIAL); /* Set an initialized buffer */
61
62- Create some dummy Widgets to test the output:
63
64    .. code-block:: c
65
66        /* Change Active Screen's background color */
67        lv_obj_set_style_bg_color(lv_screen_active(), lv_color_hex(0x003a57), LV_PART_MAIN);
68        lv_obj_set_style_text_color(lv_screen_active(), lv_color_hex(0xffffff), LV_PART_MAIN);
69
70        /* Create a spinner */
71        lv_obj_t * spinner = lv_spinner_create(lv_screen_active(), 1000, 60);
72        lv_obj_set_size(spinner, 64, 64);
73        lv_obj_align(spinner, LV_ALIGN_BOTTOM_MID, 0, 0);
74
75
76- Add a call to :cpp:func:`lv_timer_handler` inside your ``while(1)`` loop:
77
78  .. code-block:: c
79
80      /* Infinite loop */
81      while (1)
82      {
83          lv_timer_handler();
84          HAL_Delay(5);
85      }
86
87
88- Add a call to :cpp:func:`lv_tick_inc` inside the :cpp:func:`SysTick_Handler` function. Open the *stm32xxxx_it.c*
89  file (the name will depend on your specific MCU), and update the :cpp:func:`SysTick_Handler` function:
90
91  .. code-block:: c
92
93      void SysTick_Handler(void)
94      {
95          /* USER CODE BEGIN SysTick_IRQn 0 */
96
97          HAL_SYSTICK_IRQHandler();
98          lv_tick_inc(1);
99          #ifdef USE_RTOS_SYSTICK
100              osSystickHandler();
101          #endif
102
103          /* USER CODE END SysTick_IRQn 0 */
104          HAL_IncTick();
105          /* USER CODE BEGIN SysTick_IRQn 1 */
106
107          /* USER CODE END SysTick_IRQn 1 */
108      }
109
110
111- Finally, write the callback function, ``my_flush_cb``, which will send the display buffer to your LCD panel. Below is
112  one example, but it will vary depending on your setup.
113
114  .. code-block:: c
115
116      void my_flush_cb(lv_display_t * disp, const lv_area_t * area, lv_color_t * color_p)
117      {
118          //Set the drawing region
119          set_draw_window(area->x1, area->y1, area->x2, area->y2);
120
121          int height = area->y2 - area->y1 + 1;
122          int width = area->x2 - area->x1 + 1;
123
124          //We will do the SPI write manually here for speed
125          HAL_GPIO_WritePin(DC_PORT, DC_PIN, GPIO_PIN_SET);
126          //CS low to begin data
127          HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET);
128
129          //Write colour to each pixel
130          for (int i = 0; i < width * height; i++) {
131              uint16_t color_full = (color_p->red << 11) | (color_p->green << 5) | (color_p->blue);
132              parallel_write(color_full);
133
134              color_p++;
135          }
136
137          //Return CS to high
138          HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);
139
140          /* IMPORTANT!!!
141           * Inform the graphics library that you are ready with the flushing */
142          lv_display_flush_ready(disp);
143      }
144
145
146FreeRTOS Example
147----------------
148
149A minimal example using STM32CubeIDE, HAL, and CMSISv1 (FreeRTOS).
150*Note that we have not used Mutexes in this example, however LVGL is* **NOT**
151*thread safe and so Mutexes should be used*. See: :ref:`threading`
152\* ``#include "lvgl.h"`` \* Create your frame buffer(s) as global variables:
153
154.. code-block:: c
155
156    /* Frame buffers
157     * Static or global buffer(s). The second buffer is optional */
158    #define BYTES_PER_PIXEL (LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565)) /* will be 2 for RGB565 */
159    /* TODO: Declare your own BUFF_SIZE appropriate to your system. */
160    static lv_color_t buf_1[BUFF_SIZE];
161    #define BUFF_SIZE (DISPLAY_WIDTH * 10 * BYTES_PER_PIXEL)
162    static uint8_t buf_1[BUFF_SIZE];
163    static lv_color_t buf_2[BUFF_SIZE];
164
165- In your ``main`` function, after your peripherals (SPI, GPIOs, LCD
166  etc) have been initialised, initialise LVGL using :cpp:func:`lv_init`,
167  create a new display driver using :cpp:func:`lv_display_create`, and
168  register the frame buffers using :cpp:func:`lv_display_set_buffers`.
169
170  .. code-block:: c
171
172   /* Initialise LVGL UI library */
173   lv_init();
174   lv_display_t *display = lv_display_create(WIDTH, HEIGHT); /* Create the display */
175   lv_display_set_flush_cb(display, my_flush_cb);            /* Set a flush callback to draw to the display */
176   lv_display_set_buffers(disp, buf_1, buf_2, sizeof(buf_1), LV_DISPLAY_RENDER_MODE_PARTIAL); /* Set an initialized buffer */
177
178   /* Register the touch controller with LVGL - Not included here for brevity. */
179
180
181- Create some dummy Widgets to test the output:
182
183  .. code-block:: c
184
185    /* Change Active Screen's background color */
186    lv_obj_set_style_bg_color(lv_screen_active(), lv_color_hex(0x003a57), LV_PART_MAIN);
187    lv_obj_set_style_text_color(lv_screen_active(), lv_color_hex(0xffffff), LV_PART_MAIN);
188
189    /* Create a spinner */
190    lv_obj_t * spinner = lv_spinner_create(lv_screen_active(), 1000, 60);
191    lv_obj_set_size(spinner, 64, 64);
192    lv_obj_align(spinner, LV_ALIGN_BOTTOM_MID, 0, 0);
193
194- Create two threads to call :cpp:func:`lv_timer_handler`, and
195  :cpp:func:`lv_tick_inc`.You will need two ``osThreadId`` handles for
196  CMSISv1. These don't strictly have to be globally accessible in this
197  case, however STM32Cube code generation does by default. If you are
198  using CMSIS and STM32Cube code generation it should look something
199  like this:
200
201  .. code-block:: c
202
203   //Thread Handles
204   osThreadId lvgl_tickHandle;
205   osThreadId lvgl_timerHandle;
206
207   /* definition and creation of lvgl_tick */
208   osThreadDef(lvgl_tick, LVGLTick, osPriorityNormal, 0, 1024);
209   lvgl_tickHandle = osThreadCreate(osThread(lvgl_tick), NULL);
210
211   //LVGL update timer
212   osThreadDef(lvgl_timer, LVGLTimer, osPriorityNormal, 0, 1024);
213   lvgl_timerHandle = osThreadCreate(osThread(lvgl_timer), NULL);
214
215- And create the thread functions:
216
217  .. code-block:: c
218
219   /* LVGL timer for tasks. */
220   void LVGLTimer(void const * argument)
221   {
222     for(;;)
223     {
224       lv_timer_handler();
225       osDelay(20);
226     }
227   }
228   /* LVGL tick source */
229   void LVGLTick(void const * argument)
230   {
231     for(;;)
232     {
233       lv_tick_inc(10);
234       osDelay(10);
235     }
236   }
237
238- Finally, create the ``my_flush_cb`` function to output the frame
239  buffer to your LCD. The specifics of this function will vary
240  depending on which MCU features you are using. Below is an example
241  for a typical MCU interface.
242
243  .. code-block:: c
244
245   void my_flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map);
246   {
247     uint16_t * color_p = (uint16_t *)px_map;
248
249     //Set the drawing region
250     set_draw_window(area->x1, area->y1, area->x2, area->y2);
251
252     int height = area->y2 - area->y1 + 1;
253     int width = area->x2 - area->x1 + 1;
254
255     //Begin SPI Write for DATA
256     HAL_GPIO_WritePin(DC_PORT, DC_PIN, GPIO_PIN_SET);
257     HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET);
258
259     //Write colour to each pixel
260     for (int i = 0; i < width * height; i++) {
261         parallel_write(color_p);
262         color_p++;
263     }
264
265     //Return CS to high
266     HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);
267
268     /* IMPORTANT!!!
269      * Inform the graphics library that you are ready with the flushing */
270     lv_display_flush_ready(display);
271   }
272
273.. _dma2d:
274
275DMA2D Support
276-------------
277
278LVGL supports DMA2D - a feature of some STM32 MCUs which can improve performance
279when blending fills and images. Some STM32 product lines such as STM32F4 STM32F7, STM32L4,
280STM32U5, and STM32H7 include models with DMA2D support.
281
282LVGL's integration with DMA2D can be enabled by setting ``LV_USE_DRAW_DMA2D``
283to ``1`` in ``lv_conf.h``
284
285With ``LV_USE_DRAW_DMA2D_INTERRUPT`` set to ``0`` and ``LV_USE_OS`` set to ``LV_OS_NONE``,
286DMA2D will draw some fills and images concurrently with the software render where
287possible. If ``LV_USE_DRAW_DMA2D_INTERRUPT`` is set to ``1`` and ``LV_USE_OS`` set to
288``LV_OS_FREERTOS`` (or another OS) the main difference will be that the core will idle
289instead of "busywait" while waiting for a DMA2D transfer to complete.
290
291If ``LV_USE_DRAW_DMA2D_INTERRUPT`` is enabled then you are required to call
292:cpp:expr:`lv_draw_dma2d_transfer_complete_interrupt_handler` whenever the DMA2D
293"transfer complete" global interrupt is received.
294
295DMA2D also makes possible to mix layers that have color format on
296:c:macro:`LV_COLOR_FORMAT_ARGB1555` on top of :c:macro:`LV_COLOR_FORMAT_RGB565`
297layers.
298
299If your STM device has a NeoChrom GPU, you can use the :ref:`Nema GFX renderer <nema_gfx>` instead.
300