committed by
GitHub
parent
6d80144e12
commit
85e26636a3
Submodule
+1
Submodule Libraries/FreeRTOS-Kernel added at def7d2df2b
Submodule
+1
Submodule Libraries/M5GFX added at ff11e09ac0
Submodule
+1
Submodule Libraries/M5Unified added at 77017d17b9
@@ -0,0 +1,32 @@
|
||||
# Changelog
|
||||
|
||||
## 1.5.0
|
||||
|
||||
### Features
|
||||
|
||||
- Divided into files per feature
|
||||
- Added support for LVGL9
|
||||
|
||||
## 1.4.0
|
||||
|
||||
### Features
|
||||
|
||||
- Added support for USB HID mouse/keyboard as an input device
|
||||
|
||||
## 1.3.0
|
||||
|
||||
### Features
|
||||
|
||||
- Added low power interface
|
||||
|
||||
## 1.2.0
|
||||
|
||||
### Features
|
||||
|
||||
- Added support for encoder (knob) as an input device
|
||||
|
||||
## 1.1.0
|
||||
|
||||
### Features
|
||||
|
||||
- Added support for navigation buttons as an input device
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
#Get LVGL version
|
||||
#idf_component_get_property(lvgl_ver lvgl__lvgl COMPONENT_VERSION)
|
||||
#if(lvgl_ver EQUAL "")
|
||||
# idf_component_get_property(lvgl_ver lvgl COMPONENT_VERSION)
|
||||
#endif()
|
||||
set(lvgl_ver "9.0.0")
|
||||
message(STATUS "LVGL version: ${lvgl_ver}")
|
||||
|
||||
#Select folder by LVGL version
|
||||
if(lvgl_ver VERSION_LESS "9.0.0")
|
||||
message(VERBOSE "Compiling esp_lvgl_port for LVGL8")
|
||||
set(PORT_FOLDER "lvgl8")
|
||||
else()
|
||||
message(VERBOSE "Compiling esp_lvgl_port for LVGL9")
|
||||
set(PORT_FOLDER "lvgl9")
|
||||
endif()
|
||||
|
||||
set(PORT_PATH "src/${PORT_FOLDER}")
|
||||
|
||||
idf_component_register(SRCS "${PORT_PATH}/esp_lvgl_port.c" "${PORT_PATH}/esp_lvgl_port_disp.c" INCLUDE_DIRS "include" REQUIRES "esp_lcd" "lvgl" PRIV_REQUIRES "esp_timer")
|
||||
|
||||
set(ADD_SRCS "")
|
||||
set(ADD_LIBS "")
|
||||
|
||||
idf_build_get_property(build_components BUILD_COMPONENTS)
|
||||
if("espressif__button" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_button.c")
|
||||
list(APPEND ADD_LIBS idf::espressif__button)
|
||||
endif()
|
||||
if("button" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_button.c")
|
||||
list(APPEND ADD_LIBS idf::button)
|
||||
endif()
|
||||
if("espressif__esp_lcd_touch" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_touch.c")
|
||||
list(APPEND ADD_LIBS idf::espressif__esp_lcd_touch)
|
||||
endif()
|
||||
if("esp_lcd_touch" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_touch.c")
|
||||
list(APPEND ADD_LIBS idf::esp_lcd_touch)
|
||||
endif()
|
||||
if("espressif__knob" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_knob.c")
|
||||
list(APPEND ADD_LIBS idf::espressif__knob)
|
||||
endif()
|
||||
if("knob" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_knob.c")
|
||||
list(APPEND ADD_LIBS idf::knob)
|
||||
endif()
|
||||
if("espressif__usb_host_hid" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_usbhid.c" "images/${PORT_FOLDER}/img_cursor.c")
|
||||
list(APPEND ADD_LIBS idf::espressif__usb_host_hid)
|
||||
endif()
|
||||
if("usb_host_hid" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_usbhid.c" "images/${PORT_FOLDER}/img_cursor.c")
|
||||
list(APPEND ADD_LIBS idf::usb_host_hid)
|
||||
endif()
|
||||
|
||||
if(ADD_SRCS)
|
||||
target_sources(${COMPONENT_LIB} PRIVATE ${ADD_SRCS})
|
||||
endif()
|
||||
if(ADD_LIBS)
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE ${ADD_LIBS})
|
||||
endif()
|
||||
@@ -0,0 +1,277 @@
|
||||
# LVGL ESP Portation
|
||||
|
||||
[](https://components.espressif.com/components/espressif/esp_lvgl_port)
|
||||
|
||||
This component helps with using LVGL with Espressif's LCD and touch drivers. It can be used with any project with LCD display.
|
||||
|
||||
## Features
|
||||
* Initialization of the LVGL
|
||||
* Create task and timer
|
||||
* Handle rotating
|
||||
* Add/remove display (using [`esp_lcd`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html))
|
||||
* Add/remove touch input (using [`esp_lcd_touch`](https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch))
|
||||
* Add/remove navigation buttons input (using [`button`](https://github.com/espressif/esp-iot-solution/tree/master/components/button))
|
||||
* Add/remove encoder input (using [`knob`](https://github.com/espressif/esp-iot-solution/tree/master/components/knob))
|
||||
|
||||
## LVGL Version
|
||||
|
||||
This component supports **LVGL8** and **LVGL9**. By default, it selects the latest LVGL version. If you want to use a specific version (e.g. latest LVGL8), you can easily put into `idf_component.yml` in your project like this:
|
||||
|
||||
```
|
||||
lvgl/lvgl:
|
||||
version: "^8"
|
||||
public: true
|
||||
```
|
||||
|
||||
### LVGL Version Compatibility
|
||||
|
||||
This component is fully compatible with LVGL version 9. All types and functions are used from LVGL9. Some LVGL9 types are not supported in LVGL8 and there are retyping in [`esp_lvgl_port_compatibility.h`](include/esp_lvgl_port_compatibility.h) header file. **Please, be aware, that some draw and object functions are not compatible between LVGL8 and LVGL9.**
|
||||
|
||||
## Usage
|
||||
|
||||
### Initialization
|
||||
``` c
|
||||
const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
|
||||
esp_err_t err = lvgl_port_init(&lvgl_cfg);
|
||||
```
|
||||
|
||||
### Add screen
|
||||
|
||||
Add an LCD screen to the LVGL. It can be called multiple times for adding multiple LCD screens.
|
||||
|
||||
``` c
|
||||
static lv_disp_t * disp_handle;
|
||||
|
||||
/* LCD IO */
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t) 1, &io_config, &io_handle));
|
||||
|
||||
/* LCD driver initialization */
|
||||
esp_lcd_panel_handle_t lcd_panel_handle;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &lcd_panel_handle));
|
||||
|
||||
/* Add LCD screen */
|
||||
const lvgl_port_display_cfg_t disp_cfg = {
|
||||
.io_handle = io_handle,
|
||||
.panel_handle = lcd_panel_handle,
|
||||
.buffer_size = DISP_WIDTH*DISP_HEIGHT,
|
||||
.double_buffer = true,
|
||||
.hres = DISP_WIDTH,
|
||||
.vres = DISP_HEIGHT,
|
||||
.monochrome = false,
|
||||
/* Rotation values must be same as used in esp_lcd for initial settings of the screen */
|
||||
.rotation = {
|
||||
.swap_xy = false,
|
||||
.mirror_x = false,
|
||||
.mirror_y = false,
|
||||
},
|
||||
.flags = {
|
||||
.buff_dma = true,
|
||||
.swap_bytes = false,
|
||||
}
|
||||
};
|
||||
disp_handle = lvgl_port_add_disp(&disp_cfg);
|
||||
|
||||
/* ... the rest of the initialization ... */
|
||||
|
||||
/* If deinitializing LVGL port, remember to delete all displays: */
|
||||
lvgl_port_remove_disp(disp_handle);
|
||||
```
|
||||
|
||||
### Add touch input
|
||||
|
||||
Add touch input to the LVGL. It can be called more times for adding more touch inputs.
|
||||
``` c
|
||||
/* Touch driver initialization */
|
||||
...
|
||||
esp_lcd_touch_handle_t tp;
|
||||
esp_err_t err = esp_lcd_touch_new_i2c_gt911(io_handle, &tp_cfg, &tp);
|
||||
|
||||
/* Add touch input (for selected screen) */
|
||||
const lvgl_port_touch_cfg_t touch_cfg = {
|
||||
.disp = disp_handle,
|
||||
.handle = tp,
|
||||
};
|
||||
lv_indev_t* touch_handle = lvgl_port_add_touch(&touch_cfg);
|
||||
|
||||
/* ... the rest of the initialization ... */
|
||||
|
||||
/* If deinitializing LVGL port, remember to delete all touches: */
|
||||
lvgl_port_remove_touch(touch_handle);
|
||||
```
|
||||
|
||||
### Add buttons input
|
||||
|
||||
Add buttons input to the LVGL. It can be called more times for adding more buttons inputs for different displays. This feature is available only when the component `espressif/button` was added into the project.
|
||||
``` c
|
||||
/* Buttons configuration structure */
|
||||
const button_config_t bsp_button_config[] = {
|
||||
{
|
||||
.type = BUTTON_TYPE_ADC,
|
||||
.adc_button_config.adc_channel = ADC_CHANNEL_0, // ADC1 channel 0 is GPIO1
|
||||
.adc_button_config.button_index = 0,
|
||||
.adc_button_config.min = 2310, // middle is 2410mV
|
||||
.adc_button_config.max = 2510
|
||||
},
|
||||
{
|
||||
.type = BUTTON_TYPE_ADC,
|
||||
.adc_button_config.adc_channel = ADC_CHANNEL_0, // ADC1 channel 0 is GPIO1
|
||||
.adc_button_config.button_index = 1,
|
||||
.adc_button_config.min = 1880, // middle is 1980mV
|
||||
.adc_button_config.max = 2080
|
||||
},
|
||||
{
|
||||
.type = BUTTON_TYPE_ADC,
|
||||
.adc_button_config.adc_channel = ADC_CHANNEL_0, // ADC1 channel 0 is GPIO1
|
||||
.adc_button_config.button_index = 2,
|
||||
.adc_button_config.min = 720, // middle is 820mV
|
||||
.adc_button_config.max = 920
|
||||
},
|
||||
};
|
||||
|
||||
const lvgl_port_nav_btns_cfg_t btns = {
|
||||
.disp = disp_handle,
|
||||
.button_prev = &bsp_button_config[0],
|
||||
.button_next = &bsp_button_config[1],
|
||||
.button_enter = &bsp_button_config[2]
|
||||
};
|
||||
|
||||
/* Add buttons input (for selected screen) */
|
||||
lv_indev_t* buttons_handle = lvgl_port_add_navigation_buttons(&btns);
|
||||
|
||||
/* ... the rest of the initialization ... */
|
||||
|
||||
/* If deinitializing LVGL port, remember to delete all buttons: */
|
||||
lvgl_port_remove_navigation_buttons(buttons_handle);
|
||||
```
|
||||
|
||||
**Note:** When you use navigation buttons for control LVGL objects, these objects must be added to LVGL groups. See [LVGL documentation](https://docs.lvgl.io/master/overview/indev.html?highlight=lv_indev_get_act#keypad-and-encoder) for more info.
|
||||
|
||||
### Add encoder input
|
||||
|
||||
Add encoder input to the LVGL. It can be called more times for adding more encoder inputs for different displays. This feature is available only when the component `espressif/knob` was added into the project.
|
||||
``` c
|
||||
|
||||
const button_config_t encoder_btn_config = {
|
||||
.type = BUTTON_TYPE_GPIO,
|
||||
.gpio_button_config.active_level = false,
|
||||
.gpio_button_config.gpio_num = GPIO_BTN_PRESS,
|
||||
};
|
||||
|
||||
const knob_config_t encoder_a_b_config = {
|
||||
.default_direction = 0,
|
||||
.gpio_encoder_a = GPIO_ENCODER_A,
|
||||
.gpio_encoder_b = GPIO_ENCODER_B,
|
||||
};
|
||||
|
||||
/* Encoder configuration structure */
|
||||
const lvgl_port_encoder_cfg_t encoder = {
|
||||
.disp = disp_handle,
|
||||
.encoder_a_b = &encoder_a_b_config,
|
||||
.encoder_enter = &encoder_btn_config
|
||||
};
|
||||
|
||||
/* Add encoder input (for selected screen) */
|
||||
lv_indev_t* encoder_handle = lvgl_port_add_encoder(&encoder);
|
||||
|
||||
/* ... the rest of the initialization ... */
|
||||
|
||||
/* If deinitializing LVGL port, remember to delete all encoders: */
|
||||
lvgl_port_remove_encoder(encoder_handle);
|
||||
```
|
||||
|
||||
**Note:** When you use encoder for control LVGL objects, these objects must be added to LVGL groups. See [LVGL documentation](https://docs.lvgl.io/master/overview/indev.html?highlight=lv_indev_get_act#keypad-and-encoder) for more info.
|
||||
|
||||
### Add USB HID keyboard and mouse input
|
||||
|
||||
Add mouse and keyboard input to the LVGL. This feature is available only when the component [usb_host_hid](https://components.espressif.com/components/espressif/usb_host_hid) was added into the project.
|
||||
|
||||
``` c
|
||||
/* USB initialization */
|
||||
usb_host_config_t host_config = {
|
||||
.skip_phy_setup = false,
|
||||
.intr_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
};
|
||||
ESP_ERROR_CHECK(usb_host_install(&host_config));
|
||||
|
||||
...
|
||||
|
||||
/* Add mouse input device */
|
||||
const lvgl_port_hid_mouse_cfg_t mouse_cfg = {
|
||||
.disp = display,
|
||||
.sensitivity = 1, /* Sensitivity of the mouse moving */
|
||||
};
|
||||
lvgl_port_add_usb_hid_mouse_input(&mouse_cfg);
|
||||
|
||||
/* Add keyboard input device */
|
||||
const lvgl_port_hid_keyboard_cfg_t kb_cfg = {
|
||||
.disp = display,
|
||||
};
|
||||
kb_indev = lvgl_port_add_usb_hid_keyboard_input(&kb_cfg);
|
||||
```
|
||||
|
||||
Keyboard special behavior (when objects are in group):
|
||||
- **TAB**: Select next object
|
||||
- **SHIFT** + **TAB**: Select previous object
|
||||
- **ENTER**: Control object (e.g. click to button)
|
||||
- **ARROWS** or **HOME** or **END**: Move in text area
|
||||
- **DEL** or **Backspace**: Remove character in textarea
|
||||
|
||||
**Note:** When you use keyboard for control LVGL objects, these objects must be added to LVGL groups. See [LVGL documentation](https://docs.lvgl.io/master/overview/indev.html?highlight=lv_indev_get_act#keypad-and-encoder) for more info.
|
||||
|
||||
### LVGL API usage
|
||||
|
||||
Every LVGL calls must be protected with these lock/unlock commands:
|
||||
``` c
|
||||
/* Wait for the other task done the screen operation */
|
||||
lvgl_port_lock(0);
|
||||
...
|
||||
lv_obj_t * screen = lv_disp_get_scr_act(disp_handle);
|
||||
lv_obj_t * obj = lv_label_create(screen);
|
||||
...
|
||||
/* Screen operation done -> release for the other task */
|
||||
lvgl_port_unlock();
|
||||
```
|
||||
|
||||
### Rotating screen
|
||||
|
||||
LVGL port supports rotation of the display. You can select whether you'd like software rotation or hardware rotation.
|
||||
Software rotation requires no additional logic in your `flush_cb` callback.
|
||||
|
||||
Rotation mode can be selected in the `lvgl_port_display_cfg_t` structure.
|
||||
``` c
|
||||
const lvgl_port_display_cfg_t disp_cfg = {
|
||||
...
|
||||
.flags = {
|
||||
...
|
||||
.sw_rotate = true / false, // true: software; false: hardware
|
||||
}
|
||||
}
|
||||
```
|
||||
Display rotation can be changed at runtime.
|
||||
|
||||
``` c
|
||||
lv_disp_set_rotation(disp_handle, LV_DISP_ROT_90);
|
||||
```
|
||||
|
||||
**Note:** During the hardware rotating, the component call [`esp_lcd`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html) API. When using software rotation, you cannot use neither `direct_mode` nor `full_refresh` in the driver. See [LVGL documentation](https://docs.lvgl.io/8.3/porting/display.html?highlight=sw_rotate) for more info.
|
||||
|
||||
### Using PSRAM canvas
|
||||
|
||||
If the SRAM is insufficient, you can use the PSRAM as a canvas and use a small trans_buffer to carry it, this makes drawing more efficient.
|
||||
``` c
|
||||
const lvgl_port_display_cfg_t disp_cfg = {
|
||||
...
|
||||
.buffer_size = DISP_WIDTH * DISP_HEIGHT, // in PSRAM, not DMA-capable
|
||||
.trans_size = size, // in SRAM, DMA-capable
|
||||
.flags = {
|
||||
.buff_spiram = true,
|
||||
.buff_dma = false,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
Key feature of every graphical application is performance. Recommended settings for improving LCD performance is described in a separate document [here](docs/performance.md).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,159 @@
|
||||
# LCD & LVGL Performance
|
||||
|
||||
This document provides steps, how to set up your LCD and LVGL port for the best performance and comparison of different settings. All settings and measurements are valid for Espressif's chips.
|
||||
|
||||
## Performance metrics
|
||||
|
||||
In this document we will use following metrics for performance evaluation:
|
||||
|
||||
1. Measure time needed for refreshing the whole screen.
|
||||
2. Use LVGL's [lv_demo_benchmark()](https://github.com/lvgl/lvgl/tree/v8.3.6/demos/benchmark) -test suite- to measure Frames per second (weighted FPS).
|
||||
3. Use LVGL's [lv_demo_music()](https://github.com/lvgl/lvgl/tree/v8.3.6/demos/music) -demo application- to measure Frames per second (average FPS).
|
||||
|
||||
## Settings on ESP32 chips which have impact on LCD and LVGL performance
|
||||
|
||||
Following options and settings have impact on LCD performance (FPS). Some options yield only small difference in FPS (e.g. ~1 FPS), and some of them are more significant. Usually it depends on complexity of the graphical application (number of widgets...), resources (CPU time, RAM available...) and size of screen (definition and color depth).
|
||||
|
||||
Another set of key parametes are hardware related (graphical IO, frame buffer location), which are not yet covered in this document.
|
||||
|
||||
### LVGL Buffer configuration
|
||||
|
||||
**This is by far the most significant setting.** Users are encouraged to focus on correct frame buffer configuration before moving ahead with other optimizations.
|
||||
|
||||
On the other hand, the frame buffer(s) will consume significant portion of your RAM. In the graph below, you can see different frame buffer settings and resulting FPS:
|
||||
|
||||

|
||||
|
||||
Main takeaways from the graph are:
|
||||
|
||||
* The size of **LVGL buffer** and **double buffering** feature has big impact on performance.
|
||||
* Frame buffer size >25% of the screen does not bring relevant performance boost
|
||||
* Frame buffer size <10% will have severe negative effect on performance
|
||||
|
||||
*Note:* The measurements are valid for frame buffer in internal SRAM. Placing the frame buffer into external PSRAM will yield worse results.
|
||||
|
||||
### Compiler optimization level
|
||||
|
||||
Recommended level is "Performance" for good results. The "Performance" setting causes the compiled code to be larger and faster.
|
||||
|
||||
* `CONFIG_COMPILER_OPTIMIZATION_PERF=y`
|
||||
|
||||
### CPU frequency
|
||||
|
||||
The CPU frequency has a big impact on LCD performance. The recommended value is maximum -> 240 MHz.
|
||||
|
||||
* `CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y`
|
||||
|
||||
### Flash frequency and mode
|
||||
|
||||
The flash clock frequency and mode (bus width) has a big impact on LCD performance. Your choices will be limited by your hardware flash configuration. The higher the frequency and bus width, the better.
|
||||
|
||||
* `CONFIG_ESPTOOLPY_FLASHFREQ_120M=y`
|
||||
* `CONFIG_ESPTOOLPY_FLASHMODE_QIO=y` or `CONFIG_ESPTOOLPY_OCT_FLASH` on supported chips
|
||||
|
||||
More information about SPI Flash configuration can be found in [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-guides/flash_psram_config.html).
|
||||
|
||||
### Fast LVGL memory
|
||||
|
||||
This option puts the most frequently used LVGL functions into IRAM for execution speed up.
|
||||
|
||||
* `CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y`
|
||||
|
||||
### Affinity main task to second core
|
||||
|
||||
The main LVGL task can be processed on the second core of the CPU. It can increase performance. (It is available only on dual-core chips)
|
||||
|
||||
* `CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1=y`
|
||||
|
||||
### Using esp-idf `memcpy` and `memset` instead LVGL's configuration
|
||||
|
||||
Native esp-idf implementation are a little (~1-3 FPS) faster.
|
||||
|
||||
* `CONFIG_LV_MEMCPY_MEMSET_STD=y`
|
||||
|
||||
### Default LVGL display refresh period
|
||||
|
||||
This setting can improve subjective performance during screen transitions (scrolling, etc.).
|
||||
|
||||
* `CONFIG_LV_DISP_DEF_REFR_PERIOD=10`
|
||||
|
||||
## Example FPS improvement vs graphical settings
|
||||
|
||||
<img src="https://github.com/espressif/esp-bsp/blob/master/docu/pics/7inch-Capacitive-Touch-LCD-C_l.jpg?raw=true" align="right" width="300px" />
|
||||
|
||||
Default settings:
|
||||
* BSP example `display_lvgl_demos` with `ws_7inch` component
|
||||
* LCD: 7" 800x480
|
||||
* Intarface: 16bit parallel Intel 8080
|
||||
* LCD IO clock: 20 MHz
|
||||
* LVGL buffer size: 800 x 50
|
||||
* LVGL double buffer: YES
|
||||
* Optimization: Debug
|
||||
* CPU frequency: 160 MHz
|
||||
* Flash frequency: 80 MHz
|
||||
* Flash mode: DIO
|
||||
* LVGL display refresh period: 30 ms
|
||||
|
||||
### Internal RAM with DMA
|
||||
|
||||
| Average FPS | Weighted FPS | Changed settings |
|
||||
| :---: | :---: | ---------------- |
|
||||
| 17 | 32 | Default |
|
||||
| 18 | 36 | + Optimization: Performance (`CONFIG_COMPILER_OPTIMIZATION_PERF=y`) |
|
||||
| 21 | 46 | + CPU frequency: 240 MHz (`CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y`) |
|
||||
| 26 | 56 | + Flash frequency: 120 MHz, Flash mode: QIO (`CONFIG_ESPTOOLPY_FLASHFREQ_120M=y` + `CONFIG_ESPTOOLPY_FLASHMODE_QIO=y`) |
|
||||
| 28 | **60** | + LVGL fast memory enabled (`CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y`) |
|
||||
| 41 | 55 | + (`CONFIG_LV_DISP_DEF_REFR_PERIOD=10`) |
|
||||
|
||||
### PSRAM (QUAD) without DMA
|
||||
|
||||
Default changes:
|
||||
* LCD IO clock: 2 MHz
|
||||
* PSRAM frequency: 80 MHz
|
||||
|
||||
| Average FPS | Weighted FPS | Changed settings |
|
||||
| :---: | :---: | ---------------- |
|
||||
| 11 | 7 | Default |
|
||||
| 11 | 7 | + Optimization: Performance (`CONFIG_COMPILER_OPTIMIZATION_PERF=y`) |
|
||||
| 12 | 8 | + CPU frequency: 240 MHz (`CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y`) |
|
||||
| 12 | 9 | + Flash frequency: 120 MHz, PSRAM frequency: 120 MHz, Flash mode: QIO (`CONFIG_ESPTOOLPY_FLASHFREQ_120M=y` + `CONFIG_SPIRAM_SPEED_120M=y` + `CONFIG_ESPTOOLPY_FLASHMODE_QIO=y`) |
|
||||
| 12 | 9 | + LVGL fast memory enabled (`CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y`) |
|
||||
| 12 | 8 | + LVGL buffer size: 800 x 480 |
|
||||
| 26 | 8 | + (`CONFIG_LV_DISP_DEF_REFR_PERIOD=10`) |
|
||||
| 31 | 23 | + LCD clock: 10 MHz [^1] |
|
||||
|
||||
[^1]: This is not working in default and sometimes in fast changes on screen is not working properly.
|
||||
|
||||
### RGB LCD (without `esp_lvgl_port`), PSRAM (octal) with GDMA - ESP32-S3-LCD-EV-BOARD
|
||||
|
||||
<img src="https://github.com/espressif/esp-bsp/blob/master/docu/pics/esp32-s3-lcd-ev-board_800x480.png?raw=true" align="right" width="300px" />
|
||||
|
||||
Default settings:
|
||||
* BSP example `display_lvgl_demos`
|
||||
* LCD: 4.3" 800x480
|
||||
* Interface: RGB
|
||||
* LVGL buffer size: 800 x 100
|
||||
* LVGL double buffer: NO
|
||||
* Optimization: Debug
|
||||
* CPU frequency: 160 MHz
|
||||
* Flash frequency: 80 MHz
|
||||
* PSRAM frequency: 80 MHz
|
||||
* Flash mode: DIO
|
||||
* LVGL display refresh period: 30 ms
|
||||
|
||||
| Average FPS | Weighted FPS | Changed settings |
|
||||
| :---: | :---: | ---------------- |
|
||||
| 18 | 24 | Default |
|
||||
| 18 | 26 | + Optimization: Performance (`CONFIG_COMPILER_OPTIMIZATION_PERF=y`) |
|
||||
| 21 | 32 | + CPU frequency: 240 MHz (`CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y`) |
|
||||
| 21 | 32 | + Flash mode: QIO (`CONFIG_ESPTOOLPY_FLASHMODE_QIO=y`) |
|
||||
| 21 | 32 | + LVGL fast memory enabled (`CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y`) |
|
||||
| 35 | 34 | + (`CONFIG_LV_DISP_DEF_REFR_PERIOD=10`) |
|
||||
|
||||
## Conclusion
|
||||
|
||||
The graphical performance depends on a lot of things and settings, many of which affect the whole system (Compiler, Flash, CPU, PSRAM configuration...). The user should primarily focus on trade-off between frame-buffer(s) size and RAM consumption of the buffer, before optimizing the design further.
|
||||
|
||||
Other configuration options not covered in this document are:
|
||||
* Hardware interfaces, color depth, screen definition (size), clocks, LCD controller and more.
|
||||
* Complexity of the graphical application (number of LVGL objects and their styles).
|
||||
@@ -0,0 +1,9 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(touchscreen)
|
||||
@@ -0,0 +1,19 @@
|
||||
# ESP LVGL Touch Screen Example
|
||||
|
||||
Very simple example for demonstration of initialization and usage of the `esp_lvgl_port` component. This example contains four main parts:
|
||||
|
||||
## 1. LCD HW initialization - `app_lcd_init()`
|
||||
|
||||
Standard HW initialization of the LCD using [`esp_lcd`](https://github.com/espressif/esp-idf/tree/master/components/esp_lcd) component. Settings of this example are fully compatible with [ESP-BOX](https://github.com/espressif/esp-bsp/tree/master/esp-box) board.
|
||||
|
||||
## 2. Touch HW initialization - `app_touch_init()`
|
||||
|
||||
Standard HW initialization of the LCD touch using [`esp_lcd_touch`](https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch/esp_lcd_touch) component. Settings of this example are fully compatible with [ESP-BOX](https://github.com/espressif/esp-bsp/tree/master/esp-box) board.
|
||||
|
||||
## 3. LVGL port initialization - `app_lvgl_init()`
|
||||
|
||||
Initialization of the LVGL port.
|
||||
|
||||
## 4. LVGL objects example usage - `app_main_display()`
|
||||
|
||||
Very simple demonstration code of using LVGL objects after LVGL port initialization.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,8 @@
|
||||
dependencies:
|
||||
idf: ">=4.4"
|
||||
esp_lcd_touch_tt21100:
|
||||
version: "^1"
|
||||
override_path: "../../../../lcd_touch/esp_lcd_touch_tt21100/"
|
||||
esp_lvgl_port:
|
||||
version: ">=1.2.0"
|
||||
override_path: "../../../"
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
#include "esp_lcd_touch_tt21100.h"
|
||||
|
||||
/* LCD size */
|
||||
#define EXAMPLE_LCD_H_RES (320)
|
||||
#define EXAMPLE_LCD_V_RES (240)
|
||||
|
||||
/* LCD settings */
|
||||
#define EXAMPLE_LCD_SPI_NUM (SPI3_HOST)
|
||||
#define EXAMPLE_LCD_PIXEL_CLK_HZ (40 * 1000 * 1000)
|
||||
#define EXAMPLE_LCD_CMD_BITS (8)
|
||||
#define EXAMPLE_LCD_PARAM_BITS (8)
|
||||
#define EXAMPLE_LCD_COLOR_SPACE (ESP_LCD_COLOR_SPACE_BGR)
|
||||
#define EXAMPLE_LCD_BITS_PER_PIXEL (16)
|
||||
#define EXAMPLE_LCD_DRAW_BUFF_DOUBLE (1)
|
||||
#define EXAMPLE_LCD_DRAW_BUFF_HEIGHT (50)
|
||||
#define EXAMPLE_LCD_BL_ON_LEVEL (1)
|
||||
|
||||
/* LCD pins */
|
||||
#define EXAMPLE_LCD_GPIO_SCLK (GPIO_NUM_7)
|
||||
#define EXAMPLE_LCD_GPIO_MOSI (GPIO_NUM_6)
|
||||
#define EXAMPLE_LCD_GPIO_RST (GPIO_NUM_48)
|
||||
#define EXAMPLE_LCD_GPIO_DC (GPIO_NUM_4)
|
||||
#define EXAMPLE_LCD_GPIO_CS (GPIO_NUM_5)
|
||||
#define EXAMPLE_LCD_GPIO_BL (GPIO_NUM_45)
|
||||
|
||||
/* Touch settings */
|
||||
#define EXAMPLE_TOUCH_I2C_NUM (0)
|
||||
#define EXAMPLE_TOUCH_I2C_CLK_HZ (400000)
|
||||
|
||||
/* LCD touch pins */
|
||||
#define EXAMPLE_TOUCH_I2C_SCL (GPIO_NUM_18)
|
||||
#define EXAMPLE_TOUCH_I2C_SDA (GPIO_NUM_8)
|
||||
#define EXAMPLE_TOUCH_GPIO_INT (GPIO_NUM_3)
|
||||
|
||||
static const char *TAG = "EXAMPLE";
|
||||
|
||||
/* LCD IO and panel */
|
||||
static esp_lcd_panel_io_handle_t lcd_io = NULL;
|
||||
static esp_lcd_panel_handle_t lcd_panel = NULL;
|
||||
static esp_lcd_touch_handle_t touch_handle = NULL;
|
||||
|
||||
/* LVGL display and touch */
|
||||
static lv_display_t *lvgl_disp = NULL;
|
||||
static lv_indev_t *lvgl_touch_indev = NULL;
|
||||
|
||||
static esp_err_t app_lcd_init(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
/* LCD backlight */
|
||||
gpio_config_t bk_gpio_config = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << EXAMPLE_LCD_GPIO_BL
|
||||
};
|
||||
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
|
||||
|
||||
/* LCD initialization */
|
||||
ESP_LOGD(TAG, "Initialize SPI bus");
|
||||
const spi_bus_config_t buscfg = {
|
||||
.sclk_io_num = EXAMPLE_LCD_GPIO_SCLK,
|
||||
.mosi_io_num = EXAMPLE_LCD_GPIO_MOSI,
|
||||
.miso_io_num = GPIO_NUM_NC,
|
||||
.quadwp_io_num = GPIO_NUM_NC,
|
||||
.quadhd_io_num = GPIO_NUM_NC,
|
||||
.max_transfer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT * sizeof(uint16_t),
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(spi_bus_initialize(EXAMPLE_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI init failed");
|
||||
|
||||
ESP_LOGD(TAG, "Install panel IO");
|
||||
const esp_lcd_panel_io_spi_config_t io_config = {
|
||||
.dc_gpio_num = EXAMPLE_LCD_GPIO_DC,
|
||||
.cs_gpio_num = EXAMPLE_LCD_GPIO_CS,
|
||||
.pclk_hz = EXAMPLE_LCD_PIXEL_CLK_HZ,
|
||||
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
|
||||
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
|
||||
.spi_mode = 0,
|
||||
.trans_queue_depth = 10,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)EXAMPLE_LCD_SPI_NUM, &io_config, &lcd_io), err, TAG, "New panel IO failed");
|
||||
|
||||
ESP_LOGD(TAG, "Install LCD driver");
|
||||
const esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = EXAMPLE_LCD_GPIO_RST,
|
||||
.color_space = EXAMPLE_LCD_COLOR_SPACE,
|
||||
.bits_per_pixel = EXAMPLE_LCD_BITS_PER_PIXEL,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7789(lcd_io, &panel_config, &lcd_panel), err, TAG, "New panel failed");
|
||||
|
||||
esp_lcd_panel_reset(lcd_panel);
|
||||
esp_lcd_panel_init(lcd_panel);
|
||||
esp_lcd_panel_mirror(lcd_panel, true, true);
|
||||
esp_lcd_panel_disp_on_off(lcd_panel, true);
|
||||
|
||||
/* LCD backlight on */
|
||||
ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_LCD_GPIO_BL, EXAMPLE_LCD_BL_ON_LEVEL));
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
if (lcd_panel) {
|
||||
esp_lcd_panel_del(lcd_panel);
|
||||
}
|
||||
if (lcd_io) {
|
||||
esp_lcd_panel_io_del(lcd_io);
|
||||
}
|
||||
spi_bus_free(EXAMPLE_LCD_SPI_NUM);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t app_touch_init(void)
|
||||
{
|
||||
/* Initilize I2C */
|
||||
const i2c_config_t i2c_conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = EXAMPLE_TOUCH_I2C_SDA,
|
||||
.sda_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
.scl_io_num = EXAMPLE_TOUCH_I2C_SCL,
|
||||
.scl_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
.master.clk_speed = EXAMPLE_TOUCH_I2C_CLK_HZ
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(i2c_param_config(EXAMPLE_TOUCH_I2C_NUM, &i2c_conf), TAG, "I2C configuration failed");
|
||||
ESP_RETURN_ON_ERROR(i2c_driver_install(EXAMPLE_TOUCH_I2C_NUM, i2c_conf.mode, 0, 0, 0), TAG, "I2C initialization failed");
|
||||
|
||||
/* Initialize touch HW */
|
||||
const esp_lcd_touch_config_t tp_cfg = {
|
||||
.x_max = EXAMPLE_LCD_H_RES,
|
||||
.y_max = EXAMPLE_LCD_V_RES,
|
||||
.rst_gpio_num = GPIO_NUM_NC, // Shared with LCD reset
|
||||
.int_gpio_num = EXAMPLE_TOUCH_GPIO_INT,
|
||||
.levels = {
|
||||
.reset = 0,
|
||||
.interrupt = 0,
|
||||
},
|
||||
.flags = {
|
||||
.swap_xy = 0,
|
||||
.mirror_x = 1,
|
||||
.mirror_y = 0,
|
||||
},
|
||||
};
|
||||
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
|
||||
const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_TT21100_CONFIG();
|
||||
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)EXAMPLE_TOUCH_I2C_NUM, &tp_io_config, &tp_io_handle), TAG, "");
|
||||
return esp_lcd_touch_new_i2c_tt21100(tp_io_handle, &tp_cfg, &touch_handle);
|
||||
}
|
||||
|
||||
static esp_err_t app_lvgl_init(void)
|
||||
{
|
||||
/* Initialize LVGL */
|
||||
const lvgl_port_cfg_t lvgl_cfg = {
|
||||
.task_priority = 4, /* LVGL task priority */
|
||||
.task_stack = 4096, /* LVGL task stack size */
|
||||
.task_affinity = -1, /* LVGL task pinned to core (-1 is no affinity) */
|
||||
.task_max_sleep_ms = 500, /* Maximum sleep in LVGL task */
|
||||
.timer_period_ms = 5 /* LVGL timer tick period in ms */
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "LVGL port initialization failed");
|
||||
|
||||
/* Add LCD screen */
|
||||
ESP_LOGD(TAG, "Add LCD screen");
|
||||
const lvgl_port_display_cfg_t disp_cfg = {
|
||||
.io_handle = lcd_io,
|
||||
.panel_handle = lcd_panel,
|
||||
.buffer_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT * sizeof(uint16_t),
|
||||
.double_buffer = EXAMPLE_LCD_DRAW_BUFF_DOUBLE,
|
||||
.hres = EXAMPLE_LCD_H_RES,
|
||||
.vres = EXAMPLE_LCD_V_RES,
|
||||
.monochrome = false,
|
||||
/* Rotation values must be same as used in esp_lcd for initial settings of the screen */
|
||||
.rotation = {
|
||||
.swap_xy = false,
|
||||
.mirror_x = true,
|
||||
.mirror_y = true,
|
||||
},
|
||||
.flags = {
|
||||
.buff_dma = true,
|
||||
.swap_bytes = true,
|
||||
}
|
||||
};
|
||||
lvgl_disp = lvgl_port_add_disp(&disp_cfg);
|
||||
|
||||
/* Add touch input (for selected screen) */
|
||||
const lvgl_port_touch_cfg_t touch_cfg = {
|
||||
.disp = lvgl_disp,
|
||||
.handle = touch_handle,
|
||||
};
|
||||
lvgl_touch_indev = lvgl_port_add_touch(&touch_cfg);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void _app_button_cb(lv_event_t *e)
|
||||
{
|
||||
lv_disp_rotation_t rotation = lv_disp_get_rotation(lvgl_disp);
|
||||
rotation++;
|
||||
if (rotation > LV_DISPLAY_ROTATION_270) {
|
||||
rotation = LV_DISPLAY_ROTATION_0;
|
||||
}
|
||||
|
||||
/* LCD HW rotation */
|
||||
lv_disp_set_rotation(lvgl_disp, rotation);
|
||||
}
|
||||
|
||||
static void app_main_display(void)
|
||||
{
|
||||
lv_obj_t *scr = lv_scr_act();
|
||||
|
||||
/* Task lock */
|
||||
lvgl_port_lock(0);
|
||||
|
||||
/* Your LVGL objects code here .... */
|
||||
|
||||
/* Label */
|
||||
lv_obj_t *label = lv_label_create(scr);
|
||||
lv_obj_set_width(label, EXAMPLE_LCD_H_RES);
|
||||
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
lv_label_set_recolor(label, true);
|
||||
lv_label_set_text(label, "#FF0000 "LV_SYMBOL_BELL" Hello world Espressif and LVGL "LV_SYMBOL_BELL"#\n#FF9400 "LV_SYMBOL_WARNING" For simplier initialization, use BSP "LV_SYMBOL_WARNING" #");
|
||||
#else
|
||||
lv_label_set_text(label, LV_SYMBOL_BELL" Hello world Espressif and LVGL "LV_SYMBOL_BELL"\n "LV_SYMBOL_WARNING" For simplier initialization, use BSP "LV_SYMBOL_WARNING);
|
||||
#endif
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, -30);
|
||||
|
||||
/* Button */
|
||||
lv_obj_t *btn = lv_btn_create(scr);
|
||||
label = lv_label_create(btn);
|
||||
lv_label_set_text_static(label, "Rotate screen");
|
||||
lv_obj_align(btn, LV_ALIGN_BOTTOM_MID, 0, -30);
|
||||
lv_obj_add_event_cb(btn, _app_button_cb, LV_EVENT_CLICKED, NULL);
|
||||
|
||||
/* Task unlock */
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/* LCD HW initialization */
|
||||
ESP_ERROR_CHECK(app_lcd_init());
|
||||
|
||||
/* Touch initialization */
|
||||
ESP_ERROR_CHECK(app_touch_init());
|
||||
|
||||
/* LVGL initialization */
|
||||
ESP_ERROR_CHECK(app_lvgl_init());
|
||||
|
||||
/* Show LVGL objects */
|
||||
app_main_display();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
|
||||
CONFIG_LV_COLOR_16_SWAP=y
|
||||
@@ -0,0 +1,8 @@
|
||||
version: "1.5.0"
|
||||
description: ESP LVGL port
|
||||
url: https://github.com/espressif/esp-bsp/tree/master/components/esp_lvgl_port
|
||||
dependencies:
|
||||
idf: ">=4.4"
|
||||
lvgl/lvgl:
|
||||
version: ">=8,<10"
|
||||
public: true
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include("lvgl.h")
|
||||
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef LV_ATTRIBUTE_MEM_ALIGN
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef LV_ATTRIBUTE_IMG_IMG_CURSOR
|
||||
#define LV_ATTRIBUTE_IMG_IMG_CURSOR
|
||||
#endif
|
||||
|
||||
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_IMG_CURSOR uint8_t img_cursor_map[] = {
|
||||
#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
|
||||
/*Pixel format: Alpha 8 bit, Red: 3 bit, Green: 3 bit, Blue: 2 bit*/
|
||||
0x00, 0xb2, 0x00, 0xcc, 0x00, 0x71, 0x00, 0x3a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xcc, 0x00, 0xff, 0x00, 0xfc, 0x00, 0xe4, 0x00, 0xae, 0x00, 0x6b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x71, 0x00, 0xfc, 0x6d, 0xf1, 0xb6, 0xfc, 0x49, 0xf9, 0x24, 0xfe, 0x00, 0xf4, 0x00, 0xb9, 0x00, 0x5c, 0x00, 0x2e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x3a, 0x00, 0xe4, 0xb6, 0xfc, 0xff, 0xff, 0xff, 0xf9, 0xdb, 0xfe, 0x6e, 0xf3, 0x25, 0xfe, 0x00, 0xf8, 0x00, 0xd8, 0x00, 0x9c, 0x00, 0x51, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0c, 0x00, 0xae, 0x49, 0xf9, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xb7, 0xf0, 0x6e, 0xfb, 0x25, 0xf9, 0x00, 0xff, 0x00, 0xe8, 0x00, 0x9e, 0x00, 0x4a, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x6b, 0x24, 0xfe, 0xdb, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xf9, 0xb7, 0xf6, 0x49, 0xf5, 0x24, 0xff, 0x00, 0xf0, 0x00, 0xcb, 0x00, 0x88, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x13, 0x00, 0xf4, 0x6e, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x92, 0xf5, 0x24, 0xfd, 0x00, 0xff, 0x00, 0xed, 0x00, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x25, 0xfe, 0xff, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x92, 0xfc, 0x24, 0xfd, 0x00, 0xe7, 0x00, 0x78, 0x00, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0xf8, 0xb7, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xf9, 0x25, 0xfd, 0x00, 0xee, 0x00, 0x9d, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0xd7, 0x6e, 0xfb, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xf9, 0x25, 0xfd, 0x00, 0xdd, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x9c, 0x25, 0xf9, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xfa, 0x25, 0xfc, 0x00, 0xdd, 0x00, 0x2c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0xff, 0xb7, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xfa, 0x25, 0xfc, 0x00, 0xc2, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0xe8, 0x49, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xf9, 0xdb, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xfa, 0x25, 0xfd, 0x00, 0xdd, 0x00, 0x2c, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x24, 0xff, 0xff, 0xf5, 0xff, 0xf4, 0x25, 0xfd, 0x25, 0xfd, 0xdb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xfa, 0x25, 0xfc, 0x00, 0xc2, 0x00, 0x2c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0xf0, 0x92, 0xf5, 0x92, 0xfc, 0x00, 0xee, 0x00, 0xdd, 0x25, 0xfc, 0xdb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xef, 0x00, 0xff, 0x00, 0xd7,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0xcb, 0x24, 0xfd, 0x24, 0xfd, 0x00, 0x9d, 0x00, 0x37, 0x00, 0xdd, 0x25, 0xfd, 0xdb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x92, 0xf2, 0x00, 0xff, 0x00, 0xb6,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x88, 0x00, 0xff, 0x00, 0xe7, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2c, 0x00, 0xc2, 0x25, 0xfc, 0xdb, 0xfa, 0xff, 0xff, 0xff, 0xfc, 0x92, 0xf8, 0x00, 0xfe, 0x00, 0x9d, 0x00, 0x16,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0xed, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2c, 0x00, 0xdd, 0x25, 0xfc, 0xdb, 0xef, 0x92, 0xf2, 0x00, 0xfe, 0x00, 0xb9, 0x00, 0x16, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0xc2, 0x00, 0xff, 0x00, 0xff, 0x00, 0x9d, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2c, 0x00, 0xd7, 0x00, 0xb6, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
#endif
|
||||
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0
|
||||
/*Pixel format: Alpha 8 bit, Red: 5 bit, Green: 6 bit, Blue: 5 bit*/
|
||||
0x00, 0x00, 0xb2, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x71, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xcc, 0x00, 0x00, 0xff, 0x00, 0x00, 0xfc, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xae, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x71, 0x00, 0x00, 0xfc, 0xeb, 0x5a, 0xf1, 0xb2, 0x94, 0xfc, 0x08, 0x42, 0xf9, 0x04, 0x21, 0xfe, 0x41, 0x08, 0xf4, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3a, 0x00, 0x00, 0xe4, 0xb2, 0x94, 0xfc, 0xff, 0xff, 0xff, 0x3c, 0xe7, 0xf9, 0x59, 0xce, 0xfe, 0x6e, 0x73, 0xf3, 0x04, 0x21, 0xfe, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x51, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x0c, 0x00, 0x00, 0xae, 0x08, 0x42, 0xf9, 0x3c, 0xe7, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x76, 0xb5, 0xf0, 0x8e, 0x73, 0xfb, 0x45, 0x29, 0xf9, 0x82, 0x10, 0xff, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x21, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x04, 0x21, 0xfe, 0x59, 0xce, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xbb, 0xde, 0xf9, 0x96, 0xb5, 0xf6, 0x49, 0x4a, 0xf5, 0x04, 0x21, 0xff, 0x00, 0x00, 0xf0, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x88, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x41, 0x08, 0xf4, 0x6e, 0x73, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbe, 0xf7, 0xf5, 0x10, 0x84, 0xf5, 0xa2, 0x10, 0xfd, 0x00, 0x00, 0xff, 0x00, 0x00, 0xed, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x04, 0x21, 0xfe, 0xff, 0xff, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9e, 0xf7, 0xf4, 0xae, 0x73, 0xfc, 0xa2, 0x10, 0xfd, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x78, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0xf8, 0x76, 0xb5, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0xf9, 0x86, 0x31, 0xfd, 0x00, 0x00, 0xee, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0xd7, 0x8e, 0x73, 0xfb, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0xf9, 0x86, 0x31, 0xfd, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x9c, 0x45, 0x29, 0xf9, 0xbb, 0xde, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79, 0xce, 0xfa, 0x65, 0x29, 0xfc, 0x20, 0x00, 0xdd, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x82, 0x10, 0xff, 0x96, 0xb5, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x96, 0xb5, 0xfa, 0x65, 0x29, 0xfc, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xe8, 0x49, 0x4a, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0xf9, 0x59, 0xce, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79, 0xce, 0xfa, 0x65, 0x29, 0xfd, 0x20, 0x00, 0xdd, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x04, 0x21, 0xff, 0xbe, 0xf7, 0xf5, 0x9e, 0xf7, 0xf4, 0x86, 0x31, 0xfd, 0x86, 0x31, 0xfd, 0x79, 0xce, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x96, 0xb5, 0xfa, 0x65, 0x29, 0xfc, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x2c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0xf0, 0x10, 0x84, 0xf5, 0xae, 0x73, 0xfc, 0x00, 0x00, 0xee, 0x00, 0x00, 0xdd, 0x65, 0x29, 0xfc, 0x96, 0xb5, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x96, 0xb5, 0xef, 0x00, 0x00, 0xff, 0x00, 0x00, 0xd7,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0xcb, 0xa2, 0x10, 0xfd, 0xa2, 0x10, 0xfd, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x37, 0x20, 0x00, 0xdd, 0x65, 0x29, 0xfd, 0x79, 0xce, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x51, 0x8c, 0xf2, 0x00, 0x00, 0xff, 0x00, 0x00, 0xb6,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0xff, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xc2, 0x65, 0x29, 0xfc, 0x96, 0xb5, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xaf, 0x7b, 0xf8, 0x41, 0x08, 0xfe, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x16,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0xed, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x2c, 0x20, 0x00, 0xdd, 0x65, 0x29, 0xfc, 0x96, 0xb5, 0xef, 0x51, 0x8c, 0xf2, 0x41, 0x08, 0xfe, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xd7, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
#endif
|
||||
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP != 0
|
||||
/*Pixel format: Alpha 8 bit, Red: 5 bit, Green: 6 bit, Blue: 5 bit BUT the 2 color bytes are swapped*/
|
||||
0x00, 0x00, 0xb2, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x71, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xcc, 0x00, 0x00, 0xff, 0x00, 0x00, 0xfc, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xae, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x71, 0x00, 0x00, 0xfc, 0x5a, 0xeb, 0xf1, 0x94, 0xb2, 0xfc, 0x42, 0x08, 0xf9, 0x21, 0x04, 0xfe, 0x08, 0x41, 0xf4, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3a, 0x00, 0x00, 0xe4, 0x94, 0xb2, 0xfc, 0xff, 0xff, 0xff, 0xe7, 0x3c, 0xf9, 0xce, 0x59, 0xfe, 0x73, 0x6e, 0xf3, 0x21, 0x04, 0xfe, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x51, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x0c, 0x00, 0x00, 0xae, 0x42, 0x08, 0xf9, 0xe7, 0x3c, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xb5, 0x76, 0xf0, 0x73, 0x8e, 0xfb, 0x29, 0x45, 0xf9, 0x10, 0x82, 0xff, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x21, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x21, 0x04, 0xfe, 0xce, 0x59, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xde, 0xbb, 0xf9, 0xb5, 0x96, 0xf6, 0x4a, 0x49, 0xf5, 0x21, 0x04, 0xff, 0x00, 0x00, 0xf0, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x88, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x08, 0x41, 0xf4, 0x73, 0x6e, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xbe, 0xf5, 0x84, 0x10, 0xf5, 0x10, 0xa2, 0xfd, 0x00, 0x00, 0xff, 0x00, 0x00, 0xed, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x21, 0x04, 0xfe, 0xff, 0xff, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x9e, 0xf4, 0x73, 0xae, 0xfc, 0x10, 0xa2, 0xfd, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x78, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0xf8, 0xb5, 0x76, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x59, 0xf9, 0x31, 0x86, 0xfd, 0x00, 0x00, 0xee, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0xd7, 0x73, 0x8e, 0xfb, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x59, 0xf9, 0x31, 0x86, 0xfd, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x9c, 0x29, 0x45, 0xf9, 0xde, 0xbb, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x79, 0xfa, 0x29, 0x65, 0xfc, 0x00, 0x20, 0xdd, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x10, 0x82, 0xff, 0xb5, 0x96, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb5, 0x96, 0xfa, 0x29, 0x65, 0xfc, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xe8, 0x4a, 0x49, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x59, 0xf9, 0xce, 0x59, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x79, 0xfa, 0x29, 0x65, 0xfd, 0x00, 0x20, 0xdd, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x21, 0x04, 0xff, 0xf7, 0xbe, 0xf5, 0xf7, 0x9e, 0xf4, 0x31, 0x86, 0xfd, 0x31, 0x86, 0xfd, 0xce, 0x79, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb5, 0x96, 0xfa, 0x29, 0x65, 0xfc, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x2c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0xf0, 0x84, 0x10, 0xf5, 0x73, 0xae, 0xfc, 0x00, 0x00, 0xee, 0x00, 0x00, 0xdd, 0x29, 0x65, 0xfc, 0xb5, 0x96, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb5, 0x96, 0xef, 0x00, 0x00, 0xff, 0x00, 0x00, 0xd7,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0xcb, 0x10, 0xa2, 0xfd, 0x10, 0xa2, 0xfd, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x37, 0x00, 0x20, 0xdd, 0x29, 0x65, 0xfd, 0xce, 0x79, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x8c, 0x51, 0xf2, 0x00, 0x00, 0xff, 0x00, 0x00, 0xb6,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0xff, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xc2, 0x29, 0x65, 0xfc, 0xb5, 0x96, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x7b, 0xaf, 0xf8, 0x08, 0x41, 0xfe, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x16,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0xed, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x2c, 0x00, 0x20, 0xdd, 0x29, 0x65, 0xfc, 0xb5, 0x96, 0xef, 0x8c, 0x51, 0xf2, 0x08, 0x41, 0xfe, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xd7, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
#endif
|
||||
#if LV_COLOR_DEPTH == 32
|
||||
0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xfc, 0x5b, 0x5b, 0x5b, 0xf1, 0x93, 0x93, 0x93, 0xfc, 0x41, 0x41, 0x41, 0xf9, 0x1e, 0x1e, 0x1e, 0xfe, 0x06, 0x06, 0x06, 0xf4, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0xe4, 0x93, 0x93, 0x93, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xe3, 0xe3, 0xf9, 0xc8, 0xc8, 0xc8, 0xfe, 0x6c, 0x6c, 0x6c, 0xf3, 0x20, 0x20, 0x20, 0xfe, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xae, 0x41, 0x41, 0x41, 0xf9, 0xe3, 0xe3, 0xe3, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0xfb, 0xfa, 0xac, 0xac, 0xac, 0xf0, 0x6f, 0x6f, 0x6f, 0xfb, 0x26, 0x26, 0x26, 0xf9, 0x0f, 0x0f, 0x0f, 0xff, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x1e, 0x1e, 0x1e, 0xfe, 0xc8, 0xc8, 0xc8, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfb, 0xd4, 0xd4, 0xd4, 0xf9, 0xae, 0xae, 0xae, 0xf6, 0x48, 0x48, 0x48, 0xf5, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x06, 0x06, 0x06, 0xf4, 0x6c, 0x6c, 0x6c, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xf3, 0xf3, 0xf5, 0x7e, 0x7e, 0x7e, 0xf5, 0x12, 0x12, 0x12, 0xfd, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x20, 0x20, 0x20, 0xfe, 0xfb, 0xfb, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xef, 0xef, 0xf4, 0x73, 0x73, 0x73, 0xfc, 0x12, 0x12, 0x12, 0xfd, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0xf8, 0xac, 0xac, 0xac, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xf9, 0x2e, 0x2e, 0x2e, 0xfd, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0xd7, 0x6f, 0x6f, 0x6f, 0xfb, 0xfc, 0xfc, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xf9, 0x2e, 0x2e, 0x2e, 0xfd, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x9c, 0x26, 0x26, 0x26, 0xf9, 0xd4, 0xd4, 0xd4, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0xca, 0xca, 0xfa, 0x2b, 0x2b, 0x2b, 0xfc, 0x03, 0x03, 0x03, 0xdd, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x0f, 0x0f, 0x0f, 0xff, 0xae, 0xae, 0xae, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb0, 0xb0, 0xb0, 0xfa, 0x2b, 0x2b, 0x2b, 0xfc, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xe8, 0x48, 0x48, 0x48, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xf9, 0xc8, 0xc8, 0xc8, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0xca, 0xca, 0xfa, 0x2b, 0x2b, 0x2b, 0xfd, 0x03, 0x03, 0x03, 0xdd, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x1f, 0x1f, 0x1f, 0xff, 0xf3, 0xf3, 0xf3, 0xf5, 0xf0, 0xf0, 0xf0, 0xf4, 0x2e, 0x2e, 0x2e, 0xfd, 0x2e, 0x2e, 0x2e, 0xfd, 0xca, 0xca, 0xca, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb0, 0xb0, 0xb0, 0xfa, 0x2b, 0x2b, 0x2b, 0xfc, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x2c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0xf0, 0x7e, 0x7e, 0x7e, 0xf5, 0x73, 0x73, 0x73, 0xfc, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xdd, 0x2b, 0x2b, 0x2b, 0xfc, 0xb0, 0xb0, 0xb0, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0xb1, 0xb1, 0xef, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xd7,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xcb, 0x12, 0x12, 0x12, 0xfd, 0x12, 0x12, 0x12, 0xfd, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x37, 0x03, 0x03, 0x03, 0xdd, 0x2b, 0x2b, 0x2b, 0xfd, 0xca, 0xca, 0xca, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfc, 0x86, 0x86, 0x86, 0xf2, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xb6,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xc2, 0x2b, 0x2b, 0x2b, 0xfc, 0xb0, 0xb0, 0xb0, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfc, 0x74, 0x74, 0x74, 0xf8, 0x06, 0x06, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x16,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x03, 0x03, 0x03, 0xdd, 0x2b, 0x2b, 0x2b, 0xfc, 0xb1, 0xb1, 0xb1, 0xef, 0x86, 0x86, 0x86, 0xf2, 0x06, 0x06, 0x06, 0xfe, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
#endif
|
||||
};
|
||||
|
||||
const lv_img_dsc_t img_cursor = {
|
||||
.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA,
|
||||
.header.always_zero = 0,
|
||||
.header.reserved = 0,
|
||||
.header.w = 20,
|
||||
.header.h = 20,
|
||||
.data_size = 400 * LV_IMG_PX_SIZE_ALPHA_BYTE,
|
||||
.data = img_cursor_map,
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include("lvgl.h")
|
||||
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef LV_ATTRIBUTE_MEM_ALIGN
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef LV_ATTRIBUTE_IMG_DUST
|
||||
#define LV_ATTRIBUTE_IMG_DUST
|
||||
#endif
|
||||
|
||||
static const
|
||||
LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_DUST
|
||||
uint8_t img_cursor_20px_map[] = {
|
||||
|
||||
0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xfc, 0x5b, 0x5b, 0x5b, 0xf1, 0x93, 0x93, 0x93, 0xfc, 0x41, 0x41, 0x41, 0xf9, 0x1e, 0x1e, 0x1e, 0xfe, 0x06, 0x06, 0x06, 0xf4, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0xe4, 0x93, 0x93, 0x93, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xe3, 0xe3, 0xf9, 0xc8, 0xc8, 0xc8, 0xfe, 0x6c, 0x6c, 0x6c, 0xf3, 0x20, 0x20, 0x20, 0xfe, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xae, 0x41, 0x41, 0x41, 0xf9, 0xe3, 0xe3, 0xe3, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0xfb, 0xfa, 0xac, 0xac, 0xac, 0xf0, 0x6f, 0x6f, 0x6f, 0xfb, 0x26, 0x26, 0x26, 0xf9, 0x0f, 0x0f, 0x0f, 0xff, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x1e, 0x1e, 0x1e, 0xfe, 0xc8, 0xc8, 0xc8, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfb, 0xd4, 0xd4, 0xd4, 0xf9, 0xae, 0xae, 0xae, 0xf6, 0x48, 0x48, 0x48, 0xf5, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x06, 0x06, 0x06, 0xf4, 0x6c, 0x6c, 0x6c, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0xf5, 0x7e, 0x7e, 0x7e, 0xf5, 0x12, 0x12, 0x12, 0xfd, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x20, 0x20, 0x20, 0xfe, 0xfb, 0xfb, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xef, 0xef, 0xf4, 0x73, 0x73, 0x73, 0xfc, 0x12, 0x12, 0x12, 0xfd, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0xf8, 0xac, 0xac, 0xac, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xf9, 0x2e, 0x2e, 0x2e, 0xfd, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0xd7, 0x6f, 0x6f, 0x6f, 0xfb, 0xfc, 0xfc, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xf9, 0x2e, 0x2e, 0x2e, 0xfd, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x9c, 0x26, 0x26, 0x26, 0xf9, 0xd4, 0xd4, 0xd4, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0xca, 0xca, 0xfa, 0x2b, 0x2b, 0x2b, 0xfc, 0x03, 0x03, 0x03, 0xdd, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x0f, 0x0f, 0x0f, 0xff, 0xae, 0xae, 0xae, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb0, 0xb0, 0xb0, 0xfa, 0x2b, 0x2b, 0x2b, 0xfc, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xe8, 0x48, 0x48, 0x48, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xf9, 0xc8, 0xc8, 0xc8, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0xca, 0xca, 0xfa, 0x2b, 0x2b, 0x2b, 0xfd, 0x03, 0x03, 0x03, 0xdd, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x1f, 0x1f, 0x1f, 0xff, 0xf2, 0xf2, 0xf2, 0xf5, 0xf0, 0xf0, 0xf0, 0xf4, 0x2e, 0x2e, 0x2e, 0xfd, 0x2e, 0x2e, 0x2e, 0xfd, 0xca, 0xca, 0xca, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb0, 0xb0, 0xb0, 0xfa, 0x2b, 0x2b, 0x2b, 0xfc, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x2c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0xf0, 0x7e, 0x7e, 0x7e, 0xf5, 0x73, 0x73, 0x73, 0xfc, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xdd, 0x2b, 0x2b, 0x2b, 0xfc, 0xb0, 0xb0, 0xb0, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0xb1, 0xb1, 0xef, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xd7,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xcb, 0x12, 0x12, 0x12, 0xfd, 0x12, 0x12, 0x12, 0xfd, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x37, 0x03, 0x03, 0x03, 0xdd, 0x2b, 0x2b, 0x2b, 0xfd, 0xca, 0xca, 0xca, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfc, 0x86, 0x86, 0x86, 0xf2, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xb6,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xc2, 0x2b, 0x2b, 0x2b, 0xfc, 0xb0, 0xb0, 0xb0, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfc, 0x74, 0x74, 0x74, 0xf8, 0x06, 0x06, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x16,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x03, 0x03, 0x03, 0xdd, 0x2b, 0x2b, 0x2b, 0xfc, 0xb1, 0xb1, 0xb1, 0xef, 0x86, 0x86, 0x86, 0xf2, 0x06, 0x06, 0x06, 0xfe, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
};
|
||||
|
||||
const lv_img_dsc_t img_cursor = {
|
||||
.header.magic = LV_IMAGE_HEADER_MAGIC,
|
||||
.header.cf = LV_COLOR_FORMAT_ARGB8888,
|
||||
.header.flags = 0,
|
||||
.header.w = 20,
|
||||
.header.h = 20,
|
||||
.header.stride = 80,
|
||||
.data_size = 1600,
|
||||
.data = img_cursor_20px_map,
|
||||
};
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_lvgl_port_button.h"
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#include "esp_lvgl_port_disp.h"
|
||||
#include "esp_lvgl_port_knob.h"
|
||||
#include "esp_lvgl_port_touch.h"
|
||||
#include "esp_lvgl_port_usbhid.h"
|
||||
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Init configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
int task_priority; /*!< LVGL task priority */
|
||||
int task_stack; /*!< LVGL task stack size */
|
||||
int task_affinity; /*!< LVGL task pinned to core (-1 is no affinity) */
|
||||
int task_max_sleep_ms; /*!< Maximum sleep in LVGL task */
|
||||
int timer_period_ms; /*!< LVGL timer tick period in ms */
|
||||
} lvgl_port_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief LVGL port configuration structure
|
||||
*
|
||||
*/
|
||||
#define ESP_LVGL_PORT_INIT_CONFIG() \
|
||||
{ \
|
||||
.task_priority = 4, \
|
||||
.task_stack = 6144, \
|
||||
.task_affinity = -1, \
|
||||
.task_max_sleep_ms = 500, \
|
||||
.timer_period_ms = 5, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize LVGL portation
|
||||
*
|
||||
* @note This function initialize LVGL and create timer and task for LVGL right working.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if some of the create_args are not valid
|
||||
* - ESP_ERR_INVALID_STATE if esp_timer library is not initialized yet
|
||||
* - ESP_ERR_NO_MEM if memory allocation fails
|
||||
*/
|
||||
esp_err_t lvgl_port_init(const lvgl_port_cfg_t *cfg);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize LVGL portation
|
||||
*
|
||||
* @note This function deinitializes LVGL and stops the task if running.
|
||||
* Some deinitialization will be done after the task will be stopped.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t lvgl_port_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Take LVGL mutex
|
||||
*
|
||||
* @param timeout_ms Timeout in [ms]. 0 will block indefinitely.
|
||||
* @return
|
||||
* - true Mutex was taken
|
||||
* - false Mutex was NOT taken
|
||||
*/
|
||||
bool lvgl_port_lock(uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Give LVGL mutex
|
||||
*
|
||||
*/
|
||||
void lvgl_port_unlock(void);
|
||||
|
||||
/**
|
||||
* @brief Notify LVGL, that data was flushed to LCD display
|
||||
*
|
||||
* @note It should be used only when not called inside LVGL port (more in README).
|
||||
*
|
||||
* @param disp LVGL display handle (returned from lvgl_port_add_disp)
|
||||
*/
|
||||
void lvgl_port_flush_ready(lv_display_t *disp);
|
||||
|
||||
/**
|
||||
* @brief Stop lvgl task
|
||||
*
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if the timer is not running
|
||||
*/
|
||||
esp_err_t lvgl_port_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Resume lvgl task
|
||||
*
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if the timer is not running
|
||||
*/
|
||||
esp_err_t lvgl_port_resume(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port button
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
#if __has_include ("iot_button.h")
|
||||
#include "iot_button.h"
|
||||
#define ESP_LVGL_PORT_BUTTON_COMPONENT 1
|
||||
#endif
|
||||
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef ESP_LVGL_PORT_BUTTON_COMPONENT
|
||||
/**
|
||||
* @brief Configuration of the navigation buttons structure
|
||||
*/
|
||||
typedef struct {
|
||||
lv_display_t *disp; /*!< LVGL display handle (returned from lvgl_port_add_disp) */
|
||||
const button_config_t *button_prev; /*!< Navigation button for previous */
|
||||
const button_config_t *button_next; /*!< Navigation button for next */
|
||||
const button_config_t *button_enter; /*!< Navigation button for enter */
|
||||
} lvgl_port_nav_btns_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Add buttons as an input device
|
||||
*
|
||||
* @note Allocated memory in this function is not free in deinit. You must call lvgl_port_remove_navigation_buttons for free all memory!
|
||||
*
|
||||
* @param buttons_cfg Buttons configuration structure
|
||||
* @return Pointer to LVGL buttons input device or NULL when error occurred
|
||||
*/
|
||||
lv_indev_t *lvgl_port_add_navigation_buttons(const lvgl_port_nav_btns_cfg_t *buttons_cfg);
|
||||
|
||||
/**
|
||||
* @brief Remove selected buttons from input devices
|
||||
*
|
||||
* @note Free all memory used for this input device.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t lvgl_port_remove_navigation_buttons(lv_indev_t *buttons);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port compatibility
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Backward compatibility with LVGL 8
|
||||
*/
|
||||
typedef lv_disp_t lv_display_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port display
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Rotation configuration
|
||||
*/
|
||||
typedef struct {
|
||||
bool swap_xy; /*!< LCD Screen swapped X and Y (in esp_lcd driver) */
|
||||
bool mirror_x; /*!< LCD Screen mirrored X (in esp_lcd driver) */
|
||||
bool mirror_y; /*!< LCD Screen mirrored Y (in esp_lcd driver) */
|
||||
} lvgl_port_rotation_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration display structure
|
||||
*/
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_handle_t io_handle; /*!< LCD panel IO handle */
|
||||
esp_lcd_panel_handle_t panel_handle; /*!< LCD panel handle */
|
||||
uint32_t buffer_size; /*!< Size of the buffer for the screen in pixels */
|
||||
bool double_buffer; /*!< True, if should be allocated two buffers */
|
||||
uint32_t trans_size; /*!< Allocated buffer will be in SRAM to move framebuf */
|
||||
uint32_t hres; /*!< LCD display horizontal resolution */
|
||||
uint32_t vres; /*!< LCD display vertical resolution */
|
||||
bool monochrome; /*!< True, if display is monochrome and using 1bit for 1px */
|
||||
lvgl_port_rotation_cfg_t rotation; /*!< Default values of the screen rotation */
|
||||
|
||||
struct {
|
||||
unsigned int buff_dma: 1; /*!< Allocated LVGL buffer will be DMA capable */
|
||||
unsigned int buff_spiram: 1; /*!< Allocated LVGL buffer will be in PSRAM */
|
||||
unsigned int sw_rotate: 1; /*!< Use software rotation (slower) */
|
||||
unsigned int swap_bytes: 1; /*!< Swap bytes in RGB656 (16-bit) before send to LCD driver */
|
||||
} flags;
|
||||
} lvgl_port_display_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Add display handling to LVGL
|
||||
*
|
||||
* @note Allocated memory in this function is not free in deinit. You must call lvgl_port_remove_disp for free all memory!
|
||||
*
|
||||
* @param disp_cfg Display configuration structure
|
||||
* @return Pointer to LVGL display or NULL when error occurred
|
||||
*/
|
||||
lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg);
|
||||
|
||||
/**
|
||||
* @brief Remove display handling from LVGL
|
||||
*
|
||||
* @note Free all memory used for this display.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t lvgl_port_remove_disp(lv_display_t *disp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port knob
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
#if __has_include ("iot_knob.h")
|
||||
#include "iot_knob.h"
|
||||
#include "iot_button.h"
|
||||
#define ESP_LVGL_PORT_KNOB_COMPONENT 1
|
||||
#endif
|
||||
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef ESP_LVGL_PORT_KNOB_COMPONENT
|
||||
/**
|
||||
* @brief Configuration of the encoder structure
|
||||
*/
|
||||
typedef struct {
|
||||
lv_display_t *disp; /*!< LVGL display handle (returned from lvgl_port_add_disp) */
|
||||
const knob_config_t *encoder_a_b;
|
||||
const button_config_t *encoder_enter; /*!< Navigation button for enter */
|
||||
} lvgl_port_encoder_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Add encoder as an input device
|
||||
*
|
||||
* @note Allocated memory in this function is not free in deinit. You must call lvgl_port_remove_encoder for free all memory!
|
||||
*
|
||||
* @param encoder_cfg Encoder configuration structure
|
||||
* @return Pointer to LVGL encoder input device or NULL when error occurred
|
||||
*/
|
||||
lv_indev_t *lvgl_port_add_encoder(const lvgl_port_encoder_cfg_t *encoder_cfg);
|
||||
|
||||
/**
|
||||
* @brief Remove encoder from input devices
|
||||
*
|
||||
* @note Free all memory used for this input device.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t lvgl_port_remove_encoder(lv_indev_t *encoder);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port touch
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
#if __has_include ("esp_lcd_touch.h")
|
||||
#include "esp_lcd_touch.h"
|
||||
#define ESP_LVGL_PORT_TOUCH_COMPONENT 1
|
||||
#endif
|
||||
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef ESP_LVGL_PORT_TOUCH_COMPONENT
|
||||
/**
|
||||
* @brief Configuration touch structure
|
||||
*/
|
||||
typedef struct {
|
||||
lv_display_t *disp; /*!< LVGL display handle (returned from lvgl_port_add_disp) */
|
||||
esp_lcd_touch_handle_t handle; /*!< LCD touch IO handle */
|
||||
} lvgl_port_touch_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Add LCD touch as an input device
|
||||
*
|
||||
* @note Allocated memory in this function is not free in deinit. You must call lvgl_port_remove_touch for free all memory!
|
||||
*
|
||||
* @param touch_cfg Touch configuration structure
|
||||
* @return Pointer to LVGL touch input device or NULL when error occurred
|
||||
*/
|
||||
lv_indev_t *lvgl_port_add_touch(const lvgl_port_touch_cfg_t *touch_cfg);
|
||||
|
||||
/**
|
||||
* @brief Remove selected LCD touch from input devices
|
||||
*
|
||||
* @note Free all memory used for this display.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t lvgl_port_remove_touch(lv_indev_t *touch);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port USB HID
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
#if __has_include ("usb/hid_host.h")
|
||||
#define ESP_LVGL_PORT_USB_HOST_HID_COMPONENT 1
|
||||
#endif
|
||||
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef ESP_LVGL_PORT_USB_HOST_HID_COMPONENT
|
||||
/**
|
||||
* @brief Configuration of the mouse input
|
||||
*/
|
||||
typedef struct {
|
||||
lv_display_t *disp; /*!< LVGL display handle (returned from lvgl_port_add_disp) */
|
||||
uint8_t sensitivity; /*!< Mouse sensitivity (cannot be zero) */
|
||||
lv_obj_t *cursor_img; /*!< Mouse cursor image, if NULL then used default */
|
||||
} lvgl_port_hid_mouse_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration of the keyboard input
|
||||
*/
|
||||
typedef struct {
|
||||
lv_display_t *disp; /*!< LVGL display handle (returned from lvgl_port_add_disp) */
|
||||
} lvgl_port_hid_keyboard_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Add USB HID mouse as an input device
|
||||
*
|
||||
* @note The USB host must be initialized before. Use `usb_host_install` for host initialization.
|
||||
*
|
||||
* @param mouse_cfg mouse configuration structure
|
||||
* @return Pointer to LVGL buttons input device or NULL when error occurred
|
||||
*/
|
||||
lv_indev_t *lvgl_port_add_usb_hid_mouse_input(const lvgl_port_hid_mouse_cfg_t *mouse_cfg);
|
||||
|
||||
/**
|
||||
* @brief Add USB HID keyboard as an input device
|
||||
*
|
||||
* @note The USB host must be initialized before. Use `usb_host_install` for host initialization.
|
||||
*
|
||||
* @param keyboard_cfg keyboard configuration structure
|
||||
* @return Pointer to LVGL buttons input device or NULL when error occurred
|
||||
*/
|
||||
lv_indev_t *lvgl_port_add_usb_hid_keyboard_input(const lvgl_port_hid_keyboard_cfg_t *keyboard_cfg);
|
||||
|
||||
/**
|
||||
* @brief Remove selected USB HID from input devices
|
||||
*
|
||||
* @note Free all memory used for this input device. When removed all HID devices, the HID task will be freed.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t lvgl_port_remove_usb_hid_input(lv_indev_t *hid);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct lvgl_port_ctx_s {
|
||||
SemaphoreHandle_t lvgl_mux;
|
||||
esp_timer_handle_t tick_timer;
|
||||
bool running;
|
||||
int task_max_sleep_ms;
|
||||
int timer_period_ms;
|
||||
} lvgl_port_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Local variables
|
||||
*******************************************************************************/
|
||||
static lvgl_port_ctx_t lvgl_port_ctx;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
static void lvgl_port_task(void *arg);
|
||||
static esp_err_t lvgl_port_tick_init(void);
|
||||
static void lvgl_port_task_deinit(void);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
esp_err_t lvgl_port_init(const lvgl_port_cfg_t *cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(cfg->task_affinity < (configNUM_CORES), ESP_ERR_INVALID_ARG, err, TAG, "Bad core number for task! Maximum core number is %d", (configNUM_CORES - 1));
|
||||
|
||||
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
||||
|
||||
/* LVGL init */
|
||||
lv_init();
|
||||
/* Tick init */
|
||||
lvgl_port_ctx.timer_period_ms = cfg->timer_period_ms;
|
||||
ESP_RETURN_ON_ERROR(lvgl_port_tick_init(), TAG, "");
|
||||
/* Create task */
|
||||
lvgl_port_ctx.task_max_sleep_ms = cfg->task_max_sleep_ms;
|
||||
if (lvgl_port_ctx.task_max_sleep_ms == 0) {
|
||||
lvgl_port_ctx.task_max_sleep_ms = 500;
|
||||
}
|
||||
lvgl_port_ctx.lvgl_mux = xSemaphoreCreateRecursiveMutex();
|
||||
ESP_GOTO_ON_FALSE(lvgl_port_ctx.lvgl_mux, ESP_ERR_NO_MEM, err, TAG, "Create LVGL mutex fail!");
|
||||
|
||||
BaseType_t res;
|
||||
if (cfg->task_affinity < 0) {
|
||||
res = xTaskCreate(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, NULL);
|
||||
} else {
|
||||
res = xTaskCreatePinnedToCore(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, NULL, cfg->task_affinity);
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(res == pdPASS, ESP_FAIL, err, TAG, "Create LVGL task fail!");
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
lvgl_port_deinit();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_resume(void)
|
||||
{
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
|
||||
if (lvgl_port_ctx.tick_timer != NULL) {
|
||||
lv_timer_enable(true);
|
||||
ret = esp_timer_start_periodic(lvgl_port_ctx.tick_timer, lvgl_port_ctx.timer_period_ms * 1000);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_stop(void)
|
||||
{
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
|
||||
if (lvgl_port_ctx.tick_timer != NULL) {
|
||||
lv_timer_enable(false);
|
||||
ret = esp_timer_stop(lvgl_port_ctx.tick_timer);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_deinit(void)
|
||||
{
|
||||
/* Stop and delete timer */
|
||||
if (lvgl_port_ctx.tick_timer != NULL) {
|
||||
esp_timer_stop(lvgl_port_ctx.tick_timer);
|
||||
esp_timer_delete(lvgl_port_ctx.tick_timer);
|
||||
lvgl_port_ctx.tick_timer = NULL;
|
||||
}
|
||||
|
||||
/* Stop running task */
|
||||
if (lvgl_port_ctx.running) {
|
||||
lvgl_port_ctx.running = false;
|
||||
} else {
|
||||
lvgl_port_task_deinit();
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool lvgl_port_lock(uint32_t timeout_ms)
|
||||
{
|
||||
assert(lvgl_port_ctx.lvgl_mux && "lvgl_port_init must be called first");
|
||||
|
||||
const TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
return xSemaphoreTakeRecursive(lvgl_port_ctx.lvgl_mux, timeout_ticks) == pdTRUE;
|
||||
}
|
||||
|
||||
void lvgl_port_unlock(void)
|
||||
{
|
||||
assert(lvgl_port_ctx.lvgl_mux && "lvgl_port_init must be called first");
|
||||
xSemaphoreGiveRecursive(lvgl_port_ctx.lvgl_mux);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_task(void *arg)
|
||||
{
|
||||
uint32_t task_delay_ms = lvgl_port_ctx.task_max_sleep_ms;
|
||||
|
||||
ESP_LOGI(TAG, "Starting LVGL task");
|
||||
lvgl_port_ctx.running = true;
|
||||
while (lvgl_port_ctx.running) {
|
||||
if (lvgl_port_lock(0)) {
|
||||
task_delay_ms = lv_timer_handler();
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
if ((task_delay_ms > lvgl_port_ctx.task_max_sleep_ms) || (1 == task_delay_ms)) {
|
||||
task_delay_ms = lvgl_port_ctx.task_max_sleep_ms;
|
||||
} else if (task_delay_ms < 1) {
|
||||
task_delay_ms = 1;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
|
||||
}
|
||||
|
||||
lvgl_port_task_deinit();
|
||||
|
||||
/* Close task */
|
||||
vTaskDelete( NULL );
|
||||
}
|
||||
|
||||
static void lvgl_port_task_deinit(void)
|
||||
{
|
||||
if (lvgl_port_ctx.lvgl_mux) {
|
||||
vSemaphoreDelete(lvgl_port_ctx.lvgl_mux);
|
||||
}
|
||||
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
||||
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
|
||||
/* Deinitialize LVGL */
|
||||
lv_deinit();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void lvgl_port_tick_increment(void *arg)
|
||||
{
|
||||
/* Tell LVGL how many milliseconds have elapsed */
|
||||
lv_tick_inc(lvgl_port_ctx.timer_period_ms);
|
||||
}
|
||||
|
||||
static esp_err_t lvgl_port_tick_init(void)
|
||||
{
|
||||
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
|
||||
const esp_timer_create_args_t lvgl_tick_timer_args = {
|
||||
.callback = &lvgl_port_tick_increment,
|
||||
.name = "LVGL tick",
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(esp_timer_create(&lvgl_tick_timer_args, &lvgl_port_ctx.tick_timer), TAG, "Creating LVGL timer filed!");
|
||||
return esp_timer_start_periodic(lvgl_port_ctx.tick_timer, lvgl_port_ctx.timer_period_ms * 1000);
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef enum {
|
||||
LVGL_PORT_NAV_BTN_PREV,
|
||||
LVGL_PORT_NAV_BTN_NEXT,
|
||||
LVGL_PORT_NAV_BTN_ENTER,
|
||||
LVGL_PORT_NAV_BTN_CNT,
|
||||
} lvgl_port_nav_btns_t;
|
||||
|
||||
typedef struct {
|
||||
button_handle_t btn[LVGL_PORT_NAV_BTN_CNT]; /* Button handlers */
|
||||
lv_indev_drv_t indev_drv; /* LVGL input device driver */
|
||||
bool btn_prev; /* Button prev state */
|
||||
bool btn_next; /* Button next state */
|
||||
bool btn_enter; /* Button enter state */
|
||||
} lvgl_port_nav_btns_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_navigation_buttons_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_btn_down_handler(void *arg, void *arg2);
|
||||
static void lvgl_port_btn_up_handler(void *arg, void *arg2);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_navigation_buttons(const lvgl_port_nav_btns_cfg_t *buttons_cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
lv_indev_t *indev = NULL;
|
||||
assert(buttons_cfg != NULL);
|
||||
assert(buttons_cfg->disp != NULL);
|
||||
|
||||
/* Touch context */
|
||||
lvgl_port_nav_btns_ctx_t *buttons_ctx = malloc(sizeof(lvgl_port_nav_btns_ctx_t));
|
||||
if (buttons_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for buttons context allocation!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Previous button */
|
||||
if (buttons_cfg->button_prev != NULL) {
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_PREV] = iot_button_create(buttons_cfg->button_prev);
|
||||
ESP_GOTO_ON_FALSE(buttons_ctx->btn[LVGL_PORT_NAV_BTN_PREV], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
|
||||
/* Next button */
|
||||
if (buttons_cfg->button_next != NULL) {
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_NEXT] = iot_button_create(buttons_cfg->button_next);
|
||||
ESP_GOTO_ON_FALSE(buttons_ctx->btn[LVGL_PORT_NAV_BTN_NEXT], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
|
||||
/* Enter button */
|
||||
if (buttons_cfg->button_enter != NULL) {
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_ENTER] = iot_button_create(buttons_cfg->button_enter);
|
||||
ESP_GOTO_ON_FALSE(buttons_ctx->btn[LVGL_PORT_NAV_BTN_ENTER], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
|
||||
/* Button handlers */
|
||||
for (int i = 0; i < LVGL_PORT_NAV_BTN_CNT; i++) {
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(buttons_ctx->btn[i], BUTTON_PRESS_DOWN, lvgl_port_btn_down_handler, buttons_ctx));
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(buttons_ctx->btn[i], BUTTON_PRESS_UP, lvgl_port_btn_up_handler, buttons_ctx));
|
||||
}
|
||||
|
||||
buttons_ctx->btn_prev = false;
|
||||
buttons_ctx->btn_next = false;
|
||||
buttons_ctx->btn_enter = false;
|
||||
|
||||
/* Register a touchpad input device */
|
||||
lv_indev_drv_init(&buttons_ctx->indev_drv);
|
||||
buttons_ctx->indev_drv.type = LV_INDEV_TYPE_ENCODER;
|
||||
buttons_ctx->indev_drv.disp = buttons_cfg->disp;
|
||||
buttons_ctx->indev_drv.read_cb = lvgl_port_navigation_buttons_read;
|
||||
buttons_ctx->indev_drv.user_data = buttons_ctx;
|
||||
buttons_ctx->indev_drv.long_press_repeat_time = 300;
|
||||
indev = lv_indev_drv_register(&buttons_ctx->indev_drv);
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
for (int i = 0; i < LVGL_PORT_NAV_BTN_CNT; i++) {
|
||||
if (buttons_ctx->btn[i] != NULL) {
|
||||
iot_button_delete(buttons_ctx->btn[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (buttons_ctx != NULL) {
|
||||
free(buttons_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_navigation_buttons(lv_indev_t *buttons)
|
||||
{
|
||||
assert(buttons);
|
||||
lv_indev_drv_t *indev_drv = buttons->driver;
|
||||
assert(indev_drv);
|
||||
lvgl_port_nav_btns_ctx_t *buttons_ctx = (lvgl_port_nav_btns_ctx_t *)indev_drv->user_data;
|
||||
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(buttons);
|
||||
|
||||
if (buttons_ctx) {
|
||||
free(buttons_ctx);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_navigation_buttons_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
static uint32_t last_key = 0;
|
||||
assert(indev_drv);
|
||||
lvgl_port_nav_btns_ctx_t *ctx = (lvgl_port_nav_btns_ctx_t *)indev_drv->user_data;
|
||||
assert(ctx);
|
||||
|
||||
/* Buttons */
|
||||
if (ctx->btn_prev) {
|
||||
data->key = LV_KEY_LEFT;
|
||||
last_key = LV_KEY_LEFT;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else if (ctx->btn_next) {
|
||||
data->key = LV_KEY_RIGHT;
|
||||
last_key = LV_KEY_RIGHT;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else if (ctx->btn_enter) {
|
||||
data->key = LV_KEY_ENTER;
|
||||
last_key = LV_KEY_ENTER;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->key = last_key;
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_btn_down_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_nav_btns_ctx_t *ctx = (lvgl_port_nav_btns_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* PREV */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_PREV]) {
|
||||
ctx->btn_prev = true;
|
||||
}
|
||||
/* NEXT */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_NEXT]) {
|
||||
ctx->btn_next = true;
|
||||
}
|
||||
/* ENTER */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_ENTER]) {
|
||||
ctx->btn_enter = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_btn_up_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_nav_btns_ctx_t *ctx = (lvgl_port_nav_btns_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* PREV */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_PREV]) {
|
||||
ctx->btn_prev = false;
|
||||
}
|
||||
/* NEXT */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_NEXT]) {
|
||||
ctx->btn_next = false;
|
||||
}
|
||||
/* ENTER */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_ENTER]) {
|
||||
ctx->btn_enter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 4)) || (ESP_IDF_VERSION == ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||
#define LVGL_PORT_HANDLE_FLUSH_READY 0
|
||||
#else
|
||||
#define LVGL_PORT_HANDLE_FLUSH_READY 1
|
||||
#endif
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_handle_t io_handle; /* LCD panel IO handle */
|
||||
esp_lcd_panel_handle_t panel_handle; /* LCD panel handle */
|
||||
lvgl_port_rotation_cfg_t rotation; /* Default values of the screen rotation */
|
||||
lv_disp_drv_t disp_drv; /* LVGL display driver */
|
||||
lv_color_t *trans_buf; /* Buffer send to driver */
|
||||
uint32_t trans_size; /* Maximum size for one transport */
|
||||
SemaphoreHandle_t trans_sem; /* Idle transfer mutex */
|
||||
} lvgl_port_display_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||
static bool lvgl_port_flush_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx);
|
||||
#endif
|
||||
static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map);
|
||||
static void lvgl_port_update_callback(lv_disp_drv_t *drv);
|
||||
static void lvgl_port_pix_monochrome_callback(lv_disp_drv_t *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);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_disp_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
lv_disp_t *disp = NULL;
|
||||
lv_color_t *buf1 = NULL;
|
||||
lv_color_t *buf2 = NULL;
|
||||
lv_color_t *buf3 = NULL;
|
||||
SemaphoreHandle_t trans_sem = NULL;
|
||||
assert(disp_cfg != NULL);
|
||||
assert(disp_cfg->io_handle != NULL);
|
||||
assert(disp_cfg->panel_handle != NULL);
|
||||
assert(disp_cfg->buffer_size > 0);
|
||||
assert(disp_cfg->hres > 0);
|
||||
assert(disp_cfg->vres > 0);
|
||||
|
||||
/* Display context */
|
||||
lvgl_port_display_ctx_t *disp_ctx = malloc(sizeof(lvgl_port_display_ctx_t));
|
||||
ESP_GOTO_ON_FALSE(disp_ctx, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for display context allocation!");
|
||||
disp_ctx->io_handle = disp_cfg->io_handle;
|
||||
disp_ctx->panel_handle = disp_cfg->panel_handle;
|
||||
disp_ctx->rotation.swap_xy = disp_cfg->rotation.swap_xy;
|
||||
disp_ctx->rotation.mirror_x = disp_cfg->rotation.mirror_x;
|
||||
disp_ctx->rotation.mirror_y = disp_cfg->rotation.mirror_y;
|
||||
disp_ctx->trans_size = disp_cfg->trans_size;
|
||||
|
||||
uint32_t buff_caps = MALLOC_CAP_DEFAULT;
|
||||
if (disp_cfg->flags.buff_dma && disp_cfg->flags.buff_spiram && (0 == disp_cfg->trans_size)) {
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "Alloc DMA capable buffer in SPIRAM is not supported!");
|
||||
} else if (disp_cfg->flags.buff_dma) {
|
||||
buff_caps = MALLOC_CAP_DMA;
|
||||
} else if (disp_cfg->flags.buff_spiram) {
|
||||
buff_caps = MALLOC_CAP_SPIRAM;
|
||||
}
|
||||
|
||||
if (disp_cfg->trans_size) {
|
||||
buf3 = heap_caps_malloc(disp_cfg->trans_size * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
ESP_GOTO_ON_FALSE(buf3, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for buffer(transport) allocation!");
|
||||
disp_ctx->trans_buf = buf3;
|
||||
|
||||
trans_sem = xSemaphoreCreateCounting(1, 0);
|
||||
ESP_GOTO_ON_FALSE(trans_sem, ESP_ERR_NO_MEM, err, TAG, "Failed to create transport counting Semaphore");
|
||||
disp_ctx->trans_sem = trans_sem;
|
||||
}
|
||||
|
||||
/* alloc draw buffers used by LVGL */
|
||||
/* it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized */
|
||||
buf1 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color_t), buff_caps);
|
||||
ESP_GOTO_ON_FALSE(buf1, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf1) allocation!");
|
||||
if (disp_cfg->double_buffer) {
|
||||
buf2 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color_t), buff_caps);
|
||||
ESP_GOTO_ON_FALSE(buf2, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf2) allocation!");
|
||||
}
|
||||
lv_disp_draw_buf_t *disp_buf = malloc(sizeof(lv_disp_draw_buf_t));
|
||||
ESP_GOTO_ON_FALSE(disp_buf, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL display buffer allocation!");
|
||||
|
||||
/* initialize LVGL draw buffers */
|
||||
lv_disp_draw_buf_init(disp_buf, buf1, buf2, disp_cfg->buffer_size);
|
||||
|
||||
ESP_LOGD(TAG, "Register display driver to LVGL");
|
||||
lv_disp_drv_init(&disp_ctx->disp_drv);
|
||||
disp_ctx->disp_drv.hor_res = disp_cfg->hres;
|
||||
disp_ctx->disp_drv.ver_res = disp_cfg->vres;
|
||||
disp_ctx->disp_drv.flush_cb = lvgl_port_flush_callback;
|
||||
disp_ctx->disp_drv.draw_buf = disp_buf;
|
||||
disp_ctx->disp_drv.user_data = disp_ctx;
|
||||
|
||||
disp_ctx->disp_drv.sw_rotate = disp_cfg->flags.sw_rotate;
|
||||
if (disp_ctx->disp_drv.sw_rotate == false) {
|
||||
disp_ctx->disp_drv.drv_update_cb = lvgl_port_update_callback;
|
||||
}
|
||||
|
||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||
/* Register done callback */
|
||||
const esp_lcd_panel_io_callbacks_t cbs = {
|
||||
.on_color_trans_done = lvgl_port_flush_ready_callback,
|
||||
};
|
||||
esp_lcd_panel_io_register_event_callbacks(disp_ctx->io_handle, &cbs, &disp_ctx->disp_drv);
|
||||
#endif
|
||||
|
||||
/* Monochrome display settings */
|
||||
if (disp_cfg->monochrome) {
|
||||
/* When using monochromatic display, there must be used full bufer! */
|
||||
ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == disp_cfg->buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Monochromatic display must using full buffer!");
|
||||
|
||||
disp_ctx->disp_drv.full_refresh = 1;
|
||||
disp_ctx->disp_drv.set_px_cb = lvgl_port_pix_monochrome_callback;
|
||||
}
|
||||
|
||||
disp = lv_disp_drv_register(&disp_ctx->disp_drv);
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
if (buf1) {
|
||||
free(buf1);
|
||||
}
|
||||
if (buf2) {
|
||||
free(buf2);
|
||||
}
|
||||
if (buf3) {
|
||||
free(buf3);
|
||||
}
|
||||
if (trans_sem) {
|
||||
vSemaphoreDelete(trans_sem);
|
||||
}
|
||||
if (disp_ctx) {
|
||||
free(disp_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return disp;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_disp(lv_disp_t *disp)
|
||||
{
|
||||
assert(disp);
|
||||
lv_disp_drv_t *disp_drv = disp->driver;
|
||||
assert(disp_drv);
|
||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)disp_drv->user_data;
|
||||
|
||||
lv_disp_remove(disp);
|
||||
|
||||
if (disp_drv) {
|
||||
if (disp_drv->draw_buf && disp_drv->draw_buf->buf1) {
|
||||
free(disp_drv->draw_buf->buf1);
|
||||
disp_drv->draw_buf->buf1 = NULL;
|
||||
}
|
||||
if (disp_drv->draw_buf && disp_drv->draw_buf->buf2) {
|
||||
free(disp_drv->draw_buf->buf2);
|
||||
disp_drv->draw_buf->buf2 = NULL;
|
||||
}
|
||||
if (disp_drv->draw_buf) {
|
||||
free(disp_drv->draw_buf);
|
||||
disp_drv->draw_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
free(disp_ctx);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void lvgl_port_flush_ready(lv_disp_t *disp)
|
||||
{
|
||||
assert(disp);
|
||||
assert(disp->driver);
|
||||
lv_disp_flush_ready(disp->driver);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||
static bool lvgl_port_flush_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
BaseType_t taskAwake = pdFALSE;
|
||||
|
||||
lv_disp_drv_t *disp_drv = (lv_disp_drv_t *)user_ctx;
|
||||
assert(disp_drv != NULL);
|
||||
lvgl_port_display_ctx_t *disp_ctx = disp_drv->user_data;
|
||||
assert(disp_ctx != NULL);
|
||||
lv_disp_flush_ready(disp_drv);
|
||||
|
||||
if (disp_ctx->trans_size && disp_ctx->trans_sem) {
|
||||
xSemaphoreGiveFromISR(disp_ctx->trans_sem, &taskAwake);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
|
||||
{
|
||||
assert(drv != NULL);
|
||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)drv->user_data;
|
||||
assert(disp_ctx != NULL);
|
||||
|
||||
int x_draw_start;
|
||||
int x_draw_end;
|
||||
int y_draw_start;
|
||||
int y_draw_end;
|
||||
|
||||
int y_start_tmp;
|
||||
int y_end_tmp;
|
||||
|
||||
int trans_count;
|
||||
int trans_line;
|
||||
int max_line;
|
||||
|
||||
const int x_start = area->x1;
|
||||
const int x_end = area->x2;
|
||||
const int y_start = area->y1;
|
||||
const int y_end = area->y2;
|
||||
const int width = x_end - x_start + 1;
|
||||
const int height = y_end - y_start + 1;
|
||||
|
||||
lv_color_t *from = color_map;
|
||||
lv_color_t *to = NULL;
|
||||
|
||||
if (0 == disp_ctx->trans_size) {
|
||||
esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, x_start, y_start, x_end + 1, y_end + 1, color_map);
|
||||
} else {
|
||||
y_start_tmp = y_start;
|
||||
max_line = ((disp_ctx->trans_size / width) > height) ? (height) : (disp_ctx->trans_size / width);
|
||||
trans_count = height / max_line + (height % max_line ? (1) : (0));
|
||||
|
||||
for (int i = 0; i < trans_count; i++) {
|
||||
trans_line = (y_end - y_start_tmp + 1) > max_line ? max_line : (y_end - y_start_tmp + 1);
|
||||
y_end_tmp = (y_end - y_start_tmp + 1) > max_line ? (y_start_tmp + max_line - 1) : y_end;
|
||||
|
||||
to = disp_ctx->trans_buf;
|
||||
for (int y = 0; y < trans_line; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
*(to + y * (width) + x) = *(from + y * (width) + x);
|
||||
}
|
||||
}
|
||||
x_draw_start = x_start;
|
||||
x_draw_end = x_end;
|
||||
y_draw_start = y_start_tmp;
|
||||
y_draw_end = y_end_tmp;
|
||||
esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, x_draw_start, y_draw_start, x_draw_end + 1, y_draw_end + 1, to);
|
||||
|
||||
from += max_line * width;
|
||||
y_start_tmp += max_line;
|
||||
xSemaphoreTake(disp_ctx->trans_sem, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_update_callback(lv_disp_drv_t *drv)
|
||||
{
|
||||
assert(drv);
|
||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)drv->user_data;
|
||||
assert(disp_ctx != NULL);
|
||||
esp_lcd_panel_handle_t panel_handle = disp_ctx->panel_handle;
|
||||
|
||||
/* Solve rotation screen and touch */
|
||||
switch (drv->rotated) {
|
||||
case LV_DISP_ROT_NONE:
|
||||
/* Rotate LCD display */
|
||||
esp_lcd_panel_swap_xy(panel_handle, disp_ctx->rotation.swap_xy);
|
||||
esp_lcd_panel_mirror(panel_handle, disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
||||
break;
|
||||
case LV_DISP_ROT_90:
|
||||
/* Rotate LCD display */
|
||||
esp_lcd_panel_swap_xy(panel_handle, !disp_ctx->rotation.swap_xy);
|
||||
if (disp_ctx->rotation.swap_xy) {
|
||||
esp_lcd_panel_mirror(panel_handle, !disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
||||
} else {
|
||||
esp_lcd_panel_mirror(panel_handle, disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
||||
}
|
||||
break;
|
||||
case LV_DISP_ROT_180:
|
||||
/* Rotate LCD display */
|
||||
esp_lcd_panel_swap_xy(panel_handle, disp_ctx->rotation.swap_xy);
|
||||
esp_lcd_panel_mirror(panel_handle, !disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
||||
break;
|
||||
case LV_DISP_ROT_270:
|
||||
/* Rotate LCD display */
|
||||
esp_lcd_panel_swap_xy(panel_handle, !disp_ctx->rotation.swap_xy);
|
||||
if (disp_ctx->rotation.swap_xy) {
|
||||
esp_lcd_panel_mirror(panel_handle, disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
||||
} else {
|
||||
esp_lcd_panel_mirror(panel_handle, !disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_pix_monochrome_callback(lv_disp_drv_t *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)
|
||||
{
|
||||
if (drv->rotated == LV_DISP_ROT_90 || drv->rotated == LV_DISP_ROT_270) {
|
||||
lv_coord_t tmp_x = x;
|
||||
x = y;
|
||||
y = tmp_x;
|
||||
}
|
||||
|
||||
/* Write to the buffer as required for the display.
|
||||
* It writes only 1-bit for monochrome displays mapped vertically.*/
|
||||
buf += drv->hor_res * (y >> 3) + x;
|
||||
if (lv_color_to1(color)) {
|
||||
(*buf) &= ~(1 << (y % 8));
|
||||
} else {
|
||||
(*buf) |= (1 << (y % 8));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
knob_handle_t knob_handle; /* Encoder knob handlers */
|
||||
button_handle_t btn_handle; /* Encoder button handlers */
|
||||
lv_indev_drv_t indev_drv; /* LVGL input device driver */
|
||||
bool btn_enter; /* Encoder button enter state */
|
||||
} lvgl_port_encoder_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_encoder_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_encoder_btn_down_handler(void *arg, void *arg2);
|
||||
static void lvgl_port_encoder_btn_up_handler(void *arg, void *arg2);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_encoder(const lvgl_port_encoder_cfg_t *encoder_cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
lv_indev_t *indev = NULL;
|
||||
assert(encoder_cfg != NULL);
|
||||
assert(encoder_cfg->disp != NULL);
|
||||
|
||||
/* Encoder context */
|
||||
lvgl_port_encoder_ctx_t *encoder_ctx = malloc(sizeof(lvgl_port_encoder_ctx_t));
|
||||
if (encoder_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for encoder context allocation!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Encoder_a/b */
|
||||
if (encoder_cfg->encoder_a_b != NULL) {
|
||||
encoder_ctx->knob_handle = iot_knob_create(encoder_cfg->encoder_a_b);
|
||||
ESP_GOTO_ON_FALSE(encoder_ctx->knob_handle, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for knob create!");
|
||||
}
|
||||
|
||||
/* Encoder Enter */
|
||||
if (encoder_cfg->encoder_enter != NULL) {
|
||||
encoder_ctx->btn_handle = iot_button_create(encoder_cfg->encoder_enter);
|
||||
ESP_GOTO_ON_FALSE(encoder_ctx->btn_handle, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(encoder_ctx->btn_handle, BUTTON_PRESS_DOWN, lvgl_port_encoder_btn_down_handler, encoder_ctx));
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(encoder_ctx->btn_handle, BUTTON_PRESS_UP, lvgl_port_encoder_btn_up_handler, encoder_ctx));
|
||||
|
||||
encoder_ctx->btn_enter = false;
|
||||
|
||||
/* Register a encoder input device */
|
||||
lv_indev_drv_init(&encoder_ctx->indev_drv);
|
||||
encoder_ctx->indev_drv.type = LV_INDEV_TYPE_ENCODER;
|
||||
encoder_ctx->indev_drv.disp = encoder_cfg->disp;
|
||||
encoder_ctx->indev_drv.read_cb = lvgl_port_encoder_read;
|
||||
encoder_ctx->indev_drv.user_data = encoder_ctx;
|
||||
indev = lv_indev_drv_register(&encoder_ctx->indev_drv);
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
if (encoder_ctx->knob_handle != NULL) {
|
||||
iot_knob_delete(encoder_ctx->knob_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx->btn_handle != NULL) {
|
||||
iot_button_delete(encoder_ctx->btn_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx != NULL) {
|
||||
free(encoder_ctx);
|
||||
}
|
||||
}
|
||||
return indev;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_encoder(lv_indev_t *encoder)
|
||||
{
|
||||
assert(encoder);
|
||||
lv_indev_drv_t *indev_drv = encoder->driver;
|
||||
assert(indev_drv);
|
||||
lvgl_port_encoder_ctx_t *encoder_ctx = (lvgl_port_encoder_ctx_t *)indev_drv->user_data;
|
||||
|
||||
if (encoder_ctx->knob_handle != NULL) {
|
||||
iot_knob_delete(encoder_ctx->knob_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx->btn_handle != NULL) {
|
||||
iot_button_delete(encoder_ctx->btn_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx != NULL) {
|
||||
free(encoder_ctx);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_encoder_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
static int32_t last_v = 0;
|
||||
|
||||
assert(indev_drv);
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *)indev_drv->user_data;
|
||||
assert(ctx);
|
||||
|
||||
int32_t invd = iot_knob_get_count_value(ctx->knob_handle);
|
||||
knob_event_t event = iot_knob_get_event(ctx->knob_handle);
|
||||
|
||||
if (last_v ^ invd) {
|
||||
last_v = invd;
|
||||
data->enc_diff = (KNOB_LEFT == event) ? (-1) : ((KNOB_RIGHT == event) ? (1) : (0));
|
||||
} else {
|
||||
data->enc_diff = 0;
|
||||
}
|
||||
data->state = (true == ctx->btn_enter) ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
|
||||
static void lvgl_port_encoder_btn_down_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* ENTER */
|
||||
if (button == ctx->btn_handle) {
|
||||
ctx->btn_enter = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_encoder_btn_up_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* ENTER */
|
||||
if (button == ctx->btn_handle) {
|
||||
ctx->btn_enter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_touch.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_touch_handle_t handle; /* LCD touch IO handle */
|
||||
lv_indev_drv_t indev_drv; /* LVGL input device driver */
|
||||
} lvgl_port_touch_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_touch(const lvgl_port_touch_cfg_t *touch_cfg)
|
||||
{
|
||||
assert(touch_cfg != NULL);
|
||||
assert(touch_cfg->disp != NULL);
|
||||
assert(touch_cfg->handle != NULL);
|
||||
|
||||
/* Touch context */
|
||||
lvgl_port_touch_ctx_t *touch_ctx = malloc(sizeof(lvgl_port_touch_ctx_t));
|
||||
if (touch_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for touch context allocation!");
|
||||
return NULL;
|
||||
}
|
||||
touch_ctx->handle = touch_cfg->handle;
|
||||
|
||||
/* Register a touchpad input device */
|
||||
lv_indev_drv_init(&touch_ctx->indev_drv);
|
||||
touch_ctx->indev_drv.type = LV_INDEV_TYPE_POINTER;
|
||||
touch_ctx->indev_drv.disp = touch_cfg->disp;
|
||||
touch_ctx->indev_drv.read_cb = lvgl_port_touchpad_read;
|
||||
touch_ctx->indev_drv.user_data = touch_ctx;
|
||||
return lv_indev_drv_register(&touch_ctx->indev_drv);
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_touch(lv_indev_t *touch)
|
||||
{
|
||||
assert(touch);
|
||||
lv_indev_drv_t *indev_drv = touch->driver;
|
||||
assert(indev_drv);
|
||||
lvgl_port_touch_ctx_t *touch_ctx = (lvgl_port_touch_ctx_t *)indev_drv->user_data;
|
||||
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(touch);
|
||||
|
||||
if (touch_ctx) {
|
||||
free(touch_ctx);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
assert(indev_drv);
|
||||
lvgl_port_touch_ctx_t *touch_ctx = (lvgl_port_touch_ctx_t *)indev_drv->user_data;
|
||||
assert(touch_ctx->handle);
|
||||
|
||||
uint16_t touchpad_x[1] = {0};
|
||||
uint16_t touchpad_y[1] = {0};
|
||||
uint8_t touchpad_cnt = 0;
|
||||
|
||||
/* Read data from touch controller into memory */
|
||||
esp_lcd_touch_read_data(touch_ctx->handle);
|
||||
|
||||
/* Read data from touch controller */
|
||||
bool touchpad_pressed = esp_lcd_touch_get_coordinates(touch_ctx->handle, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);
|
||||
|
||||
if (touchpad_pressed && touchpad_cnt > 0) {
|
||||
data->point.x = touchpad_x[0];
|
||||
data->point.y = touchpad_y[0];
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
#include "usb/hid_host.h"
|
||||
#include "usb/hid_usage_keyboard.h"
|
||||
#include "usb/hid_usage_mouse.h"
|
||||
|
||||
/* LVGL image of cursor */
|
||||
LV_IMG_DECLARE(img_cursor)
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
QueueHandle_t queue; /* USB HID queue */
|
||||
TaskHandle_t task; /* USB HID task */
|
||||
bool running; /* USB HID task running */
|
||||
struct {
|
||||
lv_indev_drv_t drv; /* LVGL mouse input device driver */
|
||||
uint8_t sensitivity; /* Mouse sensitivity (cannot be zero) */
|
||||
int16_t x; /* Mouse X coordinate */
|
||||
int16_t y; /* Mouse Y coordinate */
|
||||
bool left_button; /* Mouse left button state */
|
||||
} mouse;
|
||||
struct {
|
||||
lv_indev_drv_t drv; /* LVGL keyboard input device driver */
|
||||
uint32_t last_key;
|
||||
bool pressed;
|
||||
} kb;
|
||||
} lvgl_port_usb_hid_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
hid_host_device_handle_t hid_device_handle;
|
||||
hid_host_driver_event_t event;
|
||||
void *arg;
|
||||
} lvgl_port_usb_hid_event_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Local variables
|
||||
*******************************************************************************/
|
||||
|
||||
static lvgl_port_usb_hid_ctx_t lvgl_hid_ctx;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static lvgl_port_usb_hid_ctx_t *lvgl_port_hid_init(void);
|
||||
static void lvgl_port_usb_hid_task(void *arg);
|
||||
static void lvgl_port_usb_hid_read_mouse(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_usb_hid_read_kb(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_usb_hid_callback(hid_host_device_handle_t hid_device_handle, const hid_host_driver_event_t event, void *arg);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_usb_hid_mouse_input(const lvgl_port_hid_mouse_cfg_t *mouse_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
assert(mouse_cfg);
|
||||
assert(mouse_cfg->disp);
|
||||
assert(mouse_cfg->disp->driver);
|
||||
|
||||
/* Initialize USB HID */
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = lvgl_port_hid_init();
|
||||
if (hid_ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Mouse sensitivity cannot be zero */
|
||||
hid_ctx->mouse.sensitivity = (mouse_cfg->sensitivity == 0 ? 1 : mouse_cfg->sensitivity);
|
||||
/* Default coordinates to screen center */
|
||||
hid_ctx->mouse.x = (mouse_cfg->disp->driver->hor_res * hid_ctx->mouse.sensitivity) / 2;
|
||||
hid_ctx->mouse.y = (mouse_cfg->disp->driver->ver_res * hid_ctx->mouse.sensitivity) / 2;
|
||||
|
||||
/* Register a mouse input device */
|
||||
lv_indev_drv_init(&hid_ctx->mouse.drv);
|
||||
hid_ctx->mouse.drv.type = LV_INDEV_TYPE_POINTER;
|
||||
hid_ctx->mouse.drv.disp = mouse_cfg->disp;
|
||||
hid_ctx->mouse.drv.read_cb = lvgl_port_usb_hid_read_mouse;
|
||||
hid_ctx->mouse.drv.user_data = hid_ctx;
|
||||
indev = lv_indev_drv_register(&hid_ctx->mouse.drv);
|
||||
|
||||
/* Set image of cursor */
|
||||
lv_obj_t *cursor = mouse_cfg->cursor_img;
|
||||
if (cursor == NULL) {
|
||||
cursor = lv_img_create(lv_scr_act());
|
||||
lv_img_set_src(cursor, &img_cursor);
|
||||
}
|
||||
lv_indev_set_cursor(indev, cursor);
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
lv_indev_t *lvgl_port_add_usb_hid_keyboard_input(const lvgl_port_hid_keyboard_cfg_t *keyboard_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
assert(keyboard_cfg);
|
||||
assert(keyboard_cfg->disp);
|
||||
|
||||
/* Initialize USB HID */
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = lvgl_port_hid_init();
|
||||
if (hid_ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Register a keyboard input device */
|
||||
lv_indev_drv_init(&hid_ctx->kb.drv);
|
||||
hid_ctx->kb.drv.type = LV_INDEV_TYPE_KEYPAD;
|
||||
hid_ctx->kb.drv.disp = keyboard_cfg->disp;
|
||||
hid_ctx->kb.drv.read_cb = lvgl_port_usb_hid_read_kb;
|
||||
hid_ctx->kb.drv.user_data = hid_ctx;
|
||||
indev = lv_indev_drv_register(&hid_ctx->kb.drv);
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_usb_hid_input(lv_indev_t *hid)
|
||||
{
|
||||
assert(hid);
|
||||
lv_indev_drv_t *indev_drv = hid->driver;
|
||||
assert(indev_drv);
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = (lvgl_port_usb_hid_ctx_t *)indev_drv->user_data;
|
||||
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(hid);
|
||||
|
||||
/* If all hid input devices are removed, stop task and clean all */
|
||||
if (lvgl_hid_ctx.mouse.drv.disp == NULL && lvgl_hid_ctx.kb.drv.disp) {
|
||||
hid_ctx->running = false;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static lvgl_port_usb_hid_ctx_t *lvgl_port_hid_init(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
/* USB HID is already initialized */
|
||||
if (lvgl_hid_ctx.task) {
|
||||
return &lvgl_hid_ctx;
|
||||
}
|
||||
|
||||
/* USB HID host driver config */
|
||||
const hid_host_driver_config_t hid_host_driver_config = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 5,
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = lvgl_port_usb_hid_callback,
|
||||
.callback_arg = &lvgl_hid_ctx,
|
||||
};
|
||||
|
||||
ret = hid_host_install(&hid_host_driver_config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "USB HID install failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lvgl_hid_ctx.queue = xQueueCreate(10, sizeof(lvgl_port_usb_hid_event_t));
|
||||
xTaskCreate(&lvgl_port_usb_hid_task, "hid_task", 4 * 1024, &lvgl_hid_ctx, 2, &lvgl_hid_ctx.task);
|
||||
|
||||
return &lvgl_hid_ctx;
|
||||
}
|
||||
|
||||
static char usb_hid_get_keyboard_char(uint8_t key, uint8_t shift)
|
||||
{
|
||||
char ret_key = 0;
|
||||
|
||||
const uint8_t keycode2ascii [57][2] = {
|
||||
{0, 0}, /* HID_KEY_NO_PRESS */
|
||||
{0, 0}, /* HID_KEY_ROLLOVER */
|
||||
{0, 0}, /* HID_KEY_POST_FAIL */
|
||||
{0, 0}, /* HID_KEY_ERROR_UNDEFINED */
|
||||
{'a', 'A'}, /* HID_KEY_A */
|
||||
{'b', 'B'}, /* HID_KEY_B */
|
||||
{'c', 'C'}, /* HID_KEY_C */
|
||||
{'d', 'D'}, /* HID_KEY_D */
|
||||
{'e', 'E'}, /* HID_KEY_E */
|
||||
{'f', 'F'}, /* HID_KEY_F */
|
||||
{'g', 'G'}, /* HID_KEY_G */
|
||||
{'h', 'H'}, /* HID_KEY_H */
|
||||
{'i', 'I'}, /* HID_KEY_I */
|
||||
{'j', 'J'}, /* HID_KEY_J */
|
||||
{'k', 'K'}, /* HID_KEY_K */
|
||||
{'l', 'L'}, /* HID_KEY_L */
|
||||
{'m', 'M'}, /* HID_KEY_M */
|
||||
{'n', 'N'}, /* HID_KEY_N */
|
||||
{'o', 'O'}, /* HID_KEY_O */
|
||||
{'p', 'P'}, /* HID_KEY_P */
|
||||
{'q', 'Q'}, /* HID_KEY_Q */
|
||||
{'r', 'R'}, /* HID_KEY_R */
|
||||
{'s', 'S'}, /* HID_KEY_S */
|
||||
{'t', 'T'}, /* HID_KEY_T */
|
||||
{'u', 'U'}, /* HID_KEY_U */
|
||||
{'v', 'V'}, /* HID_KEY_V */
|
||||
{'w', 'W'}, /* HID_KEY_W */
|
||||
{'x', 'X'}, /* HID_KEY_X */
|
||||
{'y', 'Y'}, /* HID_KEY_Y */
|
||||
{'z', 'Z'}, /* HID_KEY_Z */
|
||||
{'1', '!'}, /* HID_KEY_1 */
|
||||
{'2', '@'}, /* HID_KEY_2 */
|
||||
{'3', '#'}, /* HID_KEY_3 */
|
||||
{'4', '$'}, /* HID_KEY_4 */
|
||||
{'5', '%'}, /* HID_KEY_5 */
|
||||
{'6', '^'}, /* HID_KEY_6 */
|
||||
{'7', '&'}, /* HID_KEY_7 */
|
||||
{'8', '*'}, /* HID_KEY_8 */
|
||||
{'9', '('}, /* HID_KEY_9 */
|
||||
{'0', ')'}, /* HID_KEY_0 */
|
||||
{'\r', '\r'}, /* HID_KEY_ENTER */
|
||||
{0, 0}, /* HID_KEY_ESC */
|
||||
{'\b', 0}, /* HID_KEY_DEL */
|
||||
{0, 0}, /* HID_KEY_TAB */
|
||||
{' ', ' '}, /* HID_KEY_SPACE */
|
||||
{'-', '_'}, /* HID_KEY_MINUS */
|
||||
{'=', '+'}, /* HID_KEY_EQUAL */
|
||||
{'[', '{'}, /* HID_KEY_OPEN_BRACKET */
|
||||
{']', '}'}, /* HID_KEY_CLOSE_BRACKET */
|
||||
{'\\', '|'}, /* HID_KEY_BACK_SLASH */
|
||||
{'\\', '|'}, /* HID_KEY_SHARP */ // HOTFIX: for NonUS Keyboards repeat HID_KEY_BACK_SLASH
|
||||
{';', ':'}, /* HID_KEY_COLON */
|
||||
{'\'', '"'}, /* HID_KEY_QUOTE */
|
||||
{'`', '~'}, /* HID_KEY_TILDE */
|
||||
{',', '<'}, /* HID_KEY_LESS */
|
||||
{'.', '>'}, /* HID_KEY_GREATER */
|
||||
{'/', '?'} /* HID_KEY_SLASH */
|
||||
};
|
||||
|
||||
if (shift > 1) {
|
||||
shift = 1;
|
||||
}
|
||||
|
||||
if ((key >= HID_KEY_A) && (key <= HID_KEY_SLASH)) {
|
||||
ret_key = keycode2ascii[key][shift];
|
||||
}
|
||||
|
||||
return ret_key;
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_host_interface_callback(hid_host_device_handle_t hid_device_handle, const hid_host_interface_event_t event, void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev;
|
||||
hid_host_device_get_params(hid_device_handle, &dev);
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = (lvgl_port_usb_hid_ctx_t *)arg;
|
||||
uint8_t data[10];
|
||||
unsigned int data_length = 0;
|
||||
|
||||
assert(hid_ctx != NULL);
|
||||
|
||||
switch (event) {
|
||||
case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
|
||||
hid_host_device_get_raw_input_report_data(hid_device_handle, data, sizeof(data), &data_length);
|
||||
if (dev.proto == HID_PROTOCOL_KEYBOARD) {
|
||||
hid_keyboard_input_report_boot_t *keyboard = (hid_keyboard_input_report_boot_t *)data;
|
||||
if (data_length < sizeof(hid_keyboard_input_report_boot_t)) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) {
|
||||
if (keyboard->key[i] > HID_KEY_ERROR_UNDEFINED) {
|
||||
char key = 0;
|
||||
|
||||
/* LVGL special keys */
|
||||
if (keyboard->key[i] == HID_KEY_TAB) {
|
||||
if ((keyboard->modifier.left_shift || keyboard->modifier.right_shift)) {
|
||||
key = LV_KEY_PREV;
|
||||
} else {
|
||||
key = LV_KEY_NEXT;
|
||||
}
|
||||
} else if (keyboard->key[i] == HID_KEY_RIGHT) {
|
||||
key = LV_KEY_RIGHT;
|
||||
} else if (keyboard->key[i] == HID_KEY_LEFT) {
|
||||
key = LV_KEY_LEFT;
|
||||
} else if (keyboard->key[i] == HID_KEY_DOWN) {
|
||||
key = LV_KEY_DOWN;
|
||||
} else if (keyboard->key[i] == HID_KEY_UP) {
|
||||
key = LV_KEY_UP;
|
||||
} else if (keyboard->key[i] == HID_KEY_ENTER || keyboard->key[i] == HID_KEY_KEYPAD_ENTER) {
|
||||
key = LV_KEY_ENTER;
|
||||
} else if (keyboard->key[i] == HID_KEY_DELETE) {
|
||||
key = LV_KEY_DEL;
|
||||
} else if (keyboard->key[i] == HID_KEY_HOME) {
|
||||
key = LV_KEY_HOME;
|
||||
} else if (keyboard->key[i] == HID_KEY_END) {
|
||||
key = LV_KEY_END;
|
||||
} else {
|
||||
/* Get ASCII char */
|
||||
key = usb_hid_get_keyboard_char(keyboard->key[i], (keyboard->modifier.left_shift || keyboard->modifier.right_shift));
|
||||
}
|
||||
|
||||
if (key == 0) {
|
||||
ESP_LOGI(TAG, "Not recognized key: %c (%d)", keyboard->key[i], keyboard->key[i]);
|
||||
}
|
||||
hid_ctx->kb.last_key = key;
|
||||
hid_ctx->kb.pressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (dev.proto == HID_PROTOCOL_MOUSE) {
|
||||
hid_mouse_input_report_boot_t *mouse = (hid_mouse_input_report_boot_t *)data;
|
||||
if (data_length < sizeof(hid_mouse_input_report_boot_t)) {
|
||||
break;
|
||||
}
|
||||
hid_ctx->mouse.left_button = mouse->buttons.button1;
|
||||
hid_ctx->mouse.x += mouse->x_displacement;
|
||||
hid_ctx->mouse.y += mouse->y_displacement;
|
||||
}
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
|
||||
hid_host_device_close(hid_device_handle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_task(void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev;
|
||||
lvgl_port_usb_hid_ctx_t *ctx = (lvgl_port_usb_hid_ctx_t *)arg;
|
||||
hid_host_device_handle_t hid_device_handle = NULL;
|
||||
lvgl_port_usb_hid_event_t msg;
|
||||
|
||||
assert(ctx);
|
||||
|
||||
ctx->running = true;
|
||||
|
||||
while (ctx->running) {
|
||||
if (xQueueReceive(ctx->queue, &msg, pdMS_TO_TICKS(50))) {
|
||||
hid_device_handle = msg.hid_device_handle;
|
||||
hid_host_device_get_params(hid_device_handle, &dev);
|
||||
|
||||
switch (msg.event) {
|
||||
case HID_HOST_DRIVER_EVENT_CONNECTED:
|
||||
/* Handle mouse or keyboard */
|
||||
if (dev.proto == HID_PROTOCOL_KEYBOARD || dev.proto == HID_PROTOCOL_MOUSE) {
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = lvgl_port_usb_hid_host_interface_callback,
|
||||
.callback_arg = ctx
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK( hid_host_device_open(hid_device_handle, &dev_config) );
|
||||
ESP_ERROR_CHECK( hid_class_request_set_idle(hid_device_handle, 0, 0) );
|
||||
ESP_ERROR_CHECK( hid_class_request_set_protocol(hid_device_handle, HID_REPORT_PROTOCOL_BOOT) );
|
||||
ESP_ERROR_CHECK( hid_host_device_start(hid_device_handle) );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xQueueReset(ctx->queue);
|
||||
vQueueDelete(ctx->queue);
|
||||
|
||||
hid_host_uninstall();
|
||||
|
||||
memset(&lvgl_hid_ctx, 0, sizeof(lvgl_port_usb_hid_ctx_t));
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_read_mouse(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
int16_t width = 0;
|
||||
int16_t height = 0;
|
||||
assert(indev_drv);
|
||||
lvgl_port_usb_hid_ctx_t *ctx = (lvgl_port_usb_hid_ctx_t *)indev_drv->user_data;
|
||||
assert(ctx);
|
||||
|
||||
if (indev_drv->disp->driver->rotated == LV_DISP_ROT_NONE || indev_drv->disp->driver->rotated == LV_DISP_ROT_180) {
|
||||
width = indev_drv->disp->driver->hor_res;
|
||||
height = indev_drv->disp->driver->ver_res;
|
||||
} else {
|
||||
width = indev_drv->disp->driver->ver_res;
|
||||
height = indev_drv->disp->driver->hor_res;
|
||||
}
|
||||
|
||||
/* Screen borders */
|
||||
if (ctx->mouse.x < 0) {
|
||||
ctx->mouse.x = 0;
|
||||
} else if (ctx->mouse.x > width * ctx->mouse.sensitivity) {
|
||||
ctx->mouse.x = width * ctx->mouse.sensitivity;
|
||||
}
|
||||
if (ctx->mouse.y < 0) {
|
||||
ctx->mouse.y = 0;
|
||||
} else if (ctx->mouse.y > height * ctx->mouse.sensitivity) {
|
||||
ctx->mouse.y = height * ctx->mouse.sensitivity;
|
||||
}
|
||||
|
||||
/* Get coordinates by rotation with sensitivity */
|
||||
switch (indev_drv->disp->driver->rotated) {
|
||||
case LV_DISP_ROT_NONE:
|
||||
data->point.x = ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.y = ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
case LV_DISP_ROT_90:
|
||||
data->point.y = width - ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.x = ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
case LV_DISP_ROT_180:
|
||||
data->point.x = width - ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.y = height - ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
case LV_DISP_ROT_270:
|
||||
data->point.y = ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.x = height - ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->mouse.left_button) {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_read_kb(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
assert(indev_drv);
|
||||
lvgl_port_usb_hid_ctx_t *ctx = (lvgl_port_usb_hid_ctx_t *)indev_drv->user_data;
|
||||
assert(ctx);
|
||||
|
||||
data->key = ctx->kb.last_key;
|
||||
if (ctx->kb.pressed) {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
ctx->kb.pressed = false;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
ctx->kb.last_key = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_callback(hid_host_device_handle_t hid_device_handle, const hid_host_driver_event_t event, void *arg)
|
||||
{
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = (lvgl_port_usb_hid_ctx_t *)arg;
|
||||
|
||||
const lvgl_port_usb_hid_event_t msg = {
|
||||
.hid_device_handle = hid_device_handle,
|
||||
.event = event,
|
||||
.arg = arg
|
||||
};
|
||||
|
||||
xQueueSend(hid_ctx->queue, &msg, 0);
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct lvgl_port_ctx_s {
|
||||
SemaphoreHandle_t lvgl_mux;
|
||||
esp_timer_handle_t tick_timer;
|
||||
bool running;
|
||||
int task_max_sleep_ms;
|
||||
int timer_period_ms;
|
||||
} lvgl_port_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Local variables
|
||||
*******************************************************************************/
|
||||
static lvgl_port_ctx_t lvgl_port_ctx;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
static void lvgl_port_task(void *arg);
|
||||
static esp_err_t lvgl_port_tick_init(void);
|
||||
static void lvgl_port_task_deinit(void);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
esp_err_t lvgl_port_init(const lvgl_port_cfg_t *cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(cfg->task_affinity < (configNUM_CORES), ESP_ERR_INVALID_ARG, err, TAG, "Bad core number for task! Maximum core number is %d", (configNUM_CORES - 1));
|
||||
|
||||
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
||||
|
||||
/* LVGL init */
|
||||
lv_init();
|
||||
/* Tick init */
|
||||
lvgl_port_ctx.timer_period_ms = cfg->timer_period_ms;
|
||||
ESP_RETURN_ON_ERROR(lvgl_port_tick_init(), TAG, "");
|
||||
/* Create task */
|
||||
lvgl_port_ctx.task_max_sleep_ms = cfg->task_max_sleep_ms;
|
||||
if (lvgl_port_ctx.task_max_sleep_ms == 0) {
|
||||
lvgl_port_ctx.task_max_sleep_ms = 500;
|
||||
}
|
||||
lvgl_port_ctx.lvgl_mux = xSemaphoreCreateRecursiveMutex();
|
||||
ESP_GOTO_ON_FALSE(lvgl_port_ctx.lvgl_mux, ESP_ERR_NO_MEM, err, TAG, "Create LVGL mutex fail!");
|
||||
|
||||
BaseType_t res;
|
||||
if (cfg->task_affinity < 0) {
|
||||
res = xTaskCreate(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, NULL);
|
||||
} else {
|
||||
res = xTaskCreatePinnedToCore(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, NULL, cfg->task_affinity);
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(res == pdPASS, ESP_FAIL, err, TAG, "Create LVGL task fail!");
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
lvgl_port_deinit();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_resume(void)
|
||||
{
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
|
||||
if (lvgl_port_ctx.tick_timer != NULL) {
|
||||
lv_timer_enable(true);
|
||||
ret = esp_timer_start_periodic(lvgl_port_ctx.tick_timer, lvgl_port_ctx.timer_period_ms * 1000);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_stop(void)
|
||||
{
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
|
||||
if (lvgl_port_ctx.tick_timer != NULL) {
|
||||
lv_timer_enable(false);
|
||||
ret = esp_timer_stop(lvgl_port_ctx.tick_timer);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_deinit(void)
|
||||
{
|
||||
/* Stop and delete timer */
|
||||
if (lvgl_port_ctx.tick_timer != NULL) {
|
||||
esp_timer_stop(lvgl_port_ctx.tick_timer);
|
||||
esp_timer_delete(lvgl_port_ctx.tick_timer);
|
||||
lvgl_port_ctx.tick_timer = NULL;
|
||||
}
|
||||
|
||||
/* Stop running task */
|
||||
if (lvgl_port_ctx.running) {
|
||||
lvgl_port_ctx.running = false;
|
||||
} else {
|
||||
lvgl_port_task_deinit();
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool lvgl_port_lock(uint32_t timeout_ms)
|
||||
{
|
||||
assert(lvgl_port_ctx.lvgl_mux && "lvgl_port_init must be called first");
|
||||
|
||||
const TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
return xSemaphoreTakeRecursive(lvgl_port_ctx.lvgl_mux, timeout_ticks) == pdTRUE;
|
||||
}
|
||||
|
||||
void lvgl_port_unlock(void)
|
||||
{
|
||||
assert(lvgl_port_ctx.lvgl_mux && "lvgl_port_init must be called first");
|
||||
xSemaphoreGiveRecursive(lvgl_port_ctx.lvgl_mux);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_task(void *arg)
|
||||
{
|
||||
uint32_t task_delay_ms = lvgl_port_ctx.task_max_sleep_ms;
|
||||
|
||||
ESP_LOGI(TAG, "Starting LVGL task");
|
||||
lvgl_port_ctx.running = true;
|
||||
while (lvgl_port_ctx.running) {
|
||||
if (lv_display_get_default() && lvgl_port_lock(0)) {
|
||||
task_delay_ms = lv_timer_handler();
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
if ((task_delay_ms > lvgl_port_ctx.task_max_sleep_ms) || (1 == task_delay_ms)) {
|
||||
task_delay_ms = lvgl_port_ctx.task_max_sleep_ms;
|
||||
} else if (task_delay_ms < 1) {
|
||||
task_delay_ms = 1;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
|
||||
}
|
||||
|
||||
lvgl_port_task_deinit();
|
||||
|
||||
/* Close task */
|
||||
vTaskDelete( NULL );
|
||||
}
|
||||
|
||||
static void lvgl_port_task_deinit(void)
|
||||
{
|
||||
if (lvgl_port_ctx.lvgl_mux) {
|
||||
vSemaphoreDelete(lvgl_port_ctx.lvgl_mux);
|
||||
}
|
||||
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
||||
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
|
||||
/* Deinitialize LVGL */
|
||||
lv_deinit();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void lvgl_port_tick_increment(void *arg)
|
||||
{
|
||||
/* Tell LVGL how many milliseconds have elapsed */
|
||||
lv_tick_inc(lvgl_port_ctx.timer_period_ms);
|
||||
}
|
||||
|
||||
static esp_err_t lvgl_port_tick_init(void)
|
||||
{
|
||||
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
|
||||
const esp_timer_create_args_t lvgl_tick_timer_args = {
|
||||
.callback = &lvgl_port_tick_increment,
|
||||
.name = "LVGL tick",
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(esp_timer_create(&lvgl_tick_timer_args, &lvgl_port_ctx.tick_timer), TAG, "Creating LVGL timer filed!");
|
||||
return esp_timer_start_periodic(lvgl_port_ctx.tick_timer, lvgl_port_ctx.timer_period_ms * 1000);
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef enum {
|
||||
LVGL_PORT_NAV_BTN_PREV,
|
||||
LVGL_PORT_NAV_BTN_NEXT,
|
||||
LVGL_PORT_NAV_BTN_ENTER,
|
||||
LVGL_PORT_NAV_BTN_CNT,
|
||||
} lvgl_port_nav_btns_t;
|
||||
|
||||
typedef struct {
|
||||
button_handle_t btn[LVGL_PORT_NAV_BTN_CNT]; /* Button handlers */
|
||||
lv_indev_t *indev; /* LVGL input device driver */
|
||||
bool btn_prev; /* Button prev state */
|
||||
bool btn_next; /* Button next state */
|
||||
bool btn_enter; /* Button enter state */
|
||||
} lvgl_port_nav_btns_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_navigation_buttons_read(lv_indev_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_btn_down_handler(void *arg, void *arg2);
|
||||
static void lvgl_port_btn_up_handler(void *arg, void *arg2);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_navigation_buttons(const lvgl_port_nav_btns_cfg_t *buttons_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
esp_err_t ret = ESP_OK;
|
||||
assert(buttons_cfg != NULL);
|
||||
assert(buttons_cfg->disp != NULL);
|
||||
|
||||
/* Touch context */
|
||||
lvgl_port_nav_btns_ctx_t *buttons_ctx = malloc(sizeof(lvgl_port_nav_btns_ctx_t));
|
||||
if (buttons_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for buttons context allocation!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Previous button */
|
||||
if (buttons_cfg->button_prev != NULL) {
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_PREV] = iot_button_create(buttons_cfg->button_prev);
|
||||
ESP_GOTO_ON_FALSE(buttons_ctx->btn[LVGL_PORT_NAV_BTN_PREV], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
|
||||
/* Next button */
|
||||
if (buttons_cfg->button_next != NULL) {
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_NEXT] = iot_button_create(buttons_cfg->button_next);
|
||||
ESP_GOTO_ON_FALSE(buttons_ctx->btn[LVGL_PORT_NAV_BTN_NEXT], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
|
||||
/* Enter button */
|
||||
if (buttons_cfg->button_enter != NULL) {
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_ENTER] = iot_button_create(buttons_cfg->button_enter);
|
||||
ESP_GOTO_ON_FALSE(buttons_ctx->btn[LVGL_PORT_NAV_BTN_ENTER], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
|
||||
/* Button handlers */
|
||||
for (int i = 0; i < LVGL_PORT_NAV_BTN_CNT; i++) {
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(buttons_ctx->btn[i], BUTTON_PRESS_DOWN, lvgl_port_btn_down_handler, buttons_ctx));
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(buttons_ctx->btn[i], BUTTON_PRESS_UP, lvgl_port_btn_up_handler, buttons_ctx));
|
||||
}
|
||||
|
||||
buttons_ctx->btn_prev = false;
|
||||
buttons_ctx->btn_next = false;
|
||||
buttons_ctx->btn_enter = false;
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Register a touchpad input device */
|
||||
indev = lv_indev_create();
|
||||
lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER);
|
||||
lv_indev_set_read_cb(indev, lvgl_port_navigation_buttons_read);
|
||||
lv_indev_set_disp(indev, buttons_cfg->disp);
|
||||
lv_indev_set_user_data(indev, buttons_ctx);
|
||||
//buttons_ctx->indev->long_press_repeat_time = 300;
|
||||
buttons_ctx->indev = indev;
|
||||
lvgl_port_unlock();
|
||||
|
||||
return indev;
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
for (int i = 0; i < LVGL_PORT_NAV_BTN_CNT; i++) {
|
||||
if (buttons_ctx->btn[i] != NULL) {
|
||||
iot_button_delete(buttons_ctx->btn[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (buttons_ctx != NULL) {
|
||||
free(buttons_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return buttons_ctx->indev;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_navigation_buttons(lv_indev_t *buttons)
|
||||
{
|
||||
assert(buttons);
|
||||
lvgl_port_nav_btns_ctx_t *buttons_ctx = (lvgl_port_nav_btns_ctx_t *)lv_indev_get_user_data(buttons);
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(buttons);
|
||||
lvgl_port_unlock();
|
||||
|
||||
if (buttons_ctx) {
|
||||
free(buttons_ctx);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
static void lvgl_port_navigation_buttons_read(lv_indev_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
static uint32_t last_key = 0;
|
||||
|
||||
assert(indev_drv);
|
||||
lvgl_port_nav_btns_ctx_t *ctx = (lvgl_port_nav_btns_ctx_t *)lv_indev_get_user_data(indev_drv);
|
||||
assert(ctx);
|
||||
|
||||
/* Buttons */
|
||||
if (ctx->btn_prev) {
|
||||
data->key = LV_KEY_LEFT;
|
||||
last_key = LV_KEY_LEFT;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else if (ctx->btn_next) {
|
||||
data->key = LV_KEY_RIGHT;
|
||||
last_key = LV_KEY_RIGHT;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else if (ctx->btn_enter) {
|
||||
data->key = LV_KEY_ENTER;
|
||||
last_key = LV_KEY_ENTER;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->key = last_key;
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_btn_down_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_nav_btns_ctx_t *ctx = (lvgl_port_nav_btns_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* PREV */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_PREV]) {
|
||||
ctx->btn_prev = true;
|
||||
}
|
||||
/* NEXT */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_NEXT]) {
|
||||
ctx->btn_next = true;
|
||||
}
|
||||
/* ENTER */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_ENTER]) {
|
||||
ctx->btn_enter = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_btn_up_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_nav_btns_ctx_t *ctx = (lvgl_port_nav_btns_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* PREV */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_PREV]) {
|
||||
ctx->btn_prev = false;
|
||||
}
|
||||
/* NEXT */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_NEXT]) {
|
||||
ctx->btn_next = false;
|
||||
}
|
||||
/* ENTER */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_ENTER]) {
|
||||
ctx->btn_enter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 4)) || (ESP_IDF_VERSION == ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||
#define LVGL_PORT_HANDLE_FLUSH_READY 0
|
||||
#else
|
||||
#define LVGL_PORT_HANDLE_FLUSH_READY 1
|
||||
#endif
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_handle_t io_handle; /* LCD panel IO handle */
|
||||
esp_lcd_panel_handle_t panel_handle; /* LCD panel handle */
|
||||
lvgl_port_rotation_cfg_t rotation; /* Default values of the screen rotation */
|
||||
lv_color16_t *draw_buffs[2]; /* Display draw buffers */
|
||||
lv_display_t *disp_drv; /* LVGL display driver */
|
||||
struct {
|
||||
unsigned int monochrome: 1; /* True, if display is monochrome and using 1bit for 1px */
|
||||
unsigned int swap_bytes: 1; /* Swap bytes in RGB656 (16-bit) before send to LCD driver */
|
||||
} flags;
|
||||
} lvgl_port_display_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||
static bool lvgl_port_flush_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx);
|
||||
#endif
|
||||
static void lvgl_port_flush_callback(lv_display_t *drv, const lv_area_t *area, uint8_t *color_map);
|
||||
static void lvgl_port_disp_size_update_callback(lv_event_t *e);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
lv_display_t *disp = NULL;
|
||||
lv_color16_t *buf1 = NULL;
|
||||
lv_color16_t *buf2 = NULL;
|
||||
assert(disp_cfg != NULL);
|
||||
assert(disp_cfg->io_handle != NULL);
|
||||
assert(disp_cfg->panel_handle != NULL);
|
||||
assert(disp_cfg->buffer_size > 0);
|
||||
assert(disp_cfg->hres > 0);
|
||||
assert(disp_cfg->vres > 0);
|
||||
|
||||
/* Display context */
|
||||
lvgl_port_display_ctx_t *disp_ctx = malloc(sizeof(lvgl_port_display_ctx_t));
|
||||
ESP_GOTO_ON_FALSE(disp_ctx, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for display context allocation!");
|
||||
disp_ctx->io_handle = disp_cfg->io_handle;
|
||||
disp_ctx->panel_handle = disp_cfg->panel_handle;
|
||||
disp_ctx->rotation.swap_xy = disp_cfg->rotation.swap_xy;
|
||||
disp_ctx->rotation.mirror_x = disp_cfg->rotation.mirror_x;
|
||||
disp_ctx->rotation.mirror_y = disp_cfg->rotation.mirror_y;
|
||||
disp_ctx->flags.swap_bytes = disp_cfg->flags.swap_bytes;
|
||||
|
||||
uint32_t buff_caps = MALLOC_CAP_DEFAULT;
|
||||
if (disp_cfg->flags.buff_dma && disp_cfg->flags.buff_spiram) {
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "Alloc DMA capable buffer in SPIRAM is not supported!");
|
||||
} else if (disp_cfg->flags.buff_dma) {
|
||||
buff_caps = MALLOC_CAP_DMA;
|
||||
} else if (disp_cfg->flags.buff_spiram) {
|
||||
buff_caps = MALLOC_CAP_SPIRAM;
|
||||
}
|
||||
|
||||
/* alloc draw buffers used by LVGL */
|
||||
/* it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized */
|
||||
buf1 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color16_t), buff_caps);
|
||||
ESP_GOTO_ON_FALSE(buf1, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf1) allocation!");
|
||||
if (disp_cfg->double_buffer) {
|
||||
buf2 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color16_t), buff_caps);
|
||||
ESP_GOTO_ON_FALSE(buf2, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf2) allocation!");
|
||||
}
|
||||
|
||||
disp_ctx->draw_buffs[0] = buf1;
|
||||
disp_ctx->draw_buffs[1] = buf2;
|
||||
|
||||
lvgl_port_lock(0);
|
||||
disp = lv_display_create(disp_cfg->hres, disp_cfg->vres);
|
||||
|
||||
/* Monochrome display settings */
|
||||
if (disp_cfg->monochrome) {
|
||||
/* When using monochromatic display, there must be used full bufer! */
|
||||
ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == disp_cfg->buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Monochromatic display must using full buffer!");
|
||||
|
||||
disp_ctx->flags.monochrome = 1;
|
||||
|
||||
//lv_display_set_color_format(disp, LV_COLOR_FORMAT_RGB565);
|
||||
lv_display_set_buffers(disp, buf1, buf2, disp_cfg->buffer_size * sizeof(lv_color16_t), LV_DISPLAY_RENDER_MODE_FULL);
|
||||
} else {
|
||||
lv_display_set_buffers(disp, buf1, buf2, disp_cfg->buffer_size * sizeof(lv_color16_t), LV_DISPLAY_RENDER_MODE_PARTIAL);
|
||||
}
|
||||
|
||||
|
||||
lv_display_set_flush_cb(disp, lvgl_port_flush_callback);
|
||||
lv_display_add_event_cb(disp, lvgl_port_disp_size_update_callback, LV_EVENT_RESOLUTION_CHANGED, disp_ctx);
|
||||
|
||||
lv_display_set_user_data(disp, disp_ctx);
|
||||
disp_ctx->disp_drv = disp;
|
||||
|
||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||
/* Register done callback */
|
||||
const esp_lcd_panel_io_callbacks_t cbs = {
|
||||
.on_color_trans_done = lvgl_port_flush_ready_callback,
|
||||
};
|
||||
esp_lcd_panel_io_register_event_callbacks(disp_ctx->io_handle, &cbs, disp_ctx->disp_drv);
|
||||
#endif
|
||||
|
||||
lvgl_port_unlock();
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
if (buf1) {
|
||||
free(buf1);
|
||||
}
|
||||
if (buf2) {
|
||||
free(buf2);
|
||||
}
|
||||
if (disp_ctx) {
|
||||
free(disp_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return disp;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_disp(lv_display_t *disp)
|
||||
{
|
||||
assert(disp);
|
||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_user_data(disp);
|
||||
|
||||
lvgl_port_lock(0);
|
||||
lv_disp_remove(disp);
|
||||
lvgl_port_unlock();
|
||||
|
||||
if (disp_ctx->draw_buffs[0]) {
|
||||
free(disp_ctx->draw_buffs[0]);
|
||||
}
|
||||
|
||||
if (disp_ctx->draw_buffs[1]) {
|
||||
free(disp_ctx->draw_buffs[1]);
|
||||
}
|
||||
|
||||
free(disp_ctx);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void lvgl_port_flush_ready(lv_display_t *disp)
|
||||
{
|
||||
assert(disp);
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||
static bool lvgl_port_flush_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
lv_display_t *disp_drv = (lv_display_t *)user_ctx;
|
||||
assert(disp_drv != NULL);
|
||||
lv_disp_flush_ready(disp_drv);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void _lvgl_port_transform_monochrome(lv_display_t *display, const lv_area_t *area, uint8_t *color_map)
|
||||
{
|
||||
uint8_t *buf = color_map;
|
||||
lv_color16_t *color = (lv_color16_t *)color_map;
|
||||
uint16_t hor_res = lv_display_get_physical_horizontal_resolution(display);
|
||||
uint16_t ver_res = lv_display_get_physical_vertical_resolution(display);
|
||||
uint16_t res = hor_res;
|
||||
bool swap_xy = (lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_90 || lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_270);
|
||||
|
||||
int x1 = area->x1;
|
||||
int x2 = area->x2;
|
||||
int y1 = area->y1;
|
||||
int y2 = area->y2;
|
||||
|
||||
int out_x, out_y;
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
bool chroma_color = (color[hor_res * y + x].blue > 16);
|
||||
|
||||
if (swap_xy) {
|
||||
out_x = y;
|
||||
out_y = x;
|
||||
res = ver_res;
|
||||
} else {
|
||||
out_x = x;
|
||||
out_y = y;
|
||||
res = hor_res;
|
||||
}
|
||||
|
||||
/* Write to the buffer as required for the display.
|
||||
* It writes only 1-bit for monochrome displays mapped vertically.*/
|
||||
buf = color_map + res * (out_y >> 3) + (out_x);
|
||||
if (chroma_color) {
|
||||
(*buf) &= ~(1 << (out_y % 8));
|
||||
} else {
|
||||
(*buf) |= (1 << (out_y % 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_flush_callback(lv_display_t *drv, const lv_area_t *area, uint8_t *color_map)
|
||||
{
|
||||
assert(drv != NULL);
|
||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_user_data(drv);
|
||||
assert(disp_ctx != NULL);
|
||||
|
||||
//TODO: try to use SPI_SWAP_DATA_RX from https://docs.espressif.com/projects/esp-idf/en/v5.1/esp32s3/api-reference/peripherals/spi_master.html#c.SPI_SWAP_DATA_TX
|
||||
if (disp_ctx->flags.swap_bytes) {
|
||||
size_t len = lv_area_get_size(area);
|
||||
lv_draw_sw_rgb565_swap(color_map, len);
|
||||
}
|
||||
|
||||
/* Transfor data in buffer for monochromatic screen */
|
||||
if (disp_ctx->flags.monochrome) {
|
||||
_lvgl_port_transform_monochrome(drv, area, color_map);
|
||||
}
|
||||
|
||||
const int offsetx1 = area->x1;
|
||||
const int offsetx2 = area->x2;
|
||||
const int offsety1 = area->y1;
|
||||
const int offsety2 = area->y2;
|
||||
// copy a buffer's content to a specific area of the display
|
||||
esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
|
||||
}
|
||||
|
||||
static void lvgl_port_disp_size_update_callback(lv_event_t *e)
|
||||
{
|
||||
assert(e);
|
||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)e->user_data;
|
||||
assert(disp_ctx != NULL);
|
||||
esp_lcd_panel_handle_t panel_handle = disp_ctx->panel_handle;
|
||||
|
||||
/* Solve rotation screen and touch */
|
||||
switch (lv_display_get_rotation(disp_ctx->disp_drv)) {
|
||||
case LV_DISPLAY_ROTATION_0:
|
||||
/* Rotate LCD display */
|
||||
esp_lcd_panel_swap_xy(panel_handle, disp_ctx->rotation.swap_xy);
|
||||
esp_lcd_panel_mirror(panel_handle, disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
||||
break;
|
||||
case LV_DISPLAY_ROTATION_90:
|
||||
/* Rotate LCD display */
|
||||
esp_lcd_panel_swap_xy(panel_handle, !disp_ctx->rotation.swap_xy);
|
||||
if (disp_ctx->rotation.swap_xy) {
|
||||
esp_lcd_panel_mirror(panel_handle, !disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
||||
} else {
|
||||
esp_lcd_panel_mirror(panel_handle, disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
||||
}
|
||||
break;
|
||||
case LV_DISPLAY_ROTATION_180:
|
||||
/* Rotate LCD display */
|
||||
esp_lcd_panel_swap_xy(panel_handle, disp_ctx->rotation.swap_xy);
|
||||
esp_lcd_panel_mirror(panel_handle, !disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
||||
break;
|
||||
case LV_DISPLAY_ROTATION_270:
|
||||
/* Rotate LCD display */
|
||||
esp_lcd_panel_swap_xy(panel_handle, !disp_ctx->rotation.swap_xy);
|
||||
if (disp_ctx->rotation.swap_xy) {
|
||||
esp_lcd_panel_mirror(panel_handle, disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
||||
} else {
|
||||
esp_lcd_panel_mirror(panel_handle, !disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
knob_handle_t knob_handle; /* Encoder knob handlers */
|
||||
button_handle_t btn_handle; /* Encoder button handlers */
|
||||
lv_indev_t *indev; /* LVGL input device driver */
|
||||
bool btn_enter; /* Encoder button enter state */
|
||||
} lvgl_port_encoder_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_encoder_read(lv_indev_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_encoder_btn_down_handler(void *arg, void *arg2);
|
||||
static void lvgl_port_encoder_btn_up_handler(void *arg, void *arg2);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_encoder(const lvgl_port_encoder_cfg_t *encoder_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
esp_err_t ret = ESP_OK;
|
||||
assert(encoder_cfg != NULL);
|
||||
assert(encoder_cfg->disp != NULL);
|
||||
|
||||
/* Encoder context */
|
||||
lvgl_port_encoder_ctx_t *encoder_ctx = malloc(sizeof(lvgl_port_encoder_ctx_t));
|
||||
if (encoder_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for encoder context allocation!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Encoder_a/b */
|
||||
if (encoder_cfg->encoder_a_b != NULL) {
|
||||
encoder_ctx->knob_handle = iot_knob_create(encoder_cfg->encoder_a_b);
|
||||
ESP_GOTO_ON_FALSE(encoder_ctx->knob_handle, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for knob create!");
|
||||
}
|
||||
|
||||
/* Encoder Enter */
|
||||
if (encoder_cfg->encoder_enter != NULL) {
|
||||
encoder_ctx->btn_handle = iot_button_create(encoder_cfg->encoder_enter);
|
||||
ESP_GOTO_ON_FALSE(encoder_ctx->btn_handle, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(encoder_ctx->btn_handle, BUTTON_PRESS_DOWN, lvgl_port_encoder_btn_down_handler, encoder_ctx));
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(encoder_ctx->btn_handle, BUTTON_PRESS_UP, lvgl_port_encoder_btn_up_handler, encoder_ctx));
|
||||
|
||||
encoder_ctx->btn_enter = false;
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Register a encoder input device */
|
||||
indev = lv_indev_create();
|
||||
lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER);
|
||||
lv_indev_set_read_cb(indev, lvgl_port_encoder_read);
|
||||
lv_indev_set_disp(indev, encoder_cfg->disp);
|
||||
lv_indev_set_user_data(indev, encoder_ctx);
|
||||
encoder_ctx->indev = indev;
|
||||
lvgl_port_unlock();
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
if (encoder_ctx->knob_handle != NULL) {
|
||||
iot_knob_delete(encoder_ctx->knob_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx->btn_handle != NULL) {
|
||||
iot_button_delete(encoder_ctx->btn_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx != NULL) {
|
||||
free(encoder_ctx);
|
||||
}
|
||||
}
|
||||
return encoder_ctx->indev;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_encoder(lv_indev_t *encoder)
|
||||
{
|
||||
assert(encoder);
|
||||
lvgl_port_encoder_ctx_t *encoder_ctx = (lvgl_port_encoder_ctx_t *)lv_indev_get_user_data(encoder);
|
||||
|
||||
if (encoder_ctx->knob_handle != NULL) {
|
||||
iot_knob_delete(encoder_ctx->knob_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx->btn_handle != NULL) {
|
||||
iot_button_delete(encoder_ctx->btn_handle);
|
||||
}
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(encoder);
|
||||
lvgl_port_unlock();
|
||||
|
||||
if (encoder_ctx != NULL) {
|
||||
free(encoder_ctx);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_encoder_read(lv_indev_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
static int32_t last_v = 0;
|
||||
assert(indev_drv);
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *)lv_indev_get_user_data(indev_drv);
|
||||
assert(ctx);
|
||||
|
||||
int32_t invd = iot_knob_get_count_value(ctx->knob_handle);
|
||||
knob_event_t event = iot_knob_get_event(ctx->knob_handle);
|
||||
|
||||
if (last_v ^ invd) {
|
||||
last_v = invd;
|
||||
data->enc_diff = (KNOB_LEFT == event) ? (-1) : ((KNOB_RIGHT == event) ? (1) : (0));
|
||||
} else {
|
||||
data->enc_diff = 0;
|
||||
}
|
||||
data->state = (true == ctx->btn_enter) ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
|
||||
static void lvgl_port_encoder_btn_down_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* ENTER */
|
||||
if (button == ctx->btn_handle) {
|
||||
ctx->btn_enter = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_encoder_btn_up_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* ENTER */
|
||||
if (button == ctx->btn_handle) {
|
||||
ctx->btn_enter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_touch.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_touch_handle_t handle; /* LCD touch IO handle */
|
||||
lv_indev_t *indev; /* LVGL input device driver */
|
||||
} lvgl_port_touch_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_touchpad_read(lv_indev_t *indev_drv, lv_indev_data_t *data);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_touch(const lvgl_port_touch_cfg_t *touch_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
assert(touch_cfg != NULL);
|
||||
assert(touch_cfg->disp != NULL);
|
||||
assert(touch_cfg->handle != NULL);
|
||||
|
||||
/* Touch context */
|
||||
lvgl_port_touch_ctx_t *touch_ctx = malloc(sizeof(lvgl_port_touch_ctx_t));
|
||||
if (touch_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for touch context allocation!");
|
||||
return NULL;
|
||||
}
|
||||
touch_ctx->handle = touch_cfg->handle;
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Register a touchpad input device */
|
||||
indev = lv_indev_create();
|
||||
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
|
||||
lv_indev_set_read_cb(indev, lvgl_port_touchpad_read);
|
||||
lv_indev_set_disp(indev, touch_cfg->disp);
|
||||
lv_indev_set_user_data(indev, touch_ctx);
|
||||
touch_ctx->indev = indev;
|
||||
lvgl_port_unlock();
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_touch(lv_indev_t *touch)
|
||||
{
|
||||
assert(touch);
|
||||
lvgl_port_touch_ctx_t *touch_ctx = (lvgl_port_touch_ctx_t *)lv_indev_get_user_data(touch);
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(touch);
|
||||
lvgl_port_unlock();
|
||||
|
||||
if (touch_ctx) {
|
||||
free(touch_ctx);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_touchpad_read(lv_indev_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
assert(indev_drv);
|
||||
lvgl_port_touch_ctx_t *touch_ctx = (lvgl_port_touch_ctx_t *)lv_indev_get_user_data(indev_drv);
|
||||
assert(touch_ctx);
|
||||
assert(touch_ctx->handle);
|
||||
|
||||
uint16_t touchpad_x[1] = {0};
|
||||
uint16_t touchpad_y[1] = {0};
|
||||
uint8_t touchpad_cnt = 0;
|
||||
|
||||
/* Read data from touch controller into memory */
|
||||
esp_lcd_touch_read_data(touch_ctx->handle);
|
||||
|
||||
/* Read data from touch controller */
|
||||
bool touchpad_pressed = esp_lcd_touch_get_coordinates(touch_ctx->handle, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);
|
||||
|
||||
if (touchpad_pressed && touchpad_cnt > 0) {
|
||||
data->point.x = touchpad_x[0];
|
||||
data->point.y = touchpad_y[0];
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
#include "usb/hid_host.h"
|
||||
#include "usb/hid_usage_keyboard.h"
|
||||
#include "usb/hid_usage_mouse.h"
|
||||
|
||||
/* LVGL image of cursor */
|
||||
LV_IMG_DECLARE(img_cursor)
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
QueueHandle_t queue; /* USB HID queue */
|
||||
TaskHandle_t task; /* USB HID task */
|
||||
bool running; /* USB HID task running */
|
||||
struct {
|
||||
lv_indev_t *indev; /* LVGL mouse input device driver */
|
||||
uint8_t sensitivity; /* Mouse sensitivity (cannot be zero) */
|
||||
int16_t x; /* Mouse X coordinate */
|
||||
int16_t y; /* Mouse Y coordinate */
|
||||
bool left_button; /* Mouse left button state */
|
||||
} mouse;
|
||||
struct {
|
||||
lv_indev_t *indev; /* LVGL keyboard input device driver */
|
||||
uint32_t last_key;
|
||||
bool pressed;
|
||||
} kb;
|
||||
} lvgl_port_usb_hid_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
hid_host_device_handle_t hid_device_handle;
|
||||
hid_host_driver_event_t event;
|
||||
void *arg;
|
||||
} lvgl_port_usb_hid_event_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static lvgl_port_usb_hid_ctx_t *lvgl_port_hid_init(void);
|
||||
static void lvgl_port_usb_hid_task(void *arg);
|
||||
static void lvgl_port_usb_hid_read_mouse(lv_indev_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_usb_hid_read_kb(lv_indev_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_usb_hid_callback(hid_host_device_handle_t hid_device_handle, const hid_host_driver_event_t event, void *arg);
|
||||
|
||||
/*******************************************************************************
|
||||
* Local variables
|
||||
*******************************************************************************/
|
||||
static lvgl_port_usb_hid_ctx_t lvgl_hid_ctx;
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_usb_hid_mouse_input(const lvgl_port_hid_mouse_cfg_t *mouse_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
assert(mouse_cfg);
|
||||
assert(mouse_cfg->disp);
|
||||
|
||||
/* Initialize USB HID */
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = lvgl_port_hid_init();
|
||||
if (hid_ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Mouse sensitivity cannot be zero */
|
||||
hid_ctx->mouse.sensitivity = (mouse_cfg->sensitivity == 0 ? 1 : mouse_cfg->sensitivity);
|
||||
|
||||
int32_t ver_res = lv_display_get_vertical_resolution(mouse_cfg->disp);
|
||||
int32_t hor_res = lv_display_get_physical_horizontal_resolution(mouse_cfg->disp);
|
||||
|
||||
/* Default coordinates to screen center */
|
||||
hid_ctx->mouse.x = (hor_res * hid_ctx->mouse.sensitivity) / 2;
|
||||
hid_ctx->mouse.y = (ver_res * hid_ctx->mouse.sensitivity) / 2;
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Register a mouse input device */
|
||||
indev = lv_indev_create();
|
||||
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
|
||||
lv_indev_set_read_cb(indev, lvgl_port_usb_hid_read_mouse);
|
||||
lv_indev_set_disp(indev, mouse_cfg->disp);
|
||||
lv_indev_set_user_data(indev, hid_ctx);
|
||||
hid_ctx->mouse.indev = indev;
|
||||
lvgl_port_unlock();
|
||||
|
||||
/* Set image of cursor */
|
||||
lv_obj_t *cursor = mouse_cfg->cursor_img;
|
||||
if (cursor == NULL) {
|
||||
cursor = lv_img_create(lv_scr_act());
|
||||
lv_img_set_src(cursor, &img_cursor);
|
||||
}
|
||||
lv_indev_set_cursor(indev, cursor);
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
lv_indev_t *lvgl_port_add_usb_hid_keyboard_input(const lvgl_port_hid_keyboard_cfg_t *keyboard_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
assert(keyboard_cfg);
|
||||
assert(keyboard_cfg->disp);
|
||||
|
||||
/* Initialize USB HID */
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = lvgl_port_hid_init();
|
||||
if (hid_ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Register a mouse input device */
|
||||
indev = lv_indev_create();
|
||||
lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);
|
||||
lv_indev_set_read_cb(indev, lvgl_port_usb_hid_read_kb);
|
||||
lv_indev_set_disp(indev, keyboard_cfg->disp);
|
||||
lv_indev_set_user_data(indev, hid_ctx);
|
||||
hid_ctx->kb.indev = indev;
|
||||
lvgl_port_unlock();
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_usb_hid_input(lv_indev_t *hid)
|
||||
{
|
||||
assert(hid);
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = (lvgl_port_usb_hid_ctx_t *)lv_indev_get_user_data(hid);
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(hid);
|
||||
lvgl_port_unlock();
|
||||
|
||||
if (lvgl_hid_ctx.mouse.indev == hid) {
|
||||
lvgl_hid_ctx.mouse.indev = NULL;
|
||||
} else if (lvgl_hid_ctx.kb.indev == hid) {
|
||||
lvgl_hid_ctx.kb.indev = NULL;
|
||||
}
|
||||
|
||||
/* If all hid input devices are removed, stop task and clean all */
|
||||
if (lvgl_hid_ctx.mouse.indev == NULL && lvgl_hid_ctx.kb.indev) {
|
||||
hid_ctx->running = false;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static lvgl_port_usb_hid_ctx_t *lvgl_port_hid_init(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
/* USB HID is already initialized */
|
||||
if (lvgl_hid_ctx.task) {
|
||||
return &lvgl_hid_ctx;
|
||||
}
|
||||
|
||||
/* USB HID host driver config */
|
||||
const hid_host_driver_config_t hid_host_driver_config = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 5,
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = lvgl_port_usb_hid_callback,
|
||||
.callback_arg = &lvgl_hid_ctx,
|
||||
};
|
||||
|
||||
ret = hid_host_install(&hid_host_driver_config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "USB HID install failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lvgl_hid_ctx.queue = xQueueCreate(10, sizeof(lvgl_port_usb_hid_event_t));
|
||||
xTaskCreate(&lvgl_port_usb_hid_task, "hid_task", 4 * 1024, &lvgl_hid_ctx, 2, &lvgl_hid_ctx.task);
|
||||
|
||||
return &lvgl_hid_ctx;
|
||||
}
|
||||
|
||||
static char usb_hid_get_keyboard_char(uint8_t key, uint8_t shift)
|
||||
{
|
||||
char ret_key = 0;
|
||||
|
||||
const uint8_t keycode2ascii [57][2] = {
|
||||
{0, 0}, /* HID_KEY_NO_PRESS */
|
||||
{0, 0}, /* HID_KEY_ROLLOVER */
|
||||
{0, 0}, /* HID_KEY_POST_FAIL */
|
||||
{0, 0}, /* HID_KEY_ERROR_UNDEFINED */
|
||||
{'a', 'A'}, /* HID_KEY_A */
|
||||
{'b', 'B'}, /* HID_KEY_B */
|
||||
{'c', 'C'}, /* HID_KEY_C */
|
||||
{'d', 'D'}, /* HID_KEY_D */
|
||||
{'e', 'E'}, /* HID_KEY_E */
|
||||
{'f', 'F'}, /* HID_KEY_F */
|
||||
{'g', 'G'}, /* HID_KEY_G */
|
||||
{'h', 'H'}, /* HID_KEY_H */
|
||||
{'i', 'I'}, /* HID_KEY_I */
|
||||
{'j', 'J'}, /* HID_KEY_J */
|
||||
{'k', 'K'}, /* HID_KEY_K */
|
||||
{'l', 'L'}, /* HID_KEY_L */
|
||||
{'m', 'M'}, /* HID_KEY_M */
|
||||
{'n', 'N'}, /* HID_KEY_N */
|
||||
{'o', 'O'}, /* HID_KEY_O */
|
||||
{'p', 'P'}, /* HID_KEY_P */
|
||||
{'q', 'Q'}, /* HID_KEY_Q */
|
||||
{'r', 'R'}, /* HID_KEY_R */
|
||||
{'s', 'S'}, /* HID_KEY_S */
|
||||
{'t', 'T'}, /* HID_KEY_T */
|
||||
{'u', 'U'}, /* HID_KEY_U */
|
||||
{'v', 'V'}, /* HID_KEY_V */
|
||||
{'w', 'W'}, /* HID_KEY_W */
|
||||
{'x', 'X'}, /* HID_KEY_X */
|
||||
{'y', 'Y'}, /* HID_KEY_Y */
|
||||
{'z', 'Z'}, /* HID_KEY_Z */
|
||||
{'1', '!'}, /* HID_KEY_1 */
|
||||
{'2', '@'}, /* HID_KEY_2 */
|
||||
{'3', '#'}, /* HID_KEY_3 */
|
||||
{'4', '$'}, /* HID_KEY_4 */
|
||||
{'5', '%'}, /* HID_KEY_5 */
|
||||
{'6', '^'}, /* HID_KEY_6 */
|
||||
{'7', '&'}, /* HID_KEY_7 */
|
||||
{'8', '*'}, /* HID_KEY_8 */
|
||||
{'9', '('}, /* HID_KEY_9 */
|
||||
{'0', ')'}, /* HID_KEY_0 */
|
||||
{'\r', '\r'}, /* HID_KEY_ENTER */
|
||||
{0, 0}, /* HID_KEY_ESC */
|
||||
{'\b', 0}, /* HID_KEY_DEL */
|
||||
{0, 0}, /* HID_KEY_TAB */
|
||||
{' ', ' '}, /* HID_KEY_SPACE */
|
||||
{'-', '_'}, /* HID_KEY_MINUS */
|
||||
{'=', '+'}, /* HID_KEY_EQUAL */
|
||||
{'[', '{'}, /* HID_KEY_OPEN_BRACKET */
|
||||
{']', '}'}, /* HID_KEY_CLOSE_BRACKET */
|
||||
{'\\', '|'}, /* HID_KEY_BACK_SLASH */
|
||||
{'\\', '|'}, /* HID_KEY_SHARP */ // HOTFIX: for NonUS Keyboards repeat HID_KEY_BACK_SLASH
|
||||
{';', ':'}, /* HID_KEY_COLON */
|
||||
{'\'', '"'}, /* HID_KEY_QUOTE */
|
||||
{'`', '~'}, /* HID_KEY_TILDE */
|
||||
{',', '<'}, /* HID_KEY_LESS */
|
||||
{'.', '>'}, /* HID_KEY_GREATER */
|
||||
{'/', '?'} /* HID_KEY_SLASH */
|
||||
};
|
||||
|
||||
if (shift > 1) {
|
||||
shift = 1;
|
||||
}
|
||||
|
||||
if ((key >= HID_KEY_A) && (key <= HID_KEY_SLASH)) {
|
||||
ret_key = keycode2ascii[key][shift];
|
||||
}
|
||||
|
||||
return ret_key;
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_host_interface_callback(hid_host_device_handle_t hid_device_handle, const hid_host_interface_event_t event, void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev;
|
||||
hid_host_device_get_params(hid_device_handle, &dev);
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = (lvgl_port_usb_hid_ctx_t *)arg;
|
||||
uint8_t data[10];
|
||||
unsigned int data_length = 0;
|
||||
|
||||
assert(hid_ctx != NULL);
|
||||
|
||||
switch (event) {
|
||||
case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
|
||||
hid_host_device_get_raw_input_report_data(hid_device_handle, data, sizeof(data), &data_length);
|
||||
if (dev.proto == HID_PROTOCOL_KEYBOARD) {
|
||||
hid_keyboard_input_report_boot_t *keyboard = (hid_keyboard_input_report_boot_t *)data;
|
||||
if (data_length < sizeof(hid_keyboard_input_report_boot_t)) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) {
|
||||
if (keyboard->key[i] > HID_KEY_ERROR_UNDEFINED) {
|
||||
char key = 0;
|
||||
|
||||
/* LVGL special keys */
|
||||
if (keyboard->key[i] == HID_KEY_TAB) {
|
||||
if ((keyboard->modifier.left_shift || keyboard->modifier.right_shift)) {
|
||||
key = LV_KEY_PREV;
|
||||
} else {
|
||||
key = LV_KEY_NEXT;
|
||||
}
|
||||
} else if (keyboard->key[i] == HID_KEY_RIGHT) {
|
||||
key = LV_KEY_RIGHT;
|
||||
} else if (keyboard->key[i] == HID_KEY_LEFT) {
|
||||
key = LV_KEY_LEFT;
|
||||
} else if (keyboard->key[i] == HID_KEY_DOWN) {
|
||||
key = LV_KEY_DOWN;
|
||||
} else if (keyboard->key[i] == HID_KEY_UP) {
|
||||
key = LV_KEY_UP;
|
||||
} else if (keyboard->key[i] == HID_KEY_ENTER || keyboard->key[i] == HID_KEY_KEYPAD_ENTER) {
|
||||
key = LV_KEY_ENTER;
|
||||
} else if (keyboard->key[i] == HID_KEY_DELETE) {
|
||||
key = LV_KEY_DEL;
|
||||
} else if (keyboard->key[i] == HID_KEY_HOME) {
|
||||
key = LV_KEY_HOME;
|
||||
} else if (keyboard->key[i] == HID_KEY_END) {
|
||||
key = LV_KEY_END;
|
||||
} else {
|
||||
/* Get ASCII char */
|
||||
key = usb_hid_get_keyboard_char(keyboard->key[i], (keyboard->modifier.left_shift || keyboard->modifier.right_shift));
|
||||
}
|
||||
|
||||
if (key == 0) {
|
||||
ESP_LOGI(TAG, "Not recognized key: %c (%d)", keyboard->key[i], keyboard->key[i]);
|
||||
}
|
||||
hid_ctx->kb.last_key = key;
|
||||
hid_ctx->kb.pressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (dev.proto == HID_PROTOCOL_MOUSE) {
|
||||
hid_mouse_input_report_boot_t *mouse = (hid_mouse_input_report_boot_t *)data;
|
||||
if (data_length < sizeof(hid_mouse_input_report_boot_t)) {
|
||||
break;
|
||||
}
|
||||
hid_ctx->mouse.left_button = mouse->buttons.button1;
|
||||
hid_ctx->mouse.x += mouse->x_displacement;
|
||||
hid_ctx->mouse.y += mouse->y_displacement;
|
||||
}
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
|
||||
hid_host_device_close(hid_device_handle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_task(void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev;
|
||||
lvgl_port_usb_hid_ctx_t *ctx = (lvgl_port_usb_hid_ctx_t *)arg;
|
||||
hid_host_device_handle_t hid_device_handle = NULL;
|
||||
lvgl_port_usb_hid_event_t msg;
|
||||
|
||||
assert(ctx);
|
||||
|
||||
ctx->running = true;
|
||||
|
||||
while (ctx->running) {
|
||||
if (xQueueReceive(ctx->queue, &msg, pdMS_TO_TICKS(50))) {
|
||||
hid_device_handle = msg.hid_device_handle;
|
||||
hid_host_device_get_params(hid_device_handle, &dev);
|
||||
|
||||
switch (msg.event) {
|
||||
case HID_HOST_DRIVER_EVENT_CONNECTED:
|
||||
/* Handle mouse or keyboard */
|
||||
if (dev.proto == HID_PROTOCOL_KEYBOARD || dev.proto == HID_PROTOCOL_MOUSE) {
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = lvgl_port_usb_hid_host_interface_callback,
|
||||
.callback_arg = ctx
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK( hid_host_device_open(hid_device_handle, &dev_config) );
|
||||
ESP_ERROR_CHECK( hid_class_request_set_idle(hid_device_handle, 0, 0) );
|
||||
ESP_ERROR_CHECK( hid_class_request_set_protocol(hid_device_handle, HID_REPORT_PROTOCOL_BOOT) );
|
||||
ESP_ERROR_CHECK( hid_host_device_start(hid_device_handle) );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xQueueReset(ctx->queue);
|
||||
vQueueDelete(ctx->queue);
|
||||
|
||||
hid_host_uninstall();
|
||||
|
||||
memset(&lvgl_hid_ctx, 0, sizeof(lvgl_port_usb_hid_ctx_t));
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_read_mouse(lv_indev_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
int16_t width = 0;
|
||||
int16_t height = 0;
|
||||
assert(indev_drv);
|
||||
lvgl_port_usb_hid_ctx_t *ctx = (lvgl_port_usb_hid_ctx_t *)lv_indev_get_user_data(indev_drv);
|
||||
assert(ctx);
|
||||
|
||||
lv_display_t *disp = lv_indev_get_display(indev_drv);
|
||||
assert(disp);
|
||||
if (lv_display_get_rotation(disp) == LV_DISPLAY_ROTATION_0 || lv_display_get_rotation(disp) == LV_DISPLAY_ROTATION_180) {
|
||||
width = lv_display_get_physical_horizontal_resolution(disp);
|
||||
height = lv_display_get_vertical_resolution(disp);
|
||||
} else {
|
||||
width = lv_display_get_vertical_resolution(disp);
|
||||
height = lv_display_get_physical_horizontal_resolution(disp);
|
||||
}
|
||||
|
||||
/* Screen borders */
|
||||
if (ctx->mouse.x < 0) {
|
||||
ctx->mouse.x = 0;
|
||||
} else if (ctx->mouse.x > width * ctx->mouse.sensitivity) {
|
||||
ctx->mouse.x = width * ctx->mouse.sensitivity;
|
||||
}
|
||||
if (ctx->mouse.y < 0) {
|
||||
ctx->mouse.y = 0;
|
||||
} else if (ctx->mouse.y > height * ctx->mouse.sensitivity) {
|
||||
ctx->mouse.y = height * ctx->mouse.sensitivity;
|
||||
}
|
||||
|
||||
/* Get coordinates by rotation with sensitivity */
|
||||
switch (lv_display_get_rotation(disp)) {
|
||||
case LV_DISPLAY_ROTATION_0:
|
||||
data->point.x = ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.y = ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
case LV_DISPLAY_ROTATION_90:
|
||||
data->point.y = width - ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.x = ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
case LV_DISPLAY_ROTATION_180:
|
||||
data->point.x = width - ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.y = height - ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
case LV_DISPLAY_ROTATION_270:
|
||||
data->point.y = ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.x = height - ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->mouse.left_button) {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_read_kb(lv_indev_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
assert(indev_drv);
|
||||
lvgl_port_usb_hid_ctx_t *ctx = (lvgl_port_usb_hid_ctx_t *)lv_indev_get_user_data(indev_drv);
|
||||
assert(ctx);
|
||||
|
||||
data->key = ctx->kb.last_key;
|
||||
if (ctx->kb.pressed) {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
ctx->kb.pressed = false;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
ctx->kb.last_key = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_callback(hid_host_device_handle_t hid_device_handle, const hid_host_driver_event_t event, void *arg)
|
||||
{
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = (lvgl_port_usb_hid_ctx_t *)arg;
|
||||
|
||||
const lvgl_port_usb_hid_event_t msg = {
|
||||
.hid_device_handle = hid_device_handle,
|
||||
.event = event,
|
||||
.arg = arg
|
||||
};
|
||||
|
||||
xQueueSend(hid_ctx->queue, &msg, 0);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
file(GLOB SOURCES "src/*.c*")
|
||||
file(GLOB HEADERS "src/*.h*")
|
||||
|
||||
add_library(lv_screenshot STATIC)
|
||||
|
||||
target_sources(lv_screenshot
|
||||
PRIVATE ${SOURCES}
|
||||
PUBLIC ${HEADERS}
|
||||
)
|
||||
|
||||
target_include_directories(lv_screenshot
|
||||
PRIVATE private
|
||||
PUBLIC src
|
||||
)
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
target_link_libraries(lv_screenshot
|
||||
PUBLIC idf::lvgl
|
||||
)
|
||||
else()
|
||||
target_link_libraries(lv_screenshot
|
||||
PUBLIC lvgl
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 深圳百问网科技有限公司(www.100ask.net)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,12 @@
|
||||
## lv_screenshot
|
||||
|
||||
This library is adapted from the lv_100ask_screenshot library from 100ask on [GitHub](https://github.com/100askTeam/lv_lib_100ask).
|
||||
|
||||
The original license is available [here](LICENSE-original).
|
||||
|
||||
## Features
|
||||
|
||||
- Save LVGL screen objects (full screen) as image files: lv_scr_act(),layer_sys(),layer_top()
|
||||
- Capture and save the specified LVGL object and its children as an image file
|
||||
- Supported save as: BMP, PNG, JPG
|
||||
- more todo...
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool lve_screenshot_save_bmp_file(const uint8_t* image, uint32_t w, uint32_t h, uint32_t bpp, const char* filename);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool lv_screenshot_save_png_file(const uint8_t* image, uint32_t w, uint32_t h, uint32_t bpp, const char* filename);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "lv_screenshot.h"
|
||||
|
||||
#include "save_bmp.h"
|
||||
#include "save_png.h"
|
||||
|
||||
static void data_pre_processing(lv_image_dsc_t* snapshot, uint16_t bpp, lv_100ask_screenshot_sv_t screenshot_sv);
|
||||
|
||||
bool lv_screenshot_create(lv_obj_t* obj, lv_color_format_t cf, lv_100ask_screenshot_sv_t screenshot_sv, const char* filename) {
|
||||
lv_image_dsc_t* snapshot = lv_snapshot_take(obj, cf);
|
||||
|
||||
if (snapshot) {
|
||||
data_pre_processing(snapshot, LV_COLOR_DEPTH, screenshot_sv);
|
||||
|
||||
if (screenshot_sv == LV_100ASK_SCREENSHOT_SV_PNG) {
|
||||
if (LV_COLOR_DEPTH == 16) {
|
||||
lv_screenshot_save_png_file(snapshot->data, snapshot->header.w, snapshot->header.h, 24, filename);
|
||||
} else if (LV_COLOR_DEPTH == 32) {
|
||||
lv_screenshot_save_png_file(snapshot->data, snapshot->header.w, snapshot->header.h, 32, filename);
|
||||
}
|
||||
} else if (screenshot_sv == LV_100ASK_SCREENSHOT_SV_BMP) {
|
||||
if (LV_COLOR_DEPTH == 16) {
|
||||
lve_screenshot_save_bmp_file(snapshot->data, snapshot->header.w, snapshot->header.h, 24, filename);
|
||||
} else if (LV_COLOR_DEPTH == 32) {
|
||||
lve_screenshot_save_bmp_file(snapshot->data, snapshot->header.w, snapshot->header.h, 32, filename);
|
||||
}
|
||||
}
|
||||
|
||||
lv_snapshot_free(snapshot);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void data_pre_processing(lv_image_dsc_t* snapshot, uint16_t bpp, lv_100ask_screenshot_sv_t screenshot_sv) {
|
||||
if (bpp == 16) {
|
||||
uint16_t rgb565_data = 0;
|
||||
uint32_t count = 0;
|
||||
for (int w = 0; w < snapshot->header.w; w++) {
|
||||
for (int h = 0; h < snapshot->header.h; h++) {
|
||||
rgb565_data = (uint16_t)((*(uint8_t*)(snapshot->data + count + 1) << 8) | *(uint8_t*)(snapshot->data + count));
|
||||
if (screenshot_sv == LV_100ASK_SCREENSHOT_SV_PNG) {
|
||||
*(uint8_t*)(snapshot->data + count) = (uint8_t)(((rgb565_data) >> 11) << 3);
|
||||
*(uint8_t*)(snapshot->data + count + 1) = (uint8_t)(((rgb565_data) >> 5) << 2);
|
||||
*(uint8_t*)(snapshot->data + count + 2) = (uint8_t)(((rgb565_data) >> 0) << 3);
|
||||
} else if (screenshot_sv == LV_100ASK_SCREENSHOT_SV_BMP) {
|
||||
*(uint8_t*)(snapshot->data + count) = (uint8_t)(((rgb565_data) >> 0) << 3);
|
||||
*(uint8_t*)(snapshot->data + count + 1) = (uint8_t)(((rgb565_data) >> 5) << 2);
|
||||
*(uint8_t*)(snapshot->data + count + 2) = (uint8_t)(((rgb565_data) >> 11) << 3);
|
||||
}
|
||||
|
||||
count += 3;
|
||||
}
|
||||
}
|
||||
} else if ((screenshot_sv == LV_100ASK_SCREENSHOT_SV_PNG) && (bpp == 32)) {
|
||||
uint8_t tmp_data = 0;
|
||||
uint32_t count = 0;
|
||||
for (int w = 0; w < snapshot->header.w; w++) {
|
||||
for (int h = 0; h < snapshot->header.h; h++) {
|
||||
tmp_data = *(snapshot->data + count);
|
||||
*(uint8_t*)(snapshot->data + count) = *(snapshot->data + count + 2);
|
||||
*(uint8_t*)(snapshot->data + count + 2) = tmp_data;
|
||||
count += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
LV_100ASK_SCREENSHOT_SV_BMP = 0,
|
||||
LV_100ASK_SCREENSHOT_SV_PNG = 1,
|
||||
LV_100ASK_SCREENSHOT_SV_LAST
|
||||
} lv_100ask_screenshot_sv_t;
|
||||
|
||||
bool lv_screenshot_create(lv_obj_t* obj, lv_color_format_t cf, lv_100ask_screenshot_sv_t screenshot_sv, const char* filename);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "save_bmp.h"
|
||||
|
||||
typedef struct tagBITMAPFILEHEADER {
|
||||
uint16_t bfType;
|
||||
uint32_t bfSize;
|
||||
uint16_t bfReserved1;
|
||||
uint16_t bfReserved2;
|
||||
uint32_t bfOffBits;
|
||||
} __attribute__((packed)) BITMAPFILEHEADER, *PBITMAPFILEHEADER;
|
||||
|
||||
typedef struct tagBITMAPINFOHEADER {
|
||||
uint32_t biSize;
|
||||
uint32_t biwidth;
|
||||
uint32_t biheight;
|
||||
uint16_t biPlanes;
|
||||
uint16_t biBitCount;
|
||||
uint32_t biCompression;
|
||||
uint32_t biSizeImage;
|
||||
uint32_t biXPelsPerMeter;
|
||||
uint32_t biYPelsPerMeter;
|
||||
uint32_t biClrUsed;
|
||||
uint32_t biClrImportant;
|
||||
} __attribute__((packed)) BITMAPINFOHEADER, *PBITMAPINFOHEADER;
|
||||
|
||||
typedef struct tagRGBQUAD {
|
||||
uint8_t rgbBlue;
|
||||
uint8_t rgbGreen;
|
||||
uint8_t rgbRed;
|
||||
uint8_t rgbReserved;
|
||||
} __attribute__((packed)) RGBQUAD;
|
||||
|
||||
bool lve_screenshot_save_bmp_file(const uint8_t* image, uint32_t w, uint32_t h, uint32_t bpp, const char* filename) {
|
||||
BITMAPFILEHEADER tBmpFileHead;
|
||||
BITMAPINFOHEADER tBmpInfoHead;
|
||||
|
||||
uint32_t dwSize;
|
||||
|
||||
uint32_t bw;
|
||||
lv_fs_file_t f;
|
||||
|
||||
memset(&tBmpFileHead, 0, sizeof(BITMAPFILEHEADER));
|
||||
memset(&tBmpInfoHead, 0, sizeof(BITMAPINFOHEADER));
|
||||
|
||||
lv_fs_res_t res = lv_fs_open(&f, filename, LV_FS_MODE_WR);
|
||||
if (res != LV_FS_RES_OK) {
|
||||
LV_LOG_USER("Can't create output file %s", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
tBmpFileHead.bfType = 0x4d42;
|
||||
tBmpFileHead.bfSize = 0x36 + w * h * (bpp / 8);
|
||||
tBmpFileHead.bfOffBits = 0x00000036;
|
||||
|
||||
tBmpInfoHead.biSize = 0x00000028;
|
||||
tBmpInfoHead.biwidth = w;
|
||||
tBmpInfoHead.biheight = h;
|
||||
tBmpInfoHead.biPlanes = 0x0001;
|
||||
tBmpInfoHead.biBitCount = bpp;
|
||||
tBmpInfoHead.biCompression = 0;
|
||||
tBmpInfoHead.biSizeImage = w * h * (bpp / 8);
|
||||
tBmpInfoHead.biXPelsPerMeter = 0;
|
||||
tBmpInfoHead.biYPelsPerMeter = 0;
|
||||
tBmpInfoHead.biClrUsed = 0;
|
||||
tBmpInfoHead.biClrImportant = 0;
|
||||
|
||||
res = lv_fs_write(&f, &tBmpFileHead, sizeof(tBmpFileHead), &bw);
|
||||
if (bw != sizeof(tBmpFileHead)) {
|
||||
LV_LOG_USER("Can't write BMP File Head to %s", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
res = lv_fs_write(&f, &tBmpInfoHead, sizeof(tBmpInfoHead), &bw);
|
||||
if (bw != sizeof(tBmpInfoHead)) {
|
||||
LV_LOG_USER("Can't write BMP File Info Head to %s", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
dwSize = w * bpp / 8;
|
||||
const uint8_t* pPos = image + (h - 1) * dwSize;
|
||||
|
||||
while (pPos >= image) {
|
||||
res = lv_fs_write(&f, pPos, dwSize, &bw);
|
||||
if (bw != dwSize) {
|
||||
LV_LOG_USER("Can't write date to BMP File %s", filename);
|
||||
return false;
|
||||
}
|
||||
pPos -= dwSize;
|
||||
}
|
||||
|
||||
lv_fs_close(&f);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "save_png.h"
|
||||
#include "src/libs/lodepng/lodepng.h"
|
||||
|
||||
bool lv_screenshot_save_png_file(const uint8_t* image, uint32_t w, uint32_t h, uint32_t bpp, const char* filename) {
|
||||
if (bpp == 32) {
|
||||
return lodepng_encode32_file(filename, image, w, h);
|
||||
} else if (bpp == 24) {
|
||||
return lodepng_encode24_file(filename, image, w, h);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Submodule
+1
Submodule Libraries/lvgl added at 09cb87cdc6
Submodule
+1
Submodule Libraries/mbedtls added at daca7a3979
Reference in New Issue
Block a user