From 08384030b0e565870a61d7f2bd73e29a54b68cb9 Mon Sep 17 00:00:00 2001 From: C47D Date: Thu, 17 Dec 2020 00:02:55 -0600 Subject: [PATCH] Move drivers from the lv_port_esp32 to here --- CMakeLists.txt | 9 + README.md | 3 +- component.mk | 4 + lvgl_helpers.c | 236 ++++ lvgl_helpers.h | 85 ++ lvgl_i2c_conf.h | 116 ++ lvgl_spi_conf.h | 198 ++++ lvgl_tft/CMakeLists.txt | 53 + lvgl_tft/EVE.h | 842 ++++++++++++++ lvgl_tft/EVE_commands.c | 2313 +++++++++++++++++++++++++++++++++++++ lvgl_tft/EVE_commands.h | 203 ++++ lvgl_tft/EVE_config.h | 1043 +++++++++++++++++ lvgl_tft/FT81x.c | 323 ++++++ lvgl_tft/FT81x.h | 17 + lvgl_tft/GC9A01.c | 272 +++++ lvgl_tft/GC9A01.h | 65 ++ lvgl_tft/Kconfig | 1000 ++++++++++++++++ lvgl_tft/component.mk | 4 + lvgl_tft/disp_driver.c | 107 ++ lvgl_tft/disp_driver.h | 86 ++ lvgl_tft/disp_spi.c | 320 +++++ lvgl_tft/disp_spi.h | 81 ++ lvgl_tft/hx8357.c | 310 +++++ lvgl_tft/hx8357.h | 151 +++ lvgl_tft/il3820.c | 414 +++++++ lvgl_tft/il3820.h | 113 ++ lvgl_tft/ili9341.c | 241 ++++ lvgl_tft/ili9341.h | 65 ++ lvgl_tft/ili9481.c | 224 ++++ lvgl_tft/ili9481.h | 130 +++ lvgl_tft/ili9486.c | 214 ++++ lvgl_tft/ili9486.h | 61 + lvgl_tft/ili9488.c | 233 ++++ lvgl_tft/ili9488.h | 167 +++ lvgl_tft/jd79653a.c | 482 ++++++++ lvgl_tft/jd79653a.h | 36 + lvgl_tft/ra8875.c | 365 ++++++ lvgl_tft/ra8875.h | 118 ++ lvgl_tft/sh1107.c | 218 ++++ lvgl_tft/sh1107.h | 55 + lvgl_tft/ssd1306.c | 240 ++++ lvgl_tft/ssd1306.h | 57 + lvgl_tft/st7735s.c | 269 +++++ lvgl_tft/st7735s.h | 149 +++ lvgl_tft/st7789.c | 235 ++++ lvgl_tft/st7789.h | 120 ++ lvgl_tft/uc8151d.c | 268 +++++ lvgl_tft/uc8151d.h | 39 + lvgl_touch/CMakeLists.txt | 34 + lvgl_touch/FT81x.c | 85 ++ lvgl_touch/FT81x.h | 46 + lvgl_touch/Kconfig | 458 ++++++++ lvgl_touch/adcraw.c | 324 ++++++ lvgl_touch/adcraw.h | 79 ++ lvgl_touch/component.mk | 4 + lvgl_touch/ft6x36.c | 200 ++++ lvgl_touch/ft6x36.h | 162 +++ lvgl_touch/ra8875_touch.c | 181 +++ lvgl_touch/ra8875_touch.h | 56 + lvgl_touch/stmpe610.c | 242 ++++ lvgl_touch/stmpe610.h | 185 +++ lvgl_touch/touch_driver.c | 47 + lvgl_touch/touch_driver.h | 52 + lvgl_touch/tp_i2c.c | 43 + lvgl_touch/tp_i2c.h | 36 + lvgl_touch/tp_spi.c | 109 ++ lvgl_touch/tp_spi.h | 45 + lvgl_touch/xpt2046.c | 180 +++ lvgl_touch/xpt2046.h | 57 + 69 files changed, 14977 insertions(+), 2 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 component.mk create mode 100644 lvgl_helpers.c create mode 100644 lvgl_helpers.h create mode 100644 lvgl_i2c_conf.h create mode 100644 lvgl_spi_conf.h create mode 100644 lvgl_tft/CMakeLists.txt create mode 100644 lvgl_tft/EVE.h create mode 100644 lvgl_tft/EVE_commands.c create mode 100644 lvgl_tft/EVE_commands.h create mode 100644 lvgl_tft/EVE_config.h create mode 100644 lvgl_tft/FT81x.c create mode 100644 lvgl_tft/FT81x.h create mode 100644 lvgl_tft/GC9A01.c create mode 100644 lvgl_tft/GC9A01.h create mode 100644 lvgl_tft/Kconfig create mode 100644 lvgl_tft/component.mk create mode 100644 lvgl_tft/disp_driver.c create mode 100644 lvgl_tft/disp_driver.h create mode 100644 lvgl_tft/disp_spi.c create mode 100644 lvgl_tft/disp_spi.h create mode 100644 lvgl_tft/hx8357.c create mode 100644 lvgl_tft/hx8357.h create mode 100644 lvgl_tft/il3820.c create mode 100644 lvgl_tft/il3820.h create mode 100644 lvgl_tft/ili9341.c create mode 100644 lvgl_tft/ili9341.h create mode 100644 lvgl_tft/ili9481.c create mode 100644 lvgl_tft/ili9481.h create mode 100644 lvgl_tft/ili9486.c create mode 100644 lvgl_tft/ili9486.h create mode 100644 lvgl_tft/ili9488.c create mode 100644 lvgl_tft/ili9488.h create mode 100644 lvgl_tft/jd79653a.c create mode 100644 lvgl_tft/jd79653a.h create mode 100644 lvgl_tft/ra8875.c create mode 100644 lvgl_tft/ra8875.h create mode 100644 lvgl_tft/sh1107.c create mode 100644 lvgl_tft/sh1107.h create mode 100644 lvgl_tft/ssd1306.c create mode 100644 lvgl_tft/ssd1306.h create mode 100644 lvgl_tft/st7735s.c create mode 100644 lvgl_tft/st7735s.h create mode 100644 lvgl_tft/st7789.c create mode 100644 lvgl_tft/st7789.h create mode 100644 lvgl_tft/uc8151d.c create mode 100644 lvgl_tft/uc8151d.h create mode 100644 lvgl_touch/CMakeLists.txt create mode 100644 lvgl_touch/FT81x.c create mode 100644 lvgl_touch/FT81x.h create mode 100644 lvgl_touch/Kconfig create mode 100644 lvgl_touch/adcraw.c create mode 100644 lvgl_touch/adcraw.h create mode 100644 lvgl_touch/component.mk create mode 100644 lvgl_touch/ft6x36.c create mode 100644 lvgl_touch/ft6x36.h create mode 100644 lvgl_touch/ra8875_touch.c create mode 100644 lvgl_touch/ra8875_touch.h create mode 100644 lvgl_touch/stmpe610.c create mode 100644 lvgl_touch/stmpe610.h create mode 100644 lvgl_touch/touch_driver.c create mode 100644 lvgl_touch/touch_driver.h create mode 100644 lvgl_touch/tp_i2c.c create mode 100644 lvgl_touch/tp_i2c.h create mode 100644 lvgl_touch/tp_spi.c create mode 100644 lvgl_touch/tp_spi.h create mode 100644 lvgl_touch/xpt2046.c create mode 100644 lvgl_touch/xpt2046.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ff54b6a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +if(ESP_PLATFORM) + +file(GLOB SOURCES *.c) + +idf_component_register(SRCS ${SOURCES} + INCLUDE_DIRS . + REQUIRES lvgl) + +endif() diff --git a/README.md b/README.md index 74ef6f9..0c61046 100644 --- a/README.md +++ b/README.md @@ -1,2 +1 @@ -# lvgl_esp32_drivers -Drivers for ESP32 +# lvgl esp32 drivers diff --git a/component.mk b/component.mk new file mode 100644 index 0000000..4befe8e --- /dev/null +++ b/component.mk @@ -0,0 +1,4 @@ +# LVGL esp32 drivers + +COMPONENT_SRCDIRS := . +COMPONENT_ADD_INCLUDEDIRS := . diff --git a/lvgl_helpers.c b/lvgl_helpers.c new file mode 100644 index 0000000..8d443d0 --- /dev/null +++ b/lvgl_helpers.c @@ -0,0 +1,236 @@ +/** + * @file lvgl_helpers.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "sdkconfig.h" +#include "lvgl_helpers.h" +#include "esp_log.h" + +#include "lvgl_tft/disp_spi.h" +#include "lvgl_touch/tp_spi.h" + +#include "lvgl_spi_conf.h" +#include "lvgl_i2c_conf.h" + +#include "driver/i2c.h" + +#include "lvgl/src/lv_core/lv_refr.h" + +/********************* + * DEFINES + *********************/ + + #define TAG "lvgl_helpers" + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/* Interface and driver initialization */ +void lvgl_driver_init(void) +{ + ESP_LOGI(TAG, "Display hor size: %d, ver size: %d", LV_HOR_RES_MAX, LV_VER_RES_MAX); + ESP_LOGI(TAG, "Display buffer size: %d", DISP_BUF_SIZE); + +#if defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) + ESP_LOGI(TAG, "Initializing SPI master for FT81X"); + + lvgl_spi_driver_init(TFT_SPI_HOST, + DISP_SPI_MISO, DISP_SPI_MOSI, DISP_SPI_CLK, + SPI_BUS_MAX_TRANSFER_SZ, 1, + DISP_SPI_IO2, DISP_SPI_IO3); + + disp_spi_add_device(TFT_SPI_HOST); + disp_driver_init(); + +#if defined (CONFIG_LV_TOUCH_CONTROLLER_FT81X) + touch_driver_init(); +#endif + + return; +#endif + +#if defined (SHARED_SPI_BUS) + ESP_LOGI(TAG, "Initializing shared SPI master"); + + lvgl_spi_driver_init(TFT_SPI_HOST, + TP_SPI_MISO, DISP_SPI_MOSI, DISP_SPI_CLK, + SPI_BUS_MAX_TRANSFER_SZ, 1, + -1, -1); + + disp_spi_add_device(TFT_SPI_HOST); + tp_spi_add_device(TOUCH_SPI_HOST); + + disp_driver_init(); + touch_driver_init(); + + return; +#endif + +#if defined (SHARED_I2C_BUS) + ESP_LOGI(TAG, "Initializing shared I2C master"); + + lvgl_i2c_driver_init(DISP_I2C_PORT, + DISP_I2C_SDA, DISP_I2C_SCL, + DISP_I2C_SPEED_HZ); + + disp_driver_init(); + touch_driver_init(); + + return; +#endif + +/* Display controller initialization */ +#if defined CONFIG_LV_TFT_DISPLAY_PROTOCOL_SPI + ESP_LOGI(TAG, "Initializing SPI master for display"); + + lvgl_spi_driver_init(TFT_SPI_HOST, + DISP_SPI_MISO, DISP_SPI_MOSI, DISP_SPI_CLK, + SPI_BUS_MAX_TRANSFER_SZ, 1, + DISP_SPI_IO2, DISP_SPI_IO3); + + disp_spi_add_device(TFT_SPI_HOST); + + disp_driver_init(); +#elif defined (CONFIG_LV_TFT_DISPLAY_PROTOCOL_I2C) + ESP_LOGI(TAG, "Initializing I2C master for display"); + /* Init the i2c master on the display driver code */ + lvgl_i2c_driver_init(DISP_I2C_PORT, + DISP_I2C_SDA, DISP_I2C_SCL, + DISP_I2C_SPEED_HZ); + + disp_driver_init(); +#else +#error "No protocol defined for display controller" +#endif + +/* Touch controller initialization */ +#if CONFIG_LV_TOUCH_CONTROLLER != TOUCH_CONTROLLER_NONE + #if defined (CONFIG_LV_TOUCH_DRIVER_PROTOCOL_SPI) + ESP_LOGI(TAG, "Initializing SPI master for touch"); + + lvgl_spi_driver_init(TOUCH_SPI_HOST, + TP_SPI_MISO, TP_SPI_MOSI, TP_SPI_CLK, + 0 /* Defaults to 4094 */, 2, + -1, -1); + + tp_spi_add_device(TOUCH_SPI_HOST); + + touch_driver_init(); + #elif defined (CONFIG_LV_TOUCH_DRIVER_PROTOCOL_I2C) + ESP_LOGI(TAG, "Initializing I2C master for touch"); + + lvgl_i2c_driver_init(TOUCH_I2C_PORT, + TOUCH_I2C_SDA, TOUCH_I2C_SCL, + TOUCH_I2C_SPEED_HZ); + + touch_driver_init(); + #elif defined (CONFIG_LV_TOUCH_DRIVER_ADC) + touch_driver_init(); + #elif defined (CONFIG_LV_TOUCH_DRIVER_DISPLAY) + touch_driver_init(); + #else + #error "No protocol defined for touch controller" + #endif +#else +#endif +} + +/* Config the i2c master + * + * This should init the i2c master to be used on display and touch controllers. + * So we should be able to know if the display and touch controllers shares the + * same i2c master. + */ +bool lvgl_i2c_driver_init(int port, int sda_pin, int scl_pin, int speed_hz) +{ + esp_err_t err; + + ESP_LOGI(TAG, "Initializing I2C master port %d...", port); + ESP_LOGI(TAG, "SDA pin: %d, SCL pin: %d, Speed: %d (Hz)", + sda_pin, scl_pin, speed_hz); + + i2c_config_t conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = sda_pin, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_io_num = scl_pin, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = speed_hz, + }; + + ESP_LOGI(TAG, "Setting I2C master configuration..."); + err = i2c_param_config(port, &conf); + assert(ESP_OK == err); + + ESP_LOGI(TAG, "Installing I2C master driver..."); + err = i2c_driver_install(port, + I2C_MODE_MASTER, + 0, 0 /*I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE */, + 0 /* intr_alloc_flags */); + assert(ESP_OK == err); + + return ESP_OK != err; +} + +/* Initialize spi bus master */ +bool lvgl_spi_driver_init(int host, + int miso_pin, int mosi_pin, int sclk_pin, + int max_transfer_sz, + int dma_channel, + int quadwp_pin, int quadhd_pin) +{ +#if defined (CONFIG_IDF_TARGET_ESP32) + assert((SPI_HOST <= host) && (VSPI_HOST >= host)); + const char *spi_names[] = { + "SPI_HOST", "HSPI_HOST", "VSPI_HOST" + }; +#elif defined (CONFIG_IDF_TARGET_ESP32S2) + assert((SPI_HOST <= host) && (HSPI_HOST >= host)); + const char *spi_names[] = { + "SPI_HOST", "", "" + }; +#endif + + ESP_LOGI(TAG, "Configuring SPI host %s (%d)", spi_names[host], host); + ESP_LOGI(TAG, "MISO pin: %d, MOSI pin: %d, SCLK pin: %d, IO2/WP pin: %d, IO3/HD pin: %d", + miso_pin, mosi_pin, sclk_pin, quadwp_pin, quadhd_pin); + + ESP_LOGI(TAG, "Max transfer size: %d (bytes)", max_transfer_sz); + + spi_bus_config_t buscfg = { + .miso_io_num = miso_pin, + .mosi_io_num = mosi_pin, + .sclk_io_num = sclk_pin, + .quadwp_io_num = quadwp_pin, + .quadhd_io_num = quadhd_pin, + .max_transfer_sz = max_transfer_sz + }; + + ESP_LOGI(TAG, "Initializing SPI bus..."); + esp_err_t ret = spi_bus_initialize(host, &buscfg, dma_channel); + assert(ret == ESP_OK); + + return ESP_OK != ret; +} + diff --git a/lvgl_helpers.h b/lvgl_helpers.h new file mode 100644 index 0000000..54b4566 --- /dev/null +++ b/lvgl_helpers.h @@ -0,0 +1,85 @@ +/** + * @file lvgl_helpers.h + */ + +#ifndef LVGL_HELPERS_H +#define LVGL_HELPERS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include + +#include "lvgl_spi_conf.h" +#include "lvgl_tft/disp_driver.h" +#include "lvgl_touch/touch_driver.h" + +/********************* + * DEFINES + *********************/ +#if defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7789) +#define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40) +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7735S +#define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40) +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_HX8357 +#define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40) +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SH1107 +#define DISP_BUF_SIZE (CONFIG_LV_DISPLAY_WIDTH*CONFIG_LV_DISPLAY_HEIGHT) +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9481 +#define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40) +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9486 +#define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40) +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9488 +#define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40) +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9341 +#define DISP_BUF_SIZE (LV_HOR_RES_MAX * 64) +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306 +#define DISP_BUF_SIZE (CONFIG_LV_DISPLAY_WIDTH*CONFIG_LV_DISPLAY_HEIGHT) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) +#define DISP_BUF_LINES 40 +#define DISP_BUF_SIZE (LV_HOR_RES_MAX * DISP_BUF_LINES) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820) +#define DISP_BUF_SIZE (CONFIG_LV_DISPLAY_HEIGHT * IL3820_COLUMNS) +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875 +#define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01) +#define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A) +#define DISP_BUF_SIZE ((CONFIG_LV_DISPLAY_HEIGHT * CONFIG_LV_DISPLAY_WIDTH) / 8) // 5KB +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D) +#define DISP_BUF_SIZE ((CONFIG_LV_DISPLAY_HEIGHT * CONFIG_LV_DISPLAY_WIDTH) / 8) // 2888 bytes +#else +#error "No display controller selected" +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/* Initialize detected SPI and I2C bus and devices */ +void lvgl_driver_init(void); + +/* Initialize SPI master */ +bool lvgl_spi_driver_init(int host, int miso_pin, int mosi_pin, int sclk_pin, + int max_transfer_sz, int dma_channel, int quadwp_pin, int quadhd_pin); +/* Initialize I2C master */ +bool lvgl_i2c_driver_init(int port, int sda_pin, int scl_pin, int speed); + +/********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LVGL_HELPERS_H */ diff --git a/lvgl_i2c_conf.h b/lvgl_i2c_conf.h new file mode 100644 index 0000000..cf8ac45 --- /dev/null +++ b/lvgl_i2c_conf.h @@ -0,0 +1,116 @@ +/** + * @file lvgl_i2c_config.h + */ + +#ifndef LVGL_I2C_CONF_H +#define LVGL_I2C_CONF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +/* TODO: Define the I2C bus clock based on the selected display or touch + * controllers. */ + +/* Do both display and touch controllers uses I2C? */ +#if defined (CONFIG_LV_TOUCH_DRIVER_PROTOCOL_I2C) && \ + defined (CONFIG_LV_TFT_DISPLAY_PROTOCOL_I2C) + +#if defined (CONFIG_LV_DISPLAY_I2C_PORT_0) && \ + defined (CONFIG_LV_TOUCH_I2C_PORT_0) +#define SHARED_I2C_PORT +#define DISP_I2C_PORT I2C_NUM_0 +#endif + +#if defined (CONFIG_LV_DISPLAY_I2C_PORT_1) && \ + defined (CONFIG_LV_TOUCH_I2C_PORT_1) +#define SHARED_I2C_PORT +#define DISP_I2C_PORT I2C_NUM_1 +#endif + +#if !defined (SHARED_I2C_PORT) +#endif +#endif + +#if defined (SHARED_I2C_PORT) +/* If the port is shared the display and touch controllers must use the same + * SCL and SDA pins, otherwise let the user know with an error. */ +#if (CONFIG_LV_DISP_PIN_SDA != CONFIG_LV_TOUCH_I2C_SDA) || \ + (CONFIG_LV_DISP_PIN_SCL != CONFIG_LV_TOUCH_I2C_SCL) +#error "To share I2C port you need to choose the same SDA and SCL pins on both display and touch configurations" +#endif + +#define DISP_I2C_SDA CONFIG_LV_DISP_PIN_SDA +#define DISP_I2C_SCL CONFIG_LV_DISP_PIN_SCL +#define DISP_I2C_ORIENTATION TFT_ORIENTATION_LANDSCAPE + +/* Setting the I2C speed to the slowest one */ +#if DISP_I2C_SPEED_HZ < TOUCH_I2C_SPEED_HZ +#define DISP_I2C_SPEED_HZ 400000 /* DISP_I2C_SPEED_HZ */ +#else +#define DISP_I2C_SPEED_HZ 400000 /* DISP_I2C_SPEED_HZ */ +#endif + +#else + +/* lets check if the touch controller uses I2C... */ +#if defined (CONFIG_LV_TOUCH_DRIVER_PROTOCOL_I2C) +#if defined (CONFIG_LV_TOUCH_I2C_PORT_0) +#define TOUCH_I2C_PORT I2C_NUM_0 +#else +#define TOUCH_I2C_PORT I2C_NUM_1 +#endif +#define TOUCH_I2C_SDA CONFIG_LV_TOUCH_I2C_SDA +#define TOUCH_I2C_SCL CONFIG_LV_TOUCH_I2C_SCL +#define TOUCH_I2C_SPEED_HZ 400000 +#endif + +/* lets check if the display controller uses I2C... */ +#if defined (CONFIG_LV_TFT_DISPLAY_PROTOCOL_I2C) +#if defined (CONFIG_LV_DISPLAY_I2C_PORT_0) +#define DISP_I2C_PORT I2C_NUM_0 +#else +#define DISP_I2C_PORT I2C_NUM_1 +#endif + +#define DISP_I2C_SDA CONFIG_LV_DISP_PIN_SDA +#define DISP_I2C_SCL CONFIG_LV_DISP_PIN_SCL +#define DISP_I2C_ORIENTATION TFT_ORIENTATION_LANDSCAPE +#define DISP_I2C_SPEED_HZ 400000 +#endif + +#endif + +/********************** + * TYPEDEFS + **********************/ + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + + +/********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LVGL_I2C_CONF_H*/ diff --git a/lvgl_spi_conf.h b/lvgl_spi_conf.h new file mode 100644 index 0000000..16e63b4 --- /dev/null +++ b/lvgl_spi_conf.h @@ -0,0 +1,198 @@ +/** + * @file lvgl_spi_conf.h + * + */ + +#ifndef LVGL_SPI_CONF_H +#define LVGL_SPI_CONF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ +// DISPLAY PINS +#define DISP_SPI_MOSI CONFIG_LV_DISP_SPI_MOSI +#if defined (CONFIG_LV_DISPLAY_USE_SPI_MISO) + #define DISP_SPI_MISO CONFIG_LV_DISP_SPI_MISO + #define DISP_SPI_INPUT_DELAY_NS CONFIG_LV_DISP_SPI_INPUT_DELAY_NS +#else + #define DISP_SPI_MISO (-1) + #define DISP_SPI_INPUT_DELAY_NS (0) +#endif +#if defined(CONFIG_LV_DISP_SPI_IO2) +#define DISP_SPI_IO2 CONFIG_LV_DISP_SPI_IO2 +#else +#define DISP_SPI_IO2 (-1) +#endif +#if defined(CONFIG_LV_DISP_SPI_IO3) +#define DISP_SPI_IO3 CONFIG_LV_DISP_SPI_IO3 +#else +#define DISP_SPI_IO3 (-1) +#endif +#define DISP_SPI_CLK CONFIG_LV_DISP_SPI_CLK +#if defined (CONFIG_LV_DISPLAY_USE_SPI_CS) + #define DISP_SPI_CS CONFIG_LV_DISP_SPI_CS +#else + #define DISP_SPI_CS (-1) +#endif + +/* Define TOUCHPAD PINS when selecting a touch controller */ +#if !defined (CONFIG_LV_TOUCH_CONTROLLER_NONE) + +/* Handle FT81X special case */ +#if defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) && \ + defined (CONFIG_LV_TOUCH_CONTROLLER_FT81X) + + #define TP_SPI_MOSI CONFIG_LV_DISP_SPI_MOSI + #define TP_SPI_MISO CONFIG_LV_DISP_SPI_MISO + #define TP_SPI_CLK CONFIG_LV_DISP_SPI_CLK + #define TP_SPI_CS CONFIG_LV_DISP_SPI_CS +#else + #define TP_SPI_MOSI CONFIG_LV_TOUCH_SPI_MOSI + #define TP_SPI_MISO CONFIG_LV_TOUCH_SPI_MISO + #define TP_SPI_CLK CONFIG_LV_TOUCH_SPI_CLK + #define TP_SPI_CS CONFIG_LV_TOUCH_SPI_CS +#endif +#endif + +#define ENABLE_TOUCH_INPUT CONFIG_LV_ENABLE_TOUCH + +#if defined (CONFIG_LV_TFT_DISPLAY_SPI_HSPI) +#define TFT_SPI_HOST HSPI_HOST +#elif defined (CONFIG_LV_TFT_DISPLAY_SPI_VSPI) +#define TFT_SPI_HOST VSPI_HOST +#elif defined (CONFIG_LV_TFT_DISPLAY_SPI_FSPI) +#define TFT_SPI_HOST FSPI_HOST +#endif + +#if defined (CONFIG_LV_TFT_DISPLAY_SPI_HALF_DUPLEX) +#define DISP_SPI_HALF_DUPLEX +#else +#define DISP_SPI_FULL_DUPLEX +#endif + +#if defined (CONFIG_LV_TFT_DISPLAY_SPI_TRANS_MODE_DIO) +#define DISP_SPI_TRANS_MODE_DIO +#elif defined (CONFIG_LV_TFT_DISPLAY_SPI_TRANS_MODE_QIO) +#define DISP_SPI_TRANS_MODE_QIO +#else +#define DISP_SPI_TRANS_MODE_SIO +#endif + +#if defined (CONFIG_LV_TOUCH_CONTROLLER_SPI_HSPI) +#define TOUCH_SPI_HOST HSPI_HOST +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_SPI_VSPI) +#define TOUCH_SPI_HOST VSPI_HOST +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_SPI_FSPI) +#define TOUCH_SPI_HOST FSPI_HOST +#endif + +/* Handle the FT81X Special case */ +#if defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) + +#if defined (CONFIG_LV_TOUCH_CONTROLLER_FT81X) +#define SHARED_SPI_BUS +#else +/* Empty */ +#endif + +#else +// Detect the use of a shared SPI Bus and verify the user specified the same SPI bus for both touch and tft +#if defined (CONFIG_LV_TOUCH_DRIVER_PROTOCOL_SPI) && TP_SPI_MOSI == DISP_SPI_MOSI && TP_SPI_CLK == DISP_SPI_CLK +#if TFT_SPI_HOST != TOUCH_SPI_HOST +#error You must specify the same SPI host (HSPI, VSPI or FSPI) for both display and touch driver +#endif + +#define SHARED_SPI_BUS +#endif + +#endif + +/********************** + * TYPEDEFS + **********************/ +#if defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9481) || \ + defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9488) + +#define SPI_BUS_MAX_TRANSFER_SZ (DISP_BUF_SIZE * 3) + +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9341) || \ + defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7789) || \ + defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7735S) || \ + defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_HX8357) || \ + defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_SH1107) || \ + defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) || \ + defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820) || \ + defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A) + +#define SPI_BUS_MAX_TRANSFER_SZ (DISP_BUF_SIZE * 2) + +#else +#define SPI_BUS_MAX_TRANSFER_SZ (DISP_BUF_SIZE * 2) +#endif + +#if defined (CONFIG_LV_TFT_USE_CUSTOM_SPI_CLK_DIVIDER) +#define SPI_TFT_CLOCK_SPEED_HZ ((80 * 1000 * 1000) / CONFIG_LV_TFT_CUSTOM_SPI_CLK_DIVIDER) +#else +#if defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7789) +#define SPI_TFT_CLOCK_SPEED_HZ (20*1000*1000) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7735S) +#define SPI_TFT_CLOCK_SPEED_HZ (40*1000*1000) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_HX8357) +#define SPI_TFT_CLOCK_SPEED_HZ (26*1000*1000) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_SH1107) +#define SPI_TFT_CLOCK_SPEED_HZ (8*1000*1000) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9481) +#define SPI_TFT_CLOCK_SPEED_HZ (16*1000*1000) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9486) +#define SPI_TFT_CLOCK_SPEED_HZ (20*1000*1000) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9488) +#define SPI_TFT_CLOCK_SPEED_HZ (40*1000*1000) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9341) +#define SPI_TFT_CLOCK_SPEED_HZ (40*1000*1000) +#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) +#define SPI_TFT_CLOCK_SPEED_HZ (32*1000*1000) +#else +#define SPI_TFT_CLOCK_SPEED_HZ (40*1000*1000) +#endif + +#endif + + +#if defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7789) +#define SPI_TFT_SPI_MODE (2) +#else +#define SPI_TFT_SPI_MODE (0) +#endif + +/* Touch driver */ +#if (CONFIG_LV_TOUCH_CONTROLLER == TOUCH_CONTROLLER_STMPE610) +#define SPI_TOUCH_CLOCK_SPEED_HZ (1*1000*1000) +#define SPI_TOUCH_SPI_MODE (1) +#else +#define SPI_TOUCH_CLOCK_SPEED_HZ (2*1000*1000) +#define SPI_TOUCH_SPI_MODE (0) +#endif + +/********************** + * GLOBAL PROTOTYPES + **********************/ + + +/********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LVGL_SPI_CONF_H*/ diff --git a/lvgl_tft/CMakeLists.txt b/lvgl_tft/CMakeLists.txt new file mode 100644 index 0000000..d52f35f --- /dev/null +++ b/lvgl_tft/CMakeLists.txt @@ -0,0 +1,53 @@ +if(ESP_PLATFORM) + +set(SOURCES "disp_driver.c") + +# Include only the source file of the selected +# display controller. +if(CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9341) + list(APPEND SOURCES "ili9341.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9481) + list(APPEND SOURCES "ili9481.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9486) + list(APPEND SOURCES "ili9486.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9488) + list(APPEND SOURCES "ili9488.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7789) + list(APPEND SOURCES "st7789.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7735S) + list(APPEND SOURCES "st7735s.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_HX8357) + list(APPEND SOURCES "hx8357.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_SH1107) + list(APPEND SOURCES "sh1107.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306) + list(APPEND SOURCES "ssd1306.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) + list(APPEND SOURCES "EVE_commands.c") + list(APPEND SOURCES "FT81x.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820) + list(APPEND SOURCES "il3820.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A) + list(APPEND SOURCES "jd79653a.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D) + list(APPEND SOURCES "uc8151d.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875) + list(APPEND SOURCES "ra8875.c") +elseif(CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01) + list(APPEND SOURCES "GC9A01.c") +else() + message("DISPLAY CONTROLLER NOT DEFINED") +endif() + +if(CONFIG_LV_TFT_DISPLAY_PROTOCOL_SPI) + list(APPEND SOURCES "disp_spi.c") +endif() + +# Print the included source files +message("SOURCES contents: " "${SOURCES}") + +idf_component_register(SRCS ${SOURCES} + INCLUDE_DIRS . + REQUIRES lvgl) + +endif() diff --git a/lvgl_tft/EVE.h b/lvgl_tft/EVE.h new file mode 100644 index 0000000..2f03dee --- /dev/null +++ b/lvgl_tft/EVE.h @@ -0,0 +1,842 @@ +/* +@file EVE.h +@brief Contains FT80x/FT81x/BT81x API definitions +@version 4.1 LvGL edition +@date 2020-04-15 +@author Rudolph Riedel, David Jade + +@section LICENSE + +MIT License + +Copyright (c) 2016-2020 Rudolph Riedel and David Jade + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +@section History + +4.1 LvGL edition + + - This version is a heavily modified version of the MIT licensed FT81x code from https://github.com/RudolphRiedel/FT800-FT813 + This version is based on a fork by David Jade that added native SPI DMA support and stripped out non-ESP32 code. + It has also been trimmed down to suit LvGL's needs. Extra features can be enabled by defining FT81X_FULL +*/ + +#if defined (ESP_PLATFORM) +#include +#include +#endif + +#include "EVE_config.h" + +#ifndef EVE_H_ +#define EVE_H_ + + +#define DL_CLEAR 0x26000000UL /* requires OR'd arguments */ +#define DL_CLEAR_RGB 0x02000000UL /* requires OR'd arguments */ +#define DL_COLOR_RGB 0x04000000UL /* requires OR'd arguments */ +#define DL_POINT_SIZE 0x0D000000UL /* requires OR'd arguments */ +#define DL_END 0x21000000UL +#define DL_BEGIN 0x1F000000UL /* requires OR'd arguments */ +#define DL_DISPLAY 0x00000000UL + +#define CLR_COL 0x4 +#define CLR_STN 0x2 +#define CLR_TAG 0x1 + +/* SPI SIO/DIO/QIO tranfer widths */ +#define SPI_WIDTH_SIO 0x0 +#define SPI_WIDTH_DIO 0x1 +#define SPI_WIDTH_QIO 0x2 + + +/* Host commands */ +#define EVE_ACTIVE 0x00 /* place FT8xx in active state */ +#define EVE_STANDBY 0x41 /* place FT8xx in Standby (clk running) */ +#define EVE_SLEEP 0x42 /* place FT8xx in Sleep (clk off) */ +#define EVE_PWRDOWN 0x50 /* place FT8xx in Power Down (core off) */ +#define EVE_CLKEXT 0x44 /* select external clock source */ +#define EVE_CLKINT 0x48 /* select internal clock source */ +#define EVE_CORERST 0x68 /* reset core - all registers default and processors reset */ +#define EVE_CLK48M 0x62 /* select 48MHz PLL output */ +#define EVE_CLK36M 0x61 /* select 36MHz PLL output */ + + +/* defines used for graphics commands */ +#define EVE_NEVER 0UL +#define EVE_LESS 1UL +#define EVE_LEQUAL 2UL +#define EVE_GREATER 3UL +#define EVE_GEQUAL 4UL +#define EVE_EQUAL 5UL +#define EVE_NOTEQUAL 6UL +#define EVE_ALWAYS 7UL + + +/* Bitmap formats */ +#define EVE_ARGB1555 0UL +#define EVE_L1 1UL +#define EVE_L4 2UL +#define EVE_L8 3UL +#define EVE_RGB332 4UL +#define EVE_ARGB2 5UL +#define EVE_ARGB4 6UL +#define EVE_RGB565 7UL +#define EVE_PALETTED 8UL +#define EVE_TEXT8X8 9UL +#define EVE_TEXTVGA 10UL +#define EVE_BARGRAPH 11UL + + +/* Bitmap filter types */ +#define EVE_NEAREST 0UL +#define EVE_BILINEAR 1UL + + +/* Bitmap wrap types */ +#define EVE_BORDER 0UL +#define EVE_REPEAT 1UL + + +/* Stencil defines */ +#define EVE_KEEP 1UL +#define EVE_REPLACE 2UL +#define EVE_INCR 3UL +#define EVE_DECR 4UL +#define EVE_INVERT 5UL + + +/* Graphics display list swap defines */ +#define EVE_DLSWAP_DONE 0UL +#define EVE_DLSWAP_LINE 1UL +#define EVE_DLSWAP_FRAME 2UL + + +/* Interrupt bits */ +#define EVE_INT_SWAP 0x01 +#define EVE_INT_TOUCH 0x02 +#define EVE_INT_TAG 0x04 +#define EVE_INT_SOUND 0x08 +#define EVE_INT_PLAYBACK 0x10 +#define EVE_INT_CMDEMPTY 0x20 +#define EVE_INT_CMDFLAG 0x40 +#define EVE_INT_CONVCOMPLETE 0x80 + + +/* Touch mode */ +#define EVE_TMODE_OFF 0 +#define EVE_TMODE_ONESHOT 1 +#define EVE_TMODE_FRAME 2 +#define EVE_TMODE_CONTINUOUS 3 + + +/* Alpha blending */ +#define EVE_ZERO 0UL +#define EVE_ONE 1UL +#define EVE_SRC_ALPHA 2UL +#define EVE_DST_ALPHA 3UL +#define EVE_ONE_MINUS_SRC_ALPHA 4UL +#define EVE_ONE_MINUS_DST_ALPHA 5UL + + +/* Graphics primitives */ +#define EVE_BITMAPS 1UL +#define EVE_POINTS 2UL +#define EVE_LINES 3UL +#define EVE_LINE_STRIP 4UL +#define EVE_EDGE_STRIP_R 5UL +#define EVE_EDGE_STRIP_L 6UL +#define EVE_EDGE_STRIP_A 7UL +#define EVE_EDGE_STRIP_B 8UL +#define EVE_RECTS 9UL + + +/* Widget command */ +#define EVE_OPT_MONO 1 +#define EVE_OPT_NODL 2 +#define EVE_OPT_FLAT 256 +#define EVE_OPT_CENTERX 512 +#define EVE_OPT_CENTERY 1024 +#define EVE_OPT_CENTER (EVE_OPT_CENTERX | EVE_OPT_CENTERY) +#define EVE_OPT_NOBACK 4096 +#define EVE_OPT_NOTICKS 8192 +#define EVE_OPT_NOHM 16384 +#define EVE_OPT_NOPOINTER 16384 +#define EVE_OPT_NOSECS 32768 +#define EVE_OPT_NOHANDS 49152 +#define EVE_OPT_RIGHTX 2048 +#define EVE_OPT_SIGNED 256 + + +/* Defines related to inbuilt font */ +#define EVE_NUMCHAR_PERFONT (128L) /* number of font characters per bitmap handle */ +#define EVE_FONT_TABLE_SIZE (148L) /* size of the font table - utilized for loopup by the graphics engine */ +#define EVE_FONT_TABLE_POINTER (0xFFFFCUL) /* pointer to the inbuilt font tables starting from bitmap handle 16 */ + + +/* Audio sample type defines */ +#define EVE_LINEAR_SAMPLES 0UL /* 8bit signed samples */ +#define EVE_ULAW_SAMPLES 1UL /* 8bit ulaw samples */ +#define EVE_ADPCM_SAMPLES 2UL /* 4bit ima adpcm samples */ + + +/* Synthesized sound */ +#define EVE_SILENCE 0x00 +#define EVE_SQUAREWAVE 0x01 +#define EVE_SINEWAVE 0x02 +#define EVE_SAWTOOTH 0x03 +#define EVE_TRIANGLE 0x04 +#define EVE_BEEPING 0x05 +#define EVE_ALARM 0x06 +#define EVE_WARBLE 0x07 +#define EVE_CAROUSEL 0x08 +#define EVE_PIPS(n) (0x0F + (n)) +#define EVE_HARP 0x40 +#define EVE_XYLOPHONE 0x41 +#define EVE_TUBA 0x42 +#define EVE_GLOCKENSPIEL 0x43 +#define EVE_ORGAN 0x44 +#define EVE_TRUMPET 0x45 +#define EVE_PIANO 0x46 +#define EVE_CHIMES 0x47 +#define EVE_MUSICBOX 0x48 +#define EVE_BELL 0x49 +#define EVE_CLICK 0x50 +#define EVE_SWITCH 0x51 +#define EVE_COWBELL 0x52 +#define EVE_NOTCH 0x53 +#define EVE_HIHAT 0x54 +#define EVE_KICKDRUM 0x55 +#define EVE_POP 0x56 +#define EVE_CLACK 0x57 +#define EVE_CHACK 0x58 +#define EVE_MUTE 0x60 +#define EVE_UNMUTE 0x61 + + +/* Synthesized sound frequencies, midi note */ +#define EVE_MIDI_A0 21 +#define EVE_MIDI_A_0 22 +#define EVE_MIDI_B0 23 +#define EVE_MIDI_C1 24 +#define EVE_MIDI_C_1 25 +#define EVE_MIDI_D1 26 +#define EVE_MIDI_D_1 27 +#define EVE_MIDI_E1 28 +#define EVE_MIDI_F1 29 +#define EVE_MIDI_F_1 30 +#define EVE_MIDI_G1 31 +#define EVE_MIDI_G_1 32 +#define EVE_MIDI_A1 33 +#define EVE_MIDI_A_1 34 +#define EVE_MIDI_B1 35 +#define EVE_MIDI_C2 36 +#define EVE_MIDI_C_2 37 +#define EVE_MIDI_D2 38 +#define EVE_MIDI_D_2 39 +#define EVE_MIDI_E2 40 +#define EVE_MIDI_F2 41 +#define EVE_MIDI_F_2 42 +#define EVE_MIDI_G2 43 +#define EVE_MIDI_G_2 44 +#define EVE_MIDI_A2 45 +#define EVE_MIDI_A_2 46 +#define EVE_MIDI_B2 47 +#define EVE_MIDI_C3 48 +#define EVE_MIDI_C_3 49 +#define EVE_MIDI_D3 50 +#define EVE_MIDI_D_3 51 +#define EVE_MIDI_E3 52 +#define EVE_MIDI_F3 53 +#define EVE_MIDI_F_3 54 +#define EVE_MIDI_G3 55 +#define EVE_MIDI_G_3 56 +#define EVE_MIDI_A3 57 +#define EVE_MIDI_A_3 58 +#define EVE_MIDI_B3 59 +#define EVE_MIDI_C4 60 +#define EVE_MIDI_C_4 61 +#define EVE_MIDI_D4 62 +#define EVE_MIDI_D_4 63 +#define EVE_MIDI_E4 64 +#define EVE_MIDI_F4 65 +#define EVE_MIDI_F_4 66 +#define EVE_MIDI_G4 67 +#define EVE_MIDI_G_4 68 +#define EVE_MIDI_A4 69 +#define EVE_MIDI_A_4 70 +#define EVE_MIDI_B4 71 +#define EVE_MIDI_C5 72 +#define EVE_MIDI_C_5 73 +#define EVE_MIDI_D5 74 +#define EVE_MIDI_D_5 75 +#define EVE_MIDI_E5 76 +#define EVE_MIDI_F5 77 +#define EVE_MIDI_F_5 78 +#define EVE_MIDI_G5 79 +#define EVE_MIDI_G_5 80 +#define EVE_MIDI_A5 81 +#define EVE_MIDI_A_5 82 +#define EVE_MIDI_B5 83 +#define EVE_MIDI_C6 84 +#define EVE_MIDI_C_6 85 +#define EVE_MIDI_D6 86 +#define EVE_MIDI_D_6 87 +#define EVE_MIDI_E6 88 +#define EVE_MIDI_F6 89 +#define EVE_MIDI_F_6 90 +#define EVE_MIDI_G6 91 +#define EVE_MIDI_G_6 92 +#define EVE_MIDI_A6 93 +#define EVE_MIDI_A_6 94 +#define EVE_MIDI_B6 95 +#define EVE_MIDI_C7 96 +#define EVE_MIDI_C_7 97 +#define EVE_MIDI_D7 98 +#define EVE_MIDI_D_7 99 +#define EVE_MIDI_E7 100 +#define EVE_MIDI_F7 101 +#define EVE_MIDI_F_7 102 +#define EVE_MIDI_G7 103 +#define EVE_MIDI_G_7 104 +#define EVE_MIDI_A7 105 +#define EVE_MIDI_A_7 106 +#define EVE_MIDI_B7 107 +#define EVE_MIDI_C8 108 + + +/* GPIO bits */ +#define EVE_GPIO0 0 +#define EVE_GPIO1 1 /* default gpio pin for audio shutdown, 1 - enable, 0 - disable */ +#define EVE_GPIO7 7 /* default gpio pin for display enable, 1 - enable, 0 - disable */ + + +/* Display rotation */ +#define EVE_DISPLAY_0 0 /* 0 degrees rotation */ +#define EVE_DISPLAY_180 1 /* 180 degrees rotation */ + + +/* commands common to EVE/EVE2/EVE3 */ +#define CMD_APPEND 0xFFFFFF1E +#define CMD_BGCOLOR 0xFFFFFF09 +#define CMD_BUTTON 0xFFFFFF0D +#define CMD_CALIBRATE 0xFFFFFF15 +#define CMD_CLOCK 0xFFFFFF14 +#define CMD_COLDSTART 0xFFFFFF32 +#define CMD_DIAL 0xFFFFFF2D +#define CMD_DLSTART 0xFFFFFF00 +#define CMD_FGCOLOR 0xFFFFFF0A +#define CMD_GAUGE 0xFFFFFF13 +#define CMD_GETMATRIX 0xFFFFFF33 +#define CMD_GETPROPS 0xFFFFFF25 +#define CMD_GETPTR 0xFFFFFF23 +#define CMD_GRADCOLOR 0xFFFFFF34 +#define CMD_GRADIENT 0xFFFFFF0B +#define CMD_INFLATE 0xFFFFFF22 +#define CMD_INTERRUPT 0xFFFFFF02 +#define CMD_KEYS 0xFFFFFF0E +#define CMD_LOADIDENTITY 0xFFFFFF26 +#define CMD_LOADIMAGE 0xFFFFFF24 +#define CMD_LOGO 0xFFFFFF31 +#define CMD_MEMCPY 0xFFFFFF1D +#define CMD_MEMCRC 0xFFFFFF18 +#define CMD_MEMSET 0xFFFFFF1B +#define CMD_MEMWRITE 0xFFFFFF1A +#define CMD_MEMZERO 0xFFFFFF1C +#define CMD_NUMBER 0xFFFFFF2E +#define CMD_PROGRESS 0xFFFFFF0F +#define CMD_REGREAD 0xFFFFFF19 +#define CMD_ROTATE 0xFFFFFF29 +#define CMD_SCALE 0xFFFFFF28 +#define CMD_SCREENSAVER 0xFFFFFF2F +#define CMD_SCROLLBAR 0xFFFFFF11 +#define CMD_SETFONT 0xFFFFFF2B +#define CMD_SETMATRIX 0xFFFFFF2A +#define CMD_SKETCH 0xFFFFFF30 +#define CMD_SLIDER 0xFFFFFF10 +#define CMD_SNAPSHOT 0xFFFFFF1F +#define CMD_SPINNER 0xFFFFFF16 +#define CMD_STOP 0xFFFFFF17 +#define CMD_SWAP 0xFFFFFF01 +#define CMD_TEXT 0xFFFFFF0C +#define CMD_TOGGLE 0xFFFFFF12 +#define CMD_TRACK 0xFFFFFF2C +#define CMD_TRANSLATE 0xFFFFFF27 + + +/* the following are undocumented commands that therefore should not be used */ +#if 0 +#define CMD_CRC 0xFFFFFF03 +#define CMD_HAMMERAUX 0xFFFFFF04 +#define CMD_MARCH 0xFFFFFF05 +#define CMD_IDCT 0xFFFFFF06 +#define CMD_EXECUTE 0xFFFFFF07 +#define CMD_GETPOINT 0xFFFFFF08 +#define CMD_TOUCH_TRANSFORM 0xFFFFFF20 +#endif + + +/* FT8xx graphics engine specific macros useful for static display list generation */ +#define ALPHA_FUNC(func,ref) ((9UL<<24)|(((func)&7UL)<<8)|(((ref)&255UL)<<0)) +#define BEGIN(prim) ((31UL<<24)|(((prim)&15UL)<<0)) +#define BITMAP_HANDLE(handle) ((5UL<<24)|(((handle)&31UL)<<0)) +#define BITMAP_LAYOUT(format,linestride,height) ((7UL<<24)|(((format)&31UL)<<19)|(((linestride)&1023UL)<<9)|(((height)&511UL)<<0)) +#define BITMAP_SIZE(filter,wrapx,wrapy,width,height) ((8UL<<24)|(((filter)&1UL)<<20)|(((wrapx)&1UL)<<19)|(((wrapy)&1UL)<<18)|(((width)&511UL)<<9)|(((height)&511UL)<<0)) +#define BITMAP_TRANSFORM_A(a) ((21UL<<24)|(((a)&131071UL)<<0)) +#define BITMAP_TRANSFORM_B(b) ((22UL<<24)|(((b)&131071UL)<<0)) +#define BITMAP_TRANSFORM_C(c) ((23UL<<24)|(((c)&16777215UL)<<0)) +#define BITMAP_TRANSFORM_D(d) ((24UL<<24)|(((d)&131071UL)<<0)) +#define BITMAP_TRANSFORM_E(e) ((25UL<<24)|(((e)&131071UL)<<0)) +#define BITMAP_TRANSFORM_F(f) ((26UL<<24)|(((f)&16777215UL)<<0)) +#define BLEND_FUNC(src,dst) ((11UL<<24)|(((src)&7UL)<<3)|(((dst)&7UL)<<0)) +#define CALL(dest) ((29UL<<24)|(((dest)&65535UL)<<0)) +#define CELL(cell) ((6UL<<24)|(((cell)&127UL)<<0)) +#define CLEAR(c,s,t) ((38UL<<24)|(((c)&1UL)<<2)|(((s)&1UL)<<1)|(((t)&1UL)<<0)) +#define CLEAR_COLOR_A(alpha) ((15UL<<24)|(((alpha)&255UL)<<0)) +#define CLEAR_COLOR_RGB(red,green,blue) ((2UL<<24)|(((red)&255UL)<<16)|(((green)&255UL)<<8)|(((blue)&255UL)<<0)) +#define CLEAR_STENCIL(s) ((17UL<<24)|(((s)&255UL)<<0)) +#define CLEAR_TAG(s) ((18UL<<24)|(((s)&255UL)<<0)) +#define COLOR_A(alpha) ((16UL<<24)|(((alpha)&255UL)<<0)) +#define COLOR_MASK(r,g,b,a) ((32UL<<24)|(((r)&1UL)<<3)|(((g)&1UL)<<2)|(((b)&1UL)<<1)|(((a)&1UL)<<0)) +#define COLOR_RGB(red,green,blue) ((4UL<<24)|(((red)&255UL)<<16)|(((green)&255UL)<<8)|(((blue)&255UL)<<0)) +/* #define DISPLAY() ((0UL<<24)) */ +#define END() ((33UL<<24)) +#define JUMP(dest) ((30UL<<24)|(((dest)&65535UL)<<0)) +#define LINE_WIDTH(width) ((14UL<<24)|(((width)&4095UL)<<0)) +#define MACRO(m) ((37UL<<24)|(((m)&1UL)<<0)) +#define POINT_SIZE(size) ((13UL<<24)|(((size)&8191UL)<<0)) +#define RESTORE_CONTEXT() ((35UL<<24)) +#define RETURN() ((36UL<<24)) +#define SAVE_CONTEXT() ((34UL<<24)) +#define STENCIL_FUNC(func,ref,mask) ((10UL<<24)|(((func)&7UL)<<16)|(((ref)&255UL)<<8)|(((mask)&255UL)<<0)) +#define STENCIL_MASK(mask) ((19UL<<24)|(((mask)&255UL)<<0)) +#define STENCIL_OP(sfail,spass) ((12UL<<24)|(((sfail)&7UL)<<3)|(((spass)&7UL)<<0)) +#define TAG(s) ((3UL<<24)|(((s)&255UL)<<0)) +#define TAG_MASK(mask) ((20UL<<24)|(((mask)&1UL)<<0)) +#define VERTEX2F(x,y) ((1UL<<30)|(((x)&32767UL)<<15)|(((y)&32767UL)<<0)) +#define VERTEX2II(x,y,handle,cell) ((2UL<<30)|(((x)&511UL)<<21)|(((y)&511UL)<<12)|(((handle)&31UL)<<7)|(((cell)&127UL)<<0)) + + +/* ----------------- BT81x exclusive definitions -----------------*/ +#if defined (BT81X_ENABLE) + +#define EVE_GLFORMAT 31UL /* used with BITMAP_LAYOUT to indicate bitmap-format is specified by BITMAP_EXT_FORMAT */ + +#define DL_BITMAP_EXT_FORMAT 0x2E000000 /* requires OR'd arguments */ + +/* extended Bitmap formats */ +#define EVE_COMPRESSED_RGBA_ASTC_4x4_KHR 37808UL +#define EVE_COMPRESSED_RGBA_ASTC_5x4_KHR 37809UL +#define EVE_COMPRESSED_RGBA_ASTC_5x5_KHR 37810UL +#define EVE_COMPRESSED_RGBA_ASTC_6x5_KHR 37811UL +#define EVE_COMPRESSED_RGBA_ASTC_6x6_KHR 37812UL +#define EVE_COMPRESSED_RGBA_ASTC_8x5_KHR 37813UL +#define EVE_COMPRESSED_RGBA_ASTC_8x6_KHR 37814UL +#define EVE_COMPRESSED_RGBA_ASTC_8x8_KHR 37815UL +#define EVE_COMPRESSED_RGBA_ASTC_10x5_KHR 37816UL +#define EVE_COMPRESSED_RGBA_ASTC_10x6_KHR 37817UL +#define EVE_COMPRESSED_RGBA_ASTC_10x8_KHR 37818UL +#define EVE_COMPRESSED_RGBA_ASTC_10x10_KHR 37819UL +#define EVE_COMPRESSED_RGBA_ASTC_12x10_KHR 37820UL +#define EVE_COMPRESSED_RGBA_ASTC_12x12_KHR 37821UL + + +#define EVE_RAM_ERR_REPORT 0x309800UL /* max 128 bytes null terminated string */ +#define EVE_RAM_FLASH 0x800000UL +#define EVE_RAM_FLASH_POSTBLOB 0x801000UL + +#define EVE_OPT_FLASH 64UL +#define EVE_OPT_FORMAT 4096UL +#define EVE_OPT_FILL 8192UL + + +/* additional commands for BT81x */ +#define CMD_BITMAP_TRANSFORM 0xFFFFFF21 +#define CMD_SYNC 0xFFFFFF42 /* does not need a dedicated function, just use EVE_cmd_dl(CMD_SYNC) */ +#define CMD_FLASHERASE 0xFFFFFF44 /* does not need a dedicated function, just use EVE_cmd_dl(CMD_FLASHERASE) */ +#define CMD_FLASHWRITE 0xFFFFFF45 +#define CMD_FLASHREAD 0xFFFFFF46 +#define CMD_FLASHUPDATE 0xFFFFFF47 +#define CMD_FLASHDETACH 0xFFFFFF48 /* does not need a dedicated function, just use EVE_cmd_dl(CMD_FLASHDETACH) */ +#define CMD_FLASHATTACH 0xFFFFFF49 /* does not need a dedicated function, just use EVE_cmd_dl(CMD_FLASHATTACH) */ +#define CMD_FLASHFAST 0xFFFFFF4A +#define CMD_FLASHSPIDESEL 0xFFFFFF4B /* does not need a dedicated function, just use EVE_cmd_dl(CMD_FLASHSPIDESEL) */ +#define CMD_FLASHSPITX 0xFFFFFF4C +#define CMD_FLASHSPIRX 0xFFFFFF4D +#define CMD_FLASHSOURCE 0xFFFFFF4E +#define CMD_CLEARCACHE 0xFFFFFF4F /* does not need a dedicated function, just use EVE_cmd_dl(CMD_CLEARCACHE) */ +#define CMD_INFLATE2 0xFFFFFF50 +#define CMD_ROTATEAROUND 0xFFFFFF51 +#define CMD_RESETFONTS 0xFFFFFF52 /* does not need a dedicated function, just use EVE_cmd_dl(CMD_RESETFONTS) */ +#define CMD_ANIMSTART 0xFFFFFF53 +#define CMD_ANIMSTOP 0xFFFFFF54 +#define CMD_ANIMXY 0xFFFFFF55 +#define CMD_ANIMDRAW 0xFFFFFF56 +#define CMD_GRADIENTA 0xFFFFFF57 +#define CMD_FILLWIDTH 0xFFFFFF58 +#define CMD_APPENDF 0xFFFFFF59 +#define CMD_ANIMFRAME 0xFFFFFF5A +#define CMD_VIDEOSTARTF 0xFFFFFF5F /* does not need a dedicated function, just use EVE_cmd_dl(CMD_VIDEOSTARTF) */ + +#if 0 +/* some undocumented commands for BT81x */ +#define CMD_NOP 0xFFFFFF5B +#define CMD_SHA1 0xFFFFFF5C +#define CMD_HMAC 0xFFFFFF5D +#define CMD_LAST_ 0xFFFFFF5E + +#endif + + +/* additional registers for BT81x */ +#define REG_ADAPTIVE_FRAMERATE 0x30257cUL +#define REG_PLAYBACK_PAUSE 0x3025ecUL +#define REG_FLASH_STATUS 0x3025f0UL +#define REG_FLASH_SIZE 0x309024UL +#define REG_PLAY_CONTROL 0x30914eUL +#define REG_COPRO_PATCH_DTR 0x309162UL + + +/* BT81x graphics engine specific macros */ +#define BITMAP_EXT_FORMAT(format) ((46UL<<24)|(((format)&65535UL)<<0)) +#define BITMAP_SWIZZLE(r,g,b,a) ((47UL<<24)|(((r)&7UL)<<9)|(((g)&7UL)<<6)|(((b)&7UL)<<3)|(((a)&7UL)<<0)) +#define BITMAP_SOURCE2(flash_or_ram, addr) ((1UL<<24)|((flash_or_ram) << 23)|(((addr)&8388607UL)<<0)) +#define INT_FRR() ((48UL<<24)) + +#undef BITMAP_TRANSFORM_A +#undef BITMAP_TRANSFORM_B +#undef BITMAP_TRANSFORM_D +#undef BITMAP_TRANSFORM_E + +#define BITMAP_TRANSFORM_A_EXT(p,v) ((21UL<<24)|(((p)&1UL)<<17)|(((v)&131071UL)<<0)) +#define BITMAP_TRANSFORM_B_EXT(p,v) ((22UL<<24)|(((p)&1UL)<<17)|(((v)&131071UL)<<0)) +#define BITMAP_TRANSFORM_D_EXT(p,v) ((24UL<<24)|(((p)&1UL)<<17)|(((v)&131071UL)<<0)) +#define BITMAP_TRANSFORM_E_EXT(p,v) ((25UL<<24)|(((p)&1UL)<<17)|(((v)&131071UL)<<0)) + +#define BITMAP_TRANSFORM_A(a) BITMAP_TRANSFORM_A_EXT(0,a) +#define BITMAP_TRANSFORM_B(b) BITMAP_TRANSFORM_B_EXT(0,b) +#define BITMAP_TRANSFORM_D(d) BITMAP_TRANSFORM_D_EXT(0,d) +#define BITMAP_TRANSFORM_E(e) BITMAP_TRANSFORM_E_EXT(0,e) + +#endif + +/* ----------------- FT81x / BT81x exclusive definitions -----------------*/ +#if defined (FT81X_ENABLE) + + +/* Host commands */ +#define EVE_CLKSEL 0x61 /* configure system clock */ +#define EVE_RST_PULSE 0x68 /* reset core - all registers default and processors reset */ +#define EVE_PINDRIVE 0x70 /* setup drive strength for various pins */ +#define EVE_PIN_PD_STATE 0x71 /* setup how pins behave during power down */ + + +/* Memory definitions */ +#define EVE_RAM_G 0x000000UL +#define EVE_ROM_CHIPID 0x0C0000UL +#define EVE_ROM_FONT 0x1E0000UL +#define EVE_ROM_FONT_ADDR 0x2FFFFCUL +#define EVE_RAM_DL 0x300000UL +#define EVE_RAM_REG 0x302000UL +#define EVE_RAM_CMD 0x308000UL + + +/* Memory buffer sizes */ +#define EVE_RAM_G_SIZE 1024*1024L +#define EVE_CMDFIFO_SIZE 4*1024L +#define EVE_RAM_DL_SIZE 8*1024L + + +/* various additional defines for FT81x */ +#define EVE_ADC_DIFFERENTIAL 1UL +#define EVE_ADC_SINGLE_ENDED 0UL + +#define EVE_INT_G8 18UL +#define EVE_INT_L8C 12UL +#define EVE_INT_VGA 13UL + +#define EVE_OPT_MEDIAFIFO 16UL +#define EVE_OPT_FULLSCREEN 8UL +#define EVE_OPT_NOTEAR 4UL +#define EVE_OPT_SOUND 32UL + +#define EVE_PALETTED565 14UL +#define EVE_PALETTED4444 15UL +#define EVE_PALETTED8 16UL +#define EVE_L2 17UL + + +/* additional commands for FT81x */ +#define CMD_MEDIAFIFO 0xFFFFFF39 +#define CMD_PLAYVIDEO 0xFFFFFF3A +#define CMD_ROMFONT 0xFFFFFF3F +#define CMD_SETBASE 0xFFFFFF38 +#define CMD_SETBITMAP 0xFFFFFF43 +#define CMD_SETFONT2 0xFFFFFF3B +#define CMD_SETROTATE 0xFFFFFF36 +#define CMD_SETSCRATCH 0xFFFFFF3C +#define CMD_SNAPSHOT2 0xFFFFFF37 +#define CMD_VIDEOFRAME 0xFFFFFF41 +#define CMD_VIDEOSTART 0xFFFFFF40 + + +/* the following are undocumented commands that therefore should not be used */ +#if 0 +#define CMD_CSKETCH 0xFFFFFF35 +#define CMD_INT_RAMSHARED 0xFFFFFF3D +#define CMD_INT_SWLOADIMAGE 0xFFFFFF3E +#endif + + +/* Register definitions */ +#define REG_ANA_COMP 0x302184UL /* only listed in datasheet */ +#define REG_BIST_EN 0x302174UL /* only listed in datasheet */ +#define REG_CLOCK 0x302008UL +#define REG_CMDB_SPACE 0x302574UL +#define REG_CMDB_WRITE 0x302578UL +#define REG_CMD_DL 0x302100UL +#define REG_CMD_READ 0x3020f8UL +#define REG_CMD_WRITE 0x3020fcUL +#define REG_CPURESET 0x302020UL +#define REG_CSPREAD 0x302068UL +#define REG_CTOUCH_EXTENDED 0x302108UL +#define REG_CTOUCH_TOUCH0_XY 0x302124UL /* only listed in datasheet */ +#define REG_CTOUCH_TOUCH4_X 0x30216cUL +#define REG_CTOUCH_TOUCH4_Y 0x302120UL +#define REG_CTOUCH_TOUCH1_XY 0x30211cUL +#define REG_CTOUCH_TOUCH2_XY 0x30218cUL +#define REG_CTOUCH_TOUCH3_XY 0x302190UL +#define REG_TOUCH_CONFIG 0x302168UL +#define REG_DATESTAMP 0x302564UL /* only listed in datasheet */ +#define REG_DITHER 0x302060UL +#define REG_DLSWAP 0x302054UL +#define REG_FRAMES 0x302004UL +#define REG_FREQUENCY 0x30200cUL +#define REG_GPIO 0x302094UL +#define REG_GPIOX 0x30209cUL +#define REG_GPIOX_DIR 0x302098UL +#define REG_GPIO_DIR 0x302090UL +#define REG_HCYCLE 0x30202cUL +#define REG_HOFFSET 0x302030UL +#define REG_HSIZE 0x302034UL +#define REG_HSYNC0 0x302038UL +#define REG_HSYNC1 0x30203cUL +#define REG_ID 0x302000UL +#define REG_INT_EN 0x3020acUL +#define REG_INT_FLAGS 0x3020a8UL +#define REG_INT_MASK 0x3020b0UL +#define REG_MACRO_0 0x3020d8UL +#define REG_MACRO_1 0x3020dcUL +#define REG_MEDIAFIFO_READ 0x309014UL /* only listed in programmers guide */ +#define REG_MEDIAFIFO_WRITE 0x309018UL /* only listed in programmers guide */ +#define REG_OUTBITS 0x30205cUL +#define REG_PCLK 0x302070UL +#define REG_PCLK_POL 0x30206cUL +#define REG_PLAY 0x30208cUL +#define REG_PLAYBACK_FORMAT 0x3020c4UL +#define REG_PLAYBACK_FREQ 0x3020c0UL +#define REG_PLAYBACK_LENGTH 0x3020b8UL +#define REG_PLAYBACK_LOOP 0x3020c8UL +#define REG_PLAYBACK_PLAY 0x3020ccUL +#define REG_PLAYBACK_READPTR 0x3020bcUL +#define REG_PLAYBACK_START 0x3020b4UL +#define REG_PWM_DUTY 0x3020d4UL +#define REG_PWM_HZ 0x3020d0UL +#define REG_RENDERMODE 0x302010UL /* only listed in datasheet */ +#define REG_ROTATE 0x302058UL +#define REG_SNAPFORMAT 0x30201cUL /* only listed in datasheet */ +#define REG_SNAPSHOT 0x302018UL /* only listed in datasheet */ +#define REG_SNAPY 0x302014UL /* only listed in datasheet */ +#define REG_SOUND 0x302088UL +#define REG_SPI_WIDTH 0x302188UL /* listed with false offset in programmers guide V1.1 */ +#define REG_SWIZZLE 0x302064UL +#define REG_TAG 0x30207cUL +#define REG_TAG_X 0x302074UL +#define REG_TAG_Y 0x302078UL +#define REG_TAP_CRC 0x302024UL /* only listed in datasheet */ +#define REG_TAP_MASK 0x302028UL /* only listed in datasheet */ +#define REG_TOUCH_ADC_MODE 0x302108UL +#define REG_TOUCH_CHARGE 0x30210cUL +#define REG_TOUCH_DIRECT_XY 0x30218cUL +#define REG_TOUCH_DIRECT_Z1Z2 0x302190UL +#define REG_TOUCH_MODE 0x302104UL +#define REG_TOUCH_OVERSAMPLE 0x302114UL +#define REG_TOUCH_RAW_XY 0x30211cUL +#define REG_TOUCH_RZ 0x302120UL +#define REG_TOUCH_RZTHRESH 0x302118UL +#define REG_TOUCH_SCREEN_XY 0x302124UL +#define REG_TOUCH_SETTLE 0x302110UL +#define REG_TOUCH_TAG 0x30212cUL +#define REG_TOUCH_TAG1 0x302134UL /* only listed in datasheet */ +#define REG_TOUCH_TAG1_XY 0x302130UL /* only listed in datasheet */ +#define REG_TOUCH_TAG2 0x30213cUL /* only listed in datasheet */ +#define REG_TOUCH_TAG2_XY 0x302138UL /* only listed in datasheet */ +#define REG_TOUCH_TAG3 0x302144UL /* only listed in datasheet */ +#define REG_TOUCH_TAG3_XY 0x302140UL /* only listed in datasheet */ +#define REG_TOUCH_TAG4 0x30214cUL /* only listed in datasheet */ +#define REG_TOUCH_TAG4_XY 0x302148UL /* only listed in datasheet */ +#define REG_TOUCH_TAG_XY 0x302128UL +#define REG_TOUCH_TRANSFORM_A 0x302150UL +#define REG_TOUCH_TRANSFORM_B 0x302154UL +#define REG_TOUCH_TRANSFORM_C 0x302158UL +#define REG_TOUCH_TRANSFORM_D 0x30215cUL +#define REG_TOUCH_TRANSFORM_E 0x302160UL +#define REG_TOUCH_TRANSFORM_F 0x302164UL +#define REG_TRACKER 0x309000UL /* only listed in programmers guide */ +#define REG_TRACKER_1 0x309004UL /* only listed in programmers guide */ +#define REG_TRACKER_2 0x309008UL /* only listed in programmers guide */ +#define REG_TRACKER_3 0x30900cUL /* only listed in programmers guide */ +#define REG_TRACKER_4 0x309010UL /* only listed in programmers guide */ +#define REG_TRIM 0x302180UL +#define REG_VCYCLE 0x302040UL +#define REG_VOFFSET 0x302044UL +#define REG_VOL_PB 0x302080UL +#define REG_VOL_SOUND 0x302084UL +#define REG_VSIZE 0x302048UL +#define REG_VSYNC0 0x30204cUL +#define REG_VSYNC1 0x302050UL + +#if 0 +#define REG_BUSYBITS 0x3020e8UL /* only listed as "reserved" in datasheet */ +#define REG_CRC 0x302178UL /* only listed as "reserved" in datasheet */ +#define REG_SPI_EARLY_TX 0x30217cUL /* only listed as "reserved" in datasheet */ +#define REG_ROMSUB_SEL 0x3020f0UL /* only listed as "reserved" in datasheet */ +#define REG_TOUCH_FAULT 0x302170UL /* only listed as "reserved" in datasheet */ +#endif + + +/* FT81x graphics engine specific macros useful for static display list generation */ + +/* beware, these are different to FTDIs implementation as these take the original values as parameters and not only the upper bits */ +#define BITMAP_LAYOUT_H(linestride,height) ((40UL<<24)|((((linestride&0xC00)>>10)&3UL)<<2)|((((height&0x600)>>9)&3UL)<<0)) +#define BITMAP_SIZE_H(width,height) ((41UL<<24)|((((width&0x600)>>9)&3UL)<<2)|((((height&0x600)>>9)&3UL)<<0)) + +#define BITMAP_SOURCE(addr) ((1UL<<24)|(((addr)&4194303UL)<<0)) +//#define NOP() ((45UL<<24)) +#define PALETTE_SOURCE(addr) ((42UL<<24)|(((addr)&4194303UL)<<0)) +#define SCISSOR_SIZE(width,height) ((28UL<<24)|(((width)&4095UL)<<12)|(((height)&4095UL)<<0)) +#define SCISSOR_XY(x,y) ((27UL<<24)|(((x)&2047UL)<<11)|(((y)&2047UL)<<0)) +#define VERTEX_FORMAT(frac) ((39UL<<24)|(((frac)&7UL)<<0)) +#define VERTEX_TRANSLATE_X(x) ((43UL<<24)|(((x)&131071UL)<<0)) +#define VERTEX_TRANSLATE_Y(y) ((44UL<<24)|(((y)&131071UL)<<0)) + + + +/* ----------------- FT80x exclusive definitions -----------------*/ +#else + +/* Memory definitions */ +#define EVE_RAM_G 0x000000UL +#define EVE_ROM_CHIPID 0x0C0000UL +#define EVE_ROM_FONT 0x0BB23CUL +#define EVE_ROM_FONT_ADDR 0x0FFFFCUL +#define EVE_RAM_DL 0x100000UL +#define EVE_RAM_PAL 0x102000UL +#define EVE_RAM_CMD 0x108000UL +#define EVE_RAM_SCREENSHOT 0x1C2000UL + + +/* Memory buffer sizes */ +#define EVE_RAM_G_SIZE 256*1024L +#define EVE_CMDFIFO_SIZE 4*1024L +#define EVE_RAM_DL_SIZE 8*1024L +#define EVE_RAM_PAL_SIZE 1*1024L + + +/* Register definitions */ +#define REG_ID 0x102400UL +#define REG_FRAMES 0x102404UL +#define REG_CLOCK 0x102408UL +#define REG_FREQUENCY 0x10240CUL +#define REG_SCREENSHOT_EN 0x102410UL +#define REG_SCREENSHOT_Y 0x102414UL +#define REG_SCREENSHOT_START 0x102418UL +#define REG_CPURESET 0x10241CUL +#define REG_TAP_CRC 0x102420UL +#define REG_TAP_MASK 0x102424UL +#define REG_HCYCLE 0x102428UL +#define REG_HOFFSET 0x10242CUL +#define REG_HSIZE 0x102430UL +#define REG_HSYNC0 0x102434UL +#define REG_HSYNC1 0x102438UL +#define REG_VCYCLE 0x10243CUL +#define REG_VOFFSET 0x102440UL +#define REG_VSIZE 0x102444UL +#define REG_VSYNC0 0x102448UL +#define REG_VSYNC1 0x10244CUL +#define REG_DLSWAP 0x102450UL +#define REG_ROTATE 0x102454UL +#define REG_OUTBITS 0x102458UL +#define REG_DITHER 0x10245CUL +#define REG_SWIZZLE 0x102460UL +#define REG_CSPREAD 0x102464UL +#define REG_PCLK_POL 0x102468UL +#define REG_PCLK 0x10246CUL +#define REG_TAG_X 0x102470UL +#define REG_TAG_Y 0x102474UL +#define REG_TAG 0x102478UL +#define REG_VOL_PB 0x10247CUL +#define REG_VOL_SOUND 0x102480UL +#define REG_SOUND 0x102484UL +#define REG_PLAY 0x102488UL +#define REG_GPIO_DIR 0x10248CUL +#define REG_GPIO 0x102490UL +#define REG_INT_FLAGS 0x102498UL +#define REG_INT_EN 0x10249CUL +#define REG_INT_MASK 0x1024A0UL +#define REG_PLAYBACK_START 0x1024A4UL +#define REG_PLAYBACK_LENGTH 0x1024A8UL +#define REG_PLAYBACK_READPTR 0x1024ACUL +#define REG_PLAYBACK_FREQ 0x1024B0UL +#define REG_PLAYBACK_FORMAT 0x1024B4UL +#define REG_PLAYBACK_LOOP 0x1024B8UL +#define REG_PLAYBACK_PLAY 0x1024BCUL +#define REG_PWM_HZ 0x1024C0UL +#define REG_PWM_DUTY 0x1024C4UL +#define REG_MACRO_0 0x1024C8UL +#define REG_MACRO_1 0x1024CCUL +#define REG_SCREENSHOT_BUSY 0x1024D8UL +#define REG_CMD_READ 0x1024E4UL +#define REG_CMD_WRITE 0x1024E8UL +#define REG_CMD_DL 0x1024ECUL +#define REG_TOUCH_MODE 0x1024F0UL +#define REG_TOUCH_ADC_MODE 0x1024F4UL +#define REG_TOUCH_CHARGE 0x1024F8UL +#define REG_TOUCH_SETTLE 0x1024FCUL +#define REG_TOUCH_OVERSAMPLE 0x102500UL +#define REG_TOUCH_RZTHRESH 0x102504UL +#define REG_TOUCH_RAW_XY 0x102508UL +#define REG_TOUCH_RZ 0x10250CUL +#define REG_TOUCH_SCREEN_XY 0x102510UL +#define REG_TOUCH_TAG_XY 0x102514UL +#define REG_TOUCH_TAG 0x102518UL +#define REG_TOUCH_TRANSFORM_A 0x10251CUL +#define REG_TOUCH_TRANSFORM_B 0x102520UL +#define REG_TOUCH_TRANSFORM_C 0x102524UL +#define REG_TOUCH_TRANSFORM_D 0x102528UL +#define REG_TOUCH_TRANSFORM_E 0x10252CUL +#define REG_TOUCH_TRANSFORM_F 0x102530UL +#define REG_SCREENSHOT_READ 0x102554UL +#define REG_TRIM 0x10256CUL +#define REG_TOUCH_DIRECT_XY 0x102574UL +#define REG_TOUCH_DIRECT_Z1Z2 0x102578UL +#define REG_TRACKER 0x109000UL + +/* FT80x graphics engine specific macros useful for static display list generation */ +#define BITMAP_SOURCE(addr) ((1UL<<24)|(((addr)&1048575UL)<<0)) +#define SCISSOR_SIZE(width,height) ((28UL<<24)|(((width)&1023UL)<<10)|(((height)&1023UL)<<0)) +#define SCISSOR_XY(x,y) ((27UL<<24)|(((x)&511UL)<<9)|(((y)&511UL)<<0)) + +#endif + +#endif /* EVE_H_ */ diff --git a/lvgl_tft/EVE_commands.c b/lvgl_tft/EVE_commands.c new file mode 100644 index 0000000..67c05ab --- /dev/null +++ b/lvgl_tft/EVE_commands.c @@ -0,0 +1,2313 @@ +/* +@file EVE_commands.c +@brief contains FT8xx / BT8xx functions +@version 4.0 +@date 2020-04-13 +@author Rudolph Riedel, David Jade + +@section info + +This file needs to be renamed to EVE_command.cpp for use with Arduino. +At least für ATSAM I had the best result with -O2. +The c-standard is C99. + + +@section LICENSE + +MIT License + +Copyright (c) 2016-2020 Rudolph Riedel and David Jade + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +@section History + +4.1 LvGL edition + + - This version is a heavily modified version of the MIT licensed FT81x code from https://github.com/RudolphRiedel/FT800-FT813 + This version is based on a fork by David Jade that added native SPI DMA support and stripped out non-ESP32 code. + It has also been trimmed down to suit LvGL's needs. Extra features can be enabled by defining FT81X_FULL +*/ + + +#include + +#include "EVE.h" +#include "EVE_commands.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "soc/soc_memory_layout.h" + +#include "esp_log.h" + +#include "disp_spi.h" + +#include + +#if defined (BT81X_ENABLE) +#include +#endif + +#define TAG "FT81X" + +/* data structure for SPI reading that has (optional) space for inserted dummy byte */ +typedef struct _spi_read_data { +#if defined(DISP_SPI_FULL_DUPLEX) + uint8_t _dummy_byte; +#endif + union { + uint8_t byte; + uint16_t word; + uint32_t dword; + } __attribute__((packed)); +} spi_read_data __attribute__((aligned(4))); + + +/* Receive data helpers */ +#define member_size(type, member) sizeof(((type *)0)->member) + +#if defined(DISP_SPI_FULL_DUPLEX) +#define SPI_READ_DUMMY_LEN member_size(spi_read_data, _dummy_byte) +#else +#define SPI_READ_DUMMY_LEN 0 +#endif +#define SPI_READ_BYTE_LEN (SPI_READ_DUMMY_LEN + member_size(spi_read_data, byte)) +#define SPI_READ_WORD_LEN (SPI_READ_DUMMY_LEN + member_size(spi_read_data, word)) +#define SPI_READ_DWORD_LEN (SPI_READ_DUMMY_LEN + member_size(spi_read_data, dword)) + + +// EVE Memory Commands - used with EVE_memWritexx and EVE_memReadxx +#define MEM_WRITE 0x80 // EVE Host Memory Write +#define MEM_READ 0x00 // EVE Host Memory Read +#define MEM_WRITE_24 0x800000 // EVE Host Memory Write (24-bit format) + +volatile uint16_t cmdOffset = 0x0000; /* offset for the 4k co-processor FIFO */ + +volatile uint8_t cmd_burst = 0; /* flag to indicate cmd-burst is active */ + +// Buffer for SPI transactions +uint8_t SPIBuffer[SPI_BUFFER_SIZE]; // must be in DMA capable memory if DMA is used! +uint16_t SPIBufferIndex = 0; +disp_spi_send_flag_t SPIInherentSendFlags = 0; // additional inherent SPI flags (for DIO/QIO mode switching) +uint8_t SPIDummyReadBits = 0; // Dummy bits for reading in DIO/QIO modes + +// Macros to make SPI use explicit and less verbose +// (macros do obscure code a little but they also help code search and readability) + +// Buffer a byte +#define BUFFER_SPI_BYTE(byte) SPIBuffer[SPIBufferIndex++] = (byte); + +// Buffer a Word - little Endian format +#define BUFFER_SPI_WORD(word) \ + BUFFER_SPI_BYTE((uint8_t)(word)) \ + BUFFER_SPI_BYTE((uint8_t)((word) >> 8)) + +// Buffer a DWord - little Endian format +#define BUFFER_SPI_DWORD(dword) \ + BUFFER_SPI_BYTE((uint8_t)(dword)) \ + BUFFER_SPI_BYTE((uint8_t)((dword) >> 8)) \ + BUFFER_SPI_BYTE((uint8_t)((dword) >> 16)) \ + BUFFER_SPI_BYTE((uint8_t)((dword) >> 24)) + +// Buffer a 24-bit SPI Memory Write address - big Endian format +#define BUFFER_SPI_WRITE_ADDRESS(addr) \ + BUFFER_SPI_BYTE((uint8_t)((addr) >> 16) | MEM_WRITE) \ + BUFFER_SPI_BYTE((uint8_t)((addr) >> 8)) \ + BUFFER_SPI_BYTE((uint8_t)(addr)) + +// Send buffer +#define SEND_SPI_BUFFER() \ + disp_spi_transaction(SPIBuffer, SPIBufferIndex, (disp_spi_send_flag_t)(DISP_SPI_SEND_QUEUED | SPIInherentSendFlags), NULL, 0, 0); \ + SPIBufferIndex = 0; + +// Wait for DMA queued SPI transactions to complete +#define WAIT_SPI() \ + disp_wait_for_pending_transactions(); + + + +void DELAY_MS(uint16_t ms) +{ + vTaskDelay(ms / portTICK_PERIOD_MS); +} + + +void EVE_pdn_set(void) +{ + gpio_set_level(EVE_PDN, 0); /* Power-Down low */ +} + + +void EVE_pdn_clear(void) +{ + gpio_set_level(EVE_PDN, 1); /* Power-Down high */ +} + + +void spi_acquire() +{ + disp_spi_acquire(); +} + + +void spi_release() +{ + disp_spi_release(); +} + + +void EVE_cmdWrite(uint8_t command, uint8_t parameter) +{ + const uint8_t data[] = + { + command, + parameter, + 0x00 + }; + + disp_spi_transaction(data, sizeof(data), (disp_spi_send_flag_t)(DISP_SPI_SEND_POLLING | SPIInherentSendFlags), NULL, 0, 0); +} + + +uint8_t EVE_memRead8(uint32_t ftAddress) +{ + spi_read_data data = {}; + disp_spi_send_flag_t readflags = (disp_spi_send_flag_t)(DISP_SPI_RECEIVE | DISP_SPI_SEND_POLLING | DISP_SPI_ADDRESS_24 | SPIInherentSendFlags); + +#if defined(DISP_SPI_HALF_DUPLEX) + // in half-duplex mode the FT81x requires dummy bits + readflags |= DISP_SPI_VARIABLE_DUMMY; +#endif + + disp_spi_transaction(NULL, SPI_READ_BYTE_LEN, readflags, (uint8_t*)&data, ftAddress, SPIDummyReadBits); + return data.byte; /* return byte read */ +} + + +uint16_t EVE_memRead16(uint32_t ftAddress) +{ +#if defined(DISP_SPI_HALF_DUPLEX) + // There are esp32 issues when reading in DMA half-duplex mode that prevents reading more than 1 byte at a time so we work around that + uint16_t word = 0; + for(int i = 0; i < sizeof(word); i++) + { + word |= (EVE_memRead8(ftAddress + i) << i * 8); + } + return word; +#endif + + spi_read_data data = {}; + disp_spi_send_flag_t readflags = (disp_spi_send_flag_t)(DISP_SPI_RECEIVE | DISP_SPI_SEND_POLLING | DISP_SPI_ADDRESS_24 | SPIInherentSendFlags); + +#if defined(DISP_SPI_HALF_DUPLEX) + // in half-duplex mode the FT81x requires dummy bits + readflags |= DISP_SPI_VARIABLE_DUMMY; +#endif + + disp_spi_transaction(NULL, SPI_READ_WORD_LEN, readflags, (uint8_t*)&data, ftAddress, SPIDummyReadBits); + return data.word; /* return integer read */ +} + + +uint32_t EVE_memRead32(uint32_t ftAddress) +{ +#if defined(DISP_SPI_HALF_DUPLEX) + // There are esp32 issues when reading in DMA half-duplex mode that prevents reading more than 1 byte at a time so we work around that + uint32_t dword = 0; + for(int i = 0; i < sizeof(dword); i++) + { + dword |= (EVE_memRead8(ftAddress + i) << i * 8); + } + return dword; +#endif + + spi_read_data data = {}; + disp_spi_send_flag_t readflags = (disp_spi_send_flag_t)(DISP_SPI_RECEIVE | DISP_SPI_SEND_POLLING | DISP_SPI_ADDRESS_24 | SPIInherentSendFlags); + +#if defined(DISP_SPI_HALF_DUPLEX) + // in half-duplex mode the FT81x requires dummy bits + readflags |= DISP_SPI_VARIABLE_DUMMY; +#endif + + disp_spi_transaction(NULL, SPI_READ_DWORD_LEN, readflags, (uint8_t*)&data, ftAddress, SPIDummyReadBits); + return data.dword; /* return integer read */ +} + + +void EVE_memWrite8(uint32_t ftAddress, uint8_t ftData8) +{ + disp_spi_transaction(&ftData8, sizeof(ftData8), (disp_spi_send_flag_t)(DISP_SPI_SEND_POLLING | DISP_SPI_ADDRESS_24 | SPIInherentSendFlags), NULL, (ftAddress | MEM_WRITE_24), 0); +} + + +void EVE_memWrite16(uint32_t ftAddress, uint16_t ftData16) +{ + disp_spi_transaction((uint8_t*)&ftData16, sizeof(ftData16), (disp_spi_send_flag_t)(DISP_SPI_SEND_POLLING | DISP_SPI_ADDRESS_24 | SPIInherentSendFlags), NULL, (ftAddress | MEM_WRITE_24), 0); +} + + +void EVE_memWrite32(uint32_t ftAddress, uint32_t ftData32) +{ + disp_spi_transaction((uint8_t*)&ftData32, sizeof(ftData32), (disp_spi_send_flag_t)(DISP_SPI_SEND_POLLING | DISP_SPI_ADDRESS_24 | SPIInherentSendFlags), NULL, (ftAddress | MEM_WRITE_24), 0); +} + + +// write to EVE memory via SPI memory-mapped protocol using queued SPI transactions (i.e. DMA) +// Note: data should be in DMA-capable memory! +void EVE_memWrite_buffer(uint32_t ftAddress, const uint8_t *data, uint32_t len, bool LvGL_Flush) +{ + // chunk by DMA transfer size (SPI_TRANSER_SIZE) + uint32_t bytes_left = len; + while(bytes_left > 0) + { + uint32_t block_len = (bytes_left > SPI_TRANSER_SIZE ? SPI_TRANSER_SIZE : bytes_left); + + // only send flush on last chunk + disp_spi_send_flag_t flush_flag = 0; + if(LvGL_Flush && bytes_left - block_len == 0) + { + flush_flag = DISP_SPI_SIGNAL_FLUSH; + } + + disp_spi_transaction(data, block_len, (disp_spi_send_flag_t)(DISP_SPI_SEND_QUEUED | DISP_SPI_ADDRESS_24 | SPIInherentSendFlags | flush_flag), NULL, (ftAddress | MEM_WRITE_24), 0); + data += block_len; + ftAddress += block_len; + bytes_left -= block_len; + } +} + + +/* Check if the graphics processor completed executing the current command list. */ +/* This is the case when REG_CMD_READ matches cmdOffset, indicating that all commands have been executed. */ +uint8_t EVE_busy(void) +{ + uint16_t cmdBufferRead; + + WAIT_SPI(); // can't tell if EVE is busy if SPI is taking place + + cmdBufferRead = EVE_memRead16(REG_CMD_READ); /* read the graphics processor read pointer */ + + if(cmdBufferRead == 0xFFF) /* we have a co-processor fault, make EVE play with us again */ + { + #if defined (BT81X_ENABLE) + + uint16_t copro_patch_pointer; + + copro_patch_pointer = EVE_memRead16(REG_COPRO_PATCH_DTR); + + #endif + + EVE_memWrite8(REG_CPURESET, 1); /* hold co-processor engine in the reset condition */ + EVE_memWrite16(REG_CMD_READ, 0); /* set REG_CMD_READ to 0 */ + EVE_memWrite16(REG_CMD_WRITE, 0); /* set REG_CMD_WRITE to 0 */ + EVE_memWrite32(REG_CMD_DL, 0); /* reset REG_CMD_DL to 0 as required by the BT81x programming guide, should not hurt FT8xx */ + cmdOffset = 0; + EVE_memWrite8(REG_CPURESET, 0); /* set REG_CMD_WRITE to 0 to restart the co-processor engine*/ + + #if defined (BT81X_ENABLE) + + EVE_memWrite16(REG_COPRO_PATCH_DTR, copro_patch_pointer); + + DELAY_MS(5); /* just to be safe */ + + // reset the SPI buffer just to be cautious + SPIBufferIndex = 0; + + BUFFER_SPI_WRITE_ADDRESS(EVE_RAM_CMD + cmdOffset) + BUFFER_SPI_DWORD(CMD_FLASHATTACH) + BUFFER_SPI_DWORD(CMD_FLASHFAST) + SEND_SPI_BUFFER() + WAIT_SPI() + + cmdOffset += 8; + + BUFFER_SPI_BYTE + BUFFER_SPI_BYTE(MEM_WRITE | 0x30); /* send Memory Write plus high address byte of REG_CMD_WRITE for EVE81x */ + BUFFER_SPI_BYTE(0x20); /* send middle address byte of REG_CMD_WRITE for EVE81x */ + BUFFER_SPI_BYTE(0xfc); /* send low address byte of REG_CMD_WRITE for EVE81x */ + BUFFER_SPI_WORD(cmdOffset); + SEND_SPI_BUFFER() + WAIT_SPI() + + EVE_memWrite8(REG_PCLK, EVE_PCLK); /* restore REG_PCLK in case it was set to zero by an error */ + + DELAY_MS(5); /* just to be safe */ + + #endif + } + + if(cmdOffset != cmdBufferRead) + { + return 1; + } + else + { + return 0; + } +} + + +void EVE_get_cmdoffset(void) +{ + cmdOffset = EVE_memRead16(REG_CMD_WRITE); +} + + +void EVE_inc_cmdoffset(uint16_t increment) +{ + cmdOffset += increment; + cmdOffset &= 0x0fff; // circular +} + + +/* order the command co-processor to start processing its FIFO queue and do not wait for completion */ +void EVE_cmd_start(void) +{ + WAIT_SPI(); + + EVE_memWrite16(REG_CMD_WRITE, cmdOffset); +} + + +/* order the command co-processor to start processing its FIFO queue and wait for completion */ +void EVE_cmd_execute(void) +{ + EVE_cmd_start(); + while (EVE_busy()); +} + + +/* begin a co-processor command, this is used for all non-display-list commands */ +void EVE_begin_cmd(uint32_t command) +{ + BUFFER_SPI_WRITE_ADDRESS(EVE_RAM_CMD + cmdOffset) + BUFFER_SPI_DWORD(command) + + EVE_inc_cmdoffset(4); // only count the command size - not the write address directive +} + + +/* co-processor commands that are not used in displays lists, these are no to be used with async transfers */ + + +/* this is meant to be called outside display-list building, does not support cmd-burst */ +void EVE_cmd_memzero(uint32_t ptr, uint32_t num) +{ + EVE_begin_cmd(CMD_MEMZERO); + BUFFER_SPI_DWORD(ptr) + BUFFER_SPI_DWORD(num) + + EVE_inc_cmdoffset(8); + + SEND_SPI_BUFFER() +} + + +/* this is meant to be called outside display-list building, does not support cmd-burst */ +void EVE_cmd_memset(uint32_t ptr, uint8_t value, uint32_t num) +{ + EVE_begin_cmd(CMD_MEMSET); + BUFFER_SPI_DWORD(ptr) + BUFFER_SPI_DWORD(value) + BUFFER_SPI_DWORD(num) + + EVE_inc_cmdoffset(12); + + SEND_SPI_BUFFER() +} + + +/* this is meant to be called outside display-list building, does not support cmd-burst */ +void block_transfer(const uint8_t *data, uint32_t len); // forward ref + +void EVE_cmd_memwrite(uint32_t dest, uint32_t num, const uint8_t *data) +{ + EVE_begin_cmd(CMD_MEMWRITE); + BUFFER_SPI_DWORD(dest) + BUFFER_SPI_DWORD(num) + + EVE_inc_cmdoffset(8); + + SEND_SPI_BUFFER() + + block_transfer(data, num); // block_transfer is immediate - make sure CMD buffer is prepared! +} + + +/* this is meant to be called outside display-list building, does not support cmd-burst */ +void EVE_cmd_memcpy(uint32_t dest, uint32_t src, uint32_t num) +{ + EVE_begin_cmd(CMD_MEMCPY); + BUFFER_SPI_DWORD(dest) + BUFFER_SPI_DWORD(src) + BUFFER_SPI_DWORD(num) + + EVE_inc_cmdoffset(12); + + SEND_SPI_BUFFER() +} + + + +/* commands for loading image data into FT8xx CMD memory, with DWORD padding: */ +/* Note: these are immediate SPI transfers and assume the CMD buffer is prepared to process the data, etc... */ +// Note: data should be in DMA-capable memory! +void eve_spi_CMD_write(uint64_t addr, const uint8_t *data, uint16_t len) +{ + // we can use a direct transaction because it is already chunked + disp_spi_transaction(data, len, (disp_spi_send_flag_t)(DISP_SPI_SEND_QUEUED | DISP_SPI_ADDRESS_24 | SPIInherentSendFlags), NULL, (addr | MEM_WRITE_24), 0); + + uint8_t padding = len & 0x03; /* 0, 1, 2 or 3 */ + padding = 4 - padding; /* 4, 3, 2 or 1 */ + padding &= 3; /* 3, 2, 1 or 0 */ + + // padding is another transaction and needs it's own address (this is a DMA requirement) + if(padding) + { + addr += len; + + uint8_t padData[4] = {0}; + disp_spi_transaction(padData, padding, (disp_spi_send_flag_t)(DISP_SPI_SEND_QUEUED | DISP_SPI_ADDRESS_24 | SPIInherentSendFlags), NULL, (addr | MEM_WRITE_24), 0); + + len += padding; + } + + EVE_inc_cmdoffset(len); +} + + +void block_transfer(const uint8_t *data, uint32_t len) +{ + WAIT_SPI(); // SPI commands must be in CMD buffer first + + uint32_t bytes_left; + + bytes_left = len; + while(bytes_left > 0) + { + uint32_t block_len; + block_len = (bytes_left > BLOCK_TRANSFER_SIZE ? BLOCK_TRANSFER_SIZE : bytes_left); + + eve_spi_CMD_write(EVE_RAM_CMD + cmdOffset, data, block_len); + + data += block_len; + bytes_left -= block_len; + + // signal to process data + EVE_cmd_execute(); + } +} + +#if FT81X_FULL +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +void EVE_cmd_inflate(uint32_t ptr, const uint8_t *data, uint16_t len) +{ + EVE_begin_cmd(CMD_INFLATE); + BUFFER_SPI_DWORD(ptr) + + EVE_inc_cmdoffset(4); + + SEND_SPI_BUFFER() + + block_transfer(data, len); // block_transfer is immediate - make sure CMD buffer is prepared! +} + + +#if defined (BT81X_ENABLE) +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +void EVE_cmd_inflate2(uint32_t ptr, uint32_t options, const uint8_t *data, uint16_t len) +{ + EVE_begin_cmd(CMD_INFLATE2); + BUFFER_SPI_DWORD(ptr) + BUFFER_SPI_DWORD(options) + + EVE_inc_cmdoffset(8); + + SEND_SPI_BUFFER() + + if(options == 0) /* direct data, not by Media-FIFO or Flash */ + { + block_transfer(data, len); // block_transfer is immediate - make sure CMD buffer is prepared! + } +} +#endif + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +void EVE_cmd_loadimage(uint32_t ptr, uint32_t options, const uint8_t *data, uint16_t len) +{ + EVE_begin_cmd(CMD_LOADIMAGE); + BUFFER_SPI_DWORD(ptr) + BUFFER_SPI_DWORD(options) + + EVE_inc_cmdoffset(8); + + SEND_SPI_BUFFER() + + #if defined (BT81X_ENABLE) + if( ((options & EVE_OPT_MEDIAFIFO) == 0) && ((options & EVE_OPT_FLASH) == 0) )/* direct data, neither by Media-FIFO or from Flash */ + #elif defined (FT81X_ENABLE) + if((options & EVE_OPT_MEDIAFIFO) == 0) /* direct data, not by Media-FIFO */ + #endif + { + block_transfer(data, len); // block_transfer is immediate - make sure CMD buffer is prepared! + } +} + + +#if defined (FT81X_ENABLE) +/* this is meant to be called outside display-list building, does not support cmd-burst */ +void EVE_cmd_mediafifo(uint32_t ptr, uint32_t size) +{ + EVE_begin_cmd(CMD_MEDIAFIFO); + BUFFER_SPI_DWORD(ptr) + BUFFER_SPI_DWORD(size) + + EVE_inc_cmdoffset(8); + + SEND_SPI_BUFFER() +} +#endif + + +/* this is meant to be called outside display-list building, does not support cmd-burst */ +void EVE_cmd_interrupt(uint32_t ms) +{ + EVE_begin_cmd(CMD_INTERRUPT); + BUFFER_SPI_DWORD(ms) + + EVE_inc_cmdoffset(4); + + SEND_SPI_BUFFER() +} + + +/* this is meant to be called outside display-list building, does not support cmd-burst */ +void EVE_cmd_setfont(uint32_t font, uint32_t ptr) +{ + EVE_begin_cmd(CMD_SETFONT); + BUFFER_SPI_DWORD(font) + BUFFER_SPI_DWORD(ptr) + + EVE_inc_cmdoffset(8); + + SEND_SPI_BUFFER() +} + + +#if defined (FT81X_ENABLE) +/* this is meant to be called outside display-list building, does not support cmd-burst */ +void EVE_cmd_setfont2(uint32_t font, uint32_t ptr, uint32_t firstchar) +{ + EVE_begin_cmd(CMD_SETFONT2); + BUFFER_SPI_DWORD(font) + BUFFER_SPI_DWORD(ptr) + BUFFER_SPI_DWORD(firstchar) + + EVE_inc_cmdoffset(12); + + SEND_SPI_BUFFER() +} +#endif + + +#if defined (FT81X_ENABLE) +/* this is meant to be called outside display-list building, does not support cmd-burst */ +void EVE_cmd_setrotate(uint32_t r) +{ + EVE_begin_cmd(CMD_SETROTATE); + BUFFER_SPI_DWORD(r) + + EVE_inc_cmdoffset(4); + + SEND_SPI_BUFFER() +} +#endif + + +/* this is meant to be called outside display-list building, does not support cmd-burst */ +void EVE_cmd_snapshot(uint32_t ptr) +{ + EVE_begin_cmd(CMD_SNAPSHOT); + BUFFER_SPI_DWORD(ptr) + + EVE_inc_cmdoffset(4); + + SEND_SPI_BUFFER() +} + + +#if defined (FT81X_ENABLE) +/* this is meant to be called outside display-list building, does not support cmd-burst */ +void EVE_cmd_snapshot2(uint32_t fmt, uint32_t ptr, int16_t x0, int16_t y0, int16_t w0, int16_t h0) +{ + EVE_begin_cmd(CMD_SNAPSHOT2); + BUFFER_SPI_DWORD(fmt) + BUFFER_SPI_DWORD(ptr) + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(w0) + BUFFER_SPI_WORD(h0) + + EVE_inc_cmdoffset(16); + + SEND_SPI_BUFFER() +} +#endif + + +/* this is meant to be called outside display-list building, does not support cmd-burst */ +void EVE_cmd_track(int16_t x0, int16_t y0, int16_t w0, int16_t h0, int16_t tag) +{ + EVE_begin_cmd(CMD_TRACK); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(w0) + BUFFER_SPI_WORD(h0) + BUFFER_SPI_WORD(tag) + BUFFER_SPI_WORD(0) + + EVE_inc_cmdoffset(12); + + SEND_SPI_BUFFER() +} + + + +/* commands that return values by writing to the command-fifo */ +/* note: yes, these are different than the functions in the Programmers Guide from FTDI, + this is because I have no idea why anyone would want to pass "result" as an actual argument to these functions + when this only marks the offset the command-processor is writing to */ + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +/* crc32 = EVE_cmd_memcrc(my_ptr_to_some_memory_region, some_amount_of_bytes); */ +uint32_t EVE_cmd_memcrc(uint32_t ptr, uint32_t num) +{ + EVE_begin_cmd(CMD_MEMCRC); + BUFFER_SPI_DWORD(ptr) + BUFFER_SPI_DWORD(num) + BUFFER_SPI_DWORD(0) + + EVE_inc_cmdoffset(8); + + uint16_t offset = cmdOffset; + EVE_inc_cmdoffset(4); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); + + return (EVE_memRead32(EVE_RAM_CMD + offset)); +} + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +/* address = EVE_cmd_getpr(); */ +uint32_t EVE_cmd_getptr(void) +{ + EVE_begin_cmd(CMD_GETPTR); + + uint16_t offset = cmdOffset; + EVE_inc_cmdoffset(4); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); + + return (EVE_memRead32(EVE_RAM_CMD + offset)); +} + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +/* regvalue = EVE_cmd_regread(ptr); */ +/* this seems to be completely pointless, there is no real use for it outside a display-list since the register could be read directly */ +/* and for what purpose would this be implemented to be used in a display list?? */ +uint32_t EVE_cmd_regread(uint32_t ptr) +{ + uint16_t offset; + + EVE_begin_cmd(CMD_REGREAD); + BUFFER_SPI_DWORD(ptr) + + EVE_inc_cmdoffset(4); + + offset = cmdOffset; + EVE_inc_cmdoffset(4); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); + + return (EVE_memRead32(EVE_RAM_CMD + offset)); +} + + +/* + Get the properties of an image after a CMD_LOADIMAGE operation. + + uint32 pointer, width, height; + EVE_LIB_GetProps(&pointer, &width, &height); + + uint32 width, height; + EVE_LIB_GetProps(0, &width, &height); + + this is meant to be called outside display-list building, it includes executing the command + and waiting for completion, does not support cmd-burst +*/ +void EVE_LIB_GetProps(uint32_t *pointer, uint32_t *width, uint32_t *height) +{ + EVE_begin_cmd(CMD_GETPROPS); + + uint16_t offset = cmdOffset; + EVE_inc_cmdoffset(12); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); + + if(pointer) + { + *pointer = EVE_memRead32(EVE_RAM_CMD + offset); + } + if(width) + { + *width = EVE_memRead32(EVE_RAM_CMD + offset + 4); + } + if(height) + { + *height = EVE_memRead32(EVE_RAM_CMD + offset + 8); + } +} + + +/* FT811 / FT813 binary-blob from FTDIs AN_336 to patch the touch-engine for Goodix GT911 / GT9271 touch controllers */ +#if defined (EVE_HAS_GT911) + +#if defined (__AVR__) +#include +#else +#define PROGMEM +#endif + +const uint16_t EVE_GT911_len = 1184; +const uint8_t EVE_GT911_data[1184] PROGMEM = +{ + 26,255,255,255,32,32,48,0,4,0,0,0,2,0,0,0, + 34,255,255,255,0,176,48,0,120,218,237,84,221,111,84,69,20,63,51,179,93,160,148,101,111,76,5,44,141,123,111,161,11,219,154,16,9,16,17,229,156,75,26,11,13,21,227,3,16,252,184,179, + 45,219,143,45,41,125,144,72,67,100,150,71,189,113,18,36,17,165,100,165,198,16,32,17,149,196,240,128,161,16,164,38,54,240,0,209,72,130,15,38,125,48,66,82,30,76,19,31,172,103,46, + 139,24,255,4,227,157,204,156,51,115,102,206,231,239,220,5,170,94,129,137,75,194,216,98,94,103,117,115,121,76,131,177,125,89,125,82,123,60,243,58,142,242,204,185,243,188,118,156, + 227,155,203,238,238,195,251,205,229,71,92,28,169,190,184,84,143,113,137,53,244,103,181,237,87,253,113,137,233,48,12,198,165,181,104,139,25,84,253,155,114,74,191,0,54,138,163, + 12,62,131,207,129,23,217,34,91,31,128,65,246,163,175,213,8,147,213,107,35,203,94,108,3,111,40,171,83,24,15,165,177,222,116,97,23,188,140,206,150,42,102,181,87,78,86,182,170,134, + 215,241,121,26,243,252,2,76,115,217,139,222,206,173,136,132,81,61,35,185,39,113,23,46,199,76,178,54,151,183,224,0,40,189,28,149,182,58,131,79,152,30,76,34,98,234,162,216,133,141, + 102,39,170,40,192,101,53,201,146,191,37,77,44,177,209,74,211,5,206,187,5,6,216,47,53,96,123,22,50,103,251,192,84,17,74,227,185,56,106,51,91,161,96,182,163,48,171,141,139,65,152, + 66,66,11,102,43,158,75,36,80,147,184,147,139,112,17,235,216,103,111,239,245,92,10,175,194,40,44,58,125,5,59,112,50,103,245,4,78,192,5,156,194,51,60,191,134,75,110,173,237,46,192, + 121,156,192,115,184,218,120,67,63,115,46,11,102,10,97,232,50,235,114,182,148,118,178,41,188,12,135,77,202,124,12,96,238,35,161,234,189,129,23,249,212,139,230,25,53,48,205,52,93, + 163,117,53,154,170,81,85,163,178,70,69,66,167,241,14,46,241,1,226,136,152,179,197,59,184,148,254,49,132,48,15,176,137,192,76,131,196,105,104,162,86,81,160,165,255,26,173,162,137, + 86,145,210,183,192,55,175,194,211,60,91,120,230,184,174,27,41,131,155,40,224,29,87,179,232,16,55,55,7,165,147,81,23,165,49,101,54,224,75,180,81,108,18,29,226,69,225,110,175,224, + 42,212,25,47,130,193,110,234,192,215,252,56,74,162,24,46,251,174,54,106,68,245,14,9,155,160,22,120,207,104,240,29,90,178,140,28,24,220,47,166,112,61,251,208,192,111,56,239,238, + 93,255,251,62,99,32,193,75,61,190,235,123,229,110,218,194,85,79,225,59,98,20,238,227,235,220,11,221,149,25,180,116,194,159,111,96,192,24,213,59,139,179,156,215,69,230,19,24,35, + 135,117,206,171,206,162,67,129,234,61,235,11,104,103,84,64,223,167,254,40,163,101,92,84,43,150,46,249,219,205,7,116,11,91,104,61,57,75,223,8,48,25,28,119,252,222,113,49,86,249, + 74,180,211,156,181,61,215,168,157,7,251,199,150,242,250,91,58,132,94,121,7,53,151,139,98,6,165,153,69,214,32,110,211,100,101,31,89,45,81,98,23,205,205,197,209,109,186,198,35, + 141,191,249,25,60,132,223,153,251,98,20,239,146,139,20,217,250,41,250,137,58,177,90,57,79,51,108,233,20,253,194,187,49,222,205,114,141,96,48,175,219,107,54,111,138,22,154,103, + 108,79,58,252,179,178,79,164,195,2,153,36,39,170,199,201,167,197,85,106,8,59,177,81,46,56,2,230,75,114,17,55,112,188,65,208,137,77,114,10,115,55,58,208,197,173,122,87,6,140, + 110,42,208,124,163,70,108,241,104,18,245,98,214,187,134,53,42,221,22,182,133,211,116,148,177,194,209,192,85,90,199,58,55,203,2,229,19,137,187,161,228,154,112,203,145,125,244, + 188,220,118,228,41,201,181,41,195,144,215,183,51,80,250,21,217,16,217,200,235,109,227,188,122,218,142,60,170,224,112,240,184,130,229,224,113,5,223,148,163,80,165,183,130,187, + 132,116,64,238,161,85,220,115,139,205,98,227,244,29,102,125,7,37,243,123,223,11,26,92,63,243,116,61,191,138,123,244,160,84,186,74,31,5,174,247,119,135,199,248,253,135,242,97, + 102,145,190,144,14,85,238,221,231,193,158,48,205,25,120,248,15,220,29,158,9,70,185,30,103,229,33,254,23,237,160,172,62,193,90,222,224,232,14,200,56,90,104,142,227,120,110,6, + 21,211,203,65,150,99,151,220,247,87,164,50,159,49,239,234,58,142,0,109,108,123,18,79,227,36,100,248,222,205,96,127,120,26,171,228,69,63,36,17,252,200,17,116,242,187,227,88,143, + 247,2,75,191,6,130,59,188,11,55,240,31,243,122,152,226,183,207,154,73,188,39,219,43,105,222,87,41,143,141,140,175,73,112,184,252,61,184,16,90,250,35,168,82,119,176,57,116,94, + 200,150,22,190,179,44,104,12,235,84,149,102,252,89,154,193,99,228,106,242,125,248,64,194,255,223,127,242,83,11,255,2,70,214,226,128,0,0 +}; +#endif +#endif // FT81X_FULL + + +/* init, has to be executed with the SPI setup to 11 MHz or less as required by FT8xx / BT8xx */ +uint8_t EVE_init(void) +{ + uint8_t chipid = 0; + uint16_t timeout = 0; + + EVE_pdn_set(); + DELAY_MS(6); /* minimum time for power-down is 5ms */ + + EVE_pdn_clear(); + DELAY_MS(21); /* minimum time to allow from rising PD_N to first access is 20ms */ + + /* EVE_cmdWrite(EVE_CORERST,0); */ /* reset, only required for warm-start if PowerDown line is not used */ + +#if defined (EVE_HAS_CRYSTAL) + EVE_cmdWrite(EVE_CLKEXT, 0); /* setup EVE for external clock */ +#else + EVE_cmdWrite(EVE_CLKINT, 0); /* setup EVE for internal clock */ +#endif + +#if defined (BT81X_ENABLE) + EVE_cmdWrite(EVE_CLKSEL ,0x46); /* set clock to 72 MHz */ +#endif + + EVE_cmdWrite(EVE_ACTIVE, 0); /* start EVE */ + + /* BRT AN033 BT81X_Series_Programming_Guide V1.2 had a small change to chapter 2.4 "Initialization Sequence during Boot Up" */ + /* Send Host command “ACTIVE” and wait for at least 300 milliseconds. */ + /* Ensure that there is no SPI access during this time. */ + /* I asked Bridgetek for clarification why this has been made stricter. */ + /* From observation with quite a few of different displays I do not agree that either the 300ms are necessary or that */ + /* *reading* the SPI while EVE inits itself is causing any issues. */ + /* But since BT815 at 72MHz need 42ms anyways before they start to answer, here is my compromise, a fixed 40ms delay */ + /* to provide at least a short moment of silence for EVE */ + DELAY_MS(40); + + /* The most reliable DIO/QIO switching point is after EVE start up but before reading the ChipID. */ +#if defined(DISP_SPI_TRANS_MODE_DIO) + ESP_LOGI(TAG, "Switching to DIO mode"); + DELAY_MS(20); /* different boards may take a different delay but this generally seems to work */ + EVE_memWrite16(REG_SPI_WIDTH, SPI_WIDTH_DIO); + SPIInherentSendFlags = DISP_SPI_MODE_DIO | DISP_SPI_MODE_DIOQIO_ADDR; + SPIDummyReadBits = 4; /* Esp32 DMA SPI transaction dummy_bits works more like clock cycles, so in DIO 4 dummy_bits == 8 total bits */ +#elif defined(DISP_SPI_TRANS_MODE_QIO) + ESP_LOGI(TAG, "Switching to QIO mode"); + DELAY_MS(20); /* different boards may take a different delay but this generally seems to work */ + EVE_memWrite16(REG_SPI_WIDTH, SPI_WIDTH_QIO); + SPIInherentSendFlags = DISP_SPI_MODE_QIO | DISP_SPI_MODE_DIOQIO_ADDR; + SPIDummyReadBits = 2; /* Esp32 DMA SPI transaction dummy_bits works more like clock cycles, so in QIO 2 dummy_bits == 8 total bits */ +#elif defined(DISP_SPI_HALF_DUPLEX) + SPIDummyReadBits = 8; /* SIO half-duplex mode */ +#endif + + while(chipid != 0x7C) /* if chipid is not 0x7c, continue to read it until it is, EVE needs a moment for it's power on self-test and configuration */ + { + DELAY_MS(1); + chipid = EVE_memRead8(REG_ID); + timeout++; + if(timeout > 400) + { + ESP_LOGI(TAG, "Failed to read ChipID...aborting initialization."); + return 0; + } + } + + timeout = 0; + while (0x00 != (EVE_memRead8(REG_CPURESET) & 0x03)) /* check if EVE is in working status */ + { + DELAY_MS(1); + timeout++; + if(timeout > 50) /* experimental, 10 was the lowest value to get the BT815 started with, the touch-controller was the last to get out of reset */ + { + ESP_LOGI(TAG, "Failed to read CPU status...aborting initialization."); + return 0; + } + } + + /* tell EVE that we changed the frequency from default to 72MHz for BT8xx */ +#if defined (BT81X_ENABLE) + EVE_memWrite32(REG_FREQUENCY, 72000000); +#endif + + /* we have a display with a Goodix GT911 / GT9271 touch-controller on it, so we patch our FT811 or FT813 according to AN_336 or setup a BT815 accordingly */ +#if defined (EVE_HAS_GT911) + + #if defined (BT81X_ENABLE) + EVE_memWrite32(REG_TOUCH_CONFIG, 0x000005d1); /* switch to Goodix touch controller */ + #else + + EVE_get_cmdoffset(); + + BUFFER_SPI_WRITE_ADDRESS(EVE_RAM_CMD + cmdOffset) + SEND_SPI_BUFFER() + + spi_flash_write(EVE_GT911_data, EVE_GT911_len); + + EVE_cmd_execute(); + + EVE_memWrite8(REG_TOUCH_OVERSAMPLE, 0x0f); /* setup oversample to 0x0f as "hidden" in binary-blob for AN_336 */ + EVE_memWrite16(REG_TOUCH_CONFIG, 0x05D0); /* write magic cookie as requested by AN_336 */ + + /* specific to the EVE2 modules from Matrix-Orbital we have to use GPIO3 to reset GT911 */ + EVE_memWrite16(REG_GPIOX_DIR, 0x8008); /* Reset-Value is 0x8000, adding 0x08 sets GPIO3 to output, default-value for REG_GPIOX is 0x8000 -> Low output on GPIO3 */ + DELAY_MS(1); /* wait more than 100µs */ + EVE_memWrite8(REG_CPURESET, 0x00); /* clear all resets */ + DELAY_MS(56); /* wait more than 55ms */ + EVE_memWrite16(REG_GPIOX_DIR, 0x8000); /* setting GPIO3 back to input */ + #endif +#endif + + /* EVE_memWrite8(REG_PCLK, 0x00); */ /* set PCLK to zero - don't clock the LCD until later, line disabled because zero is reset-default and we just did a reset */ + +#if defined (EVE_ADAM101) + EVE_memWrite8(REG_PWM_DUTY, 0x80); /* turn off backlight for Glyn ADAM101 module, it uses inverted values */ +#else + EVE_memWrite8(REG_PWM_DUTY, 0); /* turn off backlight for any other module */ +#endif + + /* Initialize Display */ + EVE_memWrite16(REG_HSIZE, EVE_HSIZE); /* active display width */ + EVE_memWrite16(REG_HCYCLE, EVE_HCYCLE); /* total number of clocks per line, incl front/back porch */ + EVE_memWrite16(REG_HOFFSET, EVE_HOFFSET); /* start of active line */ + EVE_memWrite16(REG_HSYNC0, EVE_HSYNC0); /* start of horizontal sync pulse */ + EVE_memWrite16(REG_HSYNC1, EVE_HSYNC1); /* end of horizontal sync pulse */ + EVE_memWrite16(REG_VSIZE, EVE_VSIZE); /* active display height */ + EVE_memWrite16(REG_VCYCLE, EVE_VCYCLE); /* total number of lines per screen, including pre/post */ + EVE_memWrite16(REG_VOFFSET, EVE_VOFFSET); /* start of active screen */ + EVE_memWrite16(REG_VSYNC0, EVE_VSYNC0); /* start of vertical sync pulse */ + EVE_memWrite16(REG_VSYNC1, EVE_VSYNC1); /* end of vertical sync pulse */ + EVE_memWrite8(REG_SWIZZLE, EVE_SWIZZLE); /* FT8xx output to LCD - pin order */ + EVE_memWrite8(REG_PCLK_POL, EVE_PCLKPOL); /* LCD data is clocked in on this PCLK edge */ + EVE_memWrite8(REG_CSPREAD, EVE_CSPREAD); /* helps with noise, when set to 1 fewer signals are changed simultaneously, reset-default: 1 */ + + /* do not set PCLK yet - wait for just after the first display list */ + + /* configure Touch */ + EVE_memWrite8(REG_TOUCH_MODE, EVE_TMODE_CONTINUOUS); /* enable touch */ + EVE_memWrite16(REG_TOUCH_RZTHRESH, EVE_TOUCH_RZTHRESH); /* eliminate any false touches */ + + /* disable Audio for now */ + EVE_memWrite8(REG_VOL_PB, 0x00); /* turn recorded audio volume down */ + EVE_memWrite8(REG_VOL_SOUND, 0x00); /* turn synthesizer volume off */ + EVE_memWrite16(REG_SOUND, 0x6000); /* set synthesizer to mute */ + + /* write a basic display-list to get things started */ + EVE_memWrite32(EVE_RAM_DL, DL_CLEAR_RGB); + EVE_memWrite32(EVE_RAM_DL + 4, (DL_CLEAR | CLR_COL | CLR_STN | CLR_TAG)); + EVE_memWrite32(EVE_RAM_DL + 8, DL_DISPLAY); /* end of display list */ + EVE_memWrite32(REG_DLSWAP, EVE_DLSWAP_FRAME); + + /* nothing is being displayed yet... the pixel clock is still 0x00 */ + EVE_memWrite8(REG_GPIO, 0x80); /* enable the DISP signal to the LCD panel, it is set to output in REG_GPIO_DIR by default */ + EVE_memWrite8(REG_PCLK, EVE_PCLK); /* now start clocking data to the LCD panel */ + +#if defined (EVE_ADAM101) + EVE_memWrite8(REG_PWM_DUTY, 0x60); /* turn on backlight to 25% for Glyn ADAM101 module, it uses inverted values */ +#else + EVE_memWrite8(REG_PWM_DUTY, 0x20); /* turn on backlight to 25% for any other module */ +#endif + + timeout = 0; + while(EVE_busy()) /* just to be safe, should not even enter the loop */ + { + DELAY_MS(1); + timeout++; + if(timeout > 4) + { + break; /* something is wrong here, but since we made it this far through the init, just leave the loop */ + } + } + + EVE_get_cmdoffset(); /* just to be safe */ + +#if defined (EVE_DMA) + EVE_init_dma(); /* prepare DMA */ +#endif + + return 1; +} + + +/* +These eliminate the overhead of transmitting the command-fifo address with every single command, just wrap a sequence of commands +with these and the address is only transmitted once at the start of the block. +Be careful to not use any functions in the sequence that do not address the command-fifo as for example any EVE_mem...() function. +*/ +void EVE_start_cmd_burst(void) +{ + cmd_burst = 42; + + WAIT_SPI() // it is important to wait before writing to the SPI buffer as it might be in a DMA transaction + BUFFER_SPI_WRITE_ADDRESS(EVE_RAM_CMD + cmdOffset) +} + + +void EVE_end_cmd_burst(void) +{ + cmd_burst = 0; + + SEND_SPI_BUFFER() +} + + +/* begin a co-processor command */ +void EVE_start_cmd(uint32_t command) +{ + if(!cmd_burst) + { + WAIT_SPI() // it is important to wait before writing to the SPI buffer as it might be in a DMA transaction + BUFFER_SPI_WRITE_ADDRESS(EVE_RAM_CMD + cmdOffset) + } + + BUFFER_SPI_DWORD(command) + + EVE_inc_cmdoffset(4); /* update the command-ram pointer */ +} + + +/* generic function for all commands that have no arguments and all display-list specific control words */ +/* + examples: + EVE_cmd_dl(CMD_DLSTART); + EVE_cmd_dl(CMD_SWAP); + EVE_cmd_dl(CMD_SCREENSAVER); + EVE_cmd_dl(LINE_WIDTH(1*16)); + EVE_cmd_dl(VERTEX2F(0,0)); + EVE_cmd_dl(DL_BEGIN | EVE_RECTS); +*/ +void EVE_cmd_dl(uint32_t command) +{ + if(cmd_burst) + { + BUFFER_SPI_DWORD(command) + + EVE_inc_cmdoffset(4); /* update the command-ram pointer */ + } + else + { + EVE_start_cmd(command); + SEND_SPI_BUFFER() + } +} + +#if FT81X_FULL +/* write a string to co-processor memory in context of a command: no chip-select, just plain SPI-transfers */ +/* note: assumes cmdOffset is already DWORD aligned */ +void EVE_write_string(const char *text) +{ + uint8_t textindex = 0; + uint8_t padding = 0; + uint8_t *bytes = (uint8_t *) text; /* need to handle the array as bunch of bytes */ + + while(bytes[textindex] != 0) + { + BUFFER_SPI_BYTE(bytes[textindex]); + textindex++; + if(textindex > 249 || SPIBufferIndex >= SPI_BUFFER_SIZE) /* there appears to be no end for the "string", or no more room in SPI buffer, so leave */ + { + break; + } + } + + /* we need to transmit at least one 0x00 byte and up to four if the string happens to be 4-byte aligned already */ + padding = textindex & 3; /* 0, 1, 2 or 3 */ + padding = 4-padding; /* 4, 3, 2 or 1 */ + textindex += padding; + + while(padding > 0 && SPIBufferIndex < SPI_BUFFER_SIZE) + { + BUFFER_SPI_BYTE(0); + padding--; + } + + EVE_inc_cmdoffset(textindex); +} + + +/* EVE3 FLASH functions */ +#if defined (BT81X_ENABLE) + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +/* write "num" bytes from *data to the external flash on a BT81x board at address ptr */ +/* note: ptr must be 256 byte aligned, num must be a multiple of 256 */ +/* note: EVE will not do anything if the alignment requirements are not met */ +/* note: the address ptr is relative to the flash so the first address is 0x00000000 not 0x800000 */ +/* note: on AVR controllers this expects the data to be located in the controllers flash memory */ +void EVE_cmd_flashwrite(uint32_t ptr, uint32_t num, const uint8_t *data) +{ + EVE_begin_cmd(CMD_FLASHWRITE); + BUFFER_SPI_DWORD(ptr) + BUFFER_SPI_DWORD(num) + + EVE_inc_cmdoffset(8); + + SEND_SPI_BUFFER() + + WAIT_SPI() + block_transfer(data, num); +} + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +/* write "num" bytes from src in the external flash on a BT81x board to dest in RAM_G */ +/* note: src must be 64-byte aligned, dest must be 4-byte aligned, num must be a multiple of 4 */ +/* note: EVE will not do anything if the alignment requirements are not met */ +/* note: the src pointer is relative to the flash so the first address is 0x00000000 not 0x800000 */ +void EVE_cmd_flashread(uint32_t dest, uint32_t src, uint32_t num) +{ + EVE_begin_cmd(CMD_FLASHREAD); + BUFFER_SPI_DWORD(dest) + BUFFER_SPI_DWORD(src) + BUFFER_SPI_DWORD(num) + + EVE_inc_cmdoffset(12); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); +} + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +/* write "num" bytes from src in RAM_G to to the external flash on a BT81x board at address dest */ +/* note: dest must be 4096-byte aligned, src must be 4-byte aligned, num must be a multiple of 4096 */ +/* note: EVE will not do anything if the alignment requirements are not met */ +/* note: the address ptr is relative to the flash so the first address is 0x00000000 not 0x800000 */ +void EVE_cmd_flashupdate(uint32_t dest, uint32_t src, uint32_t num) +{ + EVE_begin_cmd(CMD_FLASHUPDATE); + BUFFER_SPI_DWORD(dest) + BUFFER_SPI_DWORD(src) + BUFFER_SPI_DWORD(num) + + EVE_inc_cmdoffset(12); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); +} + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +/* this is added for conveniance, using EVE_cmd_dl(CMD_FLASHERASE); followed by EVE_cmd_execute(); would work as well */ +void EVE_cmd_flasherase(void) +{ + EVE_begin_cmd(CMD_FLASHERASE); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); +} + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +/* this is added for conveniance, using EVE_cmd_dl(CMD_FLASHATTACH); followed by EVE_cmd_execute(); would work as well */ +void EVE_cmd_flashattach(void) +{ + EVE_begin_cmd(CMD_FLASHATTACH); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); +} + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +/* this is added for conveniance, using EVE_cmd_dl(CMD_FLASHDETACH); followed by EVE_cmd_execute(); would work as well */ +void EVE_cmd_flashdetach(void) +{ + EVE_begin_cmd(CMD_FLASHDETACH); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); +} + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +/* this is added for conveniance, using EVE_cmd_dl(CMD_FLASHSPIDESEL); followed by EVE_cmd_execute(); would work as well */ +void EVE_cmd_flashspidesel(void) +{ + EVE_begin_cmd(CMD_FLASHSPIDESEL); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); +} + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +uint32_t EVE_cmd_flashfast(void) +{ + uint16_t offset; + + EVE_begin_cmd(CMD_FLASHFAST); + BUFFER_SPI_DWORD(0) + + offset = cmdOffset; + EVE_inc_cmdoffset(4); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); + + return EVE_memRead32(EVE_RAM_CMD + offset); +} + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +/* write "num" bytes from *data to the BT81x SPI interface */ +/* note: raw direct access, not really useful for anything */ +void EVE_cmd_flashspitx(uint32_t num, const uint8_t *data) +{ + EVE_begin_cmd(CMD_FLASHSPITX); + BUFFER_SPI_DWORD(num) + + EVE_inc_cmdoffset(4); + + SEND_SPI_BUFFER() + + WAIT_SPI() + block_transfer(data, num); +} + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +/* write "num" bytes from the BT81x SPI interface dest in RAM_G */ +/* note: raw direct access, not really useful for anything */ +void EVE_cmd_flashspirx(uint32_t dest, uint32_t num) +{ + EVE_begin_cmd(CMD_FLASHREAD); + BUFFER_SPI_DWORD(dest) + BUFFER_SPI_DWORD(num) + + EVE_inc_cmdoffset(8); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); +} + + +/* this is meant to be called outside display-list building, it includes executing the command and waiting for completion, does not support cmd-burst */ +void EVE_cmd_flashsource(uint32_t ptr) +{ + EVE_begin_cmd(CMD_FLASHSOURCE); + BUFFER_SPI_DWORD(ptr) + + EVE_inc_cmdoffset(4); + + SEND_SPI_BUFFER() + + EVE_cmd_execute(); +} + + +/* switch the FLASH attached to a BT815/BT816 to full-speed mode, returns 0 for failing to do so, does not support cmd-burst */ +uint8_t EVE_init_flash(void) +{ + uint8_t timeout = 0; + uint8_t status; + + status = EVE_memRead8(REG_FLASH_STATUS); /* should be 0x02 - FLASH_STATUS_BASIC, power-up is done and the attached flash is detected */ + + while(status == 0) /* FLASH_STATUS_INIT - we are somehow still in init, give it a litte more time, this should never happen */ + { + status = EVE_memRead8(REG_FLASH_STATUS); + DELAY_MS(1); + timeout++; + if(timeout > 100) /* 100ms and still in init, lets call quits now and exit with an error */ + { + return 0; + } + } + + if(status == 1) /* FLASH_STATUS_DETACHED - no flash was found during init, no flash present or the detection failed, but have hope and let the BT81x have annother try */ + { + EVE_cmd_dl(CMD_FLASHATTACH); + EVE_cmd_execute(); + status = EVE_memRead8(REG_FLASH_STATUS); + if(status != 2) /* still not in FLASH_STATUS_BASIC, time to give up */ + { + return 0; + } + } + + if(status == 2) /* FLASH_STATUS_BASIC - flash detected and ready for action, lets move it up to FLASH_STATUS_FULL */ + { + uint32_t result; + + result = EVE_cmd_flashfast(); + + if(result == 0) /* cmd_flashfast was successful */ + { + return 1; + } + else /* room for improvement, cmd_flashfast provided an error code but there is no way to return it without returning a value that is FALSE all the same */ + { + return 0; + } + } + + if(status == 3) /* FLASH_STATUS_FULL - we are already there, why has this function been called? */ + { + return 1; + } + + return 0; +} +#endif + + +/* commands to draw graphics objects: */ + + +#if defined (BT81X_ENABLE) +/* as the name implies, "num_args" is the number of arguments passed to this function as variadic arguments */ +void EVE_cmd_text_var(int16_t x0, int16_t y0, int16_t font, uint16_t options, const char* text, uint8_t num_args, ...) +{ + va_list arguments; + uint8_t counter; + uint32_t data; + + va_start(arguments, num_args); + + BUFFER_SPI_DWORD(CMD_TEXT) + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(font) + BUFFER_SPI_WORD(options) + + EVE_inc_cmdoffset(12); + + EVE_write_string(text); + + if(options & EVE_OPT_FORMAT) + { + for(counter = 0; counter < num_args; counter++) + { + data = (uint32_t) va_arg(arguments, int); + BUFFER_SPI_DWORD(data) + EVE_inc_cmdoffset(4); + } + } + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } + + va_end(arguments); +} +#endif + + +void EVE_cmd_text(int16_t x0, int16_t y0, int16_t font, uint16_t options, const char* text) +{ + EVE_start_cmd(CMD_TEXT); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(font) + BUFFER_SPI_WORD(options) + + EVE_inc_cmdoffset(8); + + EVE_write_string(text); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +#if defined (BT81X_ENABLE) +/* as the name implies, "num_args" is the number of arguments passed to this function as variadic arguments */ +void EVE_cmd_button_var(int16_t x0, int16_t y0, int16_t w0, int16_t h0, int16_t font, uint16_t options, const char* text, uint8_t num_args, ...) +{ + va_list arguments; + uint8_t counter; + uint32_t data; + + va_start(arguments, num_args); + + EVE_start_cmd(CMD_BUTTON); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(w0) + BUFFER_SPI_WORD(h0) + BUFFER_SPI_WORD(font) + BUFFER_SPI_WORD(options) + + EVE_inc_cmdoffset(12); + + EVE_write_string(text); + + if(options & EVE_OPT_FORMAT) + { + for(counter = 0; counter < num_args; counter++) + { + data = (uint32_t) va_arg(arguments, int); + BUFFER_SPI_DWORD(data) + EVE_inc_cmdoffset(4); + } + } + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } + + va_end(arguments); +} +#endif + + +void EVE_cmd_button(int16_t x0, int16_t y0, int16_t w0, int16_t h0, int16_t font, uint16_t options, const char* text) +{ + EVE_start_cmd(CMD_BUTTON); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(w0) + BUFFER_SPI_WORD(h0) + BUFFER_SPI_WORD(font) + BUFFER_SPI_WORD(options) + + EVE_inc_cmdoffset(12); + + EVE_write_string(text); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +/* draw a clock */ +void EVE_cmd_clock(int16_t x0, int16_t y0, int16_t r0, uint16_t options, uint16_t hours, uint16_t minutes, uint16_t seconds, uint16_t millisecs) +{ + EVE_start_cmd(CMD_CLOCK); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(r0) + BUFFER_SPI_WORD(options) + BUFFER_SPI_WORD(hours) + BUFFER_SPI_WORD(minutes) + BUFFER_SPI_WORD(seconds) + BUFFER_SPI_WORD(millisecs) + + EVE_inc_cmdoffset(16); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_color_rgb(uint8_t red, uint8_t green, uint8_t blue) +{ + BUFFER_SPI_BYTE(green) /* low-byte */ + BUFFER_SPI_BYTE(blue) + BUFFER_SPI_BYTE(red) + BUFFER_SPI_BYTE(0x04) /* encoding for COLOR_RGB */ + + EVE_inc_cmdoffset(4); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_bgcolor(uint32_t color) +{ + EVE_start_cmd(CMD_BGCOLOR); + BUFFER_SPI_DWORD(color & 0x00ffffff) + + EVE_inc_cmdoffset(4); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_fgcolor(uint32_t color) +{ + EVE_start_cmd(CMD_FGCOLOR); + BUFFER_SPI_DWORD(color & 0x00ffffff) + + EVE_inc_cmdoffset(4); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_gradcolor(uint32_t color) +{ + EVE_start_cmd(CMD_GRADCOLOR); + BUFFER_SPI_DWORD(color & 0x00ffffff) + + EVE_inc_cmdoffset(4); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_gauge(int16_t x0, int16_t y0, int16_t r0, uint16_t options, uint16_t major, uint16_t minor, uint16_t val, uint16_t range) +{ + EVE_start_cmd(CMD_GAUGE); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(r0) + BUFFER_SPI_WORD(options) + BUFFER_SPI_WORD(major) + BUFFER_SPI_WORD(minor) + BUFFER_SPI_WORD(val) + BUFFER_SPI_WORD(range) + + EVE_inc_cmdoffset(16); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_gradient(int16_t x0, int16_t y0, uint32_t rgb0, int16_t x1, int16_t y1, uint32_t rgb1) +{ + EVE_start_cmd(CMD_GRADIENT); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_DWORD(rgb0 & 0x00ffffff) + BUFFER_SPI_WORD(x1) + BUFFER_SPI_WORD(y1) + BUFFER_SPI_DWORD(rgb1 & 0x00ffffff) + + EVE_inc_cmdoffset(16); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_keys(int16_t x0, int16_t y0, int16_t w0, int16_t h0, int16_t font, uint16_t options, const char* text) +{ + EVE_start_cmd(CMD_KEYS); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(w0) + BUFFER_SPI_WORD(h0) + BUFFER_SPI_WORD(font) + BUFFER_SPI_WORD(options) + + EVE_inc_cmdoffset(12); + + EVE_write_string(text); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_progress(int16_t x0, int16_t y0, int16_t w0, int16_t h0, uint16_t options, uint16_t val, uint16_t range) +{ + EVE_start_cmd(CMD_PROGRESS); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(w0) + BUFFER_SPI_WORD(h0) + BUFFER_SPI_WORD(options) + BUFFER_SPI_WORD(val) + BUFFER_SPI_WORD(range) + BUFFER_SPI_WORD(0) /* dummy word for 4-byte alignment */ + + EVE_inc_cmdoffset(16); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_scrollbar(int16_t x0, int16_t y0, int16_t w0, int16_t h0, uint16_t options, uint16_t val, uint16_t size, uint16_t range) +{ + EVE_start_cmd(CMD_SCROLLBAR); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(w0) + BUFFER_SPI_WORD(h0) + BUFFER_SPI_WORD(options) + BUFFER_SPI_WORD(val) + BUFFER_SPI_WORD(size) + BUFFER_SPI_WORD(range) + + EVE_inc_cmdoffset(16); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_slider(int16_t x1, int16_t y1, int16_t w1, int16_t h1, uint16_t options, uint16_t val, uint16_t range) +{ + EVE_start_cmd(CMD_SLIDER); + BUFFER_SPI_WORD(x1) + BUFFER_SPI_WORD(y1) + BUFFER_SPI_WORD(w1) + BUFFER_SPI_WORD(h1) + BUFFER_SPI_WORD(options) + BUFFER_SPI_WORD(val) + BUFFER_SPI_WORD(range) + BUFFER_SPI_WORD(0) /* dummy word for 4-byte alignment */ + + EVE_inc_cmdoffset(16); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_dial(int16_t x0, int16_t y0, int16_t r0, uint16_t options, uint16_t val) +{ + EVE_start_cmd(CMD_DIAL); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(r0) + BUFFER_SPI_WORD(options) + BUFFER_SPI_WORD(val) + BUFFER_SPI_WORD(0) /* dummy word for 4-byte alignment */ + + EVE_inc_cmdoffset(12); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +#if defined (BT81X_ENABLE) +/* as the name implies, "num_args" is the number of arguments passed to this function as variadic arguments */ +void EVE_cmd_toggle_var(int16_t x0, int16_t y0, int16_t w0, int16_t font, uint16_t options, uint16_t state, const char* text, uint8_t num_args, ...) +{ + va_list arguments; + uint8_t counter; + uint32_t data; + + va_start(arguments, num_args); + + EVE_start_cmd(CMD_TOGGLE); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(w0) + BUFFER_SPI_WORD(font) + BUFFER_SPI_WORD(options) + BUFFER_SPI_WORD(state) + + EVE_inc_cmdoffset(12); + + EVE_write_string(text); + + if(options & EVE_OPT_FORMAT) + { + for(counter = 0; counter < num_args; counter++) + { + data = (uint32_t) va_arg(arguments, int); + BUFFER_SPI_DWORD(data) + EVE_inc_cmdoffset(4); + } + } + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } + + va_end(arguments); +} +#endif + + +void EVE_cmd_toggle(int16_t x0, int16_t y0, int16_t w0, int16_t font, uint16_t options, uint16_t state, const char* text) +{ + EVE_start_cmd(CMD_TOGGLE); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(font) + BUFFER_SPI_WORD(options) + BUFFER_SPI_WORD(state) + + EVE_inc_cmdoffset(12); + + EVE_write_string(text); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +#if defined (FT81X_ENABLE) +void EVE_cmd_setbase(uint32_t base) +{ + EVE_start_cmd(CMD_SETBASE); + BUFFER_SPI_DWORD(base); + + EVE_inc_cmdoffset(4); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} +#endif +#endif // FT81X_FULL + + +#if defined (FT81X_ENABLE) +void EVE_cmd_setbitmap(uint32_t addr, uint16_t fmt, uint16_t width, uint16_t height) +{ + EVE_start_cmd(CMD_SETBITMAP); + BUFFER_SPI_DWORD(addr) + BUFFER_SPI_WORD(fmt) + BUFFER_SPI_WORD(width) + BUFFER_SPI_WORD(height) + BUFFER_SPI_WORD(0) + + EVE_inc_cmdoffset(12); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } + +} +#endif + +#if FT81X_FULL +void EVE_cmd_number(int16_t x0, int16_t y0, int16_t font, uint16_t options, int32_t number) +{ + EVE_start_cmd(CMD_NUMBER); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(font) + BUFFER_SPI_WORD(options) + BUFFER_SPI_DWORD(number) + + EVE_inc_cmdoffset(12); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_append(uint32_t ptr, uint32_t num) +{ + EVE_start_cmd(CMD_APPEND); + BUFFER_SPI_DWORD(ptr) + BUFFER_SPI_DWORD(num) + + EVE_inc_cmdoffset(8); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +/* commands for setting the bitmap transform matrix: */ + +/* + The description in the programmers guide is strange for this function. + While it is named *get*matrix, parameters 'a' to 'f' are supplied to the function + and described as "output parameter" + Best guess is that this one allows to setup the matrix coefficients manually. + If this assumption is correct it rather should be named cmd_setupmatrix(). +*/ +void EVE_cmd_getmatrix(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e, int32_t f) +{ + EVE_start_cmd(CMD_GETMATRIX); + BUFFER_SPI_DWORD(a) + BUFFER_SPI_DWORD(b) + BUFFER_SPI_DWORD(c) + BUFFER_SPI_DWORD(d) + BUFFER_SPI_DWORD(e) + BUFFER_SPI_DWORD(f) + + EVE_inc_cmdoffset(24); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_translate(int32_t tx, int32_t ty) +{ + EVE_start_cmd(CMD_TRANSLATE); + BUFFER_SPI_DWORD(tx) + BUFFER_SPI_DWORD(ty) + + EVE_inc_cmdoffset(8); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_scale(int32_t sx, int32_t sy) +{ + EVE_start_cmd(CMD_SCALE); + BUFFER_SPI_DWORD(sx) + BUFFER_SPI_DWORD(sy) + + EVE_inc_cmdoffset(8); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_rotate(int32_t ang) +{ + EVE_start_cmd(CMD_ROTATE); + BUFFER_SPI_DWORD(ang) + + EVE_inc_cmdoffset(4); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +#if defined (BT81X_ENABLE) +void EVE_cmd_rotatearound(int32_t x0, int32_t y0, int32_t angle, int32_t scale) +{ + EVE_start_cmd(CMD_ROTATEAROUND); + BUFFER_SPI_DWORD(x0) + BUFFER_SPI_DWORD(y0) + BUFFER_SPI_DWORD(ang) + BUFFER_SPI_DWORD(scale) + + EVE_inc_cmdoffset(16); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} +#endif + + +/* other commands: */ +void EVE_cmd_calibrate(void) +{ + EVE_start_cmd(CMD_CALIBRATE); + BUFFER_SPI_DWORD(0) + + EVE_inc_cmdoffset(4); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +#if defined (FT81X_ENABLE) +void EVE_cmd_romfont(uint32_t font, uint32_t romslot) +{ + EVE_start_cmd(CMD_ROMFONT); + BUFFER_SPI_DWORD(font & 0x0000ffff) + BUFFER_SPI_DWORD(romslot & 0x0000ffff) + + EVE_inc_cmdoffset(8); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} +#endif + + +#if defined (FT81X_ENABLE) +void EVE_cmd_setscratch(uint32_t handle) +{ + EVE_start_cmd(CMD_SETSCRATCH); + BUFFER_SPI_DWORD(handle) + + EVE_inc_cmdoffset(4); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} +#endif + + +void EVE_cmd_sketch(int16_t x0, int16_t y0, uint16_t w0, uint16_t h0, uint32_t ptr, uint16_t format) +{ + EVE_start_cmd(CMD_SKETCH); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(w0) + BUFFER_SPI_WORD(h0) + BUFFER_SPI_DWORD(ptr) + BUFFER_SPI_WORD(format) + BUFFER_SPI_WORD(0) /* dummy word for 4-byte alignment */ + + EVE_inc_cmdoffset(16); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_spinner(int16_t x0, int16_t y0, uint16_t style, uint16_t scale) +{ + EVE_start_cmd(CMD_SPINNER); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_WORD(style) + BUFFER_SPI_WORD(scale) + + EVE_inc_cmdoffset(8); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +/* various commands new for EVE3 */ +#if defined (BT81X_ENABLE) + +void EVE_cmd_animstart(int32_t ch, uint32_t aoptr, uint32_t loop) +{ + EVE_start_cmd(CMD_ANIMSTART); + BUFFER_SPI_DWORD(ch) + BUFFER_SPI_DWORD(aoptr) + BUFFER_SPI_DWORD(loop) + + EVE_inc_cmdoffset(12); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_animstop(int32_t ch) +{ + EVE_start_cmd(CMD_ANIMSTOP); + BUFFER_SPI_DWORD(ch) + + EVE_inc_cmdoffset(4); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_animxy(int32_t ch, int16_t x0, int16_t y0) +{ + EVE_start_cmd(CMD_ANIMXY); + BUFFER_SPI_DWORD(ch) + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + + EVE_inc_cmdoffset(8); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_animdraw(int32_t ch) +{ + EVE_start_cmd(CMD_ANIMDRAW); + BUFFER_SPI_DWORD(ch) + + EVE_inc_cmdoffset(4); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_animframe(int16_t x0, int16_t y0, uint32_t aoptr, uint32_t frame) +{ + EVE_start_cmd(CMD_ANIMFRAME); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_DWORD(aoptr) + BUFFER_SPI_DWORD(frame) + + EVE_inc_cmdoffset(12); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_gradienta(int16_t x0, int16_t y0, uint32_t argb0, int16_t x1, int16_t y1, uint32_t argb1) +{ + EVE_start_cmd(CMD_GRADIENTA); + BUFFER_SPI_WORD(x0) + BUFFER_SPI_WORD(y0) + BUFFER_SPI_DWORD(argb0) + BUFFER_SPI_WORD(x1) + BUFFER_SPI_WORD(y1) + BUFFER_SPI_DWORD(argb1) + + EVE_inc_cmdoffset(16); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_fillwidth(uint32_t s) +{ + EVE_start_cmd(CMD_FILLWIDTH); + BUFFER_SPI_DWORD(s) + + EVE_inc_cmdoffset(4); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +void EVE_cmd_appendf(uint32_t ptr, uint32_t num) +{ + EVE_start_cmd(CMD_APPENDF); + BUFFER_SPI_DWORD(ptr) + BUFFER_SPI_DWORD(num) + + EVE_inc_cmdoffset(8); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +#endif + + +/* warning! meta-command! this is a sequence of display-list commands to simplify use at the price of some overhead */ +void EVE_cmd_point(int16_t x0, int16_t y0, uint16_t size) +{ + EVE_start_cmd((DL_BEGIN | EVE_POINTS)); + + uint32_t calc = POINT_SIZE(size*16); + BUFFER_SPI_DWORD(calc) + + calc = VERTEX2F(x0 * 16, y0 * 16); + BUFFER_SPI_DWORD(calc) + + BUFFER_SPI_DWORD(DL_END) + + EVE_inc_cmdoffset(12); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +/* warning! meta-command! this is a sequence of display-list commands to simplify use at the price of some overhead */ +void EVE_cmd_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t width) +{ + EVE_start_cmd((DL_BEGIN | EVE_LINES)); + + uint32_t calc = LINE_WIDTH(width * 16); + BUFFER_SPI_DWORD(calc) + + calc = VERTEX2F(x0 * 16, y0 * 16); + BUFFER_SPI_DWORD(calc) + + calc = VERTEX2F(x1 * 16, y1 * 16); + BUFFER_SPI_DWORD(calc) + + BUFFER_SPI_DWORD(DL_END) + + EVE_inc_cmdoffset(16); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +/* warning! meta-command! this is a sequence of display-list commands to simplify use at the price of some overhead */ +void EVE_cmd_rect(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t corner) +{ + EVE_start_cmd((DL_BEGIN | EVE_RECTS)); + + uint32_t calc = LINE_WIDTH(corner * 16); + BUFFER_SPI_DWORD(calc) + + calc = VERTEX2F(x0 * 16, y0 * 16); + BUFFER_SPI_DWORD(calc) + + calc = VERTEX2F(x1 * 16, y1 * 16); + BUFFER_SPI_DWORD(calc) + + BUFFER_SPI_DWORD(DL_END) + + EVE_inc_cmdoffset(16); + + if(!cmd_burst) + { + SEND_SPI_BUFFER() + } +} + + +/* this is meant to be called outside display-list building */ +/* this function displays an interactive calibration screen, calculates the calibration values and */ +/* writes the new values to the touch matrix registers of EVE */ +/* unlike the built-in cmd_calibrate() of EVE this also works with displays that are cut down from larger ones like EVE2-38A / EVE2-38G */ +/* the height is needed as parameter as EVE_VSIZE for the EVE2-38 is 272 but the visible size is only 116 */ +/* so the call would be EVE_calibrate_manual(116); for the EVE2-38A and EVE2-38G while for most other displays */ +/* using EVE_calibrate_manual(EVE_VSIZE) would work - but for normal displays the built-in cmd_calibrate would work as expected anyways */ +/* this code was taken from the MatrixOrbital EVE2-Library on Github, adapted and modified */ +void EVE_calibrate_manual(uint16_t height) +{ + uint32_t displayX[3], displayY[3]; + uint32_t touchX[3], touchY[3]; + uint32_t touchValue; + int32_t tmp, k; + int32_t TransMatrix[6]; + uint8_t count = 0; + char num[2]; + uint8_t touch_lock = 1; + + /* these values determine where your calibration points will be drawn on your display */ + displayX[0] = (EVE_HSIZE * 0.15); + displayY[0] = (height * 0.15); + + displayX[1] = (EVE_HSIZE * 0.85); + displayY[1] = (height / 2); + + displayX[2] = (EVE_HSIZE / 2); + displayY[2] = (height * 0.85); + + while (count < 3) + { + // immediate transfer mode + EVE_cmd_dl(CMD_DLSTART); + EVE_cmd_dl(DL_CLEAR_RGB | 0x000000); + EVE_cmd_dl(DL_CLEAR | CLR_COL | CLR_STN | CLR_TAG); + + /* draw Calibration Point on screen */ + EVE_cmd_dl(DL_COLOR_RGB | 0x0000ff); + EVE_cmd_dl(POINT_SIZE(20*16)); + EVE_cmd_dl((DL_BEGIN | EVE_POINTS)); + EVE_cmd_dl(VERTEX2F((uint32_t)(displayX[count]) * 16, (uint32_t)((displayY[count])) * 16)); + EVE_cmd_dl(DL_END); + EVE_cmd_dl(DL_COLOR_RGB | 0xffffff); + EVE_cmd_text((EVE_HSIZE/2), 50, 27, EVE_OPT_CENTER, "Please tap on the dot."); + num[0] = count + 0x31; num[1] = 0; /* null terminated string of one character */ + EVE_cmd_text(displayX[count], displayY[count], 27, EVE_OPT_CENTER, num); + + EVE_cmd_dl(DL_DISPLAY); + EVE_cmd_dl(CMD_SWAP); + EVE_cmd_execute(); + + while(1) + { + touchValue = EVE_memRead32(REG_TOUCH_DIRECT_XY); /* read for any new touch tag inputs */ + + if(touch_lock) + { + if(touchValue & 0x80000000) /* check if we have no touch */ + { + touch_lock = 0; + } + } + else + { + if (!(touchValue & 0x80000000)) /* check if a touch is detected */ + { + touchX[count] = (touchValue>>16) & 0x03FF; /* raw Touchscreen Y coordinate */ + touchY[count] = touchValue & 0x03FF; /* raw Touchscreen Y coordinate */ + touch_lock = 1; + count++; + break; /* leave while(1) */ + } + } + } + } + + k = ((touchX[0] - touchX[2])*(touchY[1] - touchY[2])) - ((touchX[1] - touchX[2])*(touchY[0] - touchY[2])); + + tmp = (((displayX[0] - displayX[2]) * (touchY[1] - touchY[2])) - ((displayX[1] - displayX[2])*(touchY[0] - touchY[2]))); + TransMatrix[0] = ((int64_t)tmp << 16) / k; + + tmp = (((touchX[0] - touchX[2]) * (displayX[1] - displayX[2])) - ((displayX[0] - displayX[2])*(touchX[1] - touchX[2]))); + TransMatrix[1] = ((int64_t)tmp << 16) / k; + + tmp = ((touchY[0] * (((touchX[2] * displayX[1]) - (touchX[1] * displayX[2])))) + (touchY[1] * (((touchX[0] * displayX[2]) - (touchX[2] * displayX[0])))) + (touchY[2] * (((touchX[1] * displayX[0]) - (touchX[0] * displayX[1]))))); + TransMatrix[2] = ((int64_t)tmp << 16) / k; + + tmp = (((displayY[0] - displayY[2]) * (touchY[1] - touchY[2])) - ((displayY[1] - displayY[2])*(touchY[0] - touchY[2]))); + TransMatrix[3] = ((int64_t)tmp << 16) / k; + + tmp = (((touchX[0] - touchX[2]) * (displayY[1] - displayY[2])) - ((displayY[0] - displayY[2])*(touchX[1] - touchX[2]))); + TransMatrix[4] = ((int64_t)tmp << 16) / k; + + tmp = ((touchY[0] * (((touchX[2] * displayY[1]) - (touchX[1] * displayY[2])))) + (touchY[1] * (((touchX[0] * displayY[2]) - (touchX[2] * displayY[0])))) + (touchY[2] * (((touchX[1] * displayY[0]) - (touchX[0] * displayY[1]))))); + TransMatrix[5] = ((int64_t)tmp << 16) / k; + + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, TransMatrix[0]); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, TransMatrix[1]); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, TransMatrix[2]); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, TransMatrix[3]); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, TransMatrix[4]); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, TransMatrix[5]); +} +#endif // FT81X_FULL diff --git a/lvgl_tft/EVE_commands.h b/lvgl_tft/EVE_commands.h new file mode 100644 index 0000000..3c5c104 --- /dev/null +++ b/lvgl_tft/EVE_commands.h @@ -0,0 +1,203 @@ +/* +@file EVE_commands.h +@brief contains FT8xx / BT8xx function prototypes +@version 4.1 LvGL edition +@date 2020-04-13 +@author Rudolph Riedel, David Jade + +@section LICENSE + +MIT License + +Copyright (c) 2016-2020 Rudolph Riedel and David Jade + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +@section History + +4.1 LvGL edition + + - This version is a heavily modified version of the MIT licensed FT81x code from https://github.com/RudolphRiedel/FT800-FT813 + This version is based on a fork by David Jade that added native SPI DMA support and stripped out non-ESP32 code. + It has also been trimmed down to suit LvGL's needs. Extra features can be enabled by defining FT81X_FULL +*/ + +#include "EVE.h" + +#ifndef EVE_COMMANDS_H_ +#define EVE_COMMANDS_H_ + +#define BLOCK_TRANSFER_SIZE 3840 // block transfer size when write data to CMD buffer + +void DELAY_MS(uint16_t ms); +void EVE_pdn_set(void); +void EVE_pdn_clear(void); +void spi_acquire(); +void spi_release(); + +void EVE_cmdWrite(uint8_t command, uint8_t parameter); + +uint8_t EVE_memRead8(uint32_t ftAddress); +uint16_t EVE_memRead16(uint32_t ftAddress); +uint32_t EVE_memRead32(uint32_t ftAddress); + +void EVE_memWrite8(uint32_t ftAddress, uint8_t ftData8); +void EVE_memWrite16(uint32_t ftAddress, uint16_t ftData16); +void EVE_memWrite32(uint32_t ftAddress, uint32_t ftData32); + +void EVE_memWrite_buffer(uint32_t ftAddress, const uint8_t *data, uint32_t len, bool LvGL_Flush); + +uint8_t EVE_busy(void); + +void EVE_get_cmdoffset(void); + + +/* commands to operate on memory: */ +void EVE_cmd_memzero(uint32_t ptr, uint32_t num); +void EVE_cmd_memset(uint32_t ptr, uint8_t value, uint32_t num); +void EVE_cmd_memwrite(uint32_t dest, uint32_t num, const uint8_t *data); +void EVE_cmd_memcpy(uint32_t dest, uint32_t src, uint32_t num); + +#if FT81X_FULL +/* commands for loading image data into FT8xx memory: */ +void EVE_cmd_inflate(uint32_t ptr, const uint8_t *data, uint16_t len); +void EVE_cmd_loadimage(uint32_t ptr, uint32_t options, const uint8_t *data, uint16_t len); + +#if defined (FT81X_ENABLE) +void EVE_cmd_mediafifo(uint32_t ptr, uint32_t size); +#endif +#endif // FT81X_FULL + +void EVE_cmd_start(void); +void EVE_cmd_execute(void); + +void EVE_start_cmd_burst(void); +void EVE_end_cmd_burst(void); + +void EVE_cmd_dl(uint32_t command); + + +#if FT81X_FULL +/* EVE3 commands */ +#if defined (BT81X_ENABLE) + +void EVE_cmd_flashwrite(uint32_t ptr, uint32_t num, const uint8_t *data); +void EVE_cmd_flashread(uint32_t dest, uint32_t src, uint32_t num); +void EVE_cmd_flashupdate(uint32_t dest, uint32_t src, uint32_t num); +void EVE_cmd_flasherase(void); +void EVE_cmd_flashattach(void); +void EVE_cmd_flashdetach(void); +void EVE_cmd_flashspidesel(void); +uint32_t EVE_cmd_flashfast(void); +void EVE_cmd_flashspitx(uint32_t num, const uint8_t *data); +void EVE_cmd_flashspirx(uint32_t dest, uint32_t num); +void EVE_cmd_flashsource(uint32_t ptr); + +void EVE_cmd_inflate2(uint32_t ptr, uint32_t options, const uint8_t *data, uint16_t len); +void EVE_cmd_rotatearound(int32_t x0, int32_t y0, int32_t angle, int32_t scale); +void EVE_cmd_animstart(int32_t ch, uint32_t aoptr, uint32_t loop); +void EVE_cmd_animstop(int32_t ch); +void EVE_cmd_animxy(int32_t ch, int16_t x0, int16_t y0); +void EVE_cmd_animdraw(int32_t ch); +void EVE_cmd_animframe(int16_t x0, int16_t y0, uint32_t aoptr, uint32_t frame); +void EVE_cmd_gradienta(int16_t x0, int16_t y0, uint32_t argb0, int16_t x1, int16_t y1, uint32_t argb1); +void EVE_cmd_fillwidth(uint32_t s); +void EVE_cmd_appendf(uint32_t ptr, uint32_t num); + +uint8_t EVE_init_flash(void); +#endif + + +/* commands to draw graphics objects: */ + +#if defined (BT81X_ENABLE) +void EVE_cmd_text_var(int16_t x0, int16_t y0, int16_t font, uint16_t options, const char* text, uint8_t numargs, ...); +void EVE_cmd_button_var(int16_t x0, int16_t y0, int16_t w0, int16_t h0, int16_t font, uint16_t options, const char* text, uint8_t num_args, ...); +void EVE_cmd_toggle_var(int16_t x0, int16_t y0, int16_t w0, int16_t font, uint16_t options, uint16_t state, const char* text, uint8_t num_args, ...); +#endif + +void EVE_cmd_text(int16_t x0, int16_t y0, int16_t font, uint16_t options, const char* text); +void EVE_cmd_button(int16_t x0, int16_t y0, int16_t w0, int16_t h0, int16_t font, uint16_t options, const char* text); +void EVE_cmd_clock(int16_t x0, int16_t y0, int16_t r0, uint16_t options, uint16_t hours, uint16_t minutes, uint16_t seconds, uint16_t millisecs); +void EVE_color_rgb(uint8_t red, uint8_t green, uint8_t blue); +void EVE_cmd_bgcolor(uint32_t color); +void EVE_cmd_fgcolor(uint32_t color); +void EVE_cmd_gradcolor(uint32_t color); +void EVE_cmd_gauge(int16_t x0, int16_t y0, int16_t r0, uint16_t options, uint16_t major, uint16_t minor, uint16_t val, uint16_t range); +void EVE_cmd_gradient(int16_t x0, int16_t y0, uint32_t rgb0, int16_t x1, int16_t y1, uint32_t rgb1); +void EVE_cmd_keys(int16_t x0, int16_t y0, int16_t w0, int16_t h0, int16_t font, uint16_t options, const char* text); +void EVE_cmd_progress(int16_t x0, int16_t y0, int16_t w0, int16_t h0, uint16_t options, uint16_t val, uint16_t range); +void EVE_cmd_scrollbar(int16_t x0, int16_t y0, int16_t w0, int16_t h0, uint16_t options, uint16_t val, uint16_t size, uint16_t range); +void EVE_cmd_slider(int16_t x1, int16_t y1, int16_t w1, int16_t h1, uint16_t options, uint16_t val, uint16_t range); +void EVE_cmd_dial(int16_t x0, int16_t y0, int16_t r0, uint16_t options, uint16_t val); +void EVE_cmd_toggle(int16_t x0, int16_t y0, int16_t w0, int16_t font, uint16_t options, uint16_t state, const char* text); +void EVE_cmd_number(int16_t x0, int16_t y0, int16_t font, uint16_t options, int32_t number); +#endif // FT81X_FULL + +#if defined (FT81X_ENABLE) +#if FT81X_FULL +void EVE_cmd_setbase(uint32_t base); +#endif +void EVE_cmd_setbitmap(uint32_t addr, uint16_t fmt, uint16_t width, uint16_t height); +#endif + + +#if FT81X_FULL +void EVE_cmd_append(uint32_t ptr, uint32_t num); + + +/* commands for setting the bitmap transform matrix: */ +void EVE_cmd_getmatrix(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e, int32_t f); +void EVE_cmd_translate(int32_t tx, int32_t ty); +void EVE_cmd_scale(int32_t sx, int32_t sy); +void EVE_cmd_rotate(int32_t ang); + + +/* other commands: */ +void EVE_cmd_calibrate(void); +void EVE_cmd_interrupt(uint32_t ms); +void EVE_cmd_setfont(uint32_t font, uint32_t ptr); +#if defined (FT81X_ENABLE) +void EVE_cmd_romfont(uint32_t font, uint32_t romslot); +void EVE_cmd_setfont2(uint32_t font, uint32_t ptr, uint32_t firstchar); +void EVE_cmd_setrotate(uint32_t r); +void EVE_cmd_setscratch(uint32_t handle); +#endif +void EVE_cmd_sketch(int16_t x0, int16_t y0, uint16_t w0, uint16_t h0, uint32_t ptr, uint16_t format); +void EVE_cmd_snapshot(uint32_t ptr); +#if defined (FT81X_ENABLE) +void EVE_cmd_snapshot2(uint32_t fmt, uint32_t ptr, int16_t x0, int16_t y0, int16_t w0, int16_t h0); +#endif +void EVE_cmd_spinner(int16_t x0, int16_t y0, uint16_t style, uint16_t scale); +void EVE_cmd_track(int16_t x0, int16_t y0, int16_t w0, int16_t h0, int16_t tag); + + +/* commands that return values by writing to the command-fifo */ +uint32_t EVE_cmd_memcrc(uint32_t ptr, uint32_t num); +uint32_t EVE_cmd_getptr(void); +uint32_t EVE_cmd_regread(uint32_t ptr); +void EVE_LIB_GetProps(uint32_t *pointer, uint32_t *width, uint32_t *height); + + +/* meta-commands, sequences of several display-list entries condensed into simpler to use functions at the price of some overhead */ +void EVE_cmd_point(int16_t x0, int16_t y0, uint16_t size); +void EVE_cmd_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t w0); +void EVE_cmd_rect(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t corner); + +void EVE_calibrate_manual(uint16_t height); +#endif // FT81X_FULL + + +/* startup FT8xx: */ +uint8_t EVE_init(void); + +#endif /* EVE_COMMANDS_H_ */ diff --git a/lvgl_tft/EVE_config.h b/lvgl_tft/EVE_config.h new file mode 100644 index 0000000..53225a2 --- /dev/null +++ b/lvgl_tft/EVE_config.h @@ -0,0 +1,1043 @@ +/* +@file EVE_config.h +@brief configuration information for some TFTs +@version 4.0 +@date 2020-02-16 +@author Rudolph Riedel, David Jade + +@section LICENSE + +MIT License + +Copyright (c) 2016-2020 Rudolph Riedel amd David Jade + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +@section History + +4.1 LvGL edition + + - This version is a heavily modified version of the MIT licensed FT81x code from https://github.com/RudolphRiedel/FT800-FT813 + This version is based on a fork by David Jade that added native SPI DMA support and stripped out non-ESP32 code. + It has also been trimmed down to suit LvGL's needs. Extra features can be enabled by defining FT81X_FULL +*/ + + +#ifndef EVE_CONFIG_H_ +#define EVE_CONFIG_H_ + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "../lvgl_spi_conf.h" + +#include "FT81x.h" + +#define EVE_CLK DISP_SPI_CLK // orange +#define EVE_MISO DISP_SPI_MISO // yellow +#define EVE_MOSI DISP_SPI_MOSI // green +#define EVE_CS DISP_SPI_CS // blue +#define EVE_PDN CONFIG_LV_DISP_PIN_RST // grey + +#define SPI_TRANSER_SIZE (DISP_BUF_SIZE * (LV_COLOR_DEPTH / 8)) + +#define BYTES_PER_PIXEL (LV_COLOR_DEPTH / 8) // bytes per pixel for (16 for RGB565) +#define BYTES_PER_LINE (EVE_HSIZE * BYTES_PER_PIXEL) +#define SCREEN_BUFFER_SIZE (EVE_HSIZE * EVE_VSIZE * BYTES_PER_PIXEL) + +#define SPI_BUFFER_SIZE 256 // size in bytes (multiples of 4) of SPI transaction buffer for streaming commands + +/* select the settings for the TFT attached */ +#if 0 + #define EVE_VM800B35A + #define EVE_VM800B43A + #define EVE_VM800B50A + #define EVE_VM810C + #define EVE_ME812A + #define EVE_ME813A + #define EVE_FT810CB_HY50HD + #define EVE_FT811CB_HY50HD + #define EVE_ET07 + #define EVE_RVT28 + #define EVE_RVT35 + #define EVE_RVT43 + #define EVE_RVT50 + #define EVE_RVT70 + #define EVE_RiTFT43 + #define EVE_RiTFT50 + #define EVE_RiTFT70 + #define EVE_EVE2_29 + #define EVE_EVE2_35 + #define EVE_EVE2_35G + #define EVE_EVE2_38 + #define EVE_EVE2_38G + #define EVE_EVE2_43 + #define EVE_EVE2_43G + #define EVE_EVE2_50 + #define EVE_EVE2_50G + #define EVE_EVE2_70 + #define EVE_EVE2_70G + #define EVE_EVE3_35 + #define EVE_EVE3_35G + #define EVE_EVE3_43 + #define EVE_EVE3_43G + #define EVE_EVE3_50 + #define EVE_EVE3_50G + #define EVE_EVE3_70 + #define EVE_EVE3_70G + #define EVE_NHD_35 + #define EVE_NHD_43 + #define EVE_NHD_50 + #define EVE_NHD_70 + #define EVE_ADAM101 + #define EVE_CFAF240400C1_030SC + #define EVE_CFAF320240F_035T + #define EVE_CFAF480128A0_039TC + #define EVE_CFAF800480E0_050SC + #define EVE_PAF90 + #define EVE_SUNFLOWER + #define EVE_CONNECTEVE +#endif + + +#if defined(CONFIG_LV_FT81X_CONFIG_EVE_VM800B35A) +#define EVE_VM800B35A +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_VM800B43A) +#define EVE_VM800B43A +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_VM800B50A) +#define EVE_VM800B50A +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_VM810C) +#define EVE_VM810C +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_ME812A) +#define EVE_ME812A +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_ME813A) +#define EVE_ME813A +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_FT810CB_HY50HD) +#define EVE_FT810CB_HY50HD +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_FT811CB_HY50HD) +#define EVE_FT811CB_HY50HD +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_ET07) +#define EVE_ET07 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_RVT28) +#define EVE_RVT28 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_RVT35) +#define EVE_RVT35 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_RVT43) +#define EVE_RVT43 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_RVT50) +#define EVE_RVT50 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_RVT70) +#define EVE_RVT70 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_RiTFT43) +#define EVE_RiTFT43 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_RiTFT50) +#define EVE_RiTFT50 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_RiTFT70) +#define EVE_RiTFT70 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE2_29) +#define EVE_EVE2_29 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE2_35) +#define EVE_EVE2_35 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE2_35G) +#define EVE_EVE2_35G +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE2_38) +#define EVE_EVE2_38 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE2_38G) +#define EVE_EVE2_38G +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE2_43) +#define EVE_EVE2_43 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE2_43G) +#define EVE_EVE2_43G +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE2_50) +#define EVE_EVE2_50 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE2_50G) +#define EVE_EVE2_50G +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE2_70) +#define EVE_EVE2_70 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE2_70G) +#define EVE_EVE2_70G +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE3_35) +#define EVE_EVE3_35 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE3_35G) +#define EVE_EVE3_35G +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE3_43) +#define EVE_EVE3_43 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE3_43G) +#define EVE_EVE3_43G +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE3_50) +#define EVE_EVE3_50 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE3_50G) +#define EVE_EVE3_50G +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE3_70) +#define EVE_EVE3_70 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_EVE3_70G) +#define EVE_EVE3_70G +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_NHD_35) +#define EVE_NHD_35 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_NHD_43) +#define EVE_NHD_43 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_NHD_50) +#define EVE_NHD_50 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_NHD_70) +#define EVE_NHD_70 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_ADAM101) +#define EVE_ADAM101 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_CFAF240400C1_030SC) +#define EVE_CFAF240400C1_030SC +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_CFAF320240F_035T) +#define EVE_CFAF320240F_035T +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_CFAF480128A0_039TC) +#define EVE_CFAF480128A0_039TC +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_CFAF800480E0_050SC) +#define EVE_CFAF800480E0_050SC +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_PAF90) +#define EVE_PAF90 +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_SUNFLOWER) +#define EVE_SUNFLOWER +#elif defined(CONFIG_LV_FT81X_CONFIG_EVE_CONNECTEVE) +#define EVE_CONNECTEVE +#endif + +/* display timing parameters below */ + +/* untested */ +#if defined (EVE_EVE3_35) +#define EVE_EVE2_35 +#define EVE_HAS_CRYSTAL +#define BT81X_ENABLE +#endif + +/* untested */ +#if defined (EVE_EVE3_35G) +#define EVE_EVE2_35G +#define EVE_HAS_CRYSTAL +#define BT81X_ENABLE +#endif + +/* untested */ +#if defined (EVE_EVE3_43) +#define EVE_EVE2_43 +#define EVE_HAS_CRYSTAL +#define BT81X_ENABLE +#endif + +#if defined (EVE_EVE3_43G) +#define EVE_EVE2_43G +#define EVE_HAS_CRYSTAL +#define BT81X_ENABLE +#endif + +/* untested */ +#if defined (EVE_EVE3_50) +#define EVE_EVE2_50 +#define EVE_HAS_CRYSTAL +#define BT81X_ENABLE +#endif + +#if defined (EVE_EVE3_50G) +#define EVE_EVE2_50G +#define EVE_HAS_CRYSTAL +#define BT81X_ENABLE +#endif + +/* untested */ +#if defined (EVE_EVE3_70) +#define EVE_EVE2_70 +#define EVE_HAS_CRYSTAL +#define BT81X_ENABLE +#endif + +/* untested */ +#if defined (EVE_EVE3_70G) +#define EVE_EVE2_70G +#define EVE_HAS_CRYSTAL +#define BT81X_ENABLE +#endif + +#if defined (EVE_RiTFT43) +#define EVE_RVT43 +#define EVE_HAS_CRYSTAL +#define FT81X_ENABLE +#define BT81X_ENABLE +#endif + +/* untested */ +#if defined (EVE_RiTFT50) +#define EVE_RVT70 +#define EVE_HAS_CRYSTAL +#define BT81X_ENABLE +#endif + +/* untested */ +#if defined (EVE_RiTFT70) +#define EVE_RVT70 +#define EVE_HAS_CRYSTAL +#define BT81X_ENABLE +#endif + + +/* some test setup */ +#if defined (EVE_800x480x) +#define EVE_HSIZE (800L) /* Thd Length of visible part of line (in PCLKs) - display width */ +#define EVE_VSIZE (480L) /* Tvd Number of visible lines (in lines) - display height */ + +#define EVE_VSYNC0 (0L) /* Tvf Vertical Front Porch */ +#define EVE_VSYNC1 (10L) /* Tvf + Tvp Vertical Front Porch plus Vsync Pulse width */ +#define EVE_VOFFSET (35L) /* Tvf + Tvp + Tvb Number of non-visible lines (in lines) */ +#define EVE_VCYCLE (516L) /* Tv Total number of lines (visible and non-visible) (in lines) */ +#define EVE_HSYNC0 (0L) /* (40L) // Thf Horizontal Front Porch */ +#define EVE_HSYNC1 (88L) /* Thf + Thp Horizontal Front Porch plus Hsync Pulse width */ +#define EVE_HOFFSET (169L) /* Thf + Thp + Thb Length of non-visible part of line (in PCLK cycles) */ +#define EVE_HCYCLE (969L) /* Th Total length of line (visible and non-visible) (in PCLKs) */ +#define EVE_PCLKPOL (1L) /* PCLK polarity (0 = rising edge, 1 = falling edge) */ +#define EVE_SWIZZLE (0L) /* Defines the arrangement of the RGB pins of the FT800 */ +#define EVE_PCLK (2L) /* 60MHz / REG_PCLK = PCLK frequency 30 MHz */ +#define EVE_CSPREAD (1L) /* helps with noise, when set to 1 fewer signals are changed simultaneously, reset-default: 1 */ +#define EVE_TOUCH_RZTHRESH (1200L) /* touch-sensitivity */ +#define EVE_HAS_CRYSTAL +#define FT81X_ENABLE +#endif + + +/* VM800B35A: FT800 320x240 3.5" FTDI FT800 */ +#if defined (EVE_VM800B35A) +#define EVE_HSIZE (320L) /* Thd Length of visible part of line (in PCLKs) - display width */ +#define EVE_VSIZE (240L) /* Tvd Number of visible lines (in lines) - display height */ + +#define EVE_VSYNC0 (0L) /* Tvf Vertical Front Porch */ +#define EVE_VSYNC1 (2L) /* Tvf + Tvp Vertical Front Porch plus Vsync Pulse width */ +#define EVE_VOFFSET (13L) /* Tvf + Tvp + Tvb Number of non-visible lines (in lines) */ +#define EVE_VCYCLE (263L) /* Tv Total number of lines (visible and non-visible) (in lines) */ +#define EVE_HSYNC0 (0L) /* Thf Horizontal Front Porch */ +#define EVE_HSYNC1 (10L) /* Thf + Thp Horizontal Front Porch plus Hsync Pulse width */ +#define EVE_HOFFSET (70L) /* Thf + Thp + Thb Length of non-visible part of line (in PCLK cycles) */ +#define EVE_HCYCLE (408L) /* Th Total length of line (visible and non-visible) (in PCLKs) */ +#define EVE_PCLKPOL (0L) /* PCLK polarity (0 = rising edge, 1 = falling edge) */ +#define EVE_SWIZZLE (2L) /* Defines the arrangement of the RGB pins of the FT800 */ +#define EVE_PCLK (8L) /* 48MHz / REG_PCLK = PCLK frequency */ +#define EVE_CSPREAD (1L) /* helps with noise, when set to 1 fewer signals are changed simultaneously, reset-default: 1 */ +#define EVE_TOUCH_RZTHRESH (1200L) /* touch-sensitivity */ +#define EVE_HAS_CRYSTAL /* use external crystal or internal oscillator? */ +#endif + + +/* FTDI/BRT EVE modules VM800B43A and VM800B50A FT800 480x272 4.3" and 5.0" */ +#if defined (EVE_VM800B43A) || defined (EVE_VM800B50A) +#define EVE_HSIZE (480L) +#define EVE_VSIZE (272L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (10L) +#define EVE_VOFFSET (12L) +#define EVE_VCYCLE (292L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (41L) +#define EVE_HOFFSET (43L) +#define EVE_HCYCLE (548L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (5L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_CRYSTAL +#endif + + +/* untested */ +/* FTDI/BRT EVE2 modules VM810C50A-D, ME812A-WH50R and ME813A-WH50C, 800x480 5.0" */ +#if defined (EVE_VM810C) || defined (EVE_ME812A) || defined (EVE_ME813A) +#define EVE_HSIZE (800L) +#define EVE_VSIZE (480L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (3L) +#define EVE_VOFFSET (32L) +#define EVE_VCYCLE (525L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (48L) +#define EVE_HOFFSET (88L) +#define EVE_HCYCLE (928L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (2L) +#define EVE_CSPREAD (0L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_CRYSTAL +#define FT81X_ENABLE +#endif + + +/* FT810CB-HY50HD: FT810 800x480 5.0" HAOYU */ +#if defined (EVE_FT810CB_HY50HD) +#define EVE_HSIZE (800L) +#define EVE_VSIZE (480L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (2L) +#define EVE_VOFFSET (13L) +#define EVE_VCYCLE (525L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (20L) +#define EVE_HOFFSET (64L) +#define EVE_HCYCLE (952L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (2L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (2000L) /* touch-sensitivity */ +#define EVE_HAS_CRYSTAL +#define FT81X_ENABLE +#endif + + +/* FT811CB-HY50HD: FT811 800x480 5.0" HAOYU */ +#if defined (EVE_FT811CB_HY50HD) +#define EVE_HSIZE (800L) +#define EVE_VSIZE (480L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (2L) +#define EVE_VOFFSET (13L) +#define EVE_VCYCLE (525L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (20L) +#define EVE_HOFFSET (64L) +#define EVE_HCYCLE (952L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (2L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) /* touch-sensitivity */ +#define EVE_HAS_CRYSTAL +#define FT81X_ENABLE +#endif + + +/* untested */ +/* G-ET0700G0DM6 800x480 7.0" Glyn */ +#if defined (EVE_ET07) +#define EVE_HSIZE (800L) +#define EVE_VSIZE (480L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (2L) +#define EVE_VOFFSET (35L) +#define EVE_VCYCLE (525L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (128L) +#define EVE_HOFFSET (203L) +#define EVE_HCYCLE (1056L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (2L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define FT81X_ENABLE +#endif + + +/* untested */ +/* RVT28 240x320 2.8" Riverdi, various options, FT800/FT801 */ +#if defined (EVE_RVT28) +#define EVE_HSIZE (320L) +#define EVE_VSIZE (240L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (2L) +#define EVE_VOFFSET (2L) +#define EVE_VCYCLE (326L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (10L) +#define EVE_HOFFSET (20L) +#define EVE_HCYCLE (270L) +#define EVE_PCLKPOL (0L) +#define EVE_SWIZZLE (4L) +#define EVE_PCLK (5L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#endif + + +/* untested */ +/* RVT3.5 320x240 3.5" Riverdi, various options, FT800/FT801 */ +#if defined (EVE_RVT35) +#define EVE_HSIZE (320L) +#define EVE_VSIZE (240L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (2L) +#define EVE_VOFFSET (13L) +#define EVE_VCYCLE (263L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (10L) +#define EVE_HOFFSET (70L) +#define EVE_HCYCLE (408L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (2L) +#define EVE_PCLK (6L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#endif + + +/* untested */ +/* RVT43 / RVT4.3 480x272 4.3" Riverdi, various options, FT800/FT801 */ +#if defined (EVE_RVT43) +#define EVE_HSIZE (480L) +#define EVE_VSIZE (272L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (10L) +#define EVE_VOFFSET (12L) +#define EVE_VCYCLE (292L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (41L) +#define EVE_HOFFSET (43L) +#define EVE_HCYCLE (548L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (5L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#endif + + +/* untested */ +/* RVT50xQFxxxxx 800x480 5.0" Riverdi, various options, FT812/FT813 */ +#if defined (EVE_RVT50) +#define EVE_HSIZE (800L) +#define EVE_VSIZE (480L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (3L) +#define EVE_VOFFSET (32L) +#define EVE_VCYCLE (525L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (48L) +#define EVE_HOFFSET (88L) +#define EVE_HCYCLE (928L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (2L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define FT81X_ENABLE +#endif + + +/* RVT70xQFxxxxx 800x480 7.0" Riverdi, various options, FT812/FT813, tested with RVT70UQFNWC0x */ +#if defined (EVE_RVT70) +#define EVE_HSIZE (800L) /* Thd Length of visible part of line (in PCLKs) - display width */ +#define EVE_VSIZE (480L) /* Tvd Number of visible lines (in lines) - display height */ + +#define EVE_VSYNC0 (0L) /* Tvf Vertical Front Porch */ +#define EVE_VSYNC1 (10L) /* Tvf + Tvp Vertical Front Porch plus Vsync Pulse width */ +#define EVE_VOFFSET (23L) /* Tvf + Tvp + Tvb Number of non-visible lines (in lines) */ +#define EVE_VCYCLE (525L) /* Tv Total number of lines (visible and non-visible) (in lines) */ +#define EVE_HSYNC0 (0L) /* Thf Horizontal Front Porch */ +#define EVE_HSYNC1 (10L) /* Thf + Thp Horizontal Front Porch plus Hsync Pulse width */ +#define EVE_HOFFSET (46L) /* Thf + Thp + Thb Length of non-visible part of line (in PCLK cycles) */ +#define EVE_HCYCLE (1056L) /* Th Total length of line (visible and non-visible) (in PCLKs) */ +#define EVE_PCLKPOL (1L) /* PCLK polarity (0 = rising edge, 1 = falling edge) */ +#define EVE_SWIZZLE (0L) /* Defines the arrangement of the RGB pins of the FT800 */ +#define EVE_PCLK (2L) /* 60MHz / REG_PCLK = PCLK frequency 30 MHz */ +#define EVE_CSPREAD (1L) /* helps with noise, when set to 1 fewer signals are changed simultaneously, reset-default: 1 */ +#define EVE_TOUCH_RZTHRESH (1800L) /* touch-sensitivity */ +#define FT81X_ENABLE +#endif + + +/* untested */ +/* EVE2-29A 320x102 2.9" 1U Matrix Orbital, non-touch, FT812 */ +#if defined (EVE_EVE2_29) +#define EVE_HSIZE (320L) +#define EVE_VSIZE (102L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (2L) +#define EVE_VOFFSET (156L) +#define EVE_VCYCLE (262L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (10L) +#define EVE_HOFFSET (70L) +#define EVE_HCYCLE (408L) +#define EVE_PCLKPOL (0L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (8L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define FT81X_ENABLE +#endif + + +/* EVE2-35A 320x240 3.5" Matrix Orbital, resistive, or non-touch, FT812 */ +#if defined (EVE_EVE2_35) +#define EVE_HSIZE (320L) +#define EVE_VSIZE (240L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (2L) +#define EVE_VOFFSET (18L) +#define EVE_VCYCLE (262L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (10L) +#define EVE_HOFFSET (70L) +#define EVE_HCYCLE (408L) +#define EVE_PCLKPOL (0L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (8L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define FT81X_ENABLE +#endif + + +/* EVE2-35G 320x240 3.5" Matrix Orbital, capacitive touch, FT813 */ +#if defined (EVE_EVE2_35G) +#define EVE_HSIZE (320L) +#define EVE_VSIZE (240L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (2L) +#define EVE_VOFFSET (18L) +#define EVE_VCYCLE (262L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (10L) +#define EVE_HOFFSET (70L) +#define EVE_HCYCLE (408L) +#define EVE_PCLKPOL (0L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (8L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_GT911 /* special treatment required for out-of-spec touch-controller */ +#define FT81X_ENABLE +#endif + + +/* EVE2-38A 480x116 3.8" 1U Matrix Orbital, resistive touch, FT812 */ +#if defined (EVE_EVE2_38) +#define EVE_HSIZE (480L) +#define EVE_VSIZE (272L) + +#define EVE_VSYNC0 (152L) +#define EVE_VSYNC1 (10L) +#define EVE_VOFFSET (12L) +#define EVE_VCYCLE (292L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (41L) +#define EVE_HOFFSET (43L) +#define EVE_HCYCLE (548L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (5L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define FT81X_ENABLE +#endif + + +/* EVE2-38G 480x116 3.8" 1U Matrix Orbital, capacitive touch, FT813 */ +#if defined (EVE_EVE2_38G) +#define EVE_HSIZE (480L) +#define EVE_VSIZE (272L) + +#define EVE_VSYNC0 (152L) +#define EVE_VSYNC1 (10L) +#define EVE_VOFFSET (12L) +#define EVE_VCYCLE (292L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (41L) +#define EVE_HOFFSET (43L) +#define EVE_HCYCLE (548L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (5L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_GT911 /* special treatment required for out-of-spec touch-controller */ +#define FT81X_ENABLE +#endif + + +/* untested */ +/* EVE2-43A 480x272 4.3" Matrix Orbital, resistive or no touch, FT812 */ +#if defined (EVE_EVE2_43) +#define EVE_HSIZE (480L) +#define EVE_VSIZE (272L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (10L) +#define EVE_VOFFSET (12L) +#define EVE_VCYCLE (292L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (41L) +#define EVE_HOFFSET (43L) +#define EVE_HCYCLE (548L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (5L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define FT81X_ENABLE +#endif + + +/* EVE2-43G 480x272 4.3" Matrix Orbital, capacitive touch, FT813 */ +#if defined (EVE_EVE2_43G) +#define EVE_HSIZE (480L) +#define EVE_VSIZE (272L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (10L) +#define EVE_VOFFSET (12L) +#define EVE_VCYCLE (292L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (41L) +#define EVE_HOFFSET (43L) +#define EVE_HCYCLE (548L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (5L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_GT911 /* special treatment required for out-of-spec touch-controller */ +#define FT81X_ENABLE +#endif + + +/* untested */ +/* Matrix Orbital EVE2 modules EVE2-50A, EVE2-70A : 800x480 5.0" and 7.0" resistive, or no touch, FT812 */ +#if defined (EVE_EVE2_50) || defined (EVE_EVE2_70) +#define EVE_HSIZE (800L) +#define EVE_VSIZE (480L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (3L) +#define EVE_VOFFSET (32L) +#define EVE_VCYCLE (525L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (48L) +#define EVE_HOFFSET (88L) +#define EVE_HCYCLE (928L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (2L) +#define EVE_CSPREAD (0L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define FT81X_ENABLE +#endif + + +/* Matrix Orbital EVE2 modules EVE2-50G, EVE2-70G : 800x480 5.0" and 7.0" capacitive touch, FT813 */ +#if defined (EVE_EVE2_50G) || defined (EVE_EVE2_70G) +#define EVE_HSIZE (800L) +#define EVE_VSIZE (480L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (3L) +#define EVE_VOFFSET (32L) +#define EVE_VCYCLE (525L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (48L) +#define EVE_HOFFSET (88L) +#define EVE_HCYCLE (928L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (2L) +#define EVE_CSPREAD (0L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_GT911 /* special treatment required for out-of-spec touch-controller */ +#define FT81X_ENABLE +#endif + + +/* NHD-3.5-320240FT-CxXx-xxx 320x240 3.5" Newhaven, resistive or capacitive, FT81x */ +#if defined (EVE_NHD_35) +#define EVE_HSIZE (320L) +#define EVE_VSIZE (240L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (2L) +#define EVE_VOFFSET (13L) +#define EVE_VCYCLE (263L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (10L) +#define EVE_HOFFSET (70L) +#define EVE_HCYCLE (408L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (2L) +#define EVE_PCLK (6L) +#define EVE_CSPREAD (0L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_CRYSTAL +#define FT81X_ENABLE +#endif + + +/* untested */ +/* NHD-4.3-480272FT-CxXx-xxx 480x272 4.3" Newhaven, resistive or capacitive, FT81x */ +#if defined (EVE_NHD_43) +#define EVE_HSIZE (480L) +#define EVE_VSIZE (272L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (10L) +#define EVE_VOFFSET (12L) +#define EVE_VCYCLE (292L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (41L) +#define EVE_HOFFSET (43L) +#define EVE_HCYCLE (548L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (5L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_CRYSTAL +#define FT81X_ENABLE +#endif + + +/* untested */ +/* NHD-5.0-800480FT-CxXx-xxx 800x480 5.0" Newhaven, resistive or capacitive, FT81x */ +#if defined (EVE_NHD_50) +#define EVE_HSIZE (800L) +#define EVE_VSIZE (480L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (3L) +#define EVE_VOFFSET (32L) +#define EVE_VCYCLE (525L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (48L) +#define EVE_HOFFSET (88L) +#define EVE_HCYCLE (928L) +#define EVE_PCLKPOL (0L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (2L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_CRYSTAL +#define FT81X_ENABLE +#endif + + +/* untested */ +/* NHD-7.0-800480FT-CxXx-xxx 800x480 7.0" Newhaven, resistive or capacitive, FT81x */ +#if defined (EVE_NHD_70) +#define EVE_HSIZE (800L) +#define EVE_VSIZE (480L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (3L) +#define EVE_VOFFSET (32L) +#define EVE_VCYCLE (525L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (48L) +#define EVE_HOFFSET (88L) +#define EVE_HCYCLE (928L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (2L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_CRYSTAL +#define FT81X_ENABLE +#endif + + +/* ADAM101-LCP-SWVGA-NEW 1024x600 10.1" Glyn, capacitive, FT813 */ +#if defined (EVE_ADAM101) +#define EVE_HSIZE (1024L) +#define EVE_VSIZE (600L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (1L) +#define EVE_VOFFSET (1L) +#define EVE_VCYCLE (720L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (1L) +#define EVE_HOFFSET (1L) +#define EVE_HCYCLE (1100L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (2L) +#define EVE_CSPREAD (1L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_CRYSTAL +#define FT81X_ENABLE +#endif + + +/* Crystalfonts CFAF240400C1-030SC 240x400 3.0" , FT811 capacitive touch */ +#if defined (EVE_CFAF240400C1_030SC) +#define EVE_HSIZE (240L) +#define EVE_VSIZE (400L) + +#define EVE_VSYNC0 (4L) +#define EVE_VSYNC1 (6L) +#define EVE_VOFFSET (8L) +#define EVE_VCYCLE (409L) +#define EVE_HSYNC0 (10L) +#define EVE_HSYNC1 (20L) +#define EVE_HOFFSET (40L) +#define EVE_HCYCLE (489L) +#define EVE_PCLKPOL (0L) +#define EVE_SWIZZLE (2L) +#define EVE_PCLK (5L) +#define EVE_CSPREAD (0L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define FT81X_ENABLE +#endif + + +/* Crystalfonts CFAF320240F-035T 320x240 3.5" , FT810 resistive touch */ +#if defined (EVE_CFAF320240F_035T) +#define EVE_HSIZE (320L) +#define EVE_VSIZE (240L) + +#define EVE_VSYNC0 (1L) +#define EVE_VSYNC1 (4L) +#define EVE_VOFFSET (4L) +#define EVE_VCYCLE (245L) +#define EVE_HSYNC0 (10L) +#define EVE_HSYNC1 (20L) +#define EVE_HOFFSET (40L) +#define EVE_HCYCLE (510L) +#define EVE_PCLKPOL (0L) +#define EVE_SWIZZLE (2L) +#define EVE_PCLK (8L) +#define EVE_CSPREAD (0L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define FT81X_ENABLE +#endif + + +/* Crystalfonts CFAF480128A0-039TC 480x128 3.9" , FT811 capacitive touch */ +#if defined (EVE_CFAF480128A0_039TC) +#define EVE_HSIZE (480L) +#define EVE_VSIZE (128L) + +#define EVE_VSYNC0 (4L) +#define EVE_VSYNC1 (5L) +#define EVE_VOFFSET (8L) +#define EVE_VCYCLE (137L) +#define EVE_HSYNC0 (24L) +#define EVE_HSYNC1 (35L) +#define EVE_HOFFSET (41L) +#define EVE_HCYCLE (1042L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (7L) +#define EVE_CSPREAD (0L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define FT81X_ENABLE +#endif + + +/* Crystalfonts CFAF800480E0-050SC 800x480 5.0" , FT813 capacitive touch */ +#if defined (EVE_CFAF800480E0_050SC) +#define EVE_HSIZE (800L) +#define EVE_VSIZE (480L) + +#define EVE_VSYNC0 (7L) +#define EVE_VSYNC1 (8L) +#define EVE_VOFFSET (30L) +#define EVE_VCYCLE (511L) +#define EVE_HSYNC0 (16L) +#define EVE_HSYNC1 (17L) +#define EVE_HOFFSET (62L) +#define EVE_HCYCLE (978L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (2L) +#define EVE_CSPREAD (0L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_GT911 +#define FT81X_ENABLE +#endif + + +/* PAF90B5WFNWC01 800x480 9.0" Panasys, BT815 */ +#if defined (EVE_PAF90) +#define EVE_HSIZE (800L) /* Thd Length of visible part of line (in PCLKs) - display width */ +#define EVE_VSIZE (480L) /* Tvd Number of visible lines (in lines) - display height */ + +#define EVE_VSYNC0 (0L) /* Tvf Vertical Front Porch */ +#define EVE_VSYNC1 (10L) /* Tvf + Tvp Vertical Front Porch plus Vsync Pulse width */ +#define EVE_VOFFSET (23L) /* Tvf + Tvp + Tvb Number of non-visible lines (in lines) */ +#define EVE_VCYCLE (525L) /* Tv Total number of lines (visible and non-visible) (in lines) */ +#define EVE_HSYNC0 (0L) /* Thf Horizontal Front Porch */ +#define EVE_HSYNC1 (10L) /* Thf + Thp Horizontal Front Porch plus Hsync Pulse width */ +#define EVE_HOFFSET (46L) /* Thf + Thp + Thb Length of non-visible part of line (in PCLK cycles) */ +#define EVE_HCYCLE (1056L) /* Th Total length of line (visible and non-visible) (in PCLKs) */ +#define EVE_PCLKPOL (1L) /* PCLK polarity (0 = rising edge, 1 = falling edge) */ +#define EVE_SWIZZLE (0L) /* Defines the arrangement of the RGB pins of the FT800 */ +#define EVE_PCLK (2L) /* 60MHz / REG_PCLK = PCLK frequency 30 MHz */ +#define EVE_CSPREAD (1L) /* helps with noise, when set to 1 fewer signals are changed simultaneously, reset-default: 1 */ +#define EVE_TOUCH_RZTHRESH (1200L) /* touch-sensitivity */ +#define EVE_HAS_CRYSTAL +#define FT81X_ENABLE +#define BT81X_ENABLE +#endif + + +/* untested */ +/* Sunflower Arduino Shield, 320x240 3.5" from Cowfish, FT813, https://github.com/Cowfish-Studios/Cowfish_Sunflower_Shield_PCB */ +/* set EVE_CS to 6 and EVE_PDN to 5 in the Arduino block in EVE_target.h */ +#if defined (EVE_SUNFLOWER) +#define EVE_HSIZE (320L) +#define EVE_VSIZE (240L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (2L) +#define EVE_VOFFSET (13L) +#define EVE_VCYCLE (263L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (10L) +#define EVE_HOFFSET (70L) +#define EVE_HCYCLE (408L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (2L) +#define EVE_PCLK (6L) +#define EVE_CSPREAD (0L) +#define EVE_TOUCH_RZTHRESH (1200L) +#define EVE_HAS_CRYSTAL +#define FT81X_ENABLE +#endif + +/* untested */ +/* MikroElektronika ConnectEVE, FT800 480x272 4.3" */ +#if defined (EVE_CONNECTEVE) +#define EVE_HSIZE (480L) +#define EVE_VSIZE (272L) + +#define EVE_VSYNC0 (0L) +#define EVE_VSYNC1 (10L) +#define EVE_VOFFSET (12L) +#define EVE_VCYCLE (286L) +#define EVE_HSYNC0 (0L) +#define EVE_HSYNC1 (41L) +#define EVE_HOFFSET (43L) +#define EVE_HCYCLE (525L) +#define EVE_PCLKPOL (1L) +#define EVE_SWIZZLE (0L) +#define EVE_PCLK (5L) +#define EVE_CSPREAD (0L) +#define EVE_TOUCH_RZTHRESH (2000L) +#define EVE_HAS_CRYSTAL +#endif + +#endif /* EVE_CONFIG_H */ diff --git a/lvgl_tft/FT81x.c b/lvgl_tft/FT81x.c new file mode 100644 index 0000000..b004eba --- /dev/null +++ b/lvgl_tft/FT81x.c @@ -0,0 +1,323 @@ + +#include + +#include "driver/gpio.h" + +#include "FT81x.h" + +#include "EVE.h" +#include "EVE_commands.h" + +/* some pre-definded colors */ +#define RED 0xff0000UL +#define ORANGE 0xffa500UL +#define GREEN 0x00ff00UL +#define BLUE 0x0000ffUL +#define BLUE_1 0x5dade2L +#define YELLOW 0xffff00UL +#define PINK 0xff00ffUL +#define PURPLE 0x800080UL +#define WHITE 0xffffffUL +#define BLACK 0x000000UL + +/* memory-map defines */ +#define SCREEN_BITMAP_ADDR 0x00000000 // full screen buffer (0x00000000 - 0x000‭‭BBE40‬) + +uint8_t tft_active = 0; + +void touch_calibrate(void) +{ + +/* send pre-recorded touch calibration values, depending on the display the code is compiled for */ + +#if defined (EVE_CFAF240400C1_030SC) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x0000ed11); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0x00001139); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xfff76809); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0x00000000); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0x00010690); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0xfffadf2e); +#endif + +#if defined (EVE_CFAF320240F_035T) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x00005614); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0x0000009e); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xfff43422); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0x0000001d); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0xffffbda4); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0x00f8f2ef); +#endif + +#if defined (EVE_CFAF480128A0_039TC) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x00010485); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0x0000017f); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xfffb0bd3); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0x00000073); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0x0000e293); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0x00069904); +#endif + +#if defined (EVE_CFAF800480E0_050SC) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x000107f9); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0xffffff8c); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xfff451ae); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0x000000d2); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0x0000feac); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0xfffcfaaf); +#endif + +#if defined (EVE_PAF90) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x00000159); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0x0001019c); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xfff93625); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0x00010157); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0x00000000); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0x0000c101); +#endif + +#if defined (EVE_RiTFT43) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x000062cd); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0xfffffe45); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xfff45e0a); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0x000001a3); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0x00005b33); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0xFFFbb870); +#endif + +#if defined (EVE_EVE2_38) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x00007bed); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0x000001b0); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xfff60aa5); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0x00000095); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0xffffdcda); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0x00829c08); +#endif + +#if defined (EVE_EVE2_35G) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x000109E4); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0x000007A6); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xFFEC1EBA); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0x0000072C); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0x0001096A); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0xFFF469CF); +#endif + +#if defined (EVE_EVE2_43G) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x0000a1ff); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0x00000680); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xffe54cc2); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0xffffff53); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0x0000912c); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0xfffe628d); +#endif + +#if defined (EVE_EVE2_50G) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x000109E4); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0x000007A6); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xFFEC1EBA); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0x0000072C); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0x0001096A); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0xFFF469CF); +#endif + +#if defined (EVE_EVE2_70G) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x000105BC); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0xFFFFFA8A); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0x00004670); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0xFFFFFF75); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0x00010074); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0xFFFF14C8); +#endif + +#if defined (EVE_NHD_35) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x0000f78b); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0x00000427); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xfffcedf8); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0xfffffba4); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0x0000f756); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0x0009279e); +#endif + +#if defined (EVE_RVT70) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x000074df); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0x000000e6); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xfffd5474); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0x000001af); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0x00007e79); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0xffe9a63c); +#endif + +#if defined (EVE_FT811CB_HY50HD) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 66353); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 712); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 4293876677); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 4294966157); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 67516); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 418276); +#endif + +#if defined (EVE_ADAM101) + EVE_memWrite32(REG_TOUCH_TRANSFORM_A, 0x000101E3); + EVE_memWrite32(REG_TOUCH_TRANSFORM_B, 0x00000114); + EVE_memWrite32(REG_TOUCH_TRANSFORM_C, 0xFFF5EEBA); + EVE_memWrite32(REG_TOUCH_TRANSFORM_D, 0xFFFFFF5E); + EVE_memWrite32(REG_TOUCH_TRANSFORM_E, 0x00010226); + EVE_memWrite32(REG_TOUCH_TRANSFORM_F, 0x0000C783); +#endif + +/* activate this if you are using a module for the first time or if you need to re-calibrate it */ +/* write down the numbers on the screen and either place them in one of the pre-defined blocks above or make a new block */ +// Note: requires FT81x_FULL to be defined +#if 0 + /* calibrate touch and displays values to screen */ + EVE_cmd_dl(CMD_DLSTART); + EVE_cmd_dl(DL_CLEAR_RGB | BLACK); + EVE_cmd_dl(DL_CLEAR | CLR_COL | CLR_STN | CLR_TAG); + EVE_cmd_text((EVE_HSIZE/2), 50, 26, EVE_OPT_CENTER, "Please tap on the dot."); + EVE_cmd_calibrate(); + EVE_cmd_dl(DL_DISPLAY); + EVE_cmd_dl(CMD_SWAP); + EVE_cmd_execute(); + + uint32_t touch_a, touch_b, touch_c, touch_d, touch_e, touch_f; + + touch_a = EVE_memRead32(REG_TOUCH_TRANSFORM_A); + touch_b = EVE_memRead32(REG_TOUCH_TRANSFORM_B); + touch_c = EVE_memRead32(REG_TOUCH_TRANSFORM_C); + touch_d = EVE_memRead32(REG_TOUCH_TRANSFORM_D); + touch_e = EVE_memRead32(REG_TOUCH_TRANSFORM_E); + touch_f = EVE_memRead32(REG_TOUCH_TRANSFORM_F); + + EVE_cmd_dl(CMD_DLSTART); + EVE_cmd_dl(DL_CLEAR_RGB | BLACK); + EVE_cmd_dl(DL_CLEAR | CLR_COL | CLR_STN | CLR_TAG); + EVE_cmd_dl(TAG(0)); + + EVE_cmd_text(5, 15, 26, 0, "TOUCH_TRANSFORM_A:"); + EVE_cmd_text(5, 30, 26, 0, "TOUCH_TRANSFORM_B:"); + EVE_cmd_text(5, 45, 26, 0, "TOUCH_TRANSFORM_C:"); + EVE_cmd_text(5, 60, 26, 0, "TOUCH_TRANSFORM_D:"); + EVE_cmd_text(5, 75, 26, 0, "TOUCH_TRANSFORM_E:"); + EVE_cmd_text(5, 90, 26, 0, "TOUCH_TRANSFORM_F:"); + +#if defined (FT81X_ENABLE) + EVE_cmd_setbase(16L); /* FT81x only */ + EVE_cmd_number(310, 15, 26, EVE_OPT_RIGHTX|8, touch_a); + EVE_cmd_number(310, 30, 26, EVE_OPT_RIGHTX|8, touch_b); + EVE_cmd_number(310, 45, 26, EVE_OPT_RIGHTX|8, touch_c); + EVE_cmd_number(310, 60, 26, EVE_OPT_RIGHTX|8, touch_d); + EVE_cmd_number(310, 75, 26, EVE_OPT_RIGHTX|8, touch_e); + EVE_cmd_number(310, 90, 26, EVE_OPT_RIGHTX|8, touch_f); +#else + EVE_cmd_number(310, 15, 26, EVE_OPT_RIGHTX, touch_a); + EVE_cmd_number(310, 30, 26, EVE_OPT_RIGHTX, touch_b); + EVE_cmd_number(310, 45, 26, EVE_OPT_RIGHTX, touch_c); + EVE_cmd_number(310, 60, 26, EVE_OPT_RIGHTX, touch_d); + EVE_cmd_number(310, 75, 26, EVE_OPT_RIGHTX, touch_e); + EVE_cmd_number(310, 90, 26, EVE_OPT_RIGHTX, touch_f); +#endif + + EVE_cmd_dl(DL_DISPLAY); /* instruct the graphics processor to show the list */ + EVE_cmd_dl(CMD_SWAP); /* make this list active */ + EVE_cmd_execute(); + + while(1); +#endif +} + + +// set up a display list for a fullscreen writable bitmap +void TFT_bitmap_display(void) +{ + if(tft_active != 0) + { + EVE_start_cmd_burst(); /* start writing to the cmd-fifo as one stream of bytes, only sending the address once */ + + EVE_cmd_dl(CMD_DLSTART); /* start the display list */ + + EVE_cmd_dl(DL_CLEAR_RGB | BLACK); /* set the default clear color to black */ + EVE_cmd_dl(DL_CLEAR | CLR_COL | CLR_STN | CLR_TAG); /* clear the screen - this and the previous prevent artifacts between lists, Attributes are the color, stencil and tag buffers */ + + EVE_cmd_dl(TAG(0)); + + // fullscreen bitmap for memory-mapped direct access + EVE_cmd_dl(TAG(20)); + EVE_cmd_setbitmap(SCREEN_BITMAP_ADDR, EVE_RGB565, EVE_HSIZE, EVE_VSIZE); + EVE_cmd_dl(DL_BEGIN | EVE_BITMAPS); + EVE_cmd_dl(VERTEX2F(0, 0)); + EVE_cmd_dl(DL_END); + + EVE_cmd_dl(TAG(0)); + + EVE_cmd_dl(DL_DISPLAY); /* instruct the graphics processor to show the list */ + + EVE_cmd_dl(CMD_SWAP); /* make this list active */ + + EVE_end_cmd_burst(); /* stop writing to the cmd-fifo */ + + EVE_cmd_start(); /* order the command co-processor to start processing its FIFO queue but do not wait for completion */ + } +} + + +void FT81x_init(void) +{ + gpio_pad_select_gpio(EVE_PDN); + gpio_set_level(EVE_CS, 1); + gpio_set_direction(EVE_PDN, GPIO_MODE_OUTPUT); + + spi_acquire(); + + if(EVE_init()) + { + tft_active = 1; + + EVE_memWrite8(REG_PWM_DUTY, 0x30); /* setup backlight, range is from 0 = off to 0x80 = max */ + + touch_calibrate(); + + EVE_cmd_memset(SCREEN_BITMAP_ADDR, BLACK, SCREEN_BUFFER_SIZE); // clear screen buffer + EVE_cmd_execute(); + + TFT_bitmap_display(); // set DL for fullscreen bitmap display + } + + spi_release(); +} + + +// write fullscreen bitmap directly +void TFT_WriteScreen(uint8_t* Bitmap) +{ + EVE_memWrite_buffer(SCREEN_BITMAP_ADDR, Bitmap, SCREEN_BUFFER_SIZE, false); +} + + +// write bitmap directly, line-by-line +void TFT_WriteBitmap(uint8_t* Bitmap, uint16_t X, uint16_t Y, uint16_t Width, uint16_t Height) +{ + // calc base address + uint32_t addr = SCREEN_BITMAP_ADDR + (Y * BYTES_PER_LINE) + (X * BYTES_PER_PIXEL); + + // can we do a fast full width block transfer? + if(X == 0 && Width == EVE_HSIZE) + { + EVE_memWrite_buffer(addr, Bitmap, (Height * BYTES_PER_LINE), true); + } + else + { + // line by line mode + uint32_t bpl = Width * BYTES_PER_PIXEL; + for (uint16_t i = 0; i < Height; i++) + { + EVE_memWrite_buffer(addr, Bitmap + (i * bpl), bpl, (i == Height - 1)); + addr += BYTES_PER_LINE; + } + } +} + +// LittlevGL flush callback +void FT81x_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) +{ + TFT_WriteBitmap((uint8_t*)color_map, area->x1, area->y1, lv_area_get_width(area), lv_area_get_height(area)); +} \ No newline at end of file diff --git a/lvgl_tft/FT81x.h b/lvgl_tft/FT81x.h new file mode 100644 index 0000000..96366df --- /dev/null +++ b/lvgl_tft/FT81x.h @@ -0,0 +1,17 @@ +#ifndef FT81X_H_ +#define FT81X_H_ + +#include + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "../lvgl_helpers.h" + +void FT81x_init(void); + +void FT81x_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); + +#endif /* FT81X_H_ */ diff --git a/lvgl_tft/GC9A01.c b/lvgl_tft/GC9A01.c new file mode 100644 index 0000000..63687e3 --- /dev/null +++ b/lvgl_tft/GC9A01.c @@ -0,0 +1,272 @@ +/** + * @file GC9A01.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "GC9A01.h" +#include "disp_spi.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +/********************* + * DEFINES + *********************/ + #define TAG "GC9A01" + +/********************** + * TYPEDEFS + **********************/ + +/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} lcd_init_cmd_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void GC9A01_set_orientation(uint8_t orientation); + +static void GC9A01_send_cmd(uint8_t cmd); +static void GC9A01_send_data(void * data, uint16_t length); +static void GC9A01_send_color(void * data, uint16_t length); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void GC9A01_init(void) +{ + lcd_init_cmd_t GC_init_cmds[]={ +//////////////////////////////////////////// + {0xEF, {0}, 0}, + {0xEB, {0x14}, 1}, + + {0xFE, {0}, 0}, + {0xEF, {0}, 0}, + + {0xEB, {0x14}, 1}, + {0x84, {0x40}, 1}, + {0x85, {0xFF}, 1}, + {0x86, {0xFF}, 1}, + {0x87, {0xFF}, 1}, + {0x88, {0x0A}, 1}, + {0x89, {0x21}, 1}, + {0x8A, {0x00}, 1}, + {0x8B, {0x80}, 1}, + {0x8C, {0x01}, 1}, + {0x8D, {0x01}, 1}, + {0x8E, {0xFF}, 1}, + {0x8F, {0xFF}, 1}, + {0xB6, {0x00, 0x20}, 2}, + //call orientation + {0x3A, {0x05}, 1}, + {0x90, {0x08, 0x08, 0X08, 0X08}, 4}, + {0xBD, {0x06}, 1}, + {0xBC, {0x00}, 1}, + {0xFF, {0x60, 0x01, 0x04}, 3}, + {0xC3, {0x13}, 1}, + {0xC4, {0x13}, 1}, + {0xC9, {0x22}, 1}, + {0xBE, {0x11}, 1}, + {0xE1, {0x10, 0x0E}, 2}, + {0xDF, {0x21, 0x0C, 0x02}, 3}, + {0xF0, {0x45, 0x09, 0x08, 0x08, 0x26, 0x2A}, 6}, + {0xF1, {0x43, 0x70, 0x72, 0x36, 0x37, 0x6F}, 6}, + {0xF2, {0x45, 0x09, 0x08, 0x08, 0x26, 0x2A}, 6}, + {0xF3, {0x43, 0x70, 0x72, 0x36, 0x37, 0x6F}, 6}, + {0xED, {0x1B, 0x0B}, 2}, + {0xAE, {0x77}, 1}, + {0xCD, {0x63}, 1}, + {0x70, {0x07, 0x07, 0x04, 0x0E, 0x0F, 0x09, 0x07, 0X08, 0x03}, 9}, + {0xE8, {0x34}, 1}, + {0x62, {0x18, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x18, 0X0F, 0x71, 0xEF, 0x70, 0x70}, 12}, + {0x63, {0x18, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x18, 0X13, 0x71, 0xF3, 0x70, 0x70}, 12}, + {0x64, {0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07}, 7}, + {0x66, {0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0X00, 0x00, 0x00}, 10}, + {0x67, {0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0X10, 0x32, 0x98}, 10}, + {0x74, {0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00}, 7}, + {0x98, {0x3E, 0x07}, 2}, + {0x35, {0}, 0}, + {0x21, {0}, 0}, + {0x11, {0}, 0x80}, //0x80 delay flag + {0x29, {0}, 0x80}, //0x80 delay flag + {0, {0}, 0xff}, //init end flag +//////////////////////////////////////////// + + }; + +#if GC9A01_BCKL == 15 + gpio_config_t io_conf; + io_conf.intr_type = GPIO_PIN_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = GPIO_SEL_15; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + gpio_config(&io_conf); +#endif + + //Initialize non-SPI GPIOs + gpio_pad_select_gpio(GC9A01_DC); + gpio_set_direction(GC9A01_DC, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(GC9A01_RST); + gpio_set_direction(GC9A01_RST, GPIO_MODE_OUTPUT); + +#if GC9A01_ENABLE_BACKLIGHT_CONTROL + gpio_pad_select_gpio(GC9A01_BCKL); + gpio_set_direction(GC9A01_BCKL, GPIO_MODE_OUTPUT); +#endif + //Reset the display + gpio_set_level(GC9A01_RST, 0); + vTaskDelay(100 / portTICK_RATE_MS); + gpio_set_level(GC9A01_RST, 1); + vTaskDelay(100 / portTICK_RATE_MS); + + ESP_LOGI(TAG, "Initialization."); + + //Send all the commands + uint16_t cmd = 0; + while (GC_init_cmds[cmd].databytes!=0xff) { + GC9A01_send_cmd(GC_init_cmds[cmd].cmd); + GC9A01_send_data(GC_init_cmds[cmd].data, GC_init_cmds[cmd].databytes&0x1F); + if (GC_init_cmds[cmd].databytes & 0x80) { + vTaskDelay(100 / portTICK_RATE_MS); + } + cmd++; + } + + GC9A01_enable_backlight(true); + + GC9A01_set_orientation(CONFIG_LV_DISPLAY_ORIENTATION); + +#if GC9A01_INVERT_COLORS == 1 + GC9A01_send_cmd(0x21); +#else + GC9A01_send_cmd(0x20); +#endif +} + + +void GC9A01_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) +{ + uint8_t data[4]; + + /*Column addresses*/ + GC9A01_send_cmd(0x2A); //0x2A + data[0] = (area->x1 >> 8) & 0xFF; + data[1] = area->x1 & 0xFF; + data[2] = (area->x2 >> 8) & 0xFF; + data[3] = area->x2 & 0xFF; + GC9A01_send_data(data, 4); + + /*Page addresses*/ + GC9A01_send_cmd(0x2B); //0x2B + data[0] = (area->y1 >> 8) & 0xFF; + data[1] = area->y1 & 0xFF; + data[2] = (area->y2 >> 8) & 0xFF; + data[3] = area->y2 & 0xFF; + GC9A01_send_data(data, 4); + + /*Memory write*/ + GC9A01_send_cmd(0x2C); //0x2C + + + uint32_t size = lv_area_get_width(area) * lv_area_get_height(area); + + GC9A01_send_color((void*)color_map, size * 2); +} + +void GC9A01_enable_backlight(bool backlight) +{ +#if GC9A01_ENABLE_BACKLIGHT_CONTROL + ESP_LOGI(TAG, "%s backlight.", backlight ? "Enabling" : "Disabling"); + uint32_t tmp = 0; + +#if (GC9A01_BCKL_ACTIVE_LVL==1) + tmp = backlight ? 1 : 0; +#else + tmp = backlight ? 0 : 1; +#endif + + gpio_set_level(GC9A01_BCKL, tmp); +#endif +} + +void GC9A01_sleep_in() +{ + uint8_t data[] = {0x08}; + GC9A01_send_cmd(0x10); //0x10 Enter Sleep Mode + GC9A01_send_data(&data, 1); +} + +void GC9A01_sleep_out() +{ + uint8_t data[] = {0x08}; + GC9A01_send_cmd(0x11); //0x11 Sleep OUT + GC9A01_send_data(&data, 1); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +static void GC9A01_send_cmd(uint8_t cmd) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(GC9A01_DC, 0); /*Command mode*/ + disp_spi_send_data(&cmd, 1); +} + +static void GC9A01_send_data(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(GC9A01_DC, 1); /*Data mode*/ + disp_spi_send_data(data, length); +} + +static void GC9A01_send_color(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(GC9A01_DC, 1); /*Data mode*/ + disp_spi_send_colors(data, length); +} + +static void GC9A01_set_orientation(uint8_t orientation) +{ + // ESP_ASSERT(orientation < 4); + + const char *orientation_str[] = { + "PORTRAIT", "PORTRAIT_INVERTED", "LANDSCAPE", "LANDSCAPE_INVERTED" + }; + + ESP_LOGI(TAG, "Display orientation: %s", orientation_str[orientation]); + +#if defined CONFIG_LV_PREDEFINED_DISPLAY_M5STACK + uint8_t data[] = {0x68, 0x68, 0x08, 0x08}; /// +#elif defined (CONFIG_LV_PREDEFINED_DISPLAY_WROVER4) + uint8_t data[] = {0x4C, 0x88, 0x28, 0xE8}; /// +#elif defined (CONFIG_LV_PREDEFINED_DISPLAY_NONE) + uint8_t data[] = {0x08, 0xC8, 0x68, 0xA8}; ///ggggg +#endif + + ESP_LOGI(TAG, "0x36 command value: 0x%02X", data[orientation]); + + GC9A01_send_cmd(0x36); + GC9A01_send_data((void *) &data[orientation], 1); +} diff --git a/lvgl_tft/GC9A01.h b/lvgl_tft/GC9A01.h new file mode 100644 index 0000000..d462c8d --- /dev/null +++ b/lvgl_tft/GC9A01.h @@ -0,0 +1,65 @@ +/** + * @file lv_templ.h + * + */ + +#ifndef GC9A01_H +#define GC9A01_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "../lvgl_helpers.h" + +/********************* + * DEFINES + *********************/ +#define GC9A01_DC CONFIG_LV_DISP_PIN_DC +#define GC9A01_RST CONFIG_LV_DISP_PIN_RST +#define GC9A01_BCKL CONFIG_LV_DISP_PIN_BCKL + +#define GC9A01_ENABLE_BACKLIGHT_CONTROL CONFIG_LV_ENABLE_BACKLIGHT_CONTROL + +#if CONFIG_LV_BACKLIGHT_ACTIVE_LVL + #define GC9A01_BCKL_ACTIVE_LVL 1 +#else + #define GC9A01_BCKL_ACTIVE_LVL 0 +#endif + +#define GC9A01_INVERT_COLORS CONFIG_LV_INVERT_COLORS + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void GC9A01_init(void); +void GC9A01_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); +void GC9A01_enable_backlight(bool backlight); +void GC9A01_sleep_in(void); +void GC9A01_sleep_out(void); + +/********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*GC9A01_H*/ diff --git a/lvgl_tft/Kconfig b/lvgl_tft/Kconfig new file mode 100644 index 0000000..3cc90d5 --- /dev/null +++ b/lvgl_tft/Kconfig @@ -0,0 +1,1000 @@ +# NOTES: +# - default <> if <> work only when no prompt is available for the user + +menu "LVGL TFT Display controller" + + # Predefined display configurations for multiple + # evaluation/development boards. + choice LV_PREDEFINED_DISPLAY + prompt "Select predefined display configuration" + default LV_PREDEFINED_DISPLAY_NONE + help + Select predefined display configuration + + config LV_PREDEFINED_DISPLAY_NONE + bool "None" + config LV_PREDEFINED_DISPLAY_WROVER4 + bool "ESP-Wrover-KIT v4.1" + select LV_TFT_DISPLAY_CONTROLLER_ILI9341 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_PREDEFINED_DISPLAY_M5STACK + bool "M5Stack" + select LV_TFT_DISPLAY_CONTROLLER_ILI9341 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_PREDEFINED_DISPLAY_M5STICK + bool "M5Stick" + select LV_TFT_DISPLAY_CONTROLLER_SH1107 + select LV_TFT_DISPLAY_PROTOCOL_SPI + select LV_TFT_DISPLAY_MONOCHROME + select LV_THEME_MONO + config LV_PREDEFINED_DISPLAY_M5STICKC + bool "M5StickC" + select LV_TFT_DISPLAY_CONTROLLER_ST7735S + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_PREDEFINED_DISPLAY_ERTFT0356 + bool "ER-TFT035-6" + select LV_TFT_DISPLAY_CONTROLLER_ILI9488 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + bool "Adafruit 3.5 Featherwing" + select LV_TFT_DISPLAY_CONTROLLER_HX8357 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_PREDEFINED_DISPLAY_RPI_MPI3501 + bool "RPi MPI3501" + select LV_TFT_DISPLAY_CONTROLLER_ILI9486 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_PREDEFINED_DISPLAY_WEMOS_LOLIN + bool "Wemos Lolin OLED" + select LV_TFT_DISPLAY_CONTROLLER_SSD1306 + select LV_TFT_DISPLAY_PROTOCOL_I2C + select LV_TFT_DISPLAY_MONOCHROME + select LV_THEME_MONO + config LV_PREDEFINED_DISPLAY_ATAG + bool "AIRcable ATAGv3" + select LV_TFT_DISPLAY_CONTROLLER_IL3820 + select LV_TFT_DISPLAY_PROTOCOL_SPI + select LV_TFT_DISPLAY_MONOCHROME + select LV_THEME_MONO + config LV_PREDEFINED_DISPLAY_RPI_RA8875 + bool "RAiO RA8875" + select LV_TFT_DISPLAY_CONTROLLER_RA8875 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_PREDEFINED_DISPLAY_TTGO + bool "TTGO T-Display" + select LV_TFT_DISPLAY_CONTROLLER_ST7789 + select LV_TFT_DISPLAY_PROTOCOL_SPI + select LV_TFT_DISPLAY_OFFSETS + config LV_PREDEFINED_DISPLAY_TTGO_CAMERA_PLUS + bool "TTGO Camera Plus" + select LV_TFT_DISPLAY_CONTROLLER_ST7789 + select LV_TFT_DISPLAY_PROTOCOL_SPI + endchoice + + # START of helper symbols. + # + # Display controller symbols, + # + # This boolean configuration symbols can be used to know what + # display controller has been choosen by the user. When selected + # the symbol is set to y, then in the file sdkconfig.h + # the symbol CONFIG_ is set to 1. + # + # If you add support for a new display controller to the repository + # you must add a config option for it on this helper symbols section. + config LV_TFT_DISPLAY_CONTROLLER_ILI9341 + bool + help + ILI9341 display controller. + + config LV_TFT_DISPLAY_CONTROLLER_ILI9481 + bool + help + ILI9481 display controller. + + config LV_TFT_DISPLAY_CONTROLLER_ILI9488 + bool + help + ILI9488 display controller. + + config LV_TFT_DISPLAY_CONTROLLER_ILI9486 + bool + help + ILI9486 display controller. + + config LV_TFT_DISPLAY_CONTROLLER_ST7789 + bool + help + ST7789 display controller. + + config LV_TFT_DISPLAY_CONTROLLER_GC9A01 + bool + help + GC9A01 display controller. + + config LV_TFT_DISPLAY_CONTROLLER_ST7735S + bool + help + ST7735S display controller. + + config LV_TFT_DISPLAY_CONTROLLER_HX8357 + bool + help + HX8357 display controller. + + config LV_TFT_DISPLAY_CONTROLLER_SH1107 + bool + help + SH1107 display controller. + + config LV_TFT_DISPLAY_CONTROLLER_SSD1306 + bool + help + SSD1306 display controller. + + config LV_TFT_DISPLAY_CONTROLLER_FT81X + bool + help + FT81x display controller. + + config LV_TFT_DISPLAY_CONTROLLER_IL3820 + bool + help + IL3820 epaper display controller. + + config LV_TFT_DISPLAY_CONTROLLER_JD79653A + bool + help + FitiPower JD79653A display controller for GoodDisplay GDEW0154M09 e-paper panel + + config LV_TFT_DISPLAY_CONTROLLER_UC8151D + bool + help + UltraChip UC8151D display controller with GoodDisplay GDEW0154M10 DES e-paper panel + + config LV_TFT_DISPLAY_CONTROLLER_RA8875 + bool + help + RA8875 display controller. + + # Display controller communication protocol + # + # This symbols define the communication protocol used by the + # ESP32 to communicate with the display controller. + # This symbols can be used, but not limited, to: + # - Know what peripherals to initialize. + # - Know if the touch and display controllers share the same peripheral. + # - Etc. + config LV_TFT_DISPLAY_PROTOCOL_SPI + bool + help + Display controller protocol SPI + + config LV_TFT_DISPLAY_PROTOCOL_I2C + bool + help + Display controller protocol I2C + + config LV_TFT_DISPLAY_OFFSETS + bool + help + Display area doesn't start at address 0 + + config LV_TFT_DISPLAY_X_OFFSET + depends on LV_TFT_DISPLAY_OFFSETS + int + default 40 if LV_PREDEFINED_DISPLAY_TTGO && (LV_DISPLAY_ORIENTATION_LANDSCAPE || LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED) + default 53 if LV_PREDEFINED_DISPLAY_TTGO && (LV_DISPLAY_ORIENTATION_PORTRAIT || LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED) + default 0 + + config LV_TFT_DISPLAY_Y_OFFSET + depends on LV_TFT_DISPLAY_OFFSETS + int + default 53 if LV_PREDEFINED_DISPLAY_TTGO && (LV_DISPLAY_ORIENTATION_LANDSCAPE || LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED) + default 40 if LV_PREDEFINED_DISPLAY_TTGO && (LV_DISPLAY_ORIENTATION_PORTRAIT || LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED) + default 0 + + + # Display colors(?) + # Useful to know when the display being used is a monochrome + # display, so we can use the monochrome theme, etc. + config LV_TFT_DISPLAY_MONOCHROME + bool + help + A monochrome display is used. + # END of helper symbols + + choice + prompt "Select predefined board pinouts" if LV_PREDEFINED_DISPLAY_NONE || LV_PREDEFINED_DISPLAY_ERTFT0356 + default LV_PREDEFINED_PINS_NONE + help + Select predefined board pin out configuration. + + config LV_PREDEFINED_PINS_NONE + bool "None" + config LV_PREDEFINED_PINS_38V4 + bool "ESP32 DevKit v4 with 38 pins" + config LV_PREDEFINED_PINS_30 + bool "ESP32 Devkit v1 with 30 pins" + config LV_PREDEFINED_PINS_38V1 + bool "Dev Board with 38 pins" + config LV_PREDEFINED_PINS_TKOALA + bool "TTGO Koala" + endchoice + + # Select one of the available display controllers. + choice + prompt "Select a display controller model." if LV_PREDEFINED_DISPLAY_NONE + help + Select the controller for your display. + + config LV_TFT_DISPLAY_USER_CONTROLLER_ILI9341 + bool "ILI9341" + select LV_TFT_DISPLAY_CONTROLLER_ILI9341 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_TFT_DISPLAY_USER_CONTROLLER_ILI9481 + bool "ILI9481" + select LV_TFT_DISPLAY_CONTROLLER_ILI9481 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_TFT_DISPLAY_USER_CONTROLLER_ILI9486 + bool "ILI9486" + select LV_TFT_DISPLAY_CONTROLLER_ILI9486 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_TFT_DISPLAY_USER_CONTROLLER_ILI9488 + bool "ILI9488" + select LV_TFT_DISPLAY_CONTROLLER_ILI9488 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_TFT_DISPLAY_USER_CONTROLLER_ST7789 + bool "ST7789" + select LV_TFT_DISPLAY_CONTROLLER_ST7789 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_TFT_DISPLAY_USER_CONTROLLER_GC9A01 + bool "GC9A01" + select LV_TFT_DISPLAY_CONTROLLER_GC9A01 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_TFT_DISPLAY_USER_CONTROLLER_ST7735S + bool "ST7735S" + select LV_TFT_DISPLAY_CONTROLLER_ST7735S + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_TFT_DISPLAY_USER_CONTROLLER_HX8357 + bool "HX8357" + select LV_TFT_DISPLAY_CONTROLLER_HX8357 + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_TFT_DISPLAY_USER_CONTROLLER_SH1107 + bool "SH1107" + select LV_TFT_DISPLAY_CONTROLLER_SH1107 + select LV_TFT_DISPLAY_PROTOCOL_SPI + select LV_TFT_DISPLAY_MONOCHROME + config LV_TFT_DISPLAY_USER_CONTROLLER_SSD1306 + bool "SSD1306" + select LV_TFT_DISPLAY_CONTROLLER_SSD1306 + select LV_TFT_DISPLAY_PROTOCOL_I2C + select LV_TFT_DISPLAY_MONOCHROME + config LV_TFT_DISPLAY_USER_CONTROLLER_FT81X + bool "FT81X" + select LV_TFT_DISPLAY_CONTROLLER_FT81X + select LV_TFT_DISPLAY_PROTOCOL_SPI + config LV_TFT_DISPLAY_USER_CONTROLLER_IL3820 + bool "IL3820" + select LV_TFT_DISPLAY_CONTROLLER_IL3820 + select LV_TFT_DISPLAY_PROTOCOL_SPI + select LV_TFT_DISPLAY_MONOCHROME + config LV_TFT_DISPLAY_USER_CONTROLLER_JD79653A + bool "JD79653A" + select LV_TFT_DISPLAY_CONTROLLER_JD79653A + select LV_TFT_DISPLAY_PROTOCOL_SPI + select LV_TFT_DISPLAY_MONOCHROME + config LV_TFT_DISPLAY_USER_CONTROLLER_UC8151D + bool "UC8151D" + select LV_TFT_DISPLAY_CONTROLLER_UC8151D + select LV_TFT_DISPLAY_PROTOCOL_SPI + select LV_TFT_DISPLAY_MONOCHROME + config LV_TFT_DISPLAY_USER_CONTROLLER_RA8875 + bool "RA8875" + select LV_TFT_DISPLAY_CONTROLLER_RA8875 + select LV_TFT_DISPLAY_PROTOCOL_SPI + endchoice + + # Select one of the available FT81x configurations. + choice + prompt "Select a FT81x configuration." if LV_TFT_DISPLAY_USER_CONTROLLER_FT81X + default LV_FT81X_CONFIG_EVE_NHD_50 + help + Select a configuration for your FT81x display. + config LV_FT81X_CONFIG_EVE_VM800B35A + bool "EVE_VM800B35A" + config LV_FT81X_CONFIG_EVE_VM800B43A + bool "EVE_VM800B43A" + config LV_FT81X_CONFIG_EVE_VM800B50A + bool "EVE_VM800B50A" + config LV_FT81X_CONFIG_EVE_VM810C + bool "EVE_VM810C" + config LV_FT81X_CONFIG_EVE_ME812A + bool "EVE_ME812A" + config LV_FT81X_CONFIG_EVE_ME813A + bool "EVE_ME813A" + config LV_FT81X_CONFIG_EVE_FT810CB_HY50HD + bool "EVE_FT810CB_HY50HD" + config LV_FT81X_CONFIG_EVE_FT811CB_HY50HD + bool "EVE_FT811CB_HY50HD" + config LV_FT81X_CONFIG_EVE_ET07 + bool "EVE_ET07" + config LV_FT81X_CONFIG_EVE_RVT28 + bool "EVE_RVT28" + config LV_FT81X_CONFIG_EVE_RVT35 + bool "EVE_RVT35" + config LV_FT81X_CONFIG_EVE_RVT43 + bool "EVE_RVT43" + config LV_FT81X_CONFIG_EVE_RVT50 + bool "EVE_RVT50" + config LV_FT81X_CONFIG_EVE_RVT70 + bool "EVE_RVT70" + config LV_FT81X_CONFIG_EVE_RiTFT43 + bool "EVE_RiTFT43" + config LV_FT81X_CONFIG_EVE_RiTFT50 + bool "EVE_RiTFT50" + config LV_FT81X_CONFIG_EVE_RiTFT70 + bool "EVE_RiTFT70" + config LV_FT81X_CONFIG_EVE_EVE2_29 + bool "EVE_EVE2_29" + config LV_FT81X_CONFIG_EVE_EVE2_35 + bool "EVE_EVE2_35" + config LV_FT81X_CONFIG_EVE_EVE2_35G + bool "EVE_EVE2_35G" + config LV_FT81X_CONFIG_EVE_EVE2_38 + bool "EVE_EVE2_38" + config LV_FT81X_CONFIG_EVE_EVE2_38G + bool "EVE_EVE2_38G" + config LV_FT81X_CONFIG_EVE_EVE2_43 + bool "EVE_EVE2_43" + config LV_FT81X_CONFIG_EVE_EVE2_43G + bool "EVE_EVE2_43G" + config LV_FT81X_CONFIG_EVE_EVE2_50 + bool "EVE_EVE2_50" + config LV_FT81X_CONFIG_EVE_EVE2_50G + bool "EVE_EVE2_50G" + config LV_FT81X_CONFIG_EVE_EVE2_70 + bool "EVE_EVE2_70" + config LV_FT81X_CONFIG_EVE_EVE2_70G + bool "EVE_EVE2_70G" + config LV_FT81X_CONFIG_EVE_EVE3_35 + bool "EVE_EVE3_35" + config LV_FT81X_CONFIG_EVE_EVE3_35G + bool "EVE_EVE3_35G" + config LV_FT81X_CONFIG_EVE_EVE3_43 + bool "EVE_EVE3_43" + config LV_FT81X_CONFIG_EVE_EVE3_43G + bool "EVE_EVE3_43G" + config LV_FT81X_CONFIG_EVE_EVE3_50 + bool "EVE_EVE3_50" + config LV_FT81X_CONFIG_EVE_EVE3_50G + bool "EVE_EVE3_50G" + config LV_FT81X_CONFIG_EVE_EVE3_70 + bool "EVE_EVE3_70" + config LV_FT81X_CONFIG_EVE_EVE3_70G + bool "EVE_EVE3_70G" + config LV_FT81X_CONFIG_EVE_NHD_35 + bool "EVE_NHD_35" + config LV_FT81X_CONFIG_EVE_NHD_43 + bool "EVE_NHD_43" + config LV_FT81X_CONFIG_EVE_NHD_50 + bool "EVE_NHD_50" + config LV_FT81X_CONFIG_EVE_NHD_70 + bool "EVE_NHD_70" + config LV_FT81X_CONFIG_EVE_ADAM101 + bool "EVE_ADAM101" + config LV_FT81X_CONFIG_EVE_CFAF240400C1_030SC + bool "EVE_CFAF240400C1_030SC" + config LV_FT81X_CONFIG_EVE_CFAF320240F_035T + bool "EVE_CFAF320240F_035T" + config LV_FT81X_CONFIG_EVE_CFAF480128A0_039TC + bool "EVE_CFAF480128A0_039TC" + config LV_FT81X_CONFIG_EVE_CFAF800480E0_050SC + bool "EVE_CFAF800480E0_050SC" + config LV_FT81X_CONFIG_EVE_PAF90 + bool "EVE_PAF90" + config LV_FT81X_CONFIG_EVE_SUNFLOWER + bool "EVE_SUNFLOWER" + config LV_FT81X_CONFIG_EVE_CONNECTEVE + bool "EVE_CONNECTEVE" + endchoice + + choice + prompt "TFT SPI Bus." if LV_TFT_DISPLAY_PROTOCOL_SPI && \ + !LV_PREDEFINED_DISPLAY_TTGO + default LV_TFT_DISPLAY_SPI_VSPI if LV_PREDEFINED_DISPLAY_TTGO && \ + !IDF_TARGET_ESP32S2 + default LV_TFT_DISPLAY_SPI_FSPI if IDF_TARGET_ESP32S2 + help + Select the SPI Bus the TFT Display is attached to. + + config LV_TFT_DISPLAY_SPI_HSPI + bool "HSPI" + config LV_TFT_DISPLAY_SPI_VSPI + bool "VSPI" if !IDF_TARGET_ESP32S2 + config LV_TFT_DISPLAY_SPI_FSPI + bool "FSPI" if IDF_TARGET_ESP32S2 + endchoice + + choice + prompt "TFT Data Transfer Mode" if LV_TFT_DISPLAY_PROTOCOL_SPI + default LV_TFT_DISPLAY_SPI_TRANS_MODE_SIO + help + Select the SPI SIO/DIO/QIO Transfer Mode for the TFT Display. + + config LV_TFT_DISPLAY_SPI_TRANS_MODE_SIO + bool "SIO (MOSI/MISO)" + config LV_TFT_DISPLAY_SPI_TRANS_MODE_DIO + bool "DIO (2-bit Dual SPI)" + config LV_TFT_DISPLAY_SPI_TRANS_MODE_QIO + bool "QIO (4-bit Quad SPI)" + endchoice + + choice + prompt "TFT SPI Duplex Mode" if LV_TFT_DISPLAY_PROTOCOL_SPI + default LV_TFT_DISPLAY_SPI_FULL_DUPLEX if LV_PREDEFINED_DISPLAY_RPI_RA8875 || LV_TFT_DISPLAY_CONTROLLER_FT81X + default LV_TFT_DISPLAY_SPI_HALF_DUPLEX + help + Select the SPI Duplex Mode for the TFT Display. + + config LV_TFT_DISPLAY_SPI_HALF_DUPLEX + bool "HALF DUPLEX" + config LV_TFT_DISPLAY_SPI_FULL_DUPLEX + bool "FULL DUPLEX" + depends on LV_TFT_DISPLAY_SPI_TRANS_MODE_SIO + endchoice + + choice + prompt "Display I2C port" if LV_TFT_DISPLAY_PROTOCOL_I2C + default LV_DISPLAY_I2C_PORT_0 + help + Select the I2C port used by the display controller. + + config LV_DISPLAY_I2C_PORT_0 + bool "I2C PORT 0" + config LV_DISPLAY_I2C_PORT_1 + bool "I2C PORT 1" + endchoice + + choice + prompt "Display orientation" + depends on LV_TFT_DISPLAY_CONTROLLER_ILI9341 || \ + LV_TFT_DISPLAY_CONTROLLER_ILI9481 || \ + LV_TFT_DISPLAY_CONTROLLER_ILI9486 || \ + LV_TFT_DISPLAY_CONTROLLER_ILI9488 || \ + LV_TFT_DISPLAY_CONTROLLER_SH1107 || \ + LV_TFT_DISPLAY_CONTROLLER_SSD1306 || \ + LV_TFT_DISPLAY_CONTROLLER_FT81X || \ + LV_TFT_DISPLAY_CONTROLLER_ST7789 || \ + LV_TFT_DISPLAY_CONTROLLER_GC9A01 || \ + LV_TFT_DISPLAY_CONTROLLER_ST7735S || \ + LV_TFT_DISPLAY_CONTROLLER_IL3820 || \ + LV_TFT_DISPLAY_CONTROLLER_RA8875 || \ + LV_TFT_DISPLAY_CONTROLLER_JD79653A || \ + LV_TFT_DISPLAY_CONTROLLER_UC8151D + default LV_DISPLAY_ORIENTATION_LANDSCAPE \ + if !LV_TFT_DISPLAY_CONTROLLER_JD79653A || \ + !LV_TFT_DISPLAY_CONTROLLER_UC8151D + default LV_DISPLAY_ORIENTATION_PORTRAIT if LV_TFT_DISPLAY_CONTROLLER_JD79653A || LV_TFT_DISPLAY_CONTROLLER_UC8151D + help + Display orientation. + + config LV_DISPLAY_ORIENTATION_PORTRAIT + bool "Portrait" + config LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED + bool "Inverted Portrait" + depends on !LV_TFT_DISPLAY_CONTROLLER_IL3820 + config LV_DISPLAY_ORIENTATION_LANDSCAPE + bool "Landscape" + depends on !LV_TFT_DISPLAY_CONTROLLER_JD79653A + depends on !LV_TFT_DISPLAY_CONTROLLER_UC8151D + config LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED + bool "Inverted Landscape" + depends on !LV_TFT_DISPLAY_CONTROLLER_IL3820 + depends on !LV_TFT_DISPLAY_CONTROLLER_JD79653A + depends on !LV_TFT_DISPLAY_CONTROLLER_UC8151D + endchoice + + + # Display orientation + # This symbol is meant to be used as parameter on the display_set_orientation + # function at init. + config LV_DISPLAY_ORIENTATION + int + default 0 if LV_DISPLAY_ORIENTATION_PORTRAIT + default 1 if LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED + default 2 if LV_DISPLAY_ORIENTATION_LANDSCAPE + default 3 if LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED + + config LV_DISPLAY_WIDTH + int "TFT display width in pixels." if LV_PREDEFINED_DISPLAY_NONE || \ + LV_TFT_DISPLAY_CONTROLLER_FT81X + default 240 if ( LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_WROVER4 ) && (LV_DISPLAY_ORIENTATION_PORTRAIT) + default 240 if ( LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_WROVER4 ) && (LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED) + default 320 if ( LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_WROVER4 ) && (LV_DISPLAY_ORIENTATION_LANDSCAPE) + default 320 if ( LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_WROVER4 ) && (LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED) + default 480 if LV_PREDEFINED_DISPLAY_ERTFT0356 || LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + default 64 if ( LV_PREDEFINED_DISPLAY_WEMOS_LOLIN || LV_PREDEFINED_DISPLAY_M5STICK ) && LV_DISPLAY_ORIENTATION_PORTRAIT + default 128 if ( LV_PREDEFINED_DISPLAY_WEMOS_LOLIN || LV_PREDEFINED_DISPLAY_M5STICK ) && LV_DISPLAY_ORIENTATION_LANDSCAPE + default 800 if LV_TFT_DISPLAY_CONTROLLER_FT81X + default 128 if LV_PREDEFINED_DISPLAY_ATAG && LV_DISPLAY_ORIENTATION_PORTRAIT + default 296 if LV_PREDEFINED_DISPLAY_ATAG && LV_DISPLAY_ORIENTATION_LANDSCAPE + default 80 if LV_PREDEFINED_DISPLAY_M5STICKC && (LV_DISPLAY_ORIENTATION_PORTRAIT || LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED) + default 160 if LV_PREDEFINED_DISPLAY_M5STICKC && (LV_DISPLAY_ORIENTATION_LANDSCAPE || LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED) + default 135 if LV_PREDEFINED_DISPLAY_TTGO && (LV_DISPLAY_ORIENTATION_PORTRAIT || LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED) + default 240 if LV_PREDEFINED_DISPLAY_TTGO && (LV_DISPLAY_ORIENTATION_LANDSCAPE || LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED) + default 240 if LV_PREDEFINED_DISPLAY_TTGO_CAMERA_PLUS + default 320 + + config LV_DISPLAY_HEIGHT + int "TFT display height in pixels." if LV_PREDEFINED_DISPLAY_NONE || \ + LV_TFT_DISPLAY_CONTROLLER_FT81X + default 320 if ( LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_WROVER4 ) && (LV_DISPLAY_ORIENTATION_PORTRAIT) + default 320 if ( LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_WROVER4 ) && (LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED) + default 240 if ( LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_WROVER4 ) && (LV_DISPLAY_ORIENTATION_LANDSCAPE) + default 240 if ( LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_WROVER4 ) && (LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED) + default 320 if LV_PREDEFINED_DISPLAY_ERTFT0356 || LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + default 128 if ( LV_PREDEFINED_DISPLAY_WEMOS_LOLIN || LV_PREDEFINED_DISPLAY_M5STICK ) && LV_DISPLAY_ORIENTATION_PORTRAIT + default 64 if ( LV_PREDEFINED_DISPLAY_WEMOS_LOLIN || LV_PREDEFINED_DISPLAY_M5STICK ) && LV_DISPLAY_ORIENTATION_LANDSCAPE + default 480 if LV_TFT_DISPLAY_CONTROLLER_FT81X + default 296 if LV_PREDEFINED_DISPLAY_ATAG && LV_DISPLAY_ORIENTATION_PORTRAIT + default 128 if LV_PREDEFINED_DISPLAY_ATAG && LV_DISPLAY_ORIENTATION_LANDSCAPE + default 160 if LV_PREDEFINED_DISPLAY_M5STICKC && (LV_DISPLAY_ORIENTATION_PORTRAIT || LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED) + default 80 if LV_PREDEFINED_DISPLAY_M5STICKC && (LV_DISPLAY_ORIENTATION_LANDSCAPE || LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED) + default 240 if LV_PREDEFINED_DISPLAY_TTGO && (LV_DISPLAY_ORIENTATION_PORTRAIT || LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED) + default 135 if LV_PREDEFINED_DISPLAY_TTGO && (LV_DISPLAY_ORIENTATION_LANDSCAPE || LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED) + default 240 if LV_PREDEFINED_DISPLAY_TTGO_CAMERA_PLUS + default 240 + + config LV_TFT_USE_CUSTOM_SPI_CLK_DIVIDER + bool "Use custom SPI clock frequency." if LV_TFT_DISPLAY_PROTOCOL_SPI + default n + help + Allows to use a custom divider for the SPI clock frequency. + + choice + prompt "Select a custom frequency." + depends on LV_TFT_USE_CUSTOM_SPI_CLK_DIVIDER + default LV_TFT_SPI_CLK_DIVIDER_4 if LV_TFT_DISPLAY_CONTROLLER_ST7789 ||LV_TFT_DISPLAY_CONTROLLER_GC9A01 || LV_TFT_DISPLAY_CONTROLLER_ILI9486 + default LV_TFT_SPI_CLK_DIVIDER_5 if LV_TFT_DISPLAY_CONTROLLER_ILI9481 + default LV_TFT_SPI_CLK_DIVIDER_3 if LV_TFT_DISPLAY_CONTROLLER_HX8357 + default LV_TFT_SPI_CLK_DIVIDER_10 if LV_TFT_DISPLAY_CONTROLLER_SH1107 + default LV_TFT_SPI_CLK_DIVIDER_16 if LV_TFT_DISPLAY_CONTROLLER_JD79653A || LV_TFT_DISPLAY_CONTROLLER_UC8151D + default LV_TFT_SPI_CLK_DIVIDER_2 + + config LV_TFT_SPI_CLK_DIVIDER_1 + bool "80 MHz" + config LV_TFT_SPI_CLK_DIVIDER_2 + bool "40 MHz" + config LV_TFT_SPI_CLK_DIVIDER_3 + bool "26.67 MHz" + config LV_TFT_SPI_CLK_DIVIDER_4 + bool "20 MHz" + config LV_TFT_SPI_CLK_DIVIDER_5 + bool "16 MHz" + config LV_TFT_SPI_CLK_DIVIDER_6 + bool "13.33 MHz" + config LV_TFT_SPI_CLK_DIVIDER_7 + bool "11.43 MHz" + config LV_TFT_SPI_CLK_DIVIDER_8 + bool "10 MHz" + config LV_TFT_SPI_CLK_DIVIDER_9 + bool "8.89 MHz" + config LV_TFT_SPI_CLK_DIVIDER_10 + bool "8 MHz" + config LV_TFT_SPI_CLK_DIVIDER_12 + bool "6.67 MHz" + config LV_TFT_SPI_CLK_DIVIDER_16 + bool "5 MHz" + config LV_TFT_SPI_CLK_DIVIDER_20 + bool "4 MHz" + config LV_TFT_SPI_CLK_DIVIDER_24 + bool "3.33 MHz" + config LV_TFT_SPI_CLK_DIVIDER_32 + bool "2.5 MHz" + config LV_TFT_SPI_CLK_DIVIDER_40 + bool "2 MHz" + config LV_TFT_SPI_CLK_DIVIDER_48 + bool "1.67 MHz" + config LV_TFT_SPI_CLK_DIVIDER_80 + bool "1 MHz" + endchoice + + config LV_TFT_CUSTOM_SPI_CLK_DIVIDER + int + default 1 if LV_TFT_SPI_CLK_DIVIDER_1 + default 2 if LV_TFT_SPI_CLK_DIVIDER_2 + default 3 if LV_TFT_SPI_CLK_DIVIDER_3 + default 4 if LV_TFT_SPI_CLK_DIVIDER_4 + default 5 if LV_TFT_SPI_CLK_DIVIDER_5 + default 6 if LV_TFT_SPI_CLK_DIVIDER_6 + default 7 if LV_TFT_SPI_CLK_DIVIDER_7 + default 8 if LV_TFT_SPI_CLK_DIVIDER_8 + default 9 if LV_TFT_SPI_CLK_DIVIDER_9 + default 10 if LV_TFT_SPI_CLK_DIVIDER_10 + default 12 if LV_TFT_SPI_CLK_DIVIDER_12 + default 16 if LV_TFT_SPI_CLK_DIVIDER_16 + default 20 if LV_TFT_SPI_CLK_DIVIDER_20 + default 24 if LV_TFT_SPI_CLK_DIVIDER_24 + default 32 if LV_TFT_SPI_CLK_DIVIDER_32 + default 40 if LV_TFT_SPI_CLK_DIVIDER_40 + default 48 if LV_TFT_SPI_CLK_DIVIDER_48 + default 80 if LV_TFT_SPI_CLK_DIVIDER_80 + default 2 + + config LV_INVERT_DISPLAY + bool "IN DEPRECATION - Invert display." + default y if LV_PREDEFINED_DISPLAY_M5STACK + help + If text is backwards on your display, try enabling this. + + config LV_INVERT_COLORS + bool "Invert colors in display" if LV_TFT_DISPLAY_CONTROLLER_ILI9341 || LV_TFT_DISPLAY_CONTROLLER_ST7735S || LV_TFT_DISPLAY_CONTROLLER_ILI9481 + default y if LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_M5STICKC + help + If the colors look inverted on your display, try enabling this. + + config LV_M5STICKC_HANDLE_AXP192 + bool "Handle Backlight and TFT power for M5StickC using AXP192." if LV_PREDEFINED_DISPLAY_M5STICKC || LV_TFT_DISPLAY_CONTROLLER_ST7735S + default y if LV_PREDEFINED_DISPLAY_M5STICKC + help + Display and TFT power supply on M5StickC is controlled using an AXP192 Power Mangerment IC. + Select yes if you want to enable TFT IC (LDO3) and backlight power using AXP192 by LVGL, or select no if you want to take care of + power management in your own code. + + config LV_AXP192_PIN_SDA + int "GPIO for AXP192 I2C SDA" if LV_M5STICKC_HANDLE_AXP192 + range 0 39 + default 21 if LV_PREDEFINED_DISPLAY_M5STICKC + default 21 + help + Configure the AXP192 I2C SDA pin here. + + config LV_AXP192_PIN_SCL + int "GPIO for AXP192 I2C SCL" if LV_M5STICKC_HANDLE_AXP192 + range 0 39 + default 22 if LV_PREDEFINED_DISPLAY_M5STICKC + default 22 + help + Configure the AXP192 I2C SDA pin here. + + # menu will be visible only when LV_PREDEFINED_DISPLAY_NONE is y + menu "Display RA8875 Configuration" + visible if LV_TFT_DISPLAY_CONTROLLER_RA8875 + + config LV_DISP_RA8875_PLLDIVM + int "PLLDIVM value for RA8875 System Clock" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + range 0 1 + default 0 + help + Configure the value to use for PLLDIVM in PLLC0 register here. + + config LV_DISP_RA8875_PLLDIVN + int "PLLDIVN value for RA8875 System Clock" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + range 1 31 + default 7 + help + Configure the value to use for PLLDIVN in PLLC0 register here. + + config LV_DISP_RA8875_PLLDIVK + int "PLLDIVK value for RA8875 System Clock" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + range 0 7 + default 3 + help + Configure the value to use for PLLDIVK in PLLC1 register here. + + config LV_DISP_RA8875_PCLK_MULTIPLIER + int "PCLK Period Setting" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + range 0 3 + default 0 + help + Configure the PCLK multiplier in PCSR register here. + + config LV_DISP_RA8875_PCLK_INVERT + bool "PCLK Inversion" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + default n + help + Configure the PCLK to be inverted in PCSR register here. + + config LV_DISP_RA8875_DE_POLARITY + bool "Data Enable (DE) Polarity/Invert" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + default n + help + Set to make data enable (DE) signal active low. + + config LV_DISP_RA8875_HORI_NON_DISP_PERIOD + int "Horizontal Non-Display Period" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + range 12 274 + default 12 + help + Configure the Horizontal Non-Display Period (aka Horizontal Back Porch). + Horizontal Non-Display Period(pixels) = 8*(HNDR+1) + 2*(HNDFTR/2+1) + 2 + + config LV_DISP_RA8875_HSYNC_START + int "HSYNC Start Position" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + range 8 256 + default 8 + help + Configure start position of HSYNC (aka Horizontal Front Porch). + HSYNC Start Position(pixels) = 8*(HSTR+1) + + config LV_DISP_RA8875_HSYNC_PW + int "HSYNC Pulse Width" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + range 8 256 + default 8 + help + Configure pulse width of HSYNC. + HSYNC Pulse Width(pixels) = 8*(HPW+1) + + config LV_DISP_RA8875_HSYNC_POLARITY + bool "HSYNC Polarity/Invert" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + default n + help + Set to make HSYNC signal active high. + + config LV_DISP_RA8875_VERT_NON_DISP_PERIOD + int "Vertical Non-Display Period" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + range 1 512 + default 1 + help + Configure the Vertical Non-Display Period (aka Vertical Back Porch). + Vertical Non-Display Period(lines) = (VNDR+1) + + config LV_DISP_RA8875_VSYNC_START + int "VSYNC Start Position" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + range 1 512 + default 1 + help + Configure start position of VSYNC (aka Vertical Front Porch). + VSYNC Start Position(lines) = (VSTR+1) + + config LV_DISP_RA8875_VSYNC_PW + int "VSYNC Pulse Width" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + range 1 128 + default 1 + help + Configure pulse width of VSYNC. + VSYNC Pulse Width(lines) = (VPWR+1) + + config LV_DISP_RA8875_VSYNC_POLARITY + bool "VSYNC Polarity/Invert" + depends on LV_TFT_DISPLAY_CONTROLLER_RA8875 + default n + help + Set to make VSYNC signal active high. + + endmenu + + # menu will be visible only when LV_PREDEFINED_DISPLAY_NONE is y + menu "Display Pin Assignments" + visible if LV_PREDEFINED_DISPLAY_NONE || LV_PREDEFINED_DISPLAY_RPI_MPI3501 || LV_PREDEFINED_PINS_TKOALA + + config LV_DISP_SPI_MOSI + int "GPIO for MOSI (Master Out Slave In)" if LV_TFT_DISPLAY_PROTOCOL_SPI + range 0 39 + default 23 if LV_PREDEFINED_DISPLAY_WROVER4 + default 23 if LV_PREDEFINED_DISPLAY_ATAG + default 23 if LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_M5STICK + default 15 if LV_PREDEFINED_DISPLAY_M5STICKC + default 18 if LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + default 23 if LV_PREDEFINED_PINS_TKOALA + default 19 if LV_PREDEFINED_DISPLAY_TTGO + default 19 if LV_PREDEFINED_DISPLAY_TTGO_CAMERA_PLUS + default 13 + + help + Configure the display MOSI pin here. + + config LV_DISPLAY_USE_SPI_MISO + bool "GPIO for MISO (Master In Slave Out)" if LV_TFT_DISPLAY_PROTOCOL_SPI + default y if LV_PREDEFINED_PINS_TKOALA + help + Enable the MISO signal to control the display. You can disable + it when the display does not need MISO signal to be controlled. + + config LV_DISP_SPI_MISO + int "GPIO for MISO (Master In Slave Out)" if LV_TFT_DISPLAY_PROTOCOL_SPI + depends on LV_DISPLAY_USE_SPI_MISO + range 0 39 + default 19 if LV_PREDEFINED_PINS_TKOALA + default 0 + + help + Configure the display MISO pin here. + + config LV_DISP_SPI_INPUT_DELAY_NS + int "MISO Input Delay (ns)" if LV_TFT_DISPLAY_PROTOCOL_SPI + depends on LV_DISPLAY_USE_SPI_MISO + range 0 2147483647 + default 0 + help + The time required between SCLK and MISO being valid, including the possible clock + delay from processor to display. Leave at 0 unless you know you need a delay. + + config LV_DISP_SPI_IO2 + int "GPIO for Quad SPI IO2/WP" if LV_TFT_DISPLAY_PROTOCOL_SPI + depends on LV_TFT_DISPLAY_SPI_TRANS_MODE_QIO + range -1 39 + default 22 if LV_PREDEFINED_PINS_TKOALA && LV_TFT_DISPLAY_SPI_TRANS_MODE_QIO + default -1 + help + Configure the display Quad SPI IO2 pin here. + + config LV_DISP_SPI_IO3 + int "GPIO for Quad SPI IO3/HD" if LV_TFT_DISPLAY_PROTOCOL_SPI + depends on LV_TFT_DISPLAY_SPI_TRANS_MODE_QIO + range -1 39 + default 21 if LV_PREDEFINED_PINS_TKOALA && LV_TFT_DISPLAY_SPI_TRANS_MODE_QIO + default -1 + help + Configure the display Quad SPI IO2 pin here. + + config LV_DISP_SPI_CLK + int "GPIO for CLK (SCK / Serial Clock)" if LV_TFT_DISPLAY_PROTOCOL_SPI + range 0 39 + default 18 if LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_M5STICK + default 13 if LV_PREDEFINED_DISPLAY_M5STICKC + default 18 if LV_PREDEFINED_DISPLAY_ATAG + default 19 if LV_PREDEFINED_DISPLAY_WROVER4 + default 5 if LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + default 18 if LV_PREDEFINED_PINS_TKOALA + default 18 if LV_PREDEFINED_DISPLAY_TTGO + default 21 if LV_PREDEFINED_DISPLAY_TTGO_CAMERA_PLUS + default 14 + + help + Configure the display CLK pin here. + + config LV_DISPLAY_USE_SPI_CS + bool "Use CS signal to control the display" if LV_TFT_DISPLAY_PROTOCOL_SPI + default y + help + Enable the CS signal to control the display. You can disable + it when the display does not need CS signal to be controlled. + + config LV_DISP_SPI_CS + int "GPIO for CS (Slave Select)" if LV_TFT_DISPLAY_PROTOCOL_SPI + depends on LV_DISPLAY_USE_SPI_CS + range 0 39 + default 5 if LV_PREDEFINED_PINS_38V1 + default 14 if LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_M5STICK + default 5 if LV_PREDEFINED_DISPLAY_M5STICKC + default 22 if LV_PREDEFINED_DISPLAY_WROVER4 + default 15 if LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + default 5 if LV_PREDEFINED_PINS_TKOALA + default 26 if LV_PREDEFINED_DISPLAY_ATAG + default 5 if LV_PREDEFINED_DISPLAY_TTGO + default 12 if LV_PREDEFINED_DISPLAY_TTGO_CAMERA_PLUS + default 15 + + help + Configure the display CS pin here. + + config LV_DISPLAY_USE_DC + bool "Use DC signal to control the display" if LV_TFT_DISPLAY_PROTOCOL_SPI + default y if !LV_PREDEFINED_PINS_TKOALA + help + Enable the DC signal to control the display. You can disable + it when the display does not need DC signal to be controlled. + + config LV_DISP_PIN_DC + int "GPIO for DC (Data / Command)" if LV_TFT_DISPLAY_PROTOCOL_SPI + range 0 39 + depends on LV_DISPLAY_USE_DC + default 19 if LV_PREDEFINED_PINS_38V1 + default 17 if LV_PREDEFINED_PINS_38V4 + default 27 if LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_M5STICK + default 23 if LV_PREDEFINED_DISPLAY_M5STICKC + default 21 if LV_PREDEFINED_DISPLAY_WROVER4 + default 33 if LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + default 0 if LV_PREDEFINED_PINS_TKOALA + default 17 if LV_PREDEFINED_DISPLAY_ATAG + default 16 if LV_PREDEFINED_DISPLAY_TTGO + default 15 if LV_PREDEFINED_DISPLAY_TTGO_CAMERA_PLUS + default 2 + + help + Configure the display DC pin here. + + config LV_DISP_PIN_RST + int "GPIO for Reset" if LV_TFT_DISPLAY_PROTOCOL_SPI + range 0 39 + default 18 if LV_PREDEFINED_PINS_38V1 + default 25 if LV_PREDEFINED_PINS_38V4 + default 33 if LV_PREDEFINED_DISPLAY_M5STACK || LV_PREDEFINED_DISPLAY_M5STICK + default 18 if LV_PREDEFINED_DISPLAY_M5STICKC + default 18 if LV_PREDEFINED_DISPLAY_WROVER4 + default 4 if LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + default 4 if LV_PREDEFINED_PINS_TKOALA + default 16 if LV_PREDEFINED_DISPLAY_ATAG + default 23 if LV_PREDEFINED_DISPLAY_TTGO + default 33 if LV_PREDEFINED_DISPLAY_TTGO_CAMERA_PLUS + default 4 + + help + Configure the display Reset pin here. + + config LV_DISP_PIN_BUSY + int "GPIO for Busy" if LV_TFT_DISPLAY_CONTROLLER_IL3820 || LV_TFT_DISPLAY_CONTROLLER_JD79653A || LV_TFT_DISPLAY_CONTROLLER_UC8151D + range 0 39 + default 35 if LV_TFT_DISPLAY_CONTROLLER_IL3820 || LV_TFT_DISPLAY_CONTROLLER_JD79653A || LV_TFT_DISPLAY_CONTROLLER_UC8151D + default 35 + + help + Configure the display Busy pin here. + + config LV_ENABLE_BACKLIGHT_CONTROL + bool "Enable control of the display backlight by using an GPIO." if \ + ( LV_PREDEFINED_DISPLAY_NONE && ! ( LV_TFT_DISPLAY_CONTROLLER_SH1107 || LV_TFT_DISPLAY_CONTROLLER_SSD1306 ) ) \ + || LV_PREDEFINED_DISPLAY_RPI_MPI3501 + default y if LV_PREDEFINED_DISPLAY_M5STACK + default y if LV_PREDEFINED_DISPLAY_WROVER4 + default y if LV_PREDEFINED_DISPLAY_ERTFT0356 + default y if LV_PREDEFINED_DISPLAY_TTGO + default y if LV_PREDEFINED_DISPLAY_TTGO_CAMERA_PLUS + help + Enable controlling the display backlight using an GPIO + + config LV_BACKLIGHT_ACTIVE_LVL + bool "Is backlight turn on with a HIGH (1) logic level?" + depends on LV_ENABLE_BACKLIGHT_CONTROL + default y if LV_PREDEFINED_DISPLAY_M5STACK + default y if LV_PREDEFINED_DISPLAY_ERTFT0356 + default y if LV_PREDEFINED_DISPLAY_TTGO + default y if LV_PREDEFINED_DISPLAY_TTGO_CAMERA_PLUS + help + Some backlights are turned on with a high signal, others held low. + If enabled, a value of 1 will be sent to the display to enable the backlight, + otherwise a 0 will be expected to enable it. + + config LV_DISP_PIN_BCKL + int "GPIO for Backlight Control" + depends on LV_ENABLE_BACKLIGHT_CONTROL + range 0 39 + default 23 if LV_PREDEFINED_PINS_38V1 + default 26 if LV_PREDEFINED_PINS_38V4 + default 32 if LV_PREDEFINED_DISPLAY_M5STACK + default 5 if LV_PREDEFINED_DISPLAY_WROVER4 + default 2 if LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + default 27 if LV_PREDEFINED_DISPLAY_ERTFT0356 + default 0 if LV_PREDEFINED_PINS_TKOALA + default 4 if LV_PREDEFINED_DISPLAY_TTGO + default 2 if LV_PREDEFINED_DISPLAY_TTGO_CAMERA_PLUS + default 27 + + help + Configure the display BCLK (LED) pin here. + + config LV_DISP_PIN_SDA + int "GPIO for I2C SDA" if LV_TFT_DISPLAY_PROTOCOL_I2C + range 0 39 + default 5 if LV_PREDEFINED_DISPLAY_WEMOS_LOLIN + default 5 + + help + Configure the I2C SDA pin here. + + config LV_DISP_PIN_SCL + int "GPIO for I2C SCL" if LV_TFT_DISPLAY_PROTOCOL_I2C + range 0 39 + default 4 if LV_PREDEFINED_DISPLAY_WEMOS_LOLIN + default 4 + + help + Configure the I2C SCL pin here. + + endmenu + +endmenu diff --git a/lvgl_tft/component.mk b/lvgl_tft/component.mk new file mode 100644 index 0000000..9c75766 --- /dev/null +++ b/lvgl_tft/component.mk @@ -0,0 +1,4 @@ +# TFT drivers + +COMPONENT_SRCDIRS := . +COMPONENT_ADD_INCLUDEDIRS := . diff --git a/lvgl_tft/disp_driver.c b/lvgl_tft/disp_driver.c new file mode 100644 index 0000000..56b426d --- /dev/null +++ b/lvgl_tft/disp_driver.c @@ -0,0 +1,107 @@ +/** + * @file disp_driver.c + */ + +#include "disp_driver.h" +#include "disp_spi.h" + +void disp_driver_init(void) +{ +#if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9341 + ili9341_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9481 + ili9481_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9488 + ili9488_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7789 + st7789_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7735S + st7735s_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_HX8357 + hx8357_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9486 + ili9486_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SH1107 + sh1107_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306 + ssd1306_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X + FT81x_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 + il3820_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875 + ra8875_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01 + GC9A01_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A + jd79653a_init(); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D + uc8151d_init(); +#endif +} + +void disp_driver_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) +{ +#if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9341 + ili9341_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9481 + ili9481_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9488 + ili9488_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7789 + st7789_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7735S + st7735s_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_HX8357 + hx8357_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9486 + ili9486_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SH1107 + sh1107_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306 + ssd1306_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X + FT81x_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 + il3820_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875 + ra8875_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01 + GC9A01_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A + jd79653a_lv_fb_flush(drv, area, color_map); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D + uc8151d_lv_fb_flush(drv, area, color_map); +#endif +} + +void disp_driver_rounder(lv_disp_drv_t * disp_drv, lv_area_t * area) +{ +#if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306 + ssd1306_rounder(disp_drv, area); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SH1107 + sh1107_rounder(disp_drv, area); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 + il3820_rounder(disp_drv, area); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A + jd79653a_lv_rounder_cb(disp_drv, area); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D + uc8151d_lv_rounder_cb(disp_drv, area); +#endif +} + +void disp_driver_set_px(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa) +{ +#if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306 + ssd1306_set_px_cb(disp_drv, buf, buf_w, x, y, color, opa); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SH1107 + sh1107_set_px_cb(disp_drv, buf, buf_w, x, y, color, opa); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 + il3820_set_px_cb(disp_drv, buf, buf_w, x, y, color, opa); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A + jd79653a_lv_set_fb_cb(disp_drv, buf, buf_w, x, y, color, opa); +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D + uc8151d_lv_set_fb_cb(disp_drv, buf, buf_w, x, y, color, opa); +#endif +} diff --git a/lvgl_tft/disp_driver.h b/lvgl_tft/disp_driver.h new file mode 100644 index 0000000..1ee33c7 --- /dev/null +++ b/lvgl_tft/disp_driver.h @@ -0,0 +1,86 @@ +/** + * @file disp_driver.h + */ + +#ifndef DISP_DRIVER_H +#define DISP_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9341 +#include "ili9341.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9481 +#include "ili9481.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9488 +#include "ili9488.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7789 +#include "st7789.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7735S +#include "st7735s.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_HX8357 +#include "hx8357.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ILI9486 +#include "ili9486.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SH1107 +#include "sh1107.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306 +#include "ssd1306.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X +#include "FT81x.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 +#include "il3820.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875 +#include "ra8875.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_GC9A01 +#include "GC9A01.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A +#include "jd79653a.h" +#elif defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D +#include "uc8151d.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/* Initialize display */ +void disp_driver_init(void); + +/* Display flush callback */ +void disp_driver_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); + +/* Display rounder callback, used with monochrome dispays */ +void disp_driver_rounder(lv_disp_drv_t * disp_drv, lv_area_t * area); + +/* Display set_px callback, used with monochrome dispays */ +void disp_driver_set_px(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*DISP_DRIVER_H*/ diff --git a/lvgl_tft/disp_spi.c b/lvgl_tft/disp_spi.c new file mode 100644 index 0000000..0e4e548 --- /dev/null +++ b/lvgl_tft/disp_spi.c @@ -0,0 +1,320 @@ +/** + * @file disp_spi.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "esp_system.h" +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "esp_log.h" + +#define TAG "disp_spi" + +#include + +#include +#include +#include + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#include "disp_spi.h" +#include "disp_driver.h" + +#include "../lvgl_helpers.h" +#include "../lvgl_spi_conf.h" + +/****************************************************************************** + * Notes about DMA spi_transaction_ext_t structure pooling + * + * An xQueue is used to hold a pool of reusable SPI spi_transaction_ext_t + * structures that get used for all DMA SPI transactions. While an xQueue may + * seem like overkill it is an already built-in RTOS feature that comes at + * little cost. xQueues are also ISR safe if it ever becomes necessary to + * access the pool in the ISR callback. + * + * When a DMA request is sent, a transaction structure is removed from the + * pool, filled out, and passed off to the esp32 SPI driver. Later, when + * servicing pending SPI transaction results, the transaction structure is + * recycled back into the pool for later reuse. This matches the DMA SPI + * transaction life cycle requirements of the esp32 SPI driver. + * + * When polling or synchronously sending SPI requests, and as required by the + * esp32 SPI driver, all pending DMA transactions are first serviced. Then the + * polling SPI request takes place. + * + * When sending an asynchronous DMA SPI request, if the pool is empty, some + * small percentage of pending transactions are first serviced before sending + * any new DMA SPI transactions. Not too many and not too few as this balance + * controls DMA transaction latency. + * + * It is therefore not the design that all pending transactions must be + * serviced and placed back into the pool with DMA SPI requests - that + * will happen eventually. The pool just needs to contain enough to float some + * number of in-flight SPI requests to speed up the overall DMA SPI data rate + * and reduce transaction latency. If however a display driver uses some + * polling SPI requests or calls disp_wait_for_pending_transactions() directly, + * the pool will reach the full state more often and speed up DMA queuing. + * + *****************************************************************************/ + +/********************* + * DEFINES + *********************/ +#define SPI_TRANSACTION_POOL_SIZE 50 /* maximum number of DMA transactions simultaneously in-flight */ + +/* DMA Transactions to reserve before queueing additional DMA transactions. A 1/10th seems to be a good balance. Too many (or all) and it will increase latency. */ +#define SPI_TRANSACTION_POOL_RESERVE_PERCENTAGE 10 +#if SPI_TRANSACTION_POOL_SIZE >= SPI_TRANSACTION_POOL_RESERVE_PERCENTAGE +#define SPI_TRANSACTION_POOL_RESERVE (SPI_TRANSACTION_POOL_SIZE / SPI_TRANSACTION_POOL_RESERVE_PERCENTAGE) +#else +#define SPI_TRANSACTION_POOL_RESERVE 1 /* defines minimum size */ +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void IRAM_ATTR spi_ready (spi_transaction_t *trans); + +/********************** + * STATIC VARIABLES + **********************/ +static spi_host_device_t spi_host; +static spi_device_handle_t spi; +static QueueHandle_t TransactionPool = NULL; +static transaction_cb_t chained_post_cb; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void disp_spi_add_device_config(spi_host_device_t host, spi_device_interface_config_t *devcfg) +{ + spi_host=host; + chained_post_cb=devcfg->post_cb; + devcfg->post_cb=spi_ready; + esp_err_t ret=spi_bus_add_device(host, devcfg, &spi); + assert(ret==ESP_OK); +} + +void disp_spi_add_device(spi_host_device_t host) +{ + disp_spi_add_device_with_speed(host, SPI_TFT_CLOCK_SPEED_HZ); +} + +void disp_spi_add_device_with_speed(spi_host_device_t host, int clock_speed_hz) +{ + ESP_LOGI(TAG, "Adding SPI device"); + ESP_LOGI(TAG, "Clock speed: %dHz, mode: %d, CS pin: %d", + clock_speed_hz, SPI_TFT_SPI_MODE, DISP_SPI_CS); + + spi_device_interface_config_t devcfg={ + .clock_speed_hz = clock_speed_hz, + .mode = SPI_TFT_SPI_MODE, + .spics_io_num=DISP_SPI_CS, // CS pin + .input_delay_ns=DISP_SPI_INPUT_DELAY_NS, + .queue_size=SPI_TRANSACTION_POOL_SIZE, + .pre_cb=NULL, + .post_cb=NULL, +#if defined(DISP_SPI_HALF_DUPLEX) + .flags = SPI_DEVICE_NO_DUMMY | SPI_DEVICE_HALFDUPLEX, /* dummy bits should be explicitly handled via DISP_SPI_VARIABLE_DUMMY as needed */ +#else + #if defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X) + .flags = 0, + #elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875) + .flags = SPI_DEVICE_NO_DUMMY, + #endif +#endif + }; + + disp_spi_add_device_config(host, &devcfg); + + /* create the transaction pool and fill it with ptrs to spi_transaction_ext_t to reuse */ + if(TransactionPool == NULL) { + TransactionPool = xQueueCreate(SPI_TRANSACTION_POOL_SIZE, sizeof(spi_transaction_ext_t*)); + assert(TransactionPool != NULL); + for (size_t i = 0; i < SPI_TRANSACTION_POOL_SIZE; i++) + { + spi_transaction_ext_t* pTransaction = (spi_transaction_ext_t*)heap_caps_malloc(sizeof(spi_transaction_ext_t), MALLOC_CAP_DMA); + assert(pTransaction != NULL); + memset(pTransaction, 0, sizeof(spi_transaction_ext_t)); + xQueueSend(TransactionPool, &pTransaction, portMAX_DELAY); + } + } +} + +void disp_spi_change_device_speed(int clock_speed_hz) +{ + if (clock_speed_hz <= 0) { + clock_speed_hz = SPI_TFT_CLOCK_SPEED_HZ; + } + ESP_LOGI(TAG, "Changing SPI device clock speed: %d", clock_speed_hz); + disp_spi_remove_device(); + disp_spi_add_device_with_speed(spi_host, clock_speed_hz); +} + +void disp_spi_remove_device() +{ + /* Wait for previous pending transaction results */ + disp_wait_for_pending_transactions(); + + esp_err_t ret=spi_bus_remove_device(spi); + assert(ret==ESP_OK); +} + +void disp_spi_transaction(const uint8_t *data, size_t length, + disp_spi_send_flag_t flags, uint8_t *out, + uint64_t addr, uint8_t dummy_bits) +{ + if (0 == length) { + return; + } + + spi_transaction_ext_t t = {0}; + + /* transaction length is in bits */ + t.base.length = length * 8; + + if (length <= 4 && data != NULL) { + t.base.flags = SPI_TRANS_USE_TXDATA; + memcpy(t.base.tx_data, data, length); + } else { + t.base.tx_buffer = data; + } + + if (flags & DISP_SPI_RECEIVE) { + assert(out != NULL && (flags & (DISP_SPI_SEND_POLLING | DISP_SPI_SEND_SYNCHRONOUS))); + t.base.rx_buffer = out; + +#if defined(DISP_SPI_HALF_DUPLEX) + t.base.rxlength = t.base.length; + t.base.length = 0; /* no MOSI phase in half-duplex reads */ +#else + t.base.rxlength = 0; /* in full-duplex mode, zero means same as tx length */ +#endif + } + + if (flags & DISP_SPI_ADDRESS_8) { + t.address_bits = 8; + } else if (flags & DISP_SPI_ADDRESS_16) { + t.address_bits = 16; + } else if (flags & DISP_SPI_ADDRESS_24) { + t.address_bits = 24; + } else if (flags & DISP_SPI_ADDRESS_32) { + t.address_bits = 32; + } + if (t.address_bits) { + t.base.addr = addr; + t.base.flags |= SPI_TRANS_VARIABLE_ADDR; + } + +#if defined(DISP_SPI_HALF_DUPLEX) + if (flags & DISP_SPI_MODE_DIO) { + t.base.flags |= SPI_TRANS_MODE_DIO; + } else if (flags & DISP_SPI_MODE_QIO) { + t.base.flags |= SPI_TRANS_MODE_QIO; + } + + if (flags & DISP_SPI_MODE_DIOQIO_ADDR) { + t.base.flags |= SPI_TRANS_MODE_DIOQIO_ADDR; + } + + if ((flags & DISP_SPI_VARIABLE_DUMMY) && dummy_bits) { + t.dummy_bits = dummy_bits; + t.base.flags |= SPI_TRANS_VARIABLE_DUMMY; + } +#endif + + /* Save flags for pre/post transaction processing */ + t.base.user = (void *) flags; + + /* Poll/Complete/Queue transaction */ + if (flags & DISP_SPI_SEND_POLLING) { + disp_wait_for_pending_transactions(); /* before polling, all previous pending transactions need to be serviced */ + spi_device_polling_transmit(spi, (spi_transaction_t *) &t); + } else if (flags & DISP_SPI_SEND_SYNCHRONOUS) { + disp_wait_for_pending_transactions(); /* before synchronous queueing, all previous pending transactions need to be serviced */ + spi_device_transmit(spi, (spi_transaction_t *) &t); + } else { + + /* if necessary, ensure we can queue new transactions by servicing some previous transactions */ + if(uxQueueMessagesWaiting(TransactionPool) == 0) { + spi_transaction_t *presult; + while(uxQueueMessagesWaiting(TransactionPool) < SPI_TRANSACTION_POOL_RESERVE) { + if (spi_device_get_trans_result(spi, &presult, 1) == ESP_OK) { + xQueueSend(TransactionPool, &presult, portMAX_DELAY); /* back to the pool to be reused */ + } + } + } + + spi_transaction_ext_t *pTransaction = NULL; + xQueueReceive(TransactionPool, &pTransaction, portMAX_DELAY); + memcpy(pTransaction, &t, sizeof(t)); + if (spi_device_queue_trans(spi, (spi_transaction_t *) pTransaction, portMAX_DELAY) != ESP_OK) { + xQueueSend(TransactionPool, &pTransaction, portMAX_DELAY); /* send failed transaction back to the pool to be reused */ + } + } +} + + +void disp_wait_for_pending_transactions(void) +{ + spi_transaction_t *presult; + + while(uxQueueMessagesWaiting(TransactionPool) < SPI_TRANSACTION_POOL_SIZE) { /* service until the transaction reuse pool is full again */ + if (spi_device_get_trans_result(spi, &presult, 1) == ESP_OK) { + xQueueSend(TransactionPool, &presult, portMAX_DELAY); + } + } +} + +void disp_spi_acquire(void) +{ + esp_err_t ret = spi_device_acquire_bus(spi, portMAX_DELAY); + assert(ret == ESP_OK); +} + +void disp_spi_release(void) +{ + spi_device_release_bus(spi); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void IRAM_ATTR spi_ready(spi_transaction_t *trans) +{ + disp_spi_send_flag_t flags = (disp_spi_send_flag_t) trans->user; + + if (flags & DISP_SPI_SIGNAL_FLUSH) { + lv_disp_t * disp = NULL; + +#if (LVGL_VERSION_MAJOR >= 7) + disp = _lv_refr_get_disp_refreshing(); +#else /* Before v7 */ + disp = lv_refr_get_disp_refreshing(); +#endif + + lv_disp_flush_ready(&disp->driver); + } + + if (chained_post_cb) { + chained_post_cb(trans); + } +} + diff --git a/lvgl_tft/disp_spi.h b/lvgl_tft/disp_spi.h new file mode 100644 index 0000000..e2afb31 --- /dev/null +++ b/lvgl_tft/disp_spi.h @@ -0,0 +1,81 @@ +/** + * @file disp_spi.h + * + */ + +#ifndef DISP_SPI_H +#define DISP_SPI_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#include +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef enum _disp_spi_send_flag_t { + DISP_SPI_SEND_QUEUED = 0x00000000, + DISP_SPI_SEND_POLLING = 0x00000001, + DISP_SPI_SEND_SYNCHRONOUS = 0x00000002, + DISP_SPI_SIGNAL_FLUSH = 0x00000004, + DISP_SPI_RECEIVE = 0x00000008, + DISP_SPI_CMD_8 = 0x00000010, /* Reserved */ + DISP_SPI_CMD_16 = 0x00000020, /* Reserved */ + DISP_SPI_ADDRESS_8 = 0x00000040, + DISP_SPI_ADDRESS_16 = 0x00000080, + DISP_SPI_ADDRESS_24 = 0x00000100, + DISP_SPI_ADDRESS_32 = 0x00000200, + DISP_SPI_MODE_DIO = 0x00000400, + DISP_SPI_MODE_QIO = 0x00000800, + DISP_SPI_MODE_DIOQIO_ADDR = 0x00001000, + DISP_SPI_VARIABLE_DUMMY = 0x00002000, +} disp_spi_send_flag_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void disp_spi_add_device(spi_host_device_t host); +void disp_spi_add_device_config(spi_host_device_t host, spi_device_interface_config_t *devcfg); +void disp_spi_add_device_with_speed(spi_host_device_t host, int clock_speed_hz); +void disp_spi_change_device_speed(int clock_speed_hz); +void disp_spi_remove_device(); + +/* Important! + All buffers should also be 32-bit aligned and DMA capable to prevent extra allocations and copying. + When DMA reading (even in polling mode) the ESP32 always read in 4-byte chunks even if less is requested. + Extra space will be zero filled. Always ensure the out buffer is large enough to hold at least 4 bytes! +*/ +void disp_spi_transaction(const uint8_t *data, size_t length, + disp_spi_send_flag_t flags, uint8_t *out, uint64_t addr, uint8_t dummy_bits); + +void disp_wait_for_pending_transactions(void); +void disp_spi_acquire(void); +void disp_spi_release(void); + +static inline void disp_spi_send_data(uint8_t *data, size_t length) { + disp_spi_transaction(data, length, DISP_SPI_SEND_POLLING, NULL, 0, 0); +} + +static inline void disp_spi_send_colors(uint8_t *data, size_t length) { + disp_spi_transaction(data, length, + DISP_SPI_SEND_QUEUED | DISP_SPI_SIGNAL_FLUSH, + NULL, 0, 0); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*DISP_SPI_H*/ diff --git a/lvgl_tft/hx8357.c b/lvgl_tft/hx8357.c new file mode 100644 index 0000000..af899e1 --- /dev/null +++ b/lvgl_tft/hx8357.c @@ -0,0 +1,310 @@ +/** + * @file HX8357.c + * + * Roughly based on the Adafruit_HX8357_Library + * + * This library should work with: + * Adafruit 3.5" TFT 320x480 + Touchscreen Breakout + * http://www.adafruit.com/products/2050 + * + * Adafruit TFT FeatherWing - 3.5" 480x320 Touchscreen for Feathers + * https://www.adafruit.com/product/3651 + * + */ + +/********************* + * INCLUDES + *********************/ +#include "hx8357.h" +#include "disp_spi.h" +#include "driver/gpio.h" +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +/********************* + * DEFINES + *********************/ +#define TAG "HX8357" + +#define MADCTL_MY 0x80 ///< Bottom to top +#define MADCTL_MX 0x40 ///< Right to left +#define MADCTL_MV 0x20 ///< Reverse Mode +#define MADCTL_ML 0x10 ///< LCD refresh Bottom to top +#define MADCTL_RGB 0x00 ///< Red-Green-Blue pixel order +#define MADCTL_BGR 0x08 ///< Blue-Green-Red pixel order +#define MADCTL_MH 0x04 ///< LCD refresh right to left + +/********************** + * TYPEDEFS + **********************/ + +/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} lcd_init_cmd_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void hx8357_send_cmd(uint8_t cmd); +static void hx8357_send_data(void * data, uint16_t length); +static void hx8357_send_color(void * data, uint16_t length); + + +/********************** + * INITIALIZATION ARRAYS + **********************/ +// Taken from the Adafruit driver +static const uint8_t + initb[] = { + HX8357B_SETPOWER, 3, + 0x44, 0x41, 0x06, + HX8357B_SETVCOM, 2, + 0x40, 0x10, + HX8357B_SETPWRNORMAL, 2, + 0x05, 0x12, + HX8357B_SET_PANEL_DRIVING, 5, + 0x14, 0x3b, 0x00, 0x02, 0x11, + HX8357B_SETDISPLAYFRAME, 1, + 0x0c, // 6.8mhz + HX8357B_SETPANELRELATED, 1, + 0x01, // BGR + 0xEA, 3, // seq_undefined1, 3 args + 0x03, 0x00, 0x00, + 0xEB, 4, // undef2, 4 args + 0x40, 0x54, 0x26, 0xdb, + HX8357B_SETGAMMA, 12, + 0x00, 0x15, 0x00, 0x22, 0x00, 0x08, 0x77, 0x26, 0x66, 0x22, 0x04, 0x00, + HX8357_MADCTL, 1, + 0xC0, + HX8357_COLMOD, 1, + 0x55, + HX8357_PASET, 4, + 0x00, 0x00, 0x01, 0xDF, + HX8357_CASET, 4, + 0x00, 0x00, 0x01, 0x3F, + HX8357B_SETDISPMODE, 1, + 0x00, // CPU (DBI) and internal oscillation ?? + HX8357_SLPOUT, 0x80 + 120/5, // Exit sleep, then delay 120 ms + HX8357_DISPON, 0x80 + 10/5, // Main screen turn on, delay 10 ms + 0 // END OF COMMAND LIST + }, initd[] = { + HX8357_SWRESET, 0x80 + 100/5, // Soft reset, then delay 10 ms + HX8357D_SETC, 3, + 0xFF, 0x83, 0x57, + 0xFF, 0x80 + 500/5, // No command, just delay 300 ms + HX8357_SETRGB, 4, + 0x80, 0x00, 0x06, 0x06, // 0x80 enables SDO pin (0x00 disables) + HX8357D_SETCOM, 1, + 0x25, // -1.52V + HX8357_SETOSC, 1, + 0x68, // Normal mode 70Hz, Idle mode 55 Hz + HX8357_SETPANEL, 1, + 0x05, // BGR, Gate direction swapped + HX8357_SETPWR1, 6, + 0x00, // Not deep standby + 0x15, // BT + 0x1C, // VSPR + 0x1C, // VSNR + 0x83, // AP + 0xAA, // FS + HX8357D_SETSTBA, 6, + 0x50, // OPON normal + 0x50, // OPON idle + 0x01, // STBA + 0x3C, // STBA + 0x1E, // STBA + 0x08, // GEN + HX8357D_SETCYC, 7, + 0x02, // NW 0x02 + 0x40, // RTN + 0x00, // DIV + 0x2A, // DUM + 0x2A, // DUM + 0x0D, // GDON + 0x78, // GDOFF + HX8357D_SETGAMMA, 34, + 0x02, 0x0A, 0x11, 0x1d, 0x23, 0x35, 0x41, 0x4b, 0x4b, + 0x42, 0x3A, 0x27, 0x1B, 0x08, 0x09, 0x03, 0x02, 0x0A, + 0x11, 0x1d, 0x23, 0x35, 0x41, 0x4b, 0x4b, 0x42, 0x3A, + 0x27, 0x1B, 0x08, 0x09, 0x03, 0x00, 0x01, + HX8357_COLMOD, 1, + 0x55, // 16 bit + HX8357_MADCTL, 1, + 0xC0, + HX8357_TEON, 1, + 0x00, // TW off + HX8357_TEARLINE, 2, + 0x00, 0x02, + HX8357_SLPOUT, 0x80 + 150/5, // Exit Sleep, then delay 150 ms + HX8357_DISPON, 0x80 + 50/5, // Main screen turn on, delay 50 ms + 0, // END OF COMMAND LIST + }; + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +static uint8_t displayType = HX8357D; + +void hx8357_init(void) +{ + //Initialize non-SPI GPIOs + gpio_pad_select_gpio(HX8357_DC); + gpio_set_direction(HX8357_DC, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(HX8357_RST); + gpio_set_direction(HX8357_RST, GPIO_MODE_OUTPUT); + +#if HX8357_ENABLE_BACKLIGHT_CONTROL + gpio_pad_select_gpio(HX8357_BCKL); + gpio_set_direction(HX8357_BCKL, GPIO_MODE_OUTPUT); +#endif + + //Reset the display + gpio_set_level(HX8357_RST, 0); + vTaskDelay(10 / portTICK_RATE_MS); + gpio_set_level(HX8357_RST, 1); + vTaskDelay(120 / portTICK_RATE_MS); + + ESP_LOGI(TAG, "Initialization."); + + //Send all the commands + const uint8_t *addr = (displayType == HX8357B) ? initb : initd; + uint8_t cmd, x, numArgs; + while((cmd = *addr++) > 0) { // '0' command ends list + x = *addr++; + numArgs = x & 0x7F; + if (cmd != 0xFF) { // '255' is ignored + if (x & 0x80) { // If high bit set, numArgs is a delay time + hx8357_send_cmd(cmd); + } else { + hx8357_send_cmd(cmd); + hx8357_send_data((void *) addr, numArgs); + addr += numArgs; + } + } + if (x & 0x80) { // If high bit set... + vTaskDelay(numArgs * 5 / portTICK_RATE_MS); // numArgs is actually a delay time (5ms units) + } + } + + hx8357_set_rotation(1); + +#if HX8357_INVERT_DISPLAY + hx8357_send_cmd(HX8357_INVON);; +#endif + + hx8357_enable_backlight(true); +} + + +void hx8357_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) +{ + uint32_t size = lv_area_get_width(area) * lv_area_get_height(area); + + /* Column addresses */ + uint8_t xb[] = { + (uint8_t) (area->x1 >> 8) & 0xFF, + (uint8_t) (area->x1) & 0xFF, + (uint8_t) (area->x2 >> 8) & 0xFF, + (uint8_t) (area->x2) & 0xFF, + }; + + /* Page addresses */ + uint8_t yb[] = { + (uint8_t) (area->y1 >> 8) & 0xFF, + (uint8_t) (area->y1) & 0xFF, + (uint8_t) (area->y2 >> 8) & 0xFF, + (uint8_t) (area->y2) & 0xFF, + }; + + /*Column addresses*/ + hx8357_send_cmd(HX8357_CASET); + hx8357_send_data(xb, 4); + + /*Page addresses*/ + hx8357_send_cmd(HX8357_PASET); + hx8357_send_data(yb, 4); + + /*Memory write*/ + hx8357_send_cmd(HX8357_RAMWR); + hx8357_send_color((void*)color_map, size * 2); +} + +void hx8357_enable_backlight(bool backlight) +{ +#if HX8357_ENABLE_BACKLIGHT_CONTROL + ESP_LOGD(TAG, "%s backlight.\n", backlight ? "Enabling" : "Disabling"); + uint32_t tmp = 0; + +#if (HX8357_BCKL_ACTIVE_LVL==1) + tmp = backlight ? 1 : 0; +#else + tmp = backlight ? 0 : 1; +#endif + + gpio_set_level(HX8357_BCKL, tmp); +#endif +} + + +void hx8357_set_rotation(uint8_t r) +{ + r = r & 3; // can't be higher than 3 + + switch(r) { + case 0: + r = MADCTL_MX | MADCTL_MY | MADCTL_RGB; + break; + case 1: + r = MADCTL_MV | MADCTL_MY | MADCTL_RGB; + break; + case 2: + r = MADCTL_RGB; + break; + case 3: + r = MADCTL_MX | MADCTL_MV | MADCTL_RGB; + break; + } + + hx8357_send_cmd(HX8357_MADCTL); + hx8357_send_data(&r, 1); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +static void hx8357_send_cmd(uint8_t cmd) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(HX8357_DC, 0); /*Command mode*/ + disp_spi_send_data(&cmd, 1); +} + + +static void hx8357_send_data(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(HX8357_DC, 1); /*Data mode*/ + disp_spi_send_data(data, length); +} + + +static void hx8357_send_color(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(HX8357_DC, 1); /*Data mode*/ + disp_spi_send_colors(data, length); +} diff --git a/lvgl_tft/hx8357.h b/lvgl_tft/hx8357.h new file mode 100644 index 0000000..dc51532 --- /dev/null +++ b/lvgl_tft/hx8357.h @@ -0,0 +1,151 @@ +/** + * @file HX8357.h + * + * Roughly based on the Adafruit_HX8357_Library + * + * This library should work with: + * Adafruit 3.5" TFT 320x480 + Touchscreen Breakout + * http://www.adafruit.com/products/2050 + * + * Adafruit TFT FeatherWing - 3.5" 480x320 Touchscreen for Feathers + * https://www.adafruit.com/product/3651 + * + */ + +#ifndef HX8357_H +#define HX8357_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#include + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "../lvgl_helpers.h" + + /********************* + * DEFINES + *********************/ +#define HX8357_DC CONFIG_LV_DISP_PIN_DC +#define HX8357_RST CONFIG_LV_DISP_PIN_RST +#define HX8357_BCKL CONFIG_LV_DISP_PIN_BCKL + +#define HX8357_ENABLE_BACKLIGHT_CONTROL CONFIG_LV_ENABLE_BACKLIGHT_CONTROL + +#if CONFIG_LV_BACKLIGHT_ACTIVE_LVL + #define HX8357_BCKL_ACTIVE_LVL 1 +#else + #define HX8357_BCKL_ACTIVE_LVL 0 +#endif + +// if text/images are backwards, try setting this to 1 +#define HX8357_INVERT_DISPLAY CONFIG_LV_INVERT_DISPLAY + + +/******************* + * HX8357B/D REGS +*********************/ +#define HX8357D 0xD ///< Our internal const for D type +#define HX8357B 0xB ///< Our internal const for B type + +#define HX8357_TFTWIDTH 320 ///< 320 pixels wide +#define HX8357_TFTHEIGHT 480 ///< 480 pixels tall + +#define HX8357_NOP 0x00 ///< No op +#define HX8357_SWRESET 0x01 ///< software reset +#define HX8357_RDDID 0x04 ///< Read ID +#define HX8357_RDDST 0x09 ///< (unknown) + +#define HX8357_RDPOWMODE 0x0A ///< Read power mode Read power mode +#define HX8357_RDMADCTL 0x0B ///< Read MADCTL +#define HX8357_RDCOLMOD 0x0C ///< Column entry mode +#define HX8357_RDDIM 0x0D ///< Read display image mode +#define HX8357_RDDSDR 0x0F ///< Read dosplay signal mode + +#define HX8357_SLPIN 0x10 ///< Enter sleep mode +#define HX8357_SLPOUT 0x11 ///< Exit sleep mode +#define HX8357B_PTLON 0x12 ///< Partial mode on +#define HX8357B_NORON 0x13 ///< Normal mode + +#define HX8357_INVOFF 0x20 ///< Turn off invert +#define HX8357_INVON 0x21 ///< Turn on invert +#define HX8357_DISPOFF 0x28 ///< Display on +#define HX8357_DISPON 0x29 ///< Display off + +#define HX8357_CASET 0x2A ///< Column addr set +#define HX8357_PASET 0x2B ///< Page addr set +#define HX8357_RAMWR 0x2C ///< Write VRAM +#define HX8357_RAMRD 0x2E ///< Read VRAm + +#define HX8357B_PTLAR 0x30 ///< (unknown) +#define HX8357_TEON 0x35 ///< Tear enable on +#define HX8357_TEARLINE 0x44 ///< (unknown) +#define HX8357_MADCTL 0x36 ///< Memory access control +#define HX8357_COLMOD 0x3A ///< Color mode + +#define HX8357_SETOSC 0xB0 ///< Set oscillator +#define HX8357_SETPWR1 0xB1 ///< Set power control +#define HX8357B_SETDISPLAY 0xB2 ///< Set display mode +#define HX8357_SETRGB 0xB3 ///< Set RGB interface +#define HX8357D_SETCOM 0xB6 ///< Set VCOM voltage + +#define HX8357B_SETDISPMODE 0xB4 ///< Set display mode +#define HX8357D_SETCYC 0xB4 ///< Set display cycle reg +#define HX8357B_SETOTP 0xB7 ///< Set OTP memory +#define HX8357D_SETC 0xB9 ///< Enable extension command + +#define HX8357B_SET_PANEL_DRIVING 0xC0 ///< Set panel drive mode +#define HX8357D_SETSTBA 0xC0 ///< Set source option +#define HX8357B_SETDGC 0xC1 ///< Set DGC settings +#define HX8357B_SETID 0xC3 ///< Set ID +#define HX8357B_SETDDB 0xC4 ///< Set DDB +#define HX8357B_SETDISPLAYFRAME 0xC5 ///< Set display frame +#define HX8357B_GAMMASET 0xC8 ///< Set Gamma correction +#define HX8357B_SETCABC 0xC9 ///< Set CABC +#define HX8357_SETPANEL 0xCC ///< Set Panel + +#define HX8357B_SETPOWER 0xD0 ///< Set power control +#define HX8357B_SETVCOM 0xD1 ///< Set VCOM +#define HX8357B_SETPWRNORMAL 0xD2 ///< Set power normal + +#define HX8357B_RDID1 0xDA ///< Read ID #1 +#define HX8357B_RDID2 0xDB ///< Read ID #2 +#define HX8357B_RDID3 0xDC ///< Read ID #3 +#define HX8357B_RDID4 0xDD ///< Read ID #4 + +#define HX8357D_SETGAMMA 0xE0 ///< Set Gamma + +#define HX8357B_SETGAMMA 0xC8 ///< Set Gamma +#define HX8357B_SETPANELRELATED 0xE9 ///< Set panel related + + /********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void hx8357_init(void); +void hx8357_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); +void hx8357_enable_backlight(bool backlight); +void hx8357_set_rotation(uint8_t r); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*HX8357_H*/ diff --git a/lvgl_tft/il3820.c b/lvgl_tft/il3820.c new file mode 100644 index 0000000..8c1c20c --- /dev/null +++ b/lvgl_tft/il3820.c @@ -0,0 +1,414 @@ +/** +@file il3820.c +@brief Waveshare e-paper 2.9in b/w display +@version 1.0 +@date 2020-05-29 +@author Juergen Kienhoefer + + +@section LICENSE + +MIT License + +Copyright (c) 2020 Juergen Kienhoefer + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +/********************* + * INCLUDES + *********************/ +#include "disp_spi.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "il3820.h" + +/********************* + * DEFINES + *********************/ + #define TAG "IL3820" + +/** + * SSD1673, SSD1608 compatible EPD controller driver. + */ + +#define BIT_SET(a,b) ((a) |= (1U<<(b))) +#define BIT_CLEAR(a,b) ((a) &= ~(1U<<(b))) + +/* Number of pixels? */ +#define IL3820_PIXEL (CONFIG_LV_DISPLAY_WIDTH * CONFIG_LV_DISPLAY_HEIGHT) + +#define EPD_PANEL_NUMOF_COLUMS EPD_PANEL_WIDTH +#define EPD_PANEL_NUMOF_ROWS_PER_PAGE 8 + +/* Are pages the number of bytes to represent the panel width? in bytes */ +#define EPD_PANEL_NUMOF_PAGES (EPD_PANEL_HEIGHT / EPD_PANEL_NUMOF_ROWS_PER_PAGE) + +#define IL3820_PANEL_FIRST_PAGE 0 +#define IL3820_PANEL_LAST_PAGE (EPD_PANEL_NUMOF_PAGES - 1) +#define IL3820_PANEL_FIRST_GATE 0 +#define IL3820_PANEL_LAST_GATE (EPD_PANEL_NUMOF_COLUMS - 1) + +#define IL3820_PIXELS_PER_BYTE 8 + +uint8_t il3820_scan_mode = IL3820_DATA_ENTRY_XIYIY; + +static uint8_t il3820_lut_initial[] = { + 0x50, 0xAA, 0x55, 0xAA, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static uint8_t il3820_lut_default[] = { + 0x10, 0x18, 0x18, 0x08, 0x18, 0x18, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static uint8_t il3820_softstart[] = {0xd7, 0xd6, 0x9d}; +static uint8_t il3820_vcom[] = {0xa8}; +/* 4 dummy lines per gate */ +static uint8_t il3820_dummyline[] = {0x1a}; +/* 2us per line */ +static uint8_t il3820_gatetime[] = {0x08}; +static uint8_t il3820_border[] = {0x03}; + +static bool il3820_partial = false; + +/* Static functions */ +static void il3820_clear_cntlr_mem(uint8_t ram_cmd, bool update); +static void il3820_waitbusy(int wait_ms); +static inline void il3820_command_mode(void); +static inline void il3820_data_mode(void); +static inline void il3820_write_cmd(uint8_t cmd, uint8_t *data, size_t len); +static inline void il3820_send_cmd(uint8_t cmd); +static void il3820_send_data(uint8_t *data, uint16_t length); +static inline void il3820_set_window( uint16_t sx, uint16_t ex, uint16_t ys, uint16_t ye); +static inline void il3820_set_cursor(uint16_t sx, uint16_t ys); +static void il3820_update_display(void); +static void il3820_clear_cntlr_mem(uint8_t ram_cmd, bool update); + +/* Required by LVGL */ +void il3820_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + /* Each byte holds the data of 8 pixels, linelen is the number of bytes + * we need to cover a line of the display. */ + size_t linelen = EPD_PANEL_WIDTH / 8; + + uint8_t *buffer = (uint8_t*) color_map; + uint16_t x_addr_counter = 0; + uint16_t y_addr_counter = 0; + + /* Configure entry mode */ + il3820_write_cmd(IL3820_CMD_ENTRY_MODE, &il3820_scan_mode, 1); + + /* Configure the window based on the coordinates got from LVGL + * It looks like this epaper display controller doesn't support partial update, + * so the window is always the same, the display size. */ + il3820_set_window(0, EPD_PANEL_WIDTH - 1, 0, EPD_PANEL_HEIGHT - 1); + + /* Set the cursor at the beginning of the graphic RAM */ +#if defined (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT) + x_addr_counter = EPD_PANEL_WIDTH - 1; + y_addr_counter = EPD_PANEL_HEIGHT - 1; +#endif + + il3820_set_cursor(x_addr_counter, y_addr_counter); + + il3820_send_cmd(IL3820_CMD_WRITE_RAM); + + /* Write the pixel data to graphic RAM, linelen bytes at the time. */ + for(size_t row = 0; row <= (EPD_PANEL_HEIGHT - 1); row++){ + il3820_send_data(buffer, linelen); + buffer += IL3820_COLUMNS; + } + + il3820_set_window(0, EPD_PANEL_WIDTH - 1, 0, EPD_PANEL_HEIGHT - 1); + + il3820_update_display(); + + /* IMPORTANT!!! + * Inform the graphics library that you are ready with the flushing */ + lv_disp_flush_ready(drv); +} + + +/* Rotate the display by "software" when using PORTRAIT orientation. + * BIT_SET(byte_index, bit_index) clears the bit_index pixel at byte_index of + * the display buffer. + * BIT_CLEAR(byte_index, bit_index) sets the bit_index pixel at the byte_index + * of the display buffer. */ +void il3820_set_px_cb(struct _disp_drv_t * disp_drv, uint8_t* buf, + lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa) +{ + uint16_t byte_index = 0; + uint8_t bit_index = 0; + +#if defined (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT) + byte_index = x + ((y >> 3) * EPD_PANEL_HEIGHT); + bit_index = y & 0x7; + + if (color.full) { + BIT_SET(buf[byte_index], 7 - bit_index); + } else { + uint16_t mirrored_idx = (EPD_PANEL_HEIGHT - x) + ((y >> 3) * EPD_PANEL_HEIGHT); + BIT_CLEAR(buf[mirrored_idx], 7 - bit_index); + } +#elif defined (CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE) + byte_index = y + ((x >> 3) * EPD_PANEL_HEIGHT); + bit_index = x & 0x7; + + if (color.full) { + BIT_SET(buf[byte_index], 7 - bit_index); + } else { + BIT_CLEAR(buf[byte_index], 7 - bit_index); + } +#else +#error "Unsupported orientation used" +#endif +} + +/* Required by LVGL */ +void il3820_rounder(struct _disp_drv_t * disp_drv, lv_area_t *area) { + area->x1 = area->x1 & ~(0x7); + area->x2 = area->x2 | (0x7); +} + +/* main initialization routine */ +void il3820_init(void) +{ + uint8_t tmp[3] = {0}; + + /* Initialize non-SPI GPIOs */ + gpio_pad_select_gpio(IL3820_DC_PIN); + gpio_set_direction(IL3820_DC_PIN, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(IL3820_RST_PIN); + gpio_set_direction(IL3820_RST_PIN, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(IL3820_BUSY_PIN); + gpio_set_direction(IL3820_BUSY_PIN, GPIO_MODE_INPUT); + + /* Harware reset */ + gpio_set_level( IL3820_RST_PIN, 0); + vTaskDelay(IL3820_RESET_DELAY / portTICK_RATE_MS); + gpio_set_level( IL3820_RST_PIN, 1); + vTaskDelay(IL3820_RESET_DELAY / portTICK_RATE_MS); + + /* Software reset */ + il3820_write_cmd(IL3820_CMD_SW_RESET, NULL, 0); + + /* Busy wait for the BUSY signal to go low */ + il3820_waitbusy(IL3820_WAIT); + + /**/ + tmp[0] = ( EPD_PANEL_HEIGHT - 1) & 0xFF; + tmp[1] = ( EPD_PANEL_HEIGHT >> 8 ); + tmp[2] = 0; // GD = 0; SM = 0; TB = 0; + il3820_write_cmd(IL3820_CMD_GDO_CTRL, tmp, 3); + + /**/ + il3820_write_cmd(IL3820_CMD_SOFTSTART, il3820_softstart, sizeof(il3820_softstart)); + + /* Write VCOM register */ + il3820_write_cmd(IL3820_CMD_VCOM_VOLTAGE, il3820_vcom, 1); + /* Set dummy line period (in term of TGate) */ + il3820_write_cmd(IL3820_CMD_DUMMY_LINE, il3820_dummyline, 1); + /* Set gate line width (TGate) in us */ + il3820_write_cmd(IL3820_CMD_GATE_LINE_WIDTH, il3820_gatetime, 1); + /* Select border waveform for VBD */ + il3820_write_cmd(IL3820_CMD_BWF_CTRL, il3820_border, 1); + /**/ + il3820_write_cmd(IL3820_CMD_UPDATE_LUT, il3820_lut_initial, sizeof(il3820_lut_initial)); + /* Clear control memory and update */ + il3820_clear_cntlr_mem(IL3820_CMD_WRITE_RAM, true); + + // allow partial updates now + il3820_partial = true; + + /* Update LUT */ + il3820_write_cmd(IL3820_CMD_UPDATE_LUT, il3820_lut_default, sizeof(il3820_lut_default)); + + /* Clear control memory and update */ + il3820_clear_cntlr_mem(IL3820_CMD_WRITE_RAM, true); +} + +/* Enter deep sleep mode */ +void il3820_sleep_in(void) +{ + uint8_t data[] = {0x01}; + + /* Wait for the BUSY signal to go low */ + il3820_waitbusy(IL3820_WAIT); + + il3820_write_cmd(IL3820_CMD_SLEEP_MODE, data, 1); +} + +/* TODO: Remove the busy waiting */ +static void il3820_waitbusy(int wait_ms) +{ + int i = 0; + + vTaskDelay(10 / portTICK_RATE_MS); // 10ms delay + + for(i = 0; i < (wait_ms * 10); i++) { + if(gpio_get_level(IL3820_BUSY_PIN) != IL3820_BUSY_LEVEL) { + return; + } + + vTaskDelay(10 / portTICK_RATE_MS); + } + + ESP_LOGE( TAG, "busy exceeded %dms", i*10 ); +} + +/* Set DC signal to command mode */ +static inline void il3820_command_mode(void) +{ + gpio_set_level(IL3820_DC_PIN, 0); +} + +/* Set DC signal to data mode */ +static inline void il3820_data_mode(void) +{ + gpio_set_level(IL3820_DC_PIN, 1); +} + +static inline void il3820_write_cmd(uint8_t cmd, uint8_t *data, size_t len) +{ + disp_wait_for_pending_transactions(); + + il3820_command_mode(); + disp_spi_send_data(&cmd, 1); + + if (data != NULL) { + il3820_data_mode(); + disp_spi_send_data(data, len); + } +} + +/* Send cmd to the display */ +static inline void il3820_send_cmd(uint8_t cmd) +{ + disp_wait_for_pending_transactions(); + + il3820_command_mode(); + disp_spi_send_data(&cmd, 1); +} + +/* Send length bytes of data to the display */ +static void il3820_send_data(uint8_t *data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + + il3820_data_mode(); + disp_spi_send_colors(data, length); +} + +/* Specify the start/end positions of the window address in the X and Y + * directions by an address unit. + * + * @param sx: X Start position. + * @param ex: X End position. + * @param ys: Y Start position. + * @param ye: Y End position. + */ +static inline void il3820_set_window( uint16_t sx, uint16_t ex, uint16_t ys, uint16_t ye) +{ + uint8_t tmp[4] = {0}; + + tmp[0] = sx / 8; + tmp[1] = ex / 8; + + /* Set X address start/end */ + il3820_write_cmd(IL3820_CMD_RAM_XPOS_CTRL, tmp, 2); + + tmp[0] = ys % 256; + tmp[1] = ys / 256; + tmp[2] = ye % 256; + tmp[3] = ye / 256; + /* Set Y address start/end */ + il3820_write_cmd(IL3820_CMD_RAM_YPOS_CTRL, tmp, 4); +} + +/* Make initial settings for the RAM X and Y addresses in the address counter + * (AC). + * + * @param sx: RAM X address counter. + * @param ys: RAM Y address counter. + */ +static inline void il3820_set_cursor(uint16_t sx, uint16_t ys) +{ + uint8_t tmp[2] = {0}; + + tmp[0] = sx / 8; + il3820_write_cmd(IL3820_CMD_RAM_XPOS_CNTR, tmp, 1); + + tmp[0] = ys % 256; + tmp[1] = ys / 256; + il3820_write_cmd(IL3820_CMD_RAM_YPOS_CNTR, tmp, 2); +} + +/* After sending the RAM content we need to send the commands: + * - Display Update Control 2 + * - Master Activation + * + * NOTE: Currently we poll for the BUSY signal to go inactive, + * we might want not to do it. */ +static void il3820_update_display(void) +{ + uint8_t tmp = 0; + + if(il3820_partial) { + tmp = IL3820_CTRL2_TO_PATTERN; + } else { + tmp = (IL3820_CTRL2_ENABLE_CLK | IL3820_CTRL2_ENABLE_ANALOG | IL3820_CTRL2_TO_PATTERN); + } + + il3820_write_cmd(IL3820_CMD_UPDATE_CTRL2, &tmp, 1); + + il3820_write_cmd(IL3820_CMD_MASTER_ACTIVATION, NULL, 0); + /* Poll BUSY signal. */ + il3820_waitbusy(IL3820_WAIT); + /* XXX: Figure out what does this command do. */ + il3820_write_cmd(IL3820_CMD_TERMINATE_FRAME_RW, NULL, 0); +} + +/* Clear the graphic RAM. */ +static void il3820_clear_cntlr_mem(uint8_t ram_cmd, bool update) +{ + /* Arrays used by SPI must be word alligned */ + WORD_ALIGNED_ATTR uint8_t clear_page[IL3820_COLUMNS]; + memset(clear_page, 0xff, sizeof clear_page); + + /* Configure entry mode */ + il3820_write_cmd(IL3820_CMD_ENTRY_MODE, &il3820_scan_mode, 1); + + /* Configure the window */ + il3820_set_window(0, EPD_PANEL_WIDTH - 1, 0, EPD_PANEL_HEIGHT - 1); + + /* Send clear_page buffer to the display */ + for(int j = 0; j < EPD_PANEL_HEIGHT; j++) { + il3820_set_cursor(0, j); + il3820_write_cmd(ram_cmd, clear_page, sizeof clear_page); + } + + if (update) { + il3820_set_window( 0, EPD_PANEL_WIDTH - 1, 0, EPD_PANEL_HEIGHT - 1); + il3820_update_display(); + } +} diff --git a/lvgl_tft/il3820.h b/lvgl_tft/il3820.h new file mode 100644 index 0000000..15ff090 --- /dev/null +++ b/lvgl_tft/il3820.h @@ -0,0 +1,113 @@ +/** + * @file il3820.h + * + */ + +#ifndef IL3820_H +#define IL3820_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "sdkconfig.h" + +/* Values for Waveshare 2.9inch e-Paper Module, this values shouldn't be + * swapped to change display orientation */ +#define EPD_PANEL_WIDTH CONFIG_LV_DISPLAY_WIDTH /* 128 */ +#define EPD_PANEL_HEIGHT CONFIG_LV_DISPLAY_HEIGHT /* 296 */ + +/* 128 = panel width */ +#define IL3820_COLUMNS (EPD_PANEL_WIDTH / 8) + +#define IL3820_DC_PIN CONFIG_LV_DISP_PIN_DC +#define IL3820_RST_PIN CONFIG_LV_DISP_PIN_RST +#define IL3820_BUSY_PIN CONFIG_LV_DISP_PIN_BUSY +#define IL3820_BUSY_LEVEL 1 + +/* IL3820 commands */ +#define IL3820_CMD_GDO_CTRL 0x01 +#define IL3820_CMD_GDV_CTRL 0x03 +#define IL3820_CMD_SDV_CTRL 0x04 +#define IL3820_CMD_SOFTSTART 0x0c +#define IL3820_CMD_GSCAN_START 0x0f +#define IL3820_CMD_SLEEP_MODE 0x10 +#define IL3820_CMD_ENTRY_MODE 0x11 +#define IL3820_CMD_SW_RESET 0x12 +#define IL3820_CMD_TSENS_CTRL 0x1a +#define IL3820_CMD_MASTER_ACTIVATION 0x20 +#define IL3820_CMD_UPDATE_CTRL1 0x21 +#define IL3820_CMD_UPDATE_CTRL2 0x22 +#define IL3820_CMD_WRITE_RAM 0x24 +#define IL3820_CMD_WRITE_RED_RAM 0x26 +#define IL3820_CMD_VCOM_SENSE 0x28 +#define IL3820_CMD_VCOM_SENSE_DURATON 0x29 +#define IL3820_CMD_PRGM_VCOM_OTP 0x2a +#define IL3820_CMD_VCOM_VOLTAGE 0x2c +#define IL3820_CMD_PRGM_WS_OTP 0x30 +#define IL3820_CMD_UPDATE_LUT 0x32 +#define IL3820_CMD_PRGM_OTP_SELECTION 0x36 +#define IL3820_CMD_OTP_SELECTION_CTRL 0x37 +#define IL3820_CMD_DUMMY_LINE 0x3a +#define IL3820_CMD_GATE_LINE_WIDTH 0x3b +#define IL3820_CMD_BWF_CTRL 0x3c +#define IL3820_CMD_RAM_XPOS_CTRL 0x44 +#define IL3820_CMD_RAM_YPOS_CTRL 0x45 +#define IL3820_CMD_RAM_XPOS_CNTR 0x4e +#define IL3820_CMD_RAM_YPOS_CNTR 0x4f +#define IL3820_CMD_TERMINATE_FRAME_RW 0xff + +/* Data entry sequence modes */ +#define IL3820_DATA_ENTRY_MASK 0x07 +#define IL3820_DATA_ENTRY_XDYDX 0x00 +#define IL3820_DATA_ENTRY_XIYDX 0x01 +#define IL3820_DATA_ENTRY_XDYIX 0x02 +#define IL3820_DATA_ENTRY_XIYIX 0x03 +#define IL3820_DATA_ENTRY_XDYDY 0x04 +#define IL3820_DATA_ENTRY_XIYDY 0x05 +#define IL3820_DATA_ENTRY_XDYIY 0x06 +#define IL3820_DATA_ENTRY_XIYIY 0x07 + +/* Options for display update */ +#define IL3820_CTRL1_INITIAL_UPDATE_LL 0x00 +#define IL3820_CTRL1_INITIAL_UPDATE_LH 0x01 +#define IL3820_CTRL1_INITIAL_UPDATE_HL 0x02 +#define IL3820_CTRL1_INITIAL_UPDATE_HH 0x03 + +/* Options for display update sequence */ +#define IL3820_CTRL2_ENABLE_CLK 0x80 +#define IL3820_CTRL2_ENABLE_ANALOG 0x40 +#define IL3820_CTRL2_TO_INITIAL 0x08 +#define IL3820_CTRL2_TO_PATTERN 0x04 +#define IL3820_CTRL2_DISABLE_ANALOG 0x02 +#define IL3820_CTRL2_DISABLE_CLK 0x01 + +#define IL3820_SLEEP_MODE_DSM 0x01 +#define IL3820_SLEEP_MODE_PON 0x00 + +/* time constants in ms */ +#define IL3820_RESET_DELAY 20 +#define IL3820_BUSY_DELAY 1 +// normal wait time max 200ms +#define IL3820_WAIT 20 + +void il3820_init(void); +void il3820_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map); +void il3820_fullflush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map); +void il3820_rounder(struct _disp_drv_t * disp_drv, lv_area_t *area); +void il3820_set_px_cb(struct _disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa); +void il3820_sleep_in(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* __IL3820_REGS_H__ */ + diff --git a/lvgl_tft/ili9341.c b/lvgl_tft/ili9341.c new file mode 100644 index 0000000..ab9480f --- /dev/null +++ b/lvgl_tft/ili9341.c @@ -0,0 +1,241 @@ +/** + * @file ili9341.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "ili9341.h" +#include "disp_spi.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +/********************* + * DEFINES + *********************/ + #define TAG "ILI9341" + +/********************** + * TYPEDEFS + **********************/ + +/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} lcd_init_cmd_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void ili9341_set_orientation(uint8_t orientation); + +static void ili9341_send_cmd(uint8_t cmd); +static void ili9341_send_data(void * data, uint16_t length); +static void ili9341_send_color(void * data, uint16_t length); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void ili9341_init(void) +{ + lcd_init_cmd_t ili_init_cmds[]={ + {0xCF, {0x00, 0x83, 0X30}, 3}, + {0xED, {0x64, 0x03, 0X12, 0X81}, 4}, + {0xE8, {0x85, 0x01, 0x79}, 3}, + {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5}, + {0xF7, {0x20}, 1}, + {0xEA, {0x00, 0x00}, 2}, + {0xC0, {0x26}, 1}, /*Power control*/ + {0xC1, {0x11}, 1}, /*Power control */ + {0xC5, {0x35, 0x3E}, 2}, /*VCOM control*/ + {0xC7, {0xBE}, 1}, /*VCOM control*/ + {0x36, {0x28}, 1}, /*Memory Access Control*/ + {0x3A, {0x55}, 1}, /*Pixel Format Set*/ + {0xB1, {0x00, 0x1B}, 2}, + {0xF2, {0x08}, 1}, + {0x26, {0x01}, 1}, + {0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15}, + {0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15}, + {0x2A, {0x00, 0x00, 0x00, 0xEF}, 4}, + {0x2B, {0x00, 0x00, 0x01, 0x3f}, 4}, + {0x2C, {0}, 0}, + {0xB7, {0x07}, 1}, + {0xB6, {0x0A, 0x82, 0x27, 0x00}, 4}, + {0x11, {0}, 0x80}, + {0x29, {0}, 0x80}, + {0, {0}, 0xff}, + }; + +#if ILI9341_BCKL == 15 + gpio_config_t io_conf; + io_conf.intr_type = GPIO_PIN_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = GPIO_SEL_15; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + gpio_config(&io_conf); +#endif + + //Initialize non-SPI GPIOs + gpio_pad_select_gpio(ILI9341_DC); + gpio_set_direction(ILI9341_DC, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(ILI9341_RST); + gpio_set_direction(ILI9341_RST, GPIO_MODE_OUTPUT); + +#if ILI9341_ENABLE_BACKLIGHT_CONTROL + gpio_pad_select_gpio(ILI9341_BCKL); + gpio_set_direction(ILI9341_BCKL, GPIO_MODE_OUTPUT); +#endif + //Reset the display + gpio_set_level(ILI9341_RST, 0); + vTaskDelay(100 / portTICK_RATE_MS); + gpio_set_level(ILI9341_RST, 1); + vTaskDelay(100 / portTICK_RATE_MS); + + ESP_LOGI(TAG, "Initialization."); + + //Send all the commands + uint16_t cmd = 0; + while (ili_init_cmds[cmd].databytes!=0xff) { + ili9341_send_cmd(ili_init_cmds[cmd].cmd); + ili9341_send_data(ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes&0x1F); + if (ili_init_cmds[cmd].databytes & 0x80) { + vTaskDelay(100 / portTICK_RATE_MS); + } + cmd++; + } + + ili9341_enable_backlight(true); + + ili9341_set_orientation(CONFIG_LV_DISPLAY_ORIENTATION); + +#if ILI9341_INVERT_COLORS == 1 + ili9341_send_cmd(0x21); +#else + ili9341_send_cmd(0x20); +#endif +} + + +void ili9341_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) +{ + uint8_t data[4]; + + /*Column addresses*/ + ili9341_send_cmd(0x2A); + data[0] = (area->x1 >> 8) & 0xFF; + data[1] = area->x1 & 0xFF; + data[2] = (area->x2 >> 8) & 0xFF; + data[3] = area->x2 & 0xFF; + ili9341_send_data(data, 4); + + /*Page addresses*/ + ili9341_send_cmd(0x2B); + data[0] = (area->y1 >> 8) & 0xFF; + data[1] = area->y1 & 0xFF; + data[2] = (area->y2 >> 8) & 0xFF; + data[3] = area->y2 & 0xFF; + ili9341_send_data(data, 4); + + /*Memory write*/ + ili9341_send_cmd(0x2C); + + + uint32_t size = lv_area_get_width(area) * lv_area_get_height(area); + + ili9341_send_color((void*)color_map, size * 2); +} + +void ili9341_enable_backlight(bool backlight) +{ +#if ILI9341_ENABLE_BACKLIGHT_CONTROL + ESP_LOGI(TAG, "%s backlight.", backlight ? "Enabling" : "Disabling"); + uint32_t tmp = 0; + +#if (ILI9341_BCKL_ACTIVE_LVL==1) + tmp = backlight ? 1 : 0; +#else + tmp = backlight ? 0 : 1; +#endif + + gpio_set_level(ILI9341_BCKL, tmp); +#endif +} + +void ili9341_sleep_in() +{ + uint8_t data[] = {0x08}; + ili9341_send_cmd(0x10); + ili9341_send_data(&data, 1); +} + +void ili9341_sleep_out() +{ + uint8_t data[] = {0x08}; + ili9341_send_cmd(0x11); + ili9341_send_data(&data, 1); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +static void ili9341_send_cmd(uint8_t cmd) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ILI9341_DC, 0); /*Command mode*/ + disp_spi_send_data(&cmd, 1); +} + +static void ili9341_send_data(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ILI9341_DC, 1); /*Data mode*/ + disp_spi_send_data(data, length); +} + +static void ili9341_send_color(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ILI9341_DC, 1); /*Data mode*/ + disp_spi_send_colors(data, length); +} + +static void ili9341_set_orientation(uint8_t orientation) +{ + // ESP_ASSERT(orientation < 4); + + const char *orientation_str[] = { + "PORTRAIT", "PORTRAIT_INVERTED", "LANDSCAPE", "LANDSCAPE_INVERTED" + }; + + ESP_LOGI(TAG, "Display orientation: %s", orientation_str[orientation]); + +#if defined CONFIG_LV_PREDEFINED_DISPLAY_M5STACK + uint8_t data[] = {0x68, 0x68, 0x08, 0x08}; +#elif defined (CONFIG_LV_PREDEFINED_DISPLAY_WROVER4) + uint8_t data[] = {0x4C, 0x88, 0x28, 0xE8}; +#elif defined (CONFIG_LV_PREDEFINED_DISPLAY_NONE) + uint8_t data[] = {0x48, 0x88, 0x28, 0xE8}; +#endif + + ESP_LOGI(TAG, "0x36 command value: 0x%02X", data[orientation]); + + ili9341_send_cmd(0x36); + ili9341_send_data((void *) &data[orientation], 1); +} diff --git a/lvgl_tft/ili9341.h b/lvgl_tft/ili9341.h new file mode 100644 index 0000000..4beb4f3 --- /dev/null +++ b/lvgl_tft/ili9341.h @@ -0,0 +1,65 @@ +/** + * @file lv_templ.h + * + */ + +#ifndef ILI9341_H +#define ILI9341_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "../lvgl_helpers.h" + +/********************* + * DEFINES + *********************/ +#define ILI9341_DC CONFIG_LV_DISP_PIN_DC +#define ILI9341_RST CONFIG_LV_DISP_PIN_RST +#define ILI9341_BCKL CONFIG_LV_DISP_PIN_BCKL + +#define ILI9341_ENABLE_BACKLIGHT_CONTROL CONFIG_LV_ENABLE_BACKLIGHT_CONTROL + +#if CONFIG_LV_BACKLIGHT_ACTIVE_LVL + #define ILI9341_BCKL_ACTIVE_LVL 1 +#else + #define ILI9341_BCKL_ACTIVE_LVL 0 +#endif + +#define ILI9341_INVERT_COLORS CONFIG_LV_INVERT_COLORS + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void ili9341_init(void); +void ili9341_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); +void ili9341_enable_backlight(bool backlight); +void ili9341_sleep_in(void); +void ili9341_sleep_out(void); + +/********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*ILI9341_H*/ diff --git a/lvgl_tft/ili9481.c b/lvgl_tft/ili9481.c new file mode 100644 index 0000000..6472a93 --- /dev/null +++ b/lvgl_tft/ili9481.c @@ -0,0 +1,224 @@ +/** + * @file ili9481.c + */ + +/********************* + * INCLUDES + *********************/ +#include "ili9481.h" +#include "disp_spi.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "esp_heap_caps.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +/********************* + * DEFINES + *********************/ + #define TAG "ILI9481" + +/********************** + * TYPEDEFS + **********************/ + +/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} lcd_init_cmd_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void ili9481_set_orientation(uint8_t orientation); +static void ili9481_send_cmd(uint8_t cmd); +static void ili9481_send_data(void * data, uint16_t length); +static void ili9481_send_color(void * data, uint16_t length); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void ili9481_init(void) +{ + lcd_init_cmd_t ili_init_cmds[]={ + {ILI9481_CMD_SLEEP_OUT, {0x00}, 0x80}, + {ILI9481_CMD_POWER_SETTING, {0x07, 0x42, 0x18}, 3}, + {ILI9481_CMD_VCOM_CONTROL, {0x00, 0x07, 0x10}, 3}, + {ILI9481_CMD_POWER_CONTROL_NORMAL, {0x01, 0x02}, 2}, + {ILI9481_CMD_PANEL_DRIVE, {0x10, 0x3B, 0x00, 0x02, 0x11}, 5}, + {ILI9481_CMD_FRAME_RATE, {0x03}, 1}, + {ILI9481_CMD_FRAME_MEMORY_ACCESS, {0x0, 0x0, 0x0, 0x0}, 4}, + //{ILI9481_CMD_DISP_TIMING_NORMAL, {0x10, 0x10, 0x22}, 3}, + {ILI9481_CMD_GAMMA_SETTING, {0x00, 0x32, 0x36, 0x45, 0x06, 0x16, 0x37, 0x75, 0x77, 0x54, 0x0C, 0x00}, 12}, + {ILI9481_CMD_MEMORY_ACCESS_CONTROL, {0x0A}, 1}, +#if ILI9481_INVERT_COLORS + {ILI9481_CMD_DISP_INVERSION_ON, {}, 0}, +#endif + {ILI9481_CMD_COLMOD_PIXEL_FORMAT_SET, {0x66}, 1}, + {ILI9481_CMD_NORMAL_DISP_MODE_ON, {}, 0x80}, + {ILI9481_CMD_DISPLAY_ON, {}, 0x80}, + {0, {0}, 0xff}, + }; + + //Initialize non-SPI GPIOs + gpio_pad_select_gpio(ILI9481_DC); + gpio_set_direction(ILI9481_DC, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(ILI9481_RST); + gpio_set_direction(ILI9481_RST, GPIO_MODE_OUTPUT); + +#if ILI9481_ENABLE_BACKLIGHT_CONTROL + gpio_pad_select_gpio(ILI9481_BCKL); + gpio_set_direction(ILI9481_BCKL, GPIO_MODE_OUTPUT); +#endif + + //Reset the display + gpio_set_level(ILI9481_RST, 0); + vTaskDelay(100 / portTICK_RATE_MS); + gpio_set_level(ILI9481_RST, 1); + vTaskDelay(100 / portTICK_RATE_MS); + + ESP_LOGI(TAG, "ILI9481 initialization."); + + // Exit sleep + ili9481_send_cmd(0x01); /* Software reset */ + vTaskDelay(100 / portTICK_RATE_MS); + + //Send all the commands + uint16_t cmd = 0; + while (ili_init_cmds[cmd].databytes!=0xff) { + ili9481_send_cmd(ili_init_cmds[cmd].cmd); + ili9481_send_data(ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes&0x1F); + if (ili_init_cmds[cmd].databytes & 0x80) { + vTaskDelay(100 / portTICK_RATE_MS); + } + cmd++; + } + + ili9481_enable_backlight(true); + + ili9481_set_orientation(ILI9481_DISPLAY_ORIENTATION); +} + +// Flush function based on mvturnho repo +void ili9481_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) +{ + uint32_t size = lv_area_get_width(area) * lv_area_get_height(area); + + lv_color16_t *buffer_16bit = (lv_color16_t *) color_map; + uint8_t *mybuf; + do { + mybuf = (uint8_t *) heap_caps_malloc(3 * size * sizeof(uint8_t), MALLOC_CAP_DMA); + if (mybuf == NULL) ESP_LOGW(TAG, "Could not allocate enough DMA memory!"); + } while (mybuf == NULL); + + uint32_t LD = 0; + uint32_t j = 0; + + for (uint32_t i = 0; i < size; i++) { + LD = buffer_16bit[i].full; + mybuf[j] = (uint8_t) (((LD & 0xF800) >> 8) | ((LD & 0x8000) >> 13)); + j++; + mybuf[j] = (uint8_t) ((LD & 0x07E0) >> 3); + j++; + mybuf[j] = (uint8_t) (((LD & 0x001F) << 3) | ((LD & 0x0010) >> 2)); + j++; + } + + /* Column addresses */ + uint8_t xb[] = { + (uint8_t) (area->x1 >> 8) & 0xFF, + (uint8_t) (area->x1) & 0xFF, + (uint8_t) (area->x2 >> 8) & 0xFF, + (uint8_t) (area->x2) & 0xFF, + }; + + /* Page addresses */ + uint8_t yb[] = { + (uint8_t) (area->y1 >> 8) & 0xFF, + (uint8_t) (area->y1) & 0xFF, + (uint8_t) (area->y2 >> 8) & 0xFF, + (uint8_t) (area->y2) & 0xFF, + }; + + /*Column addresses*/ + ili9481_send_cmd(ILI9481_CMD_COLUMN_ADDRESS_SET); + ili9481_send_data(xb, 4); + + /*Page addresses*/ + ili9481_send_cmd(ILI9481_CMD_PAGE_ADDRESS_SET); + ili9481_send_data(yb, 4); + + /*Memory write*/ + ili9481_send_cmd(ILI9481_CMD_MEMORY_WRITE); + + ili9481_send_color((void *) mybuf, size * 3); + heap_caps_free(mybuf); +} + +void ili9481_enable_backlight(bool backlight) +{ +#if ILI9481_ENABLE_BACKLIGHT_CONTROL + ESP_LOGI(TAG, "%s backlight.", backlight ? "Enabling" : "Disabling"); + uint32_t tmp = 0; + +#if (ILI9481_BCKL_ACTIVE_LVL==1) + tmp = backlight ? 1 : 0; +#else + tmp = backlight ? 0 : 1; +#endif + + gpio_set_level(ILI9481_BCKL, tmp); +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +static void ili9481_send_cmd(uint8_t cmd) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ILI9481_DC, 0); /*Command mode*/ + disp_spi_send_data(&cmd, 1); +} + +static void ili9481_send_data(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ILI9481_DC, 1); /*Data mode*/ + disp_spi_send_data(data, length); +} + +static void ili9481_send_color(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ILI9481_DC, 1); /*Data mode*/ + disp_spi_send_colors(data, length); +} + +static void ili9481_set_orientation(uint8_t orientation) +{ + const char *orientation_str[] = { + "PORTRAIT", "PORTRAIT_INVERTED", "LANDSCAPE", "LANDSCAPE_INVERTED" + }; + + ESP_LOGI(TAG, "Display orientation: %s", orientation_str[orientation]); + + uint8_t data[] = {0x48, 0x4B, 0x28, 0x2B}; + ili9481_send_cmd(ILI9481_CMD_MEMORY_ACCESS_CONTROL); + ili9481_send_data((void *) &data[orientation], 1); +} diff --git a/lvgl_tft/ili9481.h b/lvgl_tft/ili9481.h new file mode 100644 index 0000000..7932190 --- /dev/null +++ b/lvgl_tft/ili9481.h @@ -0,0 +1,130 @@ +/** + * @file ili9481.h + */ + +#ifndef ILI9481_H +#define ILI9481_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#include + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "../lvgl_helpers.h" + +/********************* + * DEFINES + *********************/ +#define ILI9481_DC CONFIG_LV_DISP_PIN_DC +#define ILI9481_RST CONFIG_LV_DISP_PIN_RST +#define ILI9481_BCKL CONFIG_LV_DISP_PIN_BCKL + +#define ILI9481_ENABLE_BACKLIGHT_CONTROL CONFIG_LV_ENABLE_BACKLIGHT_CONTROL +#define ILI9481_INVERT_COLORS CONFIG_LV_INVERT_COLORS +#define ILI9481_DISPLAY_ORIENTATION CONFIG_LV_DISPLAY_ORIENTATION + +#if CONFIG_LV_BACKLIGHT_ACTIVE_LVL + #define ILI9481_BCKL_ACTIVE_LVL 1 +#else + #define ILI9481_BCKL_ACTIVE_LVL 0 +#endif + +/******************* + * ILI9481 REGS +*********************/ + +/* MIPI DCS Type1 */ +#define ILI9481_CMD_NOP 0x00 +#define ILI9481_CMD_SOFTWARE_RESET 0x01 +#define ILI9481_CMD_READ_DISP_POWER_MODE 0x0A +#define ILI9481_CMD_READ_DISP_MADCTRL 0x0B // bits 7:3 only +#define ILI9481_CMD_READ_DISP_PIXEL_FORMAT 0x0C +#define ILI9481_CMD_READ_DISP_IMAGE_MODE 0x0D +#define ILI9481_CMD_READ_DISP_SIGNAL_MODE 0x0E +#define ILI9481_CMD_READ_DISP_SELF_DIAGNOSTIC 0x0F // bits 7:6 only +#define ILI9481_CMD_ENTER_SLEEP_MODE 0x10 +#define ILI9481_CMD_SLEEP_OUT 0x11 +#define ILI9481_CMD_PARTIAL_MODE_ON 0x12 +#define ILI9481_CMD_NORMAL_DISP_MODE_ON 0x13 +#define ILI9481_CMD_DISP_INVERSION_OFF 0x20 +#define ILI9481_CMD_DISP_INVERSION_ON 0x21 +#define ILI9481_CMD_DISPLAY_OFF 0x28 +#define ILI9481_CMD_DISPLAY_ON 0x29 +#define ILI9481_CMD_COLUMN_ADDRESS_SET 0x2A +#define ILI9481_CMD_PAGE_ADDRESS_SET 0x2B +#define ILI9481_CMD_MEMORY_WRITE 0x2C +#define ILI9481_CMD_MEMORY_READ 0x2E +#define ILI9481_CMD_PARTIAL_AREA 0x30 +#define ILI9481_CMD_VERT_SCROLL_DEFINITION 0x33 +#define ILI9481_CMD_TEARING_EFFECT_LINE_OFF 0x34 +#define ILI9481_CMD_TEARING_EFFECT_LINE_ON 0x35 +#define ILI9481_CMD_MEMORY_ACCESS_CONTROL 0x36 // bits 7:3,1:0 only +#define ILI9481_CMD_VERT_SCROLL_START_ADDRESS 0x37 +#define ILI9481_CMD_IDLE_MODE_OFF 0x38 +#define ILI9481_CMD_IDLE_MODE_ON 0x39 +#define ILI9481_CMD_COLMOD_PIXEL_FORMAT_SET 0x3A +#define ILI9481_CMD_WRITE_MEMORY_CONTINUE 0x3C +#define ILI9481_CMD_READ_MEMORY_CONTINUE 0x3E +#define ILI9481_CMD_SET_TEAR_SCANLINE 0x44 +#define ILI9481_CMD_GET_SCANLINE 0x45 + +#define ILI9481_DDB_START 0xA1 +#define ILI9481_DDB_CONTINUE 0xA8 + +/* other */ +#define ILI9481_CMD_ACCESS_PROTECT 0xB0 +#define ILI9481_CMD_LOW_POWER_CONTROL 0xB1 +#define ILI9481_CMD_FRAME_MEMORY_ACCESS 0xB3 +#define ILI9481_CMD_DISPLAY_MODE 0xB4 +#define ILI9481_CMD_DEVICE_CODE 0xBF + +#define ILI9481_CMD_PANEL_DRIVE 0xC0 +#define ILI9481_CMD_DISP_TIMING_NORMAL 0xC1 +#define ILI9481_CMD_DISP_TIMING_PARTIAL 0xC2 +#define ILI9481_CMD_DISP_TIMING_IDLE 0xC3 +#define ILI9481_CMD_FRAME_RATE 0xC5 +#define ILI9481_CMD_INTERFACE_CONTROL 0xC6 +#define ILI9481_CMD_GAMMA_SETTING 0xC8 + +#define ILI9481_CMD_POWER_SETTING 0xD0 +#define ILI9481_CMD_VCOM_CONTROL 0xD1 +#define ILI9481_CMD_POWER_CONTROL_NORMAL 0xD2 +#define ILI9481_CMD_POWER_CONTROL_IDEL 0xD3 +#define ILI9481_CMD_POWER_CONTROL_PARTIAL 0xD4 + +#define ILI9481_CMD_NVMEM_WRITE 0xE0 +#define ILI9481_CMD_NVMEM_PROTECTION_KEY 0xE1 +#define ILI9481_CMD_NVMEM_STATUS_READ 0xE2 +#define ILI9481_CMD_NVMEM_PROTECTION 0xE3 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void ili9481_init(void); +void ili9481_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); +void ili9481_enable_backlight(bool backlight); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*ILI9481_H*/ diff --git a/lvgl_tft/ili9486.c b/lvgl_tft/ili9486.c new file mode 100644 index 0000000..e9b2b08 --- /dev/null +++ b/lvgl_tft/ili9486.c @@ -0,0 +1,214 @@ +/** + * @file mpi3501.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "ili9486.h" +#include "disp_spi.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +/********************* + * DEFINES + *********************/ + #define TAG "ILI9486" + +/********************** + * TYPEDEFS + **********************/ + +/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} lcd_init_cmd_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void ili9486_set_orientation(uint8_t orientation); + +static void ili9486_send_cmd(uint8_t cmd); +static void ili9486_send_data(void * data, uint16_t length); +static void ili9486_send_color(void * data, uint16_t length); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void ili9486_init(void) +{ + lcd_init_cmd_t ili_init_cmds[]={ + {0x11, {0}, 0x80}, + {0x3A, {0x55}, 1}, + {0x2C, {0x44}, 1}, + {0xC5, {0x00, 0x00, 0x00, 0x00}, 4}, + {0xE0, {0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98, 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00}, 15}, + {0XE1, {0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00}, 15}, + {0x20, {0}, 0}, /* display inversion OFF */ + {0x36, {0x48}, 1}, + {0x29, {0}, 0x80}, /* display on */ + {0x00, {0}, 0xff}, + }; + +#if ILI9486_BCKL == 15 + gpio_config_t io_conf; + io_conf.intr_type = GPIO_PIN_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = GPIO_SEL_15; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + gpio_config(&io_conf); +#endif + + //Initialize non-SPI GPIOs + gpio_pad_select_gpio(ILI9486_DC); + gpio_set_direction(ILI9486_DC, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(ILI9486_RST); + gpio_set_direction(ILI9486_RST, GPIO_MODE_OUTPUT); + +#if ILI9486_ENABLE_BACKLIGHT_CONTROL + gpio_pad_select_gpio(ILI9486_BCKL); + gpio_set_direction(ILI9486_BCKL, GPIO_MODE_OUTPUT); +#endif + + //Reset the display + gpio_set_level(ILI9486_RST, 0); + vTaskDelay(100 / portTICK_RATE_MS); + gpio_set_level(ILI9486_RST, 1); + vTaskDelay(100 / portTICK_RATE_MS); + + ESP_LOGI(TAG, "ILI9486 Initialization."); + + //Send all the commands + uint16_t cmd = 0; + while (ili_init_cmds[cmd].databytes!=0xff) { + ili9486_send_cmd(ili_init_cmds[cmd].cmd); + ili9486_send_data(ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes&0x1F); + if (ili_init_cmds[cmd].databytes & 0x80) { + vTaskDelay(100 / portTICK_RATE_MS); + } + cmd++; + } + + ili9486_enable_backlight(true); + + ili9486_set_orientation(CONFIG_LV_DISPLAY_ORIENTATION); +} + +void ili9486_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) +{ + uint8_t data[4] = {0}; + uint32_t size = 0; + + /*Column addresses*/ + ili9486_send_cmd(0x2A); + data[0] = (area->x1 >> 8) & 0xFF; + data[1] = area->x1 & 0xFF; + data[2] = (area->x2 >> 8) & 0xFF; + data[3] = area->x2 & 0xFF; + ili9486_send_data(data, 4); + + /*Page addresses*/ + ili9486_send_cmd(0x2B); + data[0] = (area->y1 >> 8) & 0xFF; + data[1] = area->y1 & 0xFF; + data[2] = (area->y2 >> 8) & 0xFF; + data[3] = area->y2 & 0xFF; + ili9486_send_data(data, 4); + + /*Memory write*/ + ili9486_send_cmd(0x2C); + + size = lv_area_get_width(area) * lv_area_get_height(area); + + ili9486_send_color((void*) color_map, size * 2); +} + +void ili9486_enable_backlight(bool backlight) +{ +#if ILI9486_ENABLE_BACKLIGHT_CONTROL + ESP_LOGI(TAG, "%s backlight.", backlight ? "Enabling" : "Disabling"); + uint32_t tmp = 0; + +#if (ILI9486_BCKL_ACTIVE_LVL==1) + tmp = backlight ? 1 : 0; +#else + tmp = backlight ? 0 : 1; +#endif + + gpio_set_level(ILI9486_BCKL, tmp); +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ +static void ili9486_send_cmd(uint8_t cmd) +{ + uint8_t to16bit[] = { + 0x00, cmd + }; + + disp_wait_for_pending_transactions(); + gpio_set_level(ILI9486_DC, 0); /*Command mode*/ + disp_spi_send_data(to16bit, sizeof to16bit); +} + +static void ili9486_send_data(void * data, uint16_t length) +{ + uint32_t i; + uint8_t to16bit[32]; + uint8_t * dummy = data; + + for(i=0; i < (length); i++) + { + to16bit[2*i+1] = dummy[i]; + to16bit[2*i] = 0x00; + } + + disp_wait_for_pending_transactions(); + gpio_set_level(ILI9486_DC, 1); /*Data mode*/ + disp_spi_send_data(to16bit, (length*2)); +} + +static void ili9486_send_color(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ILI9486_DC, 1); /*Data mode*/ + disp_spi_send_colors(data, length); +} + +static void ili9486_set_orientation(uint8_t orientation) +{ + // ESP_ASSERT(orientation < 4); + + const char *orientation_str[] = { + "PORTRAIT", "PORTRAIT_INVERTED", "LANDSCAPE", "LANDSCAPE_INVERTED" + }; + + ESP_LOGI(TAG, "Display orientation: %s", orientation_str[orientation]); + +#if defined (CONFIG_LV_PREDEFINED_DISPLAY_NONE) + uint8_t data[] = {0x48, 0x88, 0x28, 0xE8}; +#endif + + ESP_LOGI(TAG, "0x36 command value: 0x%02X", data[orientation]); + + ili9486_send_cmd(0x36); + ili9486_send_data((void *) &data[orientation], 1); +} diff --git a/lvgl_tft/ili9486.h b/lvgl_tft/ili9486.h new file mode 100644 index 0000000..f65dd80 --- /dev/null +++ b/lvgl_tft/ili9486.h @@ -0,0 +1,61 @@ +/** + * @file ili9486.h + * + */ + +#ifndef ILI9486_H +#define ILI9486_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "../lvgl_helpers.h" + +/********************* + * DEFINES + *********************/ +#define ILI9486_DC CONFIG_LV_DISP_PIN_DC +#define ILI9486_RST CONFIG_LV_DISP_PIN_RST +#define ILI9486_BCKL CONFIG_LV_DISP_PIN_BCKL + +#define ILI9486_ENABLE_BACKLIGHT_CONTROL CONFIG_LV_ENABLE_BACKLIGHT_CONTROL + +#if CONFIG_LV_BACKLIGHT_ACTIVE_LVL + #define ILI9486_BCKL_ACTIVE_LVL 1 +#else + #define ILI9486_BCKL_ACTIVE_LVL 0 +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void ili9486_init(void); +void ili9486_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); +void ili9486_enable_backlight(bool backlight); + +/********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ILI9486_H*/ diff --git a/lvgl_tft/ili9488.c b/lvgl_tft/ili9488.c new file mode 100644 index 0000000..74f7139 --- /dev/null +++ b/lvgl_tft/ili9488.c @@ -0,0 +1,233 @@ +/** + * @file ili9488.c + */ + +/********************* + * INCLUDES + *********************/ +#include "ili9488.h" +#include "disp_spi.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "esp_heap_caps.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +/********************* + * DEFINES + *********************/ + #define TAG "ILI9488" + +/********************** + * TYPEDEFS + **********************/ + +/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} lcd_init_cmd_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void ili9488_set_orientation(uint8_t orientation); + +static void ili9488_send_cmd(uint8_t cmd); +static void ili9488_send_data(void * data, uint16_t length); +static void ili9488_send_color(void * data, uint16_t length); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +// From github.com/jeremyjh/ESP32_TFT_library +// From github.com/mvturnho/ILI9488-lvgl-ESP32-WROVER-B +void ili9488_init(void) +{ + lcd_init_cmd_t ili_init_cmds[]={ + {ILI9488_CMD_SLEEP_OUT, {0x00}, 0x80}, + {ILI9488_CMD_POSITIVE_GAMMA_CORRECTION, {0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F}, 15}, + {ILI9488_CMD_NEGATIVE_GAMMA_CORRECTION, {0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F}, 15}, + {ILI9488_CMD_POWER_CONTROL_1, {0x17, 0x15}, 2}, + {ILI9488_CMD_POWER_CONTROL_2, {0x41}, 1}, + {ILI9488_CMD_VCOM_CONTROL_1, {0x00, 0x12, 0x80}, 3}, + {ILI9488_CMD_MEMORY_ACCESS_CONTROL, {(0x20 | 0x08)}, 1}, + {ILI9488_CMD_COLMOD_PIXEL_FORMAT_SET, {0x66}, 1}, + {ILI9488_CMD_INTERFACE_MODE_CONTROL, {0x00}, 1}, + {ILI9488_CMD_FRAME_RATE_CONTROL_NORMAL, {0xA0}, 1}, + {ILI9488_CMD_DISPLAY_INVERSION_CONTROL, {0x02}, 1}, + {ILI9488_CMD_DISPLAY_FUNCTION_CONTROL, {0x02, 0x02}, 2}, + {ILI9488_CMD_SET_IMAGE_FUNCTION, {0x00}, 1}, + {ILI9488_CMD_WRITE_CTRL_DISPLAY, {0x28}, 1}, + {ILI9488_CMD_WRITE_DISPLAY_BRIGHTNESS, {0x7F}, 1}, + {ILI9488_CMD_ADJUST_CONTROL_3, {0xA9, 0x51, 0x2C, 0x02}, 4}, + {ILI9488_CMD_DISPLAY_ON, {0x00}, 0x80}, + {0, {0}, 0xff}, + }; + + //Initialize non-SPI GPIOs + gpio_pad_select_gpio(ILI9488_DC); + gpio_set_direction(ILI9488_DC, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(ILI9488_RST); + gpio_set_direction(ILI9488_RST, GPIO_MODE_OUTPUT); + +#if ILI9488_ENABLE_BACKLIGHT_CONTROL + gpio_pad_select_gpio(ILI9488_BCKL); + gpio_set_direction(ILI9488_BCKL, GPIO_MODE_OUTPUT); +#endif + + //Reset the display + gpio_set_level(ILI9488_RST, 0); + vTaskDelay(100 / portTICK_RATE_MS); + gpio_set_level(ILI9488_RST, 1); + vTaskDelay(100 / portTICK_RATE_MS); + + ESP_LOGI(TAG, "ILI9488 initialization."); + + // Exit sleep + ili9488_send_cmd(0x01); /* Software reset */ + vTaskDelay(100 / portTICK_RATE_MS); + + //Send all the commands + uint16_t cmd = 0; + while (ili_init_cmds[cmd].databytes!=0xff) { + ili9488_send_cmd(ili_init_cmds[cmd].cmd); + ili9488_send_data(ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes&0x1F); + if (ili_init_cmds[cmd].databytes & 0x80) { + vTaskDelay(100 / portTICK_RATE_MS); + } + cmd++; + } + + ili9488_enable_backlight(true); + + ili9488_set_orientation(CONFIG_LV_DISPLAY_ORIENTATION); +} + +// Flush function based on mvturnho repo +void ili9488_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) +{ + uint32_t size = lv_area_get_width(area) * lv_area_get_height(area); + + lv_color16_t *buffer_16bit = (lv_color16_t *) color_map; + uint8_t *mybuf; + do { + mybuf = (uint8_t *) heap_caps_malloc(3 * size * sizeof(uint8_t), MALLOC_CAP_DMA); + if (mybuf == NULL) ESP_LOGW(TAG, "Could not allocate enough DMA memory!"); + } while (mybuf == NULL); + + uint32_t LD = 0; + uint32_t j = 0; + + for (uint32_t i = 0; i < size; i++) { + LD = buffer_16bit[i].full; + mybuf[j] = (uint8_t) (((LD & 0xF800) >> 8) | ((LD & 0x8000) >> 13)); + j++; + mybuf[j] = (uint8_t) ((LD & 0x07E0) >> 3); + j++; + mybuf[j] = (uint8_t) (((LD & 0x001F) << 3) | ((LD & 0x0010) >> 2)); + j++; + } + + /* Column addresses */ + uint8_t xb[] = { + (uint8_t) (area->x1 >> 8) & 0xFF, + (uint8_t) (area->x1) & 0xFF, + (uint8_t) (area->x2 >> 8) & 0xFF, + (uint8_t) (area->x2) & 0xFF, + }; + + /* Page addresses */ + uint8_t yb[] = { + (uint8_t) (area->y1 >> 8) & 0xFF, + (uint8_t) (area->y1) & 0xFF, + (uint8_t) (area->y2 >> 8) & 0xFF, + (uint8_t) (area->y2) & 0xFF, + }; + + /*Column addresses*/ + ili9488_send_cmd(ILI9488_CMD_COLUMN_ADDRESS_SET); + ili9488_send_data(xb, 4); + + /*Page addresses*/ + ili9488_send_cmd(ILI9488_CMD_PAGE_ADDRESS_SET); + ili9488_send_data(yb, 4); + + /*Memory write*/ + ili9488_send_cmd(ILI9488_CMD_MEMORY_WRITE); + + ili9488_send_color((void *) mybuf, size * 3); + heap_caps_free(mybuf); +} + +void ili9488_enable_backlight(bool backlight) +{ +#if ILI9488_ENABLE_BACKLIGHT_CONTROL + ESP_LOGI(TAG, "%s backlight.", backlight ? "Enabling" : "Disabling"); + uint32_t tmp = 0; + +#if (ILI9488_BCKL_ACTIVE_LVL==1) + tmp = backlight ? 1 : 0; +#else + tmp = backlight ? 0 : 1; +#endif + + gpio_set_level(ILI9488_BCKL, tmp); +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +static void ili9488_send_cmd(uint8_t cmd) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ILI9488_DC, 0); /*Command mode*/ + disp_spi_send_data(&cmd, 1); +} + +static void ili9488_send_data(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ILI9488_DC, 1); /*Data mode*/ + disp_spi_send_data(data, length); +} + +static void ili9488_send_color(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ILI9488_DC, 1); /*Data mode*/ + disp_spi_send_colors(data, length); +} + +static void ili9488_set_orientation(uint8_t orientation) +{ + // ESP_ASSERT(orientation < 4); + + const char *orientation_str[] = { + "PORTRAIT", "PORTRAIT_INVERTED", "LANDSCAPE", "LANDSCAPE_INVERTED" + }; + + ESP_LOGI(TAG, "Display orientation: %s", orientation_str[orientation]); + +#if defined (CONFIG_LV_PREDEFINED_DISPLAY_NONE) + uint8_t data[] = {0x48, 0x88, 0x28, 0xE8}; +#endif + + ESP_LOGI(TAG, "0x36 command value: 0x%02X", data[orientation]); + + ili9488_send_cmd(0x36); + ili9488_send_data((void *) &data[orientation], 1); +} diff --git a/lvgl_tft/ili9488.h b/lvgl_tft/ili9488.h new file mode 100644 index 0000000..45a7045 --- /dev/null +++ b/lvgl_tft/ili9488.h @@ -0,0 +1,167 @@ +/** + * @file ili9488.h + */ + +#ifndef ILI9844_H +#define ILI9844_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#include + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "../lvgl_helpers.h" + +/********************* + * DEFINES + *********************/ +#define ILI9488_DC CONFIG_LV_DISP_PIN_DC +#define ILI9488_RST CONFIG_LV_DISP_PIN_RST +#define ILI9488_BCKL CONFIG_LV_DISP_PIN_BCKL + +#define ILI9488_ENABLE_BACKLIGHT_CONTROL CONFIG_LV_ENABLE_BACKLIGHT_CONTROL + +#if CONFIG_LV_BACKLIGHT_ACTIVE_LVL + #define ILI9488_BCKL_ACTIVE_LVL 1 +#else + #define ILI9488_BCKL_ACTIVE_LVL 0 +#endif + +/******************* + * ILI9488 REGS +*********************/ + +/* Level 1 Commands (from the display Datasheet) */ +#define ILI9488_CMD_NOP 0x00 +#define ILI9488_CMD_SOFTWARE_RESET 0x01 +#define ILI9488_CMD_READ_DISP_ID 0x04 +#define ILI9488_CMD_READ_ERROR_DSI 0x05 +#define ILI9488_CMD_READ_DISP_STATUS 0x09 +#define ILI9488_CMD_READ_DISP_POWER_MODE 0x0A +#define ILI9488_CMD_READ_DISP_MADCTRL 0x0B +#define ILI9488_CMD_READ_DISP_PIXEL_FORMAT 0x0C +#define ILI9488_CMD_READ_DISP_IMAGE_MODE 0x0D +#define ILI9488_CMD_READ_DISP_SIGNAL_MODE 0x0E +#define ILI9488_CMD_READ_DISP_SELF_DIAGNOSTIC 0x0F +#define ILI9488_CMD_ENTER_SLEEP_MODE 0x10 +#define ILI9488_CMD_SLEEP_OUT 0x11 +#define ILI9488_CMD_PARTIAL_MODE_ON 0x12 +#define ILI9488_CMD_NORMAL_DISP_MODE_ON 0x13 +#define ILI9488_CMD_DISP_INVERSION_OFF 0x20 +#define ILI9488_CMD_DISP_INVERSION_ON 0x21 +#define ILI9488_CMD_PIXEL_OFF 0x22 +#define ILI9488_CMD_PIXEL_ON 0x23 +#define ILI9488_CMD_DISPLAY_OFF 0x28 +#define ILI9488_CMD_DISPLAY_ON 0x29 +#define ILI9488_CMD_COLUMN_ADDRESS_SET 0x2A +#define ILI9488_CMD_PAGE_ADDRESS_SET 0x2B +#define ILI9488_CMD_MEMORY_WRITE 0x2C +#define ILI9488_CMD_MEMORY_READ 0x2E +#define ILI9488_CMD_PARTIAL_AREA 0x30 +#define ILI9488_CMD_VERT_SCROLL_DEFINITION 0x33 +#define ILI9488_CMD_TEARING_EFFECT_LINE_OFF 0x34 +#define ILI9488_CMD_TEARING_EFFECT_LINE_ON 0x35 +#define ILI9488_CMD_MEMORY_ACCESS_CONTROL 0x36 +#define ILI9488_CMD_VERT_SCROLL_START_ADDRESS 0x37 +#define ILI9488_CMD_IDLE_MODE_OFF 0x38 +#define ILI9488_CMD_IDLE_MODE_ON 0x39 +#define ILI9488_CMD_COLMOD_PIXEL_FORMAT_SET 0x3A +#define ILI9488_CMD_WRITE_MEMORY_CONTINUE 0x3C +#define ILI9488_CMD_READ_MEMORY_CONTINUE 0x3E +#define ILI9488_CMD_SET_TEAR_SCANLINE 0x44 +#define ILI9488_CMD_GET_SCANLINE 0x45 +#define ILI9488_CMD_WRITE_DISPLAY_BRIGHTNESS 0x51 +#define ILI9488_CMD_READ_DISPLAY_BRIGHTNESS 0x52 +#define ILI9488_CMD_WRITE_CTRL_DISPLAY 0x53 +#define ILI9488_CMD_READ_CTRL_DISPLAY 0x54 +#define ILI9488_CMD_WRITE_CONTENT_ADAPT_BRIGHTNESS 0x55 +#define ILI9488_CMD_READ_CONTENT_ADAPT_BRIGHTNESS 0x56 +#define ILI9488_CMD_WRITE_MIN_CAB_LEVEL 0x5E +#define ILI9488_CMD_READ_MIN_CAB_LEVEL 0x5F +#define ILI9488_CMD_READ_ABC_SELF_DIAG_RES 0x68 +#define ILI9488_CMD_READ_ID1 0xDA +#define ILI9488_CMD_READ_ID2 0xDB +#define ILI9488_CMD_READ_ID3 0xDC + +/* Level 2 Commands (from the display Datasheet) */ +#define ILI9488_CMD_INTERFACE_MODE_CONTROL 0xB0 +#define ILI9488_CMD_FRAME_RATE_CONTROL_NORMAL 0xB1 +#define ILI9488_CMD_FRAME_RATE_CONTROL_IDLE_8COLOR 0xB2 +#define ILI9488_CMD_FRAME_RATE_CONTROL_PARTIAL 0xB3 +#define ILI9488_CMD_DISPLAY_INVERSION_CONTROL 0xB4 +#define ILI9488_CMD_BLANKING_PORCH_CONTROL 0xB5 +#define ILI9488_CMD_DISPLAY_FUNCTION_CONTROL 0xB6 +#define ILI9488_CMD_ENTRY_MODE_SET 0xB7 +#define ILI9488_CMD_BACKLIGHT_CONTROL_1 0xB9 +#define ILI9488_CMD_BACKLIGHT_CONTROL_2 0xBA +#define ILI9488_CMD_HS_LANES_CONTROL 0xBE +#define ILI9488_CMD_POWER_CONTROL_1 0xC0 +#define ILI9488_CMD_POWER_CONTROL_2 0xC1 +#define ILI9488_CMD_POWER_CONTROL_NORMAL_3 0xC2 +#define ILI9488_CMD_POWER_CONTROL_IDEL_4 0xC3 +#define ILI9488_CMD_POWER_CONTROL_PARTIAL_5 0xC4 +#define ILI9488_CMD_VCOM_CONTROL_1 0xC5 +#define ILI9488_CMD_CABC_CONTROL_1 0xC6 +#define ILI9488_CMD_CABC_CONTROL_2 0xC8 +#define ILI9488_CMD_CABC_CONTROL_3 0xC9 +#define ILI9488_CMD_CABC_CONTROL_4 0xCA +#define ILI9488_CMD_CABC_CONTROL_5 0xCB +#define ILI9488_CMD_CABC_CONTROL_6 0xCC +#define ILI9488_CMD_CABC_CONTROL_7 0xCD +#define ILI9488_CMD_CABC_CONTROL_8 0xCE +#define ILI9488_CMD_CABC_CONTROL_9 0xCF +#define ILI9488_CMD_NVMEM_WRITE 0xD0 +#define ILI9488_CMD_NVMEM_PROTECTION_KEY 0xD1 +#define ILI9488_CMD_NVMEM_STATUS_READ 0xD2 +#define ILI9488_CMD_READ_ID4 0xD3 +#define ILI9488_CMD_ADJUST_CONTROL_1 0xD7 +#define ILI9488_CMD_READ_ID_VERSION 0xD8 +#define ILI9488_CMD_POSITIVE_GAMMA_CORRECTION 0xE0 +#define ILI9488_CMD_NEGATIVE_GAMMA_CORRECTION 0xE1 +#define ILI9488_CMD_DIGITAL_GAMMA_CONTROL_1 0xE2 +#define ILI9488_CMD_DIGITAL_GAMMA_CONTROL_2 0xE3 +#define ILI9488_CMD_SET_IMAGE_FUNCTION 0xE9 +#define ILI9488_CMD_ADJUST_CONTROL_2 0xF2 +#define ILI9488_CMD_ADJUST_CONTROL_3 0xF7 +#define ILI9488_CMD_ADJUST_CONTROL_4 0xF8 +#define ILI9488_CMD_ADJUST_CONTROL_5 0xF9 +#define ILI9488_CMD_SPI_READ_SETTINGS 0xFB +#define ILI9488_CMD_ADJUST_CONTROL_6 0xFC +#define ILI9488_CMD_ADJUST_CONTROL_7 0xFF + +/********************** + * TYPEDEFS + **********************/ +typedef struct { + uint8_t red; + uint8_t green; + uint8_t blue; +} lv_color_custom_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void ili9488_init(void); +void ili9488_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); +void ili9488_enable_backlight(bool backlight); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*ILI9488_H*/ diff --git a/lvgl_tft/jd79653a.c b/lvgl_tft/jd79653a.c new file mode 100644 index 0000000..e39e747 --- /dev/null +++ b/lvgl_tft/jd79653a.c @@ -0,0 +1,482 @@ +/** +@file jd79653a.c +@brief GoodDisplay GDEW0154M09 e-paper display w/ FitiPower JD79653A +@version 1.0 +@date 2020-08-28 +@author Jackson Ming Hu + + +@section LICENSE + +MIT License + +Copyright (c) 2020 Jackson Ming Hu + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include +#include +#include +#include + +#include "disp_spi.h" +#include "jd79653a.h" + +#define TAG "lv_jd79653a" + +#define PIN_DC CONFIG_LV_DISP_PIN_DC +#define PIN_DC_BIT ((1ULL << (uint8_t)(CONFIG_LV_DISP_PIN_DC))) +#define PIN_RST CONFIG_LV_DISP_PIN_RST +#define PIN_RST_BIT ((1ULL << (uint8_t)(CONFIG_LV_DISP_PIN_RST))) +#define PIN_BUSY CONFIG_LV_DISP_PIN_BUSY +#define PIN_BUSY_BIT ((1ULL << (uint8_t)(CONFIG_LV_DISP_PIN_BUSY))) +#define EVT_BUSY (1UL << 0UL) +#define EPD_WIDTH CONFIG_LV_DISPLAY_WIDTH +#define EPD_HEIGHT CONFIG_LV_DISPLAY_HEIGHT +#define EPD_ROW_LEN (EPD_HEIGHT / 8u) +#define EPD_PARTIAL_CNT 5; + +#define BIT_SET(a, b) ((a) |= (1U << (b))) +#define BIT_CLEAR(a, b) ((a) &= ~(1U << (b))) + +static uint8_t partial_counter = 0; + +typedef struct +{ + uint8_t cmd; + uint8_t data[3]; + size_t len; +} jd79653a_seq_t; + +#define EPD_SEQ_LEN(x) ((sizeof(x) / sizeof(jd79653a_seq_t))) + +static const uint8_t lut_vcom_dc1[] = { + 0x01, 0x04, 0x04, 0x03, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + +static const uint8_t lut_ww1[] = { + 0x01, 0x04, 0x04, 0x03, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + +static const uint8_t lut_bw1[] = { + 0x01, 0x84, 0x84, 0x83, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + +static const uint8_t lut_wb1[] = { + 0x01, 0x44, 0x44, 0x43, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + +static const uint8_t lut_bb1[] = { + 0x01, 0x04, 0x04, 0x03, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + +static const jd79653a_seq_t init_seq[] = { +#if defined (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED) + {0x00, {0xd3, 0x0e}, 2}, // Panel settings +#elif defined(CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT) + {0x00, {0xdf, 0x0e}, 2}, // Panel settings +#else +#error "Unsupported orientation - only portrait modes are supported for now" +#endif + {0x4d, {0x55}, 1}, // Undocumented secret from demo code + {0xaa, {0x0f}, 1}, // Undocumented secret from demo code + {0xe9, {0x02}, 1}, // Undocumented secret from demo code + {0xb6, {0x11}, 1}, // Undocumented secret from demo code + {0xf3, {0x0a}, 1}, // Undocumented secret from demo code + {0x61, {0xc8, 0x00, 0xc8}, 3}, // Resolution settings + {0x60, {0x00}, 1}, // TCON + {0x50, {0x97}, 1}, // VCOM sequence + {0xe3, {0x00}, 1}, // Power saving settings +}; + +static const jd79653a_seq_t power_on_seq[] = { + {0x50, {0x97}, 1}, // VCOM sequence + {0x04, {}, 0}, // Power on +}; + + +static const jd79653a_seq_t power_off_seq[] = { + {0x50, {0xf7}, 1}, // VCOM sequence + {0x02, {}, 0}, // Power off +}; + +static EventGroupHandle_t jd79653a_evts = NULL; + +static void IRAM_ATTR jd79653a_busy_intr(void *arg) +{ + BaseType_t xResult; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xResult = xEventGroupSetBitsFromISR(jd79653a_evts, EVT_BUSY, &xHigherPriorityTaskWoken); + if (xResult == pdPASS) { + portYIELD_FROM_ISR(); + } +} + +static void jd79653a_spi_send_cmd(uint8_t cmd) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(PIN_DC, 0); // DC = 0 for command + disp_spi_send_data(&cmd, 1); +} + +static void jd79653a_spi_send_data(uint8_t *data, size_t len) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(PIN_DC, 1); // DC = 1 for data + disp_spi_send_data(data, len); +} + +static void jd79653a_spi_send_fb(uint8_t *data, size_t len) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(PIN_DC, 1); // DC = 1 for data + disp_spi_send_colors(data, len); +} + +static void jd79653a_spi_send_seq(const jd79653a_seq_t *seq, size_t len) +{ + ESP_LOGD(TAG, "Writing cmd/data sequence, count %u", len); + + if (!seq || len < 1) return; + for (size_t cmd_idx = 0; cmd_idx < len; cmd_idx++) { + jd79653a_spi_send_cmd(seq[cmd_idx].cmd); + if (seq[cmd_idx].len > 0) { + jd79653a_spi_send_data((uint8_t *) seq[cmd_idx].data, seq[cmd_idx].len); + } + } +} + +static esp_err_t jd79653a_wait_busy(uint32_t timeout_ms) +{ + uint32_t wait_ticks = (timeout_ms == 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms)); + EventBits_t bits = xEventGroupWaitBits(jd79653a_evts, + EVT_BUSY, // Wait for busy bit + pdTRUE, pdTRUE, // Clear on exit, wait for all + wait_ticks); // Timeout + + return ((bits & EVT_BUSY) != 0) ? ESP_OK : ESP_ERR_TIMEOUT; +} + +static void jd79653a_power_on() +{ + jd79653a_spi_send_seq(power_on_seq, EPD_SEQ_LEN(power_on_seq)); + vTaskDelay(pdMS_TO_TICKS(10)); + jd79653a_wait_busy(0); +} + +static void jd79653a_power_off() +{ + jd79653a_spi_send_seq(power_off_seq, EPD_SEQ_LEN(power_off_seq)); + vTaskDelay(pdMS_TO_TICKS(10)); + jd79653a_wait_busy(0); +} + +static void jd79653a_load_partial_lut() +{ + jd79653a_spi_send_cmd(0x20); // LUT VCOM register + jd79653a_spi_send_data((uint8_t *)lut_vcom_dc1, sizeof(lut_vcom_dc1)); + + jd79653a_spi_send_cmd(0x21); // LUT White-to-White + jd79653a_spi_send_data((uint8_t *)lut_ww1, sizeof(lut_ww1)); + + jd79653a_spi_send_cmd(0x22); // LUT Black-to-White + jd79653a_spi_send_data((uint8_t *)lut_bw1, sizeof(lut_bw1)); + + jd79653a_spi_send_cmd(0x23); // LUT White-to-Black + jd79653a_spi_send_data((uint8_t *)lut_wb1,sizeof(lut_wb1)); + + jd79653a_spi_send_cmd(0x24); // LUT Black-to-Black + jd79653a_spi_send_data((uint8_t *)lut_bb1, sizeof(lut_bb1)); +} + +static void jd79653a_partial_in() +{ + ESP_LOGD(TAG, "Partial in!"); + + // Panel setting: accept LUT from registers instead of OTP +#if defined (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED) + uint8_t pst_use_reg_lut[] = { 0xf3, 0x0e }; +#elif defined(CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT) + uint8_t pst_use_reg_lut[] = { 0xff, 0x0e }; +#else +#error "Unsupported orientation - only portrait modes are supported for now" +#endif + jd79653a_spi_send_cmd(0x00); + jd79653a_spi_send_data(pst_use_reg_lut, sizeof(pst_use_reg_lut)); + + // WORKAROUND: need to ignore OLD framebuffer or otherwise partial refresh won't work + uint8_t vcom = 0xb7; + jd79653a_spi_send_cmd(0x50); + jd79653a_spi_send_data(&vcom, 1); + + // Dump LUT in + jd79653a_load_partial_lut(); + + // Go partial! + jd79653a_spi_send_cmd(0x91); +} + +static void jd79653a_partial_out() +{ + ESP_LOGD(TAG, "Partial out!"); + + // Panel setting: use LUT from OTP +#if defined (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED) + uint8_t pst_use_otp_lut[] = { 0xd3, 0x0e }; +#elif defined(CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT) + uint8_t pst_use_otp_lut[] = { 0xdf, 0x0e }; +#else +#error "Unsupported orientation - only portrait modes are supported for now" +#endif + jd79653a_spi_send_cmd(0x00); + jd79653a_spi_send_data(pst_use_otp_lut, sizeof(pst_use_otp_lut)); + + // WORKAROUND: re-enable OLD framebuffer to get a better full refresh + uint8_t vcom = 0x97; + jd79653a_spi_send_cmd(0x50); + jd79653a_spi_send_data(&vcom, 1); + + // Out from partial! + jd79653a_spi_send_cmd(0x92); +} + +static void jd79653a_update_partial(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t *data) +{ + jd79653a_power_on(); + jd79653a_partial_in(); + ESP_LOGD(TAG, "x1: 0x%x, x2: 0x%x, y1: 0x%x, y2: 0x%x", x1, x2, y1, y2); + + size_t len = ((x2 - x1 + 1) * (y2 - y1 + 1)) / 8; + ESP_LOGD(TAG, "Writing PARTIAL LVGL fb with len: %u", len); + + // Set partial window + uint8_t ptl_setting[7] = { x1, x2, 0, y1, 0, y2, 0x01 }; + jd79653a_spi_send_cmd(0x90); + jd79653a_spi_send_data(ptl_setting, sizeof(ptl_setting)); + + uint8_t *data_ptr = data; + + jd79653a_spi_send_cmd(0x13); + for (size_t h_idx = 0; h_idx < EPD_HEIGHT; h_idx++) { + jd79653a_spi_send_data(data_ptr, EPD_ROW_LEN); + data_ptr += EPD_ROW_LEN; + len -= EPD_ROW_LEN; + } + + ESP_LOGD(TAG, "Partial wait start"); + + jd79653a_spi_send_cmd(0x12); + jd79653a_wait_busy(0); + + ESP_LOGD(TAG, "Partial updated"); + jd79653a_partial_out(); + jd79653a_power_off(); +} + +void jd79653a_fb_set_full_color(uint8_t color) +{ + jd79653a_power_on(); + uint8_t old_data[EPD_ROW_LEN]; + memset(old_data, ~(color), EPD_ROW_LEN); + + // Fill OLD data (maybe not necessary) + jd79653a_spi_send_cmd(0x10); + for (size_t idx = 0; idx < EPD_HEIGHT; idx++) { + jd79653a_spi_send_data(old_data, EPD_ROW_LEN); + } + + // Fill NEW data + jd79653a_spi_send_cmd(0x13); + for (size_t h_idx = 0; h_idx < EPD_HEIGHT; h_idx++) { + for (size_t w_idx = 0; w_idx < EPD_ROW_LEN; w_idx++) { + jd79653a_spi_send_data(&color, sizeof(color)); + } + } + + jd79653a_spi_send_cmd(0x12); // Issue refresh command + vTaskDelay(pdMS_TO_TICKS(100)); + jd79653a_wait_busy(0); + + jd79653a_power_off(); +} + +void jd79653a_fb_full_update(uint8_t *data, size_t len) +{ + jd79653a_power_on(); + ESP_LOGD(TAG, "Performing full update, len: %u", len); + + uint8_t *data_ptr = data; + + // Fill OLD data (maybe not necessary) + uint8_t old_data[EPD_ROW_LEN] = { 0 }; + jd79653a_spi_send_cmd(0x10); + for (size_t idx = 0; idx < EPD_HEIGHT; idx++) { + jd79653a_spi_send_data(old_data, EPD_ROW_LEN); + } + + // Fill NEW data + jd79653a_spi_send_cmd(0x13); + for (size_t h_idx = 0; h_idx < EPD_HEIGHT; h_idx++) { + jd79653a_spi_send_data(data_ptr, EPD_ROW_LEN); + data_ptr += EPD_ROW_LEN; + len -= EPD_ROW_LEN; + } + + ESP_LOGD(TAG, "Rest len: %u", len); + + jd79653a_spi_send_cmd(0x12); // Issue refresh command + vTaskDelay(pdMS_TO_TICKS(100)); + jd79653a_wait_busy(0); + + jd79653a_power_off(); +} + +void jd79653a_lv_set_fb_cb(struct _disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa) +{ + uint16_t byte_index = (x >> 3u) + (y * EPD_ROW_LEN); + uint8_t bit_index = x & 0x07u; + + if (color.full) { + BIT_SET(buf[byte_index], 7 - bit_index); + } else { + BIT_CLEAR(buf[byte_index], 7 - bit_index); + } +} + +void jd79653a_lv_rounder_cb(struct _disp_drv_t *disp_drv, lv_area_t *area) +{ + // Always send full framebuffer if it's not in partial mode + area->x1 = 0; + area->y1 = 0; + area->x2 = EPD_WIDTH - 1; + area->y2 = EPD_HEIGHT - 1; +} + +void jd79653a_lv_fb_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + size_t len = ((area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1)) / 8; + + ESP_LOGD(TAG, "x1: 0x%x, x2: 0x%x, y1: 0x%x, y2: 0x%x", area->x1, area->x2, area->y1, area->y2); + ESP_LOGD(TAG, "Writing LVGL fb with len: %u, partial counter: %u", len, partial_counter); + + uint8_t *buf = (uint8_t *) color_map; + + if (partial_counter == 0) { + ESP_LOGD(TAG, "Refreshing in FULL"); + jd79653a_fb_full_update(buf, ((EPD_HEIGHT * EPD_WIDTH) / 8)); + partial_counter = EPD_PARTIAL_CNT; // Reset partial counter here + } else { + jd79653a_update_partial(area->x1, area->y1, area->x2, area->y2, buf); + partial_counter -= 1; // ...or otherwise, decrease 1 + } + + lv_disp_flush_ready(drv); +} + +void jd79653a_deep_sleep() +{ + jd79653a_spi_send_seq(power_off_seq, EPD_SEQ_LEN(power_off_seq)); + jd79653a_wait_busy(1000); + + uint8_t check_code = 0xa5; + jd79653a_spi_send_cmd(0x07); + jd79653a_spi_send_data(&check_code, sizeof(check_code)); +} + +void jd79653a_init() +{ + // Initialise event group + jd79653a_evts = xEventGroupCreate(); + if (!jd79653a_evts) { + ESP_LOGE(TAG, "Failed when initialising event group!"); + return; + } + + // Setup output pins, output (PP) + gpio_config_t out_io_conf = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = PIN_DC_BIT | PIN_RST_BIT, + .pull_down_en = 0, + .pull_up_en = 0, + }; + ESP_ERROR_CHECK(gpio_config(&out_io_conf)); + + // Setup input pin, pull-up, input + gpio_config_t in_io_conf = { + .intr_type = GPIO_INTR_POSEDGE, + .mode = GPIO_MODE_INPUT, + .pin_bit_mask = PIN_BUSY_BIT, + .pull_down_en = 0, + .pull_up_en = 1, + }; + ESP_ERROR_CHECK(gpio_config(&in_io_conf)); + gpio_install_isr_service(0); + gpio_isr_handler_add(PIN_BUSY, jd79653a_busy_intr, (void *) PIN_BUSY); + + // Hardware reset + gpio_set_level(PIN_RST, 0); + vTaskDelay(pdMS_TO_TICKS(15)); // At least 10ms, leave 15ms for now just in case... + gpio_set_level(PIN_RST, 1); + vTaskDelay(pdMS_TO_TICKS(120)); + + // Dump in initialise sequence + jd79653a_spi_send_seq(init_seq, EPD_SEQ_LEN(init_seq)); + ESP_LOGI(TAG, "Panel init sequence sent"); + + // Check BUSY status here + jd79653a_wait_busy(0); + + ESP_LOGI(TAG, "Panel is up!"); +} diff --git a/lvgl_tft/jd79653a.h b/lvgl_tft/jd79653a.h new file mode 100644 index 0000000..6a2065a --- /dev/null +++ b/lvgl_tft/jd79653a.h @@ -0,0 +1,36 @@ +/** + * @file il3820.h + * + */ + +#ifndef JD79653A_H +#define JD79653A_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +void jd79653a_init(); +void jd79653a_deep_sleep(); + +void jd79653a_lv_set_fb_cb(struct _disp_drv_t * disp_drv, uint8_t* buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa); +void jd79653a_lv_rounder_cb(struct _disp_drv_t * disp_drv, lv_area_t *area); +void jd79653a_lv_fb_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map); + +void jd79653a_fb_set_full_color(uint8_t color); +void jd79653a_fb_full_update(uint8_t *data, size_t len); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // JD79653A_H diff --git a/lvgl_tft/ra8875.c b/lvgl_tft/ra8875.c new file mode 100644 index 0000000..04638d8 --- /dev/null +++ b/lvgl_tft/ra8875.c @@ -0,0 +1,365 @@ +/** + * @file ra8875.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "ra8875.h" +#include "disp_spi.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +/********************* + * DEFINES + *********************/ +#define DEBUG false +#define TAG "RA8875" + +#define DIV_ROUND_UP(n, d) (((n)+(d)-1)/(d)) + +#define SPI_CLOCK_SPEED_SLOW_HZ 1000000 + +#define RA8875_MODE_DATA_WRITE (0x00) +#define RA8875_MODE_DATA_READ (0x40) +#define RA8875_MODE_CMD_WRITE (0x80) +#define RA8875_MODE_STATUS_READ (0xC0) + +#if (LV_COLOR_DEPTH == 8) + #define SYSR_VAL (0x00) +#elif (LV_COLOR_DEPTH == 16) + #define SYSR_VAL (0x08) +#else + #error "Unsupported color depth (LV_COLOR_DEPTH)" +#endif +#define BYTES_PER_PIXEL (LV_COLOR_DEPTH / 8) + +#define HDWR_VAL (CONFIG_LV_DISPLAY_WIDTH/8 - 1) +#define VDHR_VAL (CONFIG_LV_DISPLAY_HEIGHT - 1) + +#define VDIR_MASK (1 << 2) +#define HDIR_MASK (1 << 3) + +#if ( CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED || CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED ) + #if CONFIG_LV_INVERT_DISPLAY + #define DPCR_VAL (VDIR_MASK) + #else + #define DPCR_VAL (VDIR_MASK | HDIR_MASK) + #endif +#else + #if CONFIG_LV_INVERT_DISPLAY + #define DPCR_VAL (HDIR_MASK) + #else + #define DPCR_VAL (0x00) + #endif +#endif + +#if CONFIG_LV_DISP_RA8875_PCLK_INVERT + #define PCSR_VAL (0x80 | CONFIG_LV_DISP_RA8875_PCLK_MULTIPLIER) +#else + #define PCSR_VAL (CONFIG_LV_DISP_RA8875_PCLK_MULTIPLIER) +#endif + +// Calculate horizontal display parameters +#if (CONFIG_LV_DISP_RA8875_HORI_NON_DISP_PERIOD >= 260) + #define HNDR_VAL (31) +#else + #define HNDR_VAL ((CONFIG_LV_DISP_RA8875_HORI_NON_DISP_PERIOD-12) / 8) +#endif +#define HNDFT (CONFIG_LV_DISP_RA8875_HORI_NON_DISP_PERIOD-(8*HNDR_VAL)-12) +#if LVGL_DISP_RA8875_DE_POLARITY + #define HNDFTR_VAL (0x80 | HNDFT) +#else + #define HNDFTR_VAL (HNDFT) +#endif +#define HSTR_VAL (CONFIG_LV_DISP_RA8875_HSYNC_START/8 - 1) +#define HPW (CONFIG_LV_DISP_RA8875_HSYNC_PW/8 - 1) +#if LVGL_DISP_RA8875_HSYNC_POLARITY + #define HPWR_VAL (0x80 | HPW) +#else + #define HPWR_VAL (HPW) +#endif + +// Calculate vertical display parameters +#define VNDR_VAL (CONFIG_LV_DISP_RA8875_VERT_NON_DISP_PERIOD - 1) +#define VSTR_VAL (CONFIG_LV_DISP_RA8875_VSYNC_START - 1) +#define VPW (CONFIG_LV_DISP_RA8875_VSYNC_PW - 1) +#if LVGL_DISP_RA8875_VSYNC_POLARITY + #define VPWR_VAL (0x80 | VPW) +#else + #define VPWR_VAL (VPW) +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void ra8875_configure_clocks(bool high_speed); +static void ra8875_set_memory_write_cursor(unsigned int x, unsigned int y); +static void ra8875_set_window(unsigned int xs, unsigned int xe, unsigned int ys, unsigned int ye); +static void ra8875_send_buffer(uint8_t * data, size_t length, bool signal_flush); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void ra8875_init(void) +{ + unsigned int i = 0; + + struct { + uint8_t cmd; // Register address of command + uint8_t data; // Value to write to register + } init_cmds[] = { + {RA8875_REG_SYSR, SYSR_VAL}, // System Configuration Register (SYSR) + {RA8875_REG_HDWR, HDWR_VAL}, // LCD Horizontal Display Width Register (HDWR) + {RA8875_REG_HNDFTR, HNDFTR_VAL}, // Horizontal Non-Display Period Fine Tuning Option Register (HNDFTR) + {RA8875_REG_HNDR, HNDR_VAL}, // Horizontal Non-Display Period Register (HNDR) + {RA8875_REG_HSTR, HSTR_VAL}, // HSYNC Start Position Register (HSTR) + {RA8875_REG_HPWR, HPWR_VAL}, // HSYNC Pulse Width Register (HPWR) + {RA8875_REG_VDHR0, VDHR_VAL & 0x0FF}, // LCD Vertical Display Height Register (VDHR0) + {RA8875_REG_VDHR1, VDHR_VAL >> 8}, // LCD Vertical Display Height Register0 (VDHR1) + {RA8875_REG_VNDR0, VNDR_VAL & 0x0FF}, // LCD Vertical Non-Display Period Register (VNDR0) + {RA8875_REG_VNDR1, VNDR_VAL >> 8}, // LCD Vertical Non-Display Period Register (VNDR1) + {RA8875_REG_VSTR0, VSTR_VAL & 0x0FF}, // VSYNC Start Position Register (VSTR0) + {RA8875_REG_VSTR1, VSTR_VAL >> 8}, // VSYNC Start Position Register (VSTR1) + {RA8875_REG_VPWR, VPWR_VAL}, // VSYNC Pulse Width Register (VPWR) + {RA8875_REG_DPCR, DPCR_VAL}, // Display Configuration Register (DPCR) + {RA8875_REG_MWCR0, 0x00}, // Memory Write Control Register 0 (MWCR0) + {RA8875_REG_MWCR1, 0x00}, // Memory Write Control Register 1 (MWCR1) + {RA8875_REG_LTPR0, 0x00}, // Layer Transparency Register0 (LTPR0) + {RA8875_REG_LTPR1, 0x00}, // Layer Transparency Register1 (LTPR1) + }; + #define INIT_CMDS_SIZE (sizeof(init_cmds)/sizeof(init_cmds[0])) + + ESP_LOGI(TAG, "Initializing RA8875..."); + +#if (CONFIG_LV_DISP_PIN_BCKL == 15) + gpio_config_t io_conf; + io_conf.intr_type = GPIO_PIN_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = GPIO_SEL_15; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + gpio_config(&io_conf); +#endif + + // Initialize non-SPI GPIOs + gpio_pad_select_gpio(RA8875_RST); + gpio_set_direction(RA8875_RST, GPIO_MODE_OUTPUT); +#ifdef CONFIG_LV_DISP_PIN_BCKL + gpio_pad_select_gpio(CONFIG_LV_DISP_PIN_BCKL); + gpio_set_direction(CONFIG_LV_DISP_PIN_BCKL, GPIO_MODE_OUTPUT); +#endif + + // Reset the RA8875 + gpio_set_level(RA8875_RST, 0); + vTaskDelay(DIV_ROUND_UP(100, portTICK_RATE_MS)); + gpio_set_level(RA8875_RST, 1); + vTaskDelay(DIV_ROUND_UP(100, portTICK_RATE_MS)); + + // Initalize RA8875 clocks (SPI must be decelerated before initializing clocks) + disp_spi_change_device_speed(SPI_CLOCK_SPEED_SLOW_HZ); + ra8875_configure_clocks(true); + disp_spi_change_device_speed(-1); + + // Send all the commands + for (i = 0; i < INIT_CMDS_SIZE; i++) { + ra8875_write_cmd(init_cmds[i].cmd, init_cmds[i].data); + } + + // Perform a memory clear (wait maximum of 100 ticks) + ra8875_write_cmd(RA8875_REG_MCLR, 0x80); + for(i = 100; i != 0; i--) { + if ((ra8875_read_cmd(RA8875_REG_MCLR) & 0x80) == 0x00) { + break; + } + vTaskDelay(1); + } + if (i == 0) { + ESP_LOGW(TAG, "WARNING: Memory clear timed out; RA8875 may be unresponsive."); + } + + // Enable the display and backlight + ra8875_enable_display(true); + ra8875_enable_backlight(true); +} + +void ra8875_enable_backlight(bool backlight) +{ +#if CONFIG_LV_ENABLE_BACKLIGHT_CONTROL + ESP_LOGI(TAG, "%s backlight.", backlight ? "Enabling" : "Disabling"); + uint32_t tmp = 0; + + #if CONFIG_LV_BACKLIGHT_ACTIVE_LVL + tmp = backlight ? 1 : 0; + #else + tmp = backlight ? 0 : 1; + #endif + +#ifdef CONFIG_LV_DISP_PIN_BCKL + gpio_set_level(CONFIG_LV_DISP_PIN_BCKL, tmp); +#endif + +#endif +} + +void ra8875_enable_display(bool enable) +{ + ESP_LOGI(TAG, "%s display.", enable ? "Enabling" : "Disabling"); + uint8_t val = enable ? (0x80) : (0x00); + ra8875_write_cmd(RA8875_REG_PWRR, val); // Power and Display Control Register (PWRR) +} + + +void ra8875_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) +{ + static lv_coord_t x1 = LV_COORD_MIN; + static lv_coord_t x2 = LV_COORD_MIN; + static lv_coord_t x = LV_COORD_MIN; + static lv_coord_t y = LV_COORD_MIN; + + size_t linelen = (area->x2 - area->x1 + 1); + uint8_t * buffer = (uint8_t*)color_map; + +#if DEBUG + ESP_LOGI(TAG, "flush: %d,%d at %d,%d", area->x1, area->x2, area->y1, area->y2 ); +#endif + + // Get lock + disp_spi_acquire(); + + // Set window if needed + if ((x1 != area->x1) || (x2 != area->x2)) { +#if DEBUG + ESP_LOGI(TAG, "flush: set window (x1,x2): %d,%d -> %d,%d", x1, x2, area->x1, area->x2); +#endif + ra8875_set_window(area->x1, area->x2, 0, CONFIG_LV_DISPLAY_HEIGHT-1); + x1 = area->x1; + x2 = area->x2; + } + + // Set cursor if needed + if ((x != area->x1) || (y != area->y1)) { +#if DEBUG + ESP_LOGI(TAG, "flush: set cursor (x,y): %d,%d -> %d,%d", x, y, area->x1, area->y1); +#endif + ra8875_set_memory_write_cursor(area->x1, area->y1); + x = area->x1; + } + + // Update to future cursor location + y = area->y2 + 1; + if (y >= CONFIG_LV_DISPLAY_HEIGHT) { + y = 0; + } + + // Write data + ra8875_send_buffer(buffer, (area->y2 - area->y1 + 1)*BYTES_PER_PIXEL*linelen, true); + + // Release lock + disp_spi_release(); +} + +void ra8875_sleep_in(void) +{ + disp_spi_change_device_speed(SPI_CLOCK_SPEED_SLOW_HZ); + + ra8875_configure_clocks(false); + + ra8875_write_cmd(RA8875_REG_PWRR, 0x00); // Power and Display Control Register (PWRR) + vTaskDelay(DIV_ROUND_UP(20, portTICK_RATE_MS)); + ra8875_write_cmd(RA8875_REG_PWRR, 0x02); // Power and Display Control Register (PWRR) +} + +void ra8875_sleep_out(void) +{ + ra8875_write_cmd(RA8875_REG_PWRR, 0x00); // Power and Display Control Register (PWRR) + vTaskDelay(DIV_ROUND_UP(20, portTICK_RATE_MS)); + + ra8875_configure_clocks(true); + + disp_spi_change_device_speed(-1); + + ra8875_write_cmd(RA8875_REG_PWRR, 0x80); // Power and Display Control Register (PWRR) + vTaskDelay(DIV_ROUND_UP(20, portTICK_RATE_MS)); +} + +uint8_t ra8875_read_cmd(uint8_t cmd) +{ + uint8_t buf[4] = {RA8875_MODE_CMD_WRITE, cmd, RA8875_MODE_DATA_READ, 0x00}; + disp_spi_transaction(buf, sizeof(buf), (disp_spi_send_flag_t)(DISP_SPI_RECEIVE | DISP_SPI_SEND_POLLING), buf, 0, 0); + return buf[3]; +} + +void ra8875_write_cmd(uint8_t cmd, uint8_t data) +{ + uint8_t buf[4] = {RA8875_MODE_CMD_WRITE, cmd, RA8875_MODE_DATA_WRITE, data}; + disp_spi_send_data(buf, sizeof(buf)); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +void ra8875_configure_clocks(bool high_speed) +{ + uint8_t val; + + val = high_speed ? ((CONFIG_LV_DISP_RA8875_PLLDIVM << 7) | CONFIG_LV_DISP_RA8875_PLLDIVN) : 0x07; + ra8875_write_cmd(RA8875_REG_PLLC1, val); // PLL Control Register 1 (PLLC1) + vTaskDelay(1); + + val = high_speed ? CONFIG_LV_DISP_RA8875_PLLDIVK : 0x03; + ra8875_write_cmd(RA8875_REG_PLLC2, val); // PLL Control Register 2 (PLLC2) + vTaskDelay(1); + + ra8875_write_cmd(RA8875_REG_PCSR, PCSR_VAL); // Pixel Clock Setting Register (PCSR) + vTaskDelay(DIV_ROUND_UP(20, portTICK_RATE_MS)); +} + +static void ra8875_set_window(unsigned int xs, unsigned int xe, unsigned int ys, unsigned int ye) +{ + ra8875_write_cmd(RA8875_REG_HSAW0, (uint8_t)(xs & 0x0FF)); // Horizontal Start Point 0 of Active Window (HSAW0) + ra8875_write_cmd(RA8875_REG_HSAW1, (uint8_t)(xs >> 8)); // Horizontal Start Point 1 of Active Window (HSAW1) + ra8875_write_cmd(RA8875_REG_VSAW0, (uint8_t)(ys & 0x0FF)); // Vertical Start Point 0 of Active Window (VSAW0) + ra8875_write_cmd(RA8875_REG_VSAW1, (uint8_t)(ys >> 8)); // Vertical Start Point 1 of Active Window (VSAW1) + ra8875_write_cmd(RA8875_REG_HEAW0, (uint8_t)(xe & 0x0FF)); // Horizontal End Point 0 of Active Window (HEAW0) + ra8875_write_cmd(RA8875_REG_HEAW1, (uint8_t)(xe >> 8)); // Horizontal End Point 1 of Active Window (HEAW1) + ra8875_write_cmd(RA8875_REG_VEAW0, (uint8_t)(ye & 0x0FF)); // Vertical End Point of Active Window 0 (VEAW0) + ra8875_write_cmd(RA8875_REG_VEAW1, (uint8_t)(ye >> 8)); // Vertical End Point of Active Window 1 (VEAW1) +} + +static void ra8875_set_memory_write_cursor(unsigned int x, unsigned int y) +{ + ra8875_write_cmd(RA8875_REG_CURH0, (uint8_t)(x & 0x0FF)); // Memory Write Cursor Horizontal Position Register 0 (CURH0) + ra8875_write_cmd(RA8875_REG_CURH1, (uint8_t)(x >> 8)); // Memory Write Cursor Horizontal Position Register 1 (CURH1) + ra8875_write_cmd(RA8875_REG_CURV0, (uint8_t)(y & 0x0FF)); // Memory Write Cursor Vertical Position Register 0 (CURV0) + ra8875_write_cmd(RA8875_REG_CURV1, (uint8_t)(y >> 8)); // Memory Write Cursor Vertical Position Register 1 (CURV1) +} + +static void ra8875_send_buffer(uint8_t * data, size_t length, bool signal_flush) +{ + disp_spi_send_flag_t flags = DISP_SPI_SEND_QUEUED | DISP_SPI_ADDRESS_24; + if (signal_flush) { + flags |= DISP_SPI_SIGNAL_FLUSH; + } + const uint64_t prefix = (RA8875_MODE_CMD_WRITE << 16) // Command write mode + | (RA8875_REG_MRWC << 8) // Memory Read/Write Command (MRWC) + | (RA8875_MODE_DATA_WRITE); // Data write mode + disp_spi_transaction(data, length, flags, NULL, prefix, 0); +} diff --git a/lvgl_tft/ra8875.h b/lvgl_tft/ra8875.h new file mode 100644 index 0000000..a6d1fe5 --- /dev/null +++ b/lvgl_tft/ra8875.h @@ -0,0 +1,118 @@ +/** + * @file ra8875.h + * + */ + +#ifndef RA8875_H +#define RA8875_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ +#define RA8875_RST CONFIG_LV_DISP_PIN_RST + +// System & Configuration Registers +#define RA8875_REG_PWRR (0x01) // Power and Display Control Register (PWRR) +#define RA8875_REG_MRWC (0x02) // Memory Read/Write Command (MRWC) +#define RA8875_REG_PCSR (0x04) // Pixel Clock Setting Register (PCSR) +#define RA8875_REG_SYSR (0x10) // System Configuration Register (SYSR) +#define RA8875_REG_HDWR (0x14) // LCD Horizontal Display Width Register (HDWR) +#define RA8875_REG_HNDFTR (0x15) // Horizontal Non-Display Period Fine Tuning Option Register (HNDFTR) +#define RA8875_REG_HNDR (0x16) // LCD Horizontal Non-Display Period Register (HNDR) +#define RA8875_REG_HSTR (0x17) // HSYNC Start Position Register (HSTR) +#define RA8875_REG_HPWR (0x18) // HSYNC Pulse Width Register (HPWR) +#define RA8875_REG_VDHR0 (0x19) // LCD Vertical Display Height Register (VDHR0) +#define RA8875_REG_VDHR1 (0x1A) // LCD Vertical Display Height Register (VDHR1) +#define RA8875_REG_VNDR0 (0x1B) // LCD Vertical Non-Display Period Register (VNDR0) +#define RA8875_REG_VNDR1 (0x1C) // LCD Vertical Non-Display Period Register (VNDR1) +#define RA8875_REG_VSTR0 (0x1D) // VSYNC Start Position Register (VSTR0) +#define RA8875_REG_VSTR1 (0x1E) // VSYNC Start Position Register (VSTR1) +#define RA8875_REG_VPWR (0x1F) // VSYNC Pulse Width Register (VPWR) + +// LCD Display Control Registers +#define RA8875_REG_DPCR (0x20) // Display Configuration Register (DPCR) + +// Active Window & Scroll Window Setting Registers +#define RA8875_REG_HSAW0 (0x30) // Horizontal Start Point 0 of Active Window (HSAW0) +#define RA8875_REG_HSAW1 (0x31) // Horizontal Start Point 1 of Active Window (HSAW1) +#define RA8875_REG_VSAW0 (0x32) // Vertical Start Point 0 of Active Window (VSAW0) +#define RA8875_REG_VSAW1 (0x33) // Vertical Start Point 1 of Active Window (VSAW1) +#define RA8875_REG_HEAW0 (0x34) // Horizontal End Point 0 of Active Window (HEAW0) +#define RA8875_REG_HEAW1 (0x35) // Horizontal End Point 1 of Active Window (HEAW1) +#define RA8875_REG_VEAW0 (0x36) // Vertical End Point 0 of Active Window (VEAW0) +#define RA8875_REG_VEAW1 (0x37) // Vertical End Point 1 of Active Window (VEAW1) + +// Cursor Setting Registers +#define RA8875_REG_MWCR0 (0x40) // Memory Write Control Register 0 (MWCR0) +#define RA8875_REG_MWCR1 (0x41) // Memory Write Control Register 1 (MWCR1) +#define RA8875_REG_CURH0 (0x46) // Memory Write Cursor Horizontal Position Register 0 (CURH0) +#define RA8875_REG_CURH1 (0x47) // Memory Write Cursor Horizontal Position Register 1 (CURH1) +#define RA8875_REG_CURV0 (0x48) // Memory Write Cursor Vertical Position Register 0 (CURV0) +#define RA8875_REG_CURV1 (0x49) // Memory Write Cursor Vertical Position Register 1 (CURV1) + +// Block Transfer Engine(BTE) Control Registers +#define RA8875_REG_LTPR0 (0x52) // Layer Transparency Register 0 (LTPR0) +#define RA8875_REG_LTPR1 (0x53) // Layer Transparency Register 1 (LTPR1) + +// Touch Panel Control Registers +#define RA8875_REG_TPCR0 (0x70) // Touch Panel Control Register 0 (TPCR0) +#define RA8875_REG_TPCR1 (0x71) // Touch Panel Control Register 1 (TPCR1) +#define RA8875_REG_TPXH (0x72) // Touch Panel X High Byte Data Register (TPXH) +#define RA8875_REG_TPYH (0x73) // Touch Panel Y High Byte Data Register (TPYH) +#define RA8875_REG_TPXYL (0x74) // Touch Panel X/Y Low Byte Data Register (TPXYL) + +// PLL Setting Registers +#define RA8875_REG_PLLC1 (0x88) // PLL Control Register 1 (PLLC1) +#define RA8875_REG_PLLC2 (0x89) // PLL Control Register 2 (PLLC2) + +// Memory Clear Register +#define RA8875_REG_MCLR (0x8E) // Memory Clear Control Register (MCLR) + +// Interrupt Control Registers +#define RA8875_REG_INTC1 (0xF0) // Interrupt Control Register1 (INTC1) +#define RA8875_REG_INTC2 (0xF1) // Interrupt Control Register1 (INTC2) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void ra8875_init(void); +void ra8875_enable_backlight(bool backlight); +void ra8875_enable_display(bool enable); +void ra8875_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); + +void ra8875_sleep_in(void); +void ra8875_sleep_out(void); + +uint8_t ra8875_read_cmd(uint8_t cmd); +void ra8875_write_cmd(uint8_t cmd, uint8_t data); + +/********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*RA8875_H*/ diff --git a/lvgl_tft/sh1107.c b/lvgl_tft/sh1107.c new file mode 100644 index 0000000..90388dc --- /dev/null +++ b/lvgl_tft/sh1107.c @@ -0,0 +1,218 @@ +/** + * @file sh1107.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "sh1107.h" +#include "disp_spi.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +/********************* + * DEFINES + *********************/ + #define TAG "SH1107" + +/********************** + * TYPEDEFS + **********************/ + +/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} lcd_init_cmd_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void sh1107_send_cmd(uint8_t cmd); +static void sh1107_send_data(void * data, uint16_t length); +static void sh1107_send_color(void * data, uint16_t length); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +#define BIT_SET(a,b) ((a) |= (1U<<(b))) +#define BIT_CLEAR(a,b) ((a) &= ~(1U<<(b))) + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void sh1107_init(void) +{ + // Use Double Bytes Commands if necessary, but not Command+Data + // Initialization taken from https://github.com/nopnop2002/esp-idf-m5stick + lcd_init_cmd_t init_cmds[]={ + {0xAE, {0}, 0}, // Turn display off + {0xDC, {0}, 0}, // Set display start line + {0x00, {0}, 0}, // ...value + {0x81, {0}, 0}, // Set display contrast + {0x2F, {0}, 0}, // ...value + {0x20, {0}, 0}, // Set memory mode + {0xA0, {0}, 0}, // Non-rotated display +#if defined CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE + {0xC8, {0}, 0}, // flipped vertical +#elif defined CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT + {0xC7, {0}, 0}, // flipped vertical +#endif + {0xA8, {0}, 0}, // Set multiplex ratio + {0x7F, {0}, 0}, // ...value + {0xD3, {0}, 0}, // Set display offset to zero + {0x60, {0}, 0}, // ...value + {0xD5, {0}, 0}, // Set display clock divider + {0x51, {0}, 0}, // ...value + {0xD9, {0}, 0}, // Set pre-charge + {0x22, {0}, 0}, // ...value + {0xDB, {0}, 0}, // Set com detect + {0x35, {0}, 0}, // ...value + {0xB0, {0}, 0}, // Set page address + {0xDA, {0}, 0}, // Set com pins + {0x12, {0}, 0}, // ...value + {0xA4, {0}, 0}, // output ram to display +#if defined CONFIG_LV_INVERT_DISPLAY + {0xA7, {0}, 0}, // inverted display +#else + {0xA6, {0}, 0}, // Non-inverted display +#endif + {0xAF, {0}, 0}, // Turn display on + {0, {0}, 0xff}, + }; + + //Initialize non-SPI GPIOs + gpio_pad_select_gpio(SH1107_DC); + gpio_set_direction(SH1107_DC, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(SH1107_RST); + gpio_set_direction(SH1107_RST, GPIO_MODE_OUTPUT); + + //Reset the display + gpio_set_level(SH1107_RST, 0); + vTaskDelay(100 / portTICK_RATE_MS); + gpio_set_level(SH1107_RST, 1); + vTaskDelay(100 / portTICK_RATE_MS); + + //Send all the commands + uint16_t cmd = 0; + while (init_cmds[cmd].databytes!=0xff) { + sh1107_send_cmd(init_cmds[cmd].cmd); + sh1107_send_data(init_cmds[cmd].data, init_cmds[cmd].databytes&0x1F); + if (init_cmds[cmd].databytes & 0x80) { + vTaskDelay(100 / portTICK_RATE_MS); + } + cmd++; + } +} + +void sh1107_set_px_cb(struct _disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa) +{ + /* buf_w will be ignored, the configured CONFIG_LV_DISPLAY_HEIGHT and _WIDTH, + and CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE and _PORTRAIT will be used. */ + uint16_t byte_index = 0; + uint8_t bit_index = 0; + +#if defined CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE + byte_index = y + (( x>>3 ) * CONFIG_LV_DISPLAY_HEIGHT); + bit_index = x & 0x7; +#elif defined CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT + byte_index = x + (( y>>3 ) * CONFIG_LV_DISPLAY_WIDTH); + bit_index = y & 0x7; +#endif + + if ((color.full == 0) && (LV_OPA_TRANSP != opa)) { + BIT_SET(buf[byte_index], bit_index); + } else { + BIT_CLEAR(buf[byte_index], bit_index); + } +} + +void sh1107_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) +{ + uint8_t columnLow = area->x1 & 0x0F; + uint8_t columnHigh = (area->x1 >> 4) & 0x0F; + uint8_t row1 = 0, row2 = 0; + uint32_t size = 0; + void *ptr; + +#if defined CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE + row1 = area->x1>>3; + row2 = area->x2>>3; +#else + row1 = area->y1>>3; + row2 = area->y2>>3; +#endif + for(int i = row1; i < row2+1; i++){ + sh1107_send_cmd(0x10 | columnHigh); // Set Higher Column Start Address for Page Addressing Mode + sh1107_send_cmd(0x00 | columnLow); // Set Lower Column Start Address for Page Addressing Mode + sh1107_send_cmd(0xB0 | i); // Set Page Start Address for Page Addressing Mode + size = area->y2 - area->y1 + 1; +#if defined CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE + ptr = color_map + i * CONFIG_LV_DISPLAY_HEIGHT; +#else + ptr = color_map + i * CONFIG_LV_DISPLAY_WIDTH; +#endif + if(i != row2){ + sh1107_send_data( (void *) ptr, size); + } else { + // complete sending data by sh1107_send_color() and thus call lv_flush_ready() + sh1107_send_color( (void *) ptr, size); + } + } +} + +void sh1107_rounder(struct _disp_drv_t * disp_drv, lv_area_t *area) +{ + // workaround: always send complete size display buffer + area->x1 = 0; + area->y1 = 0; + area->x2 = CONFIG_LV_DISPLAY_WIDTH-1; + area->y2 = CONFIG_LV_DISPLAY_HEIGHT-1; +} + +void sh1107_sleep_in() +{ + sh1107_send_cmd(0xAE); +} + +void sh1107_sleep_out() +{ + sh1107_send_cmd(0xAF); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +static void sh1107_send_cmd(uint8_t cmd) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(SH1107_DC, 0); /*Command mode*/ + disp_spi_send_data(&cmd, 1); +} + +static void sh1107_send_data(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(SH1107_DC, 1); /*Data mode*/ + disp_spi_send_data(data, length); +} + +static void sh1107_send_color(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(SH1107_DC, 1); /*Data mode*/ + disp_spi_send_colors(data, length); +} diff --git a/lvgl_tft/sh1107.h b/lvgl_tft/sh1107.h new file mode 100644 index 0000000..7a0db68 --- /dev/null +++ b/lvgl_tft/sh1107.h @@ -0,0 +1,55 @@ +/** + * @file lv_templ.h + * + */ + +#ifndef SH1107_H +#define SH1107_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "../lvgl_helpers.h" + +/********************* + * DEFINES + *********************/ +#define SH1107_DC CONFIG_LV_DISP_PIN_DC +#define SH1107_RST CONFIG_LV_DISP_PIN_RST + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void sh1107_init(void); +void sh1107_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); +void sh1107_rounder(struct _disp_drv_t * disp_drv, lv_area_t *area); +void sh1107_set_px_cb(struct _disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa); +void sh1107_sleep_in(void); +void sh1107_sleep_out(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*SH1107_H*/ diff --git a/lvgl_tft/ssd1306.c b/lvgl_tft/ssd1306.c new file mode 100644 index 0000000..c01efcd --- /dev/null +++ b/lvgl_tft/ssd1306.c @@ -0,0 +1,240 @@ +/** + * @file ssd1306.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "ssd1306.h" +#include "driver/i2c.h" +#include "disp_spi.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +/********************* + * DEFINES + *********************/ +#define TAG "SSD1306" + +// Code from https://github.com/yanbe/ssd1306-esp-idf-i2c.git is used as a starting point, +// in addition to code from https://github.com/espressif/esp-iot-solution. +// Following definitions are borrowed from +// http://robotcantalk.blogspot.com/2015/03/interfacing-arduino-with-ssd1306-driven.html +// For LittlevGL the forum has been used, in particular: https://blog.littlevgl.com/2019-05-06/oled + +// SLA (0x3C) + WRITE_MODE (0x00) = 0x78 (0b01111000) +#define OLED_I2C_ADDRESS 0x3C +#define OLED_WIDTH 128 +#define OLED_HEIGHT 64 +#define OLED_COLUMNS 128 +#define OLED_PAGES 8 +#define OLED_PIXEL_PER_PAGE 8 + +// Control byte +#define OLED_CONTROL_BYTE_CMD_SINGLE 0x80 +#define OLED_CONTROL_BYTE_CMD_STREAM 0x00 +#define OLED_CONTROL_BYTE_DATA_STREAM 0x40 + +// Fundamental commands (pg.28) +#define OLED_CMD_SET_CONTRAST 0x81 // follow with 0x7F +#define OLED_CMD_DISPLAY_RAM 0xA4 +#define OLED_CMD_DISPLAY_ALLON 0xA5 +#define OLED_CMD_DISPLAY_NORMAL 0xA6 +#define OLED_CMD_DISPLAY_INVERTED 0xA7 +#define OLED_CMD_DISPLAY_OFF 0xAE +#define OLED_CMD_DISPLAY_ON 0xAF + +// Addressing Command Table (pg.30) +#define OLED_CMD_SET_MEMORY_ADDR_MODE 0x20 // follow with 0x00 = HORZ mode +#define OLED_CMD_SET_COLUMN_RANGE 0x21 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x7F = COL127 +#define OLED_CMD_SET_PAGE_RANGE 0x22 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x07 = PAGE7 + +// Hardware Config (pg.31) +#define OLED_CMD_SET_DISPLAY_START_LINE 0x40 +#define OLED_CMD_SET_SEGMENT_REMAP 0xA1 +#define OLED_CMD_SET_MUX_RATIO 0xA8 // follow with 0x3F = 64 MUX +#define OLED_CMD_SET_COM_SCAN_MODE_NORMAL 0xC0 +#define OLED_CMD_SET_COM_SCAN_MODE_REMAP 0xC8 +#define OLED_CMD_SET_DISPLAY_OFFSET 0xD3 // follow with 0x00 +#define OLED_CMD_SET_COM_PIN_MAP 0xDA // follow with 0x12 +#define OLED_CMD_NOP 0xE3 // NOP + +// Timing and Driving Scheme (pg.32) +#define OLED_CMD_SET_DISPLAY_CLK_DIV 0xD5 // follow with 0x80 +#define OLED_CMD_SET_PRECHARGE 0xD9 // follow with 0xF1 +#define OLED_CMD_SET_VCOMH_DESELCT 0xDB // follow with 0x30 + +// Charge Pump (pg.62) +#define OLED_CMD_SET_CHARGE_PUMP 0x8D // follow with 0x14 + +#define OLED_IIC_FREQ_HZ 400000 // I2C colock frequency + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +#define BIT_SET(a,b) ((a) |= (1U<<(b))) +#define BIT_CLEAR(a,b) ((a) &= ~(1U<<(b))) + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void ssd1306_init() +{ + esp_err_t ret; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + + i2c_master_write_byte(cmd, OLED_CMD_SET_CHARGE_PUMP, true); + i2c_master_write_byte(cmd, 0x14, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_SEGMENT_REMAP, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_COM_SCAN_MODE_REMAP, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_CONTRAST, true); + +#if defined CONFIG_LV_INVERT_DISPLAY + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_INVERTED, true); // Inverted display +#else + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_NORMAL, true); // Non-inverted display +#endif + + + i2c_master_write_byte(cmd, 0xFF, true); + + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_ON, true); + i2c_master_stop(cmd); + + ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS); + if (ret == ESP_OK) { + ESP_LOGI(TAG, "OLED configured successfully"); + } else { + ESP_LOGE(TAG, "OLED configuration failed. code: 0x%.2X", ret); + } + i2c_cmd_link_delete(cmd); +} + +void ssd1306_set_px_cb(struct _disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa) { + uint16_t byte_index = x + (( y>>3 ) * buf_w); + uint8_t bit_index = y & 0x7; + + if ((color.full == 0) && (LV_OPA_TRANSP != opa)) { + BIT_SET(buf[byte_index], bit_index); + } else { + BIT_CLEAR(buf[byte_index], bit_index); + } +} + +void ssd1306_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) +{ + uint8_t row1 = 0, row2 = 0; + i2c_cmd_handle_t cmd; + +#if defined CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE + row1 = area->y1>>3; + row2 = area->y2>>3; +#else + row1 = area->y1>>3; + row2 = area->y2>>3; +#endif + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_MEMORY_ADDR_MODE, true); + i2c_master_write_byte(cmd, 0x00, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_COLUMN_RANGE, true); + i2c_master_write_byte(cmd, area->x1, true); + i2c_master_write_byte(cmd, area->x2, true); + i2c_master_write_byte(cmd, OLED_CMD_SET_PAGE_RANGE, true); + i2c_master_write_byte(cmd, row1, true); + i2c_master_write_byte(cmd, row2, true); + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true); + i2c_master_write(cmd, (uint8_t *)color_p, OLED_COLUMNS * (1+row2-row1), true); + i2c_master_stop(cmd); + i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + + lv_disp_flush_ready(disp_drv); +} + +void ssd1306_rounder(struct _disp_drv_t * disp_drv, lv_area_t *area) +{ + // area->y1 = (area->y1 & (~0x7)); + // area->y2 = (area->y2 & (~0x7)) + 7; + + // workaround: always send complete size display buffer + area->x1 = 0; + area->y1 = 0; + area->x2 = CONFIG_LV_DISPLAY_WIDTH-1; + area->y2 = CONFIG_LV_DISPLAY_HEIGHT-1; +} + +void ssd1306_sleep_in() +{ + esp_err_t ret; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_OFF, true); + i2c_master_stop(cmd); + + ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "ssd1306_display_off configuration failed. code: 0x%.2X", ret); + } + i2c_cmd_link_delete(cmd); +} + +void ssd1306_sleep_out() +{ + esp_err_t ret; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_CMD_STREAM, true); + i2c_master_write_byte(cmd, OLED_CMD_DISPLAY_ON, true); + i2c_master_stop(cmd); + + ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "ssd1306_display_on configuration failed. code: 0x%.2X", ret); + } + i2c_cmd_link_delete(cmd); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + diff --git a/lvgl_tft/ssd1306.h b/lvgl_tft/ssd1306.h new file mode 100644 index 0000000..f859a76 --- /dev/null +++ b/lvgl_tft/ssd1306.h @@ -0,0 +1,57 @@ +/** + * @file lv_templ.h + * + */ + +#ifndef SSD1306_H +#define SSD1306_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "../lvgl_helpers.h" + +/********************* + * DEFINES + *********************/ +#define SSD1306_SDA CONFIG_LV_DISP_PIN_SDA +#define SSD1306_SCL CONFIG_LV_DISP_PIN_SCL +#define SSD1306_DISPLAY_ORIENTATION TFT_ORIENTATION_LANDSCAPE + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void ssd1306_init(void); +void ssd1306_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); +void ssd1306_rounder(struct _disp_drv_t * disp_drv, lv_area_t *area); +void ssd1306_set_px_cb(struct _disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa); + +void ssd1306_sleep_in(void); +void ssd1306_sleep_out(void); + +/********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*SSD1306_H*/ diff --git a/lvgl_tft/st7735s.c b/lvgl_tft/st7735s.c new file mode 100644 index 0000000..861904c --- /dev/null +++ b/lvgl_tft/st7735s.c @@ -0,0 +1,269 @@ +/** + * @file st7735s.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "st7735s.h" +#include "disp_spi.h" +#include "driver/i2c.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +/********************* + * DEFINES + *********************/ + #define TAG "ST7735S" + #define AXP192_I2C_ADDRESS 0x34 + +/********************** + * TYPEDEFS + **********************/ + +/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} lcd_init_cmd_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void st7735s_send_cmd(uint8_t cmd); +static void st7735s_send_data(void * data, uint16_t length); +static void st7735s_send_color(void * data, uint16_t length); +static void st7735s_set_orientation(uint8_t orientation); +static void i2c_master_init(); +static void axp192_write_byte(uint8_t addr, uint8_t data); +static void axp192_init(); +static void axp192_sleep_in(); +static void axp192_sleep_out(); + +/********************** + * STATIC VARIABLES + **********************/ +uint8_t st7735s_portrait_mode = 0; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void st7735s_init(void) +{ +#ifdef CONFIG_LV_M5STICKC_HANDLE_AXP192 + i2c_master_init(); + axp192_init(); +#endif + + lcd_init_cmd_t init_cmds[]={ + {ST7735_SWRESET, {0}, 0x80}, // Software reset, 0 args, w/delay 150 + {ST7735_SLPOUT, {0}, 0x80}, // Out of sleep mode, 0 args, w/delay 500 + {ST7735_FRMCTR1, {0x01, 0x2C, 0x2D}, 3}, // Frame rate ctrl - normal mode, 3 args: Rate = fosc/(1x2+40) * (LINE+2C+2D) + {ST7735_FRMCTR2, {0x01, 0x2C, 0x2D}, 3}, // Frame rate control - idle mode, 3 args:Rate = fosc/(1x2+40) * (LINE+2C+2D) + {ST7735_FRMCTR3, {0x01, 0x2C, 0x2D,0x01, 0x2C, 0x2D}, 6}, //Frame rate ctrl - partial mode, 6 args:Dot inversion mode. Line inversion mode + {ST7735_INVCTR, {0x07}, 1}, // Display inversion ctrl, 1 arg, no delay:No inversion + {ST7735_PWCTR1, {0xA2,0x02, 0x84}, 3}, // Power control, 3 args, no delay:-4.6V AUTO mode + {ST7735_PWCTR2, {0xC5}, 1}, // Power control, 1 arg, no delay:VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD + {ST7735_PWCTR3, {0x0A, 0x00}, 2}, // Power control, 2 args, no delay: Opamp current small, Boost frequency + {ST7735_PWCTR4, {0x8A,0x2A }, 2}, // Power control, 2 args, no delay: BCLK/2, Opamp current small & Medium low + {ST7735_PWCTR5, {0x8A, 0xEE}, 2}, // Power control, 2 args, no delay: + {ST7735_VMCTR1, {0x0E}, 1}, // Power control, 1 arg, no delay: +#if ST7735S_INVERT_COLORS == 1 + {ST7735_INVON, {0}, 0}, // set inverted mode +#else + {ST7735_INVOFF, {0}, 0}, // set non-inverted mode +#endif + {ST7735_COLMOD, {0x05}, 1}, // set color mode, 1 arg, no delay: 16-bit color + {ST7735_GMCTRP1, {0x02, 0x1c, 0x07, 0x12, + 0x37, 0x32, 0x29, 0x2d, + 0x29, 0x25, 0x2B, 0x39, + 0x00, 0x01, 0x03, 0x10}, 16}, // 16 args, no delay: + {ST7735_GMCTRN1, {0x03, 0x1d, 0x07, 0x06, + 0x2E, 0x2C, 0x29, 0x2D, + 0x2E, 0x2E, 0x37, 0x3F, + 0x00, 0x00, 0x02, 0x10}, 16}, // 16 args, no delay: + {ST7735_NORON, {0}, TFT_INIT_DELAY}, // Normal display on, no args, w/delay 10 ms delay + {ST7735_DISPON, {0}, TFT_INIT_DELAY}, // Main screen turn on, no args w/delay 100 ms delay + {0, {0}, 0xff} + }; + + //Initialize non-SPI GPIOs + gpio_pad_select_gpio(ST7735S_DC); + gpio_set_direction(ST7735S_DC, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(ST7735S_RST); + gpio_set_direction(ST7735S_RST, GPIO_MODE_OUTPUT); + + //Reset the display + gpio_set_level(ST7735S_RST, 0); + vTaskDelay(100 / portTICK_RATE_MS); + gpio_set_level(ST7735S_RST, 1); + vTaskDelay(100 / portTICK_RATE_MS); + + ESP_LOGI(TAG, "ST7735S initialization."); + + //Send all the commands + uint16_t cmd = 0; + while (init_cmds[cmd].databytes!=0xff) { + st7735s_send_cmd(init_cmds[cmd].cmd); + st7735s_send_data(init_cmds[cmd].data, init_cmds[cmd].databytes&0x1F); + if (init_cmds[cmd].databytes & 0x80) { + vTaskDelay(100 / portTICK_RATE_MS); + } + cmd++; + } + +#if (CONFIG_LV_DISPLAY_ORIENTATION == 0) || (CONFIG_LV_DISPLAY_ORIENTATION == 1) + st7735s_portrait_mode = 1; +#else + st7735s_portrait_mode = 0; +#endif + + st7735s_set_orientation(CONFIG_LV_DISPLAY_ORIENTATION); +} + +void st7735s_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) +{ + uint8_t data[4]; + + /*Column addresses*/ + st7735s_send_cmd(0x2A); + data[0] = (area->x1 >> 8) & 0xFF; + data[1] = (area->x1 & 0xFF) + (st7735s_portrait_mode ? COLSTART : ROWSTART); + data[2] = (area->x2 >> 8) & 0xFF; + data[3] = (area->x2 & 0xFF) + (st7735s_portrait_mode ? COLSTART : ROWSTART); + st7735s_send_data(data, 4); + + /*Page addresses*/ + st7735s_send_cmd(0x2B); + data[0] = (area->y1 >> 8) & 0xFF; + data[1] = (area->y1 & 0xFF) + (st7735s_portrait_mode ? ROWSTART : COLSTART); + data[2] = (area->y2 >> 8) & 0xFF; + data[3] = (area->y2 & 0xFF) + (st7735s_portrait_mode ? ROWSTART : COLSTART); + st7735s_send_data(data, 4); + + /*Memory write*/ + st7735s_send_cmd(0x2C); + + uint32_t size = lv_area_get_width(area) * lv_area_get_height(area); + st7735s_send_color((void*)color_map, size * 2); +} + +void st7735s_sleep_in() +{ + st7735s_send_cmd(0x10); + axp192_sleep_in(); +} + +void st7735s_sleep_out() +{ + axp192_sleep_out(); + st7735s_send_cmd(0x11); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void st7735s_send_cmd(uint8_t cmd) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ST7735S_DC, 0); /*Command mode*/ + disp_spi_send_data(&cmd, 1); +} + +static void st7735s_send_data(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ST7735S_DC, 1); /*Data mode*/ + disp_spi_send_data(data, length); +} + +static void st7735s_send_color(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ST7735S_DC, 1); /*Data mode*/ + disp_spi_send_colors(data, length); +} + +static void st7735s_set_orientation(uint8_t orientation) +{ + const char *orientation_str[] = { + "PORTRAIT", "PORTRAIT_INVERTED", "LANDSCAPE", "LANDSCAPE_INVERTED" + }; + + ESP_LOGD(TAG, "Display orientation: %s", orientation_str[orientation]); + + /* + Portrait: 0xC8 = ST77XX_MADCTL_MX | ST77XX_MADCTL_MY | ST77XX_MADCTL_BGR + Landscape: 0xA8 = ST77XX_MADCTL_MY | ST77XX_MADCTL_MV | ST77XX_MADCTL_BGR + Remark: "inverted" is ignored here + */ + uint8_t data[] = {0xC8, 0xC8, 0xA8, 0xA8}; + + ESP_LOGD(TAG, "0x36 command value: 0x%02X", data[orientation]); + + st7735s_send_cmd(ST7735_MADCTL); + st7735s_send_data((void *) &data[orientation], 1); +} + +static void i2c_master_init() +{ + i2c_config_t i2c_config = { + .mode = I2C_MODE_MASTER, + .sda_io_num = AXP192_SDA, + .scl_io_num = AXP192_SCL, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = 400000 + }; + i2c_param_config(I2C_NUM_0, &i2c_config); + i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0); +} + +static void axp192_write_byte(uint8_t addr, uint8_t data) +{ + esp_err_t ret; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (AXP192_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, addr, true); + i2c_master_write_byte(cmd, data, true); + i2c_master_stop(cmd); + + ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10/portTICK_PERIOD_MS); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "AXP192 send failed. code: 0x%.2X", ret); + } + i2c_cmd_link_delete(cmd); +} + +static void axp192_init() +{ + // information on how to init and use AXP192 ifor M5StickC taken from + // https://forum.m5stack.com/topic/1025/m5stickc-turn-off-screen-completely + + axp192_write_byte(0x10, 0xFF); // OLED_VPP Enable + axp192_write_byte(0x28, 0xCC); // Enable LDO2&LDO3, LED&TFT 3.0V + axp192_sleep_out(); + ESP_LOGI(TAG, "AXP192 initialized, power enabled for LDO2 and LDO3"); +} + +static void axp192_sleep_in() +{ + axp192_write_byte(0x12, 0x4b); +} + +static void axp192_sleep_out() +{ + axp192_write_byte(0x12, 0x4d); +} diff --git a/lvgl_tft/st7735s.h b/lvgl_tft/st7735s.h new file mode 100644 index 0000000..03672ee --- /dev/null +++ b/lvgl_tft/st7735s.h @@ -0,0 +1,149 @@ +/** + * @file lv_templ.h + * + */ + +#ifndef ST7735S_H +#define ST7735S_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ +// #define DISP_BUF_SIZE (CONFIG_LV_DISPLAY_WIDTH*CONFIG_LV_DISPLAY_HEIGHT) +#define DISP_BUF_SIZE (LV_HOR_RES_MAX * 40) + +#define ST7735S_DC CONFIG_LV_DISP_PIN_DC +#define ST7735S_RST CONFIG_LV_DISP_PIN_RST + +#define AXP192_SDA CONFIG_LV_AXP192_PIN_SDA +#define AXP192_SCL CONFIG_LV_AXP192_PIN_SCL + +#define ST7735S_INVERT_COLORS CONFIG_LV_INVERT_COLORS + +// Defines are taken from +// https://raw.githubusercontent.com/m5stack/M5StickC/master/src/utility/ST7735_Defines.h +// and are modified to fit to the M5StickC device, and are taken from +// https://github.com/adafruit/Adafruit-ST7735-Library +// +#define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 / 1 offset) +#define COLSTART 26 +#define ROWSTART 1 + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 + +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E +#define TFT_IDXRD 0x00 + +#define TFT_MADCTL 0x36 +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_RGB 0x00 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +// ST7735 specific commands used in init +#define ST7735_NOP 0x00 +#define ST7735_SWRESET 0x01 +#define ST7735_RDDID 0x04 +#define ST7735_RDDST 0x09 + +#define ST7735_SLPIN 0x10 +#define ST7735_SLPOUT 0x11 +#define ST7735_PTLON 0x12 +#define ST7735_NORON 0x13 + +#define ST7735_INVOFF 0x20 +#define ST7735_INVON 0x21 +#define ST7735_DISPOFF 0x28 +#define ST7735_DISPON 0x29 +#define ST7735_CASET 0x2A +#define ST7735_RASET 0x2B +#define ST7735_RAMWR 0x2C +#define ST7735_RAMRD 0x2E + +#define ST7735_PTLAR 0x30 +#define ST7735_VSCRDEF 0x33 +#define ST7735_COLMOD 0x3A +#define ST7735_MADCTL 0x36 +#define ST7735_VSCRSADD 0x37 + +#define ST7735_FRMCTR1 0xB1 +#define ST7735_FRMCTR2 0xB2 +#define ST7735_FRMCTR3 0xB3 +#define ST7735_INVCTR 0xB4 +#define ST7735_DISSET5 0xB6 + +#define ST7735_PWCTR1 0xC0 +#define ST7735_PWCTR2 0xC1 +#define ST7735_PWCTR3 0xC2 +#define ST7735_PWCTR4 0xC3 +#define ST7735_PWCTR5 0xC4 +#define ST7735_VMCTR1 0xC5 + +#define ST7735_RDID1 0xDA +#define ST7735_RDID2 0xDB +#define ST7735_RDID3 0xDC +#define ST7735_RDID4 0xDD + +#define ST7735_PWCTR6 0xFC + +#define ST7735_GMCTRP1 0xE0 +#define ST7735_GMCTRN1 0xE1 + +#define ST77XX_MADCTL_MY 0x80 +#define ST77XX_MADCTL_MX 0x40 +#define ST77XX_MADCTL_MV 0x20 +#define ST77XX_MADCTL_ML 0x10 +#define ST77XX_MADCTL_RGB 0x00 +#define ST77XX_MADCTL_BGR 0x08 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void st7735s_init(void); +void st7735s_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map); +void st7735s_enable_backlight(bool backlight); +void st7735s_sleep_in(void); +void st7735s_sleep_out(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*ST7735S_H*/ diff --git a/lvgl_tft/st7789.c b/lvgl_tft/st7789.c new file mode 100644 index 0000000..206a622 --- /dev/null +++ b/lvgl_tft/st7789.c @@ -0,0 +1,235 @@ +/** + * @file st7789.c + * + * Mostly taken from lbthomsen/esp-idf-littlevgl github. + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sdkconfig.h" + +#include "esp_log.h" + +#include "st7789.h" + +#include "disp_spi.h" +#include "driver/gpio.h" + +/********************* + * DEFINES + *********************/ +#define TAG "st7789" +/********************** + * TYPEDEFS + **********************/ + +/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} lcd_init_cmd_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void st7789_set_orientation(uint8_t orientation); + +static void st7789_send_cmd(uint8_t cmd); +static void st7789_send_data(void *data, uint16_t length); +static void st7789_send_color(void *data, uint16_t length); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void st7789_init(void) +{ + lcd_init_cmd_t st7789_init_cmds[] = { + {0xCF, {0x00, 0x83, 0X30}, 3}, + {0xED, {0x64, 0x03, 0X12, 0X81}, 4}, + {ST7789_PWCTRL2, {0x85, 0x01, 0x79}, 3}, + {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5}, + {0xF7, {0x20}, 1}, + {0xEA, {0x00, 0x00}, 2}, + {ST7789_LCMCTRL, {0x26}, 1}, + {ST7789_IDSET, {0x11}, 1}, + {ST7789_VCMOFSET, {0x35, 0x3E}, 2}, + {ST7789_CABCCTRL, {0xBE}, 1}, + {ST7789_MADCTL, {0x00}, 1}, // Set to 0x28 if your display is flipped + {ST7789_COLMOD, {0x55}, 1}, + {ST7789_INVON, {0}, 0}, + {ST7789_RGBCTRL, {0x00, 0x1B}, 2}, + {0xF2, {0x08}, 1}, + {ST7789_GAMSET, {0x01}, 1}, + {ST7789_PVGAMCTRL, {0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x32, 0x44, 0x42, 0x06, 0x0E, 0x12, 0x14, 0x17}, 14}, + {ST7789_NVGAMCTRL, {0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x31, 0x54, 0x47, 0x0E, 0x1C, 0x17, 0x1B, 0x1E}, 14}, + {ST7789_CASET, {0x00, 0x00, 0x00, 0xEF}, 4}, + {ST7789_RASET, {0x00, 0x00, 0x01, 0x3f}, 4}, + {ST7789_RAMWR, {0}, 0}, + {ST7789_GCTRL, {0x07}, 1}, + {0xB6, {0x0A, 0x82, 0x27, 0x00}, 4}, + {ST7789_SLPOUT, {0}, 0x80}, + {ST7789_DISPON, {0}, 0x80}, + {0, {0}, 0xff}, + }; + + //Initialize non-SPI GPIOs + gpio_pad_select_gpio(ST7789_DC); + gpio_set_direction(ST7789_DC, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(ST7789_RST); + gpio_set_direction(ST7789_RST, GPIO_MODE_OUTPUT); + +#if ST7789_ENABLE_BACKLIGHT_CONTROL + gpio_pad_select_gpio(ST7789_BCKL); + gpio_set_direction(ST7789_BCKL, GPIO_MODE_OUTPUT); +#endif + + //Reset the display + gpio_set_level(ST7789_RST, 0); + vTaskDelay(100 / portTICK_RATE_MS); + gpio_set_level(ST7789_RST, 1); + vTaskDelay(100 / portTICK_RATE_MS); + + printf("ST7789 initialization.\n"); + + //Send all the commands + uint16_t cmd = 0; + while (st7789_init_cmds[cmd].databytes!=0xff) { + st7789_send_cmd(st7789_init_cmds[cmd].cmd); + st7789_send_data(st7789_init_cmds[cmd].data, st7789_init_cmds[cmd].databytes&0x1F); + if (st7789_init_cmds[cmd].databytes & 0x80) { + vTaskDelay(100 / portTICK_RATE_MS); + } + cmd++; + } + + st7789_enable_backlight(true); + + st7789_set_orientation(CONFIG_LV_DISPLAY_ORIENTATION); +} + +void st7789_enable_backlight(bool backlight) +{ +#if ST7789_ENABLE_BACKLIGHT_CONTROL + printf("%s backlight.\n", backlight ? "Enabling" : "Disabling"); + uint32_t tmp = 0; + +#if (ST7789_BCKL_ACTIVE_LVL==1) + tmp = backlight ? 1 : 0; +#else + tmp = backlight ? 0 : 1; +#endif + + gpio_set_level(ST7789_BCKL, tmp); +#endif +} + +/* The ST7789 display controller can drive 320*240 displays, when using a 240*240 + * display there's a gap of 80px, we need to edit the coordinates to take into + * account that gap, this is not necessary in all orientations. */ +void st7789_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map) +{ + uint8_t data[4] = {0}; + + uint16_t offsetx1 = area->x1; + uint16_t offsetx2 = area->x2; + uint16_t offsety1 = area->y1; + uint16_t offsety2 = area->y2; + +#if (CONFIG_LV_TFT_DISPLAY_OFFSETS) + offsetx1 += CONFIG_LV_TFT_DISPLAY_X_OFFSET; + offsetx2 += CONFIG_LV_TFT_DISPLAY_X_OFFSET; + offsety1 += CONFIG_LV_TFT_DISPLAY_Y_OFFSET; + offsety2 += CONFIG_LV_TFT_DISPLAY_Y_OFFSET; + +#elif (LV_HOR_RES_MAX == 240) && (LV_VER_RES_MAX == 240) +#if (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT) + offsetx1 += 80; + offsetx2 += 80; +#elif (CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED) + offsety1 += 80; + offsety2 += 80; +#endif +#endif + + /*Column addresses*/ + st7789_send_cmd(ST7789_CASET); + data[0] = (offsetx1 >> 8) & 0xFF; + data[1] = offsetx1 & 0xFF; + data[2] = (offsetx2 >> 8) & 0xFF; + data[3] = offsetx2 & 0xFF; + st7789_send_data(data, 4); + + /*Page addresses*/ + st7789_send_cmd(ST7789_RASET); + data[0] = (offsety1 >> 8) & 0xFF; + data[1] = offsety1 & 0xFF; + data[2] = (offsety2 >> 8) & 0xFF; + data[3] = offsety2 & 0xFF; + st7789_send_data(data, 4); + + /*Memory write*/ + st7789_send_cmd(ST7789_RAMWR); + + uint32_t size = lv_area_get_width(area) * lv_area_get_height(area); + + st7789_send_color((void*)color_map, size * 2); + +} + +/********************** + * STATIC FUNCTIONS + **********************/ +static void st7789_send_cmd(uint8_t cmd) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ST7789_DC, 0); + disp_spi_send_data(&cmd, 1); +} + +static void st7789_send_data(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ST7789_DC, 1); + disp_spi_send_data(data, length); +} + +static void st7789_send_color(void * data, uint16_t length) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(ST7789_DC, 1); + disp_spi_send_colors(data, length); +} + +static void st7789_set_orientation(uint8_t orientation) +{ + // ESP_ASSERT(orientation < 4); + + const char *orientation_str[] = { + "PORTRAIT", "PORTRAIT_INVERTED", "LANDSCAPE", "LANDSCAPE_INVERTED" + }; + + ESP_LOGI(TAG, "Display orientation: %s", orientation_str[orientation]); + + uint8_t data[] = + { +#if CONFIG_LV_PREDEFINED_DISPLAY_TTGO + 0x60, 0xA0, 0x00, 0xC0 +#else + 0xC0, 0x00, 0x60, 0xA0 +#endif + }; + + ESP_LOGI(TAG, "0x36 command value: 0x%02X", data[orientation]); + + st7789_send_cmd(ST7789_MADCTL); + st7789_send_data((void *) &data[orientation], 1); +} diff --git a/lvgl_tft/st7789.h b/lvgl_tft/st7789.h new file mode 100644 index 0000000..1136562 --- /dev/null +++ b/lvgl_tft/st7789.h @@ -0,0 +1,120 @@ +/** + * @file st7789.h + * + * Mostly taken from lbthomsen/esp-idf-littlevgl github. + */ + +#ifndef ST7789_H +#define ST7789_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif +#include "../lvgl_helpers.h" + +#include "sdkconfig.h" + +#define ST7789_DC CONFIG_LV_DISP_PIN_DC +#define ST7789_RST CONFIG_LV_DISP_PIN_RST +#define ST7789_BCKL CONFIG_LV_DISP_PIN_BCKL + +#define ST7789_ENABLE_BACKLIGHT_CONTROL CONFIG_LV_ENABLE_BACKLIGHT_CONTROL + +#if CONFIG_LV_BACKLIGHT_ACTIVE_LVL + #define ST7789_BCKL_ACTIVE_LVL 1 +#else + #define ST7789_BCKL_ACTIVE_LVL 0 +#endif + +/* ST7789 commands */ +#define ST7789_NOP 0x00 +#define ST7789_SWRESET 0x01 +#define ST7789_RDDID 0x04 +#define ST7789_RDDST 0x09 + +#define ST7789_RDDPM 0x0A // Read display power mode +#define ST7789_RDD_MADCTL 0x0B // Read display MADCTL +#define ST7789_RDD_COLMOD 0x0C // Read display pixel format +#define ST7789_RDDIM 0x0D // Read display image mode +#define ST7789_RDDSM 0x0E // Read display signal mode +#define ST7789_RDDSR 0x0F // Read display self-diagnostic result (ST7789V) + +#define ST7789_SLPIN 0x10 +#define ST7789_SLPOUT 0x11 +#define ST7789_PTLON 0x12 +#define ST7789_NORON 0x13 + +#define ST7789_INVOFF 0x20 +#define ST7789_INVON 0x21 +#define ST7789_GAMSET 0x26 // Gamma set +#define ST7789_DISPOFF 0x28 +#define ST7789_DISPON 0x29 +#define ST7789_CASET 0x2A +#define ST7789_RASET 0x2B +#define ST7789_RAMWR 0x2C +#define ST7789_RGBSET 0x2D // Color setting for 4096, 64K and 262K colors +#define ST7789_RAMRD 0x2E + +#define ST7789_PTLAR 0x30 +#define ST7789_VSCRDEF 0x33 // Vertical scrolling definition (ST7789V) +#define ST7789_TEOFF 0x34 // Tearing effect line off +#define ST7789_TEON 0x35 // Tearing effect line on +#define ST7789_MADCTL 0x36 // Memory data access control +#define ST7789_IDMOFF 0x38 // Idle mode off +#define ST7789_IDMON 0x39 // Idle mode on +#define ST7789_RAMWRC 0x3C // Memory write continue (ST7789V) +#define ST7789_RAMRDC 0x3E // Memory read continue (ST7789V) +#define ST7789_COLMOD 0x3A + +#define ST7789_RAMCTRL 0xB0 // RAM control +#define ST7789_RGBCTRL 0xB1 // RGB control +#define ST7789_PORCTRL 0xB2 // Porch control +#define ST7789_FRCTRL1 0xB3 // Frame rate control +#define ST7789_PARCTRL 0xB5 // Partial mode control +#define ST7789_GCTRL 0xB7 // Gate control +#define ST7789_GTADJ 0xB8 // Gate on timing adjustment +#define ST7789_DGMEN 0xBA // Digital gamma enable +#define ST7789_VCOMS 0xBB // VCOMS setting +#define ST7789_LCMCTRL 0xC0 // LCM control +#define ST7789_IDSET 0xC1 // ID setting +#define ST7789_VDVVRHEN 0xC2 // VDV and VRH command enable +#define ST7789_VRHS 0xC3 // VRH set +#define ST7789_VDVSET 0xC4 // VDV setting +#define ST7789_VCMOFSET 0xC5 // VCOMS offset set +#define ST7789_FRCTR2 0xC6 // FR Control 2 +#define ST7789_CABCCTRL 0xC7 // CABC control +#define ST7789_REGSEL1 0xC8 // Register value section 1 +#define ST7789_REGSEL2 0xCA // Register value section 2 +#define ST7789_PWMFRSEL 0xCC // PWM frequency selection +#define ST7789_PWCTRL1 0xD0 // Power control 1 +#define ST7789_VAPVANEN 0xD2 // Enable VAP/VAN signal output +#define ST7789_CMD2EN 0xDF // Command 2 enable +#define ST7789_PVGAMCTRL 0xE0 // Positive voltage gamma control +#define ST7789_NVGAMCTRL 0xE1 // Negative voltage gamma control +#define ST7789_DGMLUTR 0xE2 // Digital gamma look-up table for red +#define ST7789_DGMLUTB 0xE3 // Digital gamma look-up table for blue +#define ST7789_GATECTRL 0xE4 // Gate control +#define ST7789_SPI2EN 0xE7 // SPI2 enable +#define ST7789_PWCTRL2 0xE8 // Power control 2 +#define ST7789_EQCTRL 0xE9 // Equalize time control +#define ST7789_PROMCTRL 0xEC // Program control +#define ST7789_PROMEN 0xFA // Program mode enable +#define ST7789_NVMSET 0xFC // NVM setting +#define ST7789_PROMACT 0xFE // Program action + +void st7789_init(void); +void st7789_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map); +void st7789_enable_backlight(bool backlight); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ST7789_H */ diff --git a/lvgl_tft/uc8151d.c b/lvgl_tft/uc8151d.c new file mode 100644 index 0000000..3332120 --- /dev/null +++ b/lvgl_tft/uc8151d.c @@ -0,0 +1,268 @@ +/** + @file uc8151d.c + @brief GoodDisplay GDEW0154M10 DES e-paper display w/ UltraChip UC8151D + @version 1.0 + @date 2020-08-28 + @author Jackson Ming Hu + + + @section LICENSE + + MIT License + + Copyright (c) 2020 Jackson Ming Hu + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include +#include +#include +#include +#include + +#include "disp_spi.h" +#include "disp_driver.h" +#include "uc8151d.h" + +#define TAG "lv_uc8151d" + +#define PIN_DC CONFIG_LV_DISP_PIN_DC +#define PIN_DC_BIT ((1ULL << (uint8_t)(CONFIG_LV_DISP_PIN_DC))) +#define PIN_RST CONFIG_LV_DISP_PIN_RST +#define PIN_RST_BIT ((1ULL << (uint8_t)(CONFIG_LV_DISP_PIN_RST))) +#define PIN_BUSY CONFIG_LV_DISP_PIN_BUSY +#define PIN_BUSY_BIT ((1ULL << (uint8_t)(CONFIG_LV_DISP_PIN_BUSY))) +#define EVT_BUSY (1UL << 0UL) +#define EPD_WIDTH CONFIG_LV_DISPLAY_WIDTH +#define EPD_HEIGHT CONFIG_LV_DISPLAY_HEIGHT +#define EPD_ROW_LEN (EPD_HEIGHT / 8u) + +#define BIT_SET(a, b) ((a) |= (1U << (b))) +#define BIT_CLEAR(a, b) ((a) &= ~(1U << (b))) + +typedef struct +{ + uint8_t cmd; + uint8_t data[3]; + size_t len; +} uc8151d_seq_t; + +#define EPD_SEQ_LEN(x) ((sizeof(x) / sizeof(uc8151d_seq_t))) + +static EventGroupHandle_t uc8151d_evts = NULL; + +static void IRAM_ATTR uc8151d_busy_intr(void *arg) +{ + BaseType_t xResult; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xResult = xEventGroupSetBitsFromISR(uc8151d_evts, EVT_BUSY, &xHigherPriorityTaskWoken); + if (xResult == pdPASS) { + portYIELD_FROM_ISR(); + } +} + +static void uc8151d_spi_send_cmd(uint8_t cmd) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(PIN_DC, 0); // DC = 0 for command + disp_spi_send_data(&cmd, 1); +} + +static void uc8151d_spi_send_data(uint8_t *data, size_t len) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(PIN_DC, 1); // DC = 1 for data + disp_spi_send_data(data, len); +} + +static void uc8151d_spi_send_data_byte(uint8_t data) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(PIN_DC, 1); // DC = 1 for data + disp_spi_send_data(&data, 1); +} + +static void uc8151d_spi_send_fb(uint8_t *data, size_t len) +{ + disp_wait_for_pending_transactions(); + gpio_set_level(PIN_DC, 1); // DC = 1 for data + disp_spi_send_colors(data, len); +} + +static void uc8151d_spi_send_seq(const uc8151d_seq_t *seq, size_t len) +{ + ESP_LOGD(TAG, "Writing cmd/data sequence, count %u", len); + + if (!seq || len < 1) return; + for (size_t cmd_idx = 0; cmd_idx < len; cmd_idx++) { + uc8151d_spi_send_cmd(seq[cmd_idx].cmd); + if (seq[cmd_idx].len > 0) { + uc8151d_spi_send_data((uint8_t *) seq[cmd_idx].data, seq[cmd_idx].len); + } + } +} + +static esp_err_t uc8151d_wait_busy(uint32_t timeout_ms) +{ + uint32_t wait_ticks = (timeout_ms == 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms)); + EventBits_t bits = xEventGroupWaitBits(uc8151d_evts, + EVT_BUSY, // Wait for busy bit + pdTRUE, pdTRUE, // Clear on exit, wait for all + wait_ticks); // Timeout + + return ((bits & EVT_BUSY) != 0) ? ESP_OK : ESP_ERR_TIMEOUT; +} + +static void uc8151d_sleep() +{ + // Set VCOM to 0xf7 + uc8151d_spi_send_cmd(0x50); + uc8151d_spi_send_data_byte(0xf7); + + // Power off + uc8151d_spi_send_cmd(0x02); + uc8151d_wait_busy(0); + + // Go to sleep + uc8151d_spi_send_cmd(0x07); + uc8151d_spi_send_data_byte(0xa5); +} + +static void uc8151d_panel_init() +{ + // Hardware reset for 3 times - not sure why but it's from official demo code + for (uint8_t cnt = 0; cnt < 3; cnt++) { + gpio_set_level(PIN_RST, 0); + vTaskDelay(pdMS_TO_TICKS(10)); // At least 10ms, leave 20ms for now just in case... + gpio_set_level(PIN_RST, 1); + vTaskDelay(pdMS_TO_TICKS(10)); + } + + // Power up + uc8151d_spi_send_cmd(0x04); + uc8151d_wait_busy(0); + + // Panel settings + uc8151d_spi_send_cmd(0x00); +#if defined (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT_INVERTED) + uc8151d_spi_send_data_byte(0x13); +#elif defined (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT) + uc8151d_spi_send_data_byte(0x1f); +#endif + + // VCOM & Data intervals + uc8151d_spi_send_cmd(0x50); + uc8151d_spi_send_data_byte(0x97); +} + +static void uc8151d_full_update(uint8_t *buf) +{ + uc8151d_panel_init(); + + uint8_t *buf_ptr = buf; + uint8_t old_data[EPD_ROW_LEN] = { 0 }; + + // Fill old data + uc8151d_spi_send_cmd(0x10); + for (size_t h_idx = 0; h_idx < EPD_HEIGHT; h_idx++) { + uc8151d_spi_send_data(old_data, EPD_ROW_LEN); + } + + // Fill new data + uc8151d_spi_send_cmd(0x13); + for (size_t h_idx = 0; h_idx < EPD_HEIGHT; h_idx++) { + uc8151d_spi_send_data(buf_ptr, EPD_ROW_LEN); + buf_ptr += EPD_ROW_LEN; + } + + // Issue refresh + uc8151d_spi_send_cmd(0x12); + vTaskDelay(pdMS_TO_TICKS(10)); + uc8151d_wait_busy(0); + + uc8151d_sleep(); +} + +void uc8151d_lv_fb_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + size_t len = ((area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1)) / 8; + + ESP_LOGD(TAG, "x1: 0x%x, x2: 0x%x, y1: 0x%x, y2: 0x%x", area->x1, area->x2, area->y1, area->y2); + ESP_LOGD(TAG, "Writing LVGL fb with len: %u", len); + + uint8_t *buf = (uint8_t *) color_map; + uc8151d_full_update(buf); + + lv_disp_flush_ready(drv); + ESP_LOGD(TAG, "Ready"); +} + +void uc8151d_lv_set_fb_cb(struct _disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa) +{ + uint16_t byte_index = (x >> 3u) + (y * EPD_ROW_LEN); + uint8_t bit_index = x & 0x07u; + + if (color.full) { + BIT_SET(buf[byte_index], 7 - bit_index); + } else { + ESP_LOGD(TAG, "Clear at x: %u, y: %u", x, y); + BIT_CLEAR(buf[byte_index], 7 - bit_index); + } +} + +void uc8151d_lv_rounder_cb(struct _disp_drv_t *disp_drv, lv_area_t *area) +{ + // Always send full framebuffer if it's not in partial mode + area->x1 = 0; + area->y1 = 0; + area->x2 = EPD_WIDTH - 1; + area->y2 = EPD_HEIGHT - 1; +} + +void uc8151d_init() +{ + // Initialise event group + uc8151d_evts = xEventGroupCreate(); + if (!uc8151d_evts) { + ESP_LOGE(TAG, "Failed when initialising event group!"); + return; + } + + // Setup output pins, output (PP) + gpio_config_t out_io_conf = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = PIN_DC_BIT | PIN_RST_BIT, + .pull_down_en = 0, + .pull_up_en = 0, + }; + ESP_ERROR_CHECK(gpio_config(&out_io_conf)); + + // Setup input pin, pull-up, input + gpio_config_t in_io_conf = { + .intr_type = GPIO_INTR_POSEDGE, + .mode = GPIO_MODE_INPUT, + .pin_bit_mask = PIN_BUSY_BIT, + .pull_down_en = 0, + .pull_up_en = 1, + }; + ESP_ERROR_CHECK(gpio_config(&in_io_conf)); + gpio_install_isr_service(0); + gpio_isr_handler_add(PIN_BUSY, uc8151d_busy_intr, (void *) PIN_BUSY); + + ESP_LOGI(TAG, "IO init finished"); + uc8151d_panel_init(); + ESP_LOGI(TAG, "Panel initialised"); +} diff --git a/lvgl_tft/uc8151d.h b/lvgl_tft/uc8151d.h new file mode 100644 index 0000000..e637f0e --- /dev/null +++ b/lvgl_tft/uc8151d.h @@ -0,0 +1,39 @@ +/** + @file uc8151d.h + @brief GoodDisplay GDEW0154M09 e-paper display w/ FitiPower JD79653A + @version 1.0 + @date 2020-08-28 + @author Jackson Ming Hu + + + @section LICENSE + + MIT License + + Copyright (c) 2020 Jackson Ming Hu + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef LVGL_DEMO_UC8151D_H +#define LVGL_DEMO_UC8151D_H + +#include + +void uc8151d_init(); +void uc8151d_lv_set_fb_cb(struct _disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa); + +void uc8151d_lv_rounder_cb(struct _disp_drv_t *disp_drv, lv_area_t *area); +void uc8151d_lv_fb_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map); + +#endif //LVGL_DEMO_UC8151D_H diff --git a/lvgl_touch/CMakeLists.txt b/lvgl_touch/CMakeLists.txt new file mode 100644 index 0000000..b8b1c68 --- /dev/null +++ b/lvgl_touch/CMakeLists.txt @@ -0,0 +1,34 @@ +if(ESP_PLATFORM) + +set(SOURCES "touch_driver.c") + +# Include only the source file of the selected +# display controller. +if(CONFIG_LV_TOUCH_CONTROLLER_XPT2046) + list(APPEND SOURCES "xpt2046.c") +elseif(CONFIG_LV_TOUCH_CONTROLLER_FT6X06) + list(APPEND SOURCES "ft6x36.c") +elseif(CONFIG_LV_TOUCH_CONTROLLER_STMPE610) + list(APPEND SOURCES "stmpe610") +elseif(CONFIG_LV_TOUCH_CONTROLLER_ADCRAW) + list(APPEND SOURCES "adcraw.c") +elseif(CONFIG_LV_TOUCH_CONTROLLER_FT81X) + list(APPEND SOURCES "FT81x.c") +elseif(CONFIG_LV_TOUCH_CONTROLLER_RA8875) + list(APPEND SOURCES "ra8875_touch.c") +endif() + +if(CONFIG_LV_TOUCH_DRIVER_PROTOCOL_SPI) + list(APPEND SOURCES "tp_spi.c") +elseif(CONFIG_LV_TOUCH_DRIVER_PROTOCOL_I2C) + list(APPEND SOURCES "tp_i2c.c") +endif() + +# Print the included source files +message("SOURCES contents: " "${SOURCES}") + +idf_component_register(SRCS ${SOURCES} + INCLUDE_DIRS . + REQUIRES lvgl) + +endif() diff --git a/lvgl_touch/FT81x.c b/lvgl_touch/FT81x.c new file mode 100644 index 0000000..266abc3 --- /dev/null +++ b/lvgl_touch/FT81x.c @@ -0,0 +1,85 @@ +/** + * @file FT81x.c + */ + +/********************* + * INCLUDES + *********************/ +#include "esp_system.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" + +#include + +#include "FT81x.h" + +#include "../lvgl_tft/EVE.h" +#include "../lvgl_tft/EVE_commands.h" + + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + + +/** + * Get the current position and state of the touchpad + * @param data store the read data here + * @return false: because no more data to be read + */ +bool FT81x_read(lv_indev_drv_t * drv, lv_indev_data_t * data) +{ + static int16_t last_x = 0; + static int16_t last_y = 0; + bool touched = true; + + uint32_t XY = EVE_memRead32(REG_TOUCH_SCREEN_XY); + uint16_t Y = XY & 0xffff; + uint16_t X = XY >> 16; + + // is it not touched (or invalid because of calibration range) + if(X == 0x8000 || Y == 0x8000 || X > LV_HOR_RES_MAX || Y > LV_VER_RES_MAX) + { + touched = false; + X = last_x; + Y = last_y; + } + else + { + last_x = X; + last_y = Y; + } + + data->point.x = X; + data->point.y = Y; + data->state = (touched == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR); + + return false; +} + + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/lvgl_touch/FT81x.h b/lvgl_touch/FT81x.h new file mode 100644 index 0000000..c788770 --- /dev/null +++ b/lvgl_touch/FT81x.h @@ -0,0 +1,46 @@ +/** + * @file STMPE610.h + */ + +#ifndef FT81X_TOUCH__H +#define FT81X_TOUCH__H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include +#include +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +; +bool FT81x_read(lv_indev_drv_t * drv, lv_indev_data_t * data); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* FT81X_TOUCH__H */ diff --git a/lvgl_touch/Kconfig b/lvgl_touch/Kconfig new file mode 100644 index 0000000..bbcdad7 --- /dev/null +++ b/lvgl_touch/Kconfig @@ -0,0 +1,458 @@ +menu "LVGL Touch controller" + + config LV_TOUCH_CONTROLLER + int + default 0 if LV_TOUCH_CONTROLLER_NONE + default 1 if LV_TOUCH_CONTROLLER_XPT2046 + default 2 if LV_TOUCH_CONTROLLER_FT6X06 + default 3 if LV_TOUCH_CONTROLLER_STMPE610 + default 4 if LV_TOUCH_CONTROLLER_ADCRAW + default 5 if LV_TOUCH_CONTROLLER_FT81X + default 6 if LV_TOUCH_CONTROLLER_RA8875 + + choice + prompt "Select a touch panel controller model." + default LV_TOUCH_CONTROLLER_NONE + help + Select the controller for your touch panel. + + config LV_TOUCH_CONTROLLER_NONE + bool "None" + config LV_TOUCH_CONTROLLER_XPT2046 + select LV_TOUCH_DRIVER_PROTOCOL_SPI + bool "XPT2046" + config LV_TOUCH_CONTROLLER_FT6X06 + select LV_TOUCH_DRIVER_PROTOCOL_I2C + bool "FT6X06" + config LV_TOUCH_CONTROLLER_STMPE610 + select LV_TOUCH_DRIVER_PROTOCOL_SPI + bool "STMPE610" + config LV_TOUCH_CONTROLLER_ADCRAW + select LV_TOUCH_DRIVER_ADC + bool "ADCRAW" + config LV_TOUCH_CONTROLLER_FT81X + select LV_TOUCH_DRIVER_PROTOCOL_SPI + bool "FT81X" + config LV_TOUCH_CONTROLLER_RA8875 + select LV_TOUCH_DRIVER_DISPLAY + bool "RA8875" + endchoice + + config LV_TOUCH_DRIVER_PROTOCOL_SPI + bool + help + Touch controller protocol SPI + + config LV_TOUCH_DRIVER_PROTOCOL_I2C + bool + help + Touch controller protocol I2C + + config LV_TOUCH_DRIVER_ADC + bool + help + Touch controller via ADC + + config LV_TOUCH_DRIVER_DISPLAY + bool + help + Touch controller uses same interface/device as display + (Note: Display must be initialized before touch) + + choice + prompt "Touch I2C port" + depends on LV_TOUCH_DRIVER_PROTOCOL_I2C + + default LV_TOUCH_I2C_PORT_0 + help + Select the I2C port used by the touch controller. + + config LV_TOUCH_I2C_PORT_0 + bool "I2C PORT 0" + config LV_TOUCH_I2C_PORT_1 + bool "I2C PORT 1" + endchoice + + choice + prompt "Touch Controller SPI Bus." + depends on LV_TOUCH_DRIVER_PROTOCOL_SPI + + default LV_TOUCH_CONTROLLER_SPI_VSPI if !IDF_TARGET_ESP32S2 + default LV_TOUCH_CONTROLLER_SPI_FSPI if IDF_TARGET_ESP32S2 + help + Select the SPI Bus the TFT Display is attached to. + + config LV_TOUCH_CONTROLLER_SPI_HSPI + bool "HSPI" + config LV_TOUCH_CONTROLLER_SPI_VSPI + bool "VSPI" if !IDF_TARGET_ESP32S2 + config LV_TOUCH_CONTROLLER_SPI_FSPI + bool "FSPI" if IDF_TARGET_ESP32S2 + endchoice + + menu "Touchpanel (XPT2046) Pin Assignments" + depends on LV_TOUCH_CONTROLLER_XPT2046 + + config LV_TOUCH_SPI_MISO + int + prompt "GPIO for MISO (Master In Slave Out)" + range 0 39 + default 35 if LV_PREDEFINED_PINS_38V1 + default 19 + + help + Configure the touchpanel MISO pin here. + + config LV_TOUCH_SPI_MOSI + int + prompt "GPIO for MOSI (Master Out Slave In)" + range 0 39 + default 32 if LV_PREDEFINED_PINS_38V1 + default 23 + + help + Configure the touchpanel MOSI pin here. + + config LV_TOUCH_SPI_CLK + int "GPIO for CLK (SCK / Serial Clock)" + range 0 39 + default 26 if LV_PREDEFINED_PINS_38V1 + default 18 + help + Configure the touchpanel CLK pin here. + + config LV_TOUCH_SPI_CS + int "GPIO for CS (Slave Select)" + range 0 39 + default 33 if LV_PREDEFINED_PINS_38V1 + default 5 + help + Configure the touchpanel CS pin here. + + config LV_TOUCH_PIN_IRQ + int "GPIO for IRQ (Interrupt Request)" + range 0 39 + default 27 if LV_PREDEFINED_PINS_38V4 + default 25 + help + Configure the touchpanel CS pin here. + endmenu + + menu "Touchpanel Configuration (XPT2046)" + depends on LV_TOUCH_CONTROLLER_XPT2046 + + config LV_TOUCH_X_MIN + int + prompt "Minimum X coordinate value." + default 0 if LV_PREDEFINED_PINS_38V4 + default 200 + + config LV_TOUCH_Y_MIN + int + prompt "Minimum Y coordinate value." + default 0 if LV_PREDEFINED_PINS_38V4 + default 120 + + config LV_TOUCH_X_MAX + int + prompt "Maximum X coordinate value." + default 4095 if LV_PREDEFINED_PINS_38V4 + default 1900 + + config LV_TOUCH_Y_MAX + int + prompt "Maximum Y coordinate value." + default 4095 if LV_PREDEFINED_PINS_38V4 + default 1900 + + config LV_TOUCH_XY_SWAP + bool + prompt "Swap XY." + default y + + config LV_TOUCH_INVERT_X + bool + prompt "Invert X coordinate value." + default y + + config LV_TOUCH_INVERT_Y + bool + prompt "Invert Y coordinate value." + default y + + endmenu + + menu "Touchpanel (FT6X06) Pin Assignments" + depends on LV_TOUCH_CONTROLLER_FT6X06 + + config LV_TOUCH_I2C_SDA + int + prompt "GPIO for SDA (I2C)" + range 0 39 + default 21 + help + Configure the I2C touchpanel SDA pin here. + + config LV_TOUCH_I2C_SCL + int "GPIO for clock signal SCL (I2C)" + range 0 39 + default 22 + help + Configure the I2C touchpanel SCL pin here. + endmenu + + menu "Touchpanel Configuration (FT6X06)" + depends on LV_TOUCH_CONTROLLER_FT6X06 + + config LV_FT6X36_SWAPXY + bool + prompt "Swap X with Y coordinate." + default y + + config LV_FT6X36_INVERT_X + bool + prompt "Invert X coordinate value." + default n + + config LV_FT6X36_INVERT_Y + bool + prompt "Invert Y coordinate value." + default y + + endmenu + + menu "Touchpanel (STMPE610) Pin Assignments" + depends on LV_TOUCH_CONTROLLER_STMPE610 + + config LV_TOUCH_SPI_MISO + int + prompt "GPIO for MISO (Master In Slave Out)" + range 0 39 + default 35 if LV_PREDEFINED_PINS_38V1 + default 19 if LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + default 19 + + help + Configure the touchpanel MISO pin here. + + config LV_TOUCH_SPI_MOSI + int + prompt "GPIO for MOSI (Master Out Slave In)" + range 0 39 + default 32 if LV_PREDEFINED_PINS_38V1 + default 18 if LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + default 23 + + help + Configure the touchpanel MOSI pin here. + + config LV_TOUCH_SPI_CLK + int "GPIO for CLK (SCK / Serial Clock)" + range 0 39 + default 26 if LV_PREDEFINED_PINS_38V1 + default 5 if LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + default 18 + help + Configure the touchpanel CLK pin here. + + config LV_TOUCH_SPI_CS + int "GPIO for CS (Slave Select)" + range 0 39 + default 33 if LV_PREDEFINED_PINS_38V1 + default 32 if LV_PREDEFINED_DISPLAY_ADA_FEATHERWING + default 5 + help + Configure the touchpanel CS pin here. + endmenu + + menu "Touchpanel Configuration (STMPE610)" + depends on LV_TOUCH_CONTROLLER_STMPE610 + + config LV_TOUCH_X_MIN + int + prompt "Minimum X coordinate value." + default 160 + + config LV_TOUCH_Y_MIN + int + prompt "Minimum Y coordinate value." + default 230 + + config LV_TOUCH_X_MAX + int + prompt "Maximum X coordinate value." + default 3800 + + config LV_TOUCH_Y_MAX + int + prompt "Maximum Y coordinate value." + default 3800 + + config LV_TOUCH_XY_SWAP + bool + prompt "Swap XY." + default n + + config LV_TOUCH_INVERT_X + bool + prompt "Invert X coordinate value." + default y + + config LV_TOUCH_INVERT_Y + bool + prompt "Invert Y coordinate value." + default y + endmenu + + menu "Touchpanel (ADCRAW) Pin Assignments" + depends on LV_TOUCH_CONTROLLER_ADCRAW + + config LV_TOUCHSCREEN_RESISTIVE_PIN_YU + int + prompt "GPIO Y+" + default 26 if CONFIG_LV_PREDEFINED_DISPLAY_WROVER4 + default 26 + + help + Configure the touchpanel Y+ pin. Can be a regular GPIO. + + config LV_TOUCHSCREEN_RESISTIVE_PIN_YD + int + prompt "GPIO/ADC Y-" + default 32 if CONFIG_LV_PREDEFINED_DISPLAY_WROVER4 + default 32 + + help + Configure the touchpanel Y- pin. Must be ADC input. + + config LV_TOUCHSCREEN_RESISTIVE_PIN_XL + int + prompt "GPIO X-" + default 27 if CONFIG_LV_PREDEFINED_DISPLAY_WROVER4 + default 27 + + help + Configure the touchpanel X- pin. Can be a regular GPIO. + + config LV_TOUCHSCREEN_RESISTIVE_PIN_XR + int + prompt "GPIO/ADC X+" + default 33 if CONFIG_LV_PREDEFINED_DISPLAY_WROVER4 + default 33 + + help + Configure the touchpanel X- pin. Must be ADC input. + endmenu + + menu "Touchpanel Configuration (ADCRAW)" + depends on LV_TOUCH_CONTROLLER_ADCRAW + + config LV_TOUCH_X_MIN + int + prompt "Minimum X coordinate value." + default 160 + + config LV_TOUCH_Y_MIN + int + prompt "Minimum Y coordinate value." + default 230 + + config LV_TOUCH_X_MAX + int + prompt "Maximum X coordinate value." + default 3800 + + config LV_TOUCH_Y_MAX + int + prompt "Maximum Y coordinate value." + default 3800 + + config LV_TOUCH_XY_SWAP + bool + prompt "Swap XY." + default n + + config LV_TOUCH_INVERT_X + bool + prompt "Invert X coordinate value." + default y + + config LV_TOUCH_INVERT_Y + bool + prompt "Invert Y coordinate value." + default y + + endmenu + + menu "Touchpanel Configuration (RA8875)" + depends on LV_TOUCH_CONTROLLER_RA8875 + + config LV_TOUCH_X_MIN + int + prompt "Minimum X coordinate ADC value" + range 0 1023 + default 0 + + config LV_TOUCH_Y_MIN + int + prompt "Minimum Y coordinate ADC value" + range 0 1023 + default 0 + + config LV_TOUCH_X_MAX + int + prompt "Maximum X coordinate ADC value" + range 0 1023 + default 1023 + + config LV_TOUCH_Y_MAX + int + prompt "Maximum Y coordinate ADC value" + range 0 1023 + default 1023 + + config LV_TOUCH_XY_SWAP + bool + prompt "Swap XY" + default n + + config LV_TOUCH_INVERT_X + bool + prompt "Invert X coordinate value" + default n + + config LV_TOUCH_INVERT_Y + bool + prompt "Invert Y coordinate value" + default n + + config LV_TOUCH_RA8875_SAMPLE_TIME + int + prompt "TP Sample Time Adjusting" + range 0 7 + default 0 + + config LV_TOUCH_RA8875_ADC_CLOCK + int + prompt "ADC Clock Setting" + range 0 7 + default 0 + + config LV_TOUCH_RA8875_WAKEUP_ENABLE + bool + prompt "Touch Panel Wakeup Enable" + default n + + config LV_TOUCH_RA8875_EXTERNAL_VREF + bool + prompt "TP ADC Use External Reference Voltage Source" + default n + + config LV_TOUCH_RA8875_DEBOUNCE_ENABLE + bool + prompt "De-bounce Circuit Enable for Touch Panel Interrupt" + default y + + endmenu + +endmenu diff --git a/lvgl_touch/adcraw.c b/lvgl_touch/adcraw.c new file mode 100644 index 0000000..6170b4c --- /dev/null +++ b/lvgl_touch/adcraw.c @@ -0,0 +1,324 @@ +/** + * @file ADCRAW.c + */ + +#include "adcraw.h" +#include "esp_system.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include + +#if CONFIG_LV_TOUCH_CONTROLLER_ADCRAW + +#define TAG "ADCRAW" +#define CALIBRATIONINSET 1 // range 0 <= CALIBRATIONINSET <= 40 +#define SAMPLE_CALIBRATION_POINTS 4 +// use this scale factor to avoid working in floating point numbers +#define TOUCHSCREEN_RESISTIVE_CALIBRATION_SCALE_FACTOR 8 +#define SCALE_FACTOR (1 << TOUCHSCREEN_RESISTIVE_CALIBRATION_SCALE_FACTOR) +#define CAL_X_INSET (((GetMaxX() + 1) * (CALIBRATIONINSET >> 1)) / 100) +#define CAL_Y_INSET (((GetMaxY() + 1) * (CALIBRATIONINSET >> 1)) / 100) +#define NUMSAMPLES 8 + +static void ad_touch_handler(void *arg); + +static const esp_timer_create_args_t periodic_timer_args = { + .callback = &ad_touch_handler, +}; +static esp_timer_handle_t periodic_timer; + +// Current ADC values for X and Y channels +int16_t adcX, adcY = 0; +int16_t temp_x, temp_y, temp_z1, temp_z2; + +// coefficient values +int _trA, _trB, _trC, _trD; + +int16_t xRawTouch[SAMPLE_CALIBRATION_POINTS]; +int16_t yRawTouch[SAMPLE_CALIBRATION_POINTS]; +TOUCH_STATES state; + +const gpio_num_t yu = TOUCHSCREEN_RESISTIVE_PIN_YU; +const gpio_num_t xl = TOUCHSCREEN_RESISTIVE_PIN_XL; +const gpio_num_t yd = TOUCHSCREEN_RESISTIVE_PIN_YD; +const gpio_num_t xr = TOUCHSCREEN_RESISTIVE_PIN_XR; + +static const int gpio_to_adc[] = { + GPIO_TO_ADC_ELEMENT(TOUCHSCREEN_RESISTIVE_PIN_YD), + GPIO_TO_ADC_ELEMENT(TOUCHSCREEN_RESISTIVE_PIN_XR) +}; + +static void TouchCalculateCalPoints(void) +{ + int32_t trA, trB, trC, trD; // variables for the coefficients + int32_t trAhold, trBhold, trChold, trDhold; + int32_t test1, test2; // temp variables (must be signed type) + + int16_t xPoint[SAMPLE_CALIBRATION_POINTS], yPoint[SAMPLE_CALIBRATION_POINTS]; + + yPoint[0] = yPoint[1] = CAL_Y_INSET; + yPoint[2] = yPoint[3] = (GetMaxY() - CAL_Y_INSET); + xPoint[0] = xPoint[3] = CAL_X_INSET; + xPoint[1] = xPoint[2] = (GetMaxX() - CAL_X_INSET); + + // calculate points transfer function + // based on two simultaneous equations solve for the constants + + // use sample points 1 and 4 + // Dy1 = aTy1 + b; Dy4 = aTy4 + b + // Dx1 = cTx1 + d; Dy4 = aTy4 + b + + test1 = (int32_t)yPoint[0] - (int32_t)yPoint[3]; + test2 = (int32_t)yRawTouch[0] - (int32_t)yRawTouch[3]; + + trA = ((int32_t)((int32_t)test1 * SCALE_FACTOR) / test2); + trB = ((int32_t)((int32_t)yPoint[0] * SCALE_FACTOR) - (trA * (int32_t)yRawTouch[0])); + + test1 = (int32_t)xPoint[0] - (int32_t)xPoint[2]; + test2 = (int32_t)xRawTouch[0] - (int32_t)xRawTouch[2]; + + trC = ((int32_t)((int32_t)test1 * SCALE_FACTOR) / test2); + trD = ((int32_t)((int32_t)xPoint[0] * SCALE_FACTOR) - (trC * (int32_t)xRawTouch[0])); + + trAhold = trA; + trBhold = trB; + trChold = trC; + trDhold = trD; + + // use sample points 2 and 3 + // Dy2 = aTy2 + b; Dy3 = aTy3 + b + // Dx2 = cTx2 + d; Dy3 = aTy3 + b + + test1 = (int32_t)yPoint[1] - (int32_t)yPoint[2]; + test2 = (int32_t)yRawTouch[1] - (int32_t)yRawTouch[2]; + + trA = ((int32_t)(test1 * SCALE_FACTOR) / test2); + trB = ((int32_t)((int32_t)yPoint[1] * SCALE_FACTOR) - (trA * (int32_t)yRawTouch[1])); + + test1 = (int32_t)xPoint[1] - (int32_t)xPoint[3]; + test2 = (int32_t)xRawTouch[1] - (int32_t)xRawTouch[3]; + + trC = ((int32_t)((int32_t)test1 * SCALE_FACTOR) / test2); + trD = ((int32_t)((int32_t)xPoint[1] * SCALE_FACTOR) - (trC * (int32_t)xRawTouch[1])); + + // get the average and use it + _trA = (trA + trAhold) >> 1; + _trB = (trB + trBhold) >> 1; + _trC = (trC + trChold) >> 1; + _trD = (trD + trDhold) >> 1; +} + +void adcraw_init(void) +{ + state = IDLE; // set the state of the state machine to start the sampling + + gpio_set_drive_capability(yu, GPIO_DRIVE_CAP_3); + gpio_set_drive_capability(yd, GPIO_DRIVE_CAP_3); + gpio_set_drive_capability(xl, GPIO_DRIVE_CAP_3); + gpio_set_drive_capability(xr, GPIO_DRIVE_CAP_3); + + ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer)); + ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, 5 * 1000)); //5ms (expressed as microseconds) + + /*Load calibration data*/ + xRawTouch[0] = TOUCHCAL_ULX; + yRawTouch[0] = TOUCHCAL_ULY; + xRawTouch[1] = TOUCHCAL_URX; + yRawTouch[1] = TOUCHCAL_URY; + xRawTouch[3] = TOUCHCAL_LLX; + yRawTouch[3] = TOUCHCAL_LLY; + xRawTouch[2] = TOUCHCAL_LRX; + yRawTouch[2] = TOUCHCAL_LRY; + + TouchCalculateCalPoints(); +} + +static void setup_axis(gpio_num_t plus, gpio_num_t minus, gpio_num_t measure, gpio_num_t ignore) +{ + // Set GPIOs: + // - Float "ignore" and "measure" + gpio_pad_select_gpio(ignore); + gpio_set_direction(ignore, GPIO_MODE_DISABLE); + gpio_set_pull_mode(ignore, GPIO_FLOATING); + gpio_pad_select_gpio(measure); + gpio_set_direction(measure, GPIO_MODE_DISABLE); + gpio_set_pull_mode(measure, GPIO_FLOATING); + // - Set "plus" to 1, "minus" to 0 + gpio_config(&(gpio_config_t) { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = (1ULL << plus) | (1ULL << minus) + }); + gpio_set_level(plus, 1); + gpio_set_level(minus, 0); +} + +static void setup_adc(gpio_num_t measure) +{ + // Init ADC + adc1_channel_t channel = gpio_to_adc[measure]; + adc_gpio_init(ADC_UNIT_1, channel); + adc1_config_width(ADC_WIDTH_BIT_10); + adc1_config_channel_atten(channel, ADC_ATTEN_DB_11); +} + +static void insert_sort(int16_t array[], uint8_t size) { + uint8_t j; + int16_t save; + + for (int i = 1; i < size; i++) { + save = array[i]; + for (j = i; j >= 1 && save < array[j - 1]; j--) + array[j] = array[j - 1]; + array[j] = save; + } +} + +static void ad_touch_handler(void *arg) +{ + (void) arg; + uint8_t i; + int16_t samples[NUMSAMPLES]; + + switch (state) { + case IDLE: + adcX = 0; + adcY = 0; + + case SET_X : + setup_axis(yd, yu, xr, xl); + setup_adc(xr); + state = READ_X; + break; + + case READ_X: + for (i = 0; i < NUMSAMPLES; i++) + samples[i] = adc1_get_raw(gpio_to_adc[xr]); + insert_sort(samples, NUMSAMPLES); + temp_x = samples[NUMSAMPLES / 2]; + + case SET_Y : + setup_axis(xl, xr, yd, yu); + setup_adc(yd); + state = READ_Y; + break; + + case READ_Y: + for (i = 0; i < NUMSAMPLES; i++) + samples[i] = adc1_get_raw(gpio_to_adc[yd]); + insert_sort(samples, NUMSAMPLES); + temp_y = samples[NUMSAMPLES / 2]; + + case SET_Z1 : + setup_axis(yu, xl, yd, xr); + setup_adc(yd); + state = READ_Z1; + break; + + case READ_Z1: + temp_z1 = adc1_get_raw(gpio_to_adc[yd]); + + case SET_Z2 : + setup_axis(yu, xl, xr, yd); + setup_adc(yd); + state = READ_Z2; + break; + + case READ_Z2: + temp_z2 = adc1_get_raw(gpio_to_adc[xr]); + + if (temp_z1 < TOUCHSCREEN_RESISTIVE_PRESS_THRESHOLD) { +#if CONFIG_LV_TOUCH_XY_SWAP + adcX = temp_y; + adcY = temp_x; +#else + adcX = temp_x; + adcY = temp_y; +#endif + } + else { + adcX = -1; + adcY = -1; + } + state = SET_X; + //printf("x: %d y: %d z: %d\n", adcX, adcY, temp_z1 - temp_z2); + break; + } + + return; +} + +static int16_t TouchGetRawX(void) +{ + int16_t x = adcX; + +#if CONFIG_LV_TOUCH_INVERT_X + x = 1023 - x; +#endif + return x; +} + +static int16_t TouchGetX(void) +{ + int16_t result = TouchGetRawX(); + + if (result > 0) { + result = (int16_t)((((int32_t)_trC * result) + _trD) >> TOUCHSCREEN_RESISTIVE_CALIBRATION_SCALE_FACTOR); + } + printf("x: %d\n", result); + return (result); +} + +static int16_t TouchGetRawY(void) +{ + int16_t y = adcY; + +#if CONFIG_LV_TOUCH_INVERT_Y + y = 1023 - y; +#endif + return y; +} + +static int16_t TouchGetY(void) +{ + int16_t result = TouchGetRawY(); + + if (result > 0) { + result = (int16_t)((((int32_t)_trA * result) + (int32_t)_trB) >> TOUCHSCREEN_RESISTIVE_CALIBRATION_SCALE_FACTOR); + } + printf("y: %d\n", result); + return (result); +} + +/** + * Get the current position and state of the touchpad + * @param data store the read data here + * @return false: because no more data to be read + */ +bool adcraw_read(lv_indev_drv_t * drv, lv_indev_data_t * data) +{ + static int16_t last_x = 0; + static int16_t last_y = 0; + + int16_t x, y; + + x = TouchGetX(); + y = TouchGetY(); + + if ((x > 0) && (y > 0)) { + data->point.x = x; + data->point.y = y; + last_x = data->point.x; + last_y = data->point.y; + data->state = LV_INDEV_STATE_PR; + } + else { + data->point.x = last_x; + data->point.y = last_y; + data->state = LV_INDEV_STATE_REL; + } + + return false; +} +#endif //CONFIG_LV_TOUCH_CONTROLLER_ADCRAW diff --git a/lvgl_touch/adcraw.h b/lvgl_touch/adcraw.h new file mode 100644 index 0000000..5c555cc --- /dev/null +++ b/lvgl_touch/adcraw.h @@ -0,0 +1,79 @@ +/** +* @file ADCRAW.h +*/ + +#ifndef ADCRAW_H +#define ADCRAW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "driver/gpio.h" +#include "driver/adc.h" +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#define TOUCHSCREEN_RESISTIVE_PIN_YU CONFIG_LV_TOUCHSCREEN_RESSITIVE_PIN_YU // Y+ any gpio +#define TOUCHSCREEN_RESISTIVE_PIN_YD CONFIG_LV_TOUCHSCREEN_RESISTIVE_PIN_YD // Y- also ADC +#define TOUCHSCREEN_RESISTIVE_PIN_XL CONFIG_LV_TOUCHSCREEN_RESISTIVE_PIN_XL // X- any gpio +#define TOUCHSCREEN_RESISTIVE_PIN_XR CONFIG_LV_TOUCHSCREEN_RESISTIVE_PIN_XR // X+ also ADC + +// Default calibration points +#define TOUCHCAL_ULX 29 // Upper Left X +#define TOUCHCAL_ULY 84 // Upper Left Y +#define TOUCHCAL_URX 828 // Upper Right X +#define TOUCHCAL_URY 60 // Upper Right Y +#define TOUCHCAL_LLX 29 // Lower Left X +#define TOUCHCAL_LLY 928 // Lower Left Y +#define TOUCHCAL_LRX 828 // Lower Right X +#define TOUCHCAL_LRY 928 // Lower Right Y + +#define TOUCHSCREEN_RESISTIVE_PRESS_THRESHOLD 1023 + +/*GetMaxX Macro*/ +#if CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE +#define GetMaxX() (CONFIG_LV_DISPLAY_WIDTH - 1) +#else +#define GetMaxX() (CONFIG_LV_DISPLAY_HEIGHT - 1) +#endif + +/*GetMaxY Macro*/ +#if CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE +#define GetMaxY() (CONFIG_LV_DISPLAY_HEIGHT - 1) +#else +#define GetMaxY() (CONFIG_LV_DISPLAY_WIDTH - 1) +#endif + +#ifndef CONCAT3 +#define _CONCAT3(a,b,c) a ## b ## c +#define CONCAT3(a,b,c) _CONCAT3(a,b,c) +#endif + +#define GPIO_TO_ADC_ELEMENT(x) [x] = CONCAT3(ADC1_GPIO, x, _CHANNEL) + +typedef enum { + IDLE, + SET_X, + SET_Y, + SET_Z1, + SET_Z2, + READ_X, + READ_Y, + READ_Z1, + READ_Z2 +} TOUCH_STATES; + +void adcraw_init(void); +bool adcraw_read(lv_indev_drv_t * drv, lv_indev_data_t * data); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ADCRAW_H */ diff --git a/lvgl_touch/component.mk b/lvgl_touch/component.mk new file mode 100644 index 0000000..5b7c3ee --- /dev/null +++ b/lvgl_touch/component.mk @@ -0,0 +1,4 @@ +# Touch drivers + +COMPONENT_SRCDIRS := . +COMPONENT_ADD_INCLUDEDIRS := . diff --git a/lvgl_touch/ft6x36.c b/lvgl_touch/ft6x36.c new file mode 100644 index 0000000..7cbb45d --- /dev/null +++ b/lvgl_touch/ft6x36.c @@ -0,0 +1,200 @@ +/* +* Copyright © 2020 Wolfgang Christl + +* Permission is hereby granted, free of charge, to any person obtaining a copy of this +* software and associated documentation files (the “Software”), to deal in the Software +* without restriction, including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +* to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include +#include +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include +#else +#include +#endif +#include "ft6x36.h" +#include "tp_i2c.h" + +#define TAG "FT6X36" + + +ft6x36_status_t ft6x36_status; +uint8_t current_dev_addr; // set during init + +esp_err_t ft6x06_i2c_read8(uint8_t slave_addr, uint8_t register_addr, uint8_t *data_buf) { + i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create(); + + i2c_master_start(i2c_cmd); + i2c_master_write_byte(i2c_cmd, (slave_addr << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(i2c_cmd, register_addr, I2C_MASTER_ACK); + + i2c_master_start(i2c_cmd); + i2c_master_write_byte(i2c_cmd, (slave_addr << 1) | I2C_MASTER_READ, true); + + i2c_master_read_byte(i2c_cmd, data_buf, I2C_MASTER_NACK); + i2c_master_stop(i2c_cmd); + esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, i2c_cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(i2c_cmd); + return ret; +} + +/** + * @brief Read the FT6x36 gesture ID. Initialize first! + * @param dev_addr: I2C FT6x36 Slave address. + * @retval The gesture ID or 0x00 in case of failure + */ +uint8_t ft6x36_get_gesture_id() { + if (!ft6x36_status.inited) { + ESP_LOGE(TAG, "Init first!"); + return 0x00; + } + uint8_t data_buf; + esp_err_t ret; + if ((ret = ft6x06_i2c_read8(current_dev_addr, FT6X36_GEST_ID_REG, &data_buf) != ESP_OK)) + ESP_LOGE(TAG, "Error reading from device: %s", esp_err_to_name(ret)); + return data_buf; +} + +/** + * @brief Initialize for FT6x36 communication via I2C + * @param dev_addr: Device address on communication Bus (I2C slave address of FT6X36). + * @retval None + */ +void ft6x06_init(uint16_t dev_addr) { + if (!ft6x36_status.inited) { + +/* I2C master is initialized before calling this function */ +#if 0 + esp_err_t code = i2c_master_init(); +#else + esp_err_t code = ESP_OK; +#endif + + if (code != ESP_OK) { + ft6x36_status.inited = false; + ESP_LOGE(TAG, "Error during I2C init %s", esp_err_to_name(code)); + } else { + ft6x36_status.inited = true; + current_dev_addr = dev_addr; + uint8_t data_buf; + esp_err_t ret; + ESP_LOGI(TAG, "Found touch panel controller"); + if ((ret = ft6x06_i2c_read8(dev_addr, FT6X36_PANEL_ID_REG, &data_buf) != ESP_OK)) + ESP_LOGE(TAG, "Error reading from device: %s", + esp_err_to_name(ret)); // Only show error the first time + ESP_LOGI(TAG, "\tDevice ID: 0x%02x", data_buf); + + ft6x06_i2c_read8(dev_addr, FT6X36_CHIPSELECT_REG, &data_buf); + ESP_LOGI(TAG, "\tChip ID: 0x%02x", data_buf); + + ft6x06_i2c_read8(dev_addr, FT6X36_DEV_MODE_REG, &data_buf); + ESP_LOGI(TAG, "\tDevice mode: 0x%02x", data_buf); + + ft6x06_i2c_read8(dev_addr, FT6X36_FIRMWARE_ID_REG, &data_buf); + ESP_LOGI(TAG, "\tFirmware ID: 0x%02x", data_buf); + + ft6x06_i2c_read8(dev_addr, FT6X36_RELEASECODE_REG, &data_buf); + ESP_LOGI(TAG, "\tRelease code: 0x%02x", data_buf); + } + } +} + +/** + * @brief Get the touch screen X and Y positions values. Ignores multi touch + * @param drv: + * @param data: Store data here + * @retval Always false + */ +bool ft6x36_read(lv_indev_drv_t *drv, lv_indev_data_t *data) { + uint8_t data_xy[4]; // 2 bytes X | 2 bytes Y + uint8_t touch_pnt_cnt; // Number of detected touch points + static int16_t last_x = 0; // 12bit pixel value + static int16_t last_y = 0; // 12bit pixel value + + ft6x06_i2c_read8(current_dev_addr, FT6X36_TD_STAT_REG, &touch_pnt_cnt); + if (touch_pnt_cnt != 1) { // ignore no touch & multi touch + data->point.x = last_x; + data->point.y = last_y; + data->state = LV_INDEV_STATE_REL; + return false; + } + + // Read X value + i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create(); + + i2c_master_start(i2c_cmd); + i2c_master_write_byte(i2c_cmd, (current_dev_addr << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(i2c_cmd, FT6X36_P1_XH_REG, I2C_MASTER_ACK); + + i2c_master_start(i2c_cmd); + i2c_master_write_byte(i2c_cmd, (current_dev_addr << 1) | I2C_MASTER_READ, true); + + i2c_master_read_byte(i2c_cmd, &data_xy[0], I2C_MASTER_ACK); // reads FT6X36_P1_XH_REG + i2c_master_read_byte(i2c_cmd, &data_xy[1], I2C_MASTER_NACK); // reads FT6X36_P1_XL_REG + i2c_master_stop(i2c_cmd); + esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, i2c_cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(i2c_cmd); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Error getting X coordinates: %s", esp_err_to_name(ret)); + data->point.x = last_x; + data->point.y = last_y; + data->state = LV_INDEV_STATE_REL; // no touch detected + return false; + } + + // Read Y value + i2c_cmd = i2c_cmd_link_create(); + + i2c_master_start(i2c_cmd); + i2c_master_write_byte(i2c_cmd, (current_dev_addr << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(i2c_cmd, FT6X36_P1_YH_REG, I2C_MASTER_ACK); + + i2c_master_start(i2c_cmd); + i2c_master_write_byte(i2c_cmd, (current_dev_addr << 1) | I2C_MASTER_READ, true); + + i2c_master_read_byte(i2c_cmd, &data_xy[2], I2C_MASTER_ACK); // reads FT6X36_P1_YH_REG + i2c_master_read_byte(i2c_cmd, &data_xy[3], I2C_MASTER_NACK); // reads FT6X36_P1_YL_REG + i2c_master_stop(i2c_cmd); + ret = i2c_master_cmd_begin(I2C_NUM_0, i2c_cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(i2c_cmd); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Error getting Y coordinates: %s", esp_err_to_name(ret)); + data->point.x = last_x; + data->point.y = last_y; + data->state = LV_INDEV_STATE_REL; // no touch detected + return false; + } + + last_x = ((data_xy[0] & FT6X36_MSB_MASK) << 8) | (data_xy[1] & FT6X36_LSB_MASK); + last_y = ((data_xy[2] & FT6X36_MSB_MASK) << 8) | (data_xy[3] & FT6X36_LSB_MASK); + +#if CONFIG_LV_FT6X36_SWAPXY + int16_t swap_buf = last_x; + last_x = last_y; + last_y = swap_buf; +#endif +#if CONFIG_LV_FT6X36_INVERT_X + last_x = LV_HOR_RES - last_x; +#endif +#if CONFIG_LV_FT6X36_INVERT_Y + last_y = LV_VER_RES - last_y; +#endif + data->point.x = last_x; + data->point.y = last_y; + data->state = LV_INDEV_STATE_PR; + ESP_LOGV(TAG, "X=%u Y=%u", data->point.x, data->point.y); + return false; +} diff --git a/lvgl_touch/ft6x36.h b/lvgl_touch/ft6x36.h new file mode 100644 index 0000000..9674639 --- /dev/null +++ b/lvgl_touch/ft6x36.h @@ -0,0 +1,162 @@ +#ifndef __FT6X06_H +/* +* Copyright © 2020 Wolfgang Christl + +* Permission is hereby granted, free of charge, to any person obtaining a copy of this +* software and associated documentation files (the “Software”), to deal in the Software +* without restriction, including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +* to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#define __FT6X06_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FT6236_I2C_SLAVE_ADDR 0x38 + +/* Maximum border values of the touchscreen pad that the chip can handle */ +#define FT6X36_MAX_WIDTH ((uint16_t)800) +#define FT6X36_MAX_HEIGHT ((uint16_t)480) + +/* Max detectable simultaneous touch points */ +#define FT6X36_MAX_TOUCH_PNTS 2 + +/* Register of the current mode */ +#define FT6X36_DEV_MODE_REG 0x00 + +/* Possible modes as of FT6X36_DEV_MODE_REG */ +#define FT6X36_DEV_MODE_WORKING 0x00 +#define FT6X36_DEV_MODE_FACTORY 0x04 + +#define FT6X36_DEV_MODE_MASK 0x70 +#define FT6X36_DEV_MODE_SHIFT 4 + +/* Gesture ID register */ +#define FT6X36_GEST_ID_REG 0x01 + +/* Possible values returned by FT6X36_GEST_ID_REG */ +#define FT6X36_GEST_ID_NO_GESTURE 0x00 +#define FT6X36_GEST_ID_MOVE_UP 0x10 +#define FT6X36_GEST_ID_MOVE_RIGHT 0x14 +#define FT6X36_GEST_ID_MOVE_DOWN 0x18 +#define FT6X36_GEST_ID_MOVE_LEFT 0x1C +#define FT6X36_GEST_ID_ZOOM_IN 0x48 +#define FT6X36_GEST_ID_ZOOM_OUT 0x49 + +/* Status register: stores number of active touch points (0, 1, 2) */ +#define FT6X36_TD_STAT_REG 0x02 +#define FT6X36_TD_STAT_MASK 0x0F +#define FT6X36_TD_STAT_SHIFT 0x00 + +/* Touch events */ +#define FT6X36_TOUCH_EVT_FLAG_PRESS_DOWN 0x00 +#define FT6X36_TOUCH_EVT_FLAG_LIFT_UP 0x01 +#define FT6X36_TOUCH_EVT_FLAG_CONTACT 0x02 +#define FT6X36_TOUCH_EVT_FLAG_NO_EVENT 0x03 + +#define FT6X36_TOUCH_EVT_FLAG_SHIFT 6 +#define FT6X36_TOUCH_EVT_FLAG_MASK (3 << FT6X36_TOUCH_EVT_FLAG_SHIFT) + +#define FT6X36_MSB_MASK 0x0F +#define FT6X36_MSB_SHIFT 0 +#define FT6X36_LSB_MASK 0xFF +#define FT6X36_LSB_SHIFT 0 + +#define FT6X36_P1_XH_REG 0x03 +#define FT6X36_P1_XL_REG 0x04 +#define FT6X36_P1_YH_REG 0x05 +#define FT6X36_P1_YL_REG 0x06 + +#define FT6X36_P1_WEIGHT_REG 0x07 /* Register reporting touch pressure - read only */ +#define FT6X36_TOUCH_WEIGHT_MASK 0xFF +#define FT6X36_TOUCH_WEIGHT_SHIFT 0 + +#define FT6X36_P1_MISC_REG 0x08 /* Touch area register */ + +#define FT6X36_TOUCH_AREA_MASK (0x04 << 4) /* Values related to FT6X36_Pn_MISC_REG */ +#define FT6X36_TOUCH_AREA_SHIFT 0x04 + +#define FT6X36_P2_XH_REG 0x09 +#define FT6X36_P2_XL_REG 0x0A +#define FT6X36_P2_YH_REG 0x0B +#define FT6X36_P2_YL_REG 0x0C +#define FT6X36_P2_WEIGHT_REG 0x0D +#define FT6X36_P2_MISC_REG 0x0E + +/* Threshold for touch detection */ +#define FT6X36_TH_GROUP_REG 0x80 +#define FT6X36_THRESHOLD_MASK 0xFF /* Values FT6X36_TH_GROUP_REG : threshold related */ +#define FT6X36_THRESHOLD_SHIFT 0 + +#define FT6X36_TH_DIFF_REG 0x85 /* Filter function coefficients */ + +#define FT6X36_CTRL_REG 0x86 /* Control register */ + +#define FT6X36_CTRL_KEEP_ACTIVE_MODE 0x00 /* Will keep the Active mode when there is no touching */ +#define FT6X36_CTRL_KEEP_AUTO_SWITCH_MONITOR_MODE 0x01 /* Switching from Active mode to Monitor mode automatically when there is no touching */ + +#define FT6X36_TIME_ENTER_MONITOR_REG 0x87 /* The time period of switching from Active mode to Monitor mode when there is no touching */ + +#define FT6X36_PERIOD_ACTIVE_REG 0x88 /* Report rate in Active mode */ +#define FT6X36_PERIOD_MONITOR_REG 0x89 /* Report rate in Monitor mode */ + +#define FT6X36_RADIAN_VALUE_REG 0x91 /* The value of the minimum allowed angle while Rotating gesture mode */ + +#define FT6X36_OFFSET_LEFT_RIGHT_REG 0x92 /* Maximum offset while Moving Left and Moving Right gesture */ +#define FT6X36_OFFSET_UP_DOWN_REG 0x93 /* Maximum offset while Moving Up and Moving Down gesture */ + +#define FT6X36_DISTANCE_LEFT_RIGHT_REG 0x94 /* Minimum distance while Moving Left and Moving Right gesture */ +#define FT6X36_DISTANCE_UP_DOWN_REG 0x95 /* Minimum distance while Moving Up and Moving Down gesture */ + +#define FT6X36_LIB_VER_H_REG 0xA1 /* High 8-bit of LIB Version info */ +#define FT6X36_LIB_VER_L_REG 0xA2 /* Low 8-bit of LIB Version info */ + +#define FT6X36_CHIPSELECT_REG 0xA3 /* 0x36 for ft6236; 0x06 for ft6206 */ + +#define FT6X36_POWER_MODE_REG 0xA5 +#define FT6X36_FIRMWARE_ID_REG 0xA6 +#define FT6X36_RELEASECODE_REG 0xAF +#define FT6X36_PANEL_ID_REG 0xA8 +#define FT6X36_OPMODE_REG 0xBC + + +typedef struct { + bool inited; +} ft6x36_status_t; + +/** + * @brief Initialize for FT6x36 communication via I2C + * @param dev_addr: Device address on communication Bus (I2C slave address of FT6X36). + * @retval None + */ +void ft6x06_init(uint16_t dev_addr); + +uint8_t ft6x36_get_gesture_id(); + +/** + * @brief Get the touch screen X and Y positions values. Ignores multi touch + * @param drv: + * @param data: Store data here + * @retval Always false + */ +bool ft6x36_read(lv_indev_drv_t *drv, lv_indev_data_t *data); + +#ifdef __cplusplus +} +#endif +#endif /* __FT6X06_H */ diff --git a/lvgl_touch/ra8875_touch.c b/lvgl_touch/ra8875_touch.c new file mode 100644 index 0000000..3340e4f --- /dev/null +++ b/lvgl_touch/ra8875_touch.c @@ -0,0 +1,181 @@ +/** + * @file RA8875_TOUCH.c + */ + +/********************* + * INCLUDES + *********************/ +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include + +#include "ra8875_touch.h" + +#include "../lvgl_tft/ra8875.h" + +#ifndef CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875 + #error "Display controller must be RA8875" +#endif + +/********************* + * DEFINES + *********************/ +#define DEBUG false +#define TAG "RA8875-Touch" + +#define DIV_ROUND_UP(n, d) (((n)+(d)-1)/(d)) + +#define INTC2_TP_INT (0x04) + +#define TPCR0_ADC_TIMING ((CONFIG_LV_TOUCH_RA8875_SAMPLE_TIME << 4) | CONFIG_LV_TOUCH_RA8875_ADC_CLOCK) +#if LVGL_TOUCH_RA8875_WAKEUP_ENABLE + #define TPCR0_VAL (0x08 | TPCR0_ADC_TIMING) +#else + #define TPCR0_VAL (TPCR0_ADC_TIMING) +#endif + +#if LVGL_TOUCH_RA8875_EXTERNAL_VREF + #if LVGL_TOUCH_RA8875_DEBOUNCE_ENABLE + #define TPCR1_VAL (0x24) + #else + #define TPCR1_VAL (0x20) + #endif +#else + #if LVGL_TOUCH_RA8875_DEBOUNCE_ENABLE + #define TPCR1_VAL (0x04) + #else + #define TPCR1_VAL (0x00) + #endif +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void ra8875_corr(int * x, int * y); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void ra8875_touch_init(void) +{ + struct { + uint8_t cmd; // Register address of command + uint8_t data; // Value to write to register + } init_cmds[] = { + {RA8875_REG_TPCR0, TPCR0_VAL}, // Touch Panel Control Register 0 (TPCR0) + {RA8875_REG_TPCR1, TPCR1_VAL}, // Touch Panel Control Register 1 (TPCR1) + }; + #define INIT_CMDS_SIZE (sizeof(init_cmds)/sizeof(init_cmds[0])) + + ESP_LOGI(TAG, "Initializing RA8875 Touch..."); + + // Send all the commands + for (unsigned int i = 0; i < INIT_CMDS_SIZE; i++) { + ra8875_write_cmd(init_cmds[i].cmd, init_cmds[i].data); + } + ra8875_touch_enable(true); +} + +void ra8875_touch_enable(bool enable) +{ + ESP_LOGI(TAG, "%s touch.", enable ? "Enabling" : "Disabling"); + uint8_t val = enable ? (0x80 | TPCR0_VAL) : (TPCR0_VAL); + ra8875_write_cmd(RA8875_REG_TPCR0, val); +} + +/** + * Get the current position and state of the touchscreen + * @param data store the read data here + * @return false: because no more data to be read + */ +bool ra8875_touch_read(lv_indev_drv_t * drv, lv_indev_data_t * data) +{ + static int x = 0; + static int y = 0; + + int intr = ra8875_read_cmd(RA8875_REG_INTC2); // Interrupt Control Register2 (INTC2) + + data->state = (intr & INTC2_TP_INT) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; + + if (data->state == LV_INDEV_STATE_PR) { + x = ra8875_read_cmd(RA8875_REG_TPXH); // Touch Panel X High Byte Data Register (TPXH) + y = ra8875_read_cmd(RA8875_REG_TPYH); // Touch Panel Y High Byte Data Register (TPYH) + int xy = ra8875_read_cmd(RA8875_REG_TPXYL); // Touch Panel X/Y Low Byte Data Register (TPXYL) + + x = (x << 2) | (xy & 0x03); + y = (y << 2) | ((xy >> 2) & 0x03); + +#if DEBUG + ESP_LOGI(TAG, "Touch Poll Raw: %d,%d", x, y); +#endif + + // Convert to display coordinates + ra8875_corr(&x, &y); + + // Clear interrupt + ra8875_write_cmd(RA8875_REG_INTC2, INTC2_TP_INT); // Interrupt Control Register2 (INTC2) + } + + data->point.x = x; + data->point.y = y; + +#if DEBUG + ESP_LOGI(TAG, "Touch Poll - Event: %d; %d,%d", data->state, data->point.x, data->point.y); +#endif + + return false; +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void ra8875_corr(int * x, int * y) +{ +#if RA8875_XY_SWAP != 0 + int tmp = *x; + *x = *y; + *y = tmp; +#endif + + if ((*x) <= RA8875_X_MIN) { + (*x) = 0; + } else if ((*x) >= RA8875_X_MAX) { + (*x) = LV_HOR_RES-1; + } else { + (*x) = (((*x) - RA8875_X_MIN) * (LV_HOR_RES-1)) / (RA8875_X_MAX - RA8875_X_MIN); + } + + if ((*y) <= RA8875_Y_MIN) { + (*y) = 0; + } else if ((*y) >= RA8875_Y_MAX) { + (*y) = LV_VER_RES-1; + } else { + (*y) = (((*y) - RA8875_Y_MIN) * (LV_VER_RES-1)) / (RA8875_Y_MAX - RA8875_Y_MIN); + } + +#if RA8875_X_INV != 0 + (*x) = (LV_HOR_RES-1) - (*x); +#endif + +#if RA8875_Y_INV != 0 + (*y) = (LV_VER_RES-1) - (*y); +#endif +} diff --git a/lvgl_touch/ra8875_touch.h b/lvgl_touch/ra8875_touch.h new file mode 100644 index 0000000..7d112d3 --- /dev/null +++ b/lvgl_touch/ra8875_touch.h @@ -0,0 +1,56 @@ +/** + * @file RA8875_TOUCH.h + */ + +#ifndef RA8875X_TOUCH__H +#define RA8875X_TOUCH__H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include +#include +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +#define RA8875_X_MIN CONFIG_LV_TOUCH_X_MIN +#define RA8875_Y_MIN CONFIG_LV_TOUCH_Y_MIN +#define RA8875_X_MAX CONFIG_LV_TOUCH_X_MAX +#define RA8875_Y_MAX CONFIG_LV_TOUCH_Y_MAX +#define RA8875_X_INV CONFIG_LV_TOUCH_INVERT_X +#define RA8875_Y_INV CONFIG_LV_TOUCH_INVERT_Y +#define RA8875_XY_SWAP CONFIG_LV_TOUCH_XY_SWAP + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void ra8875_touch_init(void); +void ra8875_touch_enable(bool enable); +bool ra8875_touch_read(lv_indev_drv_t * drv, lv_indev_data_t * data); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* RA8875_TOUCH__H */ diff --git a/lvgl_touch/stmpe610.c b/lvgl_touch/stmpe610.c new file mode 100644 index 0000000..6c5ae60 --- /dev/null +++ b/lvgl_touch/stmpe610.c @@ -0,0 +1,242 @@ +/** + * @file STMPE610.c + */ + +/********************* + * INCLUDES + *********************/ +#include "stmpe610.h" +#include "esp_system.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "tp_spi.h" +#include + +/********************* + * DEFINES + *********************/ +#define TAG "STMPE610" + + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void write_8bit_reg(uint8_t reg, uint8_t val); +static uint16_t read_16bit_reg(uint8_t reg); +static uint8_t read_8bit_reg(uint8_t reg); +static void read_data(int16_t *x, int16_t *y, uint8_t *z); +static bool buffer_empty(); +static void adjust_data(int16_t * x, int16_t * y); + + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the STMPE610 + */ +void stmpe610_init(void) +{ + uint8_t u8; + uint16_t u16; + + ESP_LOGI(TAG, "Initialization."); + + // Get the initial SPI configuration + //u8 = read_8bit_reg(STMPE_SPI_CFG); + //ESP_LOGI(TAG, "SPI_CFG = 0x%x", u8); + + // Attempt a software reset + write_8bit_reg(STMPE_SYS_CTRL1, STMPE_SYS_CTRL1_RESET); + vTaskDelay(10 / portTICK_RATE_MS); + + // Reset the SPI configuration, making sure auto-increment is set + u8 = read_8bit_reg(STMPE_SPI_CFG); + write_8bit_reg(STMPE_SPI_CFG, u8 | STMPE_SPI_CFG_AA); + u8 = read_8bit_reg(STMPE_SPI_CFG); + ESP_LOGI(TAG, "SPI_CFG = 0x%x", u8); + + // Verify SPI communication + u16 = read_16bit_reg(STMPE_CHIP_ID); + if (u16 != 0x811) { + ESP_LOGE(TAG, "Incorrect version: 0x%x", u16); + } + + write_8bit_reg(STMPE_SYS_CTRL2, 0x00); // Disable clocks + write_8bit_reg(STMPE_TSC_CTRL, 0); // Disable to allow writing + + write_8bit_reg(STMPE_TSC_CTRL, + STEMP_TSC_CTRL_TRACK_0 | + STMPE_TSC_CTRL_XYZ | + STMPE_TSC_CTRL_EN); + + write_8bit_reg(STMPE_TSC_CFG, STMPE_TSC_CFG_4SAMPLE | + STMPE_TSC_CFG_DELAY_1MS | + STMPE_TSC_CFG_SETTLE_1MS); + + write_8bit_reg(STMPE_TSC_FRACTION_Z, 0x7); + write_8bit_reg(STMPE_TSC_I_DRIVE, STMPE_TSC_I_DRIVE_50MA); + + write_8bit_reg(STMPE_SYS_CTRL2, 0x04); // GPIO clock off, TSC clock on, ADC clock on + + write_8bit_reg(STMPE_ADC_CTRL1, STMPE_ADC_CTRL1_12BIT | STMPE_ADC_CTRL1_80CLK); + write_8bit_reg(STMPE_ADC_CTRL2, STMPE_ADC_CTRL2_3_25MHZ); + + write_8bit_reg(STMPE_GPIO_ALT_FUNCT, 0x00); // Disable GPIO + + write_8bit_reg(STMPE_FIFO_TH, 1); // Set FIFO threshold + write_8bit_reg(STMPE_FIFO_STA, STMPE_FIFO_STA_RESET); // Assert FIFO reset + write_8bit_reg(STMPE_FIFO_STA, 0); // Deassert FIFO reset + + write_8bit_reg(STMPE_INT_EN, 0x00); // No interrupts + write_8bit_reg(STMPE_INT_STA, 0xFF); // reset all ints +} + +/** + * Get the current position and state of the touchpad + * @param data store the read data here + * @return false: because no more data to be read + */ +bool stmpe610_read(lv_indev_drv_t * drv, lv_indev_data_t * data) +{ + static int16_t last_x = 0; + static int16_t last_y = 0; + bool valid = true; + int c = 0; + int16_t x = 0; + int16_t y = 0; + uint8_t z; + + if ((read_8bit_reg(STMPE_TSC_CTRL) & STMPE_TSC_TOUCHED) == STMPE_TSC_TOUCHED) { + // Making sure that we read all data and return the latest point + while (!buffer_empty()) { + read_data(&x, &y, &z); + c++; + } + + if (c > 0) { + //ESP_LOGI(TAG, "%d: %d %d %d", c, x, y, z); + + adjust_data(&x, &y); + last_x = x; + last_y = y; + //ESP_LOGI(TAG, " ==> %d %d", x, y); + } + + z = read_8bit_reg(STMPE_INT_STA); // Clear interrupts + z = read_8bit_reg(STMPE_FIFO_STA); + if ((z & STMPE_FIFO_STA_OFLOW) == STMPE_FIFO_STA_OFLOW) { + // Clear the FIFO if we discover an overflow + write_8bit_reg(STMPE_FIFO_STA, STMPE_FIFO_STA_RESET); + write_8bit_reg(STMPE_FIFO_STA, 0); // unreset + ESP_LOGE(TAG, "Fifo overflow"); + } + } + + if (c == 0) { + x = last_x; + y = last_y; + valid = false; + } + + data->point.x = (int16_t) x; + data->point.y = (int16_t) y; + data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; + + return false; +} + + +/********************** + * STATIC FUNCTIONS + **********************/ +static void write_8bit_reg(uint8_t reg, uint8_t val) +{ + uint8_t data_send[2]; + + data_send[0] = reg; + data_send[1] = val; + tp_spi_write_reg(data_send, 2); +} + + +static uint16_t read_16bit_reg(uint8_t reg) +{ + uint8_t data_recv[2]; + + tp_spi_read_reg(0x80 | reg, data_recv, 2); + + return data_recv[0] << 8 | data_recv[1]; +} + + +static uint8_t read_8bit_reg(uint8_t reg) +{ + uint8_t data_recv; + + tp_spi_read_reg(0x80 | reg, &data_recv, 1); + + return data_recv; +} + + +static void read_data(int16_t *x, int16_t *y, uint8_t *z) +{ + *x = read_16bit_reg(STMPE_TSC_DATA_X); + *y = read_16bit_reg(STMPE_TSC_DATA_Y); + *z = read_8bit_reg(STMPE_TSC_DATA_Z); +} + + +static bool buffer_empty() +{ + return ((read_8bit_reg(STMPE_FIFO_STA) & STMPE_FIFO_STA_EMPTY) == STMPE_FIFO_STA_EMPTY); +} + + +static void adjust_data(int16_t * x, int16_t * y) +{ +#if STMPE610_XY_SWAP != 0 + int16_t swap_tmp; + swap_tmp = *x; + *x = *y; + *y = swap_tmp; +#endif + + if((*x) > STMPE610_X_MIN)(*x) -= STMPE610_X_MIN; + else(*x) = 0; + + if((*y) > STMPE610_Y_MIN)(*y) -= STMPE610_Y_MIN; + else(*y) = 0; + + (*x) = (uint32_t)((uint32_t)(*x) * LV_HOR_RES) / + (STMPE610_X_MAX - STMPE610_X_MIN); + + (*y) = (uint32_t)((uint32_t)(*y) * LV_VER_RES) / + (STMPE610_Y_MAX - STMPE610_Y_MIN); + +#if STMPE610_X_INV != 0 + (*x) = LV_HOR_RES - (*x); +#endif + +#if STMPE610_Y_INV != 0 + (*y) = LV_VER_RES - (*y); +#endif + +} + diff --git a/lvgl_touch/stmpe610.h b/lvgl_touch/stmpe610.h new file mode 100644 index 0000000..153a6e7 --- /dev/null +++ b/lvgl_touch/stmpe610.h @@ -0,0 +1,185 @@ +/** + * @file STMPE610.h + */ + +#ifndef STMPE610_H +#define STMPE610_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include +#include +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ +/** 16-bit Chip Version **/ +#define STMPE_CHIP_ID 0x00 + +/** Reset Control **/ +#define STMPE_SYS_CTRL1 0x03 +#define STMPE_SYS_CTRL1_RESET 0x02 + +/** Clock Contrl **/ +#define STMPE_SYS_CTRL2 0x04 + +/** SPI Config **/ +#define STMPE_SPI_CFG 0x08 +#define STMPE_SPI_CFG_MODE0 0x00 +#define STMPE_SPI_CFG_MODE1 0x01 +#define STMPE_SPI_CFG_MODE2 0x02 +#define STMPE_SPI_CFG_MODE3 0x03 +#define STMPE_SPI_CFG_AA 0x04 + +/** Touchscreen controller setup **/ +#define STMPE_TSC_CTRL 0x40 +#define STMPE_TSC_CTRL_EN 0x01 +#define STMPE_TSC_CTRL_XYZ 0x00 +#define STMPE_TSC_CTRL_XY 0x02 +#define STEMP_TSC_CTRL_TRACK_0 0x00 +#define STEMP_TSC_CTRL_TRACK_4 0x10 +#define STEMP_TSC_CTRL_TRACK_8 0x20 +#define STEMP_TSC_CTRL_TRACK_16 0x30 +#define STEMP_TSC_CTRL_TRACK_32 0x40 +#define STEMP_TSC_CTRL_TRACK_64 0x50 +#define STEMP_TSC_CTRL_TRACK_92 0x60 +#define STEMP_TSC_CTRL_TRACK_127 0x70 +#define STMPE_TSC_TOUCHED 0x80 + +/** Interrupt control **/ +#define STMPE_INT_CTRL 0x09 +#define STMPE_INT_CTRL_POL_HIGH 0x04 +#define STMPE_INT_CTRL_POL_LOW 0x00 +#define STMPE_INT_CTRL_EDGE 0x02 +#define STMPE_INT_CTRL_LEVEL 0x00 +#define STMPE_INT_CTRL_ENABLE 0x01 +#define STMPE_INT_CTRL_DISABLE 0x00 + +/** Interrupt enable **/ +#define STMPE_INT_EN 0x0A +#define STMPE_INT_EN_TOUCHDET 0x01 +#define STMPE_INT_EN_FIFOTH 0x02 +#define STMPE_INT_EN_FIFOOF 0x04 +#define STMPE_INT_EN_FIFOFULL 0x08 +#define STMPE_INT_EN_FIFOEMPTY 0x10 +#define STMPE_INT_EN_ADC 0x40 +#define STMPE_INT_EN_GPIO 0x80 + +/** Interrupt status **/ +#define STMPE_INT_STA 0x0B +#define STMPE_INT_STA_TOUCHDET 0x01 + +/** ADC control **/ +#define STMPE_ADC_CTRL1 0x20 +#define STMPE_ADC_CTRL1_INT 0x00 +#define STMPE_ADC_CTRL1_EXT 0x02 +#define STMPE_ADC_CTRL1_12BIT 0x08 +#define STMPE_ADC_CTRL1_10BIT 0x00 +#define STMPE_ADC_CTRL1_36CLK 0x00 +#define STMPE_ADC_CTRL1_44CLK 0x10 +#define STMPE_ADC_CTRL1_56CLK 0x20 +#define STMPE_ADC_CTRL1_64CLK 0x30 +#define STMPE_ADC_CTRL1_80CLK 0x40 +#define STMPE_ADC_CTRL1_96CLK 0x50 +#define STMPE_ADC_CTRL1_124CLK 0x60 + +/** ADC control **/ +#define STMPE_ADC_CTRL2 0x21 +#define STMPE_ADC_CTRL2_1_625MHZ 0x00 +#define STMPE_ADC_CTRL2_3_25MHZ 0x01 +#define STMPE_ADC_CTRL2_6_5MHZ 0x02 + +/** Touchscreen controller configuration **/ +#define STMPE_TSC_CFG 0x41 +#define STMPE_TSC_CFG_1SAMPLE 0x00 +#define STMPE_TSC_CFG_2SAMPLE 0x40 +#define STMPE_TSC_CFG_4SAMPLE 0x80 +#define STMPE_TSC_CFG_8SAMPLE 0xC0 +#define STMPE_TSC_CFG_DELAY_10US 0x00 +#define STMPE_TSC_CFG_DELAY_50US 0x08 +#define STMPE_TSC_CFG_DELAY_100US 0x10 +#define STMPE_TSC_CFG_DELAY_500US 0x18 +#define STMPE_TSC_CFG_DELAY_1MS 0x20 +#define STMPE_TSC_CFG_DELAY_5MS 0x28 +#define STMPE_TSC_CFG_DELAY_10MS 0x30 +#define STMPE_TSC_CFG_DELAY_50MS 0x38 +#define STMPE_TSC_CFG_SETTLE_10US 0x00 +#define STMPE_TSC_CFG_SETTLE_100US 0x01 +#define STMPE_TSC_CFG_SETTLE_500US 0x02 +#define STMPE_TSC_CFG_SETTLE_1MS 0x03 +#define STMPE_TSC_CFG_SETTLE_5MS 0x04 +#define STMPE_TSC_CFG_SETTLE_10MS 0x05 +#define STMPE_TSC_CFG_SETTLE_50MS 0x06 +#define STMPE_TSC_CFG_SETTLE_100MS 0x07 + +/** FIFO level to generate interrupt **/ +#define STMPE_FIFO_TH 0x4A + +/** Current filled level of FIFO **/ +#define STMPE_FIFO_SIZE 0x4C + +/** Current status of FIFO **/ +#define STMPE_FIFO_STA 0x4B +#define STMPE_FIFO_STA_RESET 0x01 +#define STMPE_FIFO_STA_OFLOW 0x80 +#define STMPE_FIFO_STA_FULL 0x40 +#define STMPE_FIFO_STA_EMPTY 0x20 +#define STMPE_FIFO_STA_THTRIG 0x10 + +/** Touchscreen controller drive I **/ +#define STMPE_TSC_I_DRIVE 0x58 +#define STMPE_TSC_I_DRIVE_20MA 0x00 +#define STMPE_TSC_I_DRIVE_50MA 0x01 + +/** Data port for TSC data address **/ +#define STMPE_TSC_DATA_X 0x4D +#define STMPE_TSC_DATA_Y 0x4F +#define STMPE_TSC_DATA_Z 0x51 +#define STMPE_TSC_FRACTION_Z 0x56 + +/** GPIO **/ +#define STMPE_GPIO_SET_PIN 0x10 +#define STMPE_GPIO_CLR_PIN 0x11 +#define STMPE_GPIO_DIR 0x13 +#define STMPE_GPIO_ALT_FUNCT 0x17 + + +/** Calibration Constants **/ +#define STMPE610_X_MIN CONFIG_LV_TOUCH_X_MIN +#define STMPE610_Y_MIN CONFIG_LV_TOUCH_Y_MIN +#define STMPE610_X_MAX CONFIG_LV_TOUCH_X_MAX +#define STMPE610_Y_MAX CONFIG_LV_TOUCH_Y_MAX +#define STMPE610_XY_SWAP CONFIG_LV_TOUCH_XY_SWAP +#define STMPE610_X_INV CONFIG_LV_TOUCH_INVERT_X +#define STMPE610_Y_INV CONFIG_LV_TOUCH_INVERT_Y + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void stmpe610_init(void); +bool stmpe610_read(lv_indev_drv_t * drv, lv_indev_data_t * data); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* STMPE610_H */ diff --git a/lvgl_touch/touch_driver.c b/lvgl_touch/touch_driver.c new file mode 100644 index 0000000..b0aed88 --- /dev/null +++ b/lvgl_touch/touch_driver.c @@ -0,0 +1,47 @@ +/** + * @file touch_driver.c + */ + +#include "touch_driver.h" +#include "tp_spi.h" +#include "tp_i2c.h" + + +void touch_driver_init(void) +{ +#if defined (CONFIG_LV_TOUCH_CONTROLLER_XPT2046) + xpt2046_init(); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_FT6X06) + ft6x06_init(FT6236_I2C_SLAVE_ADDR); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_STMPE610) + stmpe610_init(); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_ADCRAW) + adcraw_init(); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_FT81X) + /* nothing to do */ +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_RA8875) + ra8875_touch_init(); +#endif +} + +bool touch_driver_read(lv_indev_drv_t *drv, lv_indev_data_t *data) +{ + bool res = false; + +#if defined (CONFIG_LV_TOUCH_CONTROLLER_XPT2046) + res = xpt2046_read(drv, data); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_FT6X06) + res = ft6x36_read(drv, data); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_STMPE610) + res = stmpe610_read(drv, data); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_ADCRAW) + res = adcraw_read(drv, data); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_FT81X) + res = FT81x_read(drv, data); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_RA8875) + res = ra8875_touch_read(drv, data); +#endif + + return res; +} + diff --git a/lvgl_touch/touch_driver.h b/lvgl_touch/touch_driver.h new file mode 100644 index 0000000..bc92f4f --- /dev/null +++ b/lvgl_touch/touch_driver.h @@ -0,0 +1,52 @@ +/** + * @file touch_driver.h + */ + +#ifndef TOUCH_DRIVER_H +#define TOUCH_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#include +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#if defined (CONFIG_LV_TOUCH_CONTROLLER_XPT2046) +#include "xpt2046.h" +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_FT6X06) +#include "ft6x36.h" +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_STMPE610) +#include "stmpe610.h" +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_ADCRAW) +#include "adcraw.h" +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_FT81X) +#include "FT81x.h" +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_RA8875) +#include "ra8875_touch.h" +#endif + +/********************* +* DEFINES +*********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void touch_driver_init(void); +bool touch_driver_read(lv_indev_drv_t *drv, lv_indev_data_t *data); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* TOUCH_DRIVER_H */ + diff --git a/lvgl_touch/tp_i2c.c b/lvgl_touch/tp_i2c.c new file mode 100644 index 0000000..dc3371c --- /dev/null +++ b/lvgl_touch/tp_i2c.c @@ -0,0 +1,43 @@ +/* +* Copyright © 2020 Wolfgang Christl + +* Permission is hereby granted, free of charge, to any person obtaining a copy of this +* software and associated documentation files (the “Software”), to deal in the Software +* without restriction, including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +* to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include +#include + +#define I2C_MASTER_FREQ_HZ 100000 /* 100kHz*/ +#define I2C_MASTER_TX_BUF_DISABLE 0 /* I2C master doesn't need buffer */ +#define I2C_MASTER_RX_BUF_DISABLE 0 /* I2C master doesn't need buffer */ + +/** + * @brief ESP32 I2C init as master + * @ret ESP32 error code + */ +esp_err_t i2c_master_init(void) { + int i2c_master_port = I2C_NUM_0; + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = CONFIG_LV_TOUCH_I2C_SDA; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.scl_io_num = CONFIG_LV_TOUCH_I2C_SCL; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = I2C_MASTER_FREQ_HZ; + i2c_param_config(i2c_master_port, &conf); + return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); +} diff --git a/lvgl_touch/tp_i2c.h b/lvgl_touch/tp_i2c.h new file mode 100644 index 0000000..5c1eb55 --- /dev/null +++ b/lvgl_touch/tp_i2c.h @@ -0,0 +1,36 @@ +/* +* Copyright © 2020 Wolfgang Christl + +* Permission is hereby granted, free of charge, to any person obtaining a copy of this +* software and associated documentation files (the “Software”), to deal in the Software +* without restriction, including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +* to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef __TS_H +#define __TS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +esp_err_t i2c_master_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __TS_H */ diff --git a/lvgl_touch/tp_spi.c b/lvgl_touch/tp_spi.c new file mode 100644 index 0000000..d2b6107 --- /dev/null +++ b/lvgl_touch/tp_spi.c @@ -0,0 +1,109 @@ +/** + * @file tp_spi.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "tp_spi.h" +#include "touch_driver.h" +#include "esp_system.h" +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include + +#include "../lvgl_helpers.h" +#include "../lvgl_spi_conf.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + + +/********************** + * STATIC PROTOTYPES + **********************/ +static spi_device_handle_t spi; + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void tp_spi_add_device_config(spi_host_device_t host, spi_device_interface_config_t *devcfg) +{ + esp_err_t ret=spi_bus_add_device(host, devcfg, &spi); + assert(ret==ESP_OK); +} + +void tp_spi_add_device(spi_host_device_t host) +{ + spi_device_interface_config_t devcfg={ + .clock_speed_hz = SPI_TOUCH_CLOCK_SPEED_HZ, + .mode = SPI_TOUCH_SPI_MODE, + .spics_io_num=TP_SPI_CS, //CS pin + .queue_size=1, + .pre_cb=NULL, + .post_cb=NULL, + .command_bits = 8, + .address_bits = 0, + .dummy_bits = 0, + .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_NO_DUMMY, + }; + + //Attach the Touch controller to the SPI bus + tp_spi_add_device_config(host, &devcfg); +} + +void tp_spi_xchg(uint8_t* data_send, uint8_t* data_recv, uint8_t byte_count) +{ + spi_transaction_t t = { + .length = byte_count * 8, // SPI transaction length is in bits + .tx_buffer = data_send, + .rx_buffer = data_recv}; + + esp_err_t ret = spi_device_transmit(spi, &t); + assert(ret == ESP_OK); +} + +void tp_spi_write_reg(uint8_t* data, uint8_t byte_count) +{ + spi_transaction_t t = { + .length = byte_count * 8, + .tx_buffer = data, + .flags = 0 + }; + + esp_err_t ret = spi_device_transmit(spi, &t); + assert(ret == ESP_OK); +} + +void tp_spi_read_reg(uint8_t reg, uint8_t* data, uint8_t byte_count) +{ + spi_transaction_t t = { + .length = (byte_count + sizeof(reg)) * 8, + .rxlength = byte_count * 8, + .cmd = reg, + .rx_buffer = data, + .flags = 0 + }; + + // Read - send first byte as command + esp_err_t ret = spi_device_transmit(spi, &t); + assert(ret == ESP_OK); +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/lvgl_touch/tp_spi.h b/lvgl_touch/tp_spi.h new file mode 100644 index 0000000..3fd30c5 --- /dev/null +++ b/lvgl_touch/tp_spi.h @@ -0,0 +1,45 @@ +/** + * @file tp_spi.h + * + */ + +#ifndef TP_SPI_H +#define TP_SPI_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void tp_spi_add_device(spi_host_device_t host); +void tp_spi_add_device_config(spi_host_device_t host, spi_device_interface_config_t *config); +void tp_spi_xchg(uint8_t* data_send, uint8_t* data_recv, uint8_t byte_count); +void tp_spi_write_reg(uint8_t* data, uint8_t byte_count); +void tp_spi_read_reg(uint8_t reg, uint8_t* data, uint8_t byte_count); + +/********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*TP_SPI_H*/ diff --git a/lvgl_touch/xpt2046.c b/lvgl_touch/xpt2046.c new file mode 100644 index 0000000..b6fe51b --- /dev/null +++ b/lvgl_touch/xpt2046.c @@ -0,0 +1,180 @@ +/** + * @file XPT2046.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "xpt2046.h" +#include "esp_system.h" +#include "esp_log.h" +#include "driver/gpio.h" +#include "tp_spi.h" +#include + +/********************* + * DEFINES + *********************/ +#define TAG "XPT2046" + +#define CMD_X_READ 0b10010000 +#define CMD_Y_READ 0b11010000 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void xpt2046_corr(int16_t * x, int16_t * y); +static void xpt2046_avg(int16_t * x, int16_t * y); + +/********************** + * STATIC VARIABLES + **********************/ +int16_t avg_buf_x[XPT2046_AVG]; +int16_t avg_buf_y[XPT2046_AVG]; +uint8_t avg_last; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the XPT2046 + */ +void xpt2046_init(void) +{ + gpio_config_t irq_config = { + .pin_bit_mask = BIT64(XPT2046_IRQ), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + + ESP_LOGI(TAG, "XPT2046 Initialization"); + + esp_err_t ret = gpio_config(&irq_config); + assert(ret == ESP_OK); +} + +/** + * Get the current position and state of the touchpad + * @param data store the read data here + * @return false: because no more data to be read + */ +bool xpt2046_read(lv_indev_drv_t * drv, lv_indev_data_t * data) +{ + static int16_t last_x = 0; + static int16_t last_y = 0; + bool valid = true; + + int16_t x = 0; + int16_t y = 0; + + uint8_t irq = gpio_get_level(XPT2046_IRQ); + + if (irq == 0) { + uint8_t data[2]; + + tp_spi_read_reg(CMD_X_READ, data, 2); + x = (data[0] << 8) | data[1]; + + tp_spi_read_reg(CMD_Y_READ, data, 2); + y = (data[0] << 8) | data[1]; + ESP_LOGI(TAG, "P(%d,%d)", x, y); + + /*Normalize Data back to 12-bits*/ + x = x >> 4; + y = y >> 4; + ESP_LOGI(TAG, "P_norm(%d,%d)", x, y); + + xpt2046_corr(&x, &y); + xpt2046_avg(&x, &y); + last_x = x; + last_y = y; + + ESP_LOGI(TAG, "x = %d, y = %d", x, y); + } else { + x = last_x; + y = last_y; + avg_last = 0; + valid = false; + } + + data->point.x = x; + data->point.y = y; + data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; + + return false; +} + +/********************** + * STATIC FUNCTIONS + **********************/ +static void xpt2046_corr(int16_t * x, int16_t * y) +{ +#if XPT2046_XY_SWAP != 0 + int16_t swap_tmp; + swap_tmp = *x; + *x = *y; + *y = swap_tmp; +#endif + + if((*x) > XPT2046_X_MIN)(*x) -= XPT2046_X_MIN; + else(*x) = 0; + + if((*y) > XPT2046_Y_MIN)(*y) -= XPT2046_Y_MIN; + else(*y) = 0; + + (*x) = (uint32_t)((uint32_t)(*x) * LV_HOR_RES) / + (XPT2046_X_MAX - XPT2046_X_MIN); + + (*y) = (uint32_t)((uint32_t)(*y) * LV_VER_RES) / + (XPT2046_Y_MAX - XPT2046_Y_MIN); + +#if XPT2046_X_INV != 0 + (*x) = LV_HOR_RES - (*x); +#endif + +#if XPT2046_Y_INV != 0 + (*y) = LV_VER_RES - (*y); +#endif + + +} + + +static void xpt2046_avg(int16_t * x, int16_t * y) +{ + /*Shift out the oldest data*/ + uint8_t i; + for(i = XPT2046_AVG - 1; i > 0 ; i--) { + avg_buf_x[i] = avg_buf_x[i - 1]; + avg_buf_y[i] = avg_buf_y[i - 1]; + } + + /*Insert the new point*/ + avg_buf_x[0] = *x; + avg_buf_y[0] = *y; + if(avg_last < XPT2046_AVG) avg_last++; + + /*Sum the x and y coordinates*/ + int32_t x_sum = 0; + int32_t y_sum = 0; + for(i = 0; i < avg_last ; i++) { + x_sum += avg_buf_x[i]; + y_sum += avg_buf_y[i]; + } + + /*Normalize the sums*/ + (*x) = (int32_t)x_sum / avg_last; + (*y) = (int32_t)y_sum / avg_last; +} diff --git a/lvgl_touch/xpt2046.h b/lvgl_touch/xpt2046.h new file mode 100644 index 0000000..8d61ce8 --- /dev/null +++ b/lvgl_touch/xpt2046.h @@ -0,0 +1,57 @@ +/** + * @file XPT2046.h + * + */ + +#ifndef XPT2046_H +#define XPT2046_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include +#include +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ +#define XPT2046_IRQ CONFIG_LV_TOUCH_PIN_IRQ + +#define XPT2046_AVG 4 +#define XPT2046_X_MIN CONFIG_LV_TOUCH_X_MIN +#define XPT2046_Y_MIN CONFIG_LV_TOUCH_Y_MIN +#define XPT2046_X_MAX CONFIG_LV_TOUCH_X_MAX +#define XPT2046_Y_MAX CONFIG_LV_TOUCH_Y_MAX +#define XPT2046_X_INV CONFIG_LV_TOUCH_INVERT_X +#define XPT2046_Y_INV CONFIG_LV_TOUCH_INVERT_Y +#define XPT2046_XY_SWAP CONFIG_LV_TOUCH_XY_SWAP + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void xpt2046_init(void); +bool xpt2046_read(lv_indev_drv_t * drv, lv_indev_data_t * data); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* XPT2046_H */