feat: add ESP32-S3 ST7789 LCD and GT911 wiring test
This commit is contained in:
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
build/
|
||||||
|
managed_components/
|
||||||
|
sdkconfig
|
||||||
|
sdkconfig.old
|
||||||
|
tmp/
|
||||||
|
work/
|
||||||
|
.codex/
|
||||||
|
.agents/
|
||||||
|
.vscode/
|
||||||
6
CMakeLists.txt
Normal file
6
CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||||
|
idf_build_set_property(MINIMAL_BUILD ON)
|
||||||
|
project(touch-lcd)
|
||||||
100
README.md
Normal file
100
README.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# SPI LCD and Touch Panel Example
|
||||||
|
|
||||||
|
[esp_lcd](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html) provides several panel drivers out-of box, e.g. ST7789, SSD1306, NT35510. However, there're a lot of other panels on the market, it's beyond `esp_lcd` component's responsibility to include them all.
|
||||||
|
|
||||||
|
`esp_lcd` allows user to add their own panel drivers in the project scope (i.e. panel driver can live outside of esp-idf), so that the upper layer code like LVGL porting code can be reused without any modifications, as long as user-implemented panel driver follows the interface defined in the `esp_lcd` component.
|
||||||
|
|
||||||
|
This example shows how to use GC9A01 or ILI9341 display driver from Component manager in esp-idf project. These components are using API provided by `esp_lcd` component. This example will draw a fancy dash board with the LVGL library.
|
||||||
|
|
||||||
|
This example uses the [esp_timer](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_timer.html) to generate the ticks needed by LVGL and uses a dedicated task to run the `lv_timer_handler()`. Since the LVGL APIs are not thread-safe, this example uses a mutex which be invoked before the call of `lv_timer_handler()` and released after it. The same mutex needs to be used in other tasks and threads around every LVGL (lv_...) related function call and code. For more porting guides, please refer to [LVGL porting doc](https://docs.lvgl.io/master/porting/index.html).
|
||||||
|
|
||||||
|
## Touch controller STMPE610
|
||||||
|
|
||||||
|
In this example you can enable touch controller STMPE610 connected via SPI. The SPI connection is shared with LCD screen.
|
||||||
|
|
||||||
|
## How to use the example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
|
||||||
|
* An ESP development board
|
||||||
|
* An GC9A01 or ILI9341 LCD panel, with SPI interface (with/without STMPE610 SPI touch)
|
||||||
|
* An USB cable for power supply and programming
|
||||||
|
|
||||||
|
### Hardware Connection
|
||||||
|
|
||||||
|
The connection between ESP Board and the LCD is as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
ESP Board GC9A01/ILI9341 Panel + TOUCH
|
||||||
|
┌──────────────────────┐ ┌────────────────────┐
|
||||||
|
│ GND ├─────────────►│ GND │
|
||||||
|
│ │ │ │
|
||||||
|
│ 3V3 ├─────────────►│ VCC │
|
||||||
|
│ │ │ │
|
||||||
|
│ PCLK ├─────────────►│ SCL │
|
||||||
|
│ │ │ │
|
||||||
|
│ MOSI ├─────────────►│ MOSI │
|
||||||
|
│ │ │ │
|
||||||
|
│ MISO |◄─────────────┤ MISO │
|
||||||
|
│ │ │ │
|
||||||
|
│ RST ├─────────────►│ RES │
|
||||||
|
│ │ │ │
|
||||||
|
│ DC ├─────────────►│ DC │
|
||||||
|
│ │ │ │
|
||||||
|
│ LCD CS ├─────────────►│ LCD CS │
|
||||||
|
│ │ │ │
|
||||||
|
│ TOUCH CS ├─────────────►│ TOUCH CS │
|
||||||
|
│ │ │ │
|
||||||
|
│ BK_LIGHT ├─────────────►│ BLK │
|
||||||
|
└──────────────────────┘ └────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
The GPIO number used by this example can be changed in [lvgl_example_main.c](main/spi_lcd_touch_example_main.c).
|
||||||
|
Especially, please pay attention to the level used to turn on the LCD backlight, some LCD module needs a low level to turn it on, while others take a high level. You can change the backlight level macro `EXAMPLE_LCD_BK_LIGHT_ON_LEVEL` in [lvgl_example_main.c](main/spi_lcd_touch_example_main.c).
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project. A fancy animation will show up on the LCD as expected.
|
||||||
|
|
||||||
|
The first time you run `idf.py` for the example will cost extra time as the build system needs to address the component dependencies and downloads the missing components from the ESP Component Registry into `managed_components` folder.
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
### Example Output
|
||||||
|
|
||||||
|
```bash
|
||||||
|
...
|
||||||
|
I (409) cpu_start: Starting scheduler on APP CPU.
|
||||||
|
I (419) example: Turn off LCD backlight
|
||||||
|
I (419) gpio: GPIO[2]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||||
|
I (429) example: Initialize SPI bus
|
||||||
|
I (439) example: Install panel IO
|
||||||
|
I (439) gpio: GPIO[5]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||||
|
I (449) example: Install GC9A01 panel driver
|
||||||
|
I (459) gpio: GPIO[3]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||||
|
I (589) gpio: GPIO[0]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||||
|
I (589) example: Initialize touch controller STMPE610
|
||||||
|
I (589) STMPE610: TouchPad ID: 0x0811
|
||||||
|
I (589) STMPE610: TouchPad Ver: 0x03
|
||||||
|
I (599) example: Turn on LCD backlight
|
||||||
|
I (599) example: Initialize LVGL library
|
||||||
|
I (609) example: Register display driver to LVGL
|
||||||
|
I (619) example: Install LVGL tick timer
|
||||||
|
I (619) example: Starting LVGL task
|
||||||
|
I (619) example: Display LVGL animation
|
||||||
|
I (619) example: Display LVGL Meter Widget
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
* Why the LCD doesn't light up?
|
||||||
|
* Check the backlight's turn-on level, and update it in `EXAMPLE_LCD_BK_LIGHT_ON_LEVEL`
|
||||||
|
|
||||||
|
For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
||||||
112
dependencies.lock
Normal file
112
dependencies.lock
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
dependencies:
|
||||||
|
atanisoft/esp_lcd_touch_xpt2046:
|
||||||
|
component_hash: 94c1d199922bc7468b5e14912ab0eb053a4a5a25680ce8a4ca2f4f18febbb01a
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.4'
|
||||||
|
- name: espressif/esp_lcd_touch
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: private
|
||||||
|
version: '>=1.0.4'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.0.5
|
||||||
|
espressif/cmake_utilities:
|
||||||
|
component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.1'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
type: service
|
||||||
|
version: 0.5.3
|
||||||
|
espressif/esp_lcd_gc9a01:
|
||||||
|
component_hash: 2ccbdfa5a1762c7a8be66770b69a256d809651d6069098852d2c829648707b5b
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.4'
|
||||||
|
- name: espressif/cmake_utilities
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: private
|
||||||
|
version: 0.*
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.2.0
|
||||||
|
espressif/esp_lcd_ili9341:
|
||||||
|
component_hash: 31f1b793aa2110dd2ae071c21ccbff0a4eb20d9a4ee40b6294c0dc0ad9552c4e
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.4'
|
||||||
|
- name: espressif/cmake_utilities
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: private
|
||||||
|
version: 0.*
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.2.0
|
||||||
|
espressif/esp_lcd_touch:
|
||||||
|
component_hash: 3f85a7d95af876f1a6ecca8eb90a81614890d0f03a038390804e5a77e2caf862
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.4.2'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
type: service
|
||||||
|
version: 1.2.1
|
||||||
|
espressif/esp_lcd_touch_gt911:
|
||||||
|
component_hash: 664c544533b91497f29b41e045bb4374a0d4d002f9d8c67c7c1bf43fb4179e3f
|
||||||
|
dependencies:
|
||||||
|
- name: espressif/esp_lcd_touch
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: public
|
||||||
|
version: ^1.1.0
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=4.4.2'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.1.1
|
||||||
|
espressif/esp_lcd_touch_stmpe610:
|
||||||
|
component_hash: d8adce47eabe3f8caa23c5acf03cfefe78012628abd974673ccb9a577c3d738b
|
||||||
|
dependencies:
|
||||||
|
- name: espressif/esp_lcd_touch
|
||||||
|
registry_url: https://components.espressif.com
|
||||||
|
require: public
|
||||||
|
version: ^1.2.0
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=5.0'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 1.0.8
|
||||||
|
idf:
|
||||||
|
source:
|
||||||
|
type: idf
|
||||||
|
version: 5.5.4
|
||||||
|
lvgl/lvgl:
|
||||||
|
component_hash: 2409fdc84e9766d2a18afd84b051aef3f838348136cdb1c10ac2e8bcdb012bf6
|
||||||
|
dependencies: []
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 9.2.0
|
||||||
|
direct_dependencies:
|
||||||
|
- atanisoft/esp_lcd_touch_xpt2046
|
||||||
|
- espressif/esp_lcd_gc9a01
|
||||||
|
- espressif/esp_lcd_ili9341
|
||||||
|
- espressif/esp_lcd_touch_gt911
|
||||||
|
- espressif/esp_lcd_touch_stmpe610
|
||||||
|
- lvgl/lvgl
|
||||||
|
manifest_hash: 8cf9f661b2064254ff1e1958c26de271dfe5b3094fef2a6cf74d3f3f64c506f0
|
||||||
|
target: esp32s3
|
||||||
|
version: 2.0.0
|
||||||
82
main/2_rgb565.bin
Normal file
82
main/2_rgb565.bin
Normal file
File diff suppressed because one or more lines are too long
3
main/CMakeLists.txt
Normal file
3
main/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
idf_component_register(SRCS "spi_lcd_touch_example_main.c"
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
EMBED_FILES "2_rgb565.bin")
|
||||||
169
main/Kconfig.projbuild
Normal file
169
main/Kconfig.projbuild
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
menu "Example Configuration"
|
||||||
|
|
||||||
|
choice EXAMPLE_LCD_CONTROLLER
|
||||||
|
prompt "LCD controller model"
|
||||||
|
default EXAMPLE_LCD_CONTROLLER_ST7789
|
||||||
|
help
|
||||||
|
Select LCD controller model
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_CONTROLLER_ST7789
|
||||||
|
bool "ST7789"
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_CONTROLLER_ST7735
|
||||||
|
bool "ST7735"
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_CONTROLLER_ILI9341
|
||||||
|
bool "ILI9341"
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_CONTROLLER_GC9A01
|
||||||
|
bool "GC9A01"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_ST7735_H_RES
|
||||||
|
int "LCD horizontal resolution"
|
||||||
|
depends on EXAMPLE_LCD_CONTROLLER_ST7735 || EXAMPLE_LCD_CONTROLLER_ST7789
|
||||||
|
default 240 if EXAMPLE_LCD_CONTROLLER_ST7789
|
||||||
|
default 128 if EXAMPLE_LCD_CONTROLLER_ST7735
|
||||||
|
help
|
||||||
|
Horizontal active pixel count.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_ST7735_V_RES
|
||||||
|
int "LCD vertical resolution"
|
||||||
|
depends on EXAMPLE_LCD_CONTROLLER_ST7735 || EXAMPLE_LCD_CONTROLLER_ST7789
|
||||||
|
default 320 if EXAMPLE_LCD_CONTROLLER_ST7789
|
||||||
|
default 160 if EXAMPLE_LCD_CONTROLLER_ST7735
|
||||||
|
help
|
||||||
|
Vertical active pixel count.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_ST7735_X_GAP
|
||||||
|
int "LCD X offset"
|
||||||
|
depends on EXAMPLE_LCD_CONTROLLER_ST7735 || EXAMPLE_LCD_CONTROLLER_ST7789
|
||||||
|
default 0
|
||||||
|
help
|
||||||
|
Extra X offset used when the visible image is shifted.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_ST7735_Y_GAP
|
||||||
|
int "LCD Y offset"
|
||||||
|
depends on EXAMPLE_LCD_CONTROLLER_ST7735 || EXAMPLE_LCD_CONTROLLER_ST7789
|
||||||
|
default 0
|
||||||
|
help
|
||||||
|
Extra Y offset used when the visible image is shifted.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_ST7735_BGR_COLOR
|
||||||
|
bool "Use BGR color order"
|
||||||
|
depends on EXAMPLE_LCD_CONTROLLER_ST7735 || EXAMPLE_LCD_CONTROLLER_ST7789
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Disable this if red and blue are swapped on the panel.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_ST7735_INVERT_COLOR
|
||||||
|
bool "Invert LCD color"
|
||||||
|
depends on EXAMPLE_LCD_CONTROLLER_ST7735 || EXAMPLE_LCD_CONTROLLER_ST7789
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable this if the panel shows inverted colors.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_ST7735_MIRROR_X
|
||||||
|
bool "Mirror LCD X axis"
|
||||||
|
depends on EXAMPLE_LCD_CONTROLLER_ST7735 || EXAMPLE_LCD_CONTROLLER_ST7789
|
||||||
|
default n
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_ST7735_MIRROR_Y
|
||||||
|
bool "Mirror LCD Y axis"
|
||||||
|
depends on EXAMPLE_LCD_CONTROLLER_ST7735 || EXAMPLE_LCD_CONTROLLER_ST7789
|
||||||
|
default n
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_ENABLED
|
||||||
|
bool "Enable LCD touch"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable this option if you wish to use display touch. You can select from touch controllers.
|
||||||
|
|
||||||
|
choice EXAMPLE_LCD_TOUCH_CONTROLLER
|
||||||
|
prompt "LCD touch controller model"
|
||||||
|
depends on EXAMPLE_LCD_TOUCH_ENABLED
|
||||||
|
default EXAMPLE_LCD_TOUCH_CONTROLLER_STMPE610
|
||||||
|
help
|
||||||
|
Select LCD touch controller model
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_CONTROLLER_STMPE610
|
||||||
|
bool "STMPE610"
|
||||||
|
help
|
||||||
|
Touch controller STMPE610 connected via SPI.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_CONTROLLER_XPT2046
|
||||||
|
bool "XPT2046"
|
||||||
|
help
|
||||||
|
Touch controller XPT2046 connected via SPI.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||||
|
bool "GT911"
|
||||||
|
help
|
||||||
|
Touch controller GT911 connected via I2C.
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_MIRROR_Y
|
||||||
|
int
|
||||||
|
default 1 if EXAMPLE_LCD_TOUCH_ENABLED && EXAMPLE_LCD_TOUCH_CONTROLLER_XPT2046
|
||||||
|
default 0
|
||||||
|
help
|
||||||
|
This value is 1 if XPT2046 touch controller is selected, 0 otherwise.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_GT911_I2C_SDA
|
||||||
|
int "GT911 I2C SDA GPIO"
|
||||||
|
depends on EXAMPLE_LCD_TOUCH_ENABLED && EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||||
|
default 15
|
||||||
|
help
|
||||||
|
GPIO number for the GT911 I2C SDA signal.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_GT911_I2C_SCL
|
||||||
|
int "GT911 I2C SCL GPIO"
|
||||||
|
depends on EXAMPLE_LCD_TOUCH_ENABLED && EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||||
|
default 16
|
||||||
|
help
|
||||||
|
GPIO number for the GT911 I2C SCL signal.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_GT911_RST_GPIO
|
||||||
|
int "GT911 reset GPIO"
|
||||||
|
depends on EXAMPLE_LCD_TOUCH_ENABLED && EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||||
|
default 17
|
||||||
|
help
|
||||||
|
GPIO number for GT911 reset. Set to -1 if the pin is not connected.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_GT911_INT_GPIO
|
||||||
|
int "GT911 interrupt GPIO"
|
||||||
|
depends on EXAMPLE_LCD_TOUCH_ENABLED && EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||||
|
default 8
|
||||||
|
help
|
||||||
|
GPIO number for GT911 interrupt. Set to -1 if the pin is not connected.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_GT911_I2C_ADDR
|
||||||
|
hex "GT911 I2C address"
|
||||||
|
depends on EXAMPLE_LCD_TOUCH_ENABLED && EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||||
|
default 0x5D
|
||||||
|
help
|
||||||
|
GT911 address. Common values are 0x5D and 0x14.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_GT911_I2C_FREQ_HZ
|
||||||
|
int "GT911 I2C clock frequency"
|
||||||
|
depends on EXAMPLE_LCD_TOUCH_ENABLED && EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||||
|
default 400000
|
||||||
|
help
|
||||||
|
I2C clock frequency in Hz.
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_GT911_SWAP_XY
|
||||||
|
bool "Swap GT911 X/Y coordinates"
|
||||||
|
depends on EXAMPLE_LCD_TOUCH_ENABLED && EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||||
|
default n
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_GT911_MIRROR_X
|
||||||
|
bool "Mirror GT911 X coordinates"
|
||||||
|
depends on EXAMPLE_LCD_TOUCH_ENABLED && EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||||
|
default n
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_TOUCH_GT911_MIRROR_Y
|
||||||
|
bool "Mirror GT911 Y coordinates"
|
||||||
|
depends on EXAMPLE_LCD_TOUCH_ENABLED && EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
|
||||||
|
default n
|
||||||
|
|
||||||
|
endmenu
|
||||||
75
main/assets/2_rgb565.bin
Normal file
75
main/assets/2_rgb565.bin
Normal file
File diff suppressed because one or more lines are too long
319
main/esp_lcd_st7735.c
Normal file
319
main/esp_lcd_st7735.c
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2026
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_lcd_panel_commands.h"
|
||||||
|
#include "esp_lcd_panel_interface.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_lcd_panel_ops.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "esp_lcd_st7735.h"
|
||||||
|
|
||||||
|
#define ST7735_CMD_FRMCTR1 0xB1
|
||||||
|
#define ST7735_CMD_FRMCTR2 0xB2
|
||||||
|
#define ST7735_CMD_FRMCTR3 0xB3
|
||||||
|
#define ST7735_CMD_INVCTR 0xB4
|
||||||
|
#define ST7735_CMD_PWCTR1 0xC0
|
||||||
|
#define ST7735_CMD_PWCTR2 0xC1
|
||||||
|
#define ST7735_CMD_PWCTR3 0xC2
|
||||||
|
#define ST7735_CMD_PWCTR4 0xC3
|
||||||
|
#define ST7735_CMD_PWCTR5 0xC4
|
||||||
|
#define ST7735_CMD_VMCTR1 0xC5
|
||||||
|
#define ST7735_CMD_GMCTRP1 0xE0
|
||||||
|
#define ST7735_CMD_GMCTRN1 0xE1
|
||||||
|
|
||||||
|
static const char *TAG = "st7735";
|
||||||
|
|
||||||
|
static esp_err_t panel_st7735_del(esp_lcd_panel_t *panel);
|
||||||
|
static esp_err_t panel_st7735_reset(esp_lcd_panel_t *panel);
|
||||||
|
static esp_err_t panel_st7735_init(esp_lcd_panel_t *panel);
|
||||||
|
static esp_err_t panel_st7735_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end,
|
||||||
|
const void *color_data);
|
||||||
|
static esp_err_t panel_st7735_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
|
||||||
|
static esp_err_t panel_st7735_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
|
||||||
|
static esp_err_t panel_st7735_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
|
||||||
|
static esp_err_t panel_st7735_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
|
||||||
|
static esp_err_t panel_st7735_disp_on_off(esp_lcd_panel_t *panel, bool on_off);
|
||||||
|
static esp_err_t panel_st7735_sleep(esp_lcd_panel_t *panel, bool sleep);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t cmd;
|
||||||
|
const void *data;
|
||||||
|
size_t data_bytes;
|
||||||
|
unsigned int delay_ms;
|
||||||
|
} st7735_lcd_init_cmd_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_lcd_panel_t base;
|
||||||
|
esp_lcd_panel_io_handle_t io;
|
||||||
|
int reset_gpio_num;
|
||||||
|
bool reset_level;
|
||||||
|
int x_gap;
|
||||||
|
int y_gap;
|
||||||
|
uint8_t fb_bits_per_pixel;
|
||||||
|
uint8_t madctl_val;
|
||||||
|
uint8_t colmod_val;
|
||||||
|
} st7735_panel_t;
|
||||||
|
|
||||||
|
static const st7735_lcd_init_cmd_t vendor_specific_init_default[] = {
|
||||||
|
{ST7735_CMD_FRMCTR1, (uint8_t []){0x01, 0x2C, 0x2D}, 3, 0},
|
||||||
|
{ST7735_CMD_FRMCTR2, (uint8_t []){0x01, 0x2C, 0x2D}, 3, 0},
|
||||||
|
{ST7735_CMD_FRMCTR3, (uint8_t []){0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D}, 6, 0},
|
||||||
|
{ST7735_CMD_INVCTR, (uint8_t []){0x07}, 1, 0},
|
||||||
|
{ST7735_CMD_PWCTR1, (uint8_t []){0xA2, 0x02, 0x84}, 3, 0},
|
||||||
|
{ST7735_CMD_PWCTR2, (uint8_t []){0xC5}, 1, 0},
|
||||||
|
{ST7735_CMD_PWCTR3, (uint8_t []){0x0A, 0x00}, 2, 0},
|
||||||
|
{ST7735_CMD_PWCTR4, (uint8_t []){0x8A, 0x2A}, 2, 0},
|
||||||
|
{ST7735_CMD_PWCTR5, (uint8_t []){0x8A, 0xEE}, 2, 0},
|
||||||
|
{ST7735_CMD_VMCTR1, (uint8_t []){0x0E}, 1, 0},
|
||||||
|
{ST7735_CMD_GMCTRP1, (uint8_t []){0x02, 0x1C, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2D,
|
||||||
|
0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10}, 16, 0},
|
||||||
|
{ST7735_CMD_GMCTRN1, (uint8_t []){0x03, 0x1D, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D,
|
||||||
|
0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10}, 16, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_new_panel_st7735(const esp_lcd_panel_io_handle_t io,
|
||||||
|
const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||||
|
esp_lcd_panel_handle_t *ret_panel)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
st7735_panel_t *st7735 = NULL;
|
||||||
|
|
||||||
|
ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||||
|
st7735 = calloc(1, sizeof(st7735_panel_t));
|
||||||
|
ESP_GOTO_ON_FALSE(st7735, ESP_ERR_NO_MEM, err, TAG, "no mem for st7735 panel");
|
||||||
|
|
||||||
|
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||||
|
gpio_config_t io_conf = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (panel_dev_config->rgb_ele_order) {
|
||||||
|
case LCD_RGB_ELEMENT_ORDER_RGB:
|
||||||
|
st7735->madctl_val = 0;
|
||||||
|
break;
|
||||||
|
case LCD_RGB_ELEMENT_ORDER_BGR:
|
||||||
|
st7735->madctl_val = LCD_CMD_BGR_BIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported RGB element order");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (panel_dev_config->bits_per_pixel) {
|
||||||
|
case 16:
|
||||||
|
st7735->colmod_val = 0x05;
|
||||||
|
st7735->fb_bits_per_pixel = 16;
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
st7735->colmod_val = 0x06;
|
||||||
|
st7735->fb_bits_per_pixel = 24;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
st7735->io = io;
|
||||||
|
st7735->reset_gpio_num = panel_dev_config->reset_gpio_num;
|
||||||
|
st7735->reset_level = panel_dev_config->flags.reset_active_high;
|
||||||
|
st7735->base.del = panel_st7735_del;
|
||||||
|
st7735->base.reset = panel_st7735_reset;
|
||||||
|
st7735->base.init = panel_st7735_init;
|
||||||
|
st7735->base.draw_bitmap = panel_st7735_draw_bitmap;
|
||||||
|
st7735->base.invert_color = panel_st7735_invert_color;
|
||||||
|
st7735->base.set_gap = panel_st7735_set_gap;
|
||||||
|
st7735->base.mirror = panel_st7735_mirror;
|
||||||
|
st7735->base.swap_xy = panel_st7735_swap_xy;
|
||||||
|
st7735->base.disp_on_off = panel_st7735_disp_on_off;
|
||||||
|
st7735->base.disp_sleep = panel_st7735_sleep;
|
||||||
|
|
||||||
|
*ret_panel = &(st7735->base);
|
||||||
|
ESP_LOGD(TAG, "new st7735 panel @%p", st7735);
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (st7735) {
|
||||||
|
if (panel_dev_config->reset_gpio_num >= 0) {
|
||||||
|
gpio_reset_pin(panel_dev_config->reset_gpio_num);
|
||||||
|
}
|
||||||
|
free(st7735);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_st7735_del(esp_lcd_panel_t *panel)
|
||||||
|
{
|
||||||
|
st7735_panel_t *st7735 = __containerof(panel, st7735_panel_t, base);
|
||||||
|
|
||||||
|
if (st7735->reset_gpio_num >= 0) {
|
||||||
|
gpio_reset_pin(st7735->reset_gpio_num);
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "del st7735 panel @%p", st7735);
|
||||||
|
free(st7735);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_st7735_reset(esp_lcd_panel_t *panel)
|
||||||
|
{
|
||||||
|
st7735_panel_t *st7735 = __containerof(panel, st7735_panel_t, base);
|
||||||
|
esp_lcd_panel_io_handle_t io = st7735->io;
|
||||||
|
|
||||||
|
if (st7735->reset_gpio_num >= 0) {
|
||||||
|
gpio_set_level(st7735->reset_gpio_num, st7735->reset_level);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
gpio_set_level(st7735->reset_gpio_num, !st7735->reset_level);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(120));
|
||||||
|
} else {
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(120));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_st7735_init(esp_lcd_panel_t *panel)
|
||||||
|
{
|
||||||
|
st7735_panel_t *st7735 = __containerof(panel, st7735_panel_t, base);
|
||||||
|
esp_lcd_panel_io_handle_t io = st7735->io;
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0), TAG, "send command failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(120));
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []) {
|
||||||
|
st7735->madctl_val,
|
||||||
|
}, 1), TAG, "send command failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t []) {
|
||||||
|
st7735->colmod_val,
|
||||||
|
}, 1), TAG, "send command failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
|
||||||
|
for (int i = 0; i < sizeof(vendor_specific_init_default) / sizeof(vendor_specific_init_default[0]); i++) {
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, vendor_specific_init_default[i].cmd,
|
||||||
|
vendor_specific_init_default[i].data,
|
||||||
|
vendor_specific_init_default[i].data_bytes),
|
||||||
|
TAG, "send command failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(vendor_specific_init_default[i].delay_ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_NORON, NULL, 0), TAG, "send command failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_st7735_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end,
|
||||||
|
const void *color_data)
|
||||||
|
{
|
||||||
|
st7735_panel_t *st7735 = __containerof(panel, st7735_panel_t, base);
|
||||||
|
assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position");
|
||||||
|
esp_lcd_panel_io_handle_t io = st7735->io;
|
||||||
|
|
||||||
|
x_start += st7735->x_gap;
|
||||||
|
x_end += st7735->x_gap;
|
||||||
|
y_start += st7735->y_gap;
|
||||||
|
y_end += st7735->y_gap;
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t []) {
|
||||||
|
(x_start >> 8) & 0xFF,
|
||||||
|
x_start & 0xFF,
|
||||||
|
((x_end - 1) >> 8) & 0xFF,
|
||||||
|
(x_end - 1) & 0xFF,
|
||||||
|
}, 4), TAG, "send command failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t []) {
|
||||||
|
(y_start >> 8) & 0xFF,
|
||||||
|
y_start & 0xFF,
|
||||||
|
((y_end - 1) >> 8) & 0xFF,
|
||||||
|
(y_end - 1) & 0xFF,
|
||||||
|
}, 4), TAG, "send command failed");
|
||||||
|
|
||||||
|
size_t len = (x_end - x_start) * (y_end - y_start) * st7735->fb_bits_per_pixel / 8;
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len), TAG, "send color failed");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_st7735_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
|
||||||
|
{
|
||||||
|
st7735_panel_t *st7735 = __containerof(panel, st7735_panel_t, base);
|
||||||
|
int command = invert_color_data ? LCD_CMD_INVON : LCD_CMD_INVOFF;
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(st7735->io, command, NULL, 0), TAG, "send command failed");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_st7735_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||||
|
{
|
||||||
|
st7735_panel_t *st7735 = __containerof(panel, st7735_panel_t, base);
|
||||||
|
|
||||||
|
if (mirror_x) {
|
||||||
|
st7735->madctl_val |= LCD_CMD_MX_BIT;
|
||||||
|
} else {
|
||||||
|
st7735->madctl_val &= ~LCD_CMD_MX_BIT;
|
||||||
|
}
|
||||||
|
if (mirror_y) {
|
||||||
|
st7735->madctl_val |= LCD_CMD_MY_BIT;
|
||||||
|
} else {
|
||||||
|
st7735->madctl_val &= ~LCD_CMD_MY_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(st7735->io, LCD_CMD_MADCTL, (uint8_t []) {
|
||||||
|
st7735->madctl_val,
|
||||||
|
}, 1), TAG, "send command failed");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_st7735_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
|
||||||
|
{
|
||||||
|
st7735_panel_t *st7735 = __containerof(panel, st7735_panel_t, base);
|
||||||
|
|
||||||
|
if (swap_axes) {
|
||||||
|
st7735->madctl_val |= LCD_CMD_MV_BIT;
|
||||||
|
} else {
|
||||||
|
st7735->madctl_val &= ~LCD_CMD_MV_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(st7735->io, LCD_CMD_MADCTL, (uint8_t []) {
|
||||||
|
st7735->madctl_val,
|
||||||
|
}, 1), TAG, "send command failed");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_st7735_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
|
||||||
|
{
|
||||||
|
st7735_panel_t *st7735 = __containerof(panel, st7735_panel_t, base);
|
||||||
|
|
||||||
|
st7735->x_gap = x_gap;
|
||||||
|
st7735->y_gap = y_gap;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_st7735_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
|
||||||
|
{
|
||||||
|
st7735_panel_t *st7735 = __containerof(panel, st7735_panel_t, base);
|
||||||
|
int command = on_off ? LCD_CMD_DISPON : LCD_CMD_DISPOFF;
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(st7735->io, command, NULL, 0), TAG, "send command failed");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t panel_st7735_sleep(esp_lcd_panel_t *panel, bool sleep)
|
||||||
|
{
|
||||||
|
st7735_panel_t *st7735 = __containerof(panel, st7735_panel_t, base);
|
||||||
|
int command = sleep ? LCD_CMD_SLPIN : LCD_CMD_SLPOUT;
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(st7735->io, command, NULL, 0), TAG, "send command failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(120));
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
34
main/esp_lcd_st7735.h
Normal file
34
main/esp_lcd_st7735.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2026
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_lcd_panel_vendor.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create LCD panel for ST7735.
|
||||||
|
*
|
||||||
|
* @param[in] io LCD panel IO handle
|
||||||
|
* @param[in] panel_dev_config general panel device configuration
|
||||||
|
* @param[out] ret_panel returned LCD panel handle
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_ARG if parameters are invalid
|
||||||
|
* - ESP_ERR_NO_MEM if out of memory
|
||||||
|
* - ESP_ERR_NOT_SUPPORTED if the requested pixel format is unsupported
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_new_panel_st7735(const esp_lcd_panel_io_handle_t io,
|
||||||
|
const esp_lcd_panel_dev_config_t *panel_dev_config,
|
||||||
|
esp_lcd_panel_handle_t *ret_panel);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
7
main/idf_component.yml
Normal file
7
main/idf_component.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
dependencies:
|
||||||
|
lvgl/lvgl: 9.2.0
|
||||||
|
esp_lcd_ili9341: ^1.0
|
||||||
|
esp_lcd_gc9a01: ^1.0
|
||||||
|
esp_lcd_touch_stmpe610: ^1.0
|
||||||
|
atanisoft/esp_lcd_touch_xpt2046: 1.0.5
|
||||||
|
espressif/esp_lcd_touch_gt911: ==1.1.1
|
||||||
56
main/lvgl_demo_ui.c
Normal file
56
main/lvgl_demo_ui.c
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This demo UI is adapted from LVGL official example: https://docs.lvgl.io/master/examples.html#loader-with-arc
|
||||||
|
|
||||||
|
#include "lvgl.h"
|
||||||
|
|
||||||
|
static lv_obj_t * btn;
|
||||||
|
static lv_display_rotation_t rotation = LV_DISP_ROTATION_0;
|
||||||
|
|
||||||
|
static void btn_cb(lv_event_t * e)
|
||||||
|
{
|
||||||
|
lv_display_t *disp = lv_event_get_user_data(e);
|
||||||
|
rotation++;
|
||||||
|
if (rotation > LV_DISP_ROTATION_270) {
|
||||||
|
rotation = LV_DISP_ROTATION_0;
|
||||||
|
}
|
||||||
|
lv_disp_set_rotation(disp, rotation);
|
||||||
|
}
|
||||||
|
static void set_angle(void * obj, int32_t v)
|
||||||
|
{
|
||||||
|
lv_arc_set_value(obj, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void example_lvgl_demo_ui(lv_display_t *disp)
|
||||||
|
{
|
||||||
|
lv_obj_t *scr = lv_display_get_screen_active(disp);
|
||||||
|
|
||||||
|
btn = lv_button_create(scr);
|
||||||
|
lv_obj_t * lbl = lv_label_create(btn);
|
||||||
|
lv_label_set_text_static(lbl, LV_SYMBOL_REFRESH" ROTATE");
|
||||||
|
lv_obj_align(btn, LV_ALIGN_BOTTOM_LEFT, 30, -30);
|
||||||
|
/*Button event*/
|
||||||
|
lv_obj_add_event_cb(btn, btn_cb, LV_EVENT_CLICKED, disp);
|
||||||
|
|
||||||
|
/*Create an Arc*/
|
||||||
|
lv_obj_t * arc = lv_arc_create(scr);
|
||||||
|
lv_arc_set_rotation(arc, 270);
|
||||||
|
lv_arc_set_bg_angles(arc, 0, 360);
|
||||||
|
lv_obj_remove_style(arc, NULL, LV_PART_KNOB); /*Be sure the knob is not displayed*/
|
||||||
|
lv_obj_remove_flag(arc, LV_OBJ_FLAG_CLICKABLE); /*To not allow adjusting by click*/
|
||||||
|
lv_obj_center(arc);
|
||||||
|
|
||||||
|
lv_anim_t a;
|
||||||
|
lv_anim_init(&a);
|
||||||
|
lv_anim_set_var(&a, arc);
|
||||||
|
lv_anim_set_exec_cb(&a, set_angle);
|
||||||
|
lv_anim_set_duration(&a, 1000);
|
||||||
|
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE); /*Just for the demo*/
|
||||||
|
lv_anim_set_repeat_delay(&a, 500);
|
||||||
|
lv_anim_set_values(&a, 0, 100);
|
||||||
|
lv_anim_start(&a);
|
||||||
|
}
|
||||||
618
main/spi_lcd_touch_example_main.c
Normal file
618
main/spi_lcd_touch_example_main.c
Normal file
@@ -0,0 +1,618 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "driver/spi_master.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
#include "esp_lcd_io_i2c.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_lcd_panel_ops.h"
|
||||||
|
#include "esp_lcd_panel_vendor.h"
|
||||||
|
#include "esp_lcd_touch.h"
|
||||||
|
#include "esp_lcd_touch_gt911.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "lvgl.h"
|
||||||
|
#include "demos/lv_demos.h"
|
||||||
|
|
||||||
|
static const char *TAG = "lvgl_touch";
|
||||||
|
|
||||||
|
#define LCD_SPI_HOST SPI2_HOST
|
||||||
|
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (10 * 1000 * 1000)
|
||||||
|
#define EXAMPLE_LCD_CMD_BITS 8
|
||||||
|
#define EXAMPLE_LCD_PARAM_BITS 8
|
||||||
|
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
|
||||||
|
#define EXAMPLE_LVGL_BUFFER_LINES 80
|
||||||
|
#define EXAMPLE_TOUCH_I2C_FREQ_HZ (100 * 1000)
|
||||||
|
#define EXAMPLE_TOUCH_LOG_PERIOD_MS 120
|
||||||
|
#define EXAMPLE_TOUCH_PROBE_TIMEOUT_MS 30
|
||||||
|
|
||||||
|
// Current adapter wiring:
|
||||||
|
// LCD: CLK->IO6, MOSI/SDA->IO7, RES->IO15, DC->IO16, CS->IO5, BLK->IO17.
|
||||||
|
// MISO is not required for LCD write-only testing.
|
||||||
|
// GT911 touch: T_SCL->IO4, T_SDA->IO18, T_INT->IO8, T_RES->IO9.
|
||||||
|
#define EXAMPLE_PIN_NUM_SCLK 6
|
||||||
|
#define EXAMPLE_PIN_NUM_MOSI 7
|
||||||
|
#define EXAMPLE_PIN_NUM_MISO GPIO_NUM_NC
|
||||||
|
#define EXAMPLE_PIN_NUM_LCD_CS 5
|
||||||
|
#define EXAMPLE_PIN_NUM_LCD_DC 16
|
||||||
|
#define EXAMPLE_PIN_NUM_LCD_RST 15
|
||||||
|
#define EXAMPLE_PIN_NUM_BK_LIGHT 17
|
||||||
|
#define EXAMPLE_PIN_NUM_TOUCH_SCL 4
|
||||||
|
#define EXAMPLE_PIN_NUM_TOUCH_SDA 18
|
||||||
|
#define EXAMPLE_PIN_NUM_TOUCH_IRQ 8
|
||||||
|
#define EXAMPLE_PIN_NUM_TOUCH_RST 9
|
||||||
|
#define EXAMPLE_LCD_H_RES CONFIG_EXAMPLE_LCD_ST7735_H_RES
|
||||||
|
#define EXAMPLE_LCD_V_RES CONFIG_EXAMPLE_LCD_ST7735_V_RES
|
||||||
|
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1
|
||||||
|
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
|
||||||
|
|
||||||
|
static esp_lcd_panel_handle_t s_panel;
|
||||||
|
static esp_lcd_touch_handle_t s_touch;
|
||||||
|
static i2c_master_bus_handle_t s_i2c_bus;
|
||||||
|
static uint8_t *s_image_buffer;
|
||||||
|
|
||||||
|
extern const uint8_t image_rgb565_start[] asm("_binary_2_rgb565_bin_start");
|
||||||
|
extern const uint8_t image_rgb565_end[] asm("_binary_2_rgb565_bin_end");
|
||||||
|
static lv_obj_t *s_coord_label;
|
||||||
|
static lv_obj_t *s_status_label;
|
||||||
|
#if !LV_USE_DEMO_WIDGETS
|
||||||
|
static lv_obj_t *s_button_label;
|
||||||
|
static lv_obj_t *s_slider_label;
|
||||||
|
static int s_button_count;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata,
|
||||||
|
void *user_ctx)
|
||||||
|
{
|
||||||
|
(void)panel_io;
|
||||||
|
(void)edata;
|
||||||
|
lv_display_flush_ready((lv_display_t *)user_ctx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
|
||||||
|
{
|
||||||
|
esp_err_t ret = esp_lcd_panel_draw_bitmap(s_panel, area->x1, area->y1, area->x2 + 1, area->y2 + 1, px_map);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "LCD flush failed: %s", esp_err_to_name(ret));
|
||||||
|
lv_display_flush_ready(disp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lvgl_touch_read_cb(lv_indev_t *indev, lv_indev_data_t *data)
|
||||||
|
{
|
||||||
|
(void)indev;
|
||||||
|
esp_lcd_touch_point_data_t point[CONFIG_ESP_LCD_TOUCH_MAX_POINTS];
|
||||||
|
uint8_t point_num = 0;
|
||||||
|
static bool was_pressed = false;
|
||||||
|
static int64_t last_log_ms = 0;
|
||||||
|
static uint8_t last_point_num = 0;
|
||||||
|
|
||||||
|
if (!s_touch) {
|
||||||
|
data->state = LV_INDEV_STATE_RELEASED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t ret = esp_lcd_touch_read_data(s_touch);
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
ret = esp_lcd_touch_get_data(s_touch, point, &point_num, CONFIG_ESP_LCD_TOUCH_MAX_POINTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == ESP_OK && point_num > 0) {
|
||||||
|
data->state = LV_INDEV_STATE_PRESSED;
|
||||||
|
data->point.x = point[0].x;
|
||||||
|
data->point.y = point[0].y;
|
||||||
|
|
||||||
|
int64_t now_ms = esp_timer_get_time() / 1000;
|
||||||
|
if (!was_pressed || point_num != last_point_num || now_ms - last_log_ms >= EXAMPLE_TOUCH_LOG_PERIOD_MS) {
|
||||||
|
ESP_LOGI(TAG, "gt911 points=%u p0=(%u,%u,%u)", point_num, point[0].x, point[0].y,
|
||||||
|
point[0].strength);
|
||||||
|
for (uint8_t i = 1; i < point_num; i++) {
|
||||||
|
ESP_LOGI(TAG, " p%u=(%u,%u,%u)", i, point[i].x, point[i].y, point[i].strength);
|
||||||
|
}
|
||||||
|
if (s_coord_label) {
|
||||||
|
lv_label_set_text_fmt(s_coord_label, "x:%u y:%u z:%u", point[0].x, point[0].y, point[0].strength);
|
||||||
|
}
|
||||||
|
if (s_status_label) {
|
||||||
|
lv_label_set_text(s_status_label, "PRESSED");
|
||||||
|
}
|
||||||
|
last_log_ms = now_ms;
|
||||||
|
}
|
||||||
|
was_pressed = true;
|
||||||
|
last_point_num = point_num;
|
||||||
|
} else {
|
||||||
|
data->state = LV_INDEV_STATE_RELEASED;
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "touch read failed: %s", esp_err_to_name(ret));
|
||||||
|
} else if (was_pressed) {
|
||||||
|
ESP_LOGI(TAG, "released");
|
||||||
|
if (s_status_label) {
|
||||||
|
lv_label_set_text(s_status_label, "RELEASED");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
was_pressed = false;
|
||||||
|
last_point_num = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lvgl_tick_cb(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lvgl_task(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
while (1) {
|
||||||
|
uint32_t delay_ms = lv_timer_handler();
|
||||||
|
if (delay_ms < 2) {
|
||||||
|
delay_ms = 2;
|
||||||
|
} else if (delay_ms > 8) {
|
||||||
|
delay_ms = 8;
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !LV_USE_DEMO_WIDGETS
|
||||||
|
static void button_event_cb(lv_event_t *e)
|
||||||
|
{
|
||||||
|
if (lv_event_get_code(e) == LV_EVENT_CLICKED) {
|
||||||
|
s_button_count++;
|
||||||
|
ESP_LOGI(TAG, "LVGL button clicked: %d", s_button_count);
|
||||||
|
lv_label_set_text_fmt(s_button_label, "Button: %d", s_button_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void slider_event_cb(lv_event_t *e)
|
||||||
|
{
|
||||||
|
lv_obj_t *slider = lv_event_get_target(e);
|
||||||
|
int value = (int)lv_slider_get_value(slider);
|
||||||
|
lv_label_set_text_fmt(s_slider_label, "Slider: %d", value);
|
||||||
|
ESP_LOGI(TAG, "LVGL slider value: %d", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void create_touch_ui(void)
|
||||||
|
{
|
||||||
|
lv_obj_t *screen = lv_screen_active();
|
||||||
|
lv_obj_set_style_bg_color(screen, lv_color_hex(0x101820), 0);
|
||||||
|
|
||||||
|
lv_obj_t *title = lv_label_create(screen);
|
||||||
|
lv_label_set_text(title, "ST7789 + GT911 Touch");
|
||||||
|
lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0);
|
||||||
|
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 12);
|
||||||
|
|
||||||
|
s_status_label = lv_label_create(screen);
|
||||||
|
lv_label_set_text(s_status_label, "RELEASED");
|
||||||
|
lv_obj_set_style_text_color(s_status_label, lv_color_hex(0x64D2FF), 0);
|
||||||
|
lv_obj_align(s_status_label, LV_ALIGN_TOP_MID, 0, 42);
|
||||||
|
|
||||||
|
s_coord_label = lv_label_create(screen);
|
||||||
|
lv_label_set_text(s_coord_label, "x:-- y:-- z:--");
|
||||||
|
lv_obj_set_style_text_color(s_coord_label, lv_color_hex(0xE9F1F7), 0);
|
||||||
|
lv_obj_align(s_coord_label, LV_ALIGN_TOP_MID, 0, 70);
|
||||||
|
|
||||||
|
lv_obj_t *button = lv_button_create(screen);
|
||||||
|
lv_obj_set_size(button, 160, 52);
|
||||||
|
lv_obj_align(button, LV_ALIGN_CENTER, 0, -20);
|
||||||
|
lv_obj_add_event_cb(button, button_event_cb, LV_EVENT_CLICKED, NULL);
|
||||||
|
s_button_label = lv_label_create(button);
|
||||||
|
lv_label_set_text(s_button_label, "Button: 0");
|
||||||
|
lv_obj_center(s_button_label);
|
||||||
|
|
||||||
|
lv_obj_t *slider = lv_slider_create(screen);
|
||||||
|
lv_obj_set_width(slider, 190);
|
||||||
|
lv_slider_set_range(slider, 0, 100);
|
||||||
|
lv_slider_set_value(slider, 50, LV_ANIM_OFF);
|
||||||
|
lv_obj_align(slider, LV_ALIGN_CENTER, 0, 70);
|
||||||
|
lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
|
||||||
|
|
||||||
|
s_slider_label = lv_label_create(screen);
|
||||||
|
lv_label_set_text(s_slider_label, "Slider: 50");
|
||||||
|
lv_obj_set_style_text_color(s_slider_label, lv_color_hex(0xE9F1F7), 0);
|
||||||
|
lv_obj_align_to(s_slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 12);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static esp_err_t init_spi_bus(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Initialize SPI bus");
|
||||||
|
spi_bus_config_t buscfg = {
|
||||||
|
.sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
|
||||||
|
.mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
|
||||||
|
.miso_io_num = EXAMPLE_PIN_NUM_MISO,
|
||||||
|
.quadwp_io_num = -1,
|
||||||
|
.quadhd_io_num = -1,
|
||||||
|
.max_transfer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LVGL_BUFFER_LINES * sizeof(lv_color_t),
|
||||||
|
};
|
||||||
|
return spi_bus_initialize(LCD_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void verify_lcd_reset_released(void)
|
||||||
|
{
|
||||||
|
// The ST7789 driver configures RST as output-only. Enable the input path so
|
||||||
|
// gpio_get_level() reports the physical voltage instead of a disabled input.
|
||||||
|
esp_err_t ret = gpio_set_direction(EXAMPLE_PIN_NUM_LCD_RST, GPIO_MODE_INPUT_OUTPUT);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "LCD RES IO%d input enable failed: %s", EXAMPLE_PIN_NUM_LCD_RST,
|
||||||
|
esp_err_to_name(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpio_set_level(EXAMPLE_PIN_NUM_LCD_RST, 1);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "LCD RES IO%d release failed: %s", EXAMPLE_PIN_NUM_LCD_RST,
|
||||||
|
esp_err_to_name(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2));
|
||||||
|
int level = gpio_get_level(EXAMPLE_PIN_NUM_LCD_RST);
|
||||||
|
ESP_LOGI(TAG, "LCD RES IO%d readback after reset: %d", EXAMPLE_PIN_NUM_LCD_RST, level);
|
||||||
|
if (level != 1) {
|
||||||
|
ESP_LOGE(TAG, "LCD RES is held low externally; check the adapter, FPC, and RES wiring");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t init_st7789_panel(esp_lcd_panel_io_handle_t *ret_io)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Initialize ST7789 panel");
|
||||||
|
|
||||||
|
gpio_config_t bk_gpio_config = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT,
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(gpio_config(&bk_gpio_config), TAG, "configure backlight GPIO failed");
|
||||||
|
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);
|
||||||
|
|
||||||
|
esp_lcd_panel_io_spi_config_t io_config = {
|
||||||
|
.dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,
|
||||||
|
.cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,
|
||||||
|
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
|
||||||
|
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
|
||||||
|
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
|
||||||
|
.spi_mode = 0,
|
||||||
|
.trans_queue_depth = 10,
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_HOST, &io_config, ret_io),
|
||||||
|
TAG, "install panel IO failed");
|
||||||
|
|
||||||
|
esp_lcd_panel_dev_config_t panel_config = {
|
||||||
|
.reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,
|
||||||
|
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
|
||||||
|
.data_endian = LCD_RGB_DATA_ENDIAN_BIG,
|
||||||
|
.bits_per_pixel = 16,
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_st7789(*ret_io, &panel_config, &s_panel), TAG,
|
||||||
|
"install ST7789 panel failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_reset(s_panel), TAG, "reset panel failed");
|
||||||
|
verify_lcd_reset_released();
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_init(s_panel), TAG, "init panel failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_set_gap(s_panel, CONFIG_EXAMPLE_LCD_ST7735_X_GAP,
|
||||||
|
CONFIG_EXAMPLE_LCD_ST7735_Y_GAP),
|
||||||
|
TAG, "set panel gap failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_disp_on_off(s_panel, true), TAG, "turn panel on failed");
|
||||||
|
|
||||||
|
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t init_display_buffer(void)
|
||||||
|
{
|
||||||
|
size_t expected_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES * sizeof(uint16_t);
|
||||||
|
size_t image_size = image_rgb565_end - image_rgb565_start;
|
||||||
|
|
||||||
|
ESP_RETURN_ON_FALSE(image_size == expected_size, ESP_ERR_INVALID_SIZE, TAG,
|
||||||
|
"embedded image size is %u bytes, expected %u",
|
||||||
|
(unsigned int)image_size, (unsigned int)expected_size);
|
||||||
|
|
||||||
|
s_image_buffer = heap_caps_malloc(expected_size, MALLOC_CAP_DMA);
|
||||||
|
ESP_RETURN_ON_FALSE(s_image_buffer, ESP_ERR_NO_MEM, TAG, "no DMA memory for image frame");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prepare_standard_color_bars(void)
|
||||||
|
{
|
||||||
|
// The DCT module's ST7789 interface requires each RGB565 pixel MSB first.
|
||||||
|
static const uint8_t colors[][sizeof(uint16_t)] = {
|
||||||
|
{0xF8, 0x00}, // red: 0xF800
|
||||||
|
{0x07, 0xE0}, // green: 0x07E0
|
||||||
|
{0x00, 0x1F}, // blue: 0x001F
|
||||||
|
{0x00, 0x00}, // black: 0x0000
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int y = 0; y < EXAMPLE_LCD_V_RES; y++) {
|
||||||
|
for (int x = 0; x < EXAMPLE_LCD_H_RES; x++) {
|
||||||
|
size_t offset = (y * EXAMPLE_LCD_H_RES + x) * sizeof(uint16_t);
|
||||||
|
memcpy(&s_image_buffer[offset], colors[x * 4 / EXAMPLE_LCD_H_RES], sizeof(uint16_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prepare_standard_image(void)
|
||||||
|
{
|
||||||
|
size_t expected_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES * sizeof(uint16_t);
|
||||||
|
|
||||||
|
// 2_rgb565.bin is stored little-endian for the ESP32, but the panel
|
||||||
|
// interface must receive the high byte of every RGB565 pixel first.
|
||||||
|
for (size_t offset = 0; offset < expected_size; offset += sizeof(uint16_t)) {
|
||||||
|
s_image_buffer[offset] = image_rgb565_start[offset + 1];
|
||||||
|
s_image_buffer[offset + 1] = image_rgb565_start[offset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t draw_display_buffer(const char *description)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Draw %s, RGB565 MSB first (first pixel: %02x %02x)", description,
|
||||||
|
s_image_buffer[0], s_image_buffer[1]);
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_draw_bitmap(s_panel, 0, 0, EXAMPLE_LCD_H_RES,
|
||||||
|
EXAMPLE_LCD_V_RES, s_image_buffer),
|
||||||
|
TAG, "queue display frame failed");
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t display_standard_diagnostic(void)
|
||||||
|
{
|
||||||
|
prepare_standard_color_bars();
|
||||||
|
ESP_RETURN_ON_ERROR(draw_display_buffer("four color bars"), TAG, "draw color bars failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||||
|
|
||||||
|
prepare_standard_image();
|
||||||
|
return draw_display_buffer("embedded 2.jpg" );
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t init_i2c_bus(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Initialize GT911 I2C bus");
|
||||||
|
i2c_master_bus_config_t i2c_bus_config = {
|
||||||
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||||
|
.i2c_port = I2C_NUM_0,
|
||||||
|
.scl_io_num = EXAMPLE_PIN_NUM_TOUCH_SCL,
|
||||||
|
.sda_io_num = EXAMPLE_PIN_NUM_TOUCH_SDA,
|
||||||
|
.glitch_ignore_cnt = 7,
|
||||||
|
.flags = {
|
||||||
|
.enable_internal_pullup = true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(i2c_new_master_bus(&i2c_bus_config, &s_i2c_bus), TAG,
|
||||||
|
"create GT911 I2C bus failed");
|
||||||
|
ESP_LOGI(TAG, "GT911 I2C bus initialized");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t release_touch_reset_before_i2c(void)
|
||||||
|
{
|
||||||
|
gpio_config_t rst_gpio_config = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_TOUCH_RST,
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Release GT911 reset on IO%d before I2C bus init", EXAMPLE_PIN_NUM_TOUCH_RST);
|
||||||
|
esp_err_t ret = gpio_config(&rst_gpio_config);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "GPIO%d configuration failed: %s (0x%x)", EXAMPLE_PIN_NUM_TOUCH_RST,
|
||||||
|
esp_err_to_name(ret), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpio_set_level(EXAMPLE_PIN_NUM_TOUCH_RST, 1);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "GPIO%d release failed: %s (0x%x)", EXAMPLE_PIN_NUM_TOUCH_RST,
|
||||||
|
esp_err_to_name(ret), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(120));
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool scan_i2c_bus_handle(i2c_master_bus_handle_t bus, const char *label, int scl, int sda)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Scan I2C devices on %s: SCL=IO%d SDA=IO%d", label, scl, sda);
|
||||||
|
for (uint8_t addr = 0x08; addr < 0x78; addr++) {
|
||||||
|
esp_err_t ret = i2c_master_probe(bus, addr, 10);
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "I2C device found on %s at 0x%02x", label, addr);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
ESP_LOGW(TAG, "No I2C device found on %s: SCL=IO%d SDA=IO%d", label, scl, sda);
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool scan_i2c_bus(void)
|
||||||
|
{
|
||||||
|
bool found = scan_i2c_bus_handle(s_i2c_bus, "configured GT911 bus", EXAMPLE_PIN_NUM_TOUCH_SCL,
|
||||||
|
EXAMPLE_PIN_NUM_TOUCH_SDA);
|
||||||
|
if (!found) {
|
||||||
|
ESP_LOGW(TAG, "Check T_SCL/T_SDA wiring, touch power, common GND, and I2C pull-ups");
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t select_gt911_address(uint8_t dev_addr)
|
||||||
|
{
|
||||||
|
uint32_t int_level = (dev_addr == ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP) ? 1 : 0;
|
||||||
|
gpio_config_t rst_gpio_config = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_TOUCH_RST,
|
||||||
|
};
|
||||||
|
gpio_config_t int_gpio_config = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pull_up_en = 1,
|
||||||
|
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_TOUCH_IRQ,
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Select GT911 address 0x%02x with RST=IO%d INT=IO%d",
|
||||||
|
dev_addr, EXAMPLE_PIN_NUM_TOUCH_RST, EXAMPLE_PIN_NUM_TOUCH_IRQ);
|
||||||
|
ESP_RETURN_ON_ERROR(gpio_config(&rst_gpio_config), TAG, "configure GT911 reset GPIO failed");
|
||||||
|
ESP_RETURN_ON_ERROR(gpio_config(&int_gpio_config), TAG, "configure GT911 INT GPIO failed");
|
||||||
|
ESP_RETURN_ON_ERROR(gpio_set_level(EXAMPLE_PIN_NUM_TOUCH_RST, 0), TAG, "drive GT911 reset low failed");
|
||||||
|
ESP_RETURN_ON_ERROR(gpio_set_level(EXAMPLE_PIN_NUM_TOUCH_IRQ, 0), TAG, "drive GT911 INT low failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
ESP_RETURN_ON_ERROR(gpio_set_level(EXAMPLE_PIN_NUM_TOUCH_IRQ, int_level), TAG, "set GT911 address level failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2));
|
||||||
|
ESP_RETURN_ON_ERROR(gpio_set_level(EXAMPLE_PIN_NUM_TOUCH_RST, 1), TAG, "release GT911 reset failed");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(80));
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t try_init_gt911_touch(uint8_t dev_addr)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Initialize GT911 touch at I2C address 0x%02x", dev_addr);
|
||||||
|
ESP_RETURN_ON_ERROR(select_gt911_address(dev_addr), TAG, "select GT911 address failed");
|
||||||
|
|
||||||
|
esp_err_t ret = i2c_master_probe(s_i2c_bus, dev_addr, EXAMPLE_TOUCH_PROBE_TIMEOUT_MS);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "GT911 address 0x%02x did not ACK: %s", dev_addr, esp_err_to_name(ret));
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
|
||||||
|
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||||
|
tp_io_config.dev_addr = dev_addr;
|
||||||
|
tp_io_config.scl_speed_hz = EXAMPLE_TOUCH_I2C_FREQ_HZ;
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c(s_i2c_bus, &tp_io_config, &tp_io_handle),
|
||||||
|
TAG, "install GT911 panel IO failed");
|
||||||
|
|
||||||
|
static esp_lcd_touch_io_gt911_config_t gt911_config;
|
||||||
|
gt911_config.dev_addr = dev_addr;
|
||||||
|
|
||||||
|
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,
|
||||||
|
.int_gpio_num = EXAMPLE_PIN_NUM_TOUCH_IRQ,
|
||||||
|
.levels = {
|
||||||
|
.reset = 0,
|
||||||
|
.interrupt = 0,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.swap_xy = 0,
|
||||||
|
.mirror_x = 0,
|
||||||
|
.mirror_y = 0,
|
||||||
|
},
|
||||||
|
.driver_data = >911_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
ret = esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &s_touch);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
s_touch = NULL;
|
||||||
|
esp_lcd_panel_io_del(tp_io_handle);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t init_gt911_touch(void)
|
||||||
|
{
|
||||||
|
esp_err_t ret = try_init_gt911_touch(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS);
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "GT911 address 0x%02x failed: %s, try 0x%02x",
|
||||||
|
ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, esp_err_to_name(ret),
|
||||||
|
ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP);
|
||||||
|
return try_init_gt911_touch(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_touch_optional(void)
|
||||||
|
{
|
||||||
|
esp_err_t ret = release_touch_reset_before_i2c();
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
ret = init_i2c_bus();
|
||||||
|
}
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
ret = init_gt911_touch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "GT911 touch initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGE(TAG, "GT911 is unavailable: %s. Continue with LCD only", esp_err_to_name(ret));
|
||||||
|
s_touch = NULL;
|
||||||
|
if (s_i2c_bus != NULL) {
|
||||||
|
scan_i2c_bus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t init_lvgl(esp_lcd_panel_io_handle_t panel_io)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Initialize LVGL");
|
||||||
|
lv_init();
|
||||||
|
|
||||||
|
size_t buffer_size = EXAMPLE_LCD_H_RES * EXAMPLE_LVGL_BUFFER_LINES * sizeof(lv_color_t);
|
||||||
|
void *draw_buf1 = heap_caps_malloc(buffer_size, MALLOC_CAP_DMA);
|
||||||
|
void *draw_buf2 = heap_caps_malloc(buffer_size, MALLOC_CAP_DMA);
|
||||||
|
ESP_RETURN_ON_FALSE(draw_buf1 && draw_buf2, ESP_ERR_NO_MEM, TAG, "no memory for LVGL draw buffers");
|
||||||
|
|
||||||
|
lv_display_t *display = lv_display_create(EXAMPLE_LCD_H_RES, EXAMPLE_LCD_V_RES);
|
||||||
|
lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565);
|
||||||
|
lv_display_set_flush_cb(display, lvgl_flush_cb);
|
||||||
|
lv_display_set_buffers(display, draw_buf1, draw_buf2, buffer_size, LV_DISPLAY_RENDER_MODE_PARTIAL);
|
||||||
|
|
||||||
|
esp_lcd_panel_io_callbacks_t cbs = {
|
||||||
|
.on_color_trans_done = notify_lvgl_flush_ready,
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_register_event_callbacks(panel_io, &cbs, display), TAG,
|
||||||
|
"register panel IO callback failed");
|
||||||
|
|
||||||
|
if (s_touch) {
|
||||||
|
lv_indev_t *indev = lv_indev_create();
|
||||||
|
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
|
||||||
|
lv_indev_set_read_cb(indev, lvgl_touch_read_cb);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "LVGL touch input is disabled because GT911 was not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
const esp_timer_create_args_t tick_timer_args = {
|
||||||
|
.callback = lvgl_tick_cb,
|
||||||
|
.name = "lvgl_tick",
|
||||||
|
};
|
||||||
|
esp_timer_handle_t tick_timer = NULL;
|
||||||
|
ESP_RETURN_ON_ERROR(esp_timer_create(&tick_timer_args, &tick_timer), TAG, "create LVGL tick failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_timer_start_periodic(tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000), TAG,
|
||||||
|
"start LVGL tick failed");
|
||||||
|
|
||||||
|
#if LV_USE_DEMO_WIDGETS
|
||||||
|
lv_demo_widgets();
|
||||||
|
#else
|
||||||
|
create_touch_ui();
|
||||||
|
#endif
|
||||||
|
xTaskCreate(lvgl_task, "lvgl", 8192, NULL, 5, NULL);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
esp_lcd_panel_io_handle_t panel_io = NULL;
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(init_spi_bus());
|
||||||
|
ESP_ERROR_CHECK(init_st7789_panel(&panel_io));
|
||||||
|
ESP_ERROR_CHECK(init_display_buffer());
|
||||||
|
ESP_ERROR_CHECK(display_standard_diagnostic());
|
||||||
|
}
|
||||||
17
sdkconfig.defaults
Normal file
17
sdkconfig.defaults
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
CONFIG_LV_CONF_SKIP=y
|
||||||
|
CONFIG_LV_USE_OBSERVER=y
|
||||||
|
CONFIG_LV_USE_SYSMON=y
|
||||||
|
CONFIG_LV_USE_DEMO_WIDGETS=y
|
||||||
|
CONFIG_LV_FONT_MONTSERRAT_12=y
|
||||||
|
CONFIG_LV_FONT_MONTSERRAT_18=y
|
||||||
|
# CONFIG_LV_BUILD_EXAMPLES is not set
|
||||||
|
CONFIG_ESP_LCD_TOUCH_MAX_POINTS=5
|
||||||
|
CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS=0
|
||||||
|
CONFIG_FREERTOS_HZ=1000
|
||||||
|
CONFIG_IDF_TARGET="esp32s3"
|
||||||
|
CONFIG_EXAMPLE_LCD_CONTROLLER_ST7789=y
|
||||||
|
CONFIG_EXAMPLE_LCD_ST7735_H_RES=240
|
||||||
|
CONFIG_EXAMPLE_LCD_ST7735_V_RES=320
|
||||||
|
CONFIG_EXAMPLE_LCD_ST7735_X_GAP=0
|
||||||
|
CONFIG_EXAMPLE_LCD_ST7735_Y_GAP=0
|
||||||
|
CONFIG_EXAMPLE_LCD_ST7735_BGR_COLOR=y
|
||||||
Reference in New Issue
Block a user