Move drivers from the lv_port_esp32 to here

This commit is contained in:
C47D 2020-12-17 00:02:55 -06:00
parent 5a32d98f83
commit 08384030b0
69 changed files with 14977 additions and 2 deletions

9
CMakeLists.txt Normal file
View file

@ -0,0 +1,9 @@
if(ESP_PLATFORM)
file(GLOB SOURCES *.c)
idf_component_register(SRCS ${SOURCES}
INCLUDE_DIRS .
REQUIRES lvgl)
endif()

View file

@ -1,2 +1 @@
# lvgl_esp32_drivers
Drivers for ESP32
# lvgl esp32 drivers

4
component.mk Normal file
View file

@ -0,0 +1,4 @@
# LVGL esp32 drivers
COMPONENT_SRCDIRS := .
COMPONENT_ADD_INCLUDEDIRS := .

236
lvgl_helpers.c Normal file
View file

@ -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;
}

85
lvgl_helpers.h Normal file
View file

@ -0,0 +1,85 @@
/**
* @file lvgl_helpers.h
*/
#ifndef LVGL_HELPERS_H
#define LVGL_HELPERS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#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 */

116
lvgl_i2c_conf.h Normal file
View file

@ -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*/

198
lvgl_spi_conf.h Normal file
View file

@ -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*/

53
lvgl_tft/CMakeLists.txt Normal file
View file

@ -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()

842
lvgl_tft/EVE.h Normal file
View file

@ -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 <stddef.h>
#include <stdint.h>
#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_ */

2313
lvgl_tft/EVE_commands.c Normal file

File diff suppressed because it is too large Load diff

203
lvgl_tft/EVE_commands.h Normal file
View file

@ -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_ */

1043
lvgl_tft/EVE_config.h Normal file

File diff suppressed because it is too large Load diff

323
lvgl_tft/FT81x.c Normal file
View file

@ -0,0 +1,323 @@
#include <stdio.h>
#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 - 0x000BBE40)
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));
}

17
lvgl_tft/FT81x.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef FT81X_H_
#define FT81X_H_
#include <stdint.h>
#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_ */

272
lvgl_tft/GC9A01.c Normal file
View file

@ -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);
}

65
lvgl_tft/GC9A01.h Normal file
View file

@ -0,0 +1,65 @@
/**
* @file lv_templ.h
*
*/
#ifndef GC9A01_H
#define GC9A01_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#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*/

1000
lvgl_tft/Kconfig Normal file

File diff suppressed because it is too large Load diff

4
lvgl_tft/component.mk Normal file
View file

@ -0,0 +1,4 @@
# TFT drivers
COMPONENT_SRCDIRS := .
COMPONENT_ADD_INCLUDEDIRS := .

107
lvgl_tft/disp_driver.c Normal file
View file

@ -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
}

86
lvgl_tft/disp_driver.h Normal file
View file

@ -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*/

320
lvgl_tft/disp_spi.c Normal file
View file

@ -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 <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#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);
}
}

81
lvgl_tft/disp_spi.h Normal file
View file

@ -0,0 +1,81 @@
/**
* @file disp_spi.h
*
*/
#ifndef DISP_SPI_H
#define DISP_SPI_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
#include <driver/spi_master.h>
/*********************
* 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*/

310
lvgl_tft/hx8357.c Normal file
View file

@ -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 <esp_log.h>
#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);
}

151
lvgl_tft/hx8357.h Normal file
View file

@ -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 <stdbool.h>
#include <stdint.h>
#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*/

414
lvgl_tft/il3820.c Normal file
View file

@ -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();
}
}

113
lvgl_tft/il3820.h Normal file
View file

@ -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__ */

241
lvgl_tft/ili9341.c Normal file
View file

@ -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);
}

65
lvgl_tft/ili9341.h Normal file
View file

@ -0,0 +1,65 @@
/**
* @file lv_templ.h
*
*/
#ifndef ILI9341_H
#define ILI9341_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#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*/

224
lvgl_tft/ili9481.c Normal file
View file

@ -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);
}

130
lvgl_tft/ili9481.h Normal file
View file

@ -0,0 +1,130 @@
/**
* @file ili9481.h
*/
#ifndef ILI9481_H
#define ILI9481_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include <stdint.h>
#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*/

214
lvgl_tft/ili9486.c Normal file
View file

@ -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);
}

61
lvgl_tft/ili9486.h Normal file
View file

@ -0,0 +1,61 @@
/**
* @file ili9486.h
*
*/
#ifndef ILI9486_H
#define ILI9486_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#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*/

233
lvgl_tft/ili9488.c Normal file
View file

@ -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);
}

167
lvgl_tft/ili9488.h Normal file
View file

@ -0,0 +1,167 @@
/**
* @file ili9488.h
*/
#ifndef ILI9844_H
#define ILI9844_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include <stdint.h>
#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*/

482
lvgl_tft/jd79653a.c Normal file
View file

@ -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 <huming2207@gmail.com>
@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 <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/event_groups.h>
#include <driver/gpio.h>
#include <esp_log.h>
#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!");
}

36
lvgl_tft/jd79653a.h Normal file
View file

@ -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

365
lvgl_tft/ra8875.c Normal file
View file

@ -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);
}

