diff --git a/README.md b/README.md index 576400f..7013f92 100644 --- a/README.md +++ b/README.md @@ -521,23 +521,22 @@ board = esp32-8048S070N ## Appendix: External dependencies -The following libraries are used from the [EspressIf component registry](https://components.espressif.com/): +The following libraries are used from the [Espressif component registry](https://components.espressif.com/): | Name | Version | |--- |--- | -| [ESP LCD ST7701](https://components.espressif.com/api/download/?object_type=component&object_id=b9904296-b88e-46a4-897a-3f5b3fa96a6e) | v1.0.0 | -| [ESP LCD CG9A01](https://components.espressif.com/api/download/?object_type=component&object_id=6f06ecdf-97a6-4eea-ad4f-c00d11bd970a) | v1.2 | -| [ESP LCD ILI9341](https://components.espressif.com/api/download/?object_type=component&object_id=680fe7b6-c70b-4560-acf9-919e5b8fa192) | v2.0 | -| [ESP LCD ST7796](https://components.espressif.com/api/download/?object_type=component&object_id=eb6095d1-642a-4e14-9daf-d46db8a1f354) | v1.2.1 | | [ESP_LCD_PANEL_IO_ADDITIONS](https://components.espressif.com/api/download/?object_type=component&object_id=fc4eba6f-2091-4b28-8703-df58c6c975c7) | v1.0.0 | | [ESP IO Expander Component](https://components.espressif.com/api/download/?object_type=component&object_id=44022a0f-c4b2-40c0-b2a2-40d7b648cb52) | v1.0.0 | | [ESP LCD Touch](https://components.espressif.com/api/download/?object_type=component&object_id=bb4a4d94-2827-4695-84d1-1b53383b8001) | v1.1.1 | -| [ESP LCD Touch CST816S](https://components.espressif.com/api/download/?object_type=component&object_id=cc8ef108-15e8-48cf-9be8-3c7e89ca493e) | v1.0.3 | -| [ESP LCD Touch GT911](https://components.espressif.com/api/download/?object_type=component&object_id=4f44d570-8a04-466e-b4bb-429f1df7a9a1) | v1.1.0 | -| [ESP LCD Touch Driver](https://components.espressif.com/api/download/?object_type=component&object_id=225971c2-051f-4619-9f91-0080315ee8b8) | v1.2.0 | ## Version history +- March 2024 + - Rewrote drivers for devices and made them cleaner + - More parametrization + - Fix for more than one point received from GT911 + - Added esp32-2432S022C + - Rotation issues fixes - January 2024 - Fixed esp32-8048S070C - Added esp32-4848S040C_I_Y_1/3 diff --git a/boards b/boards index e75f05f..e864619 160000 --- a/boards +++ b/boards @@ -1 +1 @@ -Subproject commit e75f05f68229c59de4c6aa40e38a16e012e5883f +Subproject commit e864619f48bb75cfb7d1db3387ffa5897126aa5f diff --git a/include/esp32_smartdisplay.h b/include/esp32_smartdisplay.h index d24b08b..a2031ca 100644 --- a/include/esp32_smartdisplay.h +++ b/include/esp32_smartdisplay.h @@ -6,7 +6,7 @@ // Use last PWM_CHANNEL for backlight #define PWM_CHANNEL_BCKL (SOC_LEDC_CHANNEL_NUM - 1) -#define PWM_FREQ_BCKL 20000 +#define PWM_FREQ_BCKL 400 #define PWM_BITS_BCKL 8 #define PWM_MAX_BCKL ((1 << PWM_BITS_BCKL) - 1) diff --git a/include/esp_lcd.h b/include/esp_lcd.h new file mode 100644 index 0000000..af44b8e --- /dev/null +++ b/include/esp_lcd.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include "sdkconfig.h" +#include "esp_err.h" +#include "driver/gpio.h" +#include "esp_lcd_panel_io.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +typedef struct +{ + uint8_t cmd; // Command + const uint8_t *data; // Buffer to data for the command + uint8_t bytes; // Size of the data buffer for the command + unsigned short delay_ms; // Delay in milliseconds after the command +} lcd_init_cmd_t; diff --git a/include/esp_lcd_gc9a01.h b/include/esp_lcd_gc9a01.h deleted file mode 100644 index 2d40c88..0000000 --- a/include/esp_lcd_gc9a01.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -/** - * @file - * @brief ESP LCD: GC9A01 - */ - -#pragma once - -#include "hal/spi_ll.h" -#include "esp_lcd_panel_vendor.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief LCD panel initialization commands. - * - */ -typedef struct { - int cmd; /* - -#include "hal/lcd_types.h" -#include "esp_lcd_panel_vendor.h" -#include "esp_lcd_panel_rgb.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief LCD panel initialization commands. - * - */ -typedef struct { - int cmd; /* +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct + { + const lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + } gc9a01_vendor_config_t; + + esp_err_t esp_lcd_new_panel_gc9a01(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *config, esp_lcd_panel_handle_t *handle); + +#ifdef __cplusplus +} +#endif diff --git a/include/esp_panel_ili9341.h b/include/esp_panel_ili9341.h new file mode 100644 index 0000000..ee1eed5 --- /dev/null +++ b/include/esp_panel_ili9341.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct + { + const lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + } ili9341_vendor_config_t; + + esp_err_t esp_lcd_new_panel_ili9341(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *config, esp_lcd_panel_handle_t *handle); + +#ifdef __cplusplus +} +#endif diff --git a/include/esp_panel_st7701.h b/include/esp_panel_st7701.h new file mode 100644 index 0000000..3b2b85a --- /dev/null +++ b/include/esp_panel_st7701.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct + { + const lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + } st7701_vendor_config_t; + + esp_err_t esp_lcd_new_panel_st7701(const esp_lcd_panel_io_handle_t io, const esp_lcd_rgb_panel_config_t *panel_config, const esp_lcd_panel_dev_config_t *config, esp_lcd_panel_handle_t *handle); + +#ifdef __cplusplus +} +#endif diff --git a/include/esp_panel_st7796.h b/include/esp_panel_st7796.h new file mode 100644 index 0000000..35332b4 --- /dev/null +++ b/include/esp_panel_st7796.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct + { + const lcd_init_cmd_t *init_cmds; + uint16_t init_cmds_size; + } st7796_vendor_config_t; + + esp_err_t esp_lcd_new_panel_st7796(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *config, esp_lcd_panel_handle_t *handle); + +#ifdef __cplusplus +} +#endif diff --git a/include/esp_touch_cst816s.h b/include/esp_touch_cst816s.h new file mode 100644 index 0000000..ecc05af --- /dev/null +++ b/include/esp_touch_cst816s.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +// I2C Address +#define CST816S_IO_I2C_CONFIG_DEV_ADDRESS_15 0x15 // 0x15 (0x2A/0x2B): INT High during reset or + +#ifdef __cplusplus +extern "C" +{ +#endif + + esp_err_t esp_lcd_touch_new_i2c_cst816s(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *handle); + +#ifdef __cplusplus +} +#endif diff --git a/include/esp_touch_gt911.h b/include/esp_touch_gt911.h new file mode 100644 index 0000000..6e21baf --- /dev/null +++ b/include/esp_touch_gt911.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +// Two I2C Addresses possible +#define GT911_IO_I2C_CONFIG_DEV_ADDRESS_5D 0x5D // 0x5D (0xBA/0xBB): INT High during reset or +#define GT911_IO_I2C_CONFIG_DEV_ADDRESS_14 0x14 // 0x14 (0x28/0x29): INT Low during reset + +#ifdef __cplusplus +extern "C" +{ +#endif + + esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *handle); + +#ifdef __cplusplus +} +#endif diff --git a/include/esp_touch_xpt2046.h b/include/esp_touch_xpt2046.h new file mode 100644 index 0000000..8436b2c --- /dev/null +++ b/include/esp_touch_xpt2046.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + esp_err_t esp_lcd_touch_new_spi_xpt2046(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *handle); + esp_err_t esp_lcd_touch_xpt2046_read_battery_level(const esp_lcd_touch_handle_t tp, float *output); + +#ifdef __cplusplus +} +#endif diff --git a/library.json b/library.json index aae53b6..d77ad87 100644 --- a/library.json +++ b/library.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json", "name": "esp32_smartdisplay", - "version": "2.0.7", + "version": "2.0.8", "description": "LVGL driver for Sunton ESP32 Cheap Yellow Display display boards", "keywords": "LVGL Sunton CYD LCD TFT Touch", "repository": { @@ -12,24 +12,17 @@ "srcDir": "src/", "includeDir": "include/", "flags": [ - "-D'ESP_LCD_PANEL_IO_ADDITIONS_VER_MAJOR=1'", - "-D'ESP_LCD_PANEL_IO_ADDITIONS_VER_MINOR=0'", - "-D'ESP_LCD_PANEL_IO_ADDITIONS_VER_PATCH=1'", - "-D'ESP_LCD_ST7701_VER_MAJOR=1'", - "-D'ESP_LCD_ST7701_VER_MINOR=0'", - "-D'ESP_LCD_ST7701_VER_PATCH=0'", - "-D'ESP_LCD_ST7796_VER_MAJOR=1'", - "-D'ESP_LCD_ST7796_VER_MINOR=2'", - "-D'ESP_LCD_ST7796_VER_PATCH=0'", - "-D'ESP_LCD_ILI9341_VER_MAJOR=1'", - "-D'ESP_LCD_ILI9341_VER_MINOR=2'", - "-D'ESP_LCD_ILI9341_VER_PATCH=0'", - "-D'ESP_LCD_GC9A01_VER_MAJOR=1'", - "-D'ESP_LCD_GC9A01_VER_MINOR=2'", - "-D'ESP_LCD_GC9A01_VER_PATCH=0'", - "-D'CONFIG_ESP_LCD_TOUCH_MAX_POINTS=1'", - "-D'CONFIG_XPT2046_CONVERT_ADC_TO_COORDS'", - "-D'CONFIG_XPT2046_Z_THRESHOLD=600'" + "'-D ESP_LCD_PANEL_IO_ADDITIONS_VER_MAJOR=1'", + "'-D ESP_LCD_PANEL_IO_ADDITIONS_VER_MINOR=0'", + "'-D ESP_LCD_PANEL_IO_ADDITIONS_VER_PATCH=1'" + ] + }, + "export": { + "exclude": [ + "**/.*", + "test/**/*", + "assets/**/*", + "boards/**/*" ] }, "authors": [ diff --git a/platformio.ini b/platformio.ini index 2b6f752..b2e4a2d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,6 +20,7 @@ #default_envs = esp32-2432S024R #default_envs = esp32-2432S028R #default_envs = esp32-2432S028Rv2 +#default_envs = esp32-2432S028Rv3 #default_envs = esp32-2432S032C #default_envs = esp32-2432S032N #default_envs = esp32-2432S032R @@ -44,39 +45,16 @@ platform = espressif32 framework = arduino -monitor_speed = 115200 -monitor_rts = 0 -monitor_dtr = 0 -monitor_filters = esp32_exception_decoder - -# Partition scheme for OTA -board_build.partitions = min_spiffs.csv - build_flags = -Ofast -Wall - -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE + '-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE' # LVGL settings - -D'LV_CONF_PATH=${platformio.test_dir}/lv_conf.h' - -D'ESP_LCD_PANEL_IO_ADDITIONS_VER_MAJOR=1' - -D'ESP_LCD_PANEL_IO_ADDITIONS_VER_MINOR=0' - -D'ESP_LCD_PANEL_IO_ADDITIONS_VER_PATCH=1' - -D'ESP_LCD_ST7701_VER_MAJOR=1' - -D'ESP_LCD_ST7701_VER_MINOR=0' - -D'ESP_LCD_ST7701_VER_PATCH=0' - -D'ESP_LCD_ST7796_VER_MAJOR=1' - -D'ESP_LCD_ST7796_VER_MINOR=2' - -D'ESP_LCD_ST7796_VER_PATCH=0' - -D'ESP_LCD_ILI9341_VER_MAJOR=1' - -D'ESP_LCD_ILI9341_VER_MINOR=2' - -D'ESP_LCD_ILI9341_VER_PATCH=0' - -D'ESP_LCD_GC9A01_VER_MAJOR=1' - -D'ESP_LCD_GC9A01_VER_MINOR=2' - -D'ESP_LCD_GC9A01_VER_PATCH=0' - -D'CONFIG_ESP_LCD_TOUCH_MAX_POINTS=1' - -D'CONFIG_XPT2046_CONVERT_ADC_TO_COORDS' - -D'CONFIG_XPT2046_Z_THRESHOLD=600' - + '-D LV_CONF_PATH=${platformio.test_dir}/lv_conf.h' + '-D ESP_LCD_PANEL_IO_ADDITIONS_VER_MAJOR=1' + '-D ESP_LCD_PANEL_IO_ADDITIONS_VER_MINOR=0' + '-D ESP_LCD_PANEL_IO_ADDITIONS_VER_PATCH=1' + lib_deps = lvgl/lvgl@^8.3.9 # The platformio.test_dir contains the test_main.cpp just to have an setup() and loop() function @@ -116,6 +94,9 @@ board = esp32-2432S028R [env:esp32-2432S028Rv2] board = esp32-2432S028Rv2 +[env:esp32-2432S028Rv3] +board = esp32-2432S028Rv3 + [env:esp32-2432S032C] board = esp32-2432S032C diff --git a/src/esp32_smartdisplay.c b/src/esp32_smartdisplay.c index 506c3bd..a22dd2f 100644 --- a/src/esp32_smartdisplay.c +++ b/src/esp32_smartdisplay.c @@ -1,112 +1,38 @@ #include -#include -#include + +#ifdef BOARD_HAS_TOUCH #include -#include +#endif // Defines for adaptive brightness adjustment #define BRIGHTNESS_SMOOTHING_MEASUREMENTS 100 #define BRIGHTNESS_DARK_ZONE 250 // Functions to be defined in the tft/touch driver -extern void lvgl_lcd_init(lv_disp_drv_t *drv); -extern void lvgl_touch_init(lv_indev_drv_t *drv); - -static lv_disp_drv_t disp_drv; -static lv_indev_drv_t indev_drv; +extern void lvgl_lcd_init(lv_disp_drv_t *disp_drv); +extern void lvgl_touch_init(lv_indev_drv_t *disp_drv); +lv_disp_drv_t disp_drv; lv_timer_t *update_brightness_timer; #ifdef BOARD_HAS_TOUCH +lv_indev_drv_t indev_drv; touch_calibration_data_t touch_calibration_data; void (*driver_touch_read_cb)(struct _lv_indev_drv_t *indev_drv, lv_indev_data_t *data); #endif -#if LV_USE_LOG +#ifdef LV_USE_LOG void lvgl_log(const char *buf) { log_printf("%s", buf); } #endif -// Called when driver parameters are updated (rotation) -// Top of the display is top left when connector is at the bottom -static void lvgl_update_callback(lv_disp_drv_t *drv) -{ - log_d("lvgl_update_callback"); - esp_lcd_panel_handle_t panel_handle = disp_drv.user_data; - switch (drv->rotated) - { - case LV_DISP_ROT_NONE: -#if defined(LCD_SWAP_XY) && defined(LCD_MIRROR_X) && defined(LCD_MIRROR_Y) - ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, LCD_SWAP_XY)); - ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, LCD_MIRROR_X, LCD_MIRROR_Y)); -#endif -#if defined(LCD_GAP_X) || defined(LCD_GAP_Y) - ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, LCD_GAP_X, LCD_GAP_Y)); -#endif - break; - case LV_DISP_ROT_90: -#if defined(LCD_SWAP_XY) && defined(LCD_MIRROR_X) && defined(LCD_MIRROR_Y) - ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, !LCD_SWAP_XY)); - ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, !LCD_MIRROR_X, LCD_MIRROR_Y)); -#endif -#if defined(LCD_GAP_X) || defined(LCD_GAP_Y) - ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, LCD_GAP_Y, LCD_GAP_X)); -#endif - break; - case LV_DISP_ROT_180: -#if defined(LCD_SWAP_XY) && defined(LCD_MIRROR_X) && defined(LCD_MIRROR_Y) - ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, LCD_SWAP_XY)); - ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, !LCD_MIRROR_X, !LCD_MIRROR_Y)); -#endif -#if defined(LCD_GAP_X) || defined(LCD_GAP_Y) - ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, LCD_GAP_X, LCD_GAP_Y)); -#endif - break; - case LV_DISP_ROT_270: -#if defined(LCD_SWAP_XY) && defined(LCD_MIRROR_X) && defined(LCD_MIRROR_Y) - ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, !LCD_SWAP_XY)); - ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, LCD_MIRROR_X, !LCD_MIRROR_Y)); -#endif -#if defined(LCD_GAP_X) || defined(LCD_GAP_Y) - ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, LCD_GAP_Y, LCD_GAP_X)); -#endif - break; - } - -#ifdef BOARD_HAS_TOUCH - esp_lcd_touch_handle_t touch_handle = indev_drv.user_data; - switch (drv->rotated) - { - case LV_DISP_ROT_NONE: - ESP_ERROR_CHECK(esp_lcd_touch_set_swap_xy(touch_handle, TOUCH_SWAP_XY)); - ESP_ERROR_CHECK(esp_lcd_touch_set_mirror_x(touch_handle, TOUCH_SWAP_X)); - ESP_ERROR_CHECK(esp_lcd_touch_set_mirror_y(touch_handle, TOUCH_SWAP_Y)); - break; - case LV_DISP_ROT_90: - ESP_ERROR_CHECK(esp_lcd_touch_set_swap_xy(touch_handle, !TOUCH_SWAP_XY)); - ESP_ERROR_CHECK(esp_lcd_touch_set_mirror_x(touch_handle, !TOUCH_SWAP_X)); - ESP_ERROR_CHECK(esp_lcd_touch_set_mirror_y(touch_handle, !TOUCH_SWAP_Y)); - break; - case LV_DISP_ROT_180: - ESP_ERROR_CHECK(esp_lcd_touch_set_swap_xy(touch_handle, TOUCH_SWAP_XY)); - ESP_ERROR_CHECK(esp_lcd_touch_set_mirror_x(touch_handle, TOUCH_SWAP_X)); - ESP_ERROR_CHECK(esp_lcd_touch_set_mirror_y(touch_handle, TOUCH_SWAP_Y)); - break; - case LV_DISP_ROT_270: - ESP_ERROR_CHECK(esp_lcd_touch_set_swap_xy(touch_handle, !TOUCH_SWAP_XY)); - ESP_ERROR_CHECK(esp_lcd_touch_set_mirror_x(touch_handle, !TOUCH_SWAP_X)); - ESP_ERROR_CHECK(esp_lcd_touch_set_mirror_y(touch_handle, !TOUCH_SWAP_Y)); - break; - } -#endif -} - // Set backlight intensity void smartdisplay_lcd_set_backlight(float duty) { - log_d("smartdisplay_lcd_set_backlight. duty:%2f", duty); + log_v("duty:%2f", duty); + if (duty > 1.0) duty = 1.0f; if (duty < 0.0) @@ -139,12 +65,16 @@ float smartdisplay_lcd_adaptive_brightness_cds() void adaptive_brightness(lv_timer_t *timer) { + log_v("timer:0x%08x", timer); + const smartdisplay_lcd_adaptive_brightness_cb_t callback = timer->user_data; smartdisplay_lcd_set_backlight(callback()); } void smartdisplay_lcd_set_brightness_cb(smartdisplay_lcd_adaptive_brightness_cb_t cb, uint interval) { + log_v("adaptive_brightness_cb:0x%08x, interval:%d", cb, interval); + // Delete current timer if any if (update_brightness_timer) lv_timer_del(update_brightness_timer); @@ -159,7 +89,8 @@ void smartdisplay_lcd_set_brightness_cb(smartdisplay_lcd_adaptive_brightness_cb_ #ifdef BOARD_HAS_RGB_LED void smartdisplay_led_set_rgb(bool r, bool g, bool b) { - log_d("smartdisplay_led_set_rgb. R:%d, G:%d, B:%d", r, b, b); + log_d("R:%d, G:%d, B:%d", r, b, b); + digitalWrite(RGB_LED_R, !r); digitalWrite(RGB_LED_G, !g); digitalWrite(RGB_LED_B, !b); @@ -168,10 +99,12 @@ void smartdisplay_led_set_rgb(bool r, bool g, bool b) #ifdef BOARD_HAS_TOUCH // See: https://www.maximintegrated.com/en/design/technical-documents/app-notes/5/5296.html -void lvgl_touch_calibration_transform(lv_indev_drv_t *drv, lv_indev_data_t *data) +void lvgl_touch_calibration_transform(lv_indev_drv_t *disp_drv, lv_indev_data_t *data) { + log_v("disp_drv:0x%08x, data:0x%08x", disp_drv, data); + // Call low level read from the driver - driver_touch_read_cb(drv, data); + driver_touch_read_cb(disp_drv, data); // Check if transformation is required if (touch_calibration_data.valid && data->state == LV_INDEV_STATE_PRESSED) { @@ -179,15 +112,14 @@ void lvgl_touch_calibration_transform(lv_indev_drv_t *drv, lv_indev_data_t *data lv_point_t pt = { .x = roundf(data->point.x * touch_calibration_data.alphaX + data->point.y * touch_calibration_data.betaX + touch_calibration_data.deltaX), .y = roundf(data->point.x * touch_calibration_data.alphaY + data->point.y * touch_calibration_data.betaY + touch_calibration_data.deltaY)}; - log_i("Calibrate point (%d, %d) => (%d, %d)", data->point.x, data->point.y, pt.x, pt.y); + log_d("Calibrate point (%d, %d) => (%d, %d)", data->point.x, data->point.y, pt.x, pt.y); data->point = (lv_point_t){pt.x, pt.y}; } } touch_calibration_data_t smartdisplay_compute_touch_calibration(const lv_point_t screen[3], const lv_point_t touch[3]) { - log_d("smartdisplay_compute_touch_calibration"); - touch_calibration_data.valid = false; + log_v("screen:0x%08x, touch:0x%08x", screen, touch); const float delta = ((touch[0].x - touch[2].x) * (touch[1].y - touch[2].y)) - ((touch[1].x - touch[2].x) * (touch[0].y - touch[2].y)); touch_calibration_data_t touch_calibration_data = { .valid = true, @@ -199,7 +131,7 @@ touch_calibration_data_t smartdisplay_compute_touch_calibration(const lv_point_t .deltaY = ((screen[0].y * (touch[1].x * touch[2].y - touch[2].x * touch[1].y)) - (screen[1].y * (touch[0].x * touch[2].y - touch[2].x * touch[0].y)) + (screen[2].y * (touch[0].x * touch[1].y - touch[1].x * touch[0].y))) / delta, }; - log_i("Calibration (alphaX, betaX, deltaX, alphaY, betaY, deltaY) = (%f, %f, %f, %f, %f, %f)", touch_calibration_data.alphaX, touch_calibration_data.betaX, touch_calibration_data.deltaX, touch_calibration_data.alphaY, touch_calibration_data.betaY, touch_calibration_data.deltaY); + log_d("alphaX: %f, betaX: %f, deltaX: %f, alphaY: %f, betaY: %f, deltaY: %f", touch_calibration_data.alphaX, touch_calibration_data.betaX, touch_calibration_data.deltaX, touch_calibration_data.alphaY, touch_calibration_data.betaY, touch_calibration_data.deltaY); return touch_calibration_data; }; #endif @@ -253,7 +185,7 @@ void smartdisplay_init() lv_disp_draw_buf_init(disp_drv.draw_buf, drawBuffer, NULL, LVGL_BUFFER_PIXELS); // Initialize specific driver lvgl_lcd_init(&disp_drv); - lv_disp_t *display = lv_disp_drv_register(&disp_drv); + __attribute__((unused)) lv_disp_t *display = lv_disp_drv_register(&disp_drv); // Clear screen lv_obj_clean(lv_scr_act()); // Turn backlight on (50%) @@ -269,9 +201,4 @@ void smartdisplay_init() indev_drv.read_cb = lvgl_touch_calibration_transform; lv_indev_drv_register(&indev_drv); #endif - - // Register callback for changes to the driver parameters - disp_drv.drv_update_cb = lvgl_update_callback; - // Call the callback to set the rotation - lvgl_update_callback(&disp_drv); } \ No newline at end of file diff --git a/src/esp_lcd_gc9a01.c b/src/esp_lcd_gc9a01.c deleted file mode 100644 index 336c84b..0000000 --- a/src/esp_lcd_gc9a01.c +++ /dev/null @@ -1,385 +0,0 @@ -#ifdef LCD_GC9A01_SPI - -/* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_lcd_panel_interface.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_vendor.h" -#include "esp_lcd_panel_ops.h" -#include "esp_lcd_panel_commands.h" -#include "driver/gpio.h" -#include "esp_log.h" -#include "esp_check.h" - -#include "esp_lcd_gc9a01.h" - -static const char *TAG = "gc9a01"; - -static esp_err_t panel_gc9a01_del(esp_lcd_panel_t *panel); -static esp_err_t panel_gc9a01_reset(esp_lcd_panel_t *panel); -static esp_err_t panel_gc9a01_init(esp_lcd_panel_t *panel); -static esp_err_t panel_gc9a01_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_gc9a01_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); -static esp_err_t panel_gc9a01_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); -static esp_err_t panel_gc9a01_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); -static esp_err_t panel_gc9a01_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); -static esp_err_t panel_gc9a01_disp_on_off(esp_lcd_panel_t *panel, bool off); - -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; // save current value of LCD_CMD_MADCTL register - uint8_t colmod_val; // save current value of LCD_CMD_COLMOD register - const gc9a01_lcd_init_cmd_t *init_cmds; - uint16_t init_cmds_size; -} gc9a01_panel_t; - -esp_err_t esp_lcd_new_panel_gc9a01(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; - gc9a01_panel_t *gc9a01 = NULL; - gpio_config_t io_conf = { 0 }; - - ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); - gc9a01 = (gc9a01_panel_t *)calloc(1, sizeof(gc9a01_panel_t)); - ESP_GOTO_ON_FALSE(gc9a01, ESP_ERR_NO_MEM, err, TAG, "no mem for gc9a01 panel"); - - if (panel_dev_config->reset_gpio_num >= 0) { - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.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"); - } - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - switch (panel_dev_config->color_space) { - case ESP_LCD_COLOR_SPACE_RGB: - gc9a01->madctl_val = 0; - break; - case ESP_LCD_COLOR_SPACE_BGR: - gc9a01->madctl_val |= LCD_CMD_BGR_BIT; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); - break; - } -#else - switch (panel_dev_config->rgb_endian) { - case LCD_RGB_ENDIAN_RGB: - gc9a01->madctl_val = 0; - break; - case LCD_RGB_ENDIAN_BGR: - gc9a01->madctl_val |= LCD_CMD_BGR_BIT; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported rgb endian"); - break; - } -#endif - - switch (panel_dev_config->bits_per_pixel) { - case 16: // RGB565 - gc9a01->colmod_val = 0x55; - gc9a01->fb_bits_per_pixel = 16; - break; - case 18: // RGB666 - gc9a01->colmod_val = 0x66; - // each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel - gc9a01->fb_bits_per_pixel = 24; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); - break; - } - - gc9a01->io = io; - gc9a01->reset_gpio_num = panel_dev_config->reset_gpio_num; - gc9a01->reset_level = panel_dev_config->flags.reset_active_high; - if (panel_dev_config->vendor_config) { - gc9a01->init_cmds = ((gc9a01_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds; - gc9a01->init_cmds_size = ((gc9a01_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds_size; - } - gc9a01->base.del = panel_gc9a01_del; - gc9a01->base.reset = panel_gc9a01_reset; - gc9a01->base.init = panel_gc9a01_init; - gc9a01->base.draw_bitmap = panel_gc9a01_draw_bitmap; - gc9a01->base.invert_color = panel_gc9a01_invert_color; - gc9a01->base.set_gap = panel_gc9a01_set_gap; - gc9a01->base.mirror = panel_gc9a01_mirror; - gc9a01->base.swap_xy = panel_gc9a01_swap_xy; -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - gc9a01->base.disp_off = panel_gc9a01_disp_on_off; -#else - gc9a01->base.disp_on_off = panel_gc9a01_disp_on_off; -#endif - *ret_panel = &(gc9a01->base); - ESP_LOGD(TAG, "new gc9a01 panel @%p", gc9a01); - - ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_GC9A01_VER_MAJOR, ESP_LCD_GC9A01_VER_MINOR, - ESP_LCD_GC9A01_VER_PATCH); - - return ESP_OK; - -err: - if (gc9a01) { - if (panel_dev_config->reset_gpio_num >= 0) { - gpio_reset_pin(panel_dev_config->reset_gpio_num); - } - free(gc9a01); - } - return ret; -} - -static esp_err_t panel_gc9a01_del(esp_lcd_panel_t *panel) -{ - gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base); - - if (gc9a01->reset_gpio_num >= 0) { - gpio_reset_pin(gc9a01->reset_gpio_num); - } - ESP_LOGD(TAG, "del gc9a01 panel @%p", gc9a01); - free(gc9a01); - return ESP_OK; -} - -static esp_err_t panel_gc9a01_reset(esp_lcd_panel_t *panel) -{ - gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base); - esp_lcd_panel_io_handle_t io = gc9a01->io; - - // perform hardware reset - if (gc9a01->reset_gpio_num >= 0) { - gpio_set_level(gc9a01->reset_gpio_num, gc9a01->reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); - gpio_set_level(gc9a01->reset_gpio_num, !gc9a01->reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); - } else { // perform software reset - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5ms before sending new command - } - - return ESP_OK; -} - -static const gc9a01_lcd_init_cmd_t vendor_specific_init_default[] = { -// {cmd, { data }, data_size, delay_ms} - // Enable Inter Register - {0xfe, (uint8_t []){0x00}, 0, 0}, - {0xef, (uint8_t []){0x00}, 0, 0}, - {0xeb, (uint8_t []){0x14}, 1, 0}, - {0x84, (uint8_t []){0x60}, 1, 0}, - {0x85, (uint8_t []){0xff}, 1, 0}, - {0x86, (uint8_t []){0xff}, 1, 0}, - {0x87, (uint8_t []){0xff}, 1, 0}, - {0x8e, (uint8_t []){0xff}, 1, 0}, - {0x8f, (uint8_t []){0xff}, 1, 0}, - {0x88, (uint8_t []){0x0a}, 1, 0}, - {0x89, (uint8_t []){0x23}, 1, 0}, - {0x8a, (uint8_t []){0x00}, 1, 0}, - {0x8b, (uint8_t []){0x80}, 1, 0}, - {0x8c, (uint8_t []){0x01}, 1, 0}, - {0x8d, (uint8_t []){0x03}, 1, 0}, - {0x90, (uint8_t []){0x08, 0x08, 0x08, 0x08}, 4, 0}, - {0xff, (uint8_t []){0x60, 0x01, 0x04}, 3, 0}, - {0xC3, (uint8_t []){0x13}, 1, 0}, - {0xC4, (uint8_t []){0x13}, 1, 0}, - {0xC9, (uint8_t []){0x30}, 1, 0}, - {0xbe, (uint8_t []){0x11}, 1, 0}, - {0xe1, (uint8_t []){0x10, 0x0e}, 2, 0}, - {0xdf, (uint8_t []){0x21, 0x0c, 0x02}, 3, 0}, - // Set gamma - {0xF0, (uint8_t []){0x45, 0x09, 0x08, 0x08, 0x26, 0x2a}, 6, 0}, - {0xF1, (uint8_t []){0x43, 0x70, 0x72, 0x36, 0x37, 0x6f}, 6, 0}, - {0xF2, (uint8_t []){0x45, 0x09, 0x08, 0x08, 0x26, 0x2a}, 6, 0}, - {0xF3, (uint8_t []){0x43, 0x70, 0x72, 0x36, 0x37, 0x6f}, 6, 0}, - {0xed, (uint8_t []){0x1b, 0x0b}, 2, 0}, - {0xae, (uint8_t []){0x77}, 1, 0}, - {0xcd, (uint8_t []){0x63}, 1, 0}, - {0x70, (uint8_t []){0x07, 0x07, 0x04, 0x0e, 0x0f, 0x09, 0x07, 0x08, 0x03}, 9, 0}, - {0xE8, (uint8_t []){0x34}, 1, 0}, // 4 dot inversion - {0x60, (uint8_t []){0x38, 0x0b, 0x6D, 0x6D, 0x39, 0xf0, 0x6D, 0x6D}, 8, 0}, - {0x61, (uint8_t []){0x38, 0xf4, 0x6D, 0x6D, 0x38, 0xf7, 0x6D, 0x6D}, 8, 0}, - {0x62, (uint8_t []){0x38, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x38, 0x0F, 0x71, 0xEF, 0x70, 0x70}, 12, 0}, - {0x63, (uint8_t []){0x38, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x38, 0x13, 0x71, 0xF3, 0x70, 0x70}, 12, 0}, - {0x64, (uint8_t []){0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07}, 7, 0}, - {0x66, (uint8_t []){0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00}, 10, 0}, - {0x67, (uint8_t []){0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98}, 10, 0}, - {0x74, (uint8_t []){0x10, 0x45, 0x80, 0x00, 0x00, 0x4E, 0x00}, 7, 0}, - {0x98, (uint8_t []){0x3e, 0x07}, 2, 0}, - {0x99, (uint8_t []){0x3e, 0x07}, 2, 0}, -}; - -static esp_err_t panel_gc9a01_init(esp_lcd_panel_t *panel) -{ - gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base); - esp_lcd_panel_io_handle_t io = gc9a01->io; - - // LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0), TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(100)); - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - gc9a01->madctl_val, - }, 1), TAG, "send command failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) { - gc9a01->colmod_val, - }, 1), TAG, "send command failed"); - - const gc9a01_lcd_init_cmd_t *init_cmds = NULL; - uint16_t init_cmds_size = 0; - if (gc9a01->init_cmds) { - init_cmds = gc9a01->init_cmds; - init_cmds_size = gc9a01->init_cmds_size; - } else { - init_cmds = vendor_specific_init_default; - init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(gc9a01_lcd_init_cmd_t); - } - - bool is_cmd_overwritten = false; - for (int i = 0; i < init_cmds_size; i++) { - // Check if the command has been used or conflicts with the internal - switch (init_cmds[i].cmd) { - case LCD_CMD_MADCTL: - is_cmd_overwritten = true; - gc9a01->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; - break; - case LCD_CMD_COLMOD: - is_cmd_overwritten = true; - gc9a01->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; - break; - default: - is_cmd_overwritten = false; - break; - } - - if (is_cmd_overwritten) { - ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", init_cmds[i].cmd); - } - - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); - } - ESP_LOGD(TAG, "send init commands success"); - - return ESP_OK; -} - -static esp_err_t panel_gc9a01_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) -{ - gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_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 = gc9a01->io; - - x_start += gc9a01->x_gap; - x_end += gc9a01->x_gap; - y_start += gc9a01->y_gap; - y_end += gc9a01->y_gap; - - // define an area of frame memory where MCU can access - 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"); - // transfer frame buffer - size_t len = (x_end - x_start) * (y_end - y_start) * gc9a01->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_gc9a01_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) -{ - gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base); - esp_lcd_panel_io_handle_t io = gc9a01->io; - int command = 0; - if (invert_color_data) { - command = LCD_CMD_INVON; - } else { - command = LCD_CMD_INVOFF; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); - return ESP_OK; -} - -static esp_err_t panel_gc9a01_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) -{ - gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base); - esp_lcd_panel_io_handle_t io = gc9a01->io; - if (mirror_x) { - gc9a01->madctl_val |= LCD_CMD_MX_BIT; - } else { - gc9a01->madctl_val &= ~LCD_CMD_MX_BIT; - } - if (mirror_y) { - gc9a01->madctl_val |= LCD_CMD_MY_BIT; - } else { - gc9a01->madctl_val &= ~LCD_CMD_MY_BIT; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - gc9a01->madctl_val - }, 1), TAG, "send command failed"); - return ESP_OK; -} - -static esp_err_t panel_gc9a01_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) -{ - gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base); - esp_lcd_panel_io_handle_t io = gc9a01->io; - if (swap_axes) { - gc9a01->madctl_val |= LCD_CMD_MV_BIT; - } else { - gc9a01->madctl_val &= ~LCD_CMD_MV_BIT; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - gc9a01->madctl_val - }, 1), TAG, "send command failed"); - return ESP_OK; -} - -static esp_err_t panel_gc9a01_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) -{ - gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base); - gc9a01->x_gap = x_gap; - gc9a01->y_gap = y_gap; - return ESP_OK; -} - -static esp_err_t panel_gc9a01_disp_on_off(esp_lcd_panel_t *panel, bool on_off) -{ - gc9a01_panel_t *gc9a01 = __containerof(panel, gc9a01_panel_t, base); - esp_lcd_panel_io_handle_t io = gc9a01->io; - int command = 0; - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - on_off = !on_off; -#endif - - if (on_off) { - command = LCD_CMD_DISPON; - } else { - command = LCD_CMD_DISPOFF; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); - return ESP_OK; -} - -#endif \ No newline at end of file diff --git a/src/esp_lcd_ili9341.c b/src/esp_lcd_ili9341.c deleted file mode 100644 index 82ad23b..0000000 --- a/src/esp_lcd_ili9341.c +++ /dev/null @@ -1,391 +0,0 @@ -#ifdef LCD_ILI9341_SPI - -/* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_lcd_panel_interface.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_vendor.h" -#include "esp_lcd_panel_ops.h" -#include "esp_lcd_panel_commands.h" -#include "driver/gpio.h" -#include "esp_log.h" -#include "esp_check.h" - -#include "esp_lcd_ili9341.h" - -static const char *TAG = "ili9341"; - -static esp_err_t panel_ili9341_del(esp_lcd_panel_t *panel); -static esp_err_t panel_ili9341_reset(esp_lcd_panel_t *panel); -static esp_err_t panel_ili9341_init(esp_lcd_panel_t *panel); -static esp_err_t panel_ili9341_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_ili9341_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); -static esp_err_t panel_ili9341_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); -static esp_err_t panel_ili9341_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); -static esp_err_t panel_ili9341_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); -static esp_err_t panel_ili9341_disp_on_off(esp_lcd_panel_t *panel, bool off); - -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; // save current value of LCD_CMD_MADCTL register - uint8_t colmod_val; // save current value of LCD_CMD_COLMOD register - const ili9341_lcd_init_cmd_t *init_cmds; - uint16_t init_cmds_size; -} ili9341_panel_t; - -esp_err_t esp_lcd_new_panel_ili9341(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; - ili9341_panel_t *ili9341 = NULL; - gpio_config_t io_conf = { 0 }; - - ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); - ili9341 = (ili9341_panel_t *)calloc(1, sizeof(ili9341_panel_t)); - ESP_GOTO_ON_FALSE(ili9341, ESP_ERR_NO_MEM, err, TAG, "no mem for ili9341 panel"); - - if (panel_dev_config->reset_gpio_num >= 0) { - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.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"); - } - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - switch (panel_dev_config->color_space) { - case ESP_LCD_COLOR_SPACE_RGB: - ili9341->madctl_val = 0; - break; - case ESP_LCD_COLOR_SPACE_BGR: - ili9341->madctl_val |= LCD_CMD_BGR_BIT; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); - break; - } -#else - switch (panel_dev_config->rgb_endian) { - case LCD_RGB_ENDIAN_RGB: - ili9341->madctl_val = 0; - break; - case LCD_RGB_ENDIAN_BGR: - ili9341->madctl_val |= LCD_CMD_BGR_BIT; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported rgb endian"); - break; - } -#endif - - switch (panel_dev_config->bits_per_pixel) { - case 16: // RGB565 - ili9341->colmod_val = 0x55; - ili9341->fb_bits_per_pixel = 16; - break; - case 18: // RGB666 - ili9341->colmod_val = 0x66; - // each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel - ili9341->fb_bits_per_pixel = 24; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); - break; - } - - ili9341->io = io; - ili9341->reset_gpio_num = panel_dev_config->reset_gpio_num; - ili9341->reset_level = panel_dev_config->flags.reset_active_high; - if (panel_dev_config->vendor_config) { - ili9341->init_cmds = ((ili9341_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds; - ili9341->init_cmds_size = ((ili9341_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds_size; - } - ili9341->base.del = panel_ili9341_del; - ili9341->base.reset = panel_ili9341_reset; - ili9341->base.init = panel_ili9341_init; - ili9341->base.draw_bitmap = panel_ili9341_draw_bitmap; - ili9341->base.invert_color = panel_ili9341_invert_color; - ili9341->base.set_gap = panel_ili9341_set_gap; - ili9341->base.mirror = panel_ili9341_mirror; - ili9341->base.swap_xy = panel_ili9341_swap_xy; -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - ili9341->base.disp_off = panel_ili9341_disp_on_off; -#else - ili9341->base.disp_on_off = panel_ili9341_disp_on_off; -#endif - *ret_panel = &(ili9341->base); - ESP_LOGD(TAG, "new ili9341 panel @%p", ili9341); - - ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_ILI9341_VER_MAJOR, ESP_LCD_ILI9341_VER_MINOR, - ESP_LCD_ILI9341_VER_PATCH); - - return ESP_OK; - -err: - if (ili9341) { - if (panel_dev_config->reset_gpio_num >= 0) { - gpio_reset_pin(panel_dev_config->reset_gpio_num); - } - free(ili9341); - } - return ret; -} - -static esp_err_t panel_ili9341_del(esp_lcd_panel_t *panel) -{ - ili9341_panel_t *ili9341 = __containerof(panel, ili9341_panel_t, base); - - if (ili9341->reset_gpio_num >= 0) { - gpio_reset_pin(ili9341->reset_gpio_num); - } - ESP_LOGD(TAG, "del ili9341 panel @%p", ili9341); - free(ili9341); - return ESP_OK; -} - -static esp_err_t panel_ili9341_reset(esp_lcd_panel_t *panel) -{ - ili9341_panel_t *ili9341 = __containerof(panel, ili9341_panel_t, base); - esp_lcd_panel_io_handle_t io = ili9341->io; - - // perform hardware reset - if (ili9341->reset_gpio_num >= 0) { - gpio_set_level(ili9341->reset_gpio_num, ili9341->reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); - gpio_set_level(ili9341->reset_gpio_num, !ili9341->reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); - } else { // perform software reset - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5ms before sending new command - } - - return ESP_OK; -} - -typedef struct { - uint8_t cmd; - uint8_t data[16]; - uint8_t data_bytes; // Length of data in above data array; 0xFF = end of cmds. -} lcd_init_cmd_t; - -static const ili9341_lcd_init_cmd_t vendor_specific_init_default[] = { -// {cmd, { data }, data_size, delay_ms} - /* Power contorl B, power control = 0, DC_ENA = 1 */ - {0xCF, (uint8_t []){0x00, 0xAA, 0XE0}, 3, 0}, - /* Power on sequence control, - * cp1 keeps 1 frame, 1st frame enable - * vcl = 0, ddvdh=3, vgh=1, vgl=2 - * DDVDH_ENH=1 - */ - {0xED, (uint8_t []){0x67, 0x03, 0X12, 0X81}, 4, 0}, - /* Driver timing control A, - * non-overlap=default +1 - * EQ=default - 1, CR=default - * pre-charge=default - 1 - */ - {0xE8, (uint8_t []){0x8A, 0x01, 0x78}, 3, 0}, - /* Power control A, Vcore=1.6V, DDVDH=5.6V */ - {0xCB, (uint8_t []){0x39, 0x2C, 0x00, 0x34, 0x02}, 5, 0}, - /* Pump ratio control, DDVDH=2xVCl */ - {0xF7, (uint8_t []){0x20}, 1, 0}, - - {0xF7, (uint8_t []){0x20}, 1, 0}, - /* Driver timing control, all=0 unit */ - {0xEA, (uint8_t []){0x00, 0x00}, 2, 0}, - /* Power control 1, GVDD=4.75V */ - {0xC0, (uint8_t []){0x23}, 1, 0}, - /* Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 */ - {0xC1, (uint8_t []){0x11}, 1, 0}, - /* VCOM control 1, VCOMH=4.025V, VCOML=-0.950V */ - {0xC5, (uint8_t []){0x43, 0x4C}, 2, 0}, - /* VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 */ - {0xC7, (uint8_t []){0xA0}, 1, 0}, - /* Frame rate control, f=fosc, 70Hz fps */ - {0xB1, (uint8_t []){0x00, 0x1B}, 2, 0}, - /* Enable 3G, disabled */ - {0xF2, (uint8_t []){0x00}, 1, 0}, - /* Gamma set, curve 1 */ - {0x26, (uint8_t []){0x01}, 1, 0}, - /* Positive gamma correction */ - {0xE0, (uint8_t []){0x1F, 0x36, 0x36, 0x3A, 0x0C, 0x05, 0x4F, 0X87, 0x3C, 0x08, 0x11, 0x35, 0x19, 0x13, 0x00}, 15, 0}, - /* Negative gamma correction */ - {0xE1, (uint8_t []){0x00, 0x09, 0x09, 0x05, 0x13, 0x0A, 0x30, 0x78, 0x43, 0x07, 0x0E, 0x0A, 0x26, 0x2C, 0x1F}, 15, 0}, - /* Entry mode set, Low vol detect disabled, normal display */ - {0xB7, (uint8_t []){0x07}, 1, 0}, - /* Display function control */ - {0xB6, (uint8_t []){0x08, 0x82, 0x27}, 3, 0}, -}; - -static esp_err_t panel_ili9341_init(esp_lcd_panel_t *panel) -{ - ili9341_panel_t *ili9341 = __containerof(panel, ili9341_panel_t, base); - esp_lcd_panel_io_handle_t io = ili9341->io; - - // LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0), TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(100)); - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - ili9341->madctl_val, - }, 1), TAG, "send command failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) { - ili9341->colmod_val, - }, 1), TAG, "send command failed"); - - const ili9341_lcd_init_cmd_t *init_cmds = NULL; - uint16_t init_cmds_size = 0; - if (ili9341->init_cmds) { - init_cmds = ili9341->init_cmds; - init_cmds_size = ili9341->init_cmds_size; - } else { - init_cmds = vendor_specific_init_default; - init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(ili9341_lcd_init_cmd_t); - } - - bool is_cmd_overwritten = false; - for (int i = 0; i < init_cmds_size; i++) { - // Check if the command has been used or conflicts with the internal - switch (init_cmds[i].cmd) { - case LCD_CMD_MADCTL: - is_cmd_overwritten = true; - ili9341->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; - break; - case LCD_CMD_COLMOD: - is_cmd_overwritten = true; - ili9341->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; - break; - default: - is_cmd_overwritten = false; - break; - } - - if (is_cmd_overwritten) { - ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", init_cmds[i].cmd); - } - - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); - } - ESP_LOGD(TAG, "send init commands success"); - - return ESP_OK; -} - -static esp_err_t panel_ili9341_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) -{ - ili9341_panel_t *ili9341 = __containerof(panel, ili9341_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 = ili9341->io; - - x_start += ili9341->x_gap; - x_end += ili9341->x_gap; - y_start += ili9341->y_gap; - y_end += ili9341->y_gap; - - // define an area of frame memory where MCU can access - 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"); - // transfer frame buffer - size_t len = (x_end - x_start) * (y_end - y_start) * ili9341->fb_bits_per_pixel / 8; - esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len); - - return ESP_OK; -} - -static esp_err_t panel_ili9341_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) -{ - ili9341_panel_t *ili9341 = __containerof(panel, ili9341_panel_t, base); - esp_lcd_panel_io_handle_t io = ili9341->io; - int command = 0; - if (invert_color_data) { - command = LCD_CMD_INVON; - } else { - command = LCD_CMD_INVOFF; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); - return ESP_OK; -} - -static esp_err_t panel_ili9341_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) -{ - ili9341_panel_t *ili9341 = __containerof(panel, ili9341_panel_t, base); - esp_lcd_panel_io_handle_t io = ili9341->io; - if (mirror_x) { - ili9341->madctl_val |= LCD_CMD_MX_BIT; - } else { - ili9341->madctl_val &= ~LCD_CMD_MX_BIT; - } - if (mirror_y) { - ili9341->madctl_val |= LCD_CMD_MY_BIT; - } else { - ili9341->madctl_val &= ~LCD_CMD_MY_BIT; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - ili9341->madctl_val - }, 1), TAG, "send command failed"); - return ESP_OK; -} - -static esp_err_t panel_ili9341_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) -{ - ili9341_panel_t *ili9341 = __containerof(panel, ili9341_panel_t, base); - esp_lcd_panel_io_handle_t io = ili9341->io; - if (swap_axes) { - ili9341->madctl_val |= LCD_CMD_MV_BIT; - } else { - ili9341->madctl_val &= ~LCD_CMD_MV_BIT; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - ili9341->madctl_val - }, 1), TAG, "send command failed"); - return ESP_OK; -} - -static esp_err_t panel_ili9341_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) -{ - ili9341_panel_t *ili9341 = __containerof(panel, ili9341_panel_t, base); - ili9341->x_gap = x_gap; - ili9341->y_gap = y_gap; - return ESP_OK; -} - -static esp_err_t panel_ili9341_disp_on_off(esp_lcd_panel_t *panel, bool on_off) -{ - ili9341_panel_t *ili9341 = __containerof(panel, ili9341_panel_t, base); - esp_lcd_panel_io_handle_t io = ili9341->io; - int command = 0; - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - on_off = !on_off; -#endif - - if (on_off) { - command = LCD_CMD_DISPON; - } else { - command = LCD_CMD_DISPOFF; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); - return ESP_OK; -} - -#endif \ No newline at end of file diff --git a/src/esp_lcd_st7701.c b/src/esp_lcd_st7701.c deleted file mode 100644 index 9726592..0000000 --- a/src/esp_lcd_st7701.c +++ /dev/null @@ -1,436 +0,0 @@ -#ifdef LCD_ST7701_PAR - -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - * - * Changed made bij rzeldent for esp32-4848S040: - * - Adapter to not use user_data (not available < IDF 5) - * - Use panel_ops for calling original functions - * - Replaced default initialization - */ - -#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_ops.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_rgb.h" -#include "esp_lcd_panel_vendor.h" -#include "esp_log.h" - -#include "esp_lcd_st7701.h" - -#define ST7701_CMD_SDIR (0xC7) -#define ST7701_CMD_SS_BIT (1 << 2) - -#define ST7701_CMD_CND2BKxSEL (0xFF) -#define ST7701_CMD_BKxSEL_BYTE0 (0x77) -#define ST7701_CMD_BKxSEL_BYTE1 (0x01) -#define ST7701_CMD_BKxSEL_BYTE2 (0x00) -#define ST7701_CMD_BKxSEL_BYTE3 (0x00) -#define ST7701_CMD_CN2_BIT (1 << 4) - -typedef struct { - esp_lcd_panel_t base; - esp_lcd_panel_io_handle_t io; - int reset_gpio_num; - uint8_t madctl_val; // Save current value of LCD_CMD_MADCTL register - uint8_t colmod_val; // Save current value of LCD_CMD_COLMOD register - const st7701_lcd_init_cmd_t *init_cmds; - uint16_t init_cmds_size; - struct { - unsigned int mirror_by_cmd: 1; - unsigned int auto_del_panel_io: 1; - unsigned int display_on_off_use_cmd: 1; - unsigned int reset_level: 1; - } flags; - // To save the original functions of RGB panel - esp_lcd_panel_t* original; -} st7701_panel_t; - -static const char *TAG = "st7701"; - -static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701); - -static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel); -static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel); -static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel); -static esp_err_t panel_st7701_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_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); -static esp_err_t panel_st7701_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); -static esp_err_t panel_st7701_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); -static esp_err_t panel_st7701_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); -static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool off); - -esp_err_t esp_lcd_new_panel_st7701(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_RETURN_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); - st7701_vendor_config_t *vendor_config = (st7701_vendor_config_t *)panel_dev_config->vendor_config; - ESP_RETURN_ON_FALSE(vendor_config && vendor_config->rgb_config, ESP_ERR_INVALID_ARG, TAG, "`vendor_config` and `rgb_config` are necessary"); - ESP_RETURN_ON_FALSE(!vendor_config->flags.auto_del_panel_io || !vendor_config->flags.mirror_by_cmd, - ESP_ERR_INVALID_ARG, TAG, "`mirror_by_cmd` and `auto_del_panel_io` cannot work together"); - - - esp_err_t ret = ESP_OK; - st7701_panel_t *st7701 = (st7701_panel_t *)calloc(1, sizeof(st7701_panel_t)); - ESP_RETURN_ON_FALSE(st7701, ESP_ERR_NO_MEM, TAG, "no mem for st7701 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"); - } -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - switch (panel_dev_config->color_space) { - case ESP_LCD_COLOR_SPACE_RGB: - st7701->madctl_val = 0; - break; - case ESP_LCD_COLOR_SPACE_BGR: - st7701->madctl_val |= LCD_CMD_BGR_BIT; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); - break; - } -#else - switch (panel_dev_config->rgb_endian) { - case LCD_RGB_ENDIAN_RGB: - st7701->madctl_val = 0; - break; - case LCD_RGB_ENDIAN_BGR: - st7701->madctl_val |= LCD_CMD_BGR_BIT; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported rgb endian"); - break; - } -#endif - st7701->colmod_val = 0; - switch (panel_dev_config->bits_per_pixel) { - case 16: // RGB565 - st7701->colmod_val = 0x50; - break; - case 18: // RGB666 - st7701->colmod_val = 0x60; - break; - case 24: // RGB888 - st7701->colmod_val = 0x70; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); - break; - } - - st7701->io = io; - st7701->init_cmds = vendor_config->init_cmds; - st7701->init_cmds_size = vendor_config->init_cmds_size; - st7701->reset_gpio_num = panel_dev_config->reset_gpio_num; - st7701->flags.mirror_by_cmd = vendor_config->flags.mirror_by_cmd; - st7701->flags.display_on_off_use_cmd = (vendor_config->rgb_config->disp_gpio_num >= 0) ? 0 : 1; - st7701->flags.auto_del_panel_io = vendor_config->flags.auto_del_panel_io; - st7701->flags.reset_level = panel_dev_config->flags.reset_active_high; - - if (st7701->flags.auto_del_panel_io) { - if (st7701->reset_gpio_num >= 0) { // Perform hardware reset - gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); - gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level); - } else { // Perform software reset - ESP_GOTO_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), err, TAG, "send command failed"); - } - vTaskDelay(pdMS_TO_TICKS(120)); - - /** - * In order to enable the 3-wire SPI interface pins (such as SDA and SCK) to share other pins of the RGB interface - * (such as HSYNC) and save GPIOs, we need to send LCD initialization commands via the 3-wire SPI interface before - * `esp_lcd_new_rgb_panel()` is called. - */ - ESP_GOTO_ON_ERROR(panel_st7701_send_init_cmds(st7701), err, TAG, "send init commands failed"); - // After sending the initialization commands, the 3-wire SPI interface can be deleted - ESP_GOTO_ON_ERROR(esp_lcd_panel_io_del(io), err, TAG, "delete panel IO failed"); - st7701->io = NULL; - ESP_LOGD(TAG, "delete panel IO"); - } - - // Create RGB panel - ESP_GOTO_ON_ERROR(esp_lcd_new_rgb_panel(vendor_config->rgb_config, &st7701->original), err, TAG, "create RGB panel failed"); - ESP_LOGD(TAG, "new RGB panel @%p", st7701->original); - - st7701->base.reset = panel_st7701_reset; - st7701->base.init = panel_st7701_init; - st7701->base.del = panel_st7701_del; - st7701->base.draw_bitmap = panel_st7701_draw_bitmap; - st7701->base.mirror = panel_st7701_mirror; - st7701->base.swap_xy = panel_st7701_swap_xy; - st7701->base.set_gap = panel_st7701_set_gap; - st7701->base.invert_color = panel_st7701_invert_color; - st7701->base.disp_off = panel_st7701_disp_on_off; - - *ret_panel = &(st7701->base); - ESP_LOGD(TAG, "new st7701 panel @%p", st7701); - - ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_ST7701_VER_MAJOR, ESP_LCD_ST7701_VER_MINOR, - ESP_LCD_ST7701_VER_PATCH); - - return ESP_OK; - -err: - if (st7701) { - if (panel_dev_config->reset_gpio_num >= 0) { - gpio_reset_pin(panel_dev_config->reset_gpio_num); - } - free(st7701); - } - return ret; -} - -// Init taken from Arduino_GFX as the stock st7701 (provided by EspressIf did not work) -static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = { -// {cmd, { data }, data_size, delay_ms} - {0xFF, (uint8_t[]){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0}, - {0xC0, (uint8_t[]){0x3B, 0x00}, 2, 0}, - {0xC1, (uint8_t[]){0x0D, 0x02}, 2, 0}, - {0xC2, (uint8_t[]){0x31, 0x05}, 2, 0}, - {0xCD, (uint8_t[]){0x00}, 1, 0}, - // Positive Voltage Gamma Control - {0xB0, (uint8_t[]){0x00, 0x11, 0x18, 0x0E, 0x11, 0x06, 0x07, 0x08, 0x07, 0x22, 0x04, 0x12, 0x0F, 0xAA, 0x31, 0x18}, 16, 0}, - // Negative Voltage Gamma Control - {0xB1, (uint8_t[]){0x00, 0x11, 0x19, 0x0E, 0x12, 0x07, 0x08, 0x08, 0x08, 0x22, 0x04, 0x11, 0x11, 0xA9, 0x32, 0x18}, 16, 0}, - // PAGE1 - {0xFF, (uint8_t[]){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0}, - {0xB0, (uint8_t[]){0x60}, 1, 0}, // Vop=4.7375v - {0xB1, (uint8_t[]){0x32}, 1, 0}, // VCOM=32 - {0xB2, (uint8_t[]){0x07}, 1, 0}, // VGH=15v - {0xB3, (uint8_t[]){0x80}, 1, 0}, - {0xB5, (uint8_t[]){0x49}, 1, 0}, // VGL=-10.17v - {0xB7, (uint8_t[]){0x85}, 1, 0}, - {0xB8, (uint8_t[]){0x21}, 1, 0}, // AVDD=6.6 & AVCL=-4.6 - {0xC1, (uint8_t[]){0x78}, 1, 0}, - {0xC2, (uint8_t[]){0x78}, 1, 0}, - {0xE0, (uint8_t[]){0x00, 0x1B, 0x02}, 3, 0}, - {0xE1, (uint8_t[]){0x08, 0xA0, 0x00, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x44, 0x44}, 11, 0}, - {0xE2, (uint8_t[]){0x11, 0x11, 0x44, 0x44, 0xED, 0xA0, 0x00, 0x00, 0xEC, 0xA0, 0x00, 0x00}, 12, 0}, - {0xE3, (uint8_t[]){0x00, 0x00, 0x11, 0x11}, 4, 0}, - {0xE4, (uint8_t[]){0x44, 0x44}, 2, 0}, - {0xE5, (uint8_t[]){0x0A, 0xE9, 0xD8, 0xA0, 0x0C, 0xEB, 0xD8, 0xA0, 0x0E, 0xED, 0xD8, 0xA0, 0x10, 0xEF, 0xD8, 0xA0}, 16, 0}, - {0xE6, (uint8_t[]){0x00, 0x00, 0x11, 0x11}, 4, 0}, - {0xE7, (uint8_t[]){0x44, 0x44}, 2, 0}, - {0xE8, (uint8_t[]){0x09, 0xE8, 0xD8, 0xA0, 0x0B, 0xEA, 0xD8, 0xA0, 0x0D, 0xEC, 0xD8, 0xA0, 0x0F, 0xEE, 0xD8, 0xA0}, 16, 0}, - {0xEB, (uint8_t[]){0x02, 0x00, 0xE4, 0xE4, 0x88, 0x00, 0x40}, 7, 0}, - {0xEC, (uint8_t[]){0x3C, 0x00}, 2, 0}, - {0xED, (uint8_t[]){0xAB, 0x89, 0x76, 0x54, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x45, 0x67, 0x98, 0xBA}, 16, 0}, - // VAP & VAN - {0xFF, (uint8_t[]){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0}, - {0xE5, (uint8_t[]){0xE4}, 1, 0}, - {0xFF, (uint8_t[]){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0}, - // 0x70 RGB888, 0x60 RGB666, 0x50 RGB565 - {0x3A, (uint8_t[]){0x60}, 1, 0}, - // Sleep Out - {0x11, NULL, 0, 120}, - // Display On - {0x29, NULL, 0, 0} -}; - -static esp_err_t panel_st7701_send_init_cmds(st7701_panel_t *st7701) -{ - esp_lcd_panel_io_handle_t io = st7701->io; - const st7701_lcd_init_cmd_t *init_cmds = NULL; - uint16_t init_cmds_size = 0; - bool is_command2_disable = true; - bool is_cmd_overwritten = false; - - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_CND2BKxSEL, (uint8_t []){ - ST7701_CMD_BKxSEL_BYTE0, ST7701_CMD_BKxSEL_BYTE1, ST7701_CMD_BKxSEL_BYTE2, ST7701_CMD_BKxSEL_BYTE3, 0x00 - }, 5), TAG, "Write cmd failed"); - // Set color format - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t []){ - st7701->madctl_val - }, 1), TAG, "Write cmd failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t []){ - st7701->colmod_val - }, 1), TAG, "Write cmd failed"); - - // vendor specific initialization, it can be different between manufacturers - // should consult the LCD supplier for initialization sequence code - if (st7701->init_cmds) { - init_cmds = st7701->init_cmds; - init_cmds_size = st7701->init_cmds_size; - } else { - init_cmds = vendor_specific_init_default; - init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t); - } - - for (int i = 0; i < init_cmds_size; i++) { - // Check if the command has been used or conflicts with the internal only when command2 is disable - if (is_command2_disable && (init_cmds[i].data_bytes > 0)) { - switch (init_cmds[i].cmd) { - case LCD_CMD_MADCTL: - is_cmd_overwritten = true; - st7701->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; - break; - case LCD_CMD_COLMOD: - is_cmd_overwritten = true; - st7701->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; - break; - default: - is_cmd_overwritten = false; - break; - } - - if (is_cmd_overwritten) { - is_cmd_overwritten = false; - ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", - init_cmds[i].cmd); - } - } - - // Send command - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), - TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); - - // Check if the current cmd is the command2 disable cmd - if ((init_cmds[i].cmd == ST7701_CMD_CND2BKxSEL) && (init_cmds[i].data_bytes > 4)) { - is_command2_disable = !(((uint8_t *)init_cmds[i].data)[4] & ST7701_CMD_CN2_BIT); - } - } - ESP_LOGD(TAG, "send init commands success"); - - return ESP_OK; -} - -static esp_err_t panel_st7701_init(esp_lcd_panel_t *panel) -{ - st7701_panel_t *st7701 = __containerof(panel, st7701_panel_t, base); - - if (!st7701->flags.auto_del_panel_io) { - ESP_RETURN_ON_ERROR(panel_st7701_send_init_cmds(st7701), TAG, "send init commands failed"); - } - // Init RGB panel - ESP_RETURN_ON_ERROR(esp_lcd_panel_init(st7701->original), TAG, "init RGB panel failed"); - - return ESP_OK; -} - -static esp_err_t panel_st7701_del(esp_lcd_panel_t *panel) -{ - st7701_panel_t *st7701 = __containerof(panel, st7701_panel_t, base); - - if (st7701->reset_gpio_num >= 0) { - gpio_reset_pin(st7701->reset_gpio_num); - } - // Delete RGB panel - esp_lcd_panel_del(st7701->original); - free(st7701); - ESP_LOGD(TAG, "del st7701 panel @%p", st7701); - return ESP_OK; -} - -static esp_err_t panel_st7701_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) -{ - st7701_panel_t *st7701 = __containerof(panel, st7701_panel_t, base); - return esp_lcd_panel_draw_bitmap(st7701->original, x_start, y_start, x_end, y_end, color_data); -} - -static esp_err_t panel_st7701_reset(esp_lcd_panel_t *panel) -{ - st7701_panel_t *st7701 = __containerof(panel, st7701_panel_t, base); - esp_lcd_panel_io_handle_t io = st7701->io; - - // Perform hardware reset - if (st7701->reset_gpio_num >= 0) { - gpio_set_level(st7701->reset_gpio_num, st7701->flags.reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); - gpio_set_level(st7701->reset_gpio_num, !st7701->flags.reset_level); - vTaskDelay(pdMS_TO_TICKS(120)); - } else if (io) { // Perform software reset - 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)); - } - // Reset RGB panel - ESP_RETURN_ON_ERROR(esp_lcd_panel_reset(st7701->original), TAG, "reset RGB panel failed"); - - return ESP_OK; -} - -static esp_err_t panel_st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) -{ - st7701_panel_t *st7701 = __containerof(panel, st7701_panel_t, base); - esp_lcd_panel_io_handle_t io = st7701->io; - uint8_t sdir_val = 0; - - if (st7701->flags.mirror_by_cmd) { - ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command"); - // Control mirror through LCD command - if (mirror_x) { - sdir_val = ST7701_CMD_SS_BIT; - } else { - sdir_val = 0; - } - if (mirror_y) { - st7701->madctl_val |= LCD_CMD_ML_BIT; - } else { - st7701->madctl_val &= ~LCD_CMD_ML_BIT; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, ST7701_CMD_SDIR, (uint8_t[]) { - sdir_val, - }, 1), TAG, "send command failed");; - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - st7701->madctl_val, - }, 1), TAG, "send command failed");; - } else { - // Control mirror through RGB panel - ESP_RETURN_ON_ERROR(esp_lcd_panel_mirror(st7701->original, mirror_x, mirror_y), TAG, "RGB panel mirror failed"); - } - return ESP_OK; -} - -static esp_err_t panel_st7701_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) -{ - st7701_panel_t *st7701 = __containerof(panel, st7701_panel_t, base); - return esp_lcd_panel_swap_xy(st7701->original, swap_axes); -} - -static esp_err_t panel_st7701_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) -{ - st7701_panel_t *st7701 = __containerof(panel, st7701_panel_t, base); - return esp_lcd_panel_set_gap(st7701->original, x_gap, y_gap); -} - -static esp_err_t panel_st7701_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) -{ - st7701_panel_t *st7701 = __containerof(panel, st7701_panel_t, base); - return esp_lcd_panel_invert_color(st7701->original, invert_color_data); -} - -static esp_err_t panel_st7701_disp_on_off(esp_lcd_panel_t *panel, bool on_off) -{ - st7701_panel_t *st7701 = __containerof(panel, st7701_panel_t, base); - esp_lcd_panel_io_handle_t io = st7701->io; - int command = 0; - - if (st7701->flags.display_on_off_use_cmd) { - ESP_RETURN_ON_FALSE(io, ESP_FAIL, TAG, "Panel IO is deleted, cannot send command"); - // Control display on/off through LCD command - if (on_off) { - command = LCD_CMD_DISPON; - } else { - command = LCD_CMD_DISPOFF; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); - } else { - // Control display on/off through display control signal - ESP_RETURN_ON_ERROR(esp_lcd_panel_disp_off(st7701->original, on_off), TAG, "RGB panel disp_off failed"); - } - return ESP_OK; -} - -#endif \ No newline at end of file diff --git a/src/esp_lcd_st7796.c b/src/esp_lcd_st7796.c deleted file mode 100644 index 72db32e..0000000 --- a/src/esp_lcd_st7796.c +++ /dev/null @@ -1,363 +0,0 @@ -#ifdef LCD_ST7796_SPI - -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_lcd_panel_interface.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_panel_vendor.h" -#include "esp_lcd_panel_ops.h" -#include "esp_lcd_panel_commands.h" -#include "driver/gpio.h" -#include "esp_log.h" -#include "esp_check.h" - -#include "esp_lcd_st7796.h" - -static const char *TAG = "st7796"; - -static esp_err_t panel_st7796_del(esp_lcd_panel_t *panel); -static esp_err_t panel_st7796_reset(esp_lcd_panel_t *panel); -static esp_err_t panel_st7796_init(esp_lcd_panel_t *panel); -static esp_err_t panel_st7796_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_st7796_invert_color(esp_lcd_panel_t *panel, bool invert_color_data); -static esp_err_t panel_st7796_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y); -static esp_err_t panel_st7796_swap_xy(esp_lcd_panel_t *panel, bool swap_axes); -static esp_err_t panel_st7796_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap); -static esp_err_t panel_st7796_disp_on_off(esp_lcd_panel_t *panel, bool off); - -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; // save current value of LCD_CMD_MADCTL register - uint8_t colmod_val; // save current value of LCD_CMD_COLMOD register - const st7796_lcd_init_cmd_t *init_cmds; - uint16_t init_cmds_size; -} st7796_panel_t; - -esp_err_t esp_lcd_new_panel_st7796(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; - st7796_panel_t *st7796 = NULL; - gpio_config_t io_conf = { 0 }; - - ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); - st7796 = (st7796_panel_t *)calloc(1, sizeof(st7796_panel_t)); - ESP_GOTO_ON_FALSE(st7796, ESP_ERR_NO_MEM, err, TAG, "no mem for st7796 panel"); - - if (panel_dev_config->reset_gpio_num >= 0) { - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.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"); - } - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - switch (panel_dev_config->color_space) { - case ESP_LCD_COLOR_SPACE_RGB: - st7796->madctl_val = 0; - break; - case ESP_LCD_COLOR_SPACE_BGR: - st7796->madctl_val |= LCD_CMD_BGR_BIT; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space"); - break; - } -#else - switch (panel_dev_config->rgb_endian) { - case LCD_RGB_ENDIAN_RGB: - st7796->madctl_val = 0; - break; - case LCD_RGB_ENDIAN_BGR: - st7796->madctl_val |= LCD_CMD_BGR_BIT; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported rgb endian"); - break; - } -#endif - - switch (panel_dev_config->bits_per_pixel) { - case 16: // RGB565 - st7796->colmod_val = 0x05; - st7796->fb_bits_per_pixel = 16; - break; - case 18: // RGB666 - st7796->colmod_val = 0x06; - // each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel - st7796->fb_bits_per_pixel = 24; - break; - case 24: // RGB888 - st7796->colmod_val = 0x07; - st7796->fb_bits_per_pixel = 24; - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width"); - break; - } - - st7796->io = io; - st7796->reset_gpio_num = panel_dev_config->reset_gpio_num; - st7796->reset_level = panel_dev_config->flags.reset_active_high; - if (panel_dev_config->vendor_config) { - st7796->init_cmds = ((st7796_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds; - st7796->init_cmds_size = ((st7796_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds_size; - } - st7796->base.del = panel_st7796_del; - st7796->base.reset = panel_st7796_reset; - st7796->base.init = panel_st7796_init; - st7796->base.draw_bitmap = panel_st7796_draw_bitmap; - st7796->base.invert_color = panel_st7796_invert_color; - st7796->base.set_gap = panel_st7796_set_gap; - st7796->base.mirror = panel_st7796_mirror; - st7796->base.swap_xy = panel_st7796_swap_xy; -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - st7796->base.disp_off = panel_st7796_disp_on_off; -#else - st7796->base.disp_on_off = panel_st7796_disp_on_off; -#endif - *ret_panel = &(st7796->base); - ESP_LOGD(TAG, "new st7796 panel @%p", st7796); - - ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_ST7796_VER_MAJOR, ESP_LCD_ST7796_VER_MINOR, - ESP_LCD_ST7796_VER_PATCH); - - return ESP_OK; - -err: - if (st7796) { - if (panel_dev_config->reset_gpio_num >= 0) { - gpio_reset_pin(panel_dev_config->reset_gpio_num); - } - free(st7796); - } - return ret; -} - -static esp_err_t panel_st7796_del(esp_lcd_panel_t *panel) -{ - st7796_panel_t *st7796 = __containerof(panel, st7796_panel_t, base); - - if (st7796->reset_gpio_num >= 0) { - gpio_reset_pin(st7796->reset_gpio_num); - } - ESP_LOGD(TAG, "del st7796 panel @%p", st7796); - free(st7796); - return ESP_OK; -} - -static esp_err_t panel_st7796_reset(esp_lcd_panel_t *panel) -{ - st7796_panel_t *st7796 = __containerof(panel, st7796_panel_t, base); - esp_lcd_panel_io_handle_t io = st7796->io; - - // perform hardware reset - if (st7796->reset_gpio_num >= 0) { - gpio_set_level(st7796->reset_gpio_num, st7796->reset_level); - vTaskDelay(pdMS_TO_TICKS(10)); - gpio_set_level(st7796->reset_gpio_num, !st7796->reset_level); - vTaskDelay(pdMS_TO_TICKS(120)); - } else { // perform software reset - 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)); // spec, wait at least 5ms before sending new command - } - - return ESP_OK; -} - -typedef struct { - uint8_t cmd; - uint8_t data[16]; - uint8_t data_bytes; // Length of data in above data array; 0xFF = end of cmds. -} lcd_init_cmd_t; - -static const st7796_lcd_init_cmd_t vendor_specific_init_default[] = { -// {cmd, { data }, data_size, delay_ms} - {0xf0, (uint8_t []){0xc3}, 1, 0}, - {0xf0, (uint8_t []){0x96}, 1, 0}, - {0xb4, (uint8_t []){0x01}, 1, 0}, - {0xb7, (uint8_t []){0xc6}, 1, 0}, - {0xe8, (uint8_t []){0x40, 0x8a, 0x00, 0x00, 0x29, 0x19, 0xa5, 0x33}, 8, 0}, - {0xc1, (uint8_t []){0x06}, 1, 0}, - {0xc2, (uint8_t []){0xa7}, 1, 0}, - {0xc5, (uint8_t []){0x18}, 1, 0}, - {0xe0, (uint8_t []){0xf0, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0x54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x1b}, 14, 0}, - {0xe1, (uint8_t []){0xf0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2d, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x1b}, 14, 0}, - {0xf0, (uint8_t []){0x3c}, 1, 0}, - {0xf0, (uint8_t []){0x69}, 1, 0}, -}; - -static esp_err_t panel_st7796_init(esp_lcd_panel_t *panel) -{ - st7796_panel_t *st7796 = __containerof(panel, st7796_panel_t, base); - esp_lcd_panel_io_handle_t io = st7796->io; - - // LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0), TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(100)); - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - st7796->madctl_val, - }, 1), TAG, "send command failed"); - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) { - st7796->colmod_val, - }, 1), TAG, "send command failed"); - - const st7796_lcd_init_cmd_t *init_cmds = NULL; - uint16_t init_cmds_size = 0; - if (st7796->init_cmds) { - init_cmds = st7796->init_cmds; - init_cmds_size = st7796->init_cmds_size; - } else { - init_cmds = vendor_specific_init_default; - init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7796_lcd_init_cmd_t); - } - - bool is_cmd_overwritten = false; - for (int i = 0; i < init_cmds_size; i++) { - // Check if the command has been used or conflicts with the internal - switch (init_cmds[i].cmd) { - case LCD_CMD_MADCTL: - is_cmd_overwritten = true; - st7796->madctl_val = ((uint8_t *)init_cmds[i].data)[0]; - break; - case LCD_CMD_COLMOD: - is_cmd_overwritten = true; - st7796->colmod_val = ((uint8_t *)init_cmds[i].data)[0]; - break; - default: - is_cmd_overwritten = false; - break; - } - - if (is_cmd_overwritten) { - ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", init_cmds[i].cmd); - } - - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed"); - vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms)); - } - ESP_LOGD(TAG, "send init commands success"); - - return ESP_OK; -} - -static esp_err_t panel_st7796_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) -{ - st7796_panel_t *st7796 = __containerof(panel, st7796_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 = st7796->io; - - x_start += st7796->x_gap; - x_end += st7796->x_gap; - y_start += st7796->y_gap; - y_end += st7796->y_gap; - - // define an area of frame memory where MCU can access - 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"); - // transfer frame buffer - size_t len = (x_end - x_start) * (y_end - y_start) * st7796->fb_bits_per_pixel / 8; - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len), TAG, "send command failed"); - - return ESP_OK; -} - -static esp_err_t panel_st7796_invert_color(esp_lcd_panel_t *panel, bool invert_color_data) -{ - st7796_panel_t *st7796 = __containerof(panel, st7796_panel_t, base); - esp_lcd_panel_io_handle_t io = st7796->io; - int command = 0; - if (invert_color_data) { - command = LCD_CMD_INVON; - } else { - command = LCD_CMD_INVOFF; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); - return ESP_OK; -} - -static esp_err_t panel_st7796_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) -{ - st7796_panel_t *st7796 = __containerof(panel, st7796_panel_t, base); - esp_lcd_panel_io_handle_t io = st7796->io; - if (mirror_x) { - st7796->madctl_val |= LCD_CMD_MX_BIT; - } else { - st7796->madctl_val &= ~LCD_CMD_MX_BIT; - } - if (mirror_y) { - st7796->madctl_val |= LCD_CMD_MY_BIT; - } else { - st7796->madctl_val &= ~LCD_CMD_MY_BIT; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - st7796->madctl_val - }, 1), TAG, "send command failed"); - return ESP_OK; -} - -static esp_err_t panel_st7796_swap_xy(esp_lcd_panel_t *panel, bool swap_axes) -{ - st7796_panel_t *st7796 = __containerof(panel, st7796_panel_t, base); - esp_lcd_panel_io_handle_t io = st7796->io; - if (swap_axes) { - st7796->madctl_val |= LCD_CMD_MV_BIT; - } else { - st7796->madctl_val &= ~LCD_CMD_MV_BIT; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) { - st7796->madctl_val - }, 1), TAG, "send command failed"); - return ESP_OK; -} - -static esp_err_t panel_st7796_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) -{ - st7796_panel_t *st7796 = __containerof(panel, st7796_panel_t, base); - st7796->x_gap = x_gap; - st7796->y_gap = y_gap; - return ESP_OK; -} - -static esp_err_t panel_st7796_disp_on_off(esp_lcd_panel_t *panel, bool on_off) -{ - st7796_panel_t *st7796 = __containerof(panel, st7796_panel_t, base); - esp_lcd_panel_io_handle_t io = st7796->io; - int command = 0; - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - on_off = !on_off; -#endif - - if (on_off) { - command = LCD_CMD_DISPON; - } else { - command = LCD_CMD_DISPOFF; - } - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed"); - return ESP_OK; -} - -#endif \ No newline at end of file diff --git a/src/esp_lcd_touch.c b/src/esp_lcd_touch.c index 6a72c86..779455e 100644 --- a/src/esp_lcd_touch.c +++ b/src/esp_lcd_touch.c @@ -267,5 +267,4 @@ esp_err_t esp_lcd_touch_register_interrupt_callback_with_data(esp_lcd_touch_hand return esp_lcd_touch_register_interrupt_callback(tp, callback); } - -#endif \ No newline at end of file +#endif // BOARD_HAS_TOUCH \ No newline at end of file diff --git a/src/esp_lcd_touch_cst816s.c b/src/esp_lcd_touch_cst816s.c deleted file mode 100644 index 081fa19..0000000 --- a/src/esp_lcd_touch_cst816s.c +++ /dev/null @@ -1,188 +0,0 @@ -#ifdef TOUCH_CST816S_I2C - -/* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "driver/gpio.h" -#include "driver/i2c.h" -#include "esp_system.h" -#include "esp_err.h" -#include "esp_log.h" -#include "esp_check.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_touch.h" - -#define POINT_NUM_MAX (1) - -#define DATA_START_REG (0x02) -#define CHIP_ID_REG (0xA7) - -static const char *TAG = "CST816S"; - -static esp_err_t read_data(esp_lcd_touch_handle_t tp); -static bool get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num); -static esp_err_t del(esp_lcd_touch_handle_t tp); - -static esp_err_t i2c_read_bytes(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len); - -static esp_err_t reset(esp_lcd_touch_handle_t tp); -static esp_err_t read_id(esp_lcd_touch_handle_t tp); - -esp_err_t esp_lcd_touch_new_i2c_cst816s(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *tp) -{ - ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_ARG, TAG, "Invalid io"); - ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "Invalid config"); - ESP_RETURN_ON_FALSE(tp, ESP_ERR_INVALID_ARG, TAG, "Invalid touch handle"); - - /* Prepare main structure */ - esp_err_t ret = ESP_OK; - esp_lcd_touch_handle_t cst816s = calloc(1, sizeof(esp_lcd_touch_t)); - ESP_GOTO_ON_FALSE(cst816s, ESP_ERR_NO_MEM, err, TAG, "Touch handle malloc failed"); - - /* Communication interface */ - cst816s->io = io; - /* Only supported callbacks are set */ - cst816s->read_data = read_data; - cst816s->get_xy = get_xy; - cst816s->del = del; - /* Mutex */ - cst816s->data.lock.owner = portMUX_FREE_VAL; - /* Save config */ - memcpy(&cst816s->config, config, sizeof(esp_lcd_touch_config_t)); - - /* Prepare pin for touch interrupt */ - if (cst816s->config.int_gpio_num != GPIO_NUM_NC) { - const gpio_config_t int_gpio_config = { - .mode = GPIO_MODE_INPUT, - .intr_type = GPIO_INTR_NEGEDGE, - .pin_bit_mask = BIT64(cst816s->config.int_gpio_num) - }; - ESP_GOTO_ON_ERROR(gpio_config(&int_gpio_config), err, TAG, "GPIO intr config failed"); - - /* Register interrupt callback */ - if (cst816s->config.interrupt_callback) { - esp_lcd_touch_register_interrupt_callback(cst816s, cst816s->config.interrupt_callback); - } - } - /* Prepare pin for touch controller reset */ - if (cst816s->config.rst_gpio_num != GPIO_NUM_NC) { - const gpio_config_t rst_gpio_config = { - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = BIT64(cst816s->config.rst_gpio_num) - }; - ESP_GOTO_ON_ERROR(gpio_config(&rst_gpio_config), err, TAG, "GPIO reset config failed"); - } - /* Reset controller */ - ESP_GOTO_ON_ERROR(reset(cst816s), err, TAG, "Reset failed"); - /* Read product id */ - ESP_GOTO_ON_ERROR(read_id(cst816s), err, TAG, "Read version failed"); - *tp = cst816s; - - return ESP_OK; -err: - if (cst816s) { - del(cst816s); - } - ESP_LOGE(TAG, "Initialization failed!"); - return ret; -} - -static esp_err_t read_data(esp_lcd_touch_handle_t tp) -{ - typedef struct { - uint8_t num; - uint8_t x_h : 4; - uint8_t : 4; - uint8_t x_l; - uint8_t y_h : 4; - uint8_t : 4; - uint8_t y_l; - } data_t; - - data_t point; - ESP_RETURN_ON_ERROR(i2c_read_bytes(tp, DATA_START_REG, (uint8_t *)&point, sizeof(data_t)), TAG, "I2C read failed"); - - portENTER_CRITICAL(&tp->data.lock); - point.num = (point.num > POINT_NUM_MAX ? POINT_NUM_MAX : point.num); - tp->data.points = point.num; - /* Fill all coordinates */ - for (int i = 0; i < point.num; i++) { - tp->data.coords[i].x = point.x_h << 8 | point.x_l; - tp->data.coords[i].y = point.y_h << 8 | point.y_l; - } - portEXIT_CRITICAL(&tp->data.lock); - - return ESP_OK; -} - -static bool get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num) -{ - portENTER_CRITICAL(&tp->data.lock); - /* Count of points */ - *point_num = (tp->data.points > max_point_num ? max_point_num : tp->data.points); - for (size_t i = 0; i < *point_num; i++) { - x[i] = tp->data.coords[i].x; - y[i] = tp->data.coords[i].y; - - if (strength) { - strength[i] = tp->data.coords[i].strength; - } - } - /* Invalidate */ - tp->data.points = 0; - portEXIT_CRITICAL(&tp->data.lock); - - return (*point_num > 0); -} - -static esp_err_t del(esp_lcd_touch_handle_t tp) -{ - /* Reset GPIO pin settings */ - if (tp->config.int_gpio_num != GPIO_NUM_NC) { - gpio_reset_pin(tp->config.int_gpio_num); - } - if (tp->config.rst_gpio_num != GPIO_NUM_NC) { - gpio_reset_pin(tp->config.rst_gpio_num); - } - /* Release memory */ - free(tp); - - return ESP_OK; -} - -static esp_err_t reset(esp_lcd_touch_handle_t tp) -{ - if (tp->config.rst_gpio_num != GPIO_NUM_NC) { - ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, tp->config.levels.reset), TAG, "GPIO set level failed"); - vTaskDelay(pdMS_TO_TICKS(200)); - ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, !tp->config.levels.reset), TAG, "GPIO set level failed"); - vTaskDelay(pdMS_TO_TICKS(200)); - } - - return ESP_OK; -} - -static esp_err_t read_id(esp_lcd_touch_handle_t tp) -{ - uint8_t id; - ESP_RETURN_ON_ERROR(i2c_read_bytes(tp, CHIP_ID_REG, &id, 1), TAG, "I2C read failed"); - ESP_LOGI(TAG, "IC id: %d", id); - return ESP_OK; -} - -static esp_err_t i2c_read_bytes(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len) -{ - ESP_RETURN_ON_FALSE(data, ESP_ERR_INVALID_ARG, TAG, "Invalid data"); - - return esp_lcd_panel_io_rx_param(tp->io, reg, data, len); -} - -#endif \ No newline at end of file diff --git a/src/esp_lcd_touch_gt911.c b/src/esp_lcd_touch_gt911.c deleted file mode 100644 index 7fb2fae..0000000 --- a/src/esp_lcd_touch_gt911.c +++ /dev/null @@ -1,382 +0,0 @@ -#ifdef TOUCH_GT911_I2C - -/* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_system.h" -#include "esp_err.h" -#include "esp_log.h" -#include "esp_check.h" -#include "driver/gpio.h" -#include "driver/i2c.h" -#include "esp_lcd_panel_io.h" -#include "esp_lcd_touch.h" - -static const char *TAG = "GT911"; - -/* GT911 registers */ -#define ESP_LCD_TOUCH_GT911_READ_KEY_REG (0x8093) -#define ESP_LCD_TOUCH_GT911_READ_XY_REG (0x814E) -#define ESP_LCD_TOUCH_GT911_CONFIG_REG (0x8047) -#define ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG (0x8140) -#define ESP_LCD_TOUCH_GT911_ENTER_SLEEP (0x8040) - -/* GT911 support key num */ -#define ESP_GT911_TOUCH_MAX_BUTTONS (4) - -/******************************************************************************* -* Function definitions -*******************************************************************************/ -static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp); -static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num); -#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0) -static esp_err_t esp_lcd_touch_gt911_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state); -#endif -static esp_err_t esp_lcd_touch_gt911_del(esp_lcd_touch_handle_t tp); - -/* I2C read/write */ -static esp_err_t touch_gt911_i2c_read(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len); -static esp_err_t touch_gt911_i2c_write(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t data); - -/* GT911 reset */ -static esp_err_t touch_gt911_reset(esp_lcd_touch_handle_t tp); -/* Read status and config register */ -static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp); - -/* GT911 enter/exit sleep mode */ -static esp_err_t esp_lcd_touch_gt911_enter_sleep(esp_lcd_touch_handle_t tp); -static esp_err_t esp_lcd_touch_gt911_exit_sleep(esp_lcd_touch_handle_t tp); - -/******************************************************************************* -* Public API functions -*******************************************************************************/ - -esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *out_touch) -{ - esp_err_t ret = ESP_OK; - - assert(io != NULL); - assert(config != NULL); - assert(out_touch != NULL); - - /* Prepare main structure */ - esp_lcd_touch_handle_t esp_lcd_touch_gt911 = heap_caps_calloc(1, sizeof(esp_lcd_touch_t), MALLOC_CAP_DEFAULT); - ESP_GOTO_ON_FALSE(esp_lcd_touch_gt911, ESP_ERR_NO_MEM, err, TAG, "no mem for GT911 controller"); - - /* Communication interface */ - esp_lcd_touch_gt911->io = io; - - /* Only supported callbacks are set */ - esp_lcd_touch_gt911->read_data = esp_lcd_touch_gt911_read_data; - esp_lcd_touch_gt911->get_xy = esp_lcd_touch_gt911_get_xy; -#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0) - esp_lcd_touch_gt911->get_button_state = esp_lcd_touch_gt911_get_button_state; -#endif - esp_lcd_touch_gt911->del = esp_lcd_touch_gt911_del; - esp_lcd_touch_gt911->enter_sleep = esp_lcd_touch_gt911_enter_sleep; - esp_lcd_touch_gt911->exit_sleep = esp_lcd_touch_gt911_exit_sleep; - - /* Mutex */ - esp_lcd_touch_gt911->data.lock.owner = portMUX_FREE_VAL; - - /* Save config */ - memcpy(&esp_lcd_touch_gt911->config, config, sizeof(esp_lcd_touch_config_t)); - - /* Prepare pin for touch interrupt */ - if (esp_lcd_touch_gt911->config.int_gpio_num != GPIO_NUM_NC) { - const gpio_config_t int_gpio_config = { - .mode = GPIO_MODE_INPUT, - .intr_type = (esp_lcd_touch_gt911->config.levels.interrupt ? GPIO_INTR_POSEDGE : GPIO_INTR_NEGEDGE), - .pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.int_gpio_num) - }; - ret = gpio_config(&int_gpio_config); - ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed"); - - /* Register interrupt callback */ - if (esp_lcd_touch_gt911->config.interrupt_callback) { - esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_gt911, esp_lcd_touch_gt911->config.interrupt_callback); - } - } - - /* Prepare pin for touch controller reset */ - if (esp_lcd_touch_gt911->config.rst_gpio_num != GPIO_NUM_NC) { - const gpio_config_t rst_gpio_config = { - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.rst_gpio_num) - }; - ret = gpio_config(&rst_gpio_config); - ESP_GOTO_ON_ERROR(ret, err, TAG, "GPIO config failed"); - } - - /* Reset controller */ - ret = touch_gt911_reset(esp_lcd_touch_gt911); - ESP_GOTO_ON_ERROR(ret, err, TAG, "GT911 reset failed"); - - /* Read status and config info */ - ret = touch_gt911_read_cfg(esp_lcd_touch_gt911); - ESP_GOTO_ON_ERROR(ret, err, TAG, "GT911 init failed"); - -err: - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Error (0x%x)! Touch controller GT911 initialization failed!", ret); - if (esp_lcd_touch_gt911) { - esp_lcd_touch_gt911_del(esp_lcd_touch_gt911); - } - } - - *out_touch = esp_lcd_touch_gt911; - - return ret; -} - -static esp_err_t esp_lcd_touch_gt911_enter_sleep(esp_lcd_touch_handle_t tp) -{ - esp_err_t err = touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_ENTER_SLEEP, 0x05); - ESP_RETURN_ON_ERROR(err, TAG, "Enter Sleep failed!"); - - return ESP_OK; -} - -static esp_err_t esp_lcd_touch_gt911_exit_sleep(esp_lcd_touch_handle_t tp) -{ - esp_err_t ret; - esp_lcd_touch_handle_t esp_lcd_touch_gt911 = tp; - - if (esp_lcd_touch_gt911->config.int_gpio_num != GPIO_NUM_NC) { - const gpio_config_t int_gpio_config_high = { - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.int_gpio_num) - }; - ret = gpio_config(&int_gpio_config_high); - ESP_RETURN_ON_ERROR(ret, TAG, "High GPIO config failed"); - gpio_set_level(esp_lcd_touch_gt911->config.int_gpio_num, 1); - - vTaskDelay(pdMS_TO_TICKS(5)); - - const gpio_config_t int_gpio_config_float = { - .mode = GPIO_MODE_OUTPUT_OD, - .pin_bit_mask = BIT64(esp_lcd_touch_gt911->config.int_gpio_num) - }; - ret = gpio_config(&int_gpio_config_float); - ESP_RETURN_ON_ERROR(ret, TAG, "Float GPIO config failed"); - } - - return ESP_OK; -} - -static esp_err_t esp_lcd_touch_gt911_read_data(esp_lcd_touch_handle_t tp) -{ - esp_err_t err; - uint8_t buf[41]; - uint8_t touch_cnt = 0; - uint8_t clear = 0; - size_t i = 0; - - assert(tp != NULL); - - err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, buf, 1); - ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!"); - - /* Any touch data? */ - if ((buf[0] & 0x80) == 0x00) { - touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear); -#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0) - } else if ((buf[0] & 0x10) == 0x10) { - /* Read all keys */ - uint8_t key_max = ((ESP_GT911_TOUCH_MAX_BUTTONS < CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS) ? \ - (ESP_GT911_TOUCH_MAX_BUTTONS) : (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS)); - err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_KEY_REG, &buf[0], key_max); - ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!"); - - /* Clear all */ - touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear); - ESP_RETURN_ON_ERROR(err, TAG, "I2C write error!"); - - portENTER_CRITICAL(&tp->data.lock); - - /* Buttons count */ - tp->data.buttons = key_max; - for (i = 0; i < key_max; i++) { - tp->data.button[i].status = buf[0] ? 1 : 0; - } - - portEXIT_CRITICAL(&tp->data.lock); -#endif - } else if ((buf[0] & 0x80) == 0x80) { -#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0) - portENTER_CRITICAL(&tp->data.lock); - for (i = 0; i < CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS; i++) { - tp->data.button[i].status = 0; - } - portEXIT_CRITICAL(&tp->data.lock); -#endif - /* Count of touched points */ - touch_cnt = buf[0] & 0x0f; - if (touch_cnt > 5 || touch_cnt == 0) { - touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear); - return ESP_OK; - } - - /* Read all points */ - err = touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG + 1, &buf[1], touch_cnt * 8); - ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!"); - - /* Clear all */ - err = touch_gt911_i2c_write(tp, ESP_LCD_TOUCH_GT911_READ_XY_REG, clear); - ESP_RETURN_ON_ERROR(err, TAG, "I2C read error!"); - - portENTER_CRITICAL(&tp->data.lock); - - /* Number of touched points */ - touch_cnt = (touch_cnt > CONFIG_ESP_LCD_TOUCH_MAX_POINTS ? CONFIG_ESP_LCD_TOUCH_MAX_POINTS : touch_cnt); - tp->data.points = touch_cnt; - - /* Fill all coordinates */ - for (i = 0; i < touch_cnt; i++) { - tp->data.coords[i].x = ((uint16_t)buf[(i * 8) + 3] << 8) + buf[(i * 8) + 2]; - tp->data.coords[i].y = (((uint16_t)buf[(i * 8) + 5] << 8) + buf[(i * 8) + 4]); - tp->data.coords[i].strength = (((uint16_t)buf[(i * 8) + 7] << 8) + buf[(i * 8) + 6]); - } - - portEXIT_CRITICAL(&tp->data.lock); - } - - return ESP_OK; -} - -static bool esp_lcd_touch_gt911_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num) -{ - assert(tp != NULL); - assert(x != NULL); - assert(y != NULL); - assert(point_num != NULL); - assert(max_point_num > 0); - - portENTER_CRITICAL(&tp->data.lock); - - /* Count of points */ - *point_num = (tp->data.points > max_point_num ? max_point_num : tp->data.points); - - for (size_t i = 0; i < *point_num; i++) { - x[i] = tp->data.coords[i].x; - y[i] = tp->data.coords[i].y; - - if (strength) { - strength[i] = tp->data.coords[i].strength; - } - } - - /* Invalidate */ - tp->data.points = 0; - - portEXIT_CRITICAL(&tp->data.lock); - - return (*point_num > 0); -} - -#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0) -static esp_err_t esp_lcd_touch_gt911_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state) -{ - esp_err_t err = ESP_OK; - assert(tp != NULL); - assert(state != NULL); - - *state = 0; - - portENTER_CRITICAL(&tp->data.lock); - - if (n > tp->data.buttons) { - err = ESP_ERR_INVALID_ARG; - } else { - *state = tp->data.button[n].status; - } - - portEXIT_CRITICAL(&tp->data.lock); - - return err; -} -#endif - -static esp_err_t esp_lcd_touch_gt911_del(esp_lcd_touch_handle_t tp) -{ - assert(tp != NULL); - - /* Reset GPIO pin settings */ - if (tp->config.int_gpio_num != GPIO_NUM_NC) { - gpio_reset_pin(tp->config.int_gpio_num); - if (tp->config.interrupt_callback) { - gpio_isr_handler_remove(tp->config.int_gpio_num); - } - } - - /* Reset GPIO pin settings */ - if (tp->config.rst_gpio_num != GPIO_NUM_NC) { - gpio_reset_pin(tp->config.rst_gpio_num); - } - - free(tp); - - return ESP_OK; -} - -/******************************************************************************* -* Private API function -*******************************************************************************/ - -/* Reset controller */ -static esp_err_t touch_gt911_reset(esp_lcd_touch_handle_t tp) -{ - assert(tp != NULL); - - if (tp->config.rst_gpio_num != GPIO_NUM_NC) { - ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, tp->config.levels.reset), TAG, "GPIO set level error!"); - vTaskDelay(pdMS_TO_TICKS(10)); - ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, !tp->config.levels.reset), TAG, "GPIO set level error!"); - vTaskDelay(pdMS_TO_TICKS(10)); - } - - return ESP_OK; -} - -static esp_err_t touch_gt911_read_cfg(esp_lcd_touch_handle_t tp) -{ - uint8_t buf[4]; - - assert(tp != NULL); - - ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_PRODUCT_ID_REG, (uint8_t *)&buf[0], 3), TAG, "GT911 read error!"); - ESP_RETURN_ON_ERROR(touch_gt911_i2c_read(tp, ESP_LCD_TOUCH_GT911_CONFIG_REG, (uint8_t *)&buf[3], 1), TAG, "GT911 read error!"); - - ESP_LOGI(TAG, "TouchPad_ID:0x%02x,0x%02x,0x%02x", buf[0], buf[1], buf[2]); - ESP_LOGI(TAG, "TouchPad_Config_Version:%d", buf[3]); - - return ESP_OK; -} - -static esp_err_t touch_gt911_i2c_read(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len) -{ - assert(tp != NULL); - assert(data != NULL); - - /* Read data */ - return esp_lcd_panel_io_rx_param(tp->io, reg, data, len); -} - -static esp_err_t touch_gt911_i2c_write(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t data) -{ - assert(tp != NULL); - - // *INDENT-OFF* - /* Write data */ - return esp_lcd_panel_io_tx_param(tp->io, reg, (uint8_t[]){data}, 1); - // *INDENT-ON* -} - -#endif \ No newline at end of file diff --git a/src/esp_lcd_touch_xpt2046.c b/src/esp_lcd_touch_xpt2046.c deleted file mode 100644 index c97ac9c..0000000 --- a/src/esp_lcd_touch_xpt2046.c +++ /dev/null @@ -1,277 +0,0 @@ -#ifdef TOUCH_XPT2046_SPI - -/* - * SPDX-FileCopyrightText: 2022 atanisoft (github.com/atanisoft) - * - * SPDX-License-Identifier: MIT - */ - -#include -#include -#include -#include -#include -#include -#include -// This must be included after FreeRTOS includes due to missing include -// for portMUX_TYPE -#include -#include - -#include "sdkconfig.h" - -static const char *TAG = "xpt2046"; - -enum xpt2046_registers -{ - // START ADDR SER/ INT VREF ADC - // DFR ENA INT/EXT ENA - Z_VALUE_1 = 0xB1, // 1 011 0 0 0 1 - Z_VALUE_2 = 0xC1, // 1 100 0 0 0 1 - Y_POSITION = 0x91, // 1 001 0 0 0 1 - X_POSITION = 0xD1, // 1 101 0 0 0 1 - BATTERY = 0xA7 // 1 010 0 1 1 1 -}; - -#if CONFIG_XPT2046_ENABLE_LOCKING -#define XPT2046_LOCK(lock) portENTER_CRITICAL(lock) -#define XPT2046_UNLOCK(lock) portEXIT_CRITICAL(lock) -#else -#define XPT2046_LOCK(lock) -#define XPT2046_UNLOCK(lock) -#endif - -static const uint16_t XPT2046_ADC_LIMIT = 4096; -static esp_err_t xpt2046_read_data(esp_lcd_touch_handle_t tp); -static bool xpt2046_get_xy(esp_lcd_touch_handle_t tp, - uint16_t *x, uint16_t *y, - uint16_t *strength, - uint8_t *point_num, - uint8_t max_point_num); -static esp_err_t xpt2046_del(esp_lcd_touch_handle_t tp); - -esp_err_t esp_lcd_touch_new_spi_xpt2046(const esp_lcd_panel_io_handle_t io, - const esp_lcd_touch_config_t *config, - esp_lcd_touch_handle_t *out_touch) -{ - esp_err_t ret = ESP_OK; - esp_lcd_touch_handle_t handle = NULL; - - ESP_GOTO_ON_FALSE(io, ESP_ERR_INVALID_ARG, err, TAG, - "esp_lcd_panel_io_handle_t must not be NULL"); - ESP_GOTO_ON_FALSE(config, ESP_ERR_INVALID_ARG, err, TAG, - "esp_lcd_touch_config_t must not be NULL"); - - handle = (esp_lcd_touch_handle_t)calloc(1, sizeof(esp_lcd_touch_t)); - ESP_GOTO_ON_FALSE(handle, ESP_ERR_NO_MEM, err, TAG, - "No memory available for XPT2046 state"); - handle->io = io; - handle->read_data = xpt2046_read_data; - handle->get_xy = xpt2046_get_xy; - handle->del = xpt2046_del; - handle->data.lock.owner = portMUX_FREE_VAL; - memcpy(&handle->config, config, sizeof(esp_lcd_touch_config_t)); - - // this is not yet supported by esp_lcd_touch. - if (config->int_gpio_num != GPIO_NUM_NC) - { - ESP_GOTO_ON_FALSE(GPIO_IS_VALID_GPIO(config->int_gpio_num), - ESP_ERR_INVALID_ARG, err, TAG, "Invalid GPIO Interrupt Pin"); - gpio_config_t cfg; - memset(&cfg, 0, sizeof(gpio_config_t)); - esp_rom_gpio_pad_select_gpio(config->int_gpio_num); - cfg.pin_bit_mask = BIT64(config->int_gpio_num); - cfg.mode = GPIO_MODE_INPUT; - - // If the user has provided a callback routine for the interrupt enable - // the interrupt mode on the negative edge. - if (config->interrupt_callback) - { - cfg.intr_type = GPIO_INTR_NEGEDGE; - } - - ESP_GOTO_ON_ERROR(gpio_config(&cfg), err, TAG, - "Configure GPIO for Interrupt failed"); - - // Connect the user interrupt callback routine. - if (config->interrupt_callback) - { - esp_lcd_touch_register_interrupt_callback(handle, config->interrupt_callback); - } - } - -err: - if (ret != ESP_OK) - { - if (handle) - { - xpt2046_del(handle); - handle = NULL; - } - } - - *out_touch = handle; - - return ret; -} - -static esp_err_t xpt2046_del(esp_lcd_touch_handle_t tp) -{ - if (tp != NULL) - { - if (tp->config.int_gpio_num != GPIO_NUM_NC) - { - gpio_reset_pin(tp->config.int_gpio_num); - } - } - free(tp); - - return ESP_OK; -} - -static inline esp_err_t xpt2046_read_register(esp_lcd_touch_handle_t tp, uint8_t reg, uint16_t *value) -{ - uint8_t buf[2] = {0, 0}; - ESP_RETURN_ON_ERROR(esp_lcd_panel_io_rx_param(tp->io, reg, buf, 2), TAG, "XPT2046 read error!"); - *value = ((buf[0] << 8) | (buf[1])); - return ESP_OK; -} - -static esp_err_t xpt2046_read_data(esp_lcd_touch_handle_t tp) -{ - uint16_t z1 = 0, z2 = 0, z = 0; - uint32_t x = 0, y = 0; - uint8_t point_count = 0; - - ESP_RETURN_ON_ERROR(xpt2046_read_register(tp, Z_VALUE_1, &z1), TAG, "XPT2046 read error!"); - ESP_RETURN_ON_ERROR(xpt2046_read_register(tp, Z_VALUE_2, &z2), TAG, "XPT2046 read error!"); - - // Convert the received values into a Z value. - z = (z1 >> 3) + (XPT2046_ADC_LIMIT - (z2 >> 3)); - - // If the Z (pressure) exceeds the threshold it is likely the user has - // pressed the screen, read in and average the positions. - if (z >= CONFIG_XPT2046_Z_THRESHOLD) - { - uint16_t discard_buf = 0; - - // read and discard a value as it is usually not reliable. - ESP_RETURN_ON_ERROR(xpt2046_read_register(tp, X_POSITION, &discard_buf), - TAG, "XPT2046 read error!"); - - for (uint8_t idx = 0; idx < CONFIG_ESP_LCD_TOUCH_MAX_POINTS; idx++) - { - uint16_t x_temp = 0; - uint16_t y_temp = 0; - // Read X position and convert returned data to 12bit value - ESP_RETURN_ON_ERROR(xpt2046_read_register(tp, X_POSITION, &x_temp), - TAG, "XPT2046 read error!"); - // drop lowest three bits to convert to 12-bit position - x_temp >>= 3; - -#if CONFIG_XPT2046_CONVERT_ADC_TO_COORDS - // Convert the raw ADC value into a screen coordinate and store it - // for averaging. - x += ((x_temp / (double)XPT2046_ADC_LIMIT) * tp->config.x_max); -#else - // store the raw ADC values and let the user convert them to screen - // coordinates. - x += x_temp; -#endif // CONFIG_XPT2046_CONVERT_ADC_TO_COORDS - - // Read Y position and convert returned data to 12bit value - ESP_RETURN_ON_ERROR(xpt2046_read_register(tp, Y_POSITION, &y_temp), - TAG, "XPT2046 read error!"); - // drop lowest three bits to convert to 12-bit position - y_temp >>= 3; - -#if CONFIG_XPT2046_CONVERT_ADC_TO_COORDS - // Convert the raw ADC value into a screen coordinate and store it - // for averaging. - y += ((y_temp / (double)XPT2046_ADC_LIMIT) * tp->config.y_max); -#else - // store the raw ADC values and let the user convert them to screen - // coordinates. - y += y_temp; -#endif // CONFIG_XPT2046_CONVERT_ADC_TO_COORDS - } - - // Average the accumulated coordinate data points. - x /= CONFIG_ESP_LCD_TOUCH_MAX_POINTS; - y /= CONFIG_ESP_LCD_TOUCH_MAX_POINTS; - point_count = 1; - } - - XPT2046_LOCK(&tp->data.lock); - tp->data.coords[0].x = x; - tp->data.coords[0].y = y; - tp->data.coords[0].strength = z; - tp->data.points = point_count; - XPT2046_UNLOCK(&tp->data.lock); - - return ESP_OK; -} - -static bool xpt2046_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, - uint16_t *strength, uint8_t *point_num, - uint8_t max_point_num) -{ - XPT2046_LOCK(&tp->data.lock); - - // Determine how many touch points that are available. - if (tp->data.points > max_point_num) - { - *point_num = max_point_num; - } - else - { - *point_num = tp->data.points; - } - - for (size_t i = 0; i < *point_num; i++) - { - x[i] = tp->data.coords[i].x; - y[i] = tp->data.coords[i].y; - - if (strength) - { - strength[i] = tp->data.coords[i].strength; - } - } - - // Invalidate stored touch data. - tp->data.points = 0; - - XPT2046_UNLOCK(&tp->data.lock); - - if (*point_num) - { - ESP_LOGD(TAG, "Touch point: %dx%d", x[0], y[0]); - } - else - { - ESP_LOGV(TAG, "No touch points"); - } - - return (*point_num > 0); -} - -esp_err_t esp_lcd_touch_xpt2046_read_battery_level(const esp_lcd_touch_handle_t handle, float *output) -{ - uint16_t level; - ESP_RETURN_ON_ERROR(xpt2046_read_register(handle, BATTERY, &level), TAG, "XPT2046 read error!"); - - // battery voltage is reported as 1/4 the actual voltage due to logic in - // the chip. - *output = level * 4.0; - - // adjust for internal vref of 2.5v - *output *= 2.5f; - - // adjust for ADC bit count - *output /= 4096.0f; - - return ESP_OK; -} - -#endif \ No newline at end of file diff --git a/src/esp_panel_gc9a01.c b/src/esp_panel_gc9a01.c new file mode 100644 index 0000000..fbffd0f --- /dev/null +++ b/src/esp_panel_gc9a01.c @@ -0,0 +1,391 @@ +#ifdef LCD_GC9A01_SPI + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct +{ + esp_lcd_panel_t base; + esp_lcd_panel_io_handle_t io; + esp_lcd_panel_dev_config_t panel_dev_config; + // Data + int x_gap; + int y_gap; + uint8_t madctl; +} gc9a01_panel_t; + +const lcd_init_cmd_t gc9a01_vendor_specific_init_default[] = { + // Enable Inter Register + {0xfe, (const uint8_t[]){0x00}, 0, 0}, + {0xef, (const uint8_t[]){0x00}, 0, 0}, + {0xeb, (const uint8_t[]){0x14}, 1, 0}, + {0x84, (const uint8_t[]){0x60}, 1, 0}, + {0x85, (const uint8_t[]){0xff}, 1, 0}, + {0x86, (const uint8_t[]){0xff}, 1, 0}, + {0x87, (const uint8_t[]){0xff}, 1, 0}, + {0x8e, (const uint8_t[]){0xff}, 1, 0}, + {0x8f, (const uint8_t[]){0xff}, 1, 0}, + {0x88, (const uint8_t[]){0x0a}, 1, 0}, + {0x89, (const uint8_t[]){0x23}, 1, 0}, + {0x8a, (const uint8_t[]){0x00}, 1, 0}, + {0x8b, (const uint8_t[]){0x80}, 1, 0}, + {0x8c, (const uint8_t[]){0x01}, 1, 0}, + {0x8d, (const uint8_t[]){0x03}, 1, 0}, + {0x90, (const uint8_t[]){0x08, 0x08, 0x08, 0x08}, 4, 0}, + {0xff, (const uint8_t[]){0x60, 0x01, 0x04}, 3, 0}, + {0xC3, (const uint8_t[]){0x13}, 1, 0}, + {0xC4, (const uint8_t[]){0x13}, 1, 0}, + {0xC9, (const uint8_t[]){0x30}, 1, 0}, + {0xbe, (const uint8_t[]){0x11}, 1, 0}, + {0xe1, (const uint8_t[]){0x10, 0x0e}, 2, 0}, + {0xdf, (const uint8_t[]){0x21, 0x0c, 0x02}, 3, 0}, + // Set gamma + {0xF0, (const uint8_t[]){0x45, 0x09, 0x08, 0x08, 0x26, 0x2a}, 6, 0}, + {0xF1, (const uint8_t[]){0x43, 0x70, 0x72, 0x36, 0x37, 0x6f}, 6, 0}, + {0xF2, (const uint8_t[]){0x45, 0x09, 0x08, 0x08, 0x26, 0x2a}, 6, 0}, + {0xF3, (const uint8_t[]){0x43, 0x70, 0x72, 0x36, 0x37, 0x6f}, 6, 0}, + {0xed, (const uint8_t[]){0x1b, 0x0b}, 2, 0}, + {0xae, (const uint8_t[]){0x77}, 1, 0}, + {0xcd, (const uint8_t[]){0x63}, 1, 0}, + {0x70, (const uint8_t[]){0x07, 0x07, 0x04, 0x0e, 0x0f, 0x09, 0x07, 0x08, 0x03}, 9, 0}, + {0xE8, (const uint8_t[]){0x34}, 1, 0}, // 4 dot inversion + {0x60, (const uint8_t[]){0x38, 0x0b, 0x6D, 0x6D, 0x39, 0xf0, 0x6D, 0x6D}, 8, 0}, + {0x61, (const uint8_t[]){0x38, 0xf4, 0x6D, 0x6D, 0x38, 0xf7, 0x6D, 0x6D}, 8, 0}, + {0x62, (const uint8_t[]){0x38, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x38, 0x0F, 0x71, 0xEF, 0x70, 0x70}, 12, 0}, + {0x63, (const uint8_t[]){0x38, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x38, 0x13, 0x71, 0xF3, 0x70, 0x70}, 12, 0}, + {0x64, (const uint8_t[]){0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07}, 7, 0}, + {0x66, (const uint8_t[]){0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00}, 10, 0}, + {0x67, (const uint8_t[]){0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98}, 10, 0}, + {0x74, (const uint8_t[]){0x10, 0x45, 0x80, 0x00, 0x00, 0x4E, 0x00}, 7, 0}, + {0x98, (const uint8_t[]){0x3e, 0x07}, 2, 0}, + {0x99, (const uint8_t[]){0x3e, 0x07}, 2, 0}}; + +esp_err_t gc9a01_reset(esp_lcd_panel_t *panel) +{ + log_v("panel:0x%08x", panel); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const gc9a01_panel_t *ph = (gc9a01_panel_t *)panel; + + if (ph->panel_dev_config.reset_gpio_num != GPIO_NUM_NC) + { + // Hardware reset + gpio_set_level(ph->panel_dev_config.reset_gpio_num, ph->panel_dev_config.flags.reset_active_high); + vTaskDelay(pdMS_TO_TICKS(1)); + gpio_set_level(ph->panel_dev_config.reset_gpio_num, !ph->panel_dev_config.flags.reset_active_high); + } + else + { + esp_err_t res; + // Software reset + if ((res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_SWRESET, NULL, 0)) != ESP_OK) + { + log_e("Sending LCD_CMD_SWRESET failed"); + return res; + } + } + + vTaskDelay(pdMS_TO_TICKS(120)); + + return ESP_OK; +} + +esp_err_t gc9a01_init(esp_lcd_panel_t *panel) +{ + log_v("panel:0x%08x", panel); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const gc9a01_panel_t *ph = (gc9a01_panel_t *)panel; + + esp_err_t res; + if ((res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_SLPOUT, NULL, 0)) != ESP_OK) + { + log_e("Sending SLPOUT failed"); + return res; + } + + vTaskDelay(pdMS_TO_TICKS(100)); + + uint8_t colmod; + switch (ph->panel_dev_config.bits_per_pixel) + { + case 16: // RGB565 + colmod = 0x55; + break; + case 18: // RGB666 + colmod = 0x66; + break; + default: + log_e("Invalid bits per pixel: %d. Only RGB565 and RGB666 are supported", ph->panel_dev_config.bits_per_pixel); + return ESP_ERR_INVALID_ARG; + } + + if ((res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_MADCTL, &ph->madctl, 1)) != ESP_OK || + (res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_COLMOD, &colmod, 1)) != ESP_OK) + { + log_e("Sending MADCTL/COLMOD failed"); + return res; + } + + const lcd_init_cmd_t *cmd = gc9a01_vendor_specific_init_default; + uint16_t cmds_size = sizeof(gc9a01_vendor_specific_init_default) / sizeof(lcd_init_cmd_t); + if (ph->panel_dev_config.vendor_config != NULL) + { + cmd = ((gc9a01_vendor_config_t *)ph->panel_dev_config.vendor_config)->init_cmds; + cmds_size = ((gc9a01_vendor_config_t *)ph->panel_dev_config.vendor_config)->init_cmds_size; + } + + while (cmds_size-- > 0) + { + if ((res = esp_lcd_panel_io_tx_param(ph->io, cmd->cmd, cmd->data, cmd->bytes)) != ESP_OK) + { + log_e("Sending command: 0x%02x failed", cmd->cmd); + return res; + } + + vTaskDelay(pdMS_TO_TICKS(cmd->delay_ms)); + cmd++; + } + + return ESP_OK; +} + +esp_err_t gc9a01_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) +{ + log_v("panel:0x%08x, x_start:%d, y_start:%d, x_end:%d, y_end:%d, color_data:0x%08x", panel, x_start, y_start, x_end, y_end, color_data); + if (panel == NULL || color_data == NULL) + return ESP_ERR_INVALID_ARG; + + const gc9a01_panel_t *ph = (gc9a01_panel_t *)panel; + + if (x_start >= x_end) + { + log_w("X-start greater than the x-end"); + return ESP_ERR_INVALID_ARG; + } + + if (y_start >= y_end) + { + log_w("Y-start greater than the y-end"); + return ESP_ERR_INVALID_ARG; + } + + // Correct for gap + x_start += ph->x_gap; + x_end += ph->x_gap; + y_start += ph->y_gap; + y_end += ph->y_gap; + + esp_err_t res; + const uint8_t caset[4] = {x_start >> 8, x_start, (x_end - 1) >> 8, x_end - 1}; + const uint8_t raset[4] = {y_start >> 8, y_start, (y_end - 1) >> 8, y_end - 1}; + if ((res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_CASET, caset, sizeof(caset))) != ESP_OK || + (res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_RASET, raset, sizeof(raset))) != ESP_OK) + { + log_e("Sending CASET/RASET failed"); + return res; + } + + uint8_t bytes_per_pixel = (ph->panel_dev_config.bits_per_pixel + 0x7) >> 3; + size_t len = (x_end - x_start) * (y_end - y_start) * bytes_per_pixel; + if ((res = esp_lcd_panel_io_tx_color(ph->io, LCD_CMD_RAMWR, color_data, len)) != ESP_OK) + { + log_e("Sending RAMWR failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t gc9a01_invert_color(esp_lcd_panel_t *panel, bool invert) +{ + log_v("panel:0x%08x, invert:%d", panel, invert); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const gc9a01_panel_t *ph = (gc9a01_panel_t *)panel; + + esp_err_t res; + if ((res = esp_lcd_panel_io_tx_param(ph->io, invert ? LCD_CMD_INVON : LCD_CMD_INVOFF, NULL, 0)) != ESP_OK) + { + log_e("Sending LCD_CMD_INVON/LCD_CMD_INVOFF failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t gc9a01_update_madctl(gc9a01_panel_t *ph) +{ + esp_err_t res; + if ((res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_MADCTL, &ph->madctl, 1)) != ESP_OK) + { + log_e("Sending LCD_CMD_MADCTL failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t gc9a01_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + log_v("panel:0x%08x, mirror_x:%d, mirror_y:%d", panel, mirror_x, mirror_y); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + gc9a01_panel_t *ph = (gc9a01_panel_t *)panel; + + if (mirror_x) + ph->madctl |= LCD_CMD_MX_BIT; + else + ph->madctl &= ~LCD_CMD_MX_BIT; + + if (mirror_y) + ph->madctl |= LCD_CMD_MY_BIT; + else + ph->madctl &= ~LCD_CMD_MY_BIT; + + return gc9a01_update_madctl(ph); +} + +esp_err_t gc9a01_swap_xy(esp_lcd_panel_t *panel, bool swap_xy) +{ + log_v("panel:0x%08x, swap_xy:%d", panel, swap_xy); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + gc9a01_panel_t *ph = (gc9a01_panel_t *)panel; + + if (swap_xy) + ph->madctl |= LCD_CMD_MV_BIT; + else + ph->madctl &= ~LCD_CMD_MV_BIT; + + return gc9a01_update_madctl(ph); +} + +esp_err_t gc9a01_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) +{ + log_v("panel:0x%08x, x_gap:%d, y_gap:%d", panel, x_gap, y_gap); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + gc9a01_panel_t *ph = (gc9a01_panel_t *)panel; + + ph->x_gap = x_gap; + ph->y_gap = y_gap; + + return ESP_OK; +} + +esp_err_t gc9a01_disp_off(esp_lcd_panel_t *panel, bool off) +{ + log_v("panel:0x%08x, off:%d", panel, off); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const gc9a01_panel_t *ph = (gc9a01_panel_t *)panel; + + esp_err_t res; + if ((res = esp_lcd_panel_io_tx_param(ph->io, off ? LCD_CMD_DISPOFF : LCD_CMD_DISPON, NULL, 0)) != ESP_OK) + { + log_e("Sending LCD_CMD_DISPOFF/LCD_CMD_DISPON failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t gc9a01_del(esp_lcd_panel_t *panel) +{ + log_v("panel:0x%08x", panel); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + gc9a01_panel_t *ph = (gc9a01_panel_t *)panel; + + // Reset RESET + if (ph->panel_dev_config.reset_gpio_num != GPIO_NUM_NC) + gpio_reset_pin(ph->panel_dev_config.reset_gpio_num); + + free(ph); + + return ESP_OK; +} + +esp_err_t esp_lcd_new_panel_gc9a01(const esp_lcd_panel_io_handle_t panel_io_handle, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *panel_handle) +{ + log_v("panel_io_handle:0x%08x, panel_dev_config:0x%08x, panel_handle:0x%08x", panel_io_handle, panel_dev_config, panel_handle); + if (panel_io_handle == NULL || panel_dev_config == NULL || panel_handle == NULL) + return ESP_ERR_INVALID_ARG; + + if (panel_dev_config->reset_gpio_num != GPIO_NUM_NC && !GPIO_IS_VALID_GPIO(panel_dev_config->reset_gpio_num)) + { + log_e("Invalid GPIO RST pin: %d", panel_dev_config->reset_gpio_num); + return ESP_ERR_INVALID_ARG; + } + + uint8_t madctl; + switch (panel_dev_config->color_space) + { + case ESP_LCD_COLOR_SPACE_RGB: + madctl = 0; + break; + case ESP_LCD_COLOR_SPACE_BGR: + madctl = LCD_CMD_BGR_BIT; + break; + default: + log_e("Invalid color space: %d. Only RGB and BGR are supported", panel_dev_config->color_space); + return ESP_ERR_INVALID_ARG; + } + + if (panel_dev_config->reset_gpio_num != GPIO_NUM_NC) + { + esp_err_t res; + const gpio_config_t cfg = { + .pin_bit_mask = BIT64(panel_dev_config->reset_gpio_num), + .mode = GPIO_MODE_OUTPUT}; + if ((res = gpio_config(&cfg)) != ESP_OK) + { + log_e("Configuring GPIO for RST failed"); + return res; + } + } + + gc9a01_panel_t *ph = heap_caps_calloc(1, sizeof(gc9a01_panel_t), MALLOC_CAP_DEFAULT); + if (ph == NULL) + { + log_e("No memory available for gc9a01_panel_t"); + return ESP_ERR_NO_MEM; + } + + ph->io = panel_io_handle; + memcpy(&ph->panel_dev_config, panel_dev_config, sizeof(esp_lcd_panel_dev_config_t)); + ph->madctl = madctl; + + ph->base.del = gc9a01_del; + ph->base.reset = gc9a01_reset; + ph->base.init = gc9a01_init; + ph->base.draw_bitmap = gc9a01_draw_bitmap; + ph->base.invert_color = gc9a01_invert_color; + ph->base.mirror = gc9a01_mirror; + ph->base.swap_xy = gc9a01_swap_xy; + ph->base.set_gap = gc9a01_set_gap; + ph->base.disp_off = gc9a01_disp_off; + + log_d("panel_handle: 0x%08x", ph); + *panel_handle = (esp_lcd_panel_handle_t)ph; + + return ESP_OK; +} + +#endif \ No newline at end of file diff --git a/src/esp_panel_ili9341.c b/src/esp_panel_ili9341.c new file mode 100644 index 0000000..0c0ccb1 --- /dev/null +++ b/src/esp_panel_ili9341.c @@ -0,0 +1,382 @@ +#ifdef LCD_ILI9341_SPI + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct +{ + esp_lcd_panel_t base; + esp_lcd_panel_io_handle_t panel_io_handle; + esp_lcd_panel_dev_config_t panel_dev_config; + // Data + int x_gap; + int y_gap; + uint8_t madctl; +} ili9341_panel_t; + +const lcd_init_cmd_t ili9341_vendor_specific_init_default[] = { + // Power contorl B, power control = 0, DC_ENA = 1 + {0xCF, (uint8_t[]){0x00, 0xAA, 0XE0}, 3, 0}, + // Power on sequence control, cp1 keeps 1 frame, 1st frame enable, vcl = 0, ddvdh=3, vgh=1, vgl=2, DDVDH_ENH=1 + {0xED, (uint8_t[]){0x67, 0x03, 0X12, 0X81}, 4, 0}, + // Driver timing control A, non-overlap=default +1, EQ=default - 1, CR=default. pre-charge=default - 1 + {0xE8, (uint8_t[]){0x8A, 0x01, 0x78}, 3, 0}, + // Power control A, Vcore=1.6V, DDVDH=5.6V + {0xCB, (uint8_t[]){0x39, 0x2C, 0x00, 0x34, 0x02}, 5, 0}, + // Pump ratio control, DDVDH=2xVCl + {0xF7, (uint8_t[]){0x20}, 1, 0}, + {0xF7, (uint8_t[]){0x20}, 1, 0}, + // Driver timing control, all=0 unit + {0xEA, (uint8_t[]){0x00, 0x00}, 2, 0}, + // Power control 1, GVDD=4.75V + {0xC0, (uint8_t[]){0x23}, 1, 0}, + // Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 + {0xC1, (uint8_t[]){0x11}, 1, 0}, + // VCOM control 1, VCOMH=4.025V, VCOML=-0.950V + {0xC5, (uint8_t[]){0x43, 0x4C}, 2, 0}, + // VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 + {0xC7, (uint8_t[]){0xA0}, 1, 0}, + // Frame rate control, f=fosc, 70Hz fps + {0xB1, (uint8_t[]){0x00, 0x1B}, 2, 0}, + // Enable 3G, disabled + {0xF2, (uint8_t[]){0x00}, 1, 0}, + // Gamma set, curve 1 + {0x26, (uint8_t[]){0x01}, 1, 0}, + // Positive gamma correction + {0xE0, (uint8_t[]){0x1F, 0x36, 0x36, 0x3A, 0x0C, 0x05, 0x4F, 0X87, 0x3C, 0x08, 0x11, 0x35, 0x19, 0x13, 0x00}, 15, 0}, + // Negative gamma correction + {0xE1, (uint8_t[]){0x00, 0x09, 0x09, 0x05, 0x13, 0x0A, 0x30, 0x78, 0x43, 0x07, 0x0E, 0x0A, 0x26, 0x2C, 0x1F}, 15, 0}, + // Entry mode set, Low vol detect disabled, normal display + {0xB7, (uint8_t[]){0x07}, 1, 0}, + // Display function control + {0xB6, (uint8_t[]){0x08, 0x82, 0x27}, 3, 0}}; + +esp_err_t ili9341_reset(esp_lcd_panel_t *panel) +{ + log_v("panel:0x%08x", panel); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const ili9341_panel_t *ph = (ili9341_panel_t *)panel; + + if (ph->panel_dev_config.reset_gpio_num != GPIO_NUM_NC) + { + // Hardware reset + gpio_set_level(ph->panel_dev_config.reset_gpio_num, ph->panel_dev_config.flags.reset_active_high); + vTaskDelay(pdMS_TO_TICKS(1)); + gpio_set_level(ph->panel_dev_config.reset_gpio_num, !ph->panel_dev_config.flags.reset_active_high); + } + else + { + esp_err_t res; + // Software reset + if ((res = esp_lcd_panel_io_tx_param(ph->panel_io_handle, LCD_CMD_SWRESET, NULL, 0)) != ESP_OK) + { + log_e("Sending LCD_CMD_SWRESET failed"); + return res; + } + } + + vTaskDelay(pdMS_TO_TICKS(120)); + + return ESP_OK; +} + +esp_err_t ili9341_init(esp_lcd_panel_t *panel) +{ + log_v("panel:0x%08x", panel); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const ili9341_panel_t *ph = (ili9341_panel_t *)panel; + + esp_err_t res; + if ((res = esp_lcd_panel_io_tx_param(ph->panel_io_handle, LCD_CMD_SLPOUT, NULL, 0)) != ESP_OK) + { + log_e("Sending SLPOUT failed"); + return res; + } + + vTaskDelay(pdMS_TO_TICKS(100)); + + uint8_t colmod; + switch (ph->panel_dev_config.bits_per_pixel) + { + case 16: // RGB565 + colmod = 0x55; + break; + case 18: // RGB666 + colmod = 0x66; + break; + default: + log_e("Invalid bits per pixel: %d. Only RGB565 and RGB666 are supported", ph->panel_dev_config.bits_per_pixel); + return ESP_ERR_INVALID_ARG; + } + + if ((res = esp_lcd_panel_io_tx_param(ph->panel_io_handle, LCD_CMD_MADCTL, &ph->madctl, 1)) != ESP_OK || + (res = esp_lcd_panel_io_tx_param(ph->panel_io_handle, LCD_CMD_COLMOD, &colmod, 1)) != ESP_OK) + { + log_e("Sending MADCTL/COLMOD failed"); + return res; + } + + const lcd_init_cmd_t *cmd = ili9341_vendor_specific_init_default; + uint16_t cmds_size = sizeof(ili9341_vendor_specific_init_default) / sizeof(lcd_init_cmd_t); + if (ph->panel_dev_config.vendor_config != NULL) + { + cmd = ((ili9341_vendor_config_t *)ph->panel_dev_config.vendor_config)->init_cmds; + cmds_size = ((ili9341_vendor_config_t *)ph->panel_dev_config.vendor_config)->init_cmds_size; + } + + while (cmds_size-- > 0) + { + if ((res = esp_lcd_panel_io_tx_param(ph->panel_io_handle, cmd->cmd, cmd->data, cmd->bytes)) != ESP_OK) + { + log_e("Sending command: 0x%02x failed", cmd->cmd); + return res; + } + + vTaskDelay(pdMS_TO_TICKS(cmd->delay_ms)); + cmd++; + } + + return ESP_OK; +} + +esp_err_t ili9341_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) +{ + log_v("panel:0x%08x, x_start:%d, y_start:%d, x_end:%d, y_end:%d, color_data:0x%08x", panel, x_start, y_start, x_end, y_end, color_data); + if (panel == NULL || color_data == NULL) + return ESP_ERR_INVALID_ARG; + + const ili9341_panel_t *ph = (ili9341_panel_t *)panel; + + if (x_start >= x_end) + { + log_w("X-start greater than the x-end"); + return ESP_ERR_INVALID_ARG; + } + + if (y_start >= y_end) + { + log_w("Y-start greater than the y-end"); + return ESP_ERR_INVALID_ARG; + } + + // Correct for gap + x_start += ph->x_gap; + x_end += ph->x_gap; + y_start += ph->y_gap; + y_end += ph->y_gap; + + esp_err_t res; + const uint8_t caset[4] = {x_start >> 8, x_start, (x_end - 1) >> 8, x_end - 1}; + const uint8_t raset[4] = {y_start >> 8, y_start, (y_end - 1) >> 8, y_end - 1}; + if ((res = esp_lcd_panel_io_tx_param(ph->panel_io_handle, LCD_CMD_CASET, caset, sizeof(caset))) != ESP_OK || + (res = esp_lcd_panel_io_tx_param(ph->panel_io_handle, LCD_CMD_RASET, raset, sizeof(raset))) != ESP_OK) + { + log_e("Sending CASET/RASET failed"); + return res; + } + + uint8_t bytes_per_pixel = (ph->panel_dev_config.bits_per_pixel + 0x7) >> 3; + size_t len = (x_end - x_start) * (y_end - y_start) * bytes_per_pixel; + if ((res = esp_lcd_panel_io_tx_color(ph->panel_io_handle, LCD_CMD_RAMWR, color_data, len)) != ESP_OK) + { + log_e("Sending RAMWR failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t ili9341_invert_color(esp_lcd_panel_t *panel, bool invert) +{ + log_v("panel:0x%08x, invert:%d", panel, invert); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const ili9341_panel_t *ph = (ili9341_panel_t *)panel; + + esp_err_t res; + if ((res = esp_lcd_panel_io_tx_param(ph->panel_io_handle, invert ? LCD_CMD_INVON : LCD_CMD_INVOFF, NULL, 0)) != ESP_OK) + { + log_e("Sending LCD_CMD_INVON/LCD_CMD_INVOFF failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t ili9341_update_madctl(ili9341_panel_t *ph) +{ + esp_err_t res; + if ((res = esp_lcd_panel_io_tx_param(ph->panel_io_handle, LCD_CMD_MADCTL, &ph->madctl, 1)) != ESP_OK) + { + log_e("Sending LCD_CMD_MADCTL failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t ili9341_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + log_v("panel:0x%08x, mirror_x:%d, mirror_y:%d", panel, mirror_x, mirror_y); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + ili9341_panel_t *ph = (ili9341_panel_t *)panel; + + if (mirror_x) + ph->madctl |= LCD_CMD_MX_BIT; + else + ph->madctl &= ~LCD_CMD_MX_BIT; + + if (mirror_y) + ph->madctl |= LCD_CMD_MY_BIT; + else + ph->madctl &= ~LCD_CMD_MY_BIT; + + return ili9341_update_madctl(ph); +} + +esp_err_t ili9341_swap_xy(esp_lcd_panel_t *panel, bool swap_xy) +{ + log_v("panel:0x%08x, swap_xy:%d", panel, swap_xy); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + ili9341_panel_t *ph = (ili9341_panel_t *)panel; + + if (swap_xy) + ph->madctl |= LCD_CMD_MV_BIT; + else + ph->madctl &= ~LCD_CMD_MV_BIT; + + return ili9341_update_madctl(ph); +} + +esp_err_t ili9341_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) +{ + log_v("panel:0x%08x, x_gap:%d, y_gap:%d", panel, x_gap, y_gap); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + ili9341_panel_t *ph = (ili9341_panel_t *)panel; + + ph->x_gap = x_gap; + ph->y_gap = y_gap; + + return ESP_OK; +} + +esp_err_t ili9341_disp_off(esp_lcd_panel_t *panel, bool off) +{ + log_v("panel:0x%08x, off:%d", panel, off); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const ili9341_panel_t *ph = (ili9341_panel_t *)panel; + + esp_err_t res; + if ((res = esp_lcd_panel_io_tx_param(ph->panel_io_handle, off ? LCD_CMD_DISPOFF : LCD_CMD_DISPON, NULL, 0)) != ESP_OK) + { + log_e("Sending LCD_CMD_DISPOFF/LCD_CMD_DISPON failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t ili9341_del(esp_lcd_panel_t *panel) +{ + log_v("panel:0x%08x", panel); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + ili9341_panel_t *ph = (ili9341_panel_t *)panel; + + // Reset RESET + if (ph->panel_dev_config.reset_gpio_num != GPIO_NUM_NC) + gpio_reset_pin(ph->panel_dev_config.reset_gpio_num); + + free(ph); + + return ESP_OK; +} + +esp_err_t esp_lcd_new_panel_ili9341(const esp_lcd_panel_io_handle_t panel_io_handle, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *panel_handle) +{ + log_v("panel_io_handle:0x%08x, panel_dev_config:0x%08x, panel_handle:0x%08x", panel_io_handle, panel_dev_config, panel_handle); + if (panel_io_handle == NULL || panel_dev_config == NULL || panel_handle == NULL) + return ESP_ERR_INVALID_ARG; + + if (panel_dev_config->reset_gpio_num != GPIO_NUM_NC && !GPIO_IS_VALID_GPIO(panel_dev_config->reset_gpio_num)) + { + log_e("Invalid GPIO RST pin: %d", panel_dev_config->reset_gpio_num); + return ESP_ERR_INVALID_ARG; + } + + uint8_t madctl; + switch (panel_dev_config->color_space) + { + case ESP_LCD_COLOR_SPACE_RGB: + madctl = 0; + break; + case ESP_LCD_COLOR_SPACE_BGR: + madctl = LCD_CMD_BGR_BIT; + break; + default: + log_e("Invalid color space: %d. Only RGB and BGR are supported", panel_dev_config->color_space); + return ESP_ERR_INVALID_ARG; + } + + if (panel_dev_config->reset_gpio_num != GPIO_NUM_NC) + { + esp_err_t res; + const gpio_config_t cfg = { + .pin_bit_mask = BIT64(panel_dev_config->reset_gpio_num), + .mode = GPIO_MODE_OUTPUT}; + if ((res = gpio_config(&cfg)) != ESP_OK) + { + log_e("Configuring GPIO for RST failed"); + return res; + } + } + + ili9341_panel_t *ph = heap_caps_calloc(1, sizeof(ili9341_panel_t), MALLOC_CAP_DEFAULT); + if (ph == NULL) + { + log_e("No memory available for ili9341_panel_t"); + return ESP_ERR_NO_MEM; + } + + ph->panel_io_handle = panel_io_handle; + memcpy(&ph->panel_dev_config, panel_dev_config, sizeof(esp_lcd_panel_dev_config_t)); + ph->madctl = madctl; + + ph->base.del = ili9341_del; + ph->base.reset = ili9341_reset; + ph->base.init = ili9341_init; + ph->base.draw_bitmap = ili9341_draw_bitmap; + ph->base.invert_color = ili9341_invert_color; + ph->base.mirror = ili9341_mirror; + ph->base.swap_xy = ili9341_swap_xy; + ph->base.set_gap = ili9341_set_gap; + ph->base.disp_off = ili9341_disp_off; + + log_d("panel_handle: 0x%08x", ph); + *panel_handle = (esp_lcd_panel_handle_t)ph; + + return ESP_OK; +} + +#endif \ No newline at end of file diff --git a/src/esp_panel_st7701.c b/src/esp_panel_st7701.c new file mode 100644 index 0000000..badbdb5 --- /dev/null +++ b/src/esp_panel_st7701.c @@ -0,0 +1,325 @@ +#ifdef LCD_ST7701_PAR + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ST7701_CMD_CND2BKxSEL 0xFF + +typedef struct +{ + esp_lcd_panel_t base; + esp_lcd_panel_io_handle_t io; + esp_lcd_panel_dev_config_t panel_dev_config; + // Data + uint8_t madctl; + esp_lcd_panel_t *lcd_panel; +} st7701_panel_t; + +const lcd_init_cmd_t st7701_vendor_specific_init_default[] = { + {0xFF, (uint8_t[]){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0}, + {0xC0, (uint8_t[]){0x3B, 0x00}, 2, 0}, + {0xC1, (uint8_t[]){0x0D, 0x02}, 2, 0}, + {0xC2, (uint8_t[]){0x31, 0x05}, 2, 0}, + {0xCD, (uint8_t[]){0x00}, 1, 0}, + // Positive Voltage Gamma Control + {0xB0, (uint8_t[]){0x00, 0x11, 0x18, 0x0E, 0x11, 0x06, 0x07, 0x08, 0x07, 0x22, 0x04, 0x12, 0x0F, 0xAA, 0x31, 0x18}, 16, 0}, + // Negative Voltage Gamma Control + {0xB1, (uint8_t[]){0x00, 0x11, 0x19, 0x0E, 0x12, 0x07, 0x08, 0x08, 0x08, 0x22, 0x04, 0x11, 0x11, 0xA9, 0x32, 0x18}, 16, 0}, + // PAGE1 + {0xFF, (uint8_t[]){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0}, + {0xB0, (uint8_t[]){0x60}, 1, 0}, // Vop=4.7375v + {0xB1, (uint8_t[]){0x32}, 1, 0}, // VCOM=32 + {0xB2, (uint8_t[]){0x07}, 1, 0}, // VGH=15v + {0xB3, (uint8_t[]){0x80}, 1, 0}, + {0xB5, (uint8_t[]){0x49}, 1, 0}, // VGL=-10.17v + {0xB7, (uint8_t[]){0x85}, 1, 0}, + {0xB8, (uint8_t[]){0x21}, 1, 0}, // AVDD=6.6 & AVCL=-4.6 + {0xC1, (uint8_t[]){0x78}, 1, 0}, + {0xC2, (uint8_t[]){0x78}, 1, 0}, + {0xE0, (uint8_t[]){0x00, 0x1B, 0x02}, 3, 0}, + {0xE1, (uint8_t[]){0x08, 0xA0, 0x00, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x44, 0x44}, 11, 0}, + {0xE2, (uint8_t[]){0x11, 0x11, 0x44, 0x44, 0xED, 0xA0, 0x00, 0x00, 0xEC, 0xA0, 0x00, 0x00}, 12, 0}, + {0xE3, (uint8_t[]){0x00, 0x00, 0x11, 0x11}, 4, 0}, + {0xE4, (uint8_t[]){0x44, 0x44}, 2, 0}, + {0xE5, (uint8_t[]){0x0A, 0xE9, 0xD8, 0xA0, 0x0C, 0xEB, 0xD8, 0xA0, 0x0E, 0xED, 0xD8, 0xA0, 0x10, 0xEF, 0xD8, 0xA0}, 16, 0}, + {0xE6, (uint8_t[]){0x00, 0x00, 0x11, 0x11}, 4, 0}, + {0xE7, (uint8_t[]){0x44, 0x44}, 2, 0}, + {0xE8, (uint8_t[]){0x09, 0xE8, 0xD8, 0xA0, 0x0B, 0xEA, 0xD8, 0xA0, 0x0D, 0xEC, 0xD8, 0xA0, 0x0F, 0xEE, 0xD8, 0xA0}, 16, 0}, + {0xEB, (uint8_t[]){0x02, 0x00, 0xE4, 0xE4, 0x88, 0x00, 0x40}, 7, 0}, + {0xEC, (uint8_t[]){0x3C, 0x00}, 2, 0}, + {0xED, (uint8_t[]){0xAB, 0x89, 0x76, 0x54, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x45, 0x67, 0x98, 0xBA}, 16, 0}, + // VAP & VAN + {0xFF, (uint8_t[]){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0}, + {0xE5, (uint8_t[]){0xE4}, 1, 0}, + {0xFF, (uint8_t[]){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0}, + // 0x70 RGB888, 0x60 RGB666, 0x50 RGB565 + {0x3A, (uint8_t[]){0x60}, 1, 0}, + // Sleep Out + {0x11, NULL, 0, 120}, + // Display On + {0x29, NULL, 0, 0}}; + +esp_err_t st7701_reset(esp_lcd_panel_t *panel) +{ + log_v("panel:0x%08x", panel); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const st7701_panel_t *ph = (st7701_panel_t *)panel; + + esp_err_t res; + + if (ph->panel_dev_config.reset_gpio_num != GPIO_NUM_NC) + { + // Hardware reset + gpio_set_level(ph->panel_dev_config.reset_gpio_num, ph->panel_dev_config.flags.reset_active_high); + vTaskDelay(pdMS_TO_TICKS(1)); + gpio_set_level(ph->panel_dev_config.reset_gpio_num, !ph->panel_dev_config.flags.reset_active_high); + } + else + { + // Software reset + if ((res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_SWRESET, NULL, 0)) != ESP_OK) + { + log_e("Sending LCD_CMD_SWRESET failed"); + return res; + } + } + + vTaskDelay(pdMS_TO_TICKS(5)); + + if ((res = esp_lcd_panel_reset(ph->lcd_panel)) != ESP_OK) + { + log_e("Failed to reset RGB panel"); + return res; + } + + return ESP_OK; +} + +esp_err_t st7701_init(esp_lcd_panel_t *panel) +{ + log_v("panel:0x%08x", panel); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const st7701_panel_t *ph = (st7701_panel_t *)panel; + + esp_err_t res; + const uint8_t bkxsel[] = {0x77, 0x01, 0x00, 0x00, 0x00}; + if ((res = esp_lcd_panel_io_tx_param(ph->io, ST7701_CMD_CND2BKxSEL, bkxsel, sizeof(bkxsel))) != ESP_OK) + { + log_e("Sending ST7701_CMD_CND2BKxSEL failed"); + return res; + } + + vTaskDelay(pdMS_TO_TICKS(100)); + + uint8_t colmod; + switch (ph->panel_dev_config.bits_per_pixel) + { + case 16: // RGB565 + colmod = 0x50; + break; + case 18: // RGB666 + colmod = 0x60; + break; + case 24: // RGB888 + colmod = 0x70; + break; + default: + log_e("Invalid bits per pixel: %d. Only RGB565, RGB666 and RGB888 are supported", ph->panel_dev_config.bits_per_pixel); + return ESP_ERR_INVALID_ARG; + } + + if ((res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_MADCTL, &ph->madctl, 1)) != ESP_OK || + (res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_COLMOD, &colmod, 1)) != ESP_OK) + { + log_e("Sending MADCTL/COLMOD failed"); + return res; + } + + const lcd_init_cmd_t *cmd = st7701_vendor_specific_init_default; + uint16_t cmds_size = sizeof(st7701_vendor_specific_init_default) / sizeof(lcd_init_cmd_t); + if (ph->panel_dev_config.vendor_config != NULL) + { + cmd = ((st7701_vendor_config_t *)ph->panel_dev_config.vendor_config)->init_cmds; + cmds_size = ((st7701_vendor_config_t *)ph->panel_dev_config.vendor_config)->init_cmds_size; + } + + while (cmds_size-- > 0) + { + if ((res = esp_lcd_panel_io_tx_param(ph->io, cmd->cmd, cmd->data, cmd->bytes)) != ESP_OK) + { + log_e("Sending command: 0x%02x failed", cmd->cmd); + return res; + } + + vTaskDelay(pdMS_TO_TICKS(cmd->delay_ms)); + cmd++; + } + + if ((res = esp_lcd_panel_init(ph->lcd_panel)) != ESP_OK) + { + log_e("Initializing panel failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t st7701_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) +{ + log_v("panel:0x%08x, x_start:%d, y_start:%d, x_end:%d, y_end:%d, color_data:0x%08x", panel, x_start, y_start, x_end, y_end, color_data); + + const st7701_panel_t *ph = (st7701_panel_t *)panel; + return esp_lcd_panel_draw_bitmap(ph->lcd_panel, x_start, y_start, x_end, y_end, color_data); +} + +esp_err_t st7701_invert_color(esp_lcd_panel_t *panel, bool invert) +{ + log_v("panel:0x%08x, invert:%d", panel, invert); + + const st7701_panel_t *ph = (st7701_panel_t *)panel; + return esp_lcd_panel_invert_color(ph->lcd_panel, invert); +} + +esp_err_t st7701_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + log_v("panel:0x%08x, mirror_x:%d, mirror_y:%d", panel, mirror_x, mirror_y); + + st7701_panel_t *ph = (st7701_panel_t *)panel; + return esp_lcd_panel_mirror(ph->lcd_panel, mirror_x, mirror_y); +} + +esp_err_t st7701_swap_xy(esp_lcd_panel_t *panel, bool swap_xy) +{ + log_v("panel:0x%08x, swap_xy:%d", panel, swap_xy); + + st7701_panel_t *ph = (st7701_panel_t *)panel; + return esp_lcd_panel_swap_xy(ph->lcd_panel, swap_xy); +} + +esp_err_t st7701_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) +{ + log_v("panel:0x%08x, x_gap:%d, y_gap:%d", panel, x_gap, y_gap); + + st7701_panel_t *ph = (st7701_panel_t *)panel; + return esp_lcd_panel_set_gap(ph->lcd_panel, x_gap, y_gap); +} + +esp_err_t st7701_disp_off(esp_lcd_panel_t *panel, bool off) +{ + log_v("panel:0x%08x, off:%d", panel, off); + + const st7701_panel_t *ph = (st7701_panel_t *)panel; + return esp_lcd_panel_disp_off(ph->lcd_panel, off); +} + +esp_err_t st7701_del(esp_lcd_panel_t *panel) +{ + log_v("panel:0x%08x", panel); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + st7701_panel_t *ph = (st7701_panel_t *)panel; + + // Delete the RGB Panel + esp_lcd_panel_del(ph->lcd_panel); + // Reset RESET + if (ph->panel_dev_config.reset_gpio_num != GPIO_NUM_NC) + gpio_reset_pin(ph->panel_dev_config.reset_gpio_num); + + free(ph); + + return ESP_OK; +} + +esp_err_t esp_lcd_new_panel_st7701(const esp_lcd_panel_io_handle_t io, const esp_lcd_rgb_panel_config_t *rgb_panel_config, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *panel_handle) +{ + log_v("panel_io_handle:0x%08x, rgb_panel_config:0x%08x, panel_dev_config:0x%08x, panel_handle:0x%08x", io, rgb_panel_config, panel_dev_config, panel_handle); + if (io == NULL || rgb_panel_config == NULL || panel_dev_config == NULL || panel_handle == NULL) + return ESP_ERR_INVALID_ARG; + + if (panel_dev_config->reset_gpio_num != GPIO_NUM_NC && !GPIO_IS_VALID_GPIO(panel_dev_config->reset_gpio_num)) + { + log_e("Invalid GPIO RST pin: %d", panel_dev_config->reset_gpio_num); + return ESP_ERR_INVALID_ARG; + } + + uint8_t madctl; + switch (panel_dev_config->color_space) + { + case ESP_LCD_COLOR_SPACE_RGB: + madctl = 0; + break; + case ESP_LCD_COLOR_SPACE_BGR: + madctl = LCD_CMD_BGR_BIT; + break; + default: + log_e("Invalid color space: %d. Only RGB and BGR are supported", panel_dev_config->color_space); + return ESP_ERR_INVALID_ARG; + } + + if (panel_dev_config->reset_gpio_num != GPIO_NUM_NC) + { + esp_err_t res; + const gpio_config_t cfg = { + .pin_bit_mask = BIT64(panel_dev_config->reset_gpio_num), + .mode = GPIO_MODE_OUTPUT}; + if ((res = gpio_config(&cfg)) != ESP_OK) + { + log_e("Configuring GPIO for RST failed"); + return res; + } + } + + // Create RGB panel + esp_err_t res; + esp_lcd_panel_handle_t lcd_panel; + + if ((res = esp_lcd_new_rgb_panel(rgb_panel_config, &lcd_panel)) != ESP_OK) + { + log_e("Unable to create RGB panel"); + return res; + } + + log_d("lcd_panel: 0x%08x", lcd_panel); + + st7701_panel_t *ph = heap_caps_calloc(1, sizeof(st7701_panel_t), MALLOC_CAP_DEFAULT); + if (ph == NULL) + { + log_e("No memory available for st7701_panel_t"); + return ESP_ERR_NO_MEM; + } + + ph->io = io; + memcpy(&ph->panel_dev_config, panel_dev_config, sizeof(esp_lcd_panel_dev_config_t)); + ph->madctl = madctl; + ph->lcd_panel = lcd_panel; + + ph->base.del = st7701_del; + ph->base.reset = st7701_reset; + ph->base.init = st7701_init; + ph->base.draw_bitmap = st7701_draw_bitmap; + ph->base.invert_color = st7701_invert_color; + ph->base.mirror = st7701_mirror; + ph->base.swap_xy = st7701_swap_xy; + ph->base.set_gap = st7701_set_gap; + ph->base.disp_off = st7701_disp_off; + + log_d("panel_handle: 0x%08x", ph); + *panel_handle = (esp_lcd_panel_handle_t)ph; + + return ESP_OK; +} + +#endif \ No newline at end of file diff --git a/src/esp_panel_st7796.c b/src/esp_panel_st7796.c new file mode 100644 index 0000000..6ce420e --- /dev/null +++ b/src/esp_panel_st7796.c @@ -0,0 +1,363 @@ +#ifdef LCD_ST7796_SPI + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct +{ + esp_lcd_panel_t base; + esp_lcd_panel_io_handle_t io; + esp_lcd_panel_dev_config_t config; + // Data + int x_gap; + int y_gap; + uint8_t madctl; +} st7796_panel_t; + +const lcd_init_cmd_t st7796_vendor_specific_init_default[] = { + {0xf0, (const uint8_t[]){0xc3}, 1, 0}, + {0xf0, (const uint8_t[]){0x96}, 1, 0}, + {0xb4, (const uint8_t[]){0x01}, 1, 0}, + {0xb7, (const uint8_t[]){0xc6}, 1, 0}, + {0xe8, (const uint8_t[]){0x40, 0x8a, 0x00, 0x00, 0x29, 0x19, 0xa5, 0x33}, 8, 0}, + {0xc1, (const uint8_t[]){0x06}, 1, 0}, + {0xc2, (const uint8_t[]){0xa7}, 1, 0}, + {0xc5, (const uint8_t[]){0x18}, 1, 0}, + {0xe0, (const uint8_t[]){0xf0, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0x54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x1b}, 14, 0}, + {0xe1, (const uint8_t[]){0xf0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2d, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x1b}, 14, 0}, + {0xf0, (const uint8_t[]){0x3c}, 1, 0}, + {0xf0, (const uint8_t[]){0x69}, 1, 0}, +}; + +esp_err_t st7796_reset(esp_lcd_panel_t *panel) +{ + log_v("panel:0x%08x", panel); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const st7796_panel_t *ph = (st7796_panel_t *)panel; + + if (ph->config.reset_gpio_num != GPIO_NUM_NC) + { + // Hardware reset + gpio_set_level(ph->config.reset_gpio_num, ph->config.flags.reset_active_high); + vTaskDelay(pdMS_TO_TICKS(1)); + gpio_set_level(ph->config.reset_gpio_num, !ph->config.flags.reset_active_high); + } + else + { + esp_err_t res; + // Software reset + if ((res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_SWRESET, NULL, 0)) != ESP_OK) + { + log_e("Sending LCD_CMD_SWRESET failed"); + return res; + } + } + + vTaskDelay(pdMS_TO_TICKS(5)); + + return ESP_OK; +} + +esp_err_t st7796_init(esp_lcd_panel_t *panel) +{ + log_v("panel:0x%08x", panel); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const st7796_panel_t *ph = (st7796_panel_t *)panel; + + esp_err_t res; + if ((res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_SLPOUT, NULL, 0)) != ESP_OK) + { + log_e("Sending SLPOUT failed"); + return res; + } + + vTaskDelay(pdMS_TO_TICKS(100)); + + uint8_t colmod; + switch (ph->config.bits_per_pixel) + { + case 16: // RGB565 + colmod = 0x05; + break; + case 18: // RGB666 + colmod = 0x06; + break; + case 24: // RGB888 + colmod = 0x07; + break; + default: + log_e("Invalid bits per pixel: %d. Only RGB565, RGB666 and RGB888 are supported", ph->config.bits_per_pixel); + return ESP_ERR_INVALID_ARG; + } + + if ((res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_MADCTL, &ph->madctl, 1)) != ESP_OK || + (res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_COLMOD, &colmod, 1)) != ESP_OK) + { + log_e("Sending MADCTL/COLMOD failed"); + return res; + } + + const lcd_init_cmd_t *cmd = st7796_vendor_specific_init_default; + uint16_t cmds_size = sizeof(st7796_vendor_specific_init_default) / sizeof(lcd_init_cmd_t); + if (ph->config.vendor_config != NULL) + { + cmd = ((st7796_vendor_config_t *)ph->config.vendor_config)->init_cmds; + cmds_size = ((st7796_vendor_config_t *)ph->config.vendor_config)->init_cmds_size; + } + + while (cmds_size-- > 0) + { + if ((res = esp_lcd_panel_io_tx_param(ph->io, cmd->cmd, cmd->data, cmd->bytes)) != ESP_OK) + { + log_e("Sending command: 0x%02x failed", cmd->cmd); + return res; + } + + vTaskDelay(pdMS_TO_TICKS(cmd->delay_ms)); + cmd++; + } + + return ESP_OK; +} + +esp_err_t st7796_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data) +{ + log_v("panel:0x%08x, x_start:%d, y_start:%d, x_end:%d, y_end:%d, color_data:0x%08x", panel, x_start, y_start, x_end, y_end, color_data); + if (panel == NULL || color_data == NULL) + return ESP_ERR_INVALID_ARG; + + const st7796_panel_t *ph = (st7796_panel_t *)panel; + + if (x_start >= x_end) + { + log_w("X-start greater than the x-end"); + return ESP_ERR_INVALID_ARG; + } + + if (y_start >= y_end) + { + log_w("Y-start greater than the y-end"); + return ESP_ERR_INVALID_ARG; + } + + // Correct for gap + x_start += ph->x_gap; + x_end += ph->x_gap; + y_start += ph->y_gap; + y_end += ph->y_gap; + + esp_err_t res; + const uint8_t caset[4] = {x_start >> 8, x_start, (x_end - 1) >> 8, (x_end - 1)}; + const uint8_t raset[4] = {y_start >> 8, y_start, (y_end - 1) >> 8, (y_end - 1)}; + if ((res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_CASET, caset, sizeof(caset))) != ESP_OK || + (res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_RASET, raset, sizeof(raset))) != ESP_OK) + { + log_e("Sending CASET/RASET failed"); + return res; + } + + uint8_t bytes_per_pixel = (ph->config.bits_per_pixel + 0x7) >> 3; + size_t len = (x_end - x_start) * (y_end - y_start) * bytes_per_pixel; + if ((res = esp_lcd_panel_io_tx_color(ph->io, LCD_CMD_RAMWR, color_data, len)) != ESP_OK) + { + log_e("Sending RAMWR failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t st7796_invert_color(esp_lcd_panel_t *panel, bool invert) +{ + log_v("panel:0x%08x, invert:%d", panel, invert); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const st7796_panel_t *ph = (st7796_panel_t *)panel; + + esp_err_t res; + if ((res = esp_lcd_panel_io_tx_param(ph->io, invert ? LCD_CMD_INVON : LCD_CMD_INVOFF, NULL, 0)) != ESP_OK) + { + log_e("Sending LCD_CMD_INVON/LCD_CMD_INVOFF failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t st7796_update_madctl(st7796_panel_t *ph) +{ + esp_err_t res; + if ((res = esp_lcd_panel_io_tx_param(ph->io, LCD_CMD_MADCTL, &ph->madctl, 1)) != ESP_OK) + { + log_e("Sending LCD_CMD_MADCTL failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t st7796_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y) +{ + log_v("panel:0x%08x, mirror_x:%d, mirror_y:%d", panel, mirror_x, mirror_y); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + st7796_panel_t *ph = (st7796_panel_t *)panel; + + if (mirror_x) + ph->madctl |= LCD_CMD_MX_BIT; + else + ph->madctl &= ~LCD_CMD_MX_BIT; + + if (mirror_y) + ph->madctl |= LCD_CMD_MY_BIT; + else + ph->madctl &= ~LCD_CMD_MY_BIT; + + return st7796_update_madctl(ph); +} + +esp_err_t st7796_swap_xy(esp_lcd_panel_t *panel, bool swap_xy) +{ + log_v("panel:0x%08x, swap_xy:%d", panel, swap_xy); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + st7796_panel_t *ph = (st7796_panel_t *)panel; + + if (swap_xy) + ph->madctl |= LCD_CMD_MV_BIT; + else + ph->madctl &= ~LCD_CMD_MV_BIT; + + return st7796_update_madctl(ph); +} + +esp_err_t st7796_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap) +{ + log_v("panel:0x%08x, x_gap:%d, y_gap:%d", panel, x_gap, y_gap); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + st7796_panel_t *ph = (st7796_panel_t *)panel; + + ph->x_gap = x_gap; + ph->y_gap = y_gap; + + return ESP_OK; +} + +esp_err_t st7796_disp_off(esp_lcd_panel_t *panel, bool off) +{ + log_v("panel:0x%08x, off:%d", panel, off); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + const st7796_panel_t *ph = (st7796_panel_t *)panel; + + esp_err_t res; + if ((res = esp_lcd_panel_io_tx_param(ph->io, off ? LCD_CMD_DISPOFF : LCD_CMD_DISPON, NULL, 0)) != ESP_OK) + { + log_e("Sending LCD_CMD_DISPOFF/LCD_CMD_DISPON failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t st7796_del(esp_lcd_panel_t *panel) +{ + log_v("panel:0x%08x", panel); + if (panel == NULL) + return ESP_ERR_INVALID_ARG; + + st7796_panel_t *ph = (st7796_panel_t *)panel; + + // Reset RESET + if (ph->config.reset_gpio_num != GPIO_NUM_NC) + gpio_reset_pin(ph->config.reset_gpio_num); + + free(ph); + + return ESP_OK; +} + +esp_err_t esp_lcd_new_panel_st7796(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *config, esp_lcd_panel_handle_t *handle) +{ + log_v("io:0x%08x, config:0x%08x, handle:0x%08x", io, config, handle); + if (io == NULL || config == NULL || handle == NULL) + return ESP_ERR_INVALID_ARG; + + if (config->reset_gpio_num != GPIO_NUM_NC && !GPIO_IS_VALID_GPIO(config->reset_gpio_num)) + { + log_e("Invalid GPIO RST pin: %d", config->reset_gpio_num); + return ESP_ERR_INVALID_ARG; + } + + uint8_t madctl; + switch (config->color_space) + { + case ESP_LCD_COLOR_SPACE_RGB: + madctl = 0; + break; + case ESP_LCD_COLOR_SPACE_BGR: + madctl = LCD_CMD_BGR_BIT; + break; + default: + log_e("Invalid color space: %d. Only RGB and BGR are supported", config->color_space); + return ESP_ERR_INVALID_ARG; + } + + if (config->reset_gpio_num != GPIO_NUM_NC) + { + esp_err_t res; + const gpio_config_t cfg = { + .pin_bit_mask = BIT64(config->reset_gpio_num), + .mode = GPIO_MODE_OUTPUT}; + if ((res = gpio_config(&cfg)) != ESP_OK) + { + log_e("Configuring GPIO for RST failed"); + return res; + } + } + + st7796_panel_t *ph = heap_caps_calloc(1, sizeof(st7796_panel_t), MALLOC_CAP_DEFAULT); + if (ph == NULL) + { + log_e("No memory available for st7796_panel_t"); + return ESP_ERR_NO_MEM; + } + + ph->io = io; + memcpy(&ph->config, config, sizeof(esp_lcd_panel_dev_config_t)); + ph->madctl = madctl; + + ph->base.del = st7796_del; + ph->base.reset = st7796_reset; + ph->base.init = st7796_init; + ph->base.draw_bitmap = st7796_draw_bitmap; + ph->base.invert_color = st7796_invert_color; + ph->base.mirror = st7796_mirror; + ph->base.swap_xy = st7796_swap_xy; + ph->base.set_gap = st7796_set_gap; + ph->base.disp_off = st7796_disp_off; + + log_d("handle: 0x%08x", ph); + *handle = (esp_lcd_panel_handle_t)ph; + + return ESP_OK; +} + +#endif \ No newline at end of file diff --git a/src/esp_touch_cst816s.c b/src/esp_touch_cst816s.c new file mode 100644 index 0000000..1ca139b --- /dev/null +++ b/src/esp_touch_cst816s.c @@ -0,0 +1,333 @@ +#ifdef TOUCH_CST816S_I2C + +#include +#include +#include +#include + +// Registers +#define CST816S_GESTURE_REG 0x01 +#define CST816S_FINGERNUM_REG 0x02 +#define CST816S_XPOSH_REG 0x03 +#define CST816S_XPOSL_REG 0x04 +#define CST816S_YPOSH_REG 0x05 +#define CST816S_YPOSL_REG 0x06 + +#define CST816S_BC0H_REG 0xB0 +#define CST816S_BC0L_REG 0xB1 +#define CST816S_BC1H_REG 0xB2 +#define CST816S_BC1L_REG 0xB3 + +#define CST816S_SLEEP_REG 0xA5 +#define CST816S_CHIPID_REG 0xA7 +#define CST816S_PROJID_REG 0xA8 +#define CST816S_FWVERSION_REG 0xA9 + +#define CST816S_MOTIONMASK_REG 0xEC +#define CST816S_IRQPULSEWIDTH_REG 0xED +#define CST816S_NORSCANPER_REG 0xEE +#define CST816S_MOTIONSIANGLE_REG 0xEF +#define CST816S_LPSCANRAW1H_REG 0xF0 +#define CST816S_LPSCANRAW1L_REG 0xF1 +#define CST816S_LPSCANRAW2H_REG 0xF2 +#define CST816S_LPSCANRAW2L_REG 0xF3 +#define CST816S_LPAUTOWAKEUPTIME_REG 0xF4 +#define CST816S_LPSCANTH_REG 0xF5 +#define CST816S_LPSCANWIN_REG 0xF6 +#define CST816S_LPSCANFREQ_REG 0xF7 +#define CST816S_LPSCANIDAC_REG 0xF8 +#define CST816S_AUTOSLEEPTIME_REG 0xF9 +#define CST816S_IRQCTL_REG 0xFA +#define CST816S_AUTORESET_REG 0xFB +#define CST816S_LONGPRESSTIME_REG 0xFC +#define CST816S_IOCTL_REG 0xFD +#define CST816S_AUTOSLEEP_REG 0xFE + +// Touch events +enum cst816s_touch_event +{ + none = 0, + down = 1, + up = 2, + slide_left = 3, + slide_right = 4, + click = 5, + double_click = 11, + press = 12 +}; + +typedef struct __attribute__((packed)) +{ + uint8_t id; // 0xA7 + uint8_t projectId; // 0xA8 + uint8_t fwVersion; // 0xA9 +} cst816s_info; + +typedef struct __attribute__((packed)) +{ + uint8_t x_h : 4; + uint8_t : 4; + uint8_t x_l; + uint8_t y_h : 4; + uint8_t : 4; + uint8_t y_l; +} cst816s_point; + +typedef struct __attribute__((packed)) +{ + uint8_t event; + uint8_t fingerNum; + cst816s_point point; // POSH (4 bits) + POSL (8 bits) +} cst816s_touch_event; + +esp_err_t cst816s_reset(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + esp_err_t res; + // Set RST active + if ((res = gpio_set_level(th->config.rst_gpio_num, th->config.levels.reset)) != ESP_OK) + { + log_e("Setting RST failed"); + return res; + } + + // Wait at least 100us + vTaskDelay(pdMS_TO_TICKS(1)); + + // Set RST high + if ((res = gpio_set_level(th->config.rst_gpio_num, !th->config.levels.reset)) != ESP_OK) + { + log_e("Resetting RST failed"); + return res; + } + + // Wait at least 50ms + vTaskDelay(pdMS_TO_TICKS(50)); + + return ESP_OK; +} + +esp_err_t cst816s_read_info(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + esp_err_t res; + cst816s_info info; + if ((res = esp_lcd_panel_io_rx_param(th->io, CST816S_CHIPID_REG, &info, sizeof(info))) != ESP_OK) + { + log_e("Unable to read CST816S info"); + return res; + } + + log_d("CST816S Id: 0x%02X", info.id); + log_d("CST816S Project id: %d", info.projectId); + log_d("CST816S Firmware version: %d", info.fwVersion); + + return ESP_OK; +} + +esp_err_t cst816s_enter_sleep(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + esp_err_t res; + const uint8_t data[] = {0x03}; // Sleep + if ((res = esp_lcd_panel_io_tx_param(th->io, CST816S_SLEEP_REG, data, sizeof(data))) != ESP_OK) + log_e("Unable to write GT911_CONTROL_REG"); + + return res; +} + +esp_err_t cst816s_read_data(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + esp_err_t res; + cst816s_touch_event buffer; + + // Read only the XY register + if ((res = esp_lcd_panel_io_rx_param(th->io, CST816S_GESTURE_REG, &buffer, sizeof(buffer))) != ESP_OK) + { + log_e("Unable to read CST816S point"); + return res; + } + + portENTER_CRITICAL(&th->data.lock); + if ((th->data.points = buffer.fingerNum) > 0) + { + th->data.coords[0].x = (buffer.point.x_h << 8) | buffer.point.x_l; + th->data.coords[0].y = (buffer.point.y_h << 8) | buffer.point.y_l; + th->data.coords[0].strength = 0; + } + + portEXIT_CRITICAL(&th->data.lock); + + return ESP_OK; +} + +bool cst816s_get_xy(esp_lcd_touch_handle_t th, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num) +{ + log_v("th:0x%08x, x:0x%08x, y:0x%08x, strength:0x%08x, point_num:0x%08x, max_point_num:%d", th, x, y, strength, point_num, max_point_num); + if (th == NULL || x == NULL || y == NULL || point_num == NULL) + return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&th->data.lock); + *point_num = th->data.points > max_point_num ? max_point_num : th->data.points; + for (uint8_t i = 0; i < *point_num; i++) + { + x[i] = th->data.coords[i].x; + y[i] = th->data.coords[i].y; + if (strength != NULL) + strength[i] = th->data.coords[i].strength; + } + + th->data.points = 0; + portEXIT_CRITICAL(&th->data.lock); + + return *point_num > 0; +} + +esp_err_t cst816s_del(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&th->data.lock); + + // Remove interrupts and reset INT + if (th->config.int_gpio_num != GPIO_NUM_NC) + { + if (th->config.interrupt_callback) + gpio_isr_handler_remove(th->config.int_gpio_num); + + gpio_reset_pin(th->config.int_gpio_num); + } + + // Reset RST + if (th->config.rst_gpio_num != GPIO_NUM_NC) + gpio_reset_pin(th->config.rst_gpio_num); + + free(th); + + return ESP_OK; +} + +esp_err_t esp_lcd_touch_new_i2c_cst816s(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *handle) +{ + log_v("io:0x%08x, config:0x%08x, handle:0x%08x", io, config, handle); + if (io == NULL || config == NULL || handle == NULL) + return ESP_ERR_INVALID_ARG; + + if (config->int_gpio_num != GPIO_NUM_NC && !GPIO_IS_VALID_GPIO(config->int_gpio_num)) + { + log_e("Invalid GPIO INT pin: %d", config->int_gpio_num); + return ESP_ERR_INVALID_ARG; + } + + if (config->rst_gpio_num != GPIO_NUM_NC && !GPIO_IS_VALID_GPIO(config->rst_gpio_num)) + { + log_e("Invalid GPIO RST pin: %d", config->rst_gpio_num); + return ESP_ERR_INVALID_ARG; + } + + esp_err_t res; + const esp_lcd_touch_handle_t th = heap_caps_calloc(1, sizeof(esp_lcd_touch_t), MALLOC_CAP_DEFAULT); + if (th == NULL) + { + log_e("No memory available for esp_lcd_touch_t"); + return ESP_ERR_NO_MEM; + } + + th->io = io; + th->enter_sleep = cst816s_enter_sleep; + th->read_data = cst816s_read_data; + th->get_xy = cst816s_get_xy; +#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0) + th->get_button_state = cst816s_get_button_state; +#endif + th->del = cst816s_del; + memcpy(&th->config, config, sizeof(esp_lcd_touch_config_t)); + portMUX_INITIALIZE(&th->data.lock); + + if (config->int_gpio_num != GPIO_NUM_NC) + { + esp_rom_gpio_pad_select_gpio(config->int_gpio_num); + const gpio_config_t cfg = { + .pin_bit_mask = BIT64(config->int_gpio_num), + .mode = GPIO_MODE_INPUT, + // If the user has provided a callback routine for the interrupt enable the interrupt mode on the negative edge. + .intr_type = config->interrupt_callback ? GPIO_INTR_NEGEDGE : GPIO_INTR_DISABLE}; + if ((res = gpio_config(&cfg)) != ESP_OK) + { + free(th); + log_e("Configuring GPIO for INT failed"); + return res; + } + + if (config->interrupt_callback != NULL) + { + if ((res = esp_lcd_touch_register_interrupt_callback(th, config->interrupt_callback)) != ESP_OK) + { + gpio_reset_pin(th->config.int_gpio_num); + free(th); + log_e("Registering INT callback failed"); + return res; + } + } + + if (config->rst_gpio_num != GPIO_NUM_NC) + { + esp_rom_gpio_pad_select_gpio(config->rst_gpio_num); + const gpio_config_t cfg = { + .pin_bit_mask = BIT64(config->rst_gpio_num), + .mode = GPIO_MODE_OUTPUT}; + if ((res = gpio_config(&cfg)) != ESP_OK) + { + if (th->config.int_gpio_num != GPIO_NUM_NC) + { + if (config->interrupt_callback != NULL) + gpio_isr_handler_remove(th->config.int_gpio_num); + + gpio_reset_pin(th->config.int_gpio_num); + } + + free(th); + log_e("Configuring or setting GPIO for RST failed"); + return res; + } + } + + // Reset controller + if ((res = cst816s_reset(th)) != ESP_OK) + { + log_e("GT911 reset failed"); + cst816s_del(th); + return res; + } + + // Read type and resolution + if ((res = cst816s_read_info(th)) != ESP_OK) + { + log_e("GT911 read info failed"); + cst816s_del(th); + return res; + } + } + + log_d("handle:0x%08x", th); + *handle = th; + + return ESP_OK; +} + +#endif // TOUCH_CST816S_I2C \ No newline at end of file diff --git a/src/esp_touch_gt911.c b/src/esp_touch_gt911.c new file mode 100644 index 0000000..d79930c --- /dev/null +++ b/src/esp_touch_gt911.c @@ -0,0 +1,471 @@ +#ifdef TOUCH_GT911_I2C + +#include +#include +#include +#include + +// Registers +const uint16_t GT911_KEYS_REG = 0x8093; +const uint16_t GT911_BUFFER_STATUS_REG = 0x814E; +const uint16_t GT911_TOUCH_POINTS_REG = 0x814F; +const uint16_t GT911_CONFIG_REG = 0x8047; +const uint16_t GT911_PRODUCT_ID_REG = 0x8140; +const uint16_t GT911_CONTROL_REG = 0x8040; + +const char productId[] = "911"; + +// Limits of points / buttons +#define GT911_KEYS_MAX 4 +#define GT911_TOUCH_POINTS_MAX 5 + +#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > GT911_TOUCH_MAX_BUTTONS) +#error more buttons than available +#endif + +typedef struct __attribute__((packed)) +{ + uint16_t x; + uint16_t y; +} gt911_point; + +typedef struct __attribute__((packed)) +{ + char productId[4]; // 0x8140 - 0x8143 + uint16_t fwId; // 0x8144 - 0x8145 + gt911_point resolution; // 0x8146 - 0x8147 (x), 0x8148 - 0x8149 (y) + uint8_t vendorId; // 0x814A +} gt911_info; + +typedef struct +{ + uint8_t number_points : 4; // Bit 0-3 (0x0F): Number of touch points + uint8_t have_key : 1; // Bit 4 (0x10): Have touch key + uint8_t unused : 1; // Bit 5 + uint8_t large_detect : 1; // Bit 6 (0x40): Large-area touch on touch pad + uint8_t buffer_status : 1; // Bit 7 (0x80): Coordinate (or key) is ready for host to read +} buffer_status_flags; + +// Touch events +enum gt911_touch_event +{ + none = 0, + down = 1, + up = 2, + slide = 3, + slide_end = 4 +}; + +typedef struct __attribute__((packed)) +{ + // 0x814F-0x8156, ... 0x8176 (5 points) + uint8_t event; + gt911_point point; + uint16_t area; + uint8_t reserved; +} gt911_touch_event; + +typedef struct __attribute__((packed)) +{ + union esp_lcd_touch_gt911 + { + uint8_t keys[GT911_KEYS_MAX]; + gt911_touch_event touch_points[GT911_TOUCH_POINTS_MAX]; + } data; +} gt911_key_touch_data; + +gt911_point gt911_resolution; + +esp_err_t gt911_reset(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + esp_err_t res; + // Set RST active + if ((res = gpio_set_level(th->config.rst_gpio_num, th->config.levels.reset)) != ESP_OK) + { + log_e("Setting RST failed"); + return res; + } + + // Wait at least 100us + vTaskDelay(pdMS_TO_TICKS(1)); + + // Set RST high + if ((res = gpio_set_level(th->config.rst_gpio_num, !th->config.levels.reset)) != ESP_OK) + { + log_e("Resetting RST failed"); + return res; + } + + // Wait at least 50ms + vTaskDelay(pdMS_TO_TICKS(50)); + + return ESP_OK; +} + +// This function is called if the coordinates do not match the returned coordinates. This is the case for display having another form factor, e.g. 472x320 +void gt911_process_coordinates(esp_lcd_touch_handle_t th, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num) +{ + log_v("th:0x%08x, x:0x%08x, y:0x%08x, strength:0x%08x, point_num:0x%08x, max_point_num:%d", th, x, y, strength, point_num, max_point_num); + + portENTER_CRITICAL(&th->data.lock); + uint8_t points_available = *point_num > max_point_num ? max_point_num : *point_num; + for (uint8_t i = 0; i < points_available; i++) + { + // Correct the points for the info obtained from the GT911 and configured resolution + x[i] = (x[i] * th->config.x_max) / gt911_resolution.x; + y[i] = (y[i] * th->config.y_max) / gt911_resolution.y; + log_d("Processed coordinates: (%d,%d), area:%d", x[i], y[i], strength[i]); + } + + portEXIT_CRITICAL(&th->data.lock); +} + +esp_err_t gt911_read_info(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + esp_err_t res; + gt911_info info; + if ((res = esp_lcd_panel_io_rx_param(th->io, GT911_PRODUCT_ID_REG, &info, sizeof(info))) != ESP_OK) + { + log_e("Unable to read GT911_PRODUCT_ID_REG"); + return res; + } + + if (strcmp((char *)&info.productId, productId) != 0) + { + log_e("GT911 chip not found"); + return ESP_FAIL; + } + + log_d("GT911 productId: %s", info.productId); // 0x8140 - 0x8143 + log_d("GT911 fwId: 0x%04x", info.fwId); // 0x8144 - 0x8145 + log_d("GT911 xResolution/yResolution: (%d,%d)", info.resolution.x, info.resolution.y); // 0x8146 - 0x8147 // 0x8148 - 0x8149 + log_d("GT911 vendorId: 0x%02x", info.vendorId); // 0x814A + + // Save resolution for processing to scale touch to the display + gt911_resolution = info.resolution; + if (info.resolution.x > 0 && info.resolution.y > 0 && (info.resolution.x != th->config.x_max || info.resolution.y != th->config.y_max)) + { + log_w("Resolution obtained from GT911 (%d,%d) does not match resolution (%d,%d). Enabled coordinate adjustment.", info.resolution.x, info.resolution.y, th->config.x_max, th->config.y_max); + th->config.process_coordinates = gt911_process_coordinates; + } + + return ESP_OK; +} + +esp_err_t gt911_enter_sleep(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + esp_err_t res; + const uint8_t data[] = {0x05}; // Sleep + if ((res = esp_lcd_panel_io_tx_param(th->io, GT911_CONTROL_REG, data, sizeof(data))) != ESP_OK) + log_e("Unable to write GT911_CONTROL_REG"); + + return res; +} + +esp_err_t gt911_exit_sleep(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + esp_err_t res; + if (th->config.int_gpio_num == GPIO_NUM_NC) + { + log_w("No INT pin defined"); + return ESP_OK; + } + + gpio_config_t cfg = { + .pin_bit_mask = BIT64(th->config.int_gpio_num), + .mode = GPIO_MODE_OUTPUT}; + if ((res = gpio_config(&cfg)) != ESP_OK) + { + log_e("Setting INT pin high failed"); + return res; + } + + gpio_set_level(th->config.int_gpio_num, 1); + vTaskDelay(pdMS_TO_TICKS(5)); + cfg.mode = GPIO_MODE_OUTPUT_OD; + if ((res = gpio_config(&cfg)) != ESP_OK) + { + log_e("Setting INT pin float failed"); + return res; + } + + return ESP_OK; +} + +esp_err_t gt911_read_data(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + esp_err_t res; + buffer_status_flags flags; + gt911_key_touch_data buffer; + + // Read only the XY register + if ((res = esp_lcd_panel_io_rx_param(th->io, GT911_BUFFER_STATUS_REG, &flags, sizeof(flags))) != ESP_OK) + { + log_e("Unable to read GT911_BUFFER_STATUS_REG"); + return res; + } + + if (flags.buffer_status) + { +#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0) + if (flags.have_key) + { + log_v("Buttons available"); + if ((res = esp_lcd_panel_io_rx_param(th->io, GT911_KEYS_REG, &buffer.data.keys, CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS)) != ESP_OK) + { + log_e("Unable to read GT911_KEYS_REG"); + return res; + } + + portENTER_CRITICAL(&th->data.lock); + th->data.buttons = CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS; + for (uint8_t i = 0; i < CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS; i++) + th->data.button[i].status = buffer.data.keys[i]; + + portEXIT_CRITICAL(&th->data.lock); + } +#endif + // Check if data is present + if (flags.number_points > 0) + { + log_v("Points available: %d", flags.number_points); + // Read the number of touch points + if (flags.number_points <= GT911_TOUCH_POINTS_MAX) + { + uint8_t points = flags.number_points > CONFIG_ESP_LCD_TOUCH_MAX_POINTS ? CONFIG_ESP_LCD_TOUCH_MAX_POINTS : flags.number_points; + // Read the points + if ((res = esp_lcd_panel_io_rx_param(th->io, GT911_TOUCH_POINTS_REG, &buffer.data.touch_points, points * sizeof(gt911_touch_event))) != ESP_OK) + { + log_e("Unable to read GT911_TOUCH_POINTS_REG"); + return res; + } + + portENTER_CRITICAL(&th->data.lock); + for (uint8_t i = 0; i < points; i++) + { + log_d("Point: #%d, event:%d, point:(%d,%d), area:%d", i, buffer.data.touch_points[i].event, buffer.data.touch_points[i].point.x, buffer.data.touch_points[i].point.y, buffer.data.touch_points[i].area); + th->data.coords[i].x = buffer.data.touch_points[i].point.x; + th->data.coords[i].y = buffer.data.touch_points[i].point.y; + th->data.coords[i].strength = buffer.data.touch_points[i].area; + } + + th->data.points = points; + portEXIT_CRITICAL(&th->data.lock); + } + } + } + + uint8_t clear[] = {0}; + if ((res = esp_lcd_panel_io_tx_param(th->io, GT911_BUFFER_STATUS_REG, clear, sizeof(clear))) != ESP_OK) + { + log_e("Unable to write GT911_BUFFER_STATUS_REG"); + return res; + } + + return ESP_OK; +} + +bool gt911_get_xy(esp_lcd_touch_handle_t th, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num) +{ + log_v("th:0x%08x, x:0x%08x, y:0x%08x, strength:0x%08x, point_num:0x%08x, max_point_num:%d", th, x, y, strength, point_num, max_point_num); + if (th == NULL || x == NULL || y == NULL || point_num == NULL) + return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&th->data.lock); + *point_num = th->data.points > max_point_num ? max_point_num : th->data.points; + for (uint8_t i = 0; i < *point_num; i++) + { + x[i] = th->data.coords[i].x; + y[i] = th->data.coords[i].y; + if (strength != NULL) + strength[i] = th->data.coords[i].strength; + + log_d("Touch data: x:%d, y:%d, strength:%d", x[i], y[i], th->data.coords[i].strength); + } + + th->data.points = 0; + portEXIT_CRITICAL(&th->data.lock); + + return *point_num > 0; +} + +#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0) +esp_err_t gt911_get_button_state(esp_lcd_touch_handle_t th, uint8_t n, uint8_t *state) +{ + log_v("th:0x%08x, n:%d, state:0x%08x", th, n, state); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + if (n > th->data.buttons) + { + log_e("Button out of range"); + return ESP_ERR_INVALID_ARG; + } + + portENTER_CRITICAL(&th->data.lock); + *state = th->data.button[n].status; + portEXIT_CRITICAL(&th->data.lock); + + return ESP_OK; +} +#endif + +esp_err_t gt911_del(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&th->data.lock); + // Remove gt911_info + if (th->config.user_data != NULL) + free(th->config.user_data); + + // Remove interrupts and reset INT + if (th->config.int_gpio_num != GPIO_NUM_NC) + { + if (th->config.interrupt_callback) + gpio_isr_handler_remove(th->config.int_gpio_num); + + gpio_reset_pin(th->config.int_gpio_num); + } + + // Reset RST + if (th->config.rst_gpio_num != GPIO_NUM_NC) + gpio_reset_pin(th->config.rst_gpio_num); + + free(th); + + return ESP_OK; +} + +esp_err_t esp_lcd_touch_new_i2c_gt911(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *handle) +{ + log_v("io:0x%08x, config:0x%08x, handle:0x%08x", io, config, handle); + if (io == NULL || config == NULL || handle == NULL) + return ESP_ERR_INVALID_ARG; + + if (config->int_gpio_num != GPIO_NUM_NC && !GPIO_IS_VALID_GPIO(config->int_gpio_num)) + { + log_e("Invalid GPIO INT pin: %d", config->int_gpio_num); + return ESP_ERR_INVALID_ARG; + } + + if (config->rst_gpio_num != GPIO_NUM_NC && !GPIO_IS_VALID_GPIO(config->rst_gpio_num)) + { + log_e("Invalid GPIO RST pin: %d", config->rst_gpio_num); + return ESP_ERR_INVALID_ARG; + } + + esp_err_t res; + const esp_lcd_touch_handle_t th = heap_caps_calloc(1, sizeof(esp_lcd_touch_t), MALLOC_CAP_DEFAULT); + if (th == NULL) + { + log_e("No memory available for esp_lcd_touch_t"); + return ESP_ERR_NO_MEM; + } + + th->io = io; + th->enter_sleep = gt911_enter_sleep; + th->exit_sleep = gt911_exit_sleep; + th->read_data = gt911_read_data; + th->get_xy = gt911_get_xy; +#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0) + th->get_button_state = gt911_get_button_state; +#endif + th->del = gt911_del; + memcpy(&th->config, config, sizeof(esp_lcd_touch_config_t)); + portMUX_INITIALIZE(&th->data.lock); + + if (config->int_gpio_num != GPIO_NUM_NC) + { + esp_rom_gpio_pad_select_gpio(config->int_gpio_num); + const gpio_config_t cfg = { + .pin_bit_mask = BIT64(config->int_gpio_num), + .mode = GPIO_MODE_INPUT, + // If the user has provided a callback routine for the interrupt enable the interrupt mode on the negative edge. + .intr_type = config->interrupt_callback ? GPIO_INTR_NEGEDGE : GPIO_INTR_DISABLE}; + if ((res = gpio_config(&cfg)) != ESP_OK) + { + free(th); + log_e("Configuring GPIO for INT failed"); + return res; + } + + if (config->interrupt_callback != NULL) + { + if ((res = esp_lcd_touch_register_interrupt_callback(th, config->interrupt_callback)) != ESP_OK) + { + gpio_reset_pin(th->config.int_gpio_num); + free(th); + log_e("Registering INT callback failed"); + return res; + } + } + + if (config->rst_gpio_num != GPIO_NUM_NC) + { + esp_rom_gpio_pad_select_gpio(config->rst_gpio_num); + const gpio_config_t cfg = { + .pin_bit_mask = BIT64(config->rst_gpio_num), + .mode = GPIO_MODE_OUTPUT}; + if ((res = gpio_config(&cfg)) != ESP_OK) + { + if (th->config.int_gpio_num != GPIO_NUM_NC) + { + if (config->interrupt_callback != NULL) + gpio_isr_handler_remove(th->config.int_gpio_num); + + gpio_reset_pin(th->config.int_gpio_num); + } + + free(th); + log_e("Configuring or setting GPIO for RST failed"); + return res; + } + } + } + + // Reset controller + if ((res = gt911_reset(th)) != ESP_OK) + { + log_e("GT911 reset failed"); + gt911_del(th); + return res; + } + + // Read type and resolution + if ((res = gt911_read_info(th)) != ESP_OK) + { + log_e("GT911 read info failed"); + gt911_del(th); + return res; + } + + log_d("handle:0x%08x", th); + *handle = th; + + return ESP_OK; +} + +#endif // TOUCH_GT911_I2C \ No newline at end of file diff --git a/src/esp_touch_xpt2046.c b/src/esp_touch_xpt2046.c new file mode 100644 index 0000000..27ff477 --- /dev/null +++ b/src/esp_touch_xpt2046.c @@ -0,0 +1,261 @@ +#ifdef TOUCH_XPT2046_SPI + +#include +#include +#include +#include + +// See datasheet XPT2046.pdf +const uint8_t XPT2046_START_Z1_CONVERSION = 0xB1; // S=1, ADDR=011, MODE=0 (12bits), SER/DFR=0, PD1=0, PD2=1 +const uint8_t XPT2046_START_Z2_CONVERSION = 0xC1; // S=1, ADDR=100, MODE=0 (12bits), SER/DFR=0, PD1=0, PD2=1 +const uint8_t XPT2046_START_Y_CONVERSION = 0x91; // S=1, ADDR=001, MODE=0 (12bits), SER/DFR=0, PD1=0, PD2=1 +const uint8_t XPT2046_START_X_CONVERSION = 0xD1; // S=1, ADDR=101, MODE=0 (12bits), SER/DFR=0, PD1=0, PD2=1 +const uint8_t XPT2046_START_BAT_CONVERSION = 0xA7; // S=1, ADDR=010, MODE=0 (12bits), SER/DFR=1, PD1=1, PD2=1 +const uint8_t XPT2046_START_Z1_POWER_DOWN = 0xB0; // S=1, ADDR=011, MODE=0 (12bits), SER/DFR=1, PD1=0, PD2=0 +// 12 bits ADC limit +const uint16_t XPT2046_ADC_LIMIT = (1 << 12); // 4096 + +esp_err_t xpt2046_read_register(esp_lcd_touch_handle_t th, uint8_t reg, uint16_t *value) +{ + uint8_t buf[2]; + esp_err_t res = esp_lcd_panel_io_rx_param(th->io, reg, buf, sizeof(buf)); + if (res != ESP_OK) + return res; + + *value = (buf[0] << 8) + buf[1]; + + return ESP_OK; +} + +esp_err_t xpt2046_enter_sleep(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + esp_err_t res; + uint16_t discard; + if ((res = xpt2046_read_register(th, XPT2046_START_Z1_POWER_DOWN, &discard)) != ESP_OK) + { + log_w("Could not read XPT2046_START_Z1_POWER_DOWN"); + return res; + } + + return ESP_OK; +} + +esp_err_t xpt2046_exit_sleep(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + esp_err_t res; + uint16_t discard; + if ((res = xpt2046_read_register(th, XPT2046_START_Z1_CONVERSION, &discard)) != ESP_OK) + { + log_w("Could not read XPT2046_START_Z1_CONVERSION"); + return res; + } + + return ESP_OK; +} + +esp_err_t xpt2046_read_data(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + esp_err_t res; + uint32_t x = 0, y = 0; + uint8_t points = 0; + + uint16_t z1, z2; + if (((res = xpt2046_read_register(th, XPT2046_START_Z1_CONVERSION, &z1)) != ESP_OK) || + ((res = xpt2046_read_register(th, XPT2046_START_Z2_CONVERSION, &z2)) != ESP_OK)) + { + log_w("Could not XPT2046_START_Z1_CONVERSION or XPT2046_START_Z2_CONVERSION"); + return res; + } + + // Convert to 12 bits Z value. + uint16_t z = (z1 >> 3) + (XPT2046_ADC_LIMIT - (z2 >> 3)); + // If the Z exceeds the Z threshold the user has pressed the screen + if (z >= XPT2046_Z_THRESHOLD) + { + uint16_t x_temp, y_temp; + // Discard first value as it is usually not reliable. + if ((res = xpt2046_read_register(th, XPT2046_START_X_CONVERSION, &x_temp)) != ESP_OK) + { + log_w("Could not read XPT2046_START_X_CONVERSION"); + return res; + } + + // CONFIG_ESP_LCD_TOUCH_MAX_POINTS is to average the points read and gives a better precision + for (uint8_t idx = 0; idx < CONFIG_ESP_LCD_TOUCH_MAX_POINTS; idx++) + { + // Read X and Y positions + if (((res = xpt2046_read_register(th, XPT2046_START_X_CONVERSION, &x_temp)) != ESP_OK) || + ((res = xpt2046_read_register(th, XPT2046_START_Y_CONVERSION, &y_temp)) != ESP_OK)) + { + log_w("Could not read XPT2046_START_X_CONVERSION or XPT2046_START_Y_CONVERSION"); + return res; + } + + // Add to accumulated raw ADC values + x += x_temp; + y += y_temp; + } + + // Convert X and Y to 12 bits by dropping upper 3 bits and average the accumulated coordinate data points. + x = ((x >> 3) * th->config.x_max) / XPT2046_ADC_LIMIT / CONFIG_ESP_LCD_TOUCH_MAX_POINTS; + y = ((y >> 3) * th->config.y_max) / XPT2046_ADC_LIMIT / CONFIG_ESP_LCD_TOUCH_MAX_POINTS; + points = 1; + } + + portENTER_CRITICAL(&th->data.lock); + th->data.coords[0].x = x; + th->data.coords[0].y = y; + th->data.coords[0].strength = z; + th->data.points = points; + portEXIT_CRITICAL(&th->data.lock); + + return ESP_OK; +} + +bool xpt2046_get_xy(esp_lcd_touch_handle_t th, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num) +{ + log_v("th:0x%08x, x:0x%08x, y:0x%08x, strength:0x%08x, point_num:0x%08x, max_point_num:%d", th, x, y, strength, point_num, max_point_num); + if (th == NULL || x == NULL || y == NULL || point_num == NULL) + return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&th->data.lock); + *point_num = th->data.points > max_point_num ? max_point_num : th->data.points; + for (uint8_t i = 0; i < *point_num; i++) + { + x[i] = th->data.coords[i].x; + y[i] = th->data.coords[i].y; + if (strength != NULL) + strength[i] = th->data.coords[i].strength; + + log_d("Touch data: x:%d, y:%d, area:%d", x[i], y[i], strength != NULL ? strength[i] : 0); + } + + th->data.points = 0; + portEXIT_CRITICAL(&th->data.lock); + + return *point_num > 0; +} + +esp_err_t xpt2046_del(esp_lcd_touch_handle_t th) +{ + log_v("th:0x%08x", th); + if (th == NULL) + return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&th->data.lock); + // Remove interrupts and reset INT + if (th->config.int_gpio_num != GPIO_NUM_NC) + { + if (th->config.interrupt_callback != NULL) + gpio_isr_handler_remove(th->config.int_gpio_num); + + gpio_reset_pin(th->config.int_gpio_num); + } + + free(th); + + return ESP_OK; +} + +esp_err_t esp_lcd_touch_new_spi_xpt2046(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *handle) +{ + log_v("io:0x%08x, config:0x%08x, handle:0x%08x", io, config, handle); + if (io == NULL || config == NULL || handle == NULL) + return ESP_ERR_INVALID_ARG; + + if (config->int_gpio_num != GPIO_NUM_NC && !GPIO_IS_VALID_GPIO(config->int_gpio_num)) + { + log_e("Invalid GPIO INT pin: %d", config->int_gpio_num); + return ESP_ERR_INVALID_ARG; + } + + esp_err_t res; + const esp_lcd_touch_handle_t th = heap_caps_calloc(1, sizeof(esp_lcd_touch_t), MALLOC_CAP_DEFAULT); + if (th == NULL) + { + log_e("No memory available for esp_lcd_touch_t"); + return ESP_ERR_NO_MEM; + } + + th->io = io; + th->enter_sleep = xpt2046_enter_sleep; + th->exit_sleep = xpt2046_exit_sleep; + th->read_data = xpt2046_read_data; + th->get_xy = xpt2046_get_xy; + th->del = xpt2046_del; + th->data.lock.owner = portMUX_FREE_VAL; + memcpy(&th->config, config, sizeof(esp_lcd_touch_config_t)); + + if (config->int_gpio_num != GPIO_NUM_NC) + { + esp_rom_gpio_pad_select_gpio(config->int_gpio_num); + const gpio_config_t cfg = { + .pin_bit_mask = BIT64(config->int_gpio_num), + .mode = GPIO_MODE_INPUT, + // If the user has provided a callback routine for the interrupt enable the interrupt mode on the negative edge. + .intr_type = config->interrupt_callback ? GPIO_INTR_NEGEDGE : GPIO_INTR_DISABLE}; + if ((res = gpio_config(&cfg)) != ESP_OK) + { + free(th); + log_e("Configuring GPIO for INT failed"); + return res; + } + + if (config->interrupt_callback != NULL) + { + if ((res = esp_lcd_touch_register_interrupt_callback(th, config->interrupt_callback)) != ESP_OK) + { + gpio_reset_pin(th->config.int_gpio_num); + free(th); + log_e("Registering INT callback failed"); + return res; + } + } + } + + if (config->rst_gpio_num != GPIO_NUM_NC) + log_w("RST pin defined but is not available on the XPT2046"); + + log_d("handle:0x%08x", th); + *handle = th; + + return ESP_OK; +} + +esp_err_t esp_lcd_touch_xpt2046_read_battery_level(const esp_lcd_touch_handle_t th, float *output) +{ + log_v("th:0x%08x, output:0x%08x", th, output); + + assert(th != NULL); + assert(output != NULL); + + esp_err_t res; + uint16_t level; + // Read Y position and convert returned data to 12bit value + if ((res = xpt2046_read_register(th, XPT2046_START_BAT_CONVERSION, &level)) != ESP_OK) + { + log_w("Could not read battery level"); + return res; + } + + // battery voltage is reported as 1/4 the actual voltage due to logic in the chip, then + // adjust for internal vref of 2.5v and finally + // adjust for ADC bit count + *output = level * 4 * 2.5f / XPT2046_ADC_LIMIT; + + return ESP_OK; +} + +#endif // TOUCH_XPT2046_SPI \ No newline at end of file diff --git a/src/lvgl_gt911_i2c.c b/src/lvgl_gt911_i2c.c deleted file mode 100644 index f2b6f1a..0000000 --- a/src/lvgl_gt911_i2c.c +++ /dev/null @@ -1,259 +0,0 @@ -#ifdef TOUCH_GT911_I2C - -#include -#include "driver/i2c.h" -#include "esp_lcd_touch.h" -#include "esp_lcd_touch_gt911.h" - -// The driver should take care of setting the config, but does not. - -struct __attribute__((packed)) GTInfo -{ - char productId[4]; // 0x8140 - 0x8143 - uint16_t fwId; // 0x8144 - 0x8145 - uint16_t xResolution; // 0x8146 - 0x8147 - uint16_t yResolution; // 0x8148 - 0x8149 - uint8_t vendorId; // 0x814A -}; - -struct __attribute__((packed)) GTPoint -{ - // 0x814F-0x8156, ... 0x8176 (5 points) - uint8_t trackId; - uint16_t x; - uint16_t y; - uint16_t area; - uint8_t reserved; -}; - -struct __attribute__((packed)) GTConfig -{ - uint8_t configVersion; // 0x8047 - uint16_t xResolution; // 0x8048 - 0x8049 - uint16_t yResolution; // 0x804A - 0x804B - uint8_t touchNumber; // 0x804C - uint8_t moduleSwitch1; // 0x804D - uint8_t moduleSwitch2; // 0x804E - uint8_t shakeCount; // 0x804F - uint8_t filter; // 0x8050 - uint8_t largeTouch; // 0x8051 - uint8_t noiseReduction; // 0x8052 - uint8_t screenTouchLevel; // 0x8053 - uint8_t screenLeaveLevel; // 0x8054 - uint8_t lowPowerControl; // 0x8055 - uint8_t refreshRate; // 0x8056 - uint8_t xThreshold; // 0x8057 - uint8_t yThreshold; // 0x8058 - uint8_t xSpeedLimit; // 0x8059 - reserved - uint8_t ySpeedLimit; // 0x805A - reserved - uint8_t vSpace; // 0x805B - uint8_t hSpace; // 0x805C - uint8_t miniFilter; // 0x805D - uint8_t stretchR0; // 0x805E - uint8_t stretchR1; // 0x805F - uint8_t stretchR2; // 0x8060 - uint8_t stretchRM; // 0x8061 - uint8_t drvGroupANum; // 0x8062 - uint8_t drvGroupBNum; // 0x8063 - uint8_t sensorNum; // 0x8064 - uint8_t freqAFactor; // 0x8065 - uint8_t freqBFactor; // 0x8066 - uint16_t pannelBitFreq; // 0x8067 - 0x8068 - uint16_t pannelSensorTime; // 0x8069 - 0x806A - uint8_t pannelTxGain; // 0x806B - uint8_t pannelRxGain; // 0x806C - uint8_t pannelDumpShift; // 0x806D - uint8_t drvFrameControl; // 0x806E - uint8_t chargingLevelUp; // 0x806F - uint8_t moduleSwitch3; // 0x8070 - uint8_t gestureDis; // 0x8071 - uint8_t gestureLongPressTime; // 0x8072 - uint8_t xySlopeAdjust; // 0x8073 - uint8_t gestureControl; // 0x8074 - uint8_t gestureSwitch1; // 0x8075 - uint8_t gestureSwitch2; // 0x8076 - uint8_t gestureRefreshRate; // 0x8077 - uint8_t gestureTouchLevel; // 0x8078 - uint8_t newGreenWakeUpLevel; // 0x8079 - uint8_t freqHoppingStart; // 0x807A - uint8_t freqHoppingEnd; // 0x807B - uint8_t noiseDetectTimes; // 0x807C - uint8_t hoppingFlag; // 0x807D - uint8_t hoppingThreshold; // 0x807E - uint8_t noiseThreshold; // 0x807F - uint8_t noiseMinThreshold; // 0x8080 - uint8_t NC_1; // 0x8081 - uint8_t hoppingSensorGroup; // 0x8082 - uint8_t hoppingSeg1Normalize; // 0x8083 - uint8_t hoppingSeg1Factor; // 0x8084 - uint8_t mainClockAjdust; // 0x8085 - uint8_t hoppingSeg2Normalize; // 0x8086 - uint8_t hoppingSeg2Factor; // 0x8087 - uint8_t NC_2; // 0x8088 - uint8_t hoppingSeg3Normalize; // 0x8089 - uint8_t hoppingSeg3Factor; // 0x808A - uint8_t NC_3; // 0x808B - uint8_t hoppingSeg4Normalize; // 0x808C - uint8_t hoppingSeg4Factor; // 0x808D - uint8_t NC_4; // 0x808E - uint8_t hoppingSeg5Normalize; // 0x808F - uint8_t hoppingSeg5Factor; // 0x8090 - uint8_t NC_5; // 0x8091 - uint8_t hoppingSeg6Normalize; // 0x8092 - uint8_t key[4]; // 0x8093 - 0x8096 - uint8_t keyArea; // 0x8097 - uint8_t keyTouchLevel; // 0x8098 - uint8_t keyLeaveLevel; // 0x8099 - uint8_t keySens[2]; // 0x809A - 0x809B - uint8_t keyRestrain; // 0x809C - uint8_t keyRestrainTime; // 0x809D - uint8_t gestureLargeTouch; // 0x809E - uint8_t NC_6[2]; // 0x809F - 0x80A0 - uint8_t hotknotNoiseMap; // 0x80A1 - uint8_t linkThreshold; // 0x80A2 - uint8_t pxyThreshold; // 0x80A3 - uint8_t gHotDumpShift; // 0x80A4 - uint8_t gHotRxGain; // 0x80A5 - uint8_t freqGain[4]; // 0x80A6 - 0x80A9 - uint8_t NC_7[9]; // 0x80AA - 0x80B2 - uint8_t combineDis; // 0x80B3 - uint8_t splitSet; // 0x80B4 - uint8_t NC_8[2]; // 0x80B5 - 0x80B6 - uint8_t sensorCH[14]; // 0x80B7 - 0x80C4 - uint8_t NC_9[16]; // 0x80C5 - 0x80D4 - uint8_t driverCH[26]; // 0x80D5 - 0x80EE - uint8_t NC_10[16]; // 0x80EF - 0x80FE -}; - -// Crucial: Initialize with defaults in case reading the GTInfo fails -struct GTInfo gt_info= { - .xResolution = GT911_TOUCH_CONFIG_X_MAX, - .yResolution = GT911_TOUCH_CONFIG_Y_MAX -}; - -static void gt911_lvgl_touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data) -{ - esp_lcd_touch_handle_t touch_handle = drv->user_data; - - uint16_t touch_x[1] = {0}; - uint16_t touch_y[1] = {0}; - uint16_t touch_strength[1] = {0}; - uint8_t touch_cnt = 0; - - // Read touch controller data - ESP_ERROR_CHECK(esp_lcd_touch_read_data(touch_handle)); - // Get coordinates - bool pressed = esp_lcd_touch_get_coordinates(touch_handle, touch_x, touch_y, touch_strength, &touch_cnt, 1); - if (pressed && touch_cnt > 0) - { - data->point.x = (touch_x[0] * GT911_TOUCH_CONFIG_X_MAX) / gt_info.xResolution; - data->point.y = (touch_y[0] * GT911_TOUCH_CONFIG_Y_MAX) / gt_info.yResolution; - data->state = LV_INDEV_STATE_PRESSED; - log_d("Pressed at: (%d,%d), strength: %d", data->point.x, data->point.y, touch_strength); - } - else - data->state = LV_INDEV_STATE_RELEASED; -} - -/* -uint8_t calculate_checksum_8(uint8_t *buffer, ushort length) -{ - uint8_t checksum = 0; - while (length--) - checksum += *buffer++; - - return ~checksum + 1; -} -*/ - -void lvgl_touch_init(lv_indev_drv_t *drv) -{ - log_d("lvgl_touch_init"); - // Create I2C bus - const i2c_config_t i2c_config = { - .mode = I2C_MODE_MASTER, - .sda_io_num = GT911_I2C_CONFIG_SDA_IO_NUM, - .scl_io_num = GT911_I2C_CONFIG_SCL_IO_NUM, - .sda_pullup_en = GT911_I2C_CONFIG_SDA_PULLUP_EN, - .scl_pullup_en = GT911_I2C_CONFIG_SCL_PULLUP_EN, - .master = { - .clk_speed = GT911_I2C_CONFIG_MASTER_CLK_SPEED}, - .clk_flags = GT911_I2C_CONFIG_CLK_FLAGS}; - ESP_ERROR_CHECK(i2c_param_config(GT911_I2C_HOST, &i2c_config)); - ESP_ERROR_CHECK(i2c_driver_install(GT911_I2C_HOST, i2c_config.mode, 0, 0, 0)); - - // Create IO handle - const esp_lcd_panel_io_i2c_config_t io_i2c_config = { - .dev_addr = GT911_IO_I2C_CONFIG_DEV_ADDR, - .control_phase_bytes = GT911_IO_I2C_CONFIG_CONTROL_PHASE_BYTES, - .user_ctx = drv, - .dc_bit_offset = GT911_IO_I2C_CONFIG_DC_BIT_OFFSET, - .lcd_cmd_bits = GT911_IO_I2C_CONFIG_LCD_CMD_BITS, - .lcd_param_bits = GT911_IO_I2C_CONFIG_LCD_PARAM_BITS, - .flags = { - .dc_low_on_data = GT911_IO_I2C_CONFIG_FLAGS_DC_LOW_ON_DATA, - .disable_control_phase = GT911_IO_I2C_CONFIG_FLAGS_DISABLE_CONTROL_PHASE}}; - - esp_lcd_panel_io_handle_t io_handle; - ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)GT911_I2C_HOST, &io_i2c_config, &io_handle)); - - // Read the information of the GT911 - if (esp_lcd_panel_io_rx_param(io_handle, 0x8140, >_info, sizeof(struct GTInfo)) == ESP_OK) - { - log_d("GT911 productId: %s", gt_info.productId); // 0x8140 - 0x8143 - log_d("GT911 fwId: %04x", gt_info.fwId); // 0x8144 - 0x8145 - log_d("GT911 xResolution/yResolution: (%d, %d)", gt_info.xResolution, gt_info.yResolution); // 0x8146 - 0x8147 // 0x8148 - 0x8149 - log_d("GT911 vendorId: %02x", gt_info.vendorId); // 0x814A - } - else - log_w("Unable to read GTInfo. Setting xResolution/yResolution to defaults: (%d, %d)", gt_info.xResolution, gt_info.yResolution); - /* - if (gt_info.xResolution != GT911_TOUCH_CONFIG_X_MAX || gt_info.yResolution != GT911_TOUCH_CONFIG_Y_MAX) - { - log_w("Resolution does not match configuration (%d,%d)", GT911_TOUCH_CONFIG_X_MAX, GT911_TOUCH_CONFIG_Y_MAX); - struct GTConfig gt_config; - log_i("GTConfig size: %d", sizeof(struct GTConfig)); - ESP_ERROR_CHECK(esp_lcd_panel_io_rx_param(io_handle, 0x8047, >_config, sizeof(struct GTConfig))); - log_i("GTConfig version: %d", gt_config.configVersion); - uint8_t checksum; - ESP_ERROR_CHECK(esp_lcd_panel_io_rx_param(io_handle, 0x80FF, &checksum, sizeof(uint8_t))); - if (checksum == calculate_checksum_8((uint8_t *)>_config, sizeof(struct GTConfig))) - { - log_i("Checksum OK. Updating configuration in GT911"); - gt_config.xResolution = GT911_TOUCH_CONFIG_X_MAX; - gt_config.yResolution = GT911_TOUCH_CONFIG_Y_MAX; - // Write new configuration - ESP_ERROR_CHECK(esp_lcd_panel_io_tx_param(io_handle, 0x8047, >_config, sizeof(struct GTConfig))); - checksum = calculate_checksum_8((uint8_t *)>_config, sizeof(struct GTConfig)); - // Write checksum and set the "config_fresh" bit to 1 - uint8_t checksum_buffer[2] = {checksum, 1}; - ESP_ERROR_CHECK(esp_lcd_panel_io_tx_param(io_handle, 0x80FF, &checksum_buffer, sizeof(checksum_buffer))); - log_w("Configuration in GT911 updated"); - } - else - log_e("Checksum failed. Will not touch configuration in GT911"); - } - */ - - // Create touch configuration - const esp_lcd_touch_config_t touch_config = { - .x_max = GT911_TOUCH_CONFIG_X_MAX, - .y_max = GT911_TOUCH_CONFIG_Y_MAX, - .rst_gpio_num = GT911_TOUCH_CONFIG_RST_GPIO_NUM, - .int_gpio_num = GT911_TOUCH_CONFIG_INT_GPIO_NUM, - .levels = { - .reset = GT911_TOUCH_CONFIG_LEVELS_RESET, - .interrupt = GT911_TOUCH_CONFIG_LEVELS_INTERRUPT}, - // Unfortunately not supported - //.flags = {.swap_xy = GT911_TOUCH_CONFIG_FLAGS_SWAP_XY, .mirror_x = GT911_TOUCH_CONFIG_FLAGS_MIRROR_X, .mirror_y = GT911_TOUCH_CONFIG_FLAGS_MIRROR_Y}, - .user_data = io_handle}; - - esp_lcd_touch_handle_t touch_handle; - ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(io_handle, &touch_config, &touch_handle)); - - drv->type = LV_INDEV_TYPE_POINTER; - drv->user_data = touch_handle; - drv->read_cb = gt911_lvgl_touch_cb; -} - -#endif \ No newline at end of file diff --git a/src/lvgl_gc9a01_spi.c b/src/lvgl_panel_gc9a01_spi.c similarity index 58% rename from src/lvgl_gc9a01_spi.c rename to src/lvgl_panel_gc9a01_spi.c index 6fb21ea..1c37fa6 100644 --- a/src/lvgl_gc9a01_spi.c +++ b/src/lvgl_panel_gc9a01_spi.c @@ -1,22 +1,24 @@ #ifdef LCD_GC9A01_SPI #include +#include #include #include -#include #include -#include "esp_lcd_gc9a01.h" - -bool gc9a01_color_trans_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) +bool gc9a01_color_trans_done(esp_lcd_panel_io_handle_t panel_io_handle, esp_lcd_panel_io_event_data_t *panel_io_event_data, void *user_ctx) { + log_v("panel_io_handle:0x%08x, panel_io_event_data:%0x%08x, user_ctx:0x%08x", panel_io_handle, panel_io_event_data, user_ctx); + lv_disp_drv_t *disp_driver = user_ctx; lv_disp_flush_ready(disp_driver); return false; } -void gc9a01_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color16_t *color_map) +void gc9a01_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) { + log_v("drv:0x%08x, area:%0x%08x, color_map:0x%08x", drv, area, color_map); + esp_lcd_panel_handle_t panel_handle = drv->user_data; #if LV_COLOR_16_SWAP != 1 #warning "LV_COLOR_16_SWAP should be 1 for max performance" @@ -30,7 +32,8 @@ void gc9a01_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color16_t *co void lvgl_lcd_init(lv_disp_drv_t *drv) { - log_d("lvgl_lcd_init"); + log_v("disp_drv:0x%08x", drv); + // Hardware rotation is supported drv->sw_rotate = 0; drv->rotated = LV_DISP_ROT_NONE; @@ -45,10 +48,11 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .max_transfer_sz = GC9A01_SPI_BUS_MAX_TRANSFER_SZ, .flags = GC9A01_SPI_BUS_FLAGS, .intr_flags = GC9A01_SPI_BUS_INTR_FLAGS}; + log_d("spi_bus_config: mosi_io_num:%d, miso_io_num:%d, sclk_io_num:%d, quadwp_io_num:%d, quadhd_io_num:%d, max_transfer_sz:%d, flags:0x%08x, intr_flags:0x%04x", spi_bus_config.mosi_io_num, spi_bus_config.miso_io_num, spi_bus_config.sclk_io_num, spi_bus_config.quadwp_io_num, spi_bus_config.quadhd_io_num, spi_bus_config.max_transfer_sz, spi_bus_config.flags, spi_bus_config.intr_flags); ESP_ERROR_CHECK_WITHOUT_ABORT(spi_bus_initialize(GC9A01_SPI_HOST, &spi_bus_config, GC9A01_SPI_DMA_CHANNEL)); // Attach the LCD controller to the SPI bus - const esp_lcd_panel_io_spi_config_t io_config = { + const esp_lcd_panel_io_spi_config_t io_spi_config = { .cs_gpio_num = GC9A01_SPI_CONFIG_CS_GPIO_NUM, .dc_gpio_num = GC9A01_SPI_CONFIG_DC_GPIO_NUM, .spi_mode = GC9A01_SPI_CONFIG_SPI_MODE, @@ -63,8 +67,9 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .dc_low_on_data = GC9A01_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA, .octal_mode = GC9A01_SPI_CONFIG_FLAGS_OCTAL_MODE, .lsb_first = GC9A01_SPI_CONFIG_FLAGS_LSB_FIRST}}; + log_d("io_spi_config: cs_gpio_num:%d, dc_gpio_num:%d, spi_mode:%d, pclk_hz:%d, trans_queue_depth:%d, user_ctx:0x%08x, on_color_trans_done:0x%08x, lcd_cmd_bits:%d, lcd_param_bits:%d, flags:{dc_as_cmd_phase:%d, dc_low_on_data:%d, octal_mode:%d, lsb_first:%d}", io_spi_config.cs_gpio_num, io_spi_config.dc_gpio_num, io_spi_config.spi_mode, io_spi_config.pclk_hz, io_spi_config.trans_queue_depth, io_spi_config.user_ctx, io_spi_config.on_color_trans_done, io_spi_config.lcd_cmd_bits, io_spi_config.lcd_param_bits, io_spi_config.flags.dc_as_cmd_phase, io_spi_config.flags.dc_low_on_data, io_spi_config.flags.octal_mode, io_spi_config.flags.lsb_first); esp_lcd_panel_io_handle_t io_handle; - ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)GC9A01_SPI_HOST, &io_config, &io_handle)); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)GC9A01_SPI_HOST, &io_spi_config, &io_handle)); // Create gc9a01 panel handle const esp_lcd_panel_dev_config_t panel_dev_config = { @@ -74,13 +79,24 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .flags = { .reset_active_high = GC9A01_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH}, .vendor_config = GC9A01_DEV_CONFIG_VENDOR_CONFIG}; + log_d("panel_dev_config: reset_gpio_num:%d, color_space:%d, bits_per_pixel:%d, flags:{reset_active_high:%d}, vendor_config: 0x%08x", panel_dev_config.reset_gpio_num, panel_dev_config.color_space, panel_dev_config.bits_per_pixel, panel_dev_config.flags.reset_active_high, panel_dev_config.vendor_config); esp_lcd_panel_handle_t panel_handle; ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(io_handle, &panel_dev_config, &panel_handle)); - ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); - // Colors are inverted +#ifdef LCD_IPS + // If LCD is IPS invert the colors ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true)); +#endif +#ifdef LCD_SWAP_XY + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, LCD_SWAP_XY)); +#endif +#if defined(LCD_MIRROR_X) || defined(LCD_MIRROR_Y) + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, LCD_MIRROR_X, LCD_MIRROR_Y)); +#endif +#if defined(LCD_GAP_X) || defined(LCD_GAP_Y) + ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, LCD_GAP_X, LCD_GAP_Y)); +#endif // Turn display on ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); diff --git a/src/lvgl_ili9341_spi.c b/src/lvgl_panel_ili9341_spi.c similarity index 62% rename from src/lvgl_ili9341_spi.c rename to src/lvgl_panel_ili9341_spi.c index 37c94a5..2a145b0 100644 --- a/src/lvgl_ili9341_spi.c +++ b/src/lvgl_panel_ili9341_spi.c @@ -1,13 +1,11 @@ #ifdef LCD_ILI9341_SPI #include +#include #include #include -#include #include -#include "esp_lcd_ili9341.h" - static bool ili9341_color_trans_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) { lv_disp_drv_t *disp_driver = user_ctx; @@ -15,7 +13,7 @@ static bool ili9341_color_trans_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd return false; } -static void ili9341_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color16_t *color_map) +static void ili9341_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) { esp_lcd_panel_handle_t panel_handle = drv->user_data; #if LV_COLOR_16_SWAP != 1 @@ -30,7 +28,8 @@ static void ili9341_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color void lvgl_lcd_init(lv_disp_drv_t *drv) { - log_d("lvgl_lcd_init"); + log_v("drv:0x%08x"); + // Hardware rotation is supported drv->sw_rotate = 0; drv->rotated = LV_DISP_ROT_NONE; @@ -45,10 +44,11 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .max_transfer_sz = ILI9341_SPI_BUS_MAX_TRANSFER_SZ, .flags = ILI9341_SPI_BUS_FLAGS, .intr_flags = ILI9341_SPI_BUS_INTR_FLAGS}; + log_d("spi_bus_config: mosi_io_num:%d, miso_io_num:%d, sclk_io_num:%d, quadwp_io_num:%d, quadhd_io_num:%d, max_transfer_sz:%d, flags:0x%08x, intr_flags:0x%04x", spi_bus_config.mosi_io_num, spi_bus_config.miso_io_num, spi_bus_config.sclk_io_num, spi_bus_config.quadwp_io_num, spi_bus_config.quadhd_io_num, spi_bus_config.max_transfer_sz, spi_bus_config.flags, spi_bus_config.intr_flags); ESP_ERROR_CHECK_WITHOUT_ABORT(spi_bus_initialize(ILI9341_SPI_HOST, &spi_bus_config, ILI9341_SPI_DMA_CHANNEL)); // Attach the LCD controller to the SPI bus - const esp_lcd_panel_io_spi_config_t io_config = { + const esp_lcd_panel_io_spi_config_t io_spi_config = { .cs_gpio_num = ILI9341_SPI_CONFIG_CS_GPIO_NUM, .dc_gpio_num = ILI9341_SPI_CONFIG_DC_GPIO_NUM, .spi_mode = ILI9341_SPI_CONFIG_SPI_MODE, @@ -63,8 +63,9 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .dc_low_on_data = ILI9341_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA, .octal_mode = ILI9341_SPI_CONFIG_FLAGS_OCTAL_MODE, .lsb_first = ILI9341_SPI_CONFIG_FLAGS_LSB_FIRST}}; + log_d("io_spi_config: cs_gpio_num:%d, dc_gpio_num:%d, spi_mode:%d, pclk_hz:%d, trans_queue_depth:%d, user_ctx:0x%08x, on_color_trans_done:0x%08x, lcd_cmd_bits:%d, lcd_param_bits:%d, flags:{dc_as_cmd_phase:%d, dc_low_on_data:%d, octal_mode:%d, lsb_first:%d}", io_spi_config.cs_gpio_num, io_spi_config.dc_gpio_num, io_spi_config.spi_mode, io_spi_config.pclk_hz, io_spi_config.trans_queue_depth, io_spi_config.user_ctx, io_spi_config.on_color_trans_done, io_spi_config.lcd_cmd_bits, io_spi_config.lcd_param_bits, io_spi_config.flags.dc_as_cmd_phase, io_spi_config.flags.dc_low_on_data, io_spi_config.flags.octal_mode, io_spi_config.flags.lsb_first); esp_lcd_panel_io_handle_t io_handle; - ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)ILI9341_SPI_HOST, &io_config, &io_handle)); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)ILI9341_SPI_HOST, &io_spi_config, &io_handle)); // Create ili9341 panel handle const esp_lcd_panel_dev_config_t panel_dev_config = { @@ -74,13 +75,24 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .flags = { .reset_active_high = ILI9341_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH}, .vendor_config = ILI9341_DEV_CONFIG_VENDOR_CONFIG}; - if (panel_dev_config.vendor_config) - log_d("Initialization with vendor config"); - + log_d("panel_dev_config: reset_gpio_num:%d, color_space:%d, bits_per_pixel:%d, flags:{reset_active_high:%d}, vendor_config:0x%08x", panel_dev_config.reset_gpio_num, panel_dev_config.color_space, panel_dev_config.bits_per_pixel, panel_dev_config.flags.reset_active_high, panel_dev_config.vendor_config); esp_lcd_panel_handle_t panel_handle; ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(io_handle, &panel_dev_config, &panel_handle)); ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); +#ifdef LCD_IPS + // If LCD is IPS invert the colors + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true)); +#endif +#ifdef LCD_SWAP_XY + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, LCD_SWAP_XY)); +#endif +#if defined(LCD_MIRROR_X) || defined(LCD_MIRROR_Y) + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, LCD_MIRROR_X, LCD_MIRROR_Y)); +#endif +#if defined(LCD_GAP_X) || defined(LCD_GAP_Y) + ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, LCD_GAP_X, LCD_GAP_Y)); +#endif // Turn display on ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); diff --git a/src/lvgl_st7262_par.c b/src/lvgl_panel_st7262_par.c similarity index 51% rename from src/lvgl_st7262_par.c rename to src/lvgl_panel_st7262_par.c index 7d6a6da..ef71468 100644 --- a/src/lvgl_st7262_par.c +++ b/src/lvgl_panel_st7262_par.c @@ -4,29 +4,30 @@ #include #include -static bool direct_io_frame_trans_done(esp_lcd_panel_handle_t panel, esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) +bool direct_io_frame_trans_done(esp_lcd_panel_handle_t panel, esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) { lv_disp_drv_t *disp_driver = user_ctx; lv_disp_flush_ready(disp_driver); return false; } -static void direct_io_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color16_t *color_map) +void direct_io_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) { - esp_lcd_panel_handle_t panel_handle = drv->user_data; + const esp_lcd_panel_handle_t panel_handle = drv->user_data; // LV_COLOR_16_SWAP is handled by mapping of the data ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_map)); }; void lvgl_lcd_init(lv_disp_drv_t *drv) { - log_d("lvgl_lcd_init"); + log_v("drv:0x%08x"); + // Hardware rotation is NOT supported drv->sw_rotate = 1; drv->rotated = LV_DISP_ROT_NONE; // Create direct_io panel handle - const esp_lcd_rgb_panel_config_t tft_panel_config = { + const esp_lcd_rgb_panel_config_t rgb_panel_config = { .clk_src = ST7262_PANEL_CONFIG_CLK_SRC, .timings = { .pclk_hz = ST7262_PANEL_CONFIG_TIMINGS_PCLK_HZ, @@ -61,11 +62,26 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .user_ctx = drv, .flags = {.disp_active_low = ST7262_PANEL_CONFIG_FLAGS_DISP_ACTIVE_LOW, .relax_on_idle = ST7262_PANEL_CONFIG_FLAGS_RELAX_ON_IDLE, .fb_in_psram = ST7262_PANEL_CONFIG_FLAGS_FB_IN_PSRAM} }; - + log_d("rgb_panel_config: clk_src:%d, timings:{pclk_hz:%d, h_res:%d, v_res:%d, hsync_pulse_width:%d, hsync_back_porch:%d, hsync_front_porch:%d, vsync_pulse_width:%d, vsync_back_porch:%d, vsync_front_porch:%d, flags:{hsync_idle_low:%d, vsync_idle_low:%d, de_idle_high:%d, pclk_active_neg:%d, pclk_idle_high:%d}}, data_width:%d, sram_trans_align:%d, psram_trans_align:%d, hsync_gpio_num:%d, vsync_gpio_num:%d, de_gpio_num:%d, pclk_gpio_num:%d, data_gpio_nums:[%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,], disp_gpio_num:%d, on_frame_trans_done:0x%08x, user_ctx:0x%08x, flags:{disp_active_low:%d, relax_on_idle:%d, fb_in_psram:%d}", rgb_panel_config.clk_src, rgb_panel_config.timings.pclk_hz, rgb_panel_config.timings.h_res, rgb_panel_config.timings.v_res, rgb_panel_config.timings.hsync_pulse_width, rgb_panel_config.timings.hsync_back_porch, rgb_panel_config.timings.hsync_front_porch, rgb_panel_config.timings.vsync_pulse_width, rgb_panel_config.timings.vsync_back_porch, rgb_panel_config.timings.vsync_front_porch, rgb_panel_config.timings.flags.hsync_idle_low, rgb_panel_config.timings.flags.vsync_idle_low, rgb_panel_config.timings.flags.de_idle_high, rgb_panel_config.timings.flags.pclk_active_neg, rgb_panel_config.timings.flags.pclk_idle_high, rgb_panel_config.data_width, rgb_panel_config.sram_trans_align, rgb_panel_config.psram_trans_align, rgb_panel_config.hsync_gpio_num, rgb_panel_config.vsync_gpio_num, rgb_panel_config.de_gpio_num, rgb_panel_config.pclk_gpio_num, rgb_panel_config.data_gpio_nums[0], rgb_panel_config.data_gpio_nums[1], rgb_panel_config.data_gpio_nums[2], rgb_panel_config.data_gpio_nums[3], rgb_panel_config.data_gpio_nums[4], rgb_panel_config.data_gpio_nums[5], rgb_panel_config.data_gpio_nums[6], rgb_panel_config.data_gpio_nums[7], rgb_panel_config.data_gpio_nums[8], rgb_panel_config.data_gpio_nums[9], rgb_panel_config.data_gpio_nums[10], rgb_panel_config.data_gpio_nums[11], rgb_panel_config.data_gpio_nums[12], rgb_panel_config.data_gpio_nums[13], rgb_panel_config.data_gpio_nums[14], rgb_panel_config.data_gpio_nums[15], rgb_panel_config.disp_gpio_num, rgb_panel_config.on_frame_trans_done, rgb_panel_config.user_ctx, rgb_panel_config.flags.disp_active_low, rgb_panel_config.flags.relax_on_idle, rgb_panel_config.flags.fb_in_psram); + log_d("refresh rate: %d Hz", (ST7262_PANEL_CONFIG_TIMINGS_PCLK_HZ * ST7262_PANEL_CONFIG_DATA_WIDTH) / (ST7262_PANEL_CONFIG_TIMINGS_H_RES + ST7262_PANEL_CONFIG_TIMINGS_HSYNC_PULSE_WIDTH + ST7262_PANEL_CONFIG_TIMINGS_HSYNC_BACK_PORCH + ST7262_PANEL_CONFIG_TIMINGS_HSYNC_FRONT_PORCH) / (ST7262_PANEL_CONFIG_TIMINGS_V_RES + ST7262_PANEL_CONFIG_TIMINGS_VSYNC_PULSE_WIDTH + ST7262_PANEL_CONFIG_TIMINGS_VSYNC_BACK_PORCH + ST7262_PANEL_CONFIG_TIMINGS_VSYNC_FRONT_PORCH) / SOC_LCD_RGB_DATA_WIDTH); esp_lcd_panel_handle_t panel_handle; - ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&tft_panel_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&rgb_panel_config, &panel_handle)); ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); +#ifdef LCD_IPS + // If LCD is IPS invert the colors + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true)); +#endif +#ifdef LCD_SWAP_XY + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, LCD_SWAP_XY)); +#endif +#if defined(LCD_MIRROR_X) || defined(LCD_MIRROR_Y) + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, LCD_MIRROR_X, LCD_MIRROR_Y)); +#endif +#if defined(LCD_GAP_X) || defined(LCD_GAP_Y) + ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, LCD_GAP_X, LCD_GAP_Y)); +#endif + drv->user_data = panel_handle; drv->flush_cb = direct_io_lv_flush; } diff --git a/src/lvgl_panel_st7701_par.c b/src/lvgl_panel_st7701_par.c new file mode 100644 index 0000000..8c33a08 --- /dev/null +++ b/src/lvgl_panel_st7701_par.c @@ -0,0 +1,117 @@ +#ifdef LCD_ST7701_PAR + +#include +#include +#include +#include +#include + +bool direct_io_frame_trans_done(esp_lcd_panel_handle_t panel, esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) +{ + lv_disp_drv_t *disp_driver = user_ctx; + lv_disp_flush_ready(disp_driver); + return false; +} + +void direct_io_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + const esp_lcd_panel_handle_t panel_handle = drv->user_data; + // LV_COLOR_16_SWAP is handled by mapping of the data + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_map)); +}; + +void lvgl_lcd_init(lv_disp_drv_t *drv) +{ + log_v("drv:0x%08x"); + + // Hardware rotation is NOT supported + drv->sw_rotate = 1; + drv->rotated = LV_DISP_ROT_NONE; + + // Install 3-wire SPI panel IO + esp_lcd_panel_io_3wire_spi_config_t io_3wire_spi_config = { + .line_config = { + .cs_io_type = IO_TYPE_GPIO, + .cs_gpio_num = ST7701_IO_3WIRE_SPI_LINE_CONFIG_CS_GPIO_NUM, + .scl_io_type = IO_TYPE_GPIO, + .scl_gpio_num = ST7701_IO_3WIRE_SPI_LINE_CONFIG_SCL_GPIO_NUM, + .sda_io_type = IO_TYPE_GPIO, + .sda_gpio_num = ST7701_IO_3WIRE_SPI_LINE_CONFIG_SDA_GPIO_NUM}, + .expect_clk_speed = ST7701_IO_3WIRE_SPI_EXPECT_CLK_SPEED, + .spi_mode = ST7701_IO_3WIRE_SPI_SPI_MODE, + .lcd_cmd_bytes = ST7701_IO_3WIRE_SPI_LCD_CMD_BYTES, + .lcd_param_bytes = ST7701_IO_3WIRE_SPI_LCD_PARAM_BYTES, + .flags = {.use_dc_bit = ST7701_IO_3WIRE_SPI_FLAGS_USE_DC_BIT, .dc_zero_on_data = ST7701_IO_3WIRE_SPI_FLAGS_DC_ZERO_ON_DATA, .lsb_first = ST7701_IO_3WIRE_SPI_FLAGS_LSB_FIRST, .cs_high_active = ST7701_IO_3WIRE_SPI_FLAGS_CS_HIGH_ACTIVE, .del_keep_cs_inactive = ST7701_IO_3WIRE_SPI_FLAGS_DEL_KEEP_CS_INACTIVE}}; + log_d("io_3wire_spi_config: line_config:{cs_io_type:%d, cs_gpio_num:%d, scl_io_type:%d, scl_gpio_num:%d, sda_io_type:%d, sda_gpio_num:%d}, expect_clk_speed:%d, spi_mode:%d, lcd_cmd_bytes:%d, lcd_param_bytes:%d, flags:{use_dc_bit:%d, dc_zero_on_data:%d, lsb_first:%d, cs_high_active:%d, del_keep_cs_inactive:%d}", io_3wire_spi_config.line_config.cs_io_type, io_3wire_spi_config.line_config.cs_gpio_num, io_3wire_spi_config.line_config.scl_io_type, io_3wire_spi_config.line_config.scl_gpio_num, io_3wire_spi_config.line_config.sda_io_type, io_3wire_spi_config.line_config.sda_gpio_num, io_3wire_spi_config.expect_clk_speed, io_3wire_spi_config.spi_mode, io_3wire_spi_config.lcd_cmd_bytes, io_3wire_spi_config.lcd_param_bytes, io_3wire_spi_config.flags.use_dc_bit, io_3wire_spi_config.flags.dc_zero_on_data, io_3wire_spi_config.flags.lsb_first, io_3wire_spi_config.flags.cs_high_active, io_3wire_spi_config.flags.del_keep_cs_inactive); + esp_lcd_panel_io_handle_t io_handle; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_3wire_spi(&io_3wire_spi_config, &io_handle)); + + // Create direct_io panel handle + const esp_lcd_rgb_panel_config_t rgb_panel_config = { + .clk_src = ST7701_PANEL_CONFIG_CLK_SRC, + .timings = { + .pclk_hz = ST7701_PANEL_CONFIG_TIMINGS_PCLK_HZ, + .h_res = ST7701_PANEL_CONFIG_TIMINGS_H_RES, + .v_res = ST7701_PANEL_CONFIG_TIMINGS_V_RES, + .hsync_pulse_width = ST7701_PANEL_CONFIG_TIMINGS_HSYNC_PULSE_WIDTH, + .hsync_back_porch = ST7701_PANEL_CONFIG_TIMINGS_HSYNC_BACK_PORCH, + .hsync_front_porch = ST7701_PANEL_CONFIG_TIMINGS_HSYNC_FRONT_PORCH, + .vsync_pulse_width = ST7701_PANEL_CONFIG_TIMINGS_VSYNC_PULSE_WIDTH, + .vsync_back_porch = ST7701_PANEL_CONFIG_TIMINGS_VSYNC_BACK_PORCH, + .vsync_front_porch = ST7701_PANEL_CONFIG_TIMINGS_VSYNC_FRONT_PORCH, + .flags = { + .hsync_idle_low = ST7701_PANEL_CONFIG_TIMINGS_FLAGS_HSYNC_IDLE_LOW, + .vsync_idle_low = ST7701_PANEL_CONFIG_TIMINGS_FLAGS_VSYNC_IDLE_LOW, + .de_idle_high = ST7701_PANEL_CONFIG_TIMINGS_FLAGS_DE_IDLE_HIGH, + .pclk_active_neg = ST7701_PANEL_CONFIG_TIMINGS_FLAGS_PCLK_ACTIVE_NEG, + .pclk_idle_high = ST7701_PANEL_CONFIG_TIMINGS_FLAGS_PCLK_IDLE_HIGH}}, + .data_width = ST7701_PANEL_CONFIG_DATA_WIDTH, + .sram_trans_align = ST7701_PANEL_CONFIG_SRAM_TRANS_ALIGN, + .psram_trans_align = ST7701_PANEL_CONFIG_PSRAM_TRANS_ALIGN, + .hsync_gpio_num = ST7701_PANEL_CONFIG_HSYNC_GPIO_NUM, + .vsync_gpio_num = ST7701_PANEL_CONFIG_VSYNC_GPIO_NUM, + .de_gpio_num = ST7701_PANEL_CONFIG_DE_GPIO_NUM, + .pclk_gpio_num = ST7701_PANEL_CONFIG_PCLK_GPIO_NUM, +#if LV_COLOR_16_SWAP == 0 + .data_gpio_nums = {ST7701_PANEL_CONFIG_DATA_GPIO_R0, ST7701_PANEL_CONFIG_DATA_GPIO_R1, ST7701_PANEL_CONFIG_DATA_GPIO_R2, ST7701_PANEL_CONFIG_DATA_GPIO_R3, ST7701_PANEL_CONFIG_DATA_GPIO_R4, ST7701_PANEL_CONFIG_DATA_GPIO_G0, ST7701_PANEL_CONFIG_DATA_GPIO_G1, ST7701_PANEL_CONFIG_DATA_GPIO_G2, ST7701_PANEL_CONFIG_DATA_GPIO_G3, ST7701_PANEL_CONFIG_DATA_GPIO_G4, ST7701_PANEL_CONFIG_DATA_GPIO_G5, ST7701_PANEL_CONFIG_DATA_GPIO_B0, ST7701_PANEL_CONFIG_DATA_GPIO_B1, ST7701_PANEL_CONFIG_DATA_GPIO_B2, ST7701_PANEL_CONFIG_DATA_GPIO_B3, ST7701_PANEL_CONFIG_DATA_GPIO_B4}, +#else + .data_gpio_nums = {ST7701_PANEL_CONFIG_DATA_GPIO_G3, ST7701_PANEL_CONFIG_DATA_GPIO_G4, ST7701_PANEL_CONFIG_DATA_GPIO_G5, ST7701_PANEL_CONFIG_DATA_GPIO_B0, ST7701_PANEL_CONFIG_DATA_GPIO_B1, ST7701_PANEL_CONFIG_DATA_GPIO_B2, ST7701_PANEL_CONFIG_DATA_GPIO_B3, ST7701_PANEL_CONFIG_DATA_GPIO_B4, ST7701_PANEL_CONFIG_DATA_GPIO_R0, ST7701_PANEL_CONFIG_DATA_GPIO_R1, ST7701_PANEL_CONFIG_DATA_GPIO_R2, ST7701_PANEL_CONFIG_DATA_GPIO_R3, ST7701_PANEL_CONFIG_DATA_GPIO_R4, ST7701_PANEL_CONFIG_DATA_GPIO_G0, ST7701_PANEL_CONFIG_DATA_GPIO_G1, ST7701_PANEL_CONFIG_DATA_GPIO_G2}, +#endif + .disp_gpio_num = ST7701_PANEL_CONFIG_DISP_GPIO_NUM, + .on_frame_trans_done = direct_io_frame_trans_done, + .user_ctx = drv, + .flags = {.disp_active_low = ST7701_PANEL_CONFIG_FLAGS_DISP_ACTIVE_LOW, .relax_on_idle = ST7701_PANEL_CONFIG_FLAGS_RELAX_ON_IDLE, .fb_in_psram = ST7701_PANEL_CONFIG_FLAGS_FB_IN_PSRAM} + }; + log_d("rgb_panel_config: clk_src:%d, timings:{pclk_hz:%d, h_res:%d, v_res:%d, hsync_pulse_width:%d, hsync_back_porch:%d, hsync_front_porch:%d, vsync_pulse_width:%d, vsync_back_porch:%d, vsync_front_porch:%d, flags:{hsync_idle_low:%d, vsync_idle_low:%d, de_idle_high:%d, pclk_active_neg:%d, pclk_idle_high:%d}}, data_width:%d, sram_trans_align:%d, psram_trans_align:%d, hsync_gpio_num:%d, vsync_gpio_num:%d, de_gpio_num:%d, pclk_gpio_num:%d, data_gpio_nums:[%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d], disp_gpio_num:%d, on_frame_trans_done:0x%08x, user_ctx:0x%08x, flags:{disp_active_low:%d, relax_on_idle:%d, fb_in_psram:%d}", rgb_panel_config.clk_src, rgb_panel_config.timings.pclk_hz, rgb_panel_config.timings.h_res, rgb_panel_config.timings.v_res, rgb_panel_config.timings.hsync_pulse_width, rgb_panel_config.timings.hsync_back_porch, rgb_panel_config.timings.hsync_front_porch, rgb_panel_config.timings.vsync_pulse_width, rgb_panel_config.timings.vsync_back_porch, rgb_panel_config.timings.vsync_front_porch, rgb_panel_config.timings.flags.hsync_idle_low, rgb_panel_config.timings.flags.vsync_idle_low, rgb_panel_config.timings.flags.de_idle_high, rgb_panel_config.timings.flags.pclk_active_neg, rgb_panel_config.timings.flags.pclk_idle_high, rgb_panel_config.data_width, rgb_panel_config.sram_trans_align, rgb_panel_config.psram_trans_align, rgb_panel_config.hsync_gpio_num, rgb_panel_config.vsync_gpio_num, rgb_panel_config.de_gpio_num, rgb_panel_config.pclk_gpio_num, rgb_panel_config.data_gpio_nums[0], rgb_panel_config.data_gpio_nums[1], rgb_panel_config.data_gpio_nums[2], rgb_panel_config.data_gpio_nums[3], rgb_panel_config.data_gpio_nums[4], rgb_panel_config.data_gpio_nums[5], rgb_panel_config.data_gpio_nums[6], rgb_panel_config.data_gpio_nums[7], rgb_panel_config.data_gpio_nums[8], rgb_panel_config.data_gpio_nums[9], rgb_panel_config.data_gpio_nums[10], rgb_panel_config.data_gpio_nums[11], rgb_panel_config.data_gpio_nums[12], rgb_panel_config.data_gpio_nums[13], rgb_panel_config.data_gpio_nums[14], rgb_panel_config.data_gpio_nums[15], rgb_panel_config.disp_gpio_num, rgb_panel_config.on_frame_trans_done, rgb_panel_config.user_ctx, rgb_panel_config.flags.disp_active_low, rgb_panel_config.flags.relax_on_idle, rgb_panel_config.flags.fb_in_psram); + log_d("refresh rate: %d Hz", (ST7701_PANEL_CONFIG_TIMINGS_PCLK_HZ * ST7701_PANEL_CONFIG_DATA_WIDTH) / (ST7701_PANEL_CONFIG_TIMINGS_H_RES + ST7701_PANEL_CONFIG_TIMINGS_HSYNC_PULSE_WIDTH + ST7701_PANEL_CONFIG_TIMINGS_HSYNC_BACK_PORCH + ST7701_PANEL_CONFIG_TIMINGS_HSYNC_FRONT_PORCH) / (ST7701_PANEL_CONFIG_TIMINGS_V_RES + ST7701_PANEL_CONFIG_TIMINGS_VSYNC_PULSE_WIDTH + ST7701_PANEL_CONFIG_TIMINGS_VSYNC_BACK_PORCH + ST7701_PANEL_CONFIG_TIMINGS_VSYNC_FRONT_PORCH) / SOC_LCD_RGB_DATA_WIDTH); + const esp_lcd_panel_dev_config_t panel_dev_config = { + .reset_gpio_num = ST7701_DEV_CONFIG_RESET_GPIO_NUM, + .color_space = ST7701_DEV_CONFIG_COLOR_SPACE, + .bits_per_pixel = ST7701_DEV_CONFIG_BITS_PER_PIXEL, + .flags = { + .reset_active_high = ST7701_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH}, + .vendor_config = ST7701_DEV_CONFIG_VENDOR_CONFIG}; + log_d("panel_dev_config: reset_gpio_num:%d, color_space:%d, bits_per_pixel:%d, flags:{reset_active_high:%d}, vendor_config:0x%08x", panel_dev_config.reset_gpio_num, panel_dev_config.color_space, panel_dev_config.bits_per_pixel, panel_dev_config.flags.reset_active_high, panel_dev_config.vendor_config); + esp_lcd_panel_handle_t panel_handle; + ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &rgb_panel_config, &panel_dev_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); +#ifdef LCD_IPS + // If LCD is IPS invert the colors + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true)); +#endif +#ifdef LCD_SWAP_XY + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, LCD_SWAP_XY)); +#endif +#if defined(LCD_MIRROR_X) || defined(LCD_MIRROR_Y) + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, LCD_MIRROR_X, LCD_MIRROR_Y)); +#endif +#if defined(LCD_GAP_X) || defined(LCD_GAP_Y) + ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, LCD_GAP_X, LCD_GAP_Y)); +#endif + + drv->user_data = panel_handle; + drv->flush_cb = direct_io_lv_flush; +} + +#endif \ No newline at end of file diff --git a/src/lvgl_panel_st7789_i80.c b/src/lvgl_panel_st7789_i80.c new file mode 100644 index 0000000..62b81ae --- /dev/null +++ b/src/lvgl_panel_st7789_i80.c @@ -0,0 +1,111 @@ +#ifdef LCD_ST7789_I80 + +#include +#include +#include +#include + +bool st7789_color_trans_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) +{ + lv_disp_drv_t *disp_driver = user_ctx; + lv_disp_flush_ready(disp_driver); + return false; +} + +void st7789_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + const esp_lcd_panel_handle_t panel_handle = drv->user_data; +#if LV_COLOR_16_SWAP != 1 +#warning "LV_COLOR_16_SWAP should be 1 for max performance" + ushort pixels = lv_area_get_size(area); + lv_color16_t *p = color_map; + while (pixels--) + p++->full = (uint16_t)((p->full >> 8) | (p->full << 8)); +#endif + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_map)); +}; + +void lvgl_lcd_init(lv_disp_drv_t *drv) +{ + log_v("drv:0x%08x"); + + // Hardware rotation is NOT supported + drv->sw_rotate = 1; + drv->rotated = LV_DISP_ROT_NONE; + + pinMode(ST7789_RD_GPIO, OUTPUT); + digitalWrite(ST7789_RD_GPIO, HIGH); + + const esp_lcd_i80_bus_config_t i80_bus_config = { + .clk_src = ST7789_I80_BUS_CONFIG_CLK_SRC, + .dc_gpio_num = ST7789_I80_BUS_CONFIG_DC, + .wr_gpio_num = ST7789_I80_BUS_CONFIG_WR, + .data_gpio_nums = { + ST7789_I80_BUS_CONFIG_DATA_GPIO_D8, + ST7789_I80_BUS_CONFIG_DATA_GPIO_D9, + ST7789_I80_BUS_CONFIG_DATA_GPIO_D10, + ST7789_I80_BUS_CONFIG_DATA_GPIO_D11, + ST7789_I80_BUS_CONFIG_DATA_GPIO_D12, + ST7789_I80_BUS_CONFIG_DATA_GPIO_D13, + ST7789_I80_BUS_CONFIG_DATA_GPIO_D14, + ST7789_I80_BUS_CONFIG_DATA_GPIO_D15}, + .bus_width = ST7789_I80_BUS_CONFIG_BUS_WIDTH, + // transfer 100 lines of pixels (assume pixel is RGB565) at most in one transaction + .max_transfer_bytes = ST7789_I80_BUS_CONFIG_MAX_TRANSFER_BYTES, + .psram_trans_align = ST7789_I80_BUS_CONFIG_PSRAM_TRANS_ALIGN, + .sram_trans_align = ST7789_I80_BUS_CONFIG_SRAM_TRANS_ALIGN}; + log_d("i80_bus_config: clk_src:%d, dc_gpio_num:%d, wr_gpio_num:%d, data_gpio_nums:[%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d], bus_width:%d, max_transfer_bytes:%d, psram_trans_align:%d, sram_trans_align:%d", i80_bus_config.clk_src, i80_bus_config.dc_gpio_num, i80_bus_config.wr_gpio_num, i80_bus_config.data_gpio_nums[0], i80_bus_config.data_gpio_nums[1], i80_bus_config.data_gpio_nums[2], i80_bus_config.data_gpio_nums[3], i80_bus_config.data_gpio_nums[4], i80_bus_config.data_gpio_nums[5], i80_bus_config.data_gpio_nums[6], i80_bus_config.data_gpio_nums[7], i80_bus_config.data_gpio_nums[8], i80_bus_config.data_gpio_nums[9], i80_bus_config.data_gpio_nums[10], i80_bus_config.data_gpio_nums[11], i80_bus_config.data_gpio_nums[12], i80_bus_config.data_gpio_nums[13], i80_bus_config.data_gpio_nums[14], i80_bus_config.data_gpio_nums[15], i80_bus_config.data_gpio_nums[16], i80_bus_config.data_gpio_nums[17], i80_bus_config.data_gpio_nums[18], i80_bus_config.data_gpio_nums[19], i80_bus_config.data_gpio_nums[20], i80_bus_config.data_gpio_nums[21], i80_bus_config.data_gpio_nums[22], i80_bus_config.data_gpio_nums[23], i80_bus_config.bus_width, i80_bus_config.max_transfer_bytes, i80_bus_config.psram_trans_align, i80_bus_config.sram_trans_align); + esp_lcd_i80_bus_handle_t i80_bus; + ESP_ERROR_CHECK(esp_lcd_new_i80_bus(&i80_bus_config, &i80_bus)); + + // Create direct_io panel handle + esp_lcd_panel_io_i80_config_t io_i80_config = { + .cs_gpio_num = ST7789_IO_I80_CONFIG_CS_GPIO_NUM, + .pclk_hz = ST7789_IO_I80_CONFIG_PCLK_HZ, + .on_color_trans_done = st7789_color_trans_done, + .user_ctx = drv, + .trans_queue_depth = ST7789_IO_I80_CONFIG_TRANS_QUEUE_DEPTH, + .lcd_cmd_bits = ST7789_IO_I80_CONFIG_LCD_CMD_BITS, + .lcd_param_bits = ST7789_IO_I80_CONFIG_LCD_PARAM_BITS, + .dc_levels = { + .dc_idle_level = ST7789_IO_I80_CONFIG_DC_LEVELS_DC_IDLE_LEVEL, + .dc_cmd_level = ST7789_IO_I80_CONFIG_DC_LEVELS_DC_CMD_LEVEL, + .dc_dummy_level = ST7789_IO_I80_CONFIG_DC_LEVELS_DC_DUMMY_LEVEL, + .dc_data_level = ST7789_IO_I80_CONFIG_DC_LEVELS_DC_DATA_LEVEL}, + .flags = {.cs_active_high = ST7789_IO_I80_CONFIG_FLAGS_CS_ACTIVE_HIGH, .reverse_color_bits = ST7789_IO_I80_CONFIG_FLAGS_REVERSE_COLOR_BITS, .swap_color_bytes = ST7789_IO_I80_CONFIG_FLAGS_SWAP_COLOR_BYTES, .pclk_active_neg = ST7789_IO_I80_CONFIG_FLAGS_PCLK_ACTIVE_NEG, .pclk_idle_low = ST7789_IO_I80_CONFIG_FLAGS_PCLK_IDLE_LOW}}; + log_d("io_i80_config: cs_gpio_num:%d, pclk_hz:%d, on_color_trans_done:0x%8x, user_ctx:0x%08x, trans_queue_depth:%d, lcd_cmd_bits:%d, lcd_param_bits:%d, dc_levels:{dc_idle_level:%d, dc_cmd_level:%d, dc_dummy_level:%d, dc_data_level:%d}, flags:{cs_active_high:%d, reverse_color_bits:%d, swap_color_bytes:%d, pclk_active_neg:%d, pclk_idle_low:%d}", io_i80_config.cs_gpio_num, io_i80_config.pclk_hz, io_i80_config.on_color_trans_done, io_i80_config.user_ctx, io_i80_config.trans_queue_depth, io_i80_config.lcd_cmd_bits, io_i80_config.lcd_param_bits, io_i80_config.dc_levels.dc_idle_level, io_i80_config.dc_levels.dc_cmd_level, io_i80_config.dc_levels.dc_dummy_level, io_i80_config.dc_levels.dc_data_level, io_i80_config.flags.cs_active_high, io_i80_config.flags.reverse_color_bits, io_i80_config.flags.swap_color_bytes, io_i80_config.flags.pclk_active_neg, io_i80_config.flags.pclk_idle_low); + esp_lcd_panel_io_handle_t io_handle; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_i80_config, &io_handle)); + + // Create ST7789 panel handle + const esp_lcd_panel_dev_config_t panel_dev_config = { + .reset_gpio_num = ST7789_DEV_CONFIG_RESET_GPIO_NUM, + .color_space = ST7789_DEV_CONFIG_COLOR_SPACE, + .bits_per_pixel = ST7789_DEV_CONFIG_BITS_PER_PIXEL, + .flags = { + .reset_active_high = ST7789_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH}, + .vendor_config = ST7789_DEV_CONFIG_VENDOR_CONFIG}; + log_d("panel_dev_config: reset_gpio_num:%d, color_space:%d, bits_per_pixel:%d, flags:{reset_active_high:%d}, vendor_config:0x%08x", panel_dev_config.reset_gpio_num, panel_dev_config.color_space, panel_dev_config.bits_per_pixel, panel_dev_config.flags.reset_active_high, panel_dev_config.vendor_config); + esp_lcd_panel_handle_t panel_handle; + ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_dev_config, &panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); +#ifdef LCD_IPS + // If LCD is IPS invert the colors + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true)); +#endif +#ifdef LCD_SWAP_XY + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, LCD_SWAP_XY)); +#endif +#if defined(LCD_MIRROR_X) || defined(LCD_MIRROR_Y) + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, LCD_MIRROR_X, LCD_MIRROR_Y)); +#endif +#if defined(LCD_GAP_X) || defined(LCD_GAP_Y) + ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, LCD_GAP_X, LCD_GAP_Y)); +#endif + + drv->user_data = panel_handle; + drv->flush_cb = st7789_lv_flush; +} + +#endif \ No newline at end of file diff --git a/src/lvgl_st7789_spi.c b/src/lvgl_panel_st7789_spi.c similarity index 62% rename from src/lvgl_st7789_spi.c rename to src/lvgl_panel_st7789_spi.c index cf4d08e..05719cc 100644 --- a/src/lvgl_st7789_spi.c +++ b/src/lvgl_panel_st7789_spi.c @@ -6,14 +6,14 @@ #include #include -static bool st7789_color_trans_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) +bool st7789_color_trans_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) { lv_disp_drv_t *disp_driver = user_ctx; lv_disp_flush_ready(disp_driver); return false; } -static void st7789_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color16_t *color_map) +void st7789_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) { esp_lcd_panel_handle_t panel_handle = drv->user_data; #if LV_COLOR_16_SWAP != 1 @@ -28,7 +28,8 @@ static void st7789_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color1 void lvgl_lcd_init(lv_disp_drv_t *drv) { - log_d("lvgl_lcd_init"); + log_v("drv:0x%08x"); + // Hardware rotation is supported drv->sw_rotate = 0; drv->rotated = LV_DISP_ROT_NONE; @@ -43,6 +44,7 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .max_transfer_sz = ST7789_SPI_BUS_MAX_TRANSFER_SZ, .flags = ST7789_SPI_BUS_FLAGS, .intr_flags = ST7789_SPI_BUS_INTR_FLAGS}; + log_d("spi_bus_config: mosi_io_num:%d, miso_io_num:%d, sclk_io_num:%d, quadwp_io_num:%d, quadhd_io_num:%d, max_transfer_sz:%d, flags:0x%08x, intr_flags:0x%04x", spi_bus_config.mosi_io_num, spi_bus_config.miso_io_num, spi_bus_config.sclk_io_num, spi_bus_config.quadwp_io_num, spi_bus_config.quadhd_io_num, spi_bus_config.max_transfer_sz, spi_bus_config.flags, spi_bus_config.intr_flags); ESP_ERROR_CHECK_WITHOUT_ABORT(spi_bus_initialize(ST7789_SPI_HOST, &spi_bus_config, ST7789_SPI_DMA_CHANNEL)); // Attach the LCD controller to the SPI bus @@ -61,6 +63,7 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .dc_low_on_data = ST7789_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA, .octal_mode = ST7789_SPI_CONFIG_FLAGS_OCTAL_MODE, .lsb_first = ST7789_SPI_CONFIG_FLAGS_LSB_FIRST}}; + log_d("io_spi_config: cs_gpio_num:%d, dc_gpio_num:%d, spi_mode:%d, pclk_hz:%d, trans_queue_depth:%d, user_ctx:0x%08x, on_color_trans_done:0x%08x, lcd_cmd_bits:%d, lcd_param_bits:%d, flags:{dc_as_cmd_phase:%d, dc_low_on_data:%d, octal_mode:%d, lsb_first:%d}", io_spi_config.cs_gpio_num, io_spi_config.dc_gpio_num, io_spi_config.spi_mode, io_spi_config.pclk_hz, io_spi_config.trans_queue_depth, io_spi_config.user_ctx, io_spi_config.on_color_trans_done, io_spi_config.lcd_cmd_bits, io_spi_config.lcd_param_bits, io_spi_config.flags.dc_as_cmd_phase, io_spi_config.flags.dc_low_on_data, io_spi_config.flags.octal_mode, io_spi_config.flags.lsb_first); esp_lcd_panel_io_handle_t io_handle; ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)ST7789_SPI_HOST, &io_spi_config, &io_handle)); @@ -72,19 +75,26 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .flags = { .reset_active_high = ST7789_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH}, .vendor_config = ST7789_DEV_CONFIG_VENDOR_CONFIG}; - if (panel_dev_config.vendor_config) - log_d("Initialization with vendor config"); - + log_d("panel_dev_config: reset_gpio_num:%d, color_space:%d, bits_per_pixel:%d, flags:{reset_active_high:%d}, vendor_config:0x%08x", panel_dev_config.reset_gpio_num, panel_dev_config.color_space, panel_dev_config.bits_per_pixel, panel_dev_config.flags.reset_active_high, panel_dev_config.vendor_config); esp_lcd_panel_handle_t panel_handle; ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_dev_config, &panel_handle)); - ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); - #ifdef LCD_IPS // If LCD is IPS invert the colors ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true)); #endif +#ifdef LCD_SWAP_XY + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, LCD_SWAP_XY)); +#endif +#if defined(LCD_MIRROR_X) || defined(LCD_MIRROR_Y) + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, LCD_MIRROR_X, LCD_MIRROR_Y)); +#endif +#if defined(LCD_GAP_X) || defined(LCD_GAP_Y) + ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, LCD_GAP_X, LCD_GAP_Y)); +#endif + // Turn display on + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); drv->user_data = panel_handle; drv->flush_cb = st7789_lv_flush; diff --git a/src/lvgl_st7796_spi.c b/src/lvgl_panel_st7796_spi.c similarity index 61% rename from src/lvgl_st7796_spi.c rename to src/lvgl_panel_st7796_spi.c index 8fac6de..baa7eaf 100644 --- a/src/lvgl_st7796_spi.c +++ b/src/lvgl_panel_st7796_spi.c @@ -1,21 +1,20 @@ #ifdef LCD_ST7796_SPI #include +#include #include #include #include #include -#include "esp_lcd_st7796.h" - -static bool st7796_color_trans_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) +bool st7796_color_trans_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) { lv_disp_drv_t *disp_driver = user_ctx; lv_disp_flush_ready(disp_driver); return false; } -static void st7796_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color16_t *color_map) +void st7796_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) { esp_lcd_panel_handle_t panel_handle = drv->user_data; #if LV_COLOR_16_SWAP != 1 @@ -30,7 +29,8 @@ static void st7796_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color1 void lvgl_lcd_init(lv_disp_drv_t *drv) { - log_d("lvgl_lcd_init"); + log_v("drv:0x%08x"); + // Hardware rotation is supported drv->sw_rotate = 0; drv->rotated = LV_DISP_ROT_NONE; @@ -45,6 +45,7 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .max_transfer_sz = ST7796_SPI_BUS_MAX_TRANSFER_SZ, .flags = ST7796_SPI_BUS_FLAGS, .intr_flags = ST7796_SPI_BUS_INTR_FLAGS}; + log_d("spi_bus_config: mosi_io_num:%d, miso_io_num:%d, sclk_io_num:%d, quadwp_io_num:%d, quadhd_io_num:%d, max_transfer_sz:%d, flags:0x%08x, intr_flags:0x%04x", spi_bus_config.mosi_io_num, spi_bus_config.miso_io_num, spi_bus_config.sclk_io_num, spi_bus_config.quadwp_io_num, spi_bus_config.quadhd_io_num, spi_bus_config.max_transfer_sz, spi_bus_config.flags, spi_bus_config.intr_flags); ESP_ERROR_CHECK_WITHOUT_ABORT(spi_bus_initialize(ST7796_SPI_HOST, &spi_bus_config, ST7796_SPI_DMA_CHANNEL)); // Attach the LCD controller to the SPI bus @@ -63,6 +64,7 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .dc_low_on_data = ST7796_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA, .octal_mode = ST7796_SPI_CONFIG_FLAGS_OCTAL_MODE, .lsb_first = ST7796_SPI_CONFIG_FLAGS_LSB_FIRST}}; + log_d("io_spi_config: cs_gpio_num:%d, dc_gpio_num:%d, spi_mode:%d, pclk_hz:%d, trans_queue_depth:%d, user_ctx:0x%08x, on_color_trans_done:0x%08x, lcd_cmd_bits:%d, lcd_param_bits:%d, flags:{dc_as_cmd_phase:%d, dc_low_on_data:%d, octal_mode:%d, lsb_first:%d}", io_spi_config.cs_gpio_num, io_spi_config.dc_gpio_num, io_spi_config.spi_mode, io_spi_config.pclk_hz, io_spi_config.trans_queue_depth, io_spi_config.user_ctx, io_spi_config.on_color_trans_done, io_spi_config.lcd_cmd_bits, io_spi_config.lcd_param_bits, io_spi_config.flags.dc_as_cmd_phase, io_spi_config.flags.dc_low_on_data, io_spi_config.flags.octal_mode, io_spi_config.flags.lsb_first); esp_lcd_panel_io_handle_t io_handle; ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)ST7796_SPI_HOST, &io_spi_config, &io_handle)); @@ -74,14 +76,24 @@ void lvgl_lcd_init(lv_disp_drv_t *drv) .flags = { .reset_active_high = ST7796_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH}, .vendor_config = ST7796_DEV_CONFIG_VENDOR_CONFIG}; - if (panel_dev_config.vendor_config) - log_d("Initialization with vendor config"); - + log_d("panel_dev_config: reset_gpio_num:%d, color_space:%d, bits_per_pixel:%d, flags:{reset_active_high:%d}, vendor_config:0x%08x", panel_dev_config.reset_gpio_num, panel_dev_config.color_space, panel_dev_config.bits_per_pixel, panel_dev_config.flags.reset_active_high, panel_dev_config.vendor_config); esp_lcd_panel_handle_t panel_handle; ESP_ERROR_CHECK(esp_lcd_new_panel_st7796(io_handle, &panel_dev_config, &panel_handle)); - ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); +#ifdef LCD_IPS + // If LCD is IPS invert the colors + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true)); +#endif +#ifdef LCD_SWAP_XY + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, LCD_SWAP_XY)); +#endif +#if defined(LCD_MIRROR_X) || defined(LCD_MIRROR_Y) + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, LCD_MIRROR_X, LCD_MIRROR_Y)); +#endif +#if defined(LCD_GAP_X) || defined(LCD_GAP_Y) + ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel_handle, LCD_GAP_X, LCD_GAP_Y)); +#endif // Turn display on ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); diff --git a/src/lvgl_st7701_par.c b/src/lvgl_st7701_par.c deleted file mode 100644 index 1d3607b..0000000 --- a/src/lvgl_st7701_par.c +++ /dev/null @@ -1,116 +0,0 @@ -#ifdef LCD_ST7701_PAR - -#include -#include -#include -#include -#include - -static bool direct_io_frame_trans_done(esp_lcd_panel_handle_t panel, esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) -{ - lv_disp_drv_t *disp_driver = user_ctx; - lv_disp_flush_ready(disp_driver); - return false; -} - -static void direct_io_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color16_t *color_map) -{ - esp_lcd_panel_handle_t panel_handle = drv->user_data; - // LV_COLOR_16_SWAP is handled by mapping of the data - ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_map)); -}; - -void lvgl_lcd_init(lv_disp_drv_t *drv) -{ - log_d("lvgl_lcd_init"); - // Hardware rotation is NOT supported - drv->sw_rotate = 1; - drv->rotated = LV_DISP_ROT_NONE; - - // Install 3-wire SPI panel IO - esp_lcd_panel_io_3wire_spi_config_t io_config = { - .line_config = { - .cs_io_type = IO_TYPE_GPIO, - .cs_gpio_num = ST7701_IO_3WIRE_SPI_LINE_CONFIG_CS_GPIO_NUM, - .scl_io_type = IO_TYPE_GPIO, - .scl_gpio_num = ST7701_IO_3WIRE_SPI_LINE_CONFIG_SCL_GPIO_NUM, - .sda_io_type = IO_TYPE_GPIO, - .sda_gpio_num = ST7701_IO_3WIRE_SPI_LINE_CONFIG_SDA_GPIO_NUM}, - .expect_clk_speed = ST7701_IO_3WIRE_SPI_EXPECT_CLK_SPEED, - .spi_mode = ST7701_IO_3WIRE_SPI_SPI_MODE, - .lcd_cmd_bytes = ST7701_IO_3WIRE_SPI_LCD_CMD_BYTES, - .lcd_param_bytes = ST7701_IO_3WIRE_SPI_LCD_PARAM_BYTES, - .flags = { - .use_dc_bit = ST7701_IO_3WIRE_SPI_FLAGS_USE_DC_BIT, - .dc_zero_on_data = ST7701_IO_3WIRE_SPI_FLAGS_DC_ZERO_ON_DATA, - .lsb_first = ST7701_IO_3WIRE_SPI_FLAGS_LSB_FIRST, - .cs_high_active = ST7701_IO_3WIRE_SPI_FLAGS_CS_HIGH_ACTIVE, - .del_keep_cs_inactive = ST7701_IO_3WIRE_SPI_FLAGS_DEL_KEEP_CS_INACTIVE} - }; - - esp_lcd_panel_io_handle_t io_handle; - ESP_ERROR_CHECK(esp_lcd_new_panel_io_3wire_spi(&io_config, &io_handle)); - - // Create direct_io panel handle - esp_lcd_rgb_panel_config_t tft_panel_config = { - .clk_src = ST7701_PANEL_CONFIG_CLK_SRC, - .timings = { - .pclk_hz = ST7701_PANEL_CONFIG_TIMINGS_PCLK_HZ, - .h_res = ST7701_PANEL_CONFIG_TIMINGS_H_RES, - .v_res = ST7701_PANEL_CONFIG_TIMINGS_V_RES, - .hsync_pulse_width = ST7701_PANEL_CONFIG_TIMINGS_HSYNC_PULSE_WIDTH, - .hsync_back_porch = ST7701_PANEL_CONFIG_TIMINGS_HSYNC_BACK_PORCH, - .hsync_front_porch = ST7701_PANEL_CONFIG_TIMINGS_HSYNC_FRONT_PORCH, - .vsync_pulse_width = ST7701_PANEL_CONFIG_TIMINGS_VSYNC_PULSE_WIDTH, - .vsync_back_porch = ST7701_PANEL_CONFIG_TIMINGS_VSYNC_BACK_PORCH, - .vsync_front_porch = ST7701_PANEL_CONFIG_TIMINGS_VSYNC_FRONT_PORCH, - .flags = { - .hsync_idle_low = ST7701_PANEL_CONFIG_TIMINGS_FLAGS_HSYNC_IDLE_LOW, - .vsync_idle_low = ST7701_PANEL_CONFIG_TIMINGS_FLAGS_VSYNC_IDLE_LOW, - .de_idle_high = ST7701_PANEL_CONFIG_TIMINGS_FLAGS_DE_IDLE_HIGH, - .pclk_active_neg = ST7701_PANEL_CONFIG_TIMINGS_FLAGS_PCLK_ACTIVE_NEG, - .pclk_idle_high = ST7701_PANEL_CONFIG_TIMINGS_FLAGS_PCLK_IDLE_HIGH}}, - .data_width = ST7701_PANEL_CONFIG_DATA_WIDTH, - .sram_trans_align = ST7701_PANEL_CONFIG_SRAM_TRANS_ALIGN, - .psram_trans_align = ST7701_PANEL_CONFIG_PSRAM_TRANS_ALIGN, - .hsync_gpio_num = ST7701_PANEL_CONFIG_HSYNC_GPIO_NUM, - .vsync_gpio_num = ST7701_PANEL_CONFIG_VSYNC_GPIO_NUM, - .de_gpio_num = ST7701_PANEL_CONFIG_DE_GPIO_NUM, - .pclk_gpio_num = ST7701_PANEL_CONFIG_PCLK_GPIO_NUM, -#if LV_COLOR_16_SWAP == 0 - .data_gpio_nums = {ST7701_PANEL_CONFIG_DATA_GPIO_R0, ST7701_PANEL_CONFIG_DATA_GPIO_R1, ST7701_PANEL_CONFIG_DATA_GPIO_R2, ST7701_PANEL_CONFIG_DATA_GPIO_R3, ST7701_PANEL_CONFIG_DATA_GPIO_R4, ST7701_PANEL_CONFIG_DATA_GPIO_G0, ST7701_PANEL_CONFIG_DATA_GPIO_G1, ST7701_PANEL_CONFIG_DATA_GPIO_G2, ST7701_PANEL_CONFIG_DATA_GPIO_G3, ST7701_PANEL_CONFIG_DATA_GPIO_G4, ST7701_PANEL_CONFIG_DATA_GPIO_G5, ST7701_PANEL_CONFIG_DATA_GPIO_B0, ST7701_PANEL_CONFIG_DATA_GPIO_B1, ST7701_PANEL_CONFIG_DATA_GPIO_B2, ST7701_PANEL_CONFIG_DATA_GPIO_B3, ST7701_PANEL_CONFIG_DATA_GPIO_B4}, -#else - .data_gpio_nums = {ST7701_PANEL_CONFIG_DATA_GPIO_G3, ST7701_PANEL_CONFIG_DATA_GPIO_G4, ST7701_PANEL_CONFIG_DATA_GPIO_G5, ST7701_PANEL_CONFIG_DATA_GPIO_B0, ST7701_PANEL_CONFIG_DATA_GPIO_B1, ST7701_PANEL_CONFIG_DATA_GPIO_B2, ST7701_PANEL_CONFIG_DATA_GPIO_B3, ST7701_PANEL_CONFIG_DATA_GPIO_B4, ST7701_PANEL_CONFIG_DATA_GPIO_R0, ST7701_PANEL_CONFIG_DATA_GPIO_R1, ST7701_PANEL_CONFIG_DATA_GPIO_R2, ST7701_PANEL_CONFIG_DATA_GPIO_R3, ST7701_PANEL_CONFIG_DATA_GPIO_R4, ST7701_PANEL_CONFIG_DATA_GPIO_G0, ST7701_PANEL_CONFIG_DATA_GPIO_G1, ST7701_PANEL_CONFIG_DATA_GPIO_G2}, -#endif - .disp_gpio_num = ST7701_PANEL_CONFIG_DISP_GPIO_NUM, - .on_frame_trans_done = direct_io_frame_trans_done, - .user_ctx = drv, - .flags = {.disp_active_low = ST7701_PANEL_CONFIG_FLAGS_DISP_ACTIVE_LOW, .relax_on_idle = ST7701_PANEL_CONFIG_FLAGS_RELAX_ON_IDLE, .fb_in_psram = ST7701_PANEL_CONFIG_FLAGS_FB_IN_PSRAM} - }; - - const st7701_vendor_config_t vendor_config = { - .init_cmds = ST7701_VENDOR_CONFIG_INIT_CMDS, - .init_cmds_size = ST7701_VENDOR_CONFIG_INIT_CMDS_SIZE, - .flags = { - .mirror_by_cmd = ST7701_VENDOR_CONFIG_FLAGS_MIRROR_BY_CMD, - .auto_del_panel_io = ST7701_VENDOR_CONFIG_FLAGS_AUTO_DEL_PANEL_IO}, - .rgb_config = &tft_panel_config}; - - const esp_lcd_panel_dev_config_t panel_config = { - .reset_gpio_num = ST7701_DEV_CONFIG_RESET_GPIO_NUM, - .color_space = ST7701_DEV_CONFIG_COLOR_SPACE, - .bits_per_pixel = ST7701_DEV_CONFIG_BITS_PER_PIXEL, - .flags = { - .reset_active_high = ST7701_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH}, - .vendor_config = &vendor_config}; - - esp_lcd_panel_handle_t panel_handle; - ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &panel_handle)); - ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); - ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); - - drv->user_data = panel_handle; - drv->flush_cb = direct_io_lv_flush; -} - -#endif \ No newline at end of file diff --git a/src/lvgl_st7789_i80.c b/src/lvgl_st7789_i80.c deleted file mode 100644 index b6b052c..0000000 --- a/src/lvgl_st7789_i80.c +++ /dev/null @@ -1,107 +0,0 @@ -#ifdef LCD_ST7789_I80 - -#include -#include -#include -#include - -static bool st7789_color_trans_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) -{ - lv_disp_drv_t *disp_driver = user_ctx; - lv_disp_flush_ready(disp_driver); - return false; -} - -static void st7789_lv_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color16_t *color_map) -{ - esp_lcd_panel_handle_t panel_handle = drv->user_data; -#if LV_COLOR_16_SWAP != 1 -#warning "LV_COLOR_16_SWAP should be 1 for max performance" - ushort pixels = lv_area_get_size(area); - lv_color16_t *p = color_map; - while (pixels--) - p++->full = (uint16_t)((p->full >> 8) | (p->full << 8)); -#endif - ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_map)); -}; - -void lvgl_lcd_init(lv_disp_drv_t *drv) -{ - log_d("lvgl_lcd_init"); - // Hardware rotation is NOT supported - drv->sw_rotate = 1; - drv->rotated = LV_DISP_ROT_NONE; - - pinMode(ST7789_RD_GPIO, OUTPUT); - digitalWrite(ST7789_RD_GPIO, HIGH); - - const esp_lcd_i80_bus_config_t bus_config = { - .clk_src = ST7789_I80_BUS_CONFIG_CLK_SRC, - .dc_gpio_num = ST7789_I80_BUS_CONFIG_DC, - .wr_gpio_num = ST7789_I80_BUS_CONFIG_WR, - .data_gpio_nums = { - ST7789_I80_BUS_CONFIG_DATA_GPIO_D8, - ST7789_I80_BUS_CONFIG_DATA_GPIO_D9, - ST7789_I80_BUS_CONFIG_DATA_GPIO_D10, - ST7789_I80_BUS_CONFIG_DATA_GPIO_D11, - ST7789_I80_BUS_CONFIG_DATA_GPIO_D12, - ST7789_I80_BUS_CONFIG_DATA_GPIO_D13, - ST7789_I80_BUS_CONFIG_DATA_GPIO_D14, - ST7789_I80_BUS_CONFIG_DATA_GPIO_D15 - }, - .bus_width = ST7789_I80_BUS_CONFIG_BUS_WIDTH, - // transfer 100 lines of pixels (assume pixel is RGB565) at most in one transaction - .max_transfer_bytes = ST7789_I80_BUS_CONFIG_MAX_TRANSFER_BYTES, - .psram_trans_align = ST7789_I80_BUS_CONFIG_PSRAM_TRANS_ALIGN, - .sram_trans_align = ST7789_I80_BUS_CONFIG_SRAM_TRANS_ALIGN}; - esp_lcd_i80_bus_handle_t i80_bus; - ESP_ERROR_CHECK(esp_lcd_new_i80_bus(&bus_config, &i80_bus)); - - // Create direct_io panel handle - esp_lcd_panel_io_i80_config_t io_config = { - .cs_gpio_num = ST7789_IO_I80_CONFIG_CS_GPIO_NUM, - .pclk_hz = ST7789_IO_I80_CONFIG_PCLK_HZ, - .on_color_trans_done = st7789_color_trans_done, - .user_ctx = drv, - .trans_queue_depth = ST7789_IO_I80_CONFIG_TRANS_QUEUE_DEPTH, - .lcd_cmd_bits = ST7789_IO_I80_CONFIG_LCD_CMD_BITS, - .lcd_param_bits = ST7789_IO_I80_CONFIG_LCD_PARAM_BITS, - .dc_levels = { - .dc_idle_level = ST7789_IO_I80_CONFIG_DC_LEVELS_DC_IDLE_LEVEL, - .dc_cmd_level = ST7789_IO_I80_CONFIG_DC_LEVELS_DC_CMD_LEVEL, - .dc_dummy_level = ST7789_IO_I80_CONFIG_DC_LEVELS_DC_DUMMY_LEVEL, - .dc_data_level = ST7789_IO_I80_CONFIG_DC_LEVELS_DC_DATA_LEVEL - }, - .flags = { - .cs_active_high = ST7789_IO_I80_CONFIG_FLAGS_CS_ACTIVE_HIGH, - .reverse_color_bits = ST7789_IO_I80_CONFIG_FLAGS_REVERSE_COLOR_BITS, - .swap_color_bytes = ST7789_IO_I80_CONFIG_FLAGS_SWAP_COLOR_BYTES, - .pclk_active_neg = ST7789_IO_I80_CONFIG_FLAGS_PCLK_ACTIVE_NEG, - .pclk_idle_low = ST7789_IO_I80_CONFIG_FLAGS_PCLK_IDLE_LOW}}; - esp_lcd_panel_io_handle_t io_handle; - ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle)); - - // Create ST7789 panel handle - const esp_lcd_panel_dev_config_t panel_dev_config = { - .reset_gpio_num = ST7789_DEV_CONFIG_RESET_GPIO_NUM, - .color_space = ST7789_DEV_CONFIG_COLOR_SPACE, - .bits_per_pixel = ST7789_DEV_CONFIG_BITS_PER_PIXEL, - .flags = { - .reset_active_high = ST7789_DEV_CONFIG_FLAGS_RESET_ACTIVE_HIGH}, - .vendor_config = ST7789_DEV_CONFIG_VENDOR_CONFIG}; - esp_lcd_panel_handle_t panel_handle; - ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_dev_config, &panel_handle)); - - ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); - ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); - -#ifdef LCD_IPS - // If LCD is IPS invert the colors - ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true)); -#endif - - drv->user_data = panel_handle; - drv->flush_cb = st7789_lv_flush; -} - -#endif \ No newline at end of file diff --git a/src/lvgl_cst816s_i2c.c b/src/lvgl_touch_cst816s_i2c.c similarity index 59% rename from src/lvgl_cst816s_i2c.c rename to src/lvgl_touch_cst816s_i2c.c index 49de858..b91cd7f 100644 --- a/src/lvgl_cst816s_i2c.c +++ b/src/lvgl_touch_cst816s_i2c.c @@ -1,29 +1,27 @@ #ifdef TOUCH_CST816S_I2C +#include "esp_touch_cst816s.h" #include #include "driver/i2c.h" -#include "esp_lcd_touch.h" -#include "esp_lcd_touch_cst816s.h" -static void cst816s_lvgl_touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data) +void cst816s_lvgl_touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data) { esp_lcd_touch_handle_t touch_handle = drv->user_data; - uint16_t touch_x[1] = {0}; - uint16_t touch_y[1] = {0}; - uint16_t touch_strength[1] = {0}; + uint16_t x[1]; + uint16_t y[1]; uint8_t touch_cnt = 0; // Read touch controller data ESP_ERROR_CHECK(esp_lcd_touch_read_data(touch_handle)); // Get coordinates - bool pressed = esp_lcd_touch_get_coordinates(touch_handle, touch_x, touch_y, touch_strength, &touch_cnt, 1); + bool pressed = esp_lcd_touch_get_coordinates(touch_handle, x, y, NULL, &touch_cnt, 1); if (pressed && touch_cnt > 0) { - data->point.x = touch_x[0]; - data->point.y = touch_y[0]; + data->point.x = x[0]; + data->point.y = y[0]; data->state = LV_INDEV_STATE_PRESSED; - log_d("Pressed at: (%d,%d), strength: %d", data->point.x, data->point.y, touch_strength); + log_v("Pressed at: (%d,%d)", data->point.x, data->point.y); } else data->state = LV_INDEV_STATE_RELEASED; @@ -31,7 +29,8 @@ static void cst816s_lvgl_touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data) void lvgl_touch_init(lv_indev_drv_t *drv) { - log_d("lvgl_touch_init"); + log_v("drv:0x%08x"); + // Create I2C bus const i2c_config_t i2c_config = { .mode = I2C_MODE_MASTER, @@ -42,12 +41,13 @@ void lvgl_touch_init(lv_indev_drv_t *drv) .master = { .clk_speed = CST816S_I2C_CONFIG_MASTER_CLK_SPEED}, .clk_flags = CST816S_I2C_CONFIG_CLK_FLAGS}; + log_d("i2c_config: mode:%d, sda_io_num:%d, scl_io_num:%d, sda_pullup_en:%d, scl_pullup_en:%d, master:{clk_speed:%d}, clk_flags:%d", i2c_config.mode, i2c_config.sda_io_num, i2c_config.scl_io_num, i2c_config.sda_pullup_en, i2c_config.scl_pullup_en, i2c_config.master.clk_speed, i2c_config.clk_flags); ESP_ERROR_CHECK(i2c_param_config(CST816S_I2C_HOST, &i2c_config)); ESP_ERROR_CHECK(i2c_driver_install(CST816S_I2C_HOST, i2c_config.mode, 0, 0, 0)); // Create IO handle const esp_lcd_panel_io_i2c_config_t io_i2c_config = { - .dev_addr = CST816S_IO_I2C_CONFIG_DEV_ADDR, + .dev_addr = CST816S_IO_I2C_CONFIG_DEV_ADDRESS, .control_phase_bytes = CST816S_IO_I2C_CONFIG_CONTROL_PHASE_BYTES, .user_ctx = drv, .dc_bit_offset = CST816S_IO_I2C_CONFIG_DC_BIT_OFFSET, @@ -56,6 +56,7 @@ void lvgl_touch_init(lv_indev_drv_t *drv) .flags = { .dc_low_on_data = CST816S_IO_I2C_CONFIG_FLAGS_DC_LOW_ON_DATA, .disable_control_phase = CST816S_IO_I2C_CONFIG_FLAGS_DISABLE_CONTROL_PHASE}}; + log_d("io_i2c_config: dev_addr:0x%02x, control_phase_bytes:%d, user_ctx:0x%08x, dc_bit_offset:%d, lcd_cmd_bits:%d, lcd_param_bits:%d, flags:{.dc_low_on_data:%d, disable_control_phase:%d}", io_i2c_config.dev_addr, io_i2c_config.control_phase_bytes, io_i2c_config.user_ctx, io_i2c_config.dc_bit_offset, io_i2c_config.lcd_cmd_bits, io_i2c_config.lcd_param_bits, io_i2c_config.flags.dc_low_on_data, io_i2c_config.flags.disable_control_phase); esp_lcd_panel_io_handle_t io_handle; ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)CST816S_I2C_HOST, &io_i2c_config, &io_handle)); @@ -68,9 +69,9 @@ void lvgl_touch_init(lv_indev_drv_t *drv) .levels = { .reset = CST816S_TOUCH_CONFIG_LEVELS_RESET, .interrupt = CST816S_TOUCH_CONFIG_LEVELS_INTERRUPT}, - // Unfortunately not supported - //.flags = {.swap_xy = CST816S_TOUCH_CONFIG_FLAGS_SWAP_XY, .mirror_x = CST816S_TOUCH_CONFIG_FLAGS_MIRROR_X, .mirror_y = CST816S_TOUCH_CONFIG_FLAGS_MIRROR_Y}, + .flags = {.swap_xy = TOUCH_MIRROR_XY, .mirror_x = TOUCH_MIRROR_X, .mirror_y = TOUCH_MIRROR_Y}, .user_data = io_handle}; + log_d("touch_config: x_max:%d, y_max:%d, rst_gpio_num:%d, int_gpio_num:%d, levels:{reset:%d, interrupt:%d}, flags:{swap_xy:%d, mirror_x:%d, mirror_y:%d}, user_data:0x%08x", touch_config.x_max, touch_config.y_max, touch_config.rst_gpio_num, touch_config.int_gpio_num, touch_config.levels.reset, touch_config.levels.interrupt, touch_config.flags.swap_xy, touch_config.flags.mirror_x, touch_config.flags.mirror_y, touch_config.user_data); esp_lcd_touch_handle_t touch_handle; ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_cst816s(io_handle, &touch_config, &touch_handle)); diff --git a/src/lvgl_touch_gt911_i2c.c b/src/lvgl_touch_gt911_i2c.c new file mode 100644 index 0000000..38dc776 --- /dev/null +++ b/src/lvgl_touch_gt911_i2c.c @@ -0,0 +1,84 @@ +#ifdef TOUCH_GT911_I2C + +#include +#include +#include +#include + +void gt911_lvgl_touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data) +{ + esp_lcd_touch_handle_t touch_handle = drv->user_data; + + uint16_t x[1]; + uint16_t y[1]; + uint8_t touch_cnt = 0; + + // Read touch controller data + ESP_ERROR_CHECK(esp_lcd_touch_read_data(touch_handle)); + // Get coordinates + bool pressed = esp_lcd_touch_get_coordinates(touch_handle, x, y, NULL, &touch_cnt, 1); + if (pressed && touch_cnt > 0) + { + data->point.x = x[0]; + data->point.y = y[0]; + data->state = LV_INDEV_STATE_PRESSED; + log_v("Pressed at: (%d,%d)", data->point.x, data->point.y); + } + else + data->state = LV_INDEV_STATE_RELEASED; +} + +void lvgl_touch_init(lv_indev_drv_t *drv) +{ + log_v("drv:0x%08x"); + + // Create I2C bus + const i2c_config_t i2c_config = { + .mode = I2C_MODE_MASTER, + .sda_io_num = GT911_I2C_CONFIG_SDA_IO_NUM, + .scl_io_num = GT911_I2C_CONFIG_SCL_IO_NUM, + .sda_pullup_en = GT911_I2C_CONFIG_SDA_PULLUP_EN, + .scl_pullup_en = GT911_I2C_CONFIG_SCL_PULLUP_EN, + .master = { + .clk_speed = GT911_I2C_CONFIG_MASTER_CLK_SPEED}, + .clk_flags = GT911_I2C_CONFIG_CLK_FLAGS}; + log_d("i2c_config: mode:%d, sda_io_num:%d, scl_io_num:%d, sda_pullup_en:%d, scl_pullup_en:%d, master:{clk_speed:%d}, clk_flags:%d", i2c_config.mode, i2c_config.sda_io_num, i2c_config.scl_io_num, i2c_config.sda_pullup_en, i2c_config.scl_pullup_en, i2c_config.master.clk_speed, i2c_config.clk_flags); + ESP_ERROR_CHECK(i2c_param_config(GT911_I2C_HOST, &i2c_config)); + ESP_ERROR_CHECK(i2c_driver_install(GT911_I2C_HOST, i2c_config.mode, 0, 0, 0)); + + // Create IO handle + const esp_lcd_panel_io_i2c_config_t io_i2c_config = { + .dev_addr = GT911_IO_I2C_CONFIG_DEV_ADDR, + .control_phase_bytes = GT911_IO_I2C_CONFIG_CONTROL_PHASE_BYTES, + .user_ctx = drv, + .dc_bit_offset = GT911_IO_I2C_CONFIG_DC_BIT_OFFSET, + .lcd_cmd_bits = GT911_IO_I2C_CONFIG_LCD_CMD_BITS, + .lcd_param_bits = GT911_IO_I2C_CONFIG_LCD_PARAM_BITS, + .flags = { + .dc_low_on_data = GT911_IO_I2C_CONFIG_FLAGS_DC_LOW_ON_DATA, + .disable_control_phase = GT911_IO_I2C_CONFIG_FLAGS_DISABLE_CONTROL_PHASE}}; + log_d("io_i2c_config: dev_addr:0x%02x, control_phase_bytes:%d, user_ctx:0x%08x, dc_bit_offset:%d, lcd_cmd_bits:%d, lcd_param_bits:%d, flags:{.dc_low_on_data:%d, disable_control_phase:%d}", io_i2c_config.dev_addr, io_i2c_config.control_phase_bytes, io_i2c_config.user_ctx, io_i2c_config.dc_bit_offset, io_i2c_config.lcd_cmd_bits, io_i2c_config.lcd_param_bits, io_i2c_config.flags.dc_low_on_data, io_i2c_config.flags.disable_control_phase); + esp_lcd_panel_io_handle_t io_handle; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)GT911_I2C_HOST, &io_i2c_config, &io_handle)); + + // Create touch configuration + const esp_lcd_touch_config_t touch_config = { + .x_max = GT911_TOUCH_CONFIG_X_MAX, + .y_max = GT911_TOUCH_CONFIG_Y_MAX, + .rst_gpio_num = GT911_TOUCH_CONFIG_RST_GPIO_NUM, + .int_gpio_num = GT911_TOUCH_CONFIG_INT_GPIO_NUM, + .levels = { + .reset = GT911_TOUCH_CONFIG_LEVELS_RESET, + .interrupt = GT911_TOUCH_CONFIG_LEVELS_INTERRUPT}, + .flags = {.swap_xy = TOUCH_MIRROR_XY, .mirror_x = TOUCH_MIRROR_X, .mirror_y = TOUCH_MIRROR_Y}, + .user_data = io_handle}; + log_d("touch_config: x_max:%d, y_max:%d, rst_gpio_num:%d, int_gpio_num:%d, levels:{reset:%d, interrupt:%d}, flags:{swap_xy:%d, mirror_x:%d, mirror_y:%d}, user_data:0x%08x", touch_config.x_max, touch_config.y_max, touch_config.rst_gpio_num, touch_config.int_gpio_num, touch_config.levels.reset, touch_config.levels.interrupt, touch_config.flags.swap_xy, touch_config.flags.mirror_x, touch_config.flags.mirror_y, touch_config.user_data); + esp_lcd_touch_handle_t touch_handle; + ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(io_handle, &touch_config, &touch_handle)); + + drv->type = LV_INDEV_TYPE_POINTER; + drv->user_data = touch_handle; + drv->read_cb = gt911_lvgl_touch_cb; +} + +#endif \ No newline at end of file diff --git a/src/lvgl_xpt2046_spi.c b/src/lvgl_touch_xpt2046_spi.c similarity index 57% rename from src/lvgl_xpt2046_spi.c rename to src/lvgl_touch_xpt2046_spi.c index cb642af..175e006 100644 --- a/src/lvgl_xpt2046_spi.c +++ b/src/lvgl_touch_xpt2046_spi.c @@ -1,29 +1,27 @@ #ifdef TOUCH_XPT2046_SPI #include +#include #include -#include "esp_lcd_touch.h" -#include "esp_lcd_touch_xpt2046.h" -static void xpt2046_lvgl_touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data) +void xpt2046_lvgl_touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data) { esp_lcd_touch_handle_t touch_handle = drv->user_data; - uint16_t touch_x[1] = {0}; - uint16_t touch_y[1] = {0}; - uint16_t touch_strength[1] = {0}; + uint16_t x[1]; + uint16_t y[1]; uint8_t touch_cnt = 0; // Read touch controller data ESP_ERROR_CHECK(esp_lcd_touch_read_data(touch_handle)); // Get coordinates - bool pressed = esp_lcd_touch_get_coordinates(touch_handle, touch_x, touch_y, touch_strength, &touch_cnt, 1); + bool pressed = esp_lcd_touch_get_coordinates(touch_handle, x, y, NULL, &touch_cnt, 1); if (pressed && touch_cnt > 0) { - data->point.x = touch_x[0]; - data->point.y = touch_y[0]; + data->point.x = x[0]; + data->point.y = y[0]; data->state = LV_INDEV_STATE_PRESSED; - log_d("Pressed at: (%d,%d), strength: %d", data->point.x, data->point.y, touch_strength); + log_v("Pressed at: (%d,%d)", data->point.x, data->point.y); } else data->state = LV_INDEV_STATE_RELEASED; @@ -31,7 +29,8 @@ static void xpt2046_lvgl_touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data) void lvgl_touch_init(lv_indev_drv_t *drv) { - log_d("lvgl_touch_init"); + log_v("drv:0x%08x"); + // Create SPI bus only if not already initialized (S035R shares the SPI bus) const spi_bus_config_t spi_bus_config = { .mosi_io_num = XPT2046_SPI_BUS_MOSI_IO_NUM, @@ -39,6 +38,7 @@ void lvgl_touch_init(lv_indev_drv_t *drv) .sclk_io_num = XPT2046_SPI_BUS_SCLK_IO_NUM, .quadwp_io_num = XPT2046_SPI_BUS_QUADWP_IO_NUM, .quadhd_io_num = XPT2046_SPI_BUS_QUADHD_IO_NUM}; + log_d("spi_bus_config: mosi_io_num:%d, miso_io_num:%d, sclk_io_num:%d, quadwp_io_num:%d, quadhd_io_num:%d, max_transfer_sz:%d, flags:0x%08x, intr_flags:0x%04x", spi_bus_config.mosi_io_num, spi_bus_config.miso_io_num, spi_bus_config.sclk_io_num, spi_bus_config.quadwp_io_num, spi_bus_config.quadhd_io_num, spi_bus_config.max_transfer_sz, spi_bus_config.flags, spi_bus_config.intr_flags); ESP_ERROR_CHECK_WITHOUT_ABORT(spi_bus_initialize(XPT2046_SPI_HOST, &spi_bus_config, XPT2046_SPI_DMA_CHANNEL)); // Attach the touch controller to the SPI bus @@ -56,6 +56,7 @@ void lvgl_touch_init(lv_indev_drv_t *drv) .dc_low_on_data = XPT2046_SPI_CONFIG_FLAGS_DC_LOW_ON_DATA, .octal_mode = XPT2046_SPI_CONFIG_FLAGS_OCTAL_MODE, .lsb_first = XPT2046_SPI_CONFIG_FLAGS_LSB_FIRST}}; + log_d("io_spi_config: cs_gpio_num:%d, dc_gpio_num:%d, spi_mode:%d, pclk_hz:%d, trans_queue_depth:%d, user_ctx:0x%08x, on_color_trans_done:0x%08x, lcd_cmd_bits:%d, lcd_param_bits:%d, flags:{dc_as_cmd_phase:%d, dc_low_on_data:%d, octal_mode:%d, lsb_first:%d}", io_spi_config.cs_gpio_num, io_spi_config.dc_gpio_num, io_spi_config.spi_mode, io_spi_config.pclk_hz, io_spi_config.trans_queue_depth, io_spi_config.user_ctx, io_spi_config.on_color_trans_done, io_spi_config.lcd_cmd_bits, io_spi_config.lcd_param_bits, io_spi_config.flags.dc_as_cmd_phase, io_spi_config.flags.dc_low_on_data, io_spi_config.flags.octal_mode, io_spi_config.flags.lsb_first); esp_lcd_panel_io_handle_t io_handle; ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)XPT2046_SPI_HOST, &io_spi_config, &io_handle)); @@ -68,9 +69,9 @@ void lvgl_touch_init(lv_indev_drv_t *drv) .levels = { .reset = XPT2046_TOUCH_CONFIG_LEVELS_RESET, .interrupt = XPT2046_TOUCH_CONFIG_LEVELS_INTERRUPT}, - // Unfortunately not supported - //.flags = {.swap_xy = XPT2046_TOUCH_CONFIG_FLAGS_SWAP_XY, .mirror_x = XPT2046_TOUCH_CONFIG_FLAGS_MIRROR_X, .mirror_y = XPT2046_TOUCH_CONFIG_FLAGS_MIRROR_Y}, + .flags = {.swap_xy = TOUCH_MIRROR_XY, .mirror_x = TOUCH_MIRROR_X, .mirror_y = TOUCH_MIRROR_Y}, .user_data = io_handle}; + log_d("touch_config: x_max:%d, y_max:%d, rst_gpio_num:%d, int_gpio_num:%d, levels:{reset:%d, interrupt:%d}, flags:{swap_xy:%d, mirror_x:%d, mirror_y:%d}, user_data:0x%08x", touch_config.x_max, touch_config.y_max, touch_config.rst_gpio_num, touch_config.int_gpio_num, touch_config.levels.reset, touch_config.levels.interrupt, touch_config.flags.swap_xy, touch_config.flags.mirror_x, touch_config.flags.mirror_y, touch_config.user_data); esp_lcd_touch_handle_t touch_handle; ESP_ERROR_CHECK(esp_lcd_touch_new_spi_xpt2046(io_handle, &touch_config, &touch_handle));