118
lvgl_tft/ra8875.h Normal file
View file

@ -0,0 +1,118 @@
/**
* @file ra8875.h
*
*/
#ifndef RA8875_H
#define RA8875_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#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*/

218
lvgl_tft/sh1107.c Normal file
View file

@ -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);
}

55
lvgl_tft/sh1107.h Normal file
View file

@ -0,0 +1,55 @@
/**
* @file lv_templ.h
*
*/
#ifndef SH1107_H
#define SH1107_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#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*/

240
lvgl_tft/ssd1306.c Normal file
View file

@ -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
**********************/

57
lvgl_tft/ssd1306.h Normal file
View file

@ -0,0 +1,57 @@
/**
* @file lv_templ.h
*
*/
#ifndef SSD1306_H
#define SSD1306_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#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*/

269
lvgl_tft/st7735s.c Normal file
View file

@ -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);
}

149
lvgl_tft/st7735s.h Normal file
View file

@ -0,0 +1,149 @@
/**
* @file lv_templ.h
*
*/
#ifndef ST7735S_H
#define ST7735S_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#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*/

235
lvgl_tft/st7789.c Normal file
View file

@ -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);
}

120
lvgl_tft/st7789.h Normal file
View file

@ -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 */

268
lvgl_tft/uc8151d.c Normal file
View file

@ -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 <huming2207@gmail.com>
@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 <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/event_groups.h>
#include <driver/gpio.h>
#include <esp_log.h>
#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");
}

39
lvgl_tft/uc8151d.h Normal file
View file

@ -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 <huming2207@gmail.com>
@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 <lvgl.h>
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

34
lvgl_touch/CMakeLists.txt Normal file
View file

@ -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()

85
lvgl_touch/FT81x.c Normal file
View file

@ -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 <stddef.h>
#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
**********************/

46
lvgl_touch/FT81x.h Normal file
View file

@ -0,0 +1,46 @@
/**
* @file STMPE610.h
*/
#ifndef FT81X_TOUCH__H
#define FT81X_TOUCH__H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
#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 */

458
lvgl_touch/Kconfig Normal file
View file

@ -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

324
lvgl_touch/adcraw.c Normal file
View file

@ -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 <stddef.h>
#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

79
lvgl_touch/adcraw.h Normal file
View file

@ -0,0 +1,79 @@
/**
* @file ADCRAW.h
*/
#ifndef ADCRAW_H
#define ADCRAW_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#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 */

4
lvgl_touch/component.mk Normal file
View file

@ -0,0 +1,4 @@
# Touch drivers
COMPONENT_SRCDIRS := .
COMPONENT_ADD_INCLUDEDIRS := .

200
lvgl_touch/ft6x36.c Normal file
View file

@ -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 <esp_log.h>
#include <driver/i2c.h>
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include <lvgl.h>
#else
#include <lvgl/lvgl.h>
#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;
}

162
lvgl_touch/ft6x36.h Normal file
View file

@ -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 <lvgl/src/lv_hal/lv_hal.h>
#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 */

181
lvgl_touch/ra8875_touch.c Normal file
View file

@ -0,0 +1,181 @@
/**
* @file RA8875_TOUCH.c
*/
/*********************
* INCLUDES
*********************/
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <stddef.h>
#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
}

56
lvgl_touch/ra8875_touch.h Normal file
View file

@ -0,0 +1,56 @@
/**
* @file RA8875_TOUCH.h
*/
#ifndef RA8875X_TOUCH__H
#define RA8875X_TOUCH__H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
#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 */

242
lvgl_touch/stmpe610.c Normal file
View file

@ -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 <stddef.h>
/*********************
* 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
}

185
lvgl_touch/stmpe610.h Normal file
View file

@ -0,0 +1,185 @@
/**
* @file STMPE610.h
*/
#ifndef STMPE610_H
#define STMPE610_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
#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 */

47
lvgl_touch/touch_driver.c Normal file
View file

@ -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;
}

52
lvgl_touch/touch_driver.h Normal file
View file

@ -0,0 +1,52 @@
/**
* @file touch_driver.h
*/
#ifndef TOUCH_DRIVER_H
#define TOUCH_DRIVER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
#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 */

43
lvgl_touch/tp_i2c.c Normal file
View file

@ -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 <driver/i2c.h>
#include <esp_log.h>
#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);
}

36
lvgl_touch/tp_i2c.h Normal file
View file

@ -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 <stdint.h>
esp_err_t i2c_master_init(void);
#ifdef __cplusplus
}
#endif
#endif /* __TS_H */

109
lvgl_touch/tp_spi.c Normal file
View file

@ -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 <string.h>
#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
**********************/

45
lvgl_touch/tp_spi.h Normal file
View file

@ -0,0 +1,45 @@
/**
* @file tp_spi.h
*
*/
#ifndef TP_SPI_H
#define TP_SPI_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <driver/spi_master.h>
/*********************
* 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*/

180
lvgl_touch/xpt2046.c Normal file
View file

@ -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 <stddef.h>
/*********************
* 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;
}

57
lvgl_touch/xpt2046.h Normal file
View file

@ -0,0 +1,57 @@
/**
* @file XPT2046.h
*
*/
#ifndef XPT2046_H
#define XPT2046_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
#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 */