From acf567aa3b3a7bad40d7d47ea402c5f0fe5cdaa8 Mon Sep 17 00:00:00 2001 From: Ricardo Angeli Date: Sat, 7 Sep 2019 20:35:32 -0400 Subject: [PATCH 01/39] Adding CMake support #68 --- .gitignore | 2 ++ CMakeLists.txt | 7 +++++++ components/mkspiffs/CMakeLists.txt | 3 +++ components/mkspiffs/src/mkspiffs | Bin 0 -> 138168 bytes components/spidriver/CMakeLists.txt | 3 +++ components/spiffs/CMakeLists.txt | 5 +++++ components/spiffs_image/CMakeLists.txt | 3 +++ components/tft/CMakeLists.txt | 4 ++++ main/CMakeLists.txt | 3 +++ main/Kconfig.projbuild | 8 ++++---- 10 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 components/mkspiffs/CMakeLists.txt create mode 100755 components/mkspiffs/src/mkspiffs create mode 100644 components/spidriver/CMakeLists.txt create mode 100644 components/spiffs/CMakeLists.txt create mode 100644 components/spiffs_image/CMakeLists.txt create mode 100644 components/tft/CMakeLists.txt create mode 100644 main/CMakeLists.txt diff --git a/.gitignore b/.gitignore index 6cda5aa..a0859ee 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,8 @@ sdkconfig.lobo BUILD PBUILD build/ +cmake-build-debug/ +.idea/ temp/ local/ *.dis diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..edc507d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --save-temps") +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(tft_demo) diff --git a/components/mkspiffs/CMakeLists.txt b/components/mkspiffs/CMakeLists.txt new file mode 100644 index 0000000..45d489e --- /dev/null +++ b/components/mkspiffs/CMakeLists.txt @@ -0,0 +1,3 @@ +set(COMPONENT_SRCDIRS "") +set(COMPONENT_ADD_INCLUDEDIRS "") +register_component() diff --git a/components/mkspiffs/src/mkspiffs b/components/mkspiffs/src/mkspiffs new file mode 100755 index 0000000000000000000000000000000000000000..1cea3e3dda3b57c43148848925c0fd8df920384f GIT binary patch literal 138168 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 b_^<1)^(T#Qzx>7HDyJ*(zFOzy&HsM^bdITe literal 0 HcmV?d00001 diff --git a/components/spidriver/CMakeLists.txt b/components/spidriver/CMakeLists.txt new file mode 100644 index 0000000..237d596 --- /dev/null +++ b/components/spidriver/CMakeLists.txt @@ -0,0 +1,3 @@ +set(COMPONENT_SRCDIRS ".") +set(COMPONENT_ADD_INCLUDEDIRS ".") +register_component() diff --git a/components/spiffs/CMakeLists.txt b/components/spiffs/CMakeLists.txt new file mode 100644 index 0000000..aa7d982 --- /dev/null +++ b/components/spiffs/CMakeLists.txt @@ -0,0 +1,5 @@ +set(COMPONENT_SRCDIRS ".") +set(COMPONENT_ADD_INCLUDEDIRS ".") +#set(COMPONENT_PRIV_INCLUDEDIRS "") +set(COMPONENT_PRIV_REQUIRES spi_flash) +register_component() diff --git a/components/spiffs_image/CMakeLists.txt b/components/spiffs_image/CMakeLists.txt new file mode 100644 index 0000000..45d489e --- /dev/null +++ b/components/spiffs_image/CMakeLists.txt @@ -0,0 +1,3 @@ +set(COMPONENT_SRCDIRS "") +set(COMPONENT_ADD_INCLUDEDIRS "") +register_component() diff --git a/components/tft/CMakeLists.txt b/components/tft/CMakeLists.txt new file mode 100644 index 0000000..6f5da07 --- /dev/null +++ b/components/tft/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCDIRS ".") +set(COMPONENT_ADD_INCLUDEDIRS ".") +set(COMPONENT_PRIV_REQUIRES spidriver) +register_component() diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..237d596 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,3 @@ +set(COMPONENT_SRCDIRS ".") +set(COMPONENT_ADD_INCLUDEDIRS ".") +register_component() diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index ce369bb..992ecf5 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -1,9 +1,9 @@ menu "TFT Display DEMO Configuration" config SPIFFS_BASE_ADDR - hex "SPIFFS Base address" - range 100000 1FFE000 - default 180000 + int "SPIFFS Base address" + range 1048576 33546240 + default 1572864 help Starting address of the SPIFFS area in ESP32 Flash @@ -36,7 +36,7 @@ config EXAMPLE_DISPLAY_TYPE default EXAMPLE_DISPLAY_TYPE0 help Select predefined display configuration - + config EXAMPLE_DISPLAY_TYPE0 bool "None" config EXAMPLE_DISPLAY_TYPE1 From b5b2f44057d42976e1f0de598bf98a01b7563127 Mon Sep 17 00:00:00 2001 From: Roman Roor Date: Sat, 7 Sep 2019 20:45:35 -0400 Subject: [PATCH 02/39] Fix compile errors. ESP-WROVER-KIT 4.1 menuconfig --- components/spiffs/spiffs_nucleus.c | 2 +- components/tft/tftspi.c | 7 +++--- components/tft/tftspi.h | 36 +++++++++++++++++++++++++++--- main/Kconfig.projbuild | 5 ++++- 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/components/spiffs/spiffs_nucleus.c b/components/spiffs/spiffs_nucleus.c index 44ba711..fb6104e 100644 --- a/components/spiffs/spiffs_nucleus.c +++ b/components/spiffs/spiffs_nucleus.c @@ -679,7 +679,7 @@ static s32_t spiffs_populate_ix_map_v( res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix), (u8_t *)objix); SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix->p_hdr, obj_id, objix->p_hdr.span_ix); + //SPIFFS_VALIDATE_OBJIX(objix->p_hdr, obj_id, objix->p_hdr.span_ix); // check if hdr is ok, and if objix range overlap with ix map range if ((objix->p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == diff --git a/components/tft/tftspi.c b/components/tft/tftspi.c index 090b819..8b2e653 100644 --- a/components/tft/tftspi.c +++ b/components/tft/tftspi.c @@ -2,7 +2,7 @@ * Author: LoBo (loboris@gmail.com, loboris.github) * * Module supporting SPI TFT displays based on ILI9341 & ILI9488 controllers - * + * * HIGH SPEED LOW LEVEL DISPLAY FUNCTIONS * USING DIRECT or DMA SPI TRANSFER MODEs * @@ -14,6 +14,7 @@ #include "freertos/task.h" #include "esp_heap_caps.h" #include "soc/spi_reg.h" +#include "driver/gpio.h" // ==================================================== @@ -162,7 +163,7 @@ void IRAM_ATTR disp_spi_transfer_cmd_data(int8_t cmd, uint8_t *data, uint32_t le if (idx == 16) { // SPI buffer full, send data _spi_transfer_start(disp_spi, bits, 0); - + bits = 0; idx = 0; bidx = 0; @@ -875,7 +876,7 @@ void TFT_PinsInit() #if USE_TOUCH gpio_pad_select_gpio(PIN_NUM_TCS); gpio_set_direction(PIN_NUM_TCS, GPIO_MODE_OUTPUT); -#endif +#endif #if PIN_NUM_BCKL gpio_pad_select_gpio(PIN_NUM_BCKL); gpio_set_direction(PIN_NUM_BCKL, GPIO_MODE_OUTPUT); diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index 0cd8c32..5223765 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -1,5 +1,5 @@ /* - * + * * HIGH SPEED LOW LEVEL DISPLAY FUNCTIONS USING DIRECT TRANSFER MODE * */ @@ -38,7 +38,7 @@ #if CONFIG_EXAMPLE_DISPLAY_TYPE == 1 -// ** Set the correct configuration for ESP32-WROVER-KIT v3 +// ** Set the correct configuration for ESP-WROVER-KIT v3 // -------------------------------------------------------- #define DEFAULT_DISP_TYPE DISP_TYPE_ST7789V #define DEFAULT_TFT_DISPLAY_WIDTH 240 @@ -124,6 +124,36 @@ #define PIN_BCKL_OFF 0 // GPIO value for backlight OFF // --------------------------------------------------------- +#elif CONFIG_EXAMPLE_DISPLAY_TYPE == 4 + +// ** Set the correct configuration for ESP-WROVER-KIT v4.1 +// -------------------------------------------------------- +#define DEFAULT_DISP_TYPE DISP_TYPE_ILI9341 +#define DEFAULT_TFT_DISPLAY_WIDTH 240 +#define DEFAULT_TFT_DISPLAY_HEIGHT 320 +#define DISP_COLOR_BITS_24 0x66 +#define DEFAULT_GAMMA_CURVE 0 +#define DEFAULT_SPI_CLOCK 26000000 +#define TFT_INVERT_ROTATION 0 +#define TFT_INVERT_ROTATION1 0 +#define TFT_INVERT_ROTATION2 0 +#define TFT_RGB_BGR 0x08 + +#define USE_TOUCH TOUCH_TYPE_NONE + +#define PIN_NUM_MISO 25 // SPI MISO +#define PIN_NUM_MOSI 23 // SPI MOSI +#define PIN_NUM_CLK 19 // SPI CLOCK pin +#define PIN_NUM_CS 22 // Display CS pin +#define PIN_NUM_DC 21 // Display command/data pin +#define PIN_NUM_TCS 0 // Touch screen CS pin + +#define PIN_NUM_RST 18 // GPIO used for RESET control +#define PIN_NUM_BCKL 5 // GPIO used for backlight control +#define PIN_BCKL_ON 0 // GPIO value for backlight ON +#define PIN_BCKL_OFF 1 // GPIO value for backlight OFF +// -------------------------------------------------------- + #else // Configuration for other boards, set the correct values for the display used @@ -674,4 +704,4 @@ uint32_t stmpe610_getID(); // =============================================================================== -#endif \ No newline at end of file +#endif diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 992ecf5..6566e2e 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -30,6 +30,7 @@ config EXAMPLE_DISPLAY_TYPE default 1 if EXAMPLE_DISPLAY_TYPE1 default 2 if EXAMPLE_DISPLAY_TYPE2 default 3 if EXAMPLE_DISPLAY_TYPE3 + default 4 if EXAMPLE_DISPLAY_TYPE4 choice prompt "Select predefined display configuration" @@ -40,7 +41,9 @@ config EXAMPLE_DISPLAY_TYPE config EXAMPLE_DISPLAY_TYPE0 bool "None" config EXAMPLE_DISPLAY_TYPE1 - bool "ESP-WROVER-KIT Display" + bool "ESP-WROVER-KIT v3 Display (ST7789V)" + config EXAMPLE_DISPLAY_TYPE4 + bool "ESP-WROVER-KIT v4.1 Display (ILI9341)" config EXAMPLE_DISPLAY_TYPE2 bool "Adafruit TFT Feather display" config EXAMPLE_DISPLAY_TYPE3 From d2913ddb0a9ca48dec9694affc6e79fbf705f59c Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sun, 8 Sep 2019 11:14:32 -0400 Subject: [PATCH 03/39] Make invert and RST pins configurable. --- components/tft/Kconfig | 16 ++++++++++++++++ components/tft/tftspi.h | 13 ++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 components/tft/Kconfig diff --git a/components/tft/Kconfig b/components/tft/Kconfig new file mode 100644 index 0000000..82217d6 --- /dev/null +++ b/components/tft/Kconfig @@ -0,0 +1,16 @@ +menu "TFT Display" + +config TFT_INVERT_ROTATION1 + bool "Invert rotation1." + default n + help + If text is backwards on your display, try enabling this. + + +config TFT_PIN_NUM_RST + int "GPIO for Reset" + default 0 + help + If not using an EXAMPLE display type, configure the reset pin here. + +endmenu diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index 5223765..04e9ea7 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -166,7 +166,12 @@ // for example the one on ESP-WROWER-KIT ### // ############################################# #define TFT_INVERT_ROTATION 0 + +#if defined(CONFIG_TFT_INVERT_ROTATION1) +#define TFT_INVERT_ROTATION1 CONFIG_TFT_INVERT_ROTATION1 +#else #define TFT_INVERT_ROTATION1 0 +#endif // ################################################ // ### SET TO 0X00 FOR DISPLAYS WITH RGB MATRIX ### @@ -191,7 +196,13 @@ // -------------------------------------------------------------- // ** Set Reset and Backlight pins to 0 if not used ! // ** If you want to use them, set them to some valid GPIO number -#define PIN_NUM_RST 0 // GPIO used for RESET control + +// GPIO used for RESET control +#if defined(CONFIG_TFT_PIN_NUM_RST) +#define PIN_NUM_RST CONFIG_TFT_PIN_NUM_RST +#else +#define PIN_NUM_RST 0 +#endif #define PIN_NUM_BCKL 0 // GPIO used for backlight control #define PIN_BCKL_ON 0 // GPIO value for backlight ON From 3dfc5b2ac74a5458ee6c5e937aa73facb664148a Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sat, 14 Sep 2019 16:34:12 -0400 Subject: [PATCH 04/39] Update demo for current framework. --- main/tft_demo.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/tft_demo.c b/main/tft_demo.c index d116d32..4e4ddf4 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -34,7 +34,7 @@ #include #include #include "lwip/err.h" -#include "apps/sntp/sntp.h" +#include "esp_sntp.h" #include "esp_log.h" #include "nvs_flash.h" @@ -922,7 +922,7 @@ static void poly_demo() TFT_drawPolygon(x, y, sides[i], r, TFT_BLACK, TFT_BLACK, oldrot, 1); TFT_drawPolygon(x, y, sides[i], r, color[i], color[i], rot, 1); r -= 16; - if (r <= 0) break; + if (r <= 0) { break; }; n += 2; } Wait(100); @@ -944,7 +944,7 @@ static void poly_demo() for (i=5; i>=0; i--) { TFT_drawPolygon(x, y, sides[i], r, color[i], fill[i], rot, 2); r -= 16; - if (r <= 0) break; + if (r <= 0) { break; } n += 2; } Wait(500); From 84fe00049e91f87155276c37a62c38a6627a1134 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sat, 14 Sep 2019 20:32:19 -0400 Subject: [PATCH 05/39] Add SPIFFS Kconfig; use idf_register_component --- components/spidriver/CMakeLists.txt | 5 ++--- components/spiffs/CMakeLists.txt | 8 +++----- components/spiffs/Kconfig | 27 +++++++++++++++++++++++++++ components/tft/CMakeLists.txt | 8 ++++---- 4 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 components/spiffs/Kconfig diff --git a/components/spidriver/CMakeLists.txt b/components/spidriver/CMakeLists.txt index 237d596..2422429 100644 --- a/components/spidriver/CMakeLists.txt +++ b/components/spidriver/CMakeLists.txt @@ -1,3 +1,2 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") -register_component() +FILE(GLOB SOURCES *.c) +idf_component_register(SRCS ${SOURCES} INCLUDE_DIRS ".") diff --git a/components/spiffs/CMakeLists.txt b/components/spiffs/CMakeLists.txt index aa7d982..487e50b 100644 --- a/components/spiffs/CMakeLists.txt +++ b/components/spiffs/CMakeLists.txt @@ -1,5 +1,3 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") -#set(COMPONENT_PRIV_INCLUDEDIRS "") -set(COMPONENT_PRIV_REQUIRES spi_flash) -register_component() +FILE(GLOB SOURCES *.c) +idf_component_register(SRCS ${SOURCES} INCLUDE_DIRS "." + REQUIRES spi_flash) diff --git a/components/spiffs/Kconfig b/components/spiffs/Kconfig new file mode 100644 index 0000000..08e6faa --- /dev/null +++ b/components/spiffs/Kconfig @@ -0,0 +1,27 @@ +menu "TFT SPIFFS Configuration" + +config SPIFFS_BASE_ADDR + int "SPIFFS Base address" + range 1048576 33546240 + default 1572864 + help + Starting address of the SPIFFS area in ESP32 Flash + +config SPIFFS_SIZE + int "SPIFFS Size in bytes" + range 262144 2097152 + default 1048576 + +config SPIFFS_LOG_BLOCK_SIZE + int "SPIFFS Logical block size" + range 4098 65536 + default 8192 + +config SPIFFS_LOG_PAGE_SIZE + int "SPIFFS Logical page size" + range 256 2048 + default 256 + help + Set it to the physical page size og the used SPI Flash chip. + +endmenu diff --git a/components/tft/CMakeLists.txt b/components/tft/CMakeLists.txt index 6f5da07..a5f8712 100644 --- a/components/tft/CMakeLists.txt +++ b/components/tft/CMakeLists.txt @@ -1,4 +1,4 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") -set(COMPONENT_PRIV_REQUIRES spidriver) -register_component() +FILE(GLOB SOURCES *.c) +idf_component_register(SRCS ${SOURCES} + INCLUDE_DIRS "." + REQUIRES spidriver) From 3b6270748feee7b6979709bf543638faaee7eca9 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sat, 14 Sep 2019 23:02:51 -0400 Subject: [PATCH 06/39] Silence warnings. C++ compatibility. --- components/spidriver/spi_master_lobo.h | 3 +-- components/tft/tft.h | 9 +++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/components/spidriver/spi_master_lobo.h b/components/spidriver/spi_master_lobo.h index 89144fb..bca00ca 100644 --- a/components/spidriver/spi_master_lobo.h +++ b/components/spidriver/spi_master_lobo.h @@ -21,9 +21,8 @@ #include "freertos/semphr.h" #include "soc/spi_struct.h" -#include "esp_intr.h" #include "esp_intr_alloc.h" -#include "rom/lldesc.h" +#include "esp32/rom/lldesc.h" #ifdef __cplusplus diff --git a/components/tft/tft.h b/components/tft/tft.h index 7a2cede..748445e 100644 --- a/components/tft/tft.h +++ b/components/tft/tft.h @@ -8,6 +8,11 @@ #define _TFT_H_ #include + +#ifdef __cplusplus +extern "C" { +#endif + #include "tftspi.h" typedef struct { @@ -677,3 +682,7 @@ int compile_font_file(char *fontfile, uint8_t dbg); void getFontCharacters(uint8_t *buf); #endif + +#ifdef __cplusplus +} +#endif From ab5ef940a3e7b1b91d500d306e4666905fe57e81 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sun, 15 Sep 2019 09:20:50 -0400 Subject: [PATCH 07/39] Remove unused imports. SPIFFS config has moved. --- main/Kconfig.projbuild | 24 ------------------------ main/tft_demo.c | 14 +------------- 2 files changed, 1 insertion(+), 37 deletions(-) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 6566e2e..58b5f85 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -1,29 +1,5 @@ menu "TFT Display DEMO Configuration" -config SPIFFS_BASE_ADDR - int "SPIFFS Base address" - range 1048576 33546240 - default 1572864 - help - Starting address of the SPIFFS area in ESP32 Flash - -config SPIFFS_SIZE - int "SPIFFS Size in bytes" - range 262144 2097152 - default 1048576 - -config SPIFFS_LOG_BLOCK_SIZE - int "SPIFFS Logical block size" - range 4098 65536 - default 8192 - -config SPIFFS_LOG_PAGE_SIZE - int "SPIFFS Logical page size" - range 256 2048 - default 256 - help - Set it to the phisycal page size og the used SPI Flash chip. - config EXAMPLE_DISPLAY_TYPE int default 0 if EXAMPLE_DISPLAY_TYPE0 diff --git a/main/tft_demo.c b/main/tft_demo.c index 4e4ddf4..468719b 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -8,17 +8,12 @@ */ #include -#include -#include #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "esp_system.h" -#include "driver/gpio.h" -#include "esp_system.h" -#include "esp_heap_caps.h" + #include "tftspi.h" #include "tft.h" #include "spiffs_vfs.h" @@ -26,14 +21,7 @@ #ifdef CONFIG_EXAMPLE_USE_WIFI #include "esp_wifi.h" -#include "esp_system.h" -#include "esp_event.h" -#include "esp_event_loop.h" #include "freertos/event_groups.h" -#include "esp_attr.h" -#include -#include -#include "lwip/err.h" #include "esp_sntp.h" #include "esp_log.h" #include "nvs_flash.h" From 1e04a2b62eb02a78a6fe75e5f9799b10f8d2b88d Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sun, 15 Sep 2019 11:05:49 -0400 Subject: [PATCH 08/39] Make height/width configurable and namespace the globals. --- README.md | 6 ++-- components/tft/Kconfig | 13 +++++++- components/tft/tft.c | 42 +++++++++++--------------- components/tft/tft.h | 2 +- components/tft/tftspi.c | 38 +++++++++++------------ components/tft/tftspi.h | 7 ++--- main/tft_demo.c | 67 ++++++++++++++++++----------------------- 7 files changed, 85 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index 23d5fa5..d802c87 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ If you are using the esp-idf v2.1, checkout the commit *0518df81a6566820352dad7b * **disp_select()** Activate display's CS line * **disp_deselect()** Deactivate display's CS line * **find_rd_speed()** Find maximum spi clock for successful read from display RAM - * **TFT_display_init()** Perform display initialization sequence. Sets orientation to landscape; clears the screen. SPI interface must already be setup, *tft_disp_type*, *_width*, *_height* variables must be set. + * **TFT_display_init()** Perform display initialization sequence. Sets orientation to landscape; clears the screen. SPI interface must already be setup, *tft_disp_type*, *tft_width*, *tft_height* variables must be set. * **HSBtoRGB** Converts the components of a color, as specified by the HSB model to an equivalent set of values for the default RGB model. * **TFT_setGammaCurve()** Select one of 4 Gamma curves * **compile_font_file** Function which compiles font c source file to font file which can be used in *TFT_setFont()* function to select external font. Created file have the same name as source file and extension *.fnt* @@ -125,8 +125,8 @@ If you are using the esp-idf v2.1, checkout the commit *0518df81a6566820352dad7b * **tp_caly** touch screen Y calibration constant * **gray_scale** convert all colors to gray scale if set to 1 * **max_rdclock** current spi clock for reading from display RAM - * **_width** screen width (smaller dimension) in pixels - * **_height** screen height (larger dimension) in pixels + * **tft_width** screen width (smaller dimension) in pixels + * **tft_height** screen height (larger dimension) in pixels * **tft_disp_type** current display type (DISP_TYPE_ILI9488 or DISP_TYPE_ILI9341) --- diff --git a/components/tft/Kconfig b/components/tft/Kconfig index 82217d6..bc3b62e 100644 --- a/components/tft/Kconfig +++ b/components/tft/Kconfig @@ -6,11 +6,22 @@ config TFT_INVERT_ROTATION1 help If text is backwards on your display, try enabling this. - config TFT_PIN_NUM_RST int "GPIO for Reset" default 0 help If not using an EXAMPLE display type, configure the reset pin here. +config TFT_DISPLAY_WIDTH + int "TFT display width in pixels." + default 240 + help + The smaller dimension (in portrait). + +config TFT_DISPLAY_HEIGHT + int "TFT display height in pixels." + default 320 + help + The smaller dimension (in portrait). + endmenu diff --git a/components/tft/tft.c b/components/tft/tft.c index 93d55f3..4b4a783 100644 --- a/components/tft/tft.c +++ b/components/tft/tft.c @@ -10,21 +10,15 @@ #include #include #include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_system.h" #include "tft.h" -#include "time.h" #include -#include "rom/tjpgd.h" -#include "esp_heap_caps.h" -#include "tftspi.h" +#include "esp32/rom/tjpgd.h" #define DEG_TO_RAD 0.01745329252 -#define RAD_TO_DEG 57.295779513 #define deg_to_rad 0.01745329252 + 3.14159265359 #define swap(a, b) { int16_t t = a; a = b; b = t; } -#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) + #if !defined(max) #define max(A,B) ( (A) > (B) ? (A):(B)) #endif @@ -87,8 +81,8 @@ uint32_t tp_caly = 122224794; dispWin_t dispWin = { .x1 = 0, .y1 = 0, - .x2 = DEFAULT_TFT_DISPLAY_WIDTH, - .y2 = DEFAULT_TFT_DISPLAY_HEIGHT, + .x2 = CONFIG_TFT_DISPLAY_WIDTH, + .y2 = CONFIG_TFT_DISPLAY_HEIGHT, }; Font cfont = { @@ -302,7 +296,7 @@ void TFT_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) { //================================== void TFT_fillScreen(color_t color) { - TFT_pushColorRep(0, 0, _width-1, _height-1, color, (uint32_t)(_height*_width)); + TFT_pushColorRep(0, 0, tft_width-1, tft_height-1, color, (uint32_t)(tft_height*tft_width)); } //================================== @@ -2064,8 +2058,8 @@ void TFT_setRotation(uint8_t rot) { dispWin.x1 = 0; dispWin.y1 = 0; - dispWin.x2 = _width-1; - dispWin.y2 = _height-1; + dispWin.x2 = tft_width-1; + dispWin.y2 = tft_height-1; TFT_fillScreen(_bg); } @@ -2162,8 +2156,8 @@ void TFT_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) dispWin.x2 = x2; dispWin.y2 = y2; - if (dispWin.x2 >= _width) dispWin.x2 = _width-1; - if (dispWin.y2 >= _height) dispWin.y2 = _height-1; + if (dispWin.x2 >= tft_width) dispWin.x2 = tft_width-1; + if (dispWin.y2 >= tft_height) dispWin.y2 = tft_height-1; if (dispWin.x1 > dispWin.x2) dispWin.x1 = dispWin.x2; if (dispWin.y1 > dispWin.y2) dispWin.y1 = dispWin.y2; } @@ -2171,8 +2165,8 @@ void TFT_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) //===================== void TFT_resetclipwin() { - dispWin.x2 = _width-1; - dispWin.y2 = _height-1; + dispWin.x2 = tft_width-1; + dispWin.y2 = tft_height-1; dispWin.x1 = 0; dispWin.y1 = 0; } @@ -2884,8 +2878,8 @@ int TFT_read_touch(int *x, int* y, uint8_t raw) if (((xright - xleft) <= 0) || ((ybottom - ytop) <= 0)) return 0; #if USE_TOUCH == TOUCH_TYPE_XPT2046 - int width = _width; - int height = _height; + int width = tft_width; + int height = tft_height; X = ((X - xleft) * height) / (xright - xleft); Y = ((Y - ytop) * width) / (ybottom - ytop); @@ -2911,11 +2905,11 @@ int TFT_read_touch(int *x, int* y, uint8_t raw) break; } #elif USE_TOUCH == TOUCH_TYPE_STMPE610 - int width = _width; - int height = _height; - if (_width > _height) { - width = _height; - height = _width; + int width = tft_width; + int height = tft_height; + if (tft_width > tft_height) { + width = tft_height; + height = tft_width; } X = ((X - xleft) * width) / (xright - xleft); Y = ((Y - ytop) * height) / (ybottom - ytop); diff --git a/components/tft/tft.h b/components/tft/tft.h index 748445e..bea198a 100644 --- a/components/tft/tft.h +++ b/components/tft/tft.h @@ -515,7 +515,7 @@ void set_7seg_font_atrib(uint8_t l, uint8_t w, int outline, color_t color); void TFT_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2); /* - * Resets the clipping area to full screen (0,0),(_wodth,_height) + * Resets the clipping area to full screen (0,0),(_wodth,tft_height) * */ //---------------------- diff --git a/components/tft/tftspi.c b/components/tft/tftspi.c index 8b2e653..082acf9 100644 --- a/components/tft/tftspi.c +++ b/components/tft/tftspi.c @@ -26,8 +26,8 @@ uint8_t gray_scale = 0; uint32_t max_rdclock = 8000000; // Default display dimensions -int _width = DEFAULT_TFT_DISPLAY_WIDTH; -int _height = DEFAULT_TFT_DISPLAY_HEIGHT; +int tft_width = CONFIG_TFT_DISPLAY_WIDTH; +int tft_height = CONFIG_TFT_DISPLAY_HEIGHT; // Display type, DISP_TYPE_ILI9488 or DISP_TYPE_ILI9341 uint8_t tft_disp_type = DEFAULT_DISP_TYPE; @@ -405,7 +405,7 @@ static void IRAM_ATTR _TFT_pushColorRep(color_t *color, uint32_t len, uint8_t re } */ - buf_colors = ((len > (_width*2)) ? (_width*2) : len); + buf_colors = ((len > (tft_width*2)) ? (tft_width*2) : len); buf_bytes = buf_colors * 3; // Prepare color buffer of maximum 2 color lines @@ -680,17 +680,17 @@ uint32_t find_rd_speed() gray_scale = 0; cur_speed = spi_lobo_get_speed(disp_spi); - color_line = malloc(_width*3); + color_line = malloc(tft_width*3); if (color_line == NULL) goto exit; - line_rdbuf = malloc((_width*3)+1); + line_rdbuf = malloc((tft_width*3)+1); if (line_rdbuf == NULL) goto exit; color_t *rdline = (color_t *)(line_rdbuf+1); // Fill test line with colors color = (color_t){0xEC,0xA8,0x74}; - for (int x=0; x<_width; x++) { + for (int x=0; x height - if (_width < _height) { - tmp = _width; - _width = _height; - _height = tmp; + if (tft_width < tft_height) { + tmp = tft_width; + tft_width = tft_height; + tft_height = tmp; } } else { // in portrait modes must be width < height - if (_width > _height) { - tmp = _width; - _width = _height; - _height = tmp; + if (tft_width > tft_height) { + tmp = tft_width; + tft_width = tft_height; + tft_height = tmp; } } #if TFT_INVERT_ROTATION @@ -938,7 +938,7 @@ void TFT_display_init() // Clear screen _tft_setRotation(PORTRAIT); - TFT_pushColorRep(0, 0, _width-1, _height-1, (color_t){0,0,0}, (uint32_t)(_height*_width)); + TFT_pushColorRep(0, 0, tft_width-1, tft_height-1, (color_t){0,0,0}, (uint32_t)(tft_height*tft_width)); ///Enable backlight #if PIN_NUM_BCKL diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index 04e9ea7..3ff1c8d 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -34,7 +34,6 @@ #define DISP_TYPE_ST7735 3 #define DISP_TYPE_ST7735R 4 #define DISP_TYPE_ST7735B 5 -#define DISP_TYPE_MAX 6 #if CONFIG_EXAMPLE_DISPLAY_TYPE == 1 @@ -241,8 +240,8 @@ extern uint8_t gray_scale; extern uint32_t max_rdclock; // ==== Display dimensions in pixels ============================ -extern int _width; -extern int _height; +extern int tft_width; +extern int tft_height; // ==== Display type, DISP_TYPE_ILI9488 or DISP_TYPE_ILI9341 ==== extern uint8_t tft_disp_type; @@ -700,7 +699,7 @@ void TFT_PinsInit(); // Sets orientation to landscape; clears the screen // * All pins must be configured // * SPI interface must already be setup -// * 'tft_disp_type', 'COLOR_BITS', '_width', '_height' variables must be set +// * 'tft_disp_type', 'COLOR_BITS', 'tft_width', 'tft_height' variables must be set //====================== void TFT_display_init(); diff --git a/main/tft_demo.c b/main/tft_demo.c index 468719b..582cf9c 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -171,8 +171,8 @@ static void _checkTime() _bg = (color_t){ 64, 64, 64 }; TFT_setFont(DEFAULT_FONT, NULL); - TFT_fillRect(1, _height-TFT_getfontheight()-8, _width-3, TFT_getfontheight()+6, _bg); - TFT_print(tmp_buff, CENTER, _height-TFT_getfontheight()-5); + TFT_fillRect(1, tft_height-TFT_getfontheight()-8, tft_width-3, TFT_getfontheight()+6, _bg); + TFT_print(tmp_buff, CENTER, tft_height-TFT_getfontheight()-5); cfont = curr_font; _fg = last_fg; @@ -253,14 +253,14 @@ static color_t random_color() { static void _dispTime() { Font curr_font = cfont; - if (_width < 240) TFT_setFont(DEF_SMALL_FONT, NULL); + if (tft_width < 240) TFT_setFont(DEF_SMALL_FONT, NULL); else TFT_setFont(DEFAULT_FONT, NULL); time(&time_now); time_last = time_now; tm_info = localtime(&time_now); sprintf(tmp_buff, "%02d:%02d:%02d", tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec); - TFT_print(tmp_buff, CENTER, _height-TFT_getfontheight()-5); + TFT_print(tmp_buff, CENTER, tft_height-TFT_getfontheight()-5); cfont = curr_font; } @@ -274,19 +274,19 @@ static void disp_header(char *info) _fg = TFT_YELLOW; _bg = (color_t){ 64, 64, 64 }; - if (_width < 240) TFT_setFont(DEF_SMALL_FONT, NULL); + if (tft_width < 240) TFT_setFont(DEF_SMALL_FONT, NULL); else TFT_setFont(DEFAULT_FONT, NULL); - TFT_fillRect(0, 0, _width-1, TFT_getfontheight()+8, _bg); - TFT_drawRect(0, 0, _width-1, TFT_getfontheight()+8, TFT_CYAN); + TFT_fillRect(0, 0, tft_width-1, TFT_getfontheight()+8, _bg); + TFT_drawRect(0, 0, tft_width-1, TFT_getfontheight()+8, TFT_CYAN); - TFT_fillRect(0, _height-TFT_getfontheight()-9, _width-1, TFT_getfontheight()+8, _bg); - TFT_drawRect(0, _height-TFT_getfontheight()-9, _width-1, TFT_getfontheight()+8, TFT_CYAN); + TFT_fillRect(0, tft_height-TFT_getfontheight()-9, tft_width-1, TFT_getfontheight()+8, _bg); + TFT_drawRect(0, tft_height-TFT_getfontheight()-9, tft_width-1, TFT_getfontheight()+8, TFT_CYAN); TFT_print(info, CENTER, 4); _dispTime(); _bg = TFT_BLACK; - TFT_setclipwin(0,TFT_getfontheight()+9, _width-1, _height-TFT_getfontheight()-10); + TFT_setclipwin(0,TFT_getfontheight()+9, tft_width-1, tft_height-TFT_getfontheight()-10); } //--------------------------------------------- @@ -302,18 +302,18 @@ static void update_header(char *hdr, char *ftr) last_fg = _fg; _fg = TFT_YELLOW; _bg = (color_t){ 64, 64, 64 }; - if (_width < 240) TFT_setFont(DEF_SMALL_FONT, NULL); + if (tft_width < 240) TFT_setFont(DEF_SMALL_FONT, NULL); else TFT_setFont(DEFAULT_FONT, NULL); if (hdr) { - TFT_fillRect(1, 1, _width-3, TFT_getfontheight()+6, _bg); + TFT_fillRect(1, 1, tft_width-3, TFT_getfontheight()+6, _bg); TFT_print(hdr, CENTER, 4); } if (ftr) { - TFT_fillRect(1, _height-TFT_getfontheight()-8, _width-3, TFT_getfontheight()+6, _bg); + TFT_fillRect(1, tft_height-TFT_getfontheight()-8, tft_width-3, TFT_getfontheight()+6, _bg); if (strlen(ftr) == 0) _dispTime(); - else TFT_print(ftr, CENTER, _height-TFT_getfontheight()-5); + else TFT_print(ftr, CENTER, tft_height-TFT_getfontheight()-5); } cfont = curr_font; @@ -338,19 +338,19 @@ static void test_times() { sprintf(tmp_buff, "Clear screen: %u ms", t1); TFT_print(tmp_buff, 0, 140); - color_t *color_line = heap_caps_malloc((_width*3), MALLOC_CAP_DMA); + color_t *color_line = heap_caps_malloc((tft_width*3), MALLOC_CAP_DMA); color_t *gsline = NULL; - if (gray_scale) gsline = malloc(_width*3); + if (gray_scale) gsline = malloc(tft_width*3); if (color_line) { - float hue_inc = (float)((10.0 / (float)(_height-1) * 360.0)); - for (int x=0; x<_width; x++) { - color_line[x] = HSBtoRGB(hue_inc, 1.0, (float)x / (float)_width); + float hue_inc = (float)((10.0 / (float)(tft_height-1) * 360.0)); + for (int x=0; xtm_min, tm_info->tm_sec, ms); TFT_setFont(FONT_7SEG, NULL); - if ((_width < 240) || (_height < 240)) set_7seg_font_atrib(8, 1, 1, TFT_DARKGREY); + if ((tft_width < 240) || (tft_height < 240)) set_7seg_font_atrib(8, 1, 1, TFT_DARKGREY); else set_7seg_font_atrib(12, 2, 1, TFT_DARKGREY); //TFT_clearStringRect(12, y, tmp_buff); TFT_print(tmp_buff, CENTER, y); @@ -514,7 +514,7 @@ static void font_demo() _fg = TFT_GREEN; y += TFT_getfontheight() + 12; - if ((_width < 240) || (_height < 240)) set_7seg_font_atrib(9, 1, 1, TFT_DARKGREY); + if ((tft_width < 240) || (tft_height < 240)) set_7seg_font_atrib(9, 1, 1, TFT_DARKGREY); else set_7seg_font_atrib(14, 3, 1, TFT_DARKGREY); sprintf(tmp_buff, "%02d:%02d", tm_info->tm_sec, ms / 10); //TFT_clearStringRect(12, y, tmp_buff); @@ -537,10 +537,10 @@ static void font_demo() TFT_saveClipWin(); TFT_resetclipwin(); - TFT_drawRect(38, 48, (_width*3/4) - 36, (_height*3/4) - 46, TFT_WHITE); - TFT_setclipwin(40, 50, _width*3/4, _height*3/4); + TFT_drawRect(38, 48, (tft_width*3/4) - 36, (tft_height*3/4) - 46, TFT_WHITE); + TFT_setclipwin(40, 50, tft_width*3/4, tft_height*3/4); - if ((_width < 240) || (_height < 240)) TFT_setFont(DEF_SMALL_FONT, NULL); + if ((tft_width < 240) || (tft_height < 240)) TFT_setFont(DEF_SMALL_FONT, NULL); else TFT_setFont(UBUNTU16_FONT, NULL); text_wrap = 1; end_time = clock() + GDEMO_TIME; @@ -1078,7 +1078,7 @@ void tft_demo() { if (disp_rot == 3) sprintf(tmp_buff, "PORTRAIT FLIP"); if (disp_rot == 0) sprintf(tmp_buff, "LANDSCAPE FLIP"); printf("\r\n==========================================\r\nDisplay: %s: %s %d,%d %s\r\n\r\n", - dtype, tmp_buff, _width, _height, ((gray_scale) ? "Gray" : "Color")); + dtype, tmp_buff, tft_width, tft_height, ((gray_scale) ? "Gray" : "Color")); } disp_header("Welcome to ESP32"); @@ -1239,22 +1239,13 @@ void app_main() // =================================================== // ==== Set display type ===== - tft_disp_type = DEFAULT_DISP_TYPE; + //tft_disp_type = DEFAULT_DISP_TYPE; //tft_disp_type = DISP_TYPE_ILI9341; + tft_disp_type = DISP_TYPE_ST7735B; //tft_disp_type = DISP_TYPE_ILI9488; //tft_disp_type = DISP_TYPE_ST7735B; // =================================================== - // =================================================== - // === Set display resolution if NOT using default === - // === DEFAULT_TFT_DISPLAY_WIDTH & === - // === DEFAULT_TFT_DISPLAY_HEIGHT === - _width = DEFAULT_TFT_DISPLAY_WIDTH; // smaller dimension - _height = DEFAULT_TFT_DISPLAY_HEIGHT; // larger dimension - //_width = 128; // smaller dimension - //_height = 160; // larger dimension - // =================================================== - // =================================================== // ==== Set maximum spi clock for display read ==== // operations, function 'find_rd_speed()' ==== From 7d13ee8cc5dc78def06bd38158f5b513ca21b403 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sun, 15 Sep 2019 12:16:04 -0400 Subject: [PATCH 09/39] Move display configuration into the library component. --- README.md | 37 ++++++++++++++------------ components/spiffs/Kconfig | 2 +- components/tft/Kconfig | 55 +++++++++++++++++++++++++++++++++++++++ components/tft/tftspi.h | 15 +++++++---- main/Kconfig.projbuild | 26 ------------------ main/tft_demo.c | 9 ------- 6 files changed, 86 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index d802c87..1a1c9e6 100644 --- a/README.md +++ b/README.md @@ -166,19 +166,18 @@ To run the demo, attach ILI9341, ILI9488 or ST7735 based display module to ESP32 --- -*To run the demo on* **ESP-WROWER-KIT v3** *select the following pin configuration:* -* mosi: 23 -* miso: 25 -* sck: 19 -* CS: 22 (display CS) -* DC: 21 (display DC) -* TCS: 0 (touch screen CS), not used -* RST: 18 (display RESET) -* BKLIT: 5 (Display Back light) +#### Display Kits -Also set **TFT_RGB_BGR** to 0x00 and **TFT_INVERT_ROTATION1** to 1 in *tftspi.h* +Predefined display configurations are available that will set pins, display size, and inversion properly for the specified kit. -**You can also select EXAMPLE_ESP_WROVER_KIT in menuconfig to automaticaly define correct configuration** +Access these through the `idf.py menuconfig` in Components->TFT Display + +Configurations are available for: + + "ESP-WROVER-KIT v3 Display (ST7789V)" + "ESP-WROVER-KIT v4.1 Display (ILI9341)" + "Adafruit TFT Feather Display" + "M5Stack TFT Display" --- @@ -198,17 +197,15 @@ Clone the repository `git clone https://github.com/loboris/ESP32_TFT_library.git` -Execute menuconfig and configure your Serial flash config and other settings. Included *sdkconfig.defaults* sets some defaults to be used. - -Navigate to **TFT Display DEMO Configuration** and set **SPIFFS** options. +Execute `idf.py menuconfig` and configure your Serial flash config and other settings. Included *sdkconfig.defaults* sets some defaults to be used. -Select if you want to use **wifi** (recommended) to get the time from **NTP** server and set your WiFi SSID and password. +Navigate to **Components -> TFT Display** and set **display** options or select a pre-defined display configuration for a kit. -`make menuconfig` +To enable **Wifi** in the demo (recommended - gets time from NTP), select **TFT Display DEMO Configuration** from the top-level menu and select those options. Make and flash the example. -`make all && make flash` +`idf.py build && idf.py -p flash monitor` --- @@ -224,6 +221,12 @@ Make and flash the example. You can also prepare different SFPIFFS **image** and flash it to ESP32. *This feature is only tested on Linux.* +If you change the SPIFFS image, review the SPIFFS configuration as well. This would also need to be updated if the partition table is changed. + +`idf.py menuconfig` +Navigate to **Components -> TFT SPIFFS** and set **SPIFFS** options. + + Files to be included on spiffs are already in **components/spiffs_image/image/** directory. You can add or remove the files you want to include. Then execute: diff --git a/components/spiffs/Kconfig b/components/spiffs/Kconfig index 08e6faa..a896588 100644 --- a/components/spiffs/Kconfig +++ b/components/spiffs/Kconfig @@ -1,4 +1,4 @@ -menu "TFT SPIFFS Configuration" +menu "TFT SPIFFS" config SPIFFS_BASE_ADDR int "SPIFFS Base address" diff --git a/components/tft/Kconfig b/components/tft/Kconfig index bc3b62e..0a675cb 100644 --- a/components/tft/Kconfig +++ b/components/tft/Kconfig @@ -1,5 +1,60 @@ menu "TFT Display" +config TFT_PREDEFINED_DISPLAY_TYPE + int + default 0 if TFT_PREDEFINED_DISPLAY_TYPE0 + default 1 if TFT_PREDEFINED_DISPLAY_TYPE1 + default 2 if TFT_PREDEFINED_DISPLAY_TYPE2 + default 3 if TFT_PREDEFINED_DISPLAY_TYPE3 + default 4 if TFT_PREDEFINED_DISPLAY_TYPE4 + + choice + prompt "Select predefined display configuration" + default TFT_PREDEFINED_DISPLAY_TYPE0 + help + Select predefined display configuration + + config TFT_PREDEFINED_DISPLAY_TYPE0 + bool "None" + config TFT_PREDEFINED_DISPLAY_TYPE1 + bool "ESP-WROVER-KIT v3 Display (ST7789V)" + config TFT_PREDEFINED_DISPLAY_TYPE4 + bool "ESP-WROVER-KIT v4.1 Display (ILI9341)" + config TFT_PREDEFINED_DISPLAY_TYPE2 + bool "Adafruit TFT Feather Display" + config TFT_PREDEFINED_DISPLAY_TYPE3 + bool "M5Stack TFT Display" + endchoice + +config TFT_DISPLAY_CONTROLLER_MODEL + int + default 0 if TFT_DISPLAY_CONTROLLER_ILI9341 + default 1 if TFT_DISPLAY_CONTROLLER_ILI9488 + default 2 if TFT_DISPLAY_CONTROLLER_ST7789V + default 3 if TFT_DISPLAY_CONTROLLER_ST7735 + default 4 if TFT_DISPLAY_CONTROLLER_ST7735R + default 5 if TFT_DISPLAY_CONTROLLER_ST7735B + + choice + prompt "Select a display controller model." + default TFT_DISPLAY_CONTROLLER_ILI9341 + help + Select the controller for your display. If an TFT_PREDEFINED_DISPLAY_TYPE is set, this will be overridden. + + config TFT_DISPLAY_CONTROLLER_ILI9341 + bool "ILI9341" + config TFT_DISPLAY_CONTROLLER_ILI9488 + bool "ILI9488" + config TFT_DISPLAY_CONTROLLER_ST7789V + bool "ST7789V" + config TFT_DISPLAY_CONTROLLER_ST7735 + bool "ST7735" + config TFT_DISPLAY_CONTROLLER_ST7735R + bool "ST7735R" + config TFT_DISPLAY_CONTROLLER_ST7735B + bool "ST7735B" + endchoice + config TFT_INVERT_ROTATION1 bool "Invert rotation1." default n diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index 3ff1c8d..e091182 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -35,7 +35,7 @@ #define DISP_TYPE_ST7735R 4 #define DISP_TYPE_ST7735B 5 -#if CONFIG_EXAMPLE_DISPLAY_TYPE == 1 +#if CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 1 // ** Set the correct configuration for ESP-WROVER-KIT v3 // -------------------------------------------------------- @@ -64,7 +64,7 @@ #define PIN_BCKL_OFF 1 // GPIO value for backlight OFF // -------------------------------------------------------- -#elif CONFIG_EXAMPLE_DISPLAY_TYPE == 2 +#elif CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 2 // ** Set the correct configuration for Adafruit TFT Feather // --------------------------------------------------------- @@ -93,7 +93,7 @@ #define PIN_BCKL_OFF 1 // GPIO value for backlight OFF // --------------------------------------------------------- -#elif CONFIG_EXAMPLE_DISPLAY_TYPE == 3 +#elif CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 3 // ** Set the correct configuration for M5Stack TFT // --------------------------------------------------------- @@ -123,7 +123,7 @@ #define PIN_BCKL_OFF 0 // GPIO value for backlight OFF // --------------------------------------------------------- -#elif CONFIG_EXAMPLE_DISPLAY_TYPE == 4 +#elif CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 4 // ** Set the correct configuration for ESP-WROVER-KIT v4.1 // -------------------------------------------------------- @@ -223,10 +223,15 @@ #define DEFAULT_GAMMA_CURVE 0 #define DEFAULT_SPI_CLOCK 26000000 + +#if defined(CONFIG_TFT_DISPLAY_CONTROLLER_MODEL) +#define DEFAULT_DISP_TYPE CONFIG_TFT_DISPLAY_CONTROLLER_MODEL +#else #define DEFAULT_DISP_TYPE DISP_TYPE_ILI9341 +#endif //---------------------------------------------------------------------------- -#endif // CONFIG_EXAMPLE_ESP_WROVER_KIT +#endif // CONFIG_PREDEFINED_DISPLAY_TYPE // ############################################################## diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 58b5f85..62867af 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -1,31 +1,5 @@ menu "TFT Display DEMO Configuration" -config EXAMPLE_DISPLAY_TYPE - int - default 0 if EXAMPLE_DISPLAY_TYPE0 - default 1 if EXAMPLE_DISPLAY_TYPE1 - default 2 if EXAMPLE_DISPLAY_TYPE2 - default 3 if EXAMPLE_DISPLAY_TYPE3 - default 4 if EXAMPLE_DISPLAY_TYPE4 - - choice - prompt "Select predefined display configuration" - default EXAMPLE_DISPLAY_TYPE0 - help - Select predefined display configuration - - config EXAMPLE_DISPLAY_TYPE0 - bool "None" - config EXAMPLE_DISPLAY_TYPE1 - bool "ESP-WROVER-KIT v3 Display (ST7789V)" - config EXAMPLE_DISPLAY_TYPE4 - bool "ESP-WROVER-KIT v4.1 Display (ILI9341)" - config EXAMPLE_DISPLAY_TYPE2 - bool "Adafruit TFT Feather display" - config EXAMPLE_DISPLAY_TYPE3 - bool "M5Stack TFT display" - endchoice - config EXAMPLE_USE_WIFI bool "Use wifi in TFT Demo" default n diff --git a/main/tft_demo.c b/main/tft_demo.c index 582cf9c..18aa827 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -1237,15 +1237,6 @@ void app_main() // === SET GLOBAL VARIABLES ========================== - // =================================================== - // ==== Set display type ===== - //tft_disp_type = DEFAULT_DISP_TYPE; - //tft_disp_type = DISP_TYPE_ILI9341; - tft_disp_type = DISP_TYPE_ST7735B; - //tft_disp_type = DISP_TYPE_ILI9488; - //tft_disp_type = DISP_TYPE_ST7735B; - // =================================================== - // =================================================== // ==== Set maximum spi clock for display read ==== // operations, function 'find_rd_speed()' ==== From abdebccb4a6332360b0ca52280286b4cbc55dcdd Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sun, 15 Sep 2019 13:30:08 -0400 Subject: [PATCH 10/39] Fix default and docs for SPIFFS. --- README.md | 10 +++++----- components/mkspiffs/src/mkspiffs | Bin 138168 -> 160736 bytes components/spiffs/Kconfig | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1a1c9e6..f254381 100644 --- a/README.md +++ b/README.md @@ -215,18 +215,18 @@ Make and flash the example. **To flash already prepared image to flash** execute: -`make copyfs` +`ESPPORT= make copyfs` --- -You can also prepare different SFPIFFS **image** and flash it to ESP32. *This feature is only tested on Linux.* +You can also prepare different SFPIFFS **image** and flash it to ESP32. -If you change the SPIFFS image, review the SPIFFS configuration as well. This would also need to be updated if the partition table is changed. +The example partition file reserves 1MB for SPIFFS. +If you change the spiffs partition size, update the SPIFFS configuration as well. `idf.py menuconfig` Navigate to **Components -> TFT SPIFFS** and set **SPIFFS** options. - Files to be included on spiffs are already in **components/spiffs_image/image/** directory. You can add or remove the files you want to include. Then execute: @@ -237,7 +237,7 @@ to create **spiffs image** in *build* directory **without flashing** to ESP32 Or execute: -`make flashfs` +`ESPPORT= make flashfs` to create **spiffs image** in *build* directory and **flash** it to ESP32 diff --git a/components/mkspiffs/src/mkspiffs b/components/mkspiffs/src/mkspiffs index 1cea3e3dda3b57c43148848925c0fd8df920384f..ce47612287ead7ce19a87fbc4c8abfe197104b43 100755 GIT binary patch literal 160736 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 b_^<1)^(T#Qzx>7HDyJ*(zFOzy&HsM^bdITe diff --git a/components/spiffs/Kconfig b/components/spiffs/Kconfig index a896588..e578fb7 100644 --- a/components/spiffs/Kconfig +++ b/components/spiffs/Kconfig @@ -3,7 +3,7 @@ menu "TFT SPIFFS" config SPIFFS_BASE_ADDR int "SPIFFS Base address" range 1048576 33546240 - default 1572864 + default 0x180000 help Starting address of the SPIFFS area in ESP32 Flash From 6167d6ef1086ebd530d13fded0a173bdde9c7bd5 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sun, 15 Sep 2019 14:24:02 -0400 Subject: [PATCH 11/39] Update to release/v4.0 and document idf_register_component installation. --- README.md | 57 ++++++++++++++++++++++++++++++++++++++------- main/CMakeLists.txt | 13 ++++++++--- main/tft_demo.c | 2 +- 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index f254381..1479983 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,7 @@ --- -**This library must be built with the latest esp-idf master branch and xtensa toolchain** - -If you are using the esp-idf v2.1, checkout the commit *0518df81a6566820352dad7bf6c539995d41ad18* +**This library must be built with the esp-idf release/v4.0 branch and xtensa toolchain** --- @@ -152,7 +150,7 @@ Full **demo application**, well documented, is included, please **analyze it** t | GND | GND | Power supply ground | | 3.3V or +5V | Vcc | Power supply positive | -**Make shure the display module has 3.3V compatible interface, if not you must use level shifter!** +**Make sure the display module has 3.3V compatible interface, if not you must use level shifter!** --- @@ -189,17 +187,56 @@ Using *make menuconfig* **select tick rate 1000** ( → Component config → Fre --- -#### How to build +#### Installing as Library +This repository is intended to be installable as a component library using the ESP-IDF 4.0 build system. + +It is recommended though that you first follow the [demo instructions](#building-the-demo) below to build this repository as a standalone example to validate your hardware and have a basis to learn the library features. + +When you are ready to incorporate it into your existing project, it is recommended to: + +```shell +mkdir -p externals +git submodule add https://github.com/jeremyjh/ESP32_TFT_library.git externals/ESP32_TFT_library +``` + +In your project's root CMakeLists.txt add the components folder to `EXTRA_COMPONENT_DIRS` - make sure this is before the project() config e.g. -Configure your esp32 build environment as for **esp-idf examples** +```cmake +cmake_minimum_required(VERSION 3.5) -Clone the repository +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + set(EXTRA_COMPONENT_DIRS + externals/ESP32_TFT_library/components) -`git clone https://github.com/loboris/ESP32_TFT_library.git` +project(hello-world) +``` + +In your own components//CMakeLists.txt or main/CMakeLists.txt add `tft` and `spiffs` (if you are using spiffs) as `REQUIRES` e.g. + +```cmake +set(SOURCES tft_demo.c) +idf_component_register( + SRCS ${SOURCES} + INCLUDE_DIRS + ${CMAKE_CURRENT_LIST_DIR} + $ENV{IDF_PATH}/components + REQUIRES + tft + spiffs +) +``` + +--- + +#### Building the Demo + +Clone the repository to your esp folder (same level as esp-idf, as explained [in instructions](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html)). + +`git clone https://github.com/jeremyjh/ESP32_TFT_library.git` Execute `idf.py menuconfig` and configure your Serial flash config and other settings. Included *sdkconfig.defaults* sets some defaults to be used. -Navigate to **Components -> TFT Display** and set **display** options or select a pre-defined display configuration for a kit. +Navigate to **Components -> TFT Display** and set **display and pin** options or select a pre-defined display configuration for a kit. To enable **Wifi** in the demo (recommended - gets time from NTP), select **TFT Display DEMO Configuration** from the top-level menu and select those options. @@ -207,6 +244,8 @@ Make and flash the example. `idf.py build && idf.py -p flash monitor` +Deploy the SPIFFS image as below to make the image and font examples work. + --- #### Prepare **SPIFFS** image diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 237d596..d5e3002 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,3 +1,10 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") -register_component() +set(SOURCES tft_demo.c) +idf_component_register( + SRCS ${SOURCES} + INCLUDE_DIRS + ${CMAKE_CURRENT_LIST_DIR} + $ENV{IDF_PATH}/components + REQUIRES + tft + spiffs +) diff --git a/main/tft_demo.c b/main/tft_demo.c index 18aa827..efb587a 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -24,7 +24,7 @@ #include "freertos/event_groups.h" #include "esp_sntp.h" #include "esp_log.h" -#include "nvs_flash.h" +#include "nvs_flash/include/nvs_flash.h" #endif From c2223040cfaa9b7f86c77f5feb0113ca28933e9b Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sun, 15 Sep 2019 14:58:04 -0400 Subject: [PATCH 12/39] Make all pins configurable. --- README.md | 3 +- components/tft/Kconfig | 50 ++++++++++++++++++++++++++++++++- components/tft/tftspi.h | 62 ++++++++++++++++++++++++++++++----------- 3 files changed, 96 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 1479983..8601fd2 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,7 @@ To run the demo, attach ILI9341, ILI9488 or ST7735 based display module to ESP32 * DC: 26 (display DC) * TCS: 25 (touch screen CS) +**Custom PINS can be defined in `idf.py menuconfig` in Components -> TFT Display menu** --- #### Display Kits @@ -179,8 +180,6 @@ Configurations are available for: --- -**If you want to use different pins, change them in** *tftspi.h* - **if you want to use the touch screen functions, set** `#define USE_TOUCH 1` in *tftspi.h* Using *make menuconfig* **select tick rate 1000** ( → Component config → FreeRTOS → Tick rate (Hz) ) to get more accurate timings diff --git a/components/tft/Kconfig b/components/tft/Kconfig index 0a675cb..59e88c4 100644 --- a/components/tft/Kconfig +++ b/components/tft/Kconfig @@ -61,11 +61,59 @@ config TFT_INVERT_ROTATION1 help If text is backwards on your display, try enabling this. +config TFT_PIN_NUM_MOSI + int "GPIO for MOSI (Master Out Slave In)" + default 23 + help + If not using a predefined display type, configure the MOSI pin here. + +config TFT_PIN_NUM_MISO + int "GPIO for MISO (Master In Slave Out)" + default 19 + help + If not using a predefined display type, configure the MISO pin here. + +config TFT_PIN_NUM_SCK + int "GPIO for SCK (Serial Clock)" + default 18 + help + If not using a predefined display type, configure the SCK pin here. + +config TFT_PIN_NUM_CS + int "GPIO for CS (Slave Select)" + default 5 + help + If not using a predefined display type, configure the CS pin here. + +config TFT_PIN_NUM_DC + int "GPIO for DC (Data \ Command)" + default 26 + help + If not using a predefined display type, configure the DC pin here. + +config TFT_PIN_NUM_TCS + int "GPIO for TCS (Touchscreen)" + default 25 + help + Optional. If not using a predefined display type, configure the TCS (touch screen) pin here. + config TFT_PIN_NUM_RST int "GPIO for Reset" default 0 help - If not using an EXAMPLE display type, configure the reset pin here. + Optional. If not using a predefined display type, configure the reset pin here. + +config TFT_PIN_NUM_BCKL + int "GPIO for Back-light control" + default 0 + help + Optional. If not using a predefined display type, configure the blacklight pin here. + +config TFT_ENABLE_TOUCH_SCREEN + bool "Enable Touch Screen" + default "n" + help + Enables touch screen features. config TFT_DISPLAY_WIDTH int "TFT display width in pixels." diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index e091182..1984be1 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -185,34 +185,64 @@ // The pins configured here are the native spi pins for HSPI interface // Any other valid pin combination can be used -#define PIN_NUM_MISO 19 // SPI MISO -#define PIN_NUM_MOSI 23 // SPI MOSI -#define PIN_NUM_CLK 18 // SPI CLOCK pin -#define PIN_NUM_CS 5 // Display CS pin -#define PIN_NUM_DC 26 // Display command/data pin -#define PIN_NUM_TCS 25 // Touch screen CS pin (NOT used if USE_TOUCH=0) -// -------------------------------------------------------------- -// ** Set Reset and Backlight pins to 0 if not used ! -// ** If you want to use them, set them to some valid GPIO number +#if defined(CONFIG_TFT_PIN_NUM_MISO) +#define PIN_NUM_MISO CONFIG_TFT_PIN_NUM_MISO +#else +#define PIN_NUM_MISO 19 +#endif + +#if defined(CONFIG_TFT_PIN_NUM_MOSI) +#define PIN_NUM_MOSI CONFIG_TFT_PIN_NUM_MOSI +#else +#define PIN_NUM_MOSI 23 +#endif + +#if defined(CONFIG_TFT_PIN_NUM_CLK) +#define PIN_NUM_CLK CONFIG_TFT_PIN_NUM_CLK +#else +#define PIN_NUM_CLK 18 +#endif + +#if defined(CONFIG_TFT_PIN_NUM_CS) +#define PIN_NUM_CS CONFIG_TFT_PIN_NUM_CS +#else +#define PIN_NUM_CS 5 +#endif + +#if defined(CONFIG_TFT_PIN_NUM_DC) +#define PIN_NUM_DC CONFIG_TFT_PIN_NUM_DC +#else +#define PIN_NUM_DC 26 +#endif + +#if defined(CONFIG_TFT_PIN_NUM_TCS) +#define PIN_NUM_TCS CONFIG_TFT_PIN_NUM_TCS +#else +#define PIN_NUM_TCS 25 +#endif -// GPIO used for RESET control #if defined(CONFIG_TFT_PIN_NUM_RST) #define PIN_NUM_RST CONFIG_TFT_PIN_NUM_RST #else #define PIN_NUM_RST 0 #endif -#define PIN_NUM_BCKL 0 // GPIO used for backlight control +#if defined(CONFIG_TFT_PIN_NUM_BCKL) +#define PIN_NUM_BCKL CONFIG_TFT_PIN_NUM_BCKL +#else +#define PIN_NUM_BCKL 0 +#endif + #define PIN_BCKL_ON 0 // GPIO value for backlight ON #define PIN_BCKL_OFF 1 // GPIO value for backlight OFF // -------------------------------------------------------------- -// ####################################################### -// Set this to 1 if you want to use touch screen functions -// ####################################################### -#define USE_TOUCH TOUCH_TYPE_NONE -// ####################################################### +#if defined(CONFIG_TFT_ENABLE_TOUCH_SCREEN) +#define USE_TOUCH CONFIG_TFT_ENABLE_TOUCH_SCREEN +#else +#define USE_TOUCH TOUCH_TYPE_NONE +#endif // ####################################################################### // Default display width (smaller dimension) and height (larger dimension) From bead8c0f5befc5c1a3ff6f59c204bda06d6280ac Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sun, 15 Sep 2019 15:30:19 -0400 Subject: [PATCH 13/39] Namespace all global variables. --- README.md | 30 +- components/tft/tft.c | 708 ++++++++++++++++++++-------------------- components/tft/tft.h | 46 +-- components/tft/tftspi.c | 236 +++++++------- components/tft/tftspi.h | 20 +- main/tft_demo.c | 254 +++++++------- 6 files changed, 647 insertions(+), 647 deletions(-) diff --git a/README.md b/README.md index 8601fd2..3ac2001 100644 --- a/README.md +++ b/README.md @@ -106,23 +106,23 @@ * **Global wariables** - * **orientation** current screen orientation - * **font_ratate** current font rotate angle (0~395) - * **font_transparent** if not 0 draw fonts transparent - * **font_forceFixed** if not zero force drawing proportional fonts with fixed width - * **text_wrap** if not 0 wrap long text to the new line, else clip - * **_fg** current foreground color for fonts - * **_bg** current background for non transparent fonts - * **dispWin** current display clip window - * **_angleOffset** angle offset for arc, polygon and line by angle functions - * **image_debug** print debug messages during image decode if set to 1 - * **cfont** Currently used font structure + * **tft_orientation** current screen orientation + * **tft_font_ratate** current font rotate angle (0~395) + * **tft_font_transparent** if not 0 draw fonts transparent + * **tft_font_forceFixed** if not zero force drawing proportional fonts with fixed width + * **tft_text_wrap** if not 0 wrap long text to the new line, else clip + * **tft__fg** current foreground color for fonts + * **tft__bg** current background for non transparent fonts + * **tft_dispWin** current display clip window + * **tft__angleOffset** angle offset for arc, polygon and line by angle functions + * **tft_image_debug** print debug messages during image decode if set to 1 + * **tft_cfont** Currently used font structure * **TFT_X** X position of the next character after TFT_print() function * **TFT_Y** Y position of the next character after TFT_print() function - * **tp_calx** touch screen X calibration constant - * **tp_caly** touch screen Y calibration constant - * **gray_scale** convert all colors to gray scale if set to 1 - * **max_rdclock** current spi clock for reading from display RAM + * **tft_tp_calx** touch screen X calibration constant + * **tft_tp_caly** touch screen Y calibration constant + * **tft_gray_scale** convert all colors to gray scale if set to 1 + * **tft_max_rdclock** current spi clock for reading from display RAM * **tft_width** screen width (smaller dimension) in pixels * **tft_height** screen height (larger dimension) in pixels * **tft_disp_type** current display type (DISP_TYPE_ILI9488 or DISP_TYPE_ILI9341) diff --git a/components/tft/tft.c b/components/tft/tft.c index 4b4a783..bb16357 100644 --- a/components/tft/tft.c +++ b/components/tft/tft.c @@ -61,31 +61,31 @@ const color_t TFT_PINK = { 252, 192, 202 }; // ============================================================== // ==== Set default values of global variables ================== -uint8_t orientation = LANDSCAPE;// screen orientation -uint16_t font_rotate = 0; // font rotation -uint8_t font_transparent = 0; -uint8_t font_forceFixed = 0; -uint8_t text_wrap = 0; // character wrapping to new line -color_t _fg = { 0, 255, 0}; -color_t _bg = { 0, 0, 0}; -uint8_t image_debug = 0; +uint8_t tft_orientation = LANDSCAPE;// screen tft_orientation +uint16_t tft_font_rotate = 0; // font rotation +uint8_t tft_font_transparent = 0; +uint8_t tft_font_forceFixed = 0; +uint8_t tft_text_wrap = 0; // character wrapping to new line +color_t tft_fg = { 0, 255, 0}; +color_t tft_bg = { 0, 0, 0}; +uint8_t tft_image_debug = 0; -float _angleOffset = DEFAULT_ANGLE_OFFSET; +float tft_angleOffset = DEFAULT_ANGLE_OFFSET; int TFT_X = 0; int TFT_Y = 0; -uint32_t tp_calx = 7472920; -uint32_t tp_caly = 122224794; +uint32_t tft_tp_calx = 7472920; +uint32_t tft_tp_caly = 122224794; -dispWin_t dispWin = { +dispWin_t tft_dispWin = { .x1 = 0, .y1 = 0, .x2 = CONFIG_TFT_DISPLAY_WIDTH, .y2 = CONFIG_TFT_DISPLAY_HEIGHT, }; -Font cfont = { +Font tft_cfont = { .font = tft_DefaultFont, .x_size = 0, .y_size = 0x0B, @@ -94,8 +94,8 @@ Font cfont = { .bitmap = 1, }; -uint8_t font_buffered_char = 1; -uint8_t font_line_space = 0; +uint8_t tft_font_buffered_char = 1; +uint8_t tft_font_line_space = 0; // ============================================================== @@ -118,7 +118,7 @@ static float _arcAngleMax = DEFAULT_ARC_ANGLE_MAX; // ========================================================================= -// ** All drawings are clipped to 'dispWin' ** +// ** All drawings are clipped to 'tft_dispWin' ** // ** All x,y coordinates in public functions are relative to clip window ** // =========== : Public functions // ----------- : Local functions @@ -140,20 +140,20 @@ int TFT_compare_colors(color_t c1, color_t c2) //------------------------------------------------------------------------ static void _drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) { - if ((x < dispWin.x1) || (y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return; + if ((x < tft_dispWin.x1) || (y < tft_dispWin.y1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return; drawPixel(x, y, color, sel); } //==================================================================== void TFT_drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) { - _drawPixel(x+dispWin.x1, y+dispWin.y1, color, sel); + _drawPixel(x+tft_dispWin.x1, y+tft_dispWin.y1, color, sel); } //=========================================== color_t TFT_readPixel(int16_t x, int16_t y) { - if ((x < dispWin.x1) || (y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return TFT_BLACK; + if ((x < tft_dispWin.x1) || (y < tft_dispWin.y1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return TFT_BLACK; return readPixel(x, y); } @@ -161,13 +161,13 @@ color_t TFT_readPixel(int16_t x, int16_t y) { //-------------------------------------------------------------------------- static void _drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) { // clipping - if ((x < dispWin.x1) || (x > dispWin.x2) || (y > dispWin.y2)) return; - if (y < dispWin.y1) { - h -= (dispWin.y1 - y); - y = dispWin.y1; + if ((x < tft_dispWin.x1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return; + if (y < tft_dispWin.y1) { + h -= (tft_dispWin.y1 - y); + y = tft_dispWin.y1; } if (h < 0) h = 0; - if ((y + h) > (dispWin.y2+1)) h = dispWin.y2 - y + 1; + if ((y + h) > (tft_dispWin.y2+1)) h = tft_dispWin.y2 - y + 1; if (h == 0) h = 1; TFT_pushColorRep(x, y, x, y+h-1, color, (uint32_t)h); } @@ -175,13 +175,13 @@ static void _drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) { //-------------------------------------------------------------------------- static void _drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) { // clipping - if ((y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return; - if (x < dispWin.x1) { - w -= (dispWin.x1 - x); - x = dispWin.x1; + if ((y < tft_dispWin.y1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return; + if (x < tft_dispWin.x1) { + w -= (tft_dispWin.x1 - x); + x = tft_dispWin.x1; } if (w < 0) w = 0; - if ((x + w) > (dispWin.x2+1)) w = dispWin.x2 - x + 1; + if ((x + w) > (tft_dispWin.x2+1)) w = tft_dispWin.x2 - x + 1; if (w == 0) w = 1; TFT_pushColorRep(x, y, x+w-1, y, color, (uint32_t)w); @@ -189,12 +189,12 @@ static void _drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) { //====================================================================== void TFT_drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) { - _drawFastVLine(x+dispWin.x1, y+dispWin.y1, h, color); + _drawFastVLine(x+tft_dispWin.x1, y+tft_dispWin.y1, h, color); } //====================================================================== void TFT_drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) { - _drawFastHLine(x+dispWin.x1, y+dispWin.y1, w, color); + _drawFastHLine(x+tft_dispWin.x1, y+tft_dispWin.y1, w, color); } // Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer this uses @@ -262,28 +262,28 @@ static void _drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t co //============================================================================== void TFT_drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color) { - _drawLine(x0+dispWin.x1, y0+dispWin.y1, x1+dispWin.x1, y1+dispWin.y1, color); + _drawLine(x0+tft_dispWin.x1, y0+tft_dispWin.y1, x1+tft_dispWin.x1, y1+tft_dispWin.y1, color); } // fill a rectangle //-------------------------------------------------------------------------------- static void _fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) { // clipping - if ((x >= dispWin.x2) || (y > dispWin.y2)) return; + if ((x >= tft_dispWin.x2) || (y > tft_dispWin.y2)) return; - if (x < dispWin.x1) { - w -= (dispWin.x1 - x); - x = dispWin.x1; + if (x < tft_dispWin.x1) { + w -= (tft_dispWin.x1 - x); + x = tft_dispWin.x1; } - if (y < dispWin.y1) { - h -= (dispWin.y1 - y); - y = dispWin.y1; + if (y < tft_dispWin.y1) { + h -= (tft_dispWin.y1 - y); + y = tft_dispWin.y1; } if (w < 0) w = 0; if (h < 0) h = 0; - if ((x + w) > (dispWin.x2+1)) w = dispWin.x2 - x + 1; - if ((y + h) > (dispWin.y2+1)) h = dispWin.y2 - y + 1; + if ((x + w) > (tft_dispWin.x2+1)) w = tft_dispWin.x2 - x + 1; + if ((y + h) > (tft_dispWin.y2+1)) h = tft_dispWin.y2 - y + 1; if (w == 0) w = 1; if (h == 0) h = 1; TFT_pushColorRep(x, y, x+w-1, y+h-1, color, (uint32_t)(h*w)); @@ -291,7 +291,7 @@ static void _fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) //============================================================================ void TFT_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) { - _fillRect(x+dispWin.x1, y+dispWin.y1, w, h, color); + _fillRect(x+tft_dispWin.x1, y+tft_dispWin.y1, w, h, color); } //================================== @@ -301,8 +301,8 @@ void TFT_fillScreen(color_t color) { //================================== void TFT_fillWindow(color_t color) { - TFT_pushColorRep(dispWin.x1, dispWin.y1, dispWin.x2, dispWin.y2, - color, (uint32_t)((dispWin.x2-dispWin.x1+1) * (dispWin.y2-dispWin.y1+1))); + TFT_pushColorRep(tft_dispWin.x1, tft_dispWin.y1, tft_dispWin.x2, tft_dispWin.y2, + color, (uint32_t)((tft_dispWin.x2-tft_dispWin.x1+1) * (tft_dispWin.y2-tft_dispWin.y1+1))); } // ^^^============= Basics drawing functions ================================^^^ @@ -320,7 +320,7 @@ static void _drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t col //=============================================================================== void TFT_drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) { - _drawRect(x1+dispWin.x1, y1+dispWin.y1, w, h, color); + _drawRect(x1+tft_dispWin.x1, y1+tft_dispWin.y1, w, h, color); } //------------------------------------------------------------------------------------------------- @@ -397,8 +397,8 @@ static void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornerna //============================================================================================= void TFT_drawRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color) { - x += dispWin.x1; - y += dispWin.y1; + x += tft_dispWin.x1; + y += tft_dispWin.y1; // smarter version _drawFastHLine(x + r, y, w - 2 * r, color); // Top @@ -417,8 +417,8 @@ void TFT_drawRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, //============================================================================================= void TFT_fillRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color) { - x += dispWin.x1; - y += dispWin.y1; + x += tft_dispWin.x1; + y += tft_dispWin.y1; // smarter version _fillRect(x + r, y, w - 2 * r, h, color); @@ -437,25 +437,25 @@ static void _drawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t lengt _drawLine( x, y, - x + length * cos((angle + _angleOffset) * DEG_TO_RAD), - y + length * sin((angle + _angleOffset) * DEG_TO_RAD), color); + x + length * cos((angle + tft_angleOffset) * DEG_TO_RAD), + y + length * sin((angle + tft_angleOffset) * DEG_TO_RAD), color); } //--------------------------------------------------------------------------------------------------------------- static void _DrawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t start, uint16_t length, color_t color) { _drawLine( - x + start * cos((angle + _angleOffset) * DEG_TO_RAD), - y + start * sin((angle + _angleOffset) * DEG_TO_RAD), - x + (start + length) * cos((angle + _angleOffset) * DEG_TO_RAD), - y + (start + length) * sin((angle + _angleOffset) * DEG_TO_RAD), color); + x + start * cos((angle + tft_angleOffset) * DEG_TO_RAD), + y + start * sin((angle + tft_angleOffset) * DEG_TO_RAD), + x + (start + length) * cos((angle + tft_angleOffset) * DEG_TO_RAD), + y + (start + length) * sin((angle + tft_angleOffset) * DEG_TO_RAD), color); } //=========================================================================================================== void TFT_drawLineByAngle(uint16_t x, uint16_t y, uint16_t start, uint16_t len, uint16_t angle, color_t color) { - x += dispWin.x1; - y += dispWin.y1; + x += tft_dispWin.x1; + y += tft_dispWin.y1; if (start == 0) _drawLineByAngle(x, y, angle, len, color); else _DrawLineByAngle(x, y, angle, start, len, color); @@ -474,12 +474,12 @@ static void _drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, ui //================================================================================================================ void TFT_drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color) { - x0 += dispWin.x1; - y0 += dispWin.y1; - x1 += dispWin.x1; - y1 += dispWin.y1; - x2 += dispWin.x1; - y2 += dispWin.y1; + x0 += tft_dispWin.x1; + y0 += tft_dispWin.y1; + x1 += tft_dispWin.x1; + y1 += tft_dispWin.y1; + x2 += tft_dispWin.x1; + y2 += tft_dispWin.y1; _drawLine(x0, y0, x1, y1, color); _drawLine(x1, y1, x2, y2, color); @@ -568,16 +568,16 @@ static void _fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, ui void TFT_fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color) { _fillTriangle( - x0 + dispWin.x1, y0 + dispWin.y1, - x1 + dispWin.x1, y1 + dispWin.y1, - x2 + dispWin.x1, y2 + dispWin.y1, + x0 + tft_dispWin.x1, y0 + tft_dispWin.y1, + x1 + tft_dispWin.x1, y1 + tft_dispWin.y1, + x2 + tft_dispWin.x1, y2 + tft_dispWin.y1, color); } //==================================================================== void TFT_drawCircle(int16_t x, int16_t y, int radius, color_t color) { - x += dispWin.x1; - y += dispWin.y1; + x += tft_dispWin.x1; + y += tft_dispWin.y1; int f = 1 - radius; int ddF_x = 1; int ddF_y = -2 * radius; @@ -612,8 +612,8 @@ void TFT_drawCircle(int16_t x, int16_t y, int radius, color_t color) { //==================================================================== void TFT_fillCircle(int16_t x, int16_t y, int radius, color_t color) { - x += dispWin.x1; - y += dispWin.y1; + x += tft_dispWin.x1; + y += tft_dispWin.y1; _drawFastVLine(x, y-radius, 2*radius+1, color); fillCircleHelper(x, y, radius, 3, 0, color); @@ -637,8 +637,8 @@ static void _draw_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t //===================================================================================================== void TFT_drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option) { - x0 += dispWin.x1; - y0 += dispWin.y1; + x0 += tft_dispWin.x1; + y0 += tft_dispWin.y1; uint16_t x, y; int32_t xchg, ychg; @@ -737,8 +737,8 @@ static void _draw_filled_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, ui //===================================================================================================== void TFT_fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option) { - x0 += dispWin.x1; - y0 += dispWin.y1; + x0 += tft_dispWin.x1; + y0 += tft_dispWin.y1; uint16_t x, y; int32_t xchg, ychg; @@ -870,8 +870,8 @@ static void _fillArcOffsetted(uint16_t cx, uint16_t cy, uint16_t radius, uint16_ //=========================================================================================================================== void TFT_drawArc(uint16_t cx, uint16_t cy, uint16_t r, uint16_t th, float start, float end, color_t color, color_t fillcolor) { - cx += dispWin.x1; - cy += dispWin.y1; + cx += tft_dispWin.x1; + cy += tft_dispWin.y1; if (th < 1) th = 1; if (th > r) th = r; @@ -881,8 +881,8 @@ void TFT_drawArc(uint16_t cx, uint16_t cy, uint16_t r, uint16_t th, float start, float astart = fmodf(start, _arcAngleMax); float aend = fmodf(end, _arcAngleMax); - astart += _angleOffset; - aend += _angleOffset; + astart += tft_angleOffset; + aend += tft_angleOffset; if (astart < 0) astart += (float)360; if (aend < 0) aend += (float)360; @@ -917,10 +917,10 @@ void TFT_drawArc(uint16_t cx, uint16_t cy, uint16_t r, uint16_t th, float start, //============================================================================================================= void TFT_drawPolygon(int cx, int cy, int sides, int diameter, color_t color, color_t fill, int rot, uint8_t th) { - cx += dispWin.x1; - cy += dispWin.y1; + cx += tft_dispWin.x1; + cy += tft_dispWin.y1; - int deg = rot - _angleOffset; + int deg = rot - tft_angleOffset; int f = TFT_compare_colors(fill, color); if (sides < MIN_POLIGON_SIDES) sides = MIN_POLIGON_SIDES; // This ensures the minimum side number @@ -965,8 +965,8 @@ void TFT_drawPolygon(int cx, int cy, int sides, int diameter, color_t color, col //===================================================================================== void TFT_drawStar(int cx, int cy, int diameter, color_t color, bool fill, float factor) { - cx += dispWin.x1; - cy += dispWin.y1; + cx += tft_dispWin.x1; + cy += tft_dispWin.y1; factor = constrain(factor, 1.0, 4.0); uint8_t sides = 5; @@ -1324,14 +1324,14 @@ int compile_font_file(char *fontfile, uint8_t dbg) // ----------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- -// Character drawing rectangle is (0, 0) (xDelta-1, cfont.y_size-1) +// Character drawing rectangle is (0, 0) (xDelta-1, tft_cfont.y_size-1) // Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1) //--------------------------------------------------------------------------------------------- //---------------------------------- void getFontCharacters(uint8_t *buf) { - if (cfont.bitmap == 2) { + if (tft_cfont.bitmap == 2) { //For 7 segment font only characters 0,1,2,3,4,5,6,7,8,9, . , - , : , / are available. for (uint8_t n=0; n < 11; n++) { buf[n] = n + 0x30; @@ -1343,11 +1343,11 @@ void getFontCharacters(uint8_t *buf) return; } - if (cfont.x_size > 0) { - for (uint8_t n=0; n < cfont.numchars; n++) { - buf[n] = cfont.offset + n; + if (tft_cfont.x_size > 0) { + for (uint8_t n=0; n < tft_cfont.numchars; n++) { + buf[n] = tft_cfont.offset + n; } - buf[cfont.numchars] = '\0'; + buf[tft_cfont.numchars] = '\0'; return; } @@ -1355,12 +1355,12 @@ void getFontCharacters(uint8_t *buf) uint8_t cc, cw, ch, n; n = 0; - cc = cfont.font[tempPtr++]; + cc = tft_cfont.font[tempPtr++]; while (cc != 0xFF) { - cfont.numchars++; + tft_cfont.numchars++; tempPtr++; - cw = cfont.font[tempPtr++]; - ch = cfont.font[tempPtr++]; + cw = tft_cfont.font[tempPtr++]; + ch = tft_cfont.font[tempPtr++]; tempPtr++; tempPtr++; if (cw != 0) { @@ -1368,7 +1368,7 @@ void getFontCharacters(uint8_t *buf) tempPtr += (((cw * ch)-1) / 8) + 1; } buf[n++] = cc; - cc = cfont.font[tempPtr++]; + cc = tft_cfont.font[tempPtr++]; } buf[n] = '\0'; } @@ -1380,29 +1380,29 @@ static void getMaxWidthHeight() uint16_t tempPtr = 4; // point at first char data uint8_t cc, cw, ch, cd, cy; - cfont.numchars = 0; - cfont.max_x_size = 0; + tft_cfont.numchars = 0; + tft_cfont.max_x_size = 0; - cc = cfont.font[tempPtr++]; + cc = tft_cfont.font[tempPtr++]; while (cc != 0xFF) { - cfont.numchars++; - cy = cfont.font[tempPtr++]; - cw = cfont.font[tempPtr++]; - ch = cfont.font[tempPtr++]; + tft_cfont.numchars++; + cy = tft_cfont.font[tempPtr++]; + cw = tft_cfont.font[tempPtr++]; + ch = tft_cfont.font[tempPtr++]; tempPtr++; - cd = cfont.font[tempPtr++]; + cd = tft_cfont.font[tempPtr++]; cy += ch; - if (cw > cfont.max_x_size) cfont.max_x_size = cw; - if (cd > cfont.max_x_size) cfont.max_x_size = cd; - if (ch > cfont.y_size) cfont.y_size = ch; - if (cy > cfont.y_size) cfont.y_size = cy; + if (cw > tft_cfont.max_x_size) tft_cfont.max_x_size = cw; + if (cd > tft_cfont.max_x_size) tft_cfont.max_x_size = cd; + if (ch > tft_cfont.y_size) tft_cfont.y_size = ch; + if (cy > tft_cfont.y_size) tft_cfont.y_size = cy; if (cw != 0) { // packed bits tempPtr += (((cw * ch)-1) / 8) + 1; } - cc = cfont.font[tempPtr++]; + cc = tft_cfont.font[tempPtr++]; } - cfont.size = tempPtr; + tft_cfont.size = tempPtr; } // Return the Glyph data for an individual character in the proportional font @@ -1411,15 +1411,15 @@ static uint8_t getCharPtr(uint8_t c) { uint16_t tempPtr = 4; // point at first char data do { - fontChar.charCode = cfont.font[tempPtr++]; + fontChar.charCode = tft_cfont.font[tempPtr++]; if (fontChar.charCode == 0xFF) return 0; - fontChar.adjYOffset = cfont.font[tempPtr++]; - fontChar.width = cfont.font[tempPtr++]; - fontChar.height = cfont.font[tempPtr++]; - fontChar.xOffset = cfont.font[tempPtr++]; + fontChar.adjYOffset = tft_cfont.font[tempPtr++]; + fontChar.width = tft_cfont.font[tempPtr++]; + fontChar.height = tft_cfont.font[tempPtr++]; + fontChar.xOffset = tft_cfont.font[tempPtr++]; fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset); - fontChar.xDelta = cfont.font[tempPtr++]; + fontChar.xDelta = tft_cfont.font[tempPtr++]; if (c != fontChar.charCode && fontChar.charCode != 0xFF) { if (fontChar.width != 0) { @@ -1431,9 +1431,9 @@ static uint8_t getCharPtr(uint8_t c) { fontChar.dataPtr = tempPtr; if (c == fontChar.charCode) { - if (font_forceFixed > 0) { + if (tft_font_forceFixed > 0) { // fix width & offset for forced fixed width - fontChar.xDelta = cfont.max_x_size; + fontChar.xDelta = tft_cfont.max_x_size; fontChar.xOffset = (fontChar.xDelta - fontChar.width) / 2; } } @@ -1445,26 +1445,26 @@ static uint8_t getCharPtr(uint8_t c) { /* //----------------------- static void _testFont() { - if (cfont.x_size) { + if (tft_cfont.x_size) { printf("FONT TEST: fixed font\r\n"); return; } uint16_t tempPtr = 4; // point at first char data uint8_t c = 0x20; for (c=0x20; c <0xFF; c++) { - fontChar.charCode = cfont.font[tempPtr++]; + fontChar.charCode = tft_cfont.font[tempPtr++]; if (fontChar.charCode == 0xFF) break; if (fontChar.charCode != c) { printf("FONT TEST: last sequential char: %d, expected %d\r\n", fontChar.charCode, c); break; } c = fontChar.charCode; - fontChar.adjYOffset = cfont.font[tempPtr++]; - fontChar.width = cfont.font[tempPtr++]; - fontChar.height = cfont.font[tempPtr++]; - fontChar.xOffset = cfont.font[tempPtr++]; + fontChar.adjYOffset = tft_cfont.font[tempPtr++]; + fontChar.width = tft_cfont.font[tempPtr++]; + fontChar.height = tft_cfont.font[tempPtr++]; + fontChar.xOffset = tft_cfont.font[tempPtr++]; fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset); - fontChar.xDelta = cfont.font[tempPtr++]; + fontChar.xDelta = tft_cfont.font[tempPtr++]; if (fontChar.charCode != 0xFF) { if (fontChar.width != 0) { @@ -1473,47 +1473,47 @@ static void _testFont() { } } } - printf("FONT TEST: W=%d H=%d last char: %d [%c]; length: %d\r\n", cfont.max_x_size, cfont.y_size, c, c, tempPtr); + printf("FONT TEST: W=%d H=%d last char: %d [%c]; length: %d\r\n", tft_cfont.max_x_size, tft_cfont.y_size, c, c, tempPtr); } */ //=================================================== void TFT_setFont(uint8_t font, const char *font_file) { - cfont.font = NULL; + tft_cfont.font = NULL; if (font == FONT_7SEG) { - cfont.bitmap = 2; - cfont.x_size = 24; - cfont.y_size = 6; - cfont.offset = 0; - cfont.color = _fg; + tft_cfont.bitmap = 2; + tft_cfont.x_size = 24; + tft_cfont.y_size = 6; + tft_cfont.offset = 0; + tft_cfont.color = tft_fg; } else { if (font == USER_FONT) { - if (load_file_font(font_file, 0) != 0) cfont.font = tft_DefaultFont; - else cfont.font = userfont; + if (load_file_font(font_file, 0) != 0) tft_cfont.font = tft_DefaultFont; + else tft_cfont.font = userfont; } - else if (font == DEJAVU18_FONT) cfont.font = tft_Dejavu18; - else if (font == DEJAVU24_FONT) cfont.font = tft_Dejavu24; - else if (font == UBUNTU16_FONT) cfont.font = tft_Ubuntu16; - else if (font == COMIC24_FONT) cfont.font = tft_Comic24; - else if (font == MINYA24_FONT) cfont.font = tft_minya24; - else if (font == TOONEY32_FONT) cfont.font = tft_tooney32; - else if (font == SMALL_FONT) cfont.font = tft_SmallFont; - else if (font == DEF_SMALL_FONT) cfont.font = tft_def_small; - else cfont.font = tft_DefaultFont; - - cfont.bitmap = 1; - cfont.x_size = cfont.font[0]; - cfont.y_size = cfont.font[1]; - if (cfont.x_size > 0) { - cfont.offset = cfont.font[2]; - cfont.numchars = cfont.font[3]; - cfont.size = cfont.x_size * cfont.y_size * cfont.numchars; + else if (font == DEJAVU18_FONT) tft_cfont.font = tft_Dejavu18; + else if (font == DEJAVU24_FONT) tft_cfont.font = tft_Dejavu24; + else if (font == UBUNTU16_FONT) tft_cfont.font = tft_Ubuntu16; + else if (font == COMIC24_FONT) tft_cfont.font = tft_Comic24; + else if (font == MINYA24_FONT) tft_cfont.font = tft_minya24; + else if (font == TOONEY32_FONT) tft_cfont.font = tft_tooney32; + else if (font == SMALL_FONT) tft_cfont.font = tft_SmallFont; + else if (font == DEF_SMALL_FONT) tft_cfont.font = tft_def_small; + else tft_cfont.font = tft_DefaultFont; + + tft_cfont.bitmap = 1; + tft_cfont.x_size = tft_cfont.font[0]; + tft_cfont.y_size = tft_cfont.font[1]; + if (tft_cfont.x_size > 0) { + tft_cfont.offset = tft_cfont.font[2]; + tft_cfont.numchars = tft_cfont.font[3]; + tft_cfont.size = tft_cfont.x_size * tft_cfont.y_size * tft_cfont.numchars; } else { - cfont.offset = 4; + tft_cfont.offset = 4; getMaxWidthHeight(); } //_testFont(); @@ -1532,7 +1532,7 @@ void TFT_setFont(uint8_t font, const char *font_file) // Data[n] // ----------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- -// Character drawing rectangle is (0, 0) (xDelta-1, cfont.y_size-1) +// Character drawing rectangle is (0, 0) (xDelta-1, tft_cfont.y_size-1) // Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1) //--------------------------------------------------------------------------------------------- @@ -1545,16 +1545,16 @@ static int printProportionalChar(int x, int y) { char_width = ((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta); - if ((font_buffered_char) && (!font_transparent)) { + if ((tft_font_buffered_char) && (!tft_font_transparent)) { int len, bufPos; // === buffer Glyph data for faster sending === - len = char_width * cfont.y_size; + len = char_width * tft_cfont.y_size; color_t *color_line = heap_caps_malloc(len*3, MALLOC_CAP_DMA); if (color_line) { // fill with background color for (int n = 0; n < len; n++) { - color_line[n] = _bg; + color_line[n] = tft_bg; } // set character pixels to foreground color uint8_t mask = 0x80; @@ -1562,12 +1562,12 @@ static int printProportionalChar(int x, int y) { for (i=0; i < fontChar.width; i++) { if (((i + (j*fontChar.width)) % 8) == 0) { mask = 0x80; - ch = cfont.font[fontChar.dataPtr++]; + ch = tft_cfont.font[fontChar.dataPtr++]; } if ((ch & mask) != 0) { // visible pixel bufPos = ((j + fontChar.adjYOffset) * char_width) + (fontChar.xOffset + i); // bufY + bufX - color_line[bufPos] = _fg; + color_line[bufPos] = tft_fg; /* bufY = (j + fontChar.adjYOffset) * char_width; bufX = fontChar.xOffset + i; @@ -1575,9 +1575,9 @@ static int printProportionalChar(int x, int y) { printf("[%c] X ERR: %d\r\n", fontChar.charCode, bufX); } bufPos = bufY + bufX; - if ((bufPos < len) && (bufPos > 0)) color_line[bufPos] = _fg; + if ((bufPos < len) && (bufPos > 0)) color_line[bufPos] = tft_fg; else printf("[%c] ERR: %d > %d W=%d H=%d bufX=%d bufY=%d X=%d Y=%d\r\n", - fontChar.charCode, bufPos, len, char_width, cfont.y_size, bufX, bufY, fontChar.xOffset + i, j + fontChar.adjYOffset); + fontChar.charCode, bufPos, len, char_width, tft_cfont.y_size, bufX, bufY, fontChar.xOffset + i, j + fontChar.adjYOffset); */ } mask >>= 1; @@ -1585,7 +1585,7 @@ static int printProportionalChar(int x, int y) { } // send to display in one transaction disp_select(); - send_data(x, y, x+char_width-1, y+cfont.y_size-1, len, color_line); + send_data(x, y, x+char_width-1, y+tft_cfont.y_size-1, len, color_line); disp_deselect(); free(color_line); @@ -1595,7 +1595,7 @@ static int printProportionalChar(int x, int y) { int cx, cy; - if (!font_transparent) _fillRect(x, y, char_width+1, cfont.y_size, _bg); + if (!tft_font_transparent) _fillRect(x, y, char_width+1, tft_cfont.y_size, tft_bg); // draw Glyph uint8_t mask = 0x80; @@ -1604,13 +1604,13 @@ static int printProportionalChar(int x, int y) { for (i=0; i < fontChar.width; i++) { if (((i + (j*fontChar.width)) % 8) == 0) { mask = 0x80; - ch = cfont.font[fontChar.dataPtr++]; + ch = tft_cfont.font[fontChar.dataPtr++]; } if ((ch & mask) !=0) { cx = (uint16_t)(x+fontChar.xOffset+i); cy = (uint16_t)(y+j+fontChar.adjYOffset); - _drawPixel(cx, cy, _fg, 0); + _drawPixel(cx, cy, tft_fg, 0); } mask >>= 1; } @@ -1627,28 +1627,28 @@ static void printChar(uint8_t c, int x, int y) { uint16_t k, temp, cx, cy, len; // fz = bytes per char row - fz = cfont.x_size/8; - if (cfont.x_size % 8) fz++; + fz = tft_cfont.x_size/8; + if (tft_cfont.x_size % 8) fz++; // get character position in buffer - temp = ((c-cfont.offset)*((fz)*cfont.y_size))+4; + temp = ((c-tft_cfont.offset)*((fz)*tft_cfont.y_size))+4; - if ((font_buffered_char) && (!font_transparent)) { + if ((tft_font_buffered_char) && (!tft_font_transparent)) { // === buffer Glyph data for faster sending === - len = cfont.x_size * cfont.y_size; + len = tft_cfont.x_size * tft_cfont.y_size; color_t *color_line = heap_caps_malloc(len*3, MALLOC_CAP_DMA); if (color_line) { // fill with background color for (int n = 0; n < len; n++) { - color_line[n] = _bg; + color_line[n] = tft_bg; } // set character pixels to foreground color - for (j=0; j>= 1; } } @@ -1656,7 +1656,7 @@ static void printChar(uint8_t c, int x, int y) { } // send to display in one transaction disp_select(); - send_data(x, y, x+cfont.x_size-1, y+cfont.y_size-1, len, color_line); + send_data(x, y, x+tft_cfont.x_size-1, y+tft_cfont.y_size-1, len, color_line); disp_deselect(); free(color_line); @@ -1664,18 +1664,18 @@ static void printChar(uint8_t c, int x, int y) { } } - if (!font_transparent) _fillRect(x, y, cfont.x_size, cfont.y_size, _bg); + if (!tft_font_transparent) _fillRect(x, y, tft_cfont.x_size, tft_cfont.y_size, tft_bg); disp_select(); - for (j=0; j>= 1; } @@ -1690,7 +1690,7 @@ static void printChar(uint8_t c, int x, int y) { //--------------------------------------------------- static int rotatePropChar(int x, int y, int offset) { uint8_t ch = 0; - double radian = font_rotate * DEG_TO_RAD; + double radian = tft_font_rotate * DEG_TO_RAD; float cos_radian = cos(radian); float sin_radian = sin(radian); @@ -1700,14 +1700,14 @@ static int rotatePropChar(int x, int y, int offset) { for (int i=0; i < fontChar.width; i++) { if (((i + (j*fontChar.width)) % 8) == 0) { mask = 0x80; - ch = cfont.font[fontChar.dataPtr++]; + ch = tft_cfont.font[fontChar.dataPtr++]; } int newX = (int)(x + (((offset + i) * cos_radian) - ((j+fontChar.adjYOffset)*sin_radian))); int newY = (int)(y + (((j+fontChar.adjYOffset) * cos_radian) + ((offset + i) * sin_radian))); - if ((ch & mask) != 0) _drawPixel(newX,newY,_fg, 0); - else if (!font_transparent) _drawPixel(newX,newY,_bg, 0); + if ((ch & mask) != 0) _drawPixel(newX,newY,tft_fg, 0); + else if (!tft_font_transparent) _drawPixel(newX,newY,tft_bg, 0); mask >>= 1; } @@ -1723,26 +1723,26 @@ static void rotateChar(uint8_t c, int x, int y, int pos) { uint8_t i,j,ch,fz,mask; uint16_t temp; int newx,newy; - double radian = font_rotate*0.0175; + double radian = tft_font_rotate*0.0175; float cos_radian = cos(radian); float sin_radian = sin(radian); int zz; - if( cfont.x_size < 8 ) fz = cfont.x_size; - else fz = cfont.x_size/8; - temp=((c-cfont.offset)*((fz)*cfont.y_size))+4; + if( tft_cfont.x_size < 8 ) fz = tft_cfont.x_size; + else fz = tft_cfont.x_size/8; + temp=((c-tft_cfont.offset)*((fz)*tft_cfont.y_size))+4; disp_select(); - for (j=0; j>= 1; } } @@ -1750,20 +1750,20 @@ static void rotateChar(uint8_t c, int x, int y, int pos) { } disp_deselect(); // calculate x,y for the next char - TFT_X = (int)(x + ((pos+1) * cfont.x_size * cos_radian)); - TFT_Y = (int)(y + ((pos+1) * cfont.x_size * sin_radian)); + TFT_X = (int)(x + ((pos+1) * tft_cfont.x_size * cos_radian)); + TFT_Y = (int)(y + ((pos+1) * tft_cfont.x_size * sin_radian)); } //---------------------- static int _7seg_width() { - return (2 * (2 * cfont.y_size + 1)) + cfont.x_size; + return (2 * (2 * tft_cfont.y_size + 1)) + tft_cfont.x_size; } //----------------------- static int _7seg_height() { - return (3 * (2 * cfont.y_size + 1)) + (2 * cfont.x_size); + return (3 * (2 * tft_cfont.y_size + 1)) + (2 * tft_cfont.x_size); } // Returns the string width in pixels. @@ -1773,8 +1773,8 @@ int TFT_getStringWidth(char* str) { int strWidth = 0; - if (cfont.bitmap == 2) strWidth = ((_7seg_width()+2) * strlen(str)) - 2; // 7-segment font - else if (cfont.x_size != 0) strWidth = strlen(str) * cfont.x_size; // fixed width font + if (tft_cfont.bitmap == 2) strWidth = ((_7seg_width()+2) * strlen(str)) - 2; // 7-segment font + else if (tft_cfont.x_size != 0) strWidth = strlen(str) * tft_cfont.x_size; // fixed width font else { // calculate the width of the string of proportional characters char* tempStrptr = str; @@ -1793,7 +1793,7 @@ void TFT_clearStringRect(int x, int y, char *str) { int w = TFT_getStringWidth(str); int h = TFT_getfontheight(); - TFT_fillRect(x+dispWin.x1, y+dispWin.y1, w, h, _bg); + TFT_fillRect(x+tft_dispWin.x1, y+tft_dispWin.y1, w, h, tft_bg); } //============================================================================== @@ -1830,7 +1830,7 @@ static void barVert(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, c _fillTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, color); _fillTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, color); _fillRect(x, y+2*w+1, 2*w+1, l, color); - if (cfont.offset) { + if (tft_cfont.offset) { _drawTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, outline); _drawTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, outline); _drawRect(x, y+2*w+1, 2*w+1, l, outline); @@ -1842,7 +1842,7 @@ static void barHor(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, co _fillTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, color); _fillTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, color); _fillRect(x+2*w+1, y, l, 2*w+1, color); - if (cfont.offset) { + if (tft_cfont.offset) { _drawTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, outline); _drawTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, outline); _drawRect(x+2*w+1, y, l, 2*w+1, outline); @@ -1858,63 +1858,63 @@ static void _draw7seg(int16_t x, int16_t y, int8_t num, int16_t w, int16_t l, co int16_t d = 2*w+l+1; // === Clear unused segments === - if (!(c & 0x001)) barVert(x+d, y+d, w, l, _bg, _bg); - if (!(c & 0x002)) barVert(x, y+d, w, l, _bg, _bg); - if (!(c & 0x004)) barVert(x+d, y, w, l, _bg, _bg); - if (!(c & 0x008)) barVert(x, y, w, l, _bg, _bg); - if (!(c & 0x010)) barHor(x, y+2*d, w, l, _bg, _bg); - if (!(c & 0x020)) barHor(x, y+d, w, l, _bg, _bg); - if (!(c & 0x040)) barHor(x, y, w, l, _bg, _bg); + if (!(c & 0x001)) barVert(x+d, y+d, w, l, tft_bg, tft_bg); + if (!(c & 0x002)) barVert(x, y+d, w, l, tft_bg, tft_bg); + if (!(c & 0x004)) barVert(x+d, y, w, l, tft_bg, tft_bg); + if (!(c & 0x008)) barVert(x, y, w, l, tft_bg, tft_bg); + if (!(c & 0x010)) barHor(x, y+2*d, w, l, tft_bg, tft_bg); + if (!(c & 0x020)) barHor(x, y+d, w, l, tft_bg, tft_bg); + if (!(c & 0x040)) barHor(x, y, w, l, tft_bg, tft_bg); if (!(c & 0x080)) { // low point - _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg); - if (cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg); + _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, tft_bg); + if (tft_cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, tft_bg); } if (!(c & 0x100)) { // down middle point - _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg); - if (cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg); + _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, tft_bg); + if (tft_cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, tft_bg); } if (!(c & 0x800)) { // up middle point - _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg); - if (cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg); + _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, tft_bg); + if (tft_cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, tft_bg); } if (!(c & 0x200)) { // middle, minus - _fillRect(x+2*w+1, y+d, l, 2*w+1, _bg); - if (cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, _bg); + _fillRect(x+2*w+1, y+d, l, 2*w+1, tft_bg); + if (tft_cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, tft_bg); } // === Draw used segments === - if (c & 0x001) barVert(x+d, y+d, w, l, color, cfont.color); // down right - if (c & 0x002) barVert(x, y+d, w, l, color, cfont.color); // down left - if (c & 0x004) barVert(x+d, y, w, l, color, cfont.color); // up right - if (c & 0x008) barVert(x, y, w, l, color, cfont.color); // up left - if (c & 0x010) barHor(x, y+2*d, w, l, color, cfont.color); // down - if (c & 0x020) barHor(x, y+d, w, l, color, cfont.color); // middle - if (c & 0x040) barHor(x, y, w, l, color, cfont.color); // up + if (c & 0x001) barVert(x+d, y+d, w, l, color, tft_cfont.color); // down right + if (c & 0x002) barVert(x, y+d, w, l, color, tft_cfont.color); // down left + if (c & 0x004) barVert(x+d, y, w, l, color, tft_cfont.color); // up right + if (c & 0x008) barVert(x, y, w, l, color, tft_cfont.color); // up left + if (c & 0x010) barHor(x, y+2*d, w, l, color, tft_cfont.color); // down + if (c & 0x020) barHor(x, y+d, w, l, color, tft_cfont.color); // middle + if (c & 0x040) barHor(x, y, w, l, color, tft_cfont.color); // up if (c & 0x080) { // low point _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, color); - if (cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, cfont.color); + if (tft_cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, tft_cfont.color); } if (c & 0x100) { // down middle point _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, color); - if (cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, cfont.color); + if (tft_cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, tft_cfont.color); } if (c & 0x800) { // up middle point _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, color); - if (cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, cfont.color); + if (tft_cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, tft_cfont.color); } if (c & 0x200) { // middle, minus _fillRect(x+2*w+1, y+d, l, 2*w+1, color); - if (cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, cfont.color); + if (tft_cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, tft_cfont.color); } } //============================================================================== @@ -1924,56 +1924,56 @@ void TFT_print(char *st, int x, int y) { int stl, i, tmpw, tmph, fh; uint8_t ch; - if (cfont.bitmap == 0) return; // wrong font selected + if (tft_cfont.bitmap == 0) return; // wrong font selected // ** Rotated strings cannot be aligned - if ((font_rotate != 0) && ((x <= CENTER) || (y <= CENTER))) return; + if ((tft_font_rotate != 0) && ((x <= CENTER) || (y <= CENTER))) return; - if ((x < LASTX) || (font_rotate == 0)) TFT_OFFSET = 0; + if ((x < LASTX) || (tft_font_rotate == 0)) TFT_OFFSET = 0; if ((x >= LASTX) && (x < LASTY)) x = TFT_X + (x-LASTX); - else if (x > CENTER) x += dispWin.x1; + else if (x > CENTER) x += tft_dispWin.x1; if (y >= LASTY) y = TFT_Y + (y-LASTY); - else if (y > CENTER) y += dispWin.y1; + else if (y > CENTER) y += tft_dispWin.y1; // ** Get number of characters in string to print stl = strlen(st); // ** Calculate CENTER, RIGHT or BOTTOM position tmpw = TFT_getStringWidth(st); // string width in pixels - fh = cfont.y_size; // font height - if ((cfont.x_size != 0) && (cfont.bitmap == 2)) { + fh = tft_cfont.y_size; // font height + if ((tft_cfont.x_size != 0) && (tft_cfont.bitmap == 2)) { // 7-segment font - fh = (3 * (2 * cfont.y_size + 1)) + (2 * cfont.x_size); // 7-seg character height + fh = (3 * (2 * tft_cfont.y_size + 1)) + (2 * tft_cfont.x_size); // 7-seg character height } - if (x == RIGHT) x = dispWin.x2 - tmpw + dispWin.x1; - else if (x == CENTER) x = (((dispWin.x2 - dispWin.x1 + 1) - tmpw) / 2) + dispWin.x1; + if (x == RIGHT) x = tft_dispWin.x2 - tmpw + tft_dispWin.x1; + else if (x == CENTER) x = (((tft_dispWin.x2 - tft_dispWin.x1 + 1) - tmpw) / 2) + tft_dispWin.x1; - if (y == BOTTOM) y = dispWin.y2 - fh + dispWin.y1; - else if (y==CENTER) y = (((dispWin.y2 - dispWin.y1 + 1) - (fh/2)) / 2) + dispWin.y1; + if (y == BOTTOM) y = tft_dispWin.y2 - fh + tft_dispWin.y1; + else if (y==CENTER) y = (((tft_dispWin.y2 - tft_dispWin.y1 + 1) - (fh/2)) / 2) + tft_dispWin.y1; - if (x < dispWin.x1) x = dispWin.x1; - if (y < dispWin.y1) y = dispWin.y1; - if ((x > dispWin.x2) || (y > dispWin.y2)) return; + if (x < tft_dispWin.x1) x = tft_dispWin.x1; + if (y < tft_dispWin.y1) y = tft_dispWin.y1; + if ((x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return; TFT_X = x; TFT_Y = y; // ** Adjust y position - tmph = cfont.y_size; // font height + tmph = tft_cfont.y_size; // font height // for non-proportional fonts, char width is the same for all chars - tmpw = cfont.x_size; - if (cfont.x_size != 0) { - if (cfont.bitmap == 2) { // 7-segment font + tmpw = tft_cfont.x_size; + if (tft_cfont.x_size != 0) { + if (tft_cfont.bitmap == 2) { // 7-segment font tmpw = _7seg_width(); // character width tmph = _7seg_height(); // character height } } else TFT_OFFSET = 0; // fixed font; offset not needed - if ((TFT_Y + tmph - 1) > dispWin.y2) return; + if ((TFT_Y + tmph - 1) > tft_dispWin.y2) return; int offset = TFT_OFFSET; @@ -1981,36 +1981,36 @@ void TFT_print(char *st, int x, int y) { ch = st[i]; // get string character if (ch == 0x0D) { // === '\r', erase to eol ==== - if ((!font_transparent) && (font_rotate==0)) _fillRect(TFT_X, TFT_Y, dispWin.x2+1-TFT_X, tmph, _bg); + if ((!tft_font_transparent) && (tft_font_rotate==0)) _fillRect(TFT_X, TFT_Y, tft_dispWin.x2+1-TFT_X, tmph, tft_bg); } else if (ch == 0x0A) { // ==== '\n', new line ==== - if (cfont.bitmap == 1) { - TFT_Y += tmph + font_line_space; - if (TFT_Y > (dispWin.y2-tmph)) break; - TFT_X = dispWin.x1; + if (tft_cfont.bitmap == 1) { + TFT_Y += tmph + tft_font_line_space; + if (TFT_Y > (tft_dispWin.y2-tmph)) break; + TFT_X = tft_dispWin.x1; } } else { // ==== other characters ==== - if (cfont.x_size == 0) { + if (tft_cfont.x_size == 0) { // for proportional font get character data to 'fontChar' if (getCharPtr(ch)) tmpw = fontChar.xDelta; else continue; } // check if character can be displayed in the current line - if ((TFT_X+tmpw) > (dispWin.x2)) { - if (text_wrap == 0) break; - TFT_Y += tmph + font_line_space; - if (TFT_Y > (dispWin.y2-tmph)) break; - TFT_X = dispWin.x1; + if ((TFT_X+tmpw) > (tft_dispWin.x2)) { + if (tft_text_wrap == 0) break; + TFT_Y += tmph + tft_font_line_space; + if (TFT_Y > (tft_dispWin.y2-tmph)) break; + TFT_X = tft_dispWin.x1; } // Let's print the character - if (cfont.x_size == 0) { + if (tft_cfont.x_size == 0) { // == proportional font - if (font_rotate == 0) TFT_X += printProportionalChar(TFT_X, TFT_Y) + 1; + if (tft_font_rotate == 0) TFT_X += printProportionalChar(TFT_X, TFT_Y) + 1; else { // rotated proportional font offset += rotatePropChar(x, y, offset); @@ -2018,18 +2018,18 @@ void TFT_print(char *st, int x, int y) { } } else { - if (cfont.bitmap == 1) { + if (tft_cfont.bitmap == 1) { // == fixed font - if ((ch < cfont.offset) || ((ch-cfont.offset) > cfont.numchars)) ch = cfont.offset; - if (font_rotate == 0) { + if ((ch < tft_cfont.offset) || ((ch-tft_cfont.offset) > tft_cfont.numchars)) ch = tft_cfont.offset; + if (tft_font_rotate == 0) { printChar(ch, TFT_X, TFT_Y); TFT_X += tmpw; } else rotateChar(ch, x, y, i); } - else if (cfont.bitmap == 2) { + else if (tft_cfont.bitmap == 2) { // == 7-segment font == - _draw7seg(TFT_X, TFT_Y, ch, cfont.y_size, cfont.x_size, _fg); + _draw7seg(TFT_X, TFT_Y, ch, tft_cfont.y_size, tft_cfont.x_size, tft_fg); TFT_X += (tmpw + 2); } } @@ -2052,16 +2052,16 @@ void TFT_setRotation(uint8_t rot) { } } else { - orientation = rot; + tft_orientation = rot; _tft_setRotation(rot); } - dispWin.x1 = 0; - dispWin.y1 = 0; - dispWin.x2 = tft_width-1; - dispWin.y2 = tft_height-1; + tft_dispWin.x1 = 0; + tft_dispWin.y1 = 0; + tft_dispWin.x2 = tft_width-1; + tft_dispWin.y2 = tft_height-1; - TFT_fillScreen(_bg); + TFT_fillScreen(tft_bg); } // Send the command to invert all of the colors. @@ -2151,29 +2151,29 @@ color_t HSBtoRGB(float _hue, float _sat, float _brightness) { //===================================================================== void TFT_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { - dispWin.x1 = x1; - dispWin.y1 = y1; - dispWin.x2 = x2; - dispWin.y2 = y2; + tft_dispWin.x1 = x1; + tft_dispWin.y1 = y1; + tft_dispWin.x2 = x2; + tft_dispWin.y2 = y2; - if (dispWin.x2 >= tft_width) dispWin.x2 = tft_width-1; - if (dispWin.y2 >= tft_height) dispWin.y2 = tft_height-1; - if (dispWin.x1 > dispWin.x2) dispWin.x1 = dispWin.x2; - if (dispWin.y1 > dispWin.y2) dispWin.y1 = dispWin.y2; + if (tft_dispWin.x2 >= tft_width) tft_dispWin.x2 = tft_width-1; + if (tft_dispWin.y2 >= tft_height) tft_dispWin.y2 = tft_height-1; + if (tft_dispWin.x1 > tft_dispWin.x2) tft_dispWin.x1 = tft_dispWin.x2; + if (tft_dispWin.y1 > tft_dispWin.y2) tft_dispWin.y1 = tft_dispWin.y2; } //===================== void TFT_resetclipwin() { - dispWin.x2 = tft_width-1; - dispWin.y2 = tft_height-1; - dispWin.x1 = 0; - dispWin.y1 = 0; + tft_dispWin.x2 = tft_width-1; + tft_dispWin.y2 = tft_height-1; + tft_dispWin.x1 = 0; + tft_dispWin.y1 = 0; } //========================================================================== void set_7seg_font_atrib(uint8_t l, uint8_t w, int outline, color_t color) { - if (cfont.bitmap != 2) return; + if (tft_cfont.bitmap != 2) return; if (l < 6) l = 6; if (l > 40) l = 40; @@ -2181,21 +2181,21 @@ void set_7seg_font_atrib(uint8_t l, uint8_t w, int outline, color_t color) { if (w > (l/2)) w = l/2; if (w > 12) w = 12; - cfont.x_size = l; - cfont.y_size = w; - cfont.offset = outline; - cfont.color = color; + tft_cfont.x_size = l; + tft_cfont.y_size = w; + tft_cfont.offset = outline; + tft_cfont.color = color; } //========================================== int TFT_getfontsize(int *width, int* height) { - if (cfont.bitmap == 1) { - if (cfont.x_size != 0) *width = cfont.x_size; // fixed width font - else *width = cfont.max_x_size; // proportional font - *height = cfont.y_size; + if (tft_cfont.bitmap == 1) { + if (tft_cfont.x_size != 0) *width = tft_cfont.x_size; // fixed width font + else *width = tft_cfont.max_x_size; // proportional font + *height = tft_cfont.y_size; } - else if (cfont.bitmap == 2) { + else if (tft_cfont.bitmap == 2) { // 7-segment font *width = _7seg_width(); *height = _7seg_height(); @@ -2211,27 +2211,27 @@ int TFT_getfontsize(int *width, int* height) //===================== int TFT_getfontheight() { - if (cfont.bitmap == 1) return cfont.y_size; // Bitmap font - else if (cfont.bitmap == 2) return _7seg_height(); // 7-segment font + if (tft_cfont.bitmap == 1) return tft_cfont.y_size; // Bitmap font + else if (tft_cfont.bitmap == 2) return _7seg_height(); // 7-segment font return 0; } //==================== void TFT_saveClipWin() { - dispWinTemp.x1 = dispWin.x1; - dispWinTemp.y1 = dispWin.y1; - dispWinTemp.x2 = dispWin.x2; - dispWinTemp.y2 = dispWin.y2; + dispWinTemp.x1 = tft_dispWin.x1; + dispWinTemp.y1 = tft_dispWin.y1; + dispWinTemp.x2 = tft_dispWin.x2; + dispWinTemp.y2 = tft_dispWin.y2; } //======================= void TFT_restoreClipWin() { - dispWin.x1 = dispWinTemp.x1; - dispWin.y1 = dispWinTemp.y1; - dispWin.x2 = dispWinTemp.x2; - dispWin.y2 = dispWinTemp.y2; + tft_dispWin.x1 = dispWinTemp.x1; + tft_dispWin.y1 = dispWinTemp.y1; + tft_dispWin.x2 = dispWinTemp.x2; + tft_dispWin.y2 = dispWinTemp.y2; } @@ -2319,20 +2319,20 @@ static UINT tjd_output ( int right = rect->right + dev->x; int bottom = rect->bottom + dev->y; - if ((left > dispWin.x2) || (top > dispWin.y2)) return 1; // out of screen area, return - if ((right < dispWin.x1) || (bottom < dispWin.y1)) return 1;// out of screen area, return + if ((left > tft_dispWin.x2) || (top > tft_dispWin.y2)) return 1; // out of screen area, return + if ((right < tft_dispWin.x1) || (bottom < tft_dispWin.y1)) return 1;// out of screen area, return - if (left < dispWin.x1) dleft = dispWin.x1; + if (left < tft_dispWin.x1) dleft = tft_dispWin.x1; else dleft = left; - if (top < dispWin.y1) dtop = dispWin.y1; + if (top < tft_dispWin.y1) dtop = tft_dispWin.y1; else dtop = top; - if (right > dispWin.x2) dright = dispWin.x2; + if (right > tft_dispWin.x2) dright = tft_dispWin.x2; else dright = right; - if (bottom > dispWin.y2) dbottom = dispWin.y2; + if (bottom > tft_dispWin.y2) dbottom = tft_dispWin.y2; else dbottom = bottom; - if ((dleft > dispWin.x2) || (dtop > dispWin.y2)) return 1; // out of screen area, return - if ((dright < dispWin.x1) || (dbottom < dispWin.y1)) return 1; // out of screen area, return + if ((dleft > tft_dispWin.x2) || (dtop > tft_dispWin.y2)) return 1; // out of screen area, return + if ((dright < tft_dispWin.x1) || (dbottom < tft_dispWin.y1)) return 1; // out of screen area, return uint32_t len = ((dright-dleft+1) * (dbottom-dtop+1)); // calculate length of data @@ -2394,13 +2394,13 @@ void TFT_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int s dev.bufptr = 0; if (stat(fname, &sb) != 0) { - if (image_debug) printf("File error: %ss\r\n", strerror(errno)); + if (tft_image_debug) printf("File error: %ss\r\n", strerror(errno)); goto exit; } dev.fhndl = fopen(fname, "r"); if (!dev.fhndl) { - if (image_debug) printf("Error opening file: %s\r\n", strerror(errno)); + if (tft_image_debug) printf("Error opening file: %s\r\n", strerror(errno)); goto exit; } } @@ -2412,28 +2412,28 @@ void TFT_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int s if (dev.membuff) rc = jd_prepare(&jd, tjd_buf_input, (void *)work, sz_work, &dev); else rc = jd_prepare(&jd, tjd_input, (void *)work, sz_work, &dev); if (rc == JDR_OK) { - if (x == CENTER) x = ((dispWin.x2 - dispWin.x1 + 1 - (int)(jd.width >> scale)) / 2) + dispWin.x1; - else if (x == RIGHT) x = dispWin.x2 + 1 - (int)(jd.width >> scale); + if (x == CENTER) x = ((tft_dispWin.x2 - tft_dispWin.x1 + 1 - (int)(jd.width >> scale)) / 2) + tft_dispWin.x1; + else if (x == RIGHT) x = tft_dispWin.x2 + 1 - (int)(jd.width >> scale); - if (y == CENTER) y = ((dispWin.y2 - dispWin.y1 + 1 - (int)(jd.height >> scale)) / 2) + dispWin.y1; - else if (y == BOTTOM) y = dispWin.y2 + 1 - (int)(jd.height >> scale); + if (y == CENTER) y = ((tft_dispWin.y2 - tft_dispWin.y1 + 1 - (int)(jd.height >> scale)) / 2) + tft_dispWin.y1; + else if (y == BOTTOM) y = tft_dispWin.y2 + 1 - (int)(jd.height >> scale); - if (x < ((dispWin.x2-1) * -1)) x = (dispWin.x2-1) * -1; - if (y < ((dispWin.y2-1)) * -1) y = (dispWin.y2-1) * -1; - if (x > (dispWin.x2-1)) x = dispWin.x2 - 1; - if (y > (dispWin.y2-1)) y = dispWin.y2-1; + if (x < ((tft_dispWin.x2-1) * -1)) x = (tft_dispWin.x2-1) * -1; + if (y < ((tft_dispWin.y2-1)) * -1) y = (tft_dispWin.y2-1) * -1; + if (x > (tft_dispWin.x2-1)) x = tft_dispWin.x2 - 1; + if (y > (tft_dispWin.y2-1)) y = tft_dispWin.y2-1; dev.x = x; dev.y = y; dev.linbuf[0] = heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE*3, MALLOC_CAP_DMA); if (dev.linbuf[0] == NULL) { - if (image_debug) printf("Error allocating line buffer #0\r\n"); + if (tft_image_debug) printf("Error allocating line buffer #0\r\n"); goto exit; } dev.linbuf[1] = heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE*3, MALLOC_CAP_DMA); if (dev.linbuf[1] == NULL) { - if (image_debug) printf("Error allocating line buffer #1\r\n"); + if (tft_image_debug) printf("Error allocating line buffer #1\r\n"); goto exit; } @@ -2443,16 +2443,16 @@ void TFT_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int s disp_deselect(); if (rc != JDR_OK) { - if (image_debug) printf("jpg decompression error %d\r\n", rc); + if (tft_image_debug) printf("jpg decompression error %d\r\n", rc); } - if (image_debug) printf("Jpg size: %dx%d, position; %d,%d, scale: %d, bytes used: %d\r\n", jd.width, jd.height, x, y, scale, jd.sz_pool); + if (tft_image_debug) printf("Jpg size: %dx%d, position; %d,%d, scale: %d, bytes used: %d\r\n", jd.width, jd.height, x, y, scale, jd.sz_pool); } else { - if (image_debug) printf("jpg prepare error %d\r\n", rc); + if (tft_image_debug) printf("jpg prepare error %d\r\n", rc); } } else { - if (image_debug) printf("work buffer allocation error\r\n"); + if (tft_image_debug) printf("work buffer allocation error\r\n"); } exit: @@ -2545,21 +2545,21 @@ int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int img_xlen = img_xsize / scale_pix; // image display horizontal size img_ylen = img_ysize / scale_pix; // image display vertical size - if (x == CENTER) x = ((dispWin.x2 - dispWin.x1 + 1 - img_xlen) / 2) + dispWin.x1; - else if (x == RIGHT) x = dispWin.x2 + 1 - img_xlen; + if (x == CENTER) x = ((tft_dispWin.x2 - tft_dispWin.x1 + 1 - img_xlen) / 2) + tft_dispWin.x1; + else if (x == RIGHT) x = tft_dispWin.x2 + 1 - img_xlen; - if (y == CENTER) y = ((dispWin.y2 - dispWin.y1 + 1 - img_ylen) / 2) + dispWin.y1; - else if (y == BOTTOM) y = dispWin.y2 + 1 - img_ylen; + if (y == CENTER) y = ((tft_dispWin.y2 - tft_dispWin.y1 + 1 - img_ylen) / 2) + tft_dispWin.y1; + else if (y == BOTTOM) y = tft_dispWin.y2 + 1 - img_ylen; - if ((x < ((dispWin.x2 + 1) * -1)) || (x > (dispWin.x2 + 1)) || (y < ((dispWin.y2 + 1) * -1)) || (y > (dispWin.y2 + 1))) { + if ((x < ((tft_dispWin.x2 + 1) * -1)) || (x > (tft_dispWin.x2 + 1)) || (y < ((tft_dispWin.y2 + 1) * -1)) || (y > (tft_dispWin.y2 + 1))) { sprintf(err_buf, "out of display area (%d,%d", x, y); err = -10; goto exit; } // ** set display and image areas - if (x < dispWin.x1) { - disp_xstart = dispWin.x1; + if (x < tft_dispWin.x1) { + disp_xstart = tft_dispWin.x1; img_xstart = -x; // image pixel line X offset img_xlen += x; } @@ -2567,8 +2567,8 @@ int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int disp_xstart = x; img_xstart = 0; } - if (y < dispWin.y1) { - disp_ystart = dispWin.y1; + if (y < tft_dispWin.y1) { + disp_ystart = tft_dispWin.y1; img_ystart = -y; // image pixel line Y offset img_ylen += y; } @@ -2578,12 +2578,12 @@ int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int } disp_xend = disp_xstart + img_xlen - 1; disp_yend = disp_ystart + img_ylen - 1; - if (disp_xend > dispWin.x2) { - disp_xend = dispWin.x2; + if (disp_xend > tft_dispWin.x2) { + disp_xend = tft_dispWin.x2; img_xlen = disp_xend - disp_xstart + 1; } - if (disp_yend > dispWin.y2) { - disp_yend = dispWin.y2; + if (disp_yend > tft_dispWin.y2) { + disp_yend = tft_dispWin.y2; img_ylen = disp_yend - disp_ystart + 1; } @@ -2646,7 +2646,7 @@ int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int } } - if (image_debug) printf("BMP: image size: (%d,%d) scale: %d disp size: (%d,%d) img xofs: %d img yofs: %d at: %d,%d; line buf: 2* %d scale buf: %d\r\n", + if (tft_image_debug) printf("BMP: image size: (%d,%d) scale: %d disp size: (%d,%d) img xofs: %d img yofs: %d at: %d,%d; line buf: 2* %d scale buf: %d\r\n", img_xsize, img_ysize, scale_pix, img_xlen, img_ylen, img_xstart, img_ystart, disp_xstart, disp_ystart, img_xsize*3, ((scale) ? (rd_len*scale_pix) : 0)); // * Select the display @@ -2737,7 +2737,7 @@ int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int if (line_buf[0]) free(line_buf[0]); if (line_buf[1]) free(line_buf[1]); if (fhndl) fclose(fhndl); - if ((err) && (image_debug)) printf("Error: %d [%s]\r\n", err, err_buf); + if ((err) && (tft_image_debug)) printf("Error: %d [%s]\r\n", err, err_buf); return err; } @@ -2749,7 +2749,7 @@ int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int //------------------------------------------------------- static int tp_get_data_xpt2046(uint8_t type, int samples) { - if (ts_spi == NULL) return 0; + if (tft_ts_spi == NULL) return 0; int n, result, val = 0; uint32_t i = 0; @@ -2811,7 +2811,7 @@ static int tp_get_data_xpt2046(uint8_t type, int samples) static int TFT_read_touch_xpt2046(int *x, int* y) { int res = 0, result = -1; - if (spi_lobo_device_select(ts_spi, 0) != ESP_OK) return 0; + if (spi_lobo_device_select(tft_ts_spi, 0) != ESP_OK) return 0; result = tp_get_data_xpt2046(0xB0, 3); // Z; pressure; touch detect if (result <= 50) goto exit; @@ -2828,7 +2828,7 @@ static int TFT_read_touch_xpt2046(int *x, int* y) *y = result; res = 1; exit: - spi_lobo_device_deselect(ts_spi); + spi_lobo_device_deselect(tft_ts_spi); return res; } #endif @@ -2838,7 +2838,7 @@ int TFT_read_touch(int *x, int* y, uint8_t raw) { *x = 0; *y = 0; - if (ts_spi == NULL) return 0; + if (tft_ts_spi == NULL) return 0; #if USE_TOUCH == TOUCH_TYPE_NONE return 0; #else @@ -2846,13 +2846,13 @@ int TFT_read_touch(int *x, int* y, uint8_t raw) int X=0, Y=0; #if USE_TOUCH == TOUCH_TYPE_XPT2046 - uint32_t tp_calx = TP_CALX_XPT2046; - uint32_t tp_caly = TP_CALY_XPT2046; + uint32_t tft_tp_calx = TP_CALX_XPT2046; + uint32_t tft_tp_caly = TP_CALY_XPT2046; result = TFT_read_touch_xpt2046(&X, &Y); if (result == 0) return 0; #elif USE_TOUCH == TOUCH_TYPE_STMPE610 - uint32_t tp_calx = TP_CALX_STMPE610; - uint32_t tp_caly = TP_CALY_STMPE610; + uint32_t tft_tp_calx = TP_CALX_STMPE610; + uint32_t tft_tp_caly = TP_CALY_STMPE610; uint16_t Xx, Yy, Z=0; result = stmpe610_get_touch(&Xx, &Yy, &Z); if (result == 0) return 0; @@ -2870,10 +2870,10 @@ int TFT_read_touch(int *x, int* y, uint8_t raw) // Calibrate the result int tmp; - int xleft = (tp_calx >> 16) & 0x3FFF; - int xright = tp_calx & 0x3FFF; - int ytop = (tp_caly >> 16) & 0x3FFF; - int ybottom = tp_caly & 0x3FFF; + int xleft = (tft_tp_calx >> 16) & 0x3FFF; + int xright = tft_tp_calx & 0x3FFF; + int ytop = (tft_tp_caly >> 16) & 0x3FFF; + int ybottom = tft_tp_caly & 0x3FFF; if (((xright - xleft) <= 0) || ((ybottom - ytop) <= 0)) return 0; @@ -2888,7 +2888,7 @@ int TFT_read_touch(int *x, int* y, uint8_t raw) if (Y < 0) Y = 0; if (Y > width-1) Y = width-1; - switch (orientation) { + switch (tft_orientation) { case PORTRAIT: tmp = X; X = width - Y - 1; @@ -2919,7 +2919,7 @@ int TFT_read_touch(int *x, int* y, uint8_t raw) if (Y < 0) Y = 0; if (Y > height-1) Y = height-1; - switch (orientation) { + switch (tft_orientation) { case PORTRAIT_FLIP: X = width - X - 1; Y = height - Y - 1; diff --git a/components/tft/tft.h b/components/tft/tft.h index bea198a..912b432 100644 --- a/components/tft/tft.h +++ b/components/tft/tft.h @@ -38,26 +38,26 @@ typedef struct { //========================================================================================== // ==== Global variables =================================================================== //========================================================================================== -extern uint8_t orientation; // current screen orientation -extern uint16_t font_rotate; // current font font_rotate angle (0~395) -extern uint8_t font_transparent; // if not 0 draw fonts transparent -extern uint8_t font_forceFixed; // if not zero force drawing proportional fonts with fixed width -extern uint8_t font_buffered_char; -extern uint8_t font_line_space; // additional spacing between text lines; added to font height -extern uint8_t text_wrap; // if not 0 wrap long text to the new line, else clip -extern color_t _fg; // current foreground color for fonts -extern color_t _bg; // current background for non transparent fonts -extern dispWin_t dispWin; // display clip window -extern float _angleOffset; // angle offset for arc, polygon and line by angle functions -extern uint8_t image_debug; // print debug messages during image decode if set to 1 - -extern Font cfont; // Current font structure +extern uint8_t tft_orientation; // current screen tft_orientation +extern uint16_t tft_font_rotate; // current font tft_font_rotate angle (0~395) +extern uint8_t tft_font_transparent; // if not 0 draw fonts transparent +extern uint8_t tft_font_forceFixed; // if not zero force drawing proportional fonts with fixed width +extern uint8_t tft_font_buffered_char; +extern uint8_t tft_font_line_space; // additional spacing between text lines; added to font height +extern uint8_t tft_text_wrap; // if not 0 wrap long text to the new line, else clip +extern color_t tft_fg; // current foreground color for fonts +extern color_t tft_bg; // current background for non transparent fonts +extern dispWin_t tft_dispWin; // display clip window +extern float tft_angleOffset; // angle offset for arc, polygon and line by angle functions +extern uint8_t tft_image_debug; // print debug messages during image decode if set to 1 + +extern Font tft_cfont; // Current font structure extern int TFT_X; // X position of the next character after TFT_print() function extern int TFT_Y; // Y position of the next character after TFT_print() function -extern uint32_t tp_calx; // touch screen X calibration constant -extern uint32_t tp_caly; // touch screen Y calibration constant +extern uint32_t tft_tp_calx; // touch screen X calibration constant +extern uint32_t tft_tp_caly; // touch screen Y calibration constant // ========================================================================================= @@ -204,7 +204,7 @@ void TFT_drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color) /* * Draw line on screen from (x,y) point at given angle * Line drawing angle starts at lower right quadrant of the screen and is offseted by - * '_angleOffset' global variable (default: -90 degrees) + * 'tft_angleOffset' global variable (default: -90 degrees) * * Params: * x: horizontal start position @@ -387,7 +387,7 @@ void TFT_fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t /* * Draw circle arc on screen * Arc drawing angle starts at lower right quadrant of the screen and is offseted by - * '_angleOffset' global variable (default: -90 degrees) + * 'tft_angleOffset' global variable (default: -90 degrees) * * Params: * cx: arc center X position @@ -460,11 +460,11 @@ int TFT_getfontheight(); /* * Write text to display. * - * Rotation of the displayed text depends on 'font_rotate' variable (0~360) - * if 'font_transparent' variable is set to 1, no background pixels will be printed + * Rotation of the displayed text depends on 'tft_font_rotate' variable (0~360) + * if 'tft_font_transparent' variable is set to 1, no background pixels will be printed * - * If the text does not fit the screen width it will be clipped (if text_wrap=0), - * or continued on next line (if text_wrap=1) + * If the text does not fit the screen width it will be clipped (if tft_text_wrap=0), + * or continued on next line (if tft_text_wrap=1) * * Two special characters are allowed in strings: * ‘\r’ CR (0x0D), clears the display to EOL @@ -644,7 +644,7 @@ int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int /* * Get the touch panel coordinates. - * The coordinates are adjusted to screen orientation if raw=0 + * The coordinates are adjusted to screen tft_orientation if raw=0 * * Params: * x: pointer to X coordinate diff --git a/components/tft/tftspi.c b/components/tft/tftspi.c index 082acf9..51ceba4 100644 --- a/components/tft/tftspi.c +++ b/components/tft/tftspi.c @@ -21,9 +21,9 @@ // ==== Global variables, default values ============== // Converts colors to grayscale if set to 1 -uint8_t gray_scale = 0; +uint8_t tft_gray_scale = 0; // Spi clock for reading data from display memory in Hz -uint32_t max_rdclock = 8000000; +uint32_t tft_max_rdclock = 8000000; // Default display dimensions int tft_width = CONFIG_TFT_DISPLAY_WIDTH; @@ -33,8 +33,8 @@ int tft_height = CONFIG_TFT_DISPLAY_HEIGHT; uint8_t tft_disp_type = DEFAULT_DISP_TYPE; // Spi device handles for display and touch screen -spi_lobo_device_handle_t disp_spi = NULL; -spi_lobo_device_handle_t ts_spi = NULL; +spi_lobo_device_handle_t tft_disp_spi = NULL; +spi_lobo_device_handle_t tft_ts_spi = NULL; // ==================================================== @@ -56,21 +56,21 @@ static uint8_t _dma_sending = 0; esp_err_t IRAM_ATTR wait_trans_finish(uint8_t free_line) { // Wait for SPI bus ready - while (disp_spi->host->hw->cmd.usr); + while (tft_disp_spi->host->hw->cmd.usr); if ((free_line) && (trans_cline)) { free(trans_cline); trans_cline = NULL; } if (_dma_sending) { //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset. - if (disp_spi->host->dma_chan) spi_lobo_dmaworkaround_idle(disp_spi->host->dma_chan); + if (tft_disp_spi->host->dma_chan) spi_lobo_dmaworkaround_idle(tft_disp_spi->host->dma_chan); // Reset DMA - disp_spi->host->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; - disp_spi->host->hw->dma_out_link.start=0; - disp_spi->host->hw->dma_in_link.start=0; - disp_spi->host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); - disp_spi->host->hw->dma_conf.out_data_burst_en=1; + tft_disp_spi->host->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; + tft_disp_spi->host->hw->dma_out_link.start=0; + tft_disp_spi->host->hw->dma_in_link.start=0; + tft_disp_spi->host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); + tft_disp_spi->host->hw->dma_conf.out_data_burst_en=1; _dma_sending = 0; } return ESP_OK; @@ -80,14 +80,14 @@ esp_err_t IRAM_ATTR wait_trans_finish(uint8_t free_line) esp_err_t IRAM_ATTR disp_select() { wait_trans_finish(1); - return spi_lobo_device_select(disp_spi, 0); + return spi_lobo_device_select(tft_disp_spi, 0); } //--------------------------------- esp_err_t IRAM_ATTR disp_deselect() { wait_trans_finish(1); - return spi_lobo_device_deselect(disp_spi); + return spi_lobo_device_deselect(tft_disp_spi); } //--------------------------------------------------------------------------------------------------- @@ -114,26 +114,26 @@ static void IRAM_ATTR _spi_transfer_start(spi_lobo_device_handle_t spi_dev, int //------------------------------------------------ void IRAM_ATTR disp_spi_transfer_cmd(int8_t cmd) { // Wait for SPI bus ready - while (disp_spi->host->hw->cmd.usr); + while (tft_disp_spi->host->hw->cmd.usr); // Set DC to 0 (command mode); gpio_set_level(PIN_NUM_DC, 0); - disp_spi->host->hw->data_buf[0] = (uint32_t)cmd; - _spi_transfer_start(disp_spi, 8, 0); + tft_disp_spi->host->hw->data_buf[0] = (uint32_t)cmd; + _spi_transfer_start(tft_disp_spi, 8, 0); } // Send command with data to display, display must be selected //---------------------------------------------------------------------------------- void IRAM_ATTR disp_spi_transfer_cmd_data(int8_t cmd, uint8_t *data, uint32_t len) { // Wait for SPI bus ready - while (disp_spi->host->hw->cmd.usr); + while (tft_disp_spi->host->hw->cmd.usr); // Set DC to 0 (command mode); gpio_set_level(PIN_NUM_DC, 0); - disp_spi->host->hw->data_buf[0] = (uint32_t)cmd; - _spi_transfer_start(disp_spi, 8, 0); + tft_disp_spi->host->hw->data_buf[0] = (uint32_t)cmd; + _spi_transfer_start(tft_disp_spi, 8, 0); if ((len == 0) || (data == NULL)) return; @@ -151,25 +151,25 @@ void IRAM_ATTR disp_spi_transfer_cmd_data(int8_t cmd, uint8_t *data, uint32_t le bits += 8; bidx += 8; if (count == len) { - disp_spi->host->hw->data_buf[idx] = wd; + tft_disp_spi->host->hw->data_buf[idx] = wd; break; } if (bidx == 32) { - disp_spi->host->hw->data_buf[idx] = wd; + tft_disp_spi->host->hw->data_buf[idx] = wd; idx++; bidx = 0; wd = 0; } if (idx == 16) { // SPI buffer full, send data - _spi_transfer_start(disp_spi, bits, 0); + _spi_transfer_start(tft_disp_spi, bits, 0); bits = 0; idx = 0; bidx = 0; } } - if (bits > 0) _spi_transfer_start(disp_spi, bits, 0); + if (bits > 0) _spi_transfer_start(tft_disp_spi, bits, 0); } // Set the address window for display write & read commands, display must be selected @@ -179,47 +179,47 @@ static void IRAM_ATTR disp_spi_transfer_addrwin(uint16_t x1, uint16_t x2, uint16 taskDISABLE_INTERRUPTS(); // Wait for SPI bus ready - while (disp_spi->host->hw->cmd.usr); + while (tft_disp_spi->host->hw->cmd.usr); gpio_set_level(PIN_NUM_DC, 0); - disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_CASET; - disp_spi->host->hw->user.usr_mosi_highpart = 0; - disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; - disp_spi->host->hw->user.usr_mosi = 1; - disp_spi->host->hw->miso_dlen.usr_miso_dbitlen = 0; - disp_spi->host->hw->user.usr_miso = 0; + tft_disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_CASET; + tft_disp_spi->host->hw->user.usr_mosi_highpart = 0; + tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; + tft_disp_spi->host->hw->user.usr_mosi = 1; + tft_disp_spi->host->hw->miso_dlen.usr_miso_dbitlen = 0; + tft_disp_spi->host->hw->user.usr_miso = 0; - disp_spi->host->hw->cmd.usr = 1; // Start transfer + tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer wd = (uint32_t)(x1>>8); wd |= (uint32_t)(x1&0xff) << 8; wd |= (uint32_t)(x2>>8) << 16; wd |= (uint32_t)(x2&0xff) << 24; - while (disp_spi->host->hw->cmd.usr); // wait transfer end + while (tft_disp_spi->host->hw->cmd.usr); // wait transfer end gpio_set_level(PIN_NUM_DC, 1); - disp_spi->host->hw->data_buf[0] = wd; - disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 31; - disp_spi->host->hw->cmd.usr = 1; // Start transfer + tft_disp_spi->host->hw->data_buf[0] = wd; + tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 31; + tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer - while (disp_spi->host->hw->cmd.usr); + while (tft_disp_spi->host->hw->cmd.usr); gpio_set_level(PIN_NUM_DC, 0); - disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_PASET; - disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; - disp_spi->host->hw->cmd.usr = 1; // Start transfer + tft_disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_PASET; + tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; + tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer wd = (uint32_t)(y1>>8); wd |= (uint32_t)(y1&0xff) << 8; wd |= (uint32_t)(y2>>8) << 16; wd |= (uint32_t)(y2&0xff) << 24; - while (disp_spi->host->hw->cmd.usr); + while (tft_disp_spi->host->hw->cmd.usr); gpio_set_level(PIN_NUM_DC, 1); - disp_spi->host->hw->data_buf[0] = wd; - disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 31; - disp_spi->host->hw->cmd.usr = 1; // Start transfer - while (disp_spi->host->hw->cmd.usr); + tft_disp_spi->host->hw->data_buf[0] = wd; + tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 31; + tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer + while (tft_disp_spi->host->hw->cmd.usr); taskENABLE_INTERRUPTS(); } @@ -242,7 +242,7 @@ static color_t IRAM_ATTR color2gs(color_t color) //------------------------------------------------------------------------ void IRAM_ATTR drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) { - if (!(disp_spi->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)) return; + if (!(tft_disp_spi->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)) return; if (sel) { if (disp_select()) return; @@ -251,17 +251,17 @@ void IRAM_ATTR drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) uint32_t wd = 0; color_t _color = color; - if (gray_scale) _color = color2gs(color); + if (tft_gray_scale) _color = color2gs(color); taskDISABLE_INTERRUPTS(); disp_spi_transfer_addrwin(x, x+1, y, y+1); // Send RAM WRITE command gpio_set_level(PIN_NUM_DC, 0); - disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_RAMWR; - disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; - disp_spi->host->hw->cmd.usr = 1; // Start transfer - while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready + tft_disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_RAMWR; + tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; + tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer + while (tft_disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready wd = (uint32_t)_color.r; wd |= (uint32_t)_color.g << 8; @@ -270,10 +270,10 @@ void IRAM_ATTR drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) // Set DC to 1 (data mode); gpio_set_level(PIN_NUM_DC, 1); - disp_spi->host->hw->data_buf[0] = wd; - disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 23; - disp_spi->host->hw->cmd.usr = 1; // Start transfer - while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready + tft_disp_spi->host->hw->data_buf[0] = wd; + tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 23; + tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer + while (tft_disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready taskENABLE_INTERRUPTS(); if (sel) disp_deselect(); @@ -283,18 +283,18 @@ void IRAM_ATTR drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) static void IRAM_ATTR _dma_send(uint8_t *data, uint32_t size) { //Fill DMA descriptors - spi_lobo_dmaworkaround_transfer_active(disp_spi->host->dma_chan); //mark channel as active - spi_lobo_setup_dma_desc_links(disp_spi->host->dmadesc_tx, size, data, false); - disp_spi->host->hw->user.usr_mosi_highpart=0; - disp_spi->host->hw->dma_out_link.addr=(int)(&disp_spi->host->dmadesc_tx[0]) & 0xFFFFF; - disp_spi->host->hw->dma_out_link.start=1; - disp_spi->host->hw->user.usr_mosi_highpart=0; + spi_lobo_dmaworkaround_transfer_active(tft_disp_spi->host->dma_chan); //mark channel as active + spi_lobo_setup_dma_desc_links(tft_disp_spi->host->dmadesc_tx, size, data, false); + tft_disp_spi->host->hw->user.usr_mosi_highpart=0; + tft_disp_spi->host->hw->dma_out_link.addr=(int)(&tft_disp_spi->host->dmadesc_tx[0]) & 0xFFFFF; + tft_disp_spi->host->hw->dma_out_link.start=1; + tft_disp_spi->host->hw->user.usr_mosi_highpart=0; - disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = (size * 8) - 1; + tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = (size * 8) - 1; _dma_sending = 1; // Start transfer - disp_spi->host->hw->cmd.usr = 1; + tft_disp_spi->host->hw->cmd.usr = 1; } //--------------------------------------------------------------------------- @@ -308,12 +308,12 @@ static void IRAM_ATTR _direct_send(color_t *color, uint32_t len, uint8_t rep) taskDISABLE_INTERRUPTS(); color_t _color = color[0]; - if ((rep) && (gray_scale)) _color = color2gs(color[0]); + if ((rep) && (tft_gray_scale)) _color = color2gs(color[0]); while (len) { // ** Get color data from color buffer ** if (rep == 0) { - if (gray_scale) _color = color2gs(color[cidx]); + if (tft_gray_scale) _color = color2gs(color[cidx]); else _color = color[cidx]; } @@ -322,7 +322,7 @@ static void IRAM_ATTR _direct_send(color_t *color, uint32_t len, uint8_t rep) if (wbits == 32) { bits += wbits; wbits = 0; - disp_spi->host->hw->data_buf[idx++] = wd; + tft_disp_spi->host->hw->data_buf[idx++] = wd; wd = 0; } wd |= (uint32_t)_color.g << wbits; @@ -330,7 +330,7 @@ static void IRAM_ATTR _direct_send(color_t *color, uint32_t len, uint8_t rep) if (wbits == 32) { bits += wbits; wbits = 0; - disp_spi->host->hw->data_buf[idx++] = wd; + tft_disp_spi->host->hw->data_buf[idx++] = wd; wd = 0; } wd |= (uint32_t)_color.b << wbits; @@ -338,16 +338,16 @@ static void IRAM_ATTR _direct_send(color_t *color, uint32_t len, uint8_t rep) if (wbits == 32) { bits += wbits; wbits = 0; - disp_spi->host->hw->data_buf[idx++] = wd; + tft_disp_spi->host->hw->data_buf[idx++] = wd; wd = 0; } len--; // Decrement colors counter if (rep == 0) cidx++; // if not repeating color, increment color buffer index } if (bits) { - while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready - disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1; // set number of bits to be sent - disp_spi->host->hw->cmd.usr = 1; // Start transfer + while (tft_disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready + tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1; // set number of bits to be sent + tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer } taskENABLE_INTERRUPTS(); } @@ -362,14 +362,14 @@ static void IRAM_ATTR _direct_send(color_t *color, uint32_t len, uint8_t rep) static void IRAM_ATTR _TFT_pushColorRep(color_t *color, uint32_t len, uint8_t rep, uint8_t wait) { if (len == 0) return; - if (!(disp_spi->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)) return; + if (!(tft_disp_spi->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)) return; // Send RAM WRITE command gpio_set_level(PIN_NUM_DC, 0); - disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_RAMWR; - disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; - disp_spi->host->hw->cmd.usr = 1; // Start transfer - while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready + tft_disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_RAMWR; + tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; + tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer + while (tft_disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready gpio_set_level(PIN_NUM_DC, 1); // Set DC to 1 (data mode); @@ -381,7 +381,7 @@ static void IRAM_ATTR _TFT_pushColorRep(color_t *color, uint32_t len, uint8_t re else if (rep == 0) { // ==== use DMA transfer ==== // ** Prepare data - if (gray_scale) { + if (tft_gray_scale) { for (int n=0; n> 4); */ - spi_lobo_device_select(ts_spi, 0); + spi_lobo_device_select(tft_ts_spi, 0); - ts_spi->host->hw->data_buf[0] = type; - _spi_transfer_start(ts_spi, 24, 24); - uint16_t res = (uint16_t)(ts_spi->host->hw->data_buf[0] >> 8); + tft_ts_spi->host->hw->data_buf[0] = type; + _spi_transfer_start(tft_ts_spi, 24, 24); + uint16_t res = (uint16_t)(tft_ts_spi->host->hw->data_buf[0] >> 8); - spi_lobo_device_deselect(ts_spi); + spi_lobo_device_deselect(tft_ts_spi); return res; } @@ -558,36 +558,36 @@ int IRAM_ATTR touch_get_data(uint8_t type) //--------------------------------------------------------- static void IRAM_ATTR stmpe610_write_reg(uint8_t reg, uint8_t val) { - spi_lobo_device_select(ts_spi, 0); + spi_lobo_device_select(tft_ts_spi, 0); - ts_spi->host->hw->data_buf[0] = (val << 8) | reg; - _spi_transfer_start(ts_spi, 16, 0); + tft_ts_spi->host->hw->data_buf[0] = (val << 8) | reg; + _spi_transfer_start(tft_ts_spi, 16, 0); - spi_lobo_device_deselect(ts_spi); + spi_lobo_device_deselect(tft_ts_spi); } //----------------------------------------------- static uint8_t IRAM_ATTR stmpe610_read_byte(uint8_t reg) { - spi_lobo_device_select(ts_spi, 0); + spi_lobo_device_select(tft_ts_spi, 0); - ts_spi->host->hw->data_buf[0] = (reg << 8) | (reg | 0x80); - _spi_transfer_start(ts_spi, 16, 16); - uint8_t res = ts_spi->host->hw->data_buf[0] >> 8; + tft_ts_spi->host->hw->data_buf[0] = (reg << 8) | (reg | 0x80); + _spi_transfer_start(tft_ts_spi, 16, 16); + uint8_t res = tft_ts_spi->host->hw->data_buf[0] >> 8; - spi_lobo_device_deselect(ts_spi); + spi_lobo_device_deselect(tft_ts_spi); return res; } //----------------------------------------- static uint16_t IRAM_ATTR stmpe610_read_word(uint8_t reg) { - spi_lobo_device_select(ts_spi, 0); + spi_lobo_device_select(tft_ts_spi, 0); - ts_spi->host->hw->data_buf[0] = ((((reg+1) << 8) | ((reg+1) | 0x80)) << 16) | (reg << 8) | (reg | 0x80); - _spi_transfer_start(ts_spi, 32, 32); - uint16_t res = (uint16_t)(ts_spi->host->hw->data_buf[0] & 0xFF00); - res |= (uint16_t)(ts_spi->host->hw->data_buf[0] >> 24); + tft_ts_spi->host->hw->data_buf[0] = ((((reg+1) << 8) | ((reg+1) | 0x80)) << 16) | (reg << 8) | (reg | 0x80); + _spi_transfer_start(tft_ts_spi, 32, 32); + uint16_t res = (uint16_t)(tft_ts_spi->host->hw->data_buf[0] & 0xFF00); + res |= (uint16_t)(tft_ts_spi->host->hw->data_buf[0] >> 24); - spi_lobo_device_deselect(ts_spi); + spi_lobo_device_deselect(tft_ts_spi); return res; } @@ -675,10 +675,10 @@ uint32_t find_rd_speed() int line_check; color_t *color_line = NULL; uint8_t *line_rdbuf = NULL; - uint8_t gs = gray_scale; + uint8_t gs = tft_gray_scale; - gray_scale = 0; - cur_speed = spi_lobo_get_speed(disp_spi); + tft_gray_scale = 0; + cur_speed = spi_lobo_get_speed(tft_disp_spi); color_line = malloc(tft_width*3); if (color_line == NULL) goto exit; @@ -696,7 +696,7 @@ uint32_t find_rd_speed() // Find maximum read spi clock for (uint32_t speed=2000000; speed<=cur_speed; speed += 1000000) { - change_speed = spi_lobo_set_speed(disp_spi, speed); + change_speed = spi_lobo_set_speed(tft_disp_spi, speed); if (change_speed == 0) goto exit; memset(line_rdbuf, 0, tft_width*sizeof(color_t)+1); @@ -726,12 +726,12 @@ uint32_t find_rd_speed() } exit: - gray_scale = gs; + tft_gray_scale = gs; if (line_rdbuf) free(line_rdbuf); if (color_line) free(color_line); // restore spi clk - change_speed = spi_lobo_set_speed(disp_spi, cur_speed); + change_speed = spi_lobo_set_speed(tft_disp_spi, cur_speed); return max_speed; } @@ -908,26 +908,26 @@ void TFT_display_init() assert(ret==ESP_OK); //Send all the initialization commands if (tft_disp_type == DISP_TYPE_ILI9341) { - commandList(disp_spi, ILI9341_init); + commandList(tft_disp_spi, ILI9341_init); } else if (tft_disp_type == DISP_TYPE_ILI9488) { - commandList(disp_spi, ILI9488_init); + commandList(tft_disp_spi, ILI9488_init); } else if (tft_disp_type == DISP_TYPE_ST7789V) { - commandList(disp_spi, ST7789V_init); + commandList(tft_disp_spi, ST7789V_init); } else if (tft_disp_type == DISP_TYPE_ST7735) { - commandList(disp_spi, STP7735_init); + commandList(tft_disp_spi, STP7735_init); } else if (tft_disp_type == DISP_TYPE_ST7735R) { - commandList(disp_spi, STP7735R_init); - commandList(disp_spi, Rcmd2green); - commandList(disp_spi, Rcmd3); + commandList(tft_disp_spi, STP7735R_init); + commandList(tft_disp_spi, Rcmd2green); + commandList(tft_disp_spi, Rcmd3); } else if (tft_disp_type == DISP_TYPE_ST7735B) { - commandList(disp_spi, STP7735R_init); - commandList(disp_spi, Rcmd2red); - commandList(disp_spi, Rcmd3); + commandList(tft_disp_spi, STP7735R_init); + commandList(tft_disp_spi, Rcmd2red); + commandList(tft_disp_spi, Rcmd3); uint8_t dt = 0xC0; disp_spi_transfer_cmd_data(TFT_MADCTL, &dt, 1); } diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index 1984be1..c8637ee 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -22,7 +22,7 @@ #define TP_CALX_STMPE610 21368532 #define TP_CALY_STMPE610 11800144 -// === Screen orientation constants === +// === Screen tft_orientation constants === #define PORTRAIT 0 #define LANDSCAPE 1 #define PORTRAIT_FLIP 2 @@ -269,10 +269,10 @@ // ############################################################## // ==== Converts colors to grayscale if 1 ======================= -extern uint8_t gray_scale; +extern uint8_t tft_gray_scale; // ==== Spi clock for reading data from display memory in Hz ==== -extern uint32_t max_rdclock; +extern uint32_t tft_max_rdclock; // ==== Display dimensions in pixels ============================ extern int tft_width; @@ -282,8 +282,8 @@ extern int tft_height; extern uint8_t tft_disp_type; // ==== Spi device handles for display and touch screen ========= -extern spi_lobo_device_handle_t disp_spi; -extern spi_lobo_device_handle_t ts_spi; +extern spi_lobo_device_handle_t tft_disp_spi; +extern spi_lobo_device_handle_t tft_ts_spi; // ############################################################## @@ -422,7 +422,7 @@ static const uint8_t ST7789V_init[] = { ST_CMD_PWCTR1, 2, 0xA4, 0xA1, TFT_CMD_GMCTRP1, 14, 0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19, TFT_CMD_GMCTRN1, 14, 0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19, - TFT_MADCTL, 1, (MADCTL_MX | TFT_RGB_BGR), // Memory Access Control (orientation) + TFT_MADCTL, 1, (MADCTL_MX | TFT_RGB_BGR), // Memory Access Control (tft_orientation) TFT_CMD_PIXFMT, 1, DISP_COLOR_BITS_24, // *** INTERFACE PIXEL FORMAT: 0x66 -> 18 bit; 0x55 -> 16 bit TFT_CMD_SLPOUT, TFT_CMD_DELAY, 120, // Sleep out, // 120 ms delay TFT_DISPON, TFT_CMD_DELAY, 120, @@ -449,7 +449,7 @@ static const uint8_t ILI9341_init[] = { TFT_CMD_PWCTR2, 1, 0x10, //Power control SAP[2:0];BT[3:0] TFT_CMD_VMCTR1, 2, 0x3e, 0x28, //VCM control TFT_CMD_VMCTR2, 1, 0x86, //VCM control2 - TFT_MADCTL, 1, // Memory Access Control (orientation) + TFT_MADCTL, 1, // Memory Access Control (tft_orientation) (MADCTL_MX | TFT_RGB_BGR), // *** INTERFACE PIXEL FORMAT: 0x66 -> 18 bit; 0x55 -> 16 bit TFT_CMD_PIXFMT, 1, DISP_COLOR_BITS_24, @@ -493,9 +493,9 @@ static const uint8_t ILI9488_init[] = { 0x80, #if TFT_INVERT_ROTATION - TFT_MADCTL, 1, (MADCTL_MV | TFT_RGB_BGR), // Memory Access Control (orientation), set to portrait + TFT_MADCTL, 1, (MADCTL_MV | TFT_RGB_BGR), // Memory Access Control (tft_orientation), set to portrait #else - TFT_MADCTL, 1, (MADCTL_MX | TFT_RGB_BGR), // Memory Access Control (orientation), set to portrait + TFT_MADCTL, 1, (MADCTL_MX | TFT_RGB_BGR), // Memory Access Control (tft_orientation), set to portrait #endif // *** INTERFACE PIXEL FORMAT: 0x66 -> 18 bit; @@ -731,7 +731,7 @@ void _tft_setRotation(uint8_t rot); void TFT_PinsInit(); // Perform display initialization sequence -// Sets orientation to landscape; clears the screen +// Sets tft_orientation to landscape; clears the screen // * All pins must be configured // * SPI interface must already be setup // * 'tft_disp_type', 'COLOR_BITS', 'tft_width', 'tft_height' variables must be set diff --git a/main/tft_demo.c b/main/tft_demo.c index efb587a..6663a2f 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -164,19 +164,19 @@ static void _checkTime() TFT_saveClipWin(); TFT_resetclipwin(); - Font curr_font = cfont; - last_bg = _bg; - last_fg = _fg; - _fg = TFT_YELLOW; - _bg = (color_t){ 64, 64, 64 }; + Font curr_font = tft_cfont; + last_bg = tft_bg; + last_fg = tft_fg; + tft_fg = TFT_YELLOW; + tft_bg = (color_t){ 64, 64, 64 }; TFT_setFont(DEFAULT_FONT, NULL); - TFT_fillRect(1, tft_height-TFT_getfontheight()-8, tft_width-3, TFT_getfontheight()+6, _bg); + TFT_fillRect(1, tft_height-TFT_getfontheight()-8, tft_width-3, TFT_getfontheight()+6, tft_bg); TFT_print(tmp_buff, CENTER, tft_height-TFT_getfontheight()-5); - cfont = curr_font; - _fg = last_fg; - _bg = last_bg; + tft_cfont = curr_font; + tft_fg = last_fg; + tft_bg = last_bg; TFT_restoreClipWin(); } @@ -252,7 +252,7 @@ static color_t random_color() { //--------------------- static void _dispTime() { - Font curr_font = cfont; + Font curr_font = tft_cfont; if (tft_width < 240) TFT_setFont(DEF_SMALL_FONT, NULL); else TFT_setFont(DEFAULT_FONT, NULL); @@ -262,7 +262,7 @@ static void _dispTime() sprintf(tmp_buff, "%02d:%02d:%02d", tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec); TFT_print(tmp_buff, CENTER, tft_height-TFT_getfontheight()-5); - cfont = curr_font; + tft_cfont = curr_font; } //--------------------------------- @@ -271,21 +271,21 @@ static void disp_header(char *info) TFT_fillScreen(TFT_BLACK); TFT_resetclipwin(); - _fg = TFT_YELLOW; - _bg = (color_t){ 64, 64, 64 }; + tft_fg = TFT_YELLOW; + tft_bg = (color_t){ 64, 64, 64 }; if (tft_width < 240) TFT_setFont(DEF_SMALL_FONT, NULL); else TFT_setFont(DEFAULT_FONT, NULL); - TFT_fillRect(0, 0, tft_width-1, TFT_getfontheight()+8, _bg); + TFT_fillRect(0, 0, tft_width-1, TFT_getfontheight()+8, tft_bg); TFT_drawRect(0, 0, tft_width-1, TFT_getfontheight()+8, TFT_CYAN); - TFT_fillRect(0, tft_height-TFT_getfontheight()-9, tft_width-1, TFT_getfontheight()+8, _bg); + TFT_fillRect(0, tft_height-TFT_getfontheight()-9, tft_width-1, TFT_getfontheight()+8, tft_bg); TFT_drawRect(0, tft_height-TFT_getfontheight()-9, tft_width-1, TFT_getfontheight()+8, TFT_CYAN); TFT_print(info, CENTER, 4); _dispTime(); - _bg = TFT_BLACK; + tft_bg = TFT_BLACK; TFT_setclipwin(0,TFT_getfontheight()+9, tft_width-1, tft_height-TFT_getfontheight()-10); } @@ -297,28 +297,28 @@ static void update_header(char *hdr, char *ftr) TFT_saveClipWin(); TFT_resetclipwin(); - Font curr_font = cfont; - last_bg = _bg; - last_fg = _fg; - _fg = TFT_YELLOW; - _bg = (color_t){ 64, 64, 64 }; + Font curr_font = tft_cfont; + last_bg = tft_bg; + last_fg = tft_fg; + tft_fg = TFT_YELLOW; + tft_bg = (color_t){ 64, 64, 64 }; if (tft_width < 240) TFT_setFont(DEF_SMALL_FONT, NULL); else TFT_setFont(DEFAULT_FONT, NULL); if (hdr) { - TFT_fillRect(1, 1, tft_width-3, TFT_getfontheight()+6, _bg); + TFT_fillRect(1, 1, tft_width-3, TFT_getfontheight()+6, tft_bg); TFT_print(hdr, CENTER, 4); } if (ftr) { - TFT_fillRect(1, tft_height-TFT_getfontheight()-8, tft_width-3, TFT_getfontheight()+6, _bg); + TFT_fillRect(1, tft_height-TFT_getfontheight()-8, tft_width-3, TFT_getfontheight()+6, tft_bg); if (strlen(ftr) == 0) _dispTime(); else TFT_print(ftr, CENTER, tft_height-TFT_getfontheight()-5); } - cfont = curr_font; - _fg = last_fg; - _bg = last_bg; + tft_cfont = curr_font; + tft_fg = last_fg; + tft_bg = last_bg; TFT_restoreClipWin(); } @@ -340,7 +340,7 @@ static void test_times() { color_t *color_line = heap_caps_malloc((tft_width*3), MALLOC_CAP_DMA); color_t *gsline = NULL; - if (gray_scale) gsline = malloc(tft_width*3); + if (tft_gray_scale) gsline = malloc(tft_width*3); if (color_line) { float hue_inc = (float)((10.0 / (float)(tft_height-1) * 360.0)); for (int x=0; xtm_min, tm_info->tm_sec, ms); TFT_setFont(FONT_7SEG, NULL); if ((tft_width < 240) || (tft_height < 240)) set_7seg_font_atrib(8, 1, 1, TFT_DARKGREY); @@ -512,7 +512,7 @@ static void font_demo() TFT_print(tmp_buff, CENTER, y); n++; - _fg = TFT_GREEN; + tft_fg = TFT_GREEN; y += TFT_getfontheight() + 12; if ((tft_width < 240) || (tft_height < 240)) set_7seg_font_atrib(9, 1, 1, TFT_DARKGREY); else set_7seg_font_atrib(14, 3, 1, TFT_DARKGREY); @@ -521,7 +521,7 @@ static void font_demo() TFT_print(tmp_buff, CENTER, y); n++; - _fg = random_color(); + tft_fg = random_color(); y += TFT_getfontheight() + 8; set_7seg_font_atrib(6, 1, 1, TFT_DARKGREY); getFontCharacters((uint8_t *)tmp_buff); @@ -542,15 +542,15 @@ static void font_demo() if ((tft_width < 240) || (tft_height < 240)) TFT_setFont(DEF_SMALL_FONT, NULL); else TFT_setFont(UBUNTU16_FONT, NULL); - text_wrap = 1; + tft_text_wrap = 1; end_time = clock() + GDEMO_TIME; n = 0; while ((clock() < end_time) && (Wait(0))) { - _fg = random_color(); + tft_fg = random_color(); TFT_print("This text is printed inside the window.\nLong line can be wrapped to the next line.\nWelcome to ESP32", 0, 0); n++; } - text_wrap = 0; + tft_text_wrap = 0; sprintf(tmp_buff, "%d STRINGS", n); update_header(NULL, tmp_buff); Wait(-GDEMO_INFO_TIME); @@ -568,10 +568,10 @@ static void rect_demo() uint32_t end_time = clock() + GDEMO_TIME; n = 0; while ((clock() < end_time) && (Wait(0))) { - x = rand_interval(4, dispWin.x2-4); - y = rand_interval(4, dispWin.y2-2); - w = rand_interval(2, dispWin.x2-x); - h = rand_interval(2, dispWin.y2-y); + x = rand_interval(4, tft_dispWin.x2-4); + y = rand_interval(4, tft_dispWin.y2-2); + w = rand_interval(2, tft_dispWin.x2-x); + h = rand_interval(2, tft_dispWin.y2-y); TFT_drawRect(x,y,w,h,random_color()); n++; } @@ -584,10 +584,10 @@ static void rect_demo() end_time = clock() + GDEMO_TIME; n = 0; while ((clock() < end_time) && (Wait(0))) { - x = rand_interval(4, dispWin.x2-4); - y = rand_interval(4, dispWin.y2-2); - w = rand_interval(2, dispWin.x2-x); - h = rand_interval(2, dispWin.y2-y); + x = rand_interval(4, tft_dispWin.x2-4); + y = rand_interval(4, tft_dispWin.y2-2); + w = rand_interval(2, tft_dispWin.x2-x); + h = rand_interval(2, tft_dispWin.y2-y); TFT_fillRect(x,y,w,h,random_color()); TFT_drawRect(x,y,w,h,random_color()); n++; @@ -607,8 +607,8 @@ static void pixel_demo() uint32_t end_time = clock() + GDEMO_TIME; n = 0; while ((clock() < end_time) && (Wait(0))) { - x = rand_interval(0, dispWin.x2); - y = rand_interval(0, dispWin.y2); + x = rand_interval(0, tft_dispWin.x2); + y = rand_interval(0, tft_dispWin.y2); TFT_drawPixel(x,y,random_color(),1); n++; } @@ -627,10 +627,10 @@ static void line_demo() uint32_t end_time = clock() + GDEMO_TIME; n = 0; while ((clock() < end_time) && (Wait(0))) { - x1 = rand_interval(0, dispWin.x2); - y1 = rand_interval(0, dispWin.y2); - x2 = rand_interval(0, dispWin.x2); - y2 = rand_interval(0, dispWin.y2); + x1 = rand_interval(0, tft_dispWin.x2); + y1 = rand_interval(0, tft_dispWin.y2); + x2 = rand_interval(0, tft_dispWin.x2); + y2 = rand_interval(0, tft_dispWin.y2); TFT_drawLine(x1,y1,x2,y2,random_color()); n++; } @@ -646,8 +646,8 @@ static void aline_demo() disp_header("LINE BY ANGLE DEMO"); - x = (dispWin.x2 - dispWin.x1) / 2; - y = (dispWin.y2 - dispWin.y1) / 2; + x = (tft_dispWin.x2 - tft_dispWin.x1) / 2; + y = (tft_dispWin.y2 - tft_dispWin.y1) / 2; if (x < y) len = x - 8; else len = y -8; @@ -686,8 +686,8 @@ static void arc_demo() disp_header("ARC DEMO"); - x = (dispWin.x2 - dispWin.x1) / 2; - y = (dispWin.y2 - dispWin.y1) / 2; + x = (tft_dispWin.x2 - tft_dispWin.x1) / 2; + y = (tft_dispWin.y2 - tft_dispWin.y1) / 2; th = 6; uint32_t end_time = clock() + GDEMO_TIME; @@ -749,8 +749,8 @@ static void circle_demo() uint32_t end_time = clock() + GDEMO_TIME; n = 0; while ((clock() < end_time) && (Wait(0))) { - x = rand_interval(8, dispWin.x2-8); - y = rand_interval(8, dispWin.y2-8); + x = rand_interval(8, tft_dispWin.x2-8); + y = rand_interval(8, tft_dispWin.y2-8); if (x < y) r = rand_interval(2, x/2); else r = rand_interval(2, y/2); TFT_drawCircle(x,y,r,random_color()); @@ -765,8 +765,8 @@ static void circle_demo() end_time = clock() + GDEMO_TIME; n = 0; while ((clock() < end_time) && (Wait(0))) { - x = rand_interval(8, dispWin.x2-8); - y = rand_interval(8, dispWin.y2-8); + x = rand_interval(8, tft_dispWin.x2-8); + y = rand_interval(8, tft_dispWin.y2-8); if (x < y) r = rand_interval(2, x/2); else r = rand_interval(2, y/2); TFT_fillCircle(x,y,r,random_color()); @@ -788,8 +788,8 @@ static void ellipse_demo() uint32_t end_time = clock() + GDEMO_TIME; n = 0; while ((clock() < end_time) && (Wait(0))) { - x = rand_interval(8, dispWin.x2-8); - y = rand_interval(8, dispWin.y2-8); + x = rand_interval(8, tft_dispWin.x2-8); + y = rand_interval(8, tft_dispWin.y2-8); if (x < y) rx = rand_interval(2, x/4); else rx = rand_interval(2, y/4); if (x < y) ry = rand_interval(2, x/4); @@ -806,8 +806,8 @@ static void ellipse_demo() end_time = clock() + GDEMO_TIME; n = 0; while ((clock() < end_time) && (Wait(0))) { - x = rand_interval(8, dispWin.x2-8); - y = rand_interval(8, dispWin.y2-8); + x = rand_interval(8, tft_dispWin.x2-8); + y = rand_interval(8, tft_dispWin.y2-8); if (x < y) rx = rand_interval(2, x/4); else rx = rand_interval(2, y/4); if (x < y) ry = rand_interval(2, x/4); @@ -826,8 +826,8 @@ static void ellipse_demo() n = 0; int k = 1; while ((clock() < end_time) && (Wait(0))) { - x = rand_interval(8, dispWin.x2-8); - y = rand_interval(8, dispWin.y2-8); + x = rand_interval(8, tft_dispWin.x2-8); + y = rand_interval(8, tft_dispWin.y2-8); if (x < y) rx = rand_interval(2, x/4); else rx = rand_interval(2, y/4); if (x < y) ry = rand_interval(2, x/4); @@ -852,12 +852,12 @@ static void triangle_demo() uint32_t end_time = clock() + GDEMO_TIME; n = 0; while ((clock() < end_time) && (Wait(0))) { - x1 = rand_interval(4, dispWin.x2-4); - y1 = rand_interval(4, dispWin.y2-2); - x2 = rand_interval(4, dispWin.x2-4); - y2 = rand_interval(4, dispWin.y2-2); - x3 = rand_interval(4, dispWin.x2-4); - y3 = rand_interval(4, dispWin.y2-2); + x1 = rand_interval(4, tft_dispWin.x2-4); + y1 = rand_interval(4, tft_dispWin.y2-2); + x2 = rand_interval(4, tft_dispWin.x2-4); + y2 = rand_interval(4, tft_dispWin.y2-2); + x3 = rand_interval(4, tft_dispWin.x2-4); + y3 = rand_interval(4, tft_dispWin.y2-2); TFT_drawTriangle(x1,y1,x2,y2,x3,y3,random_color()); n++; } @@ -870,12 +870,12 @@ static void triangle_demo() end_time = clock() + GDEMO_TIME; n = 0; while ((clock() < end_time) && (Wait(0))) { - x1 = rand_interval(4, dispWin.x2-4); - y1 = rand_interval(4, dispWin.y2-2); - x2 = rand_interval(4, dispWin.x2-4); - y2 = rand_interval(4, dispWin.y2-2); - x3 = rand_interval(4, dispWin.x2-4); - y3 = rand_interval(4, dispWin.y2-2); + x1 = rand_interval(4, tft_dispWin.x2-4); + y1 = rand_interval(4, tft_dispWin.y2-2); + x2 = rand_interval(4, tft_dispWin.x2-4); + y2 = rand_interval(4, tft_dispWin.y2-2); + x3 = rand_interval(4, tft_dispWin.x2-4); + y3 = rand_interval(4, tft_dispWin.y2-2); TFT_fillTriangle(x1,y1,x2,y2,x3,y3,random_color()); TFT_drawTriangle(x1,y1,x2,y2,x3,y3,random_color()); n++; @@ -896,8 +896,8 @@ static void poly_demo() disp_header("POLYGON DEMO"); - x = (dispWin.x2 - dispWin.x1) / 2; - y = (dispWin.y2 - dispWin.y1) / 2; + x = (tft_dispWin.x2 - tft_dispWin.x1) / 2; + y = (tft_dispWin.y2 - tft_dispWin.y1) / 2; rot = 0; oldrot = 0; @@ -951,7 +951,7 @@ static void touch_demo() disp_header("TOUCH DEMO"); TFT_setFont(DEFAULT_FONT, NULL); - _fg = TFT_YELLOW; + tft_fg = TFT_YELLOW; TFT_print("Touch to draw", CENTER, 40); TFT_print("Touch footer to clear", CENTER, 60); @@ -960,11 +960,11 @@ static void touch_demo() while (1) { if (TFT_read_touch(&tx, &ty, 0)) { // Touched - if (((tx >= dispWin.x1) && (tx <= dispWin.x2)) && - ((ty >= dispWin.y1) && (ty <= dispWin.y2))) { + if (((tx >= tft_dispWin.x1) && (tx <= tft_dispWin.x2)) && + ((ty >= tft_dispWin.y1) && (ty <= tft_dispWin.y2))) { if ((doexit > 2) || ((abs(tx-ltx) < 5) && (abs(ty-lty) < 5))) { if (((abs(tx-ltx) > 0) || (abs(ty-lty) > 0))) { - TFT_fillCircle(tx-dispWin.x1, ty-dispWin.y1, 4,random_color()); + TFT_fillCircle(tx-tft_dispWin.x1, ty-tft_dispWin.y1, 4,random_color()); sprintf(tmp_buff, "%d,%d", tx, ty); update_header(NULL, tmp_buff); } @@ -973,7 +973,7 @@ static void touch_demo() } doexit = 0; } - else if (ty > (dispWin.y2+5)) TFT_fillWindow(TFT_BLACK); + else if (ty > (tft_dispWin.y2+5)) TFT_fillWindow(TFT_BLACK); else { doexit++; if (doexit == 2) update_header(NULL, "---"); @@ -995,13 +995,13 @@ static void touch_demo() //=============== void tft_demo() { - font_rotate = 0; - text_wrap = 0; - font_transparent = 0; - font_forceFixed = 0; + tft_font_rotate = 0; + tft_text_wrap = 0; + tft_font_transparent = 0; + tft_font_forceFixed = 0; TFT_resetclipwin(); - image_debug = 0; + tft_image_debug = 0; char dtype[16]; @@ -1030,22 +1030,22 @@ void tft_demo() { uint8_t disp_rot = PORTRAIT; _demo_pass = 0; - gray_scale = 0; + tft_gray_scale = 0; doprint = 1; TFT_setRotation(disp_rot); disp_header("ESP32 TFT DEMO"); TFT_setFont(COMIC24_FONT, NULL); int tempy = TFT_getfontheight() + 4; - _fg = TFT_ORANGE; - TFT_print("ESP32", CENTER, (dispWin.y2-dispWin.y1)/2 - tempy); + tft_fg = TFT_ORANGE; + TFT_print("ESP32", CENTER, (tft_dispWin.y2-tft_dispWin.y1)/2 - tempy); TFT_setFont(UBUNTU16_FONT, NULL); - _fg = TFT_CYAN; + tft_fg = TFT_CYAN; TFT_print("TFT Demo", CENTER, LASTY+tempy); tempy = TFT_getfontheight() + 4; TFT_setFont(DEFAULT_FONT, NULL); - _fg = TFT_GREEN; - sprintf(tmp_buff, "Read speed: %5.2f MHz", (float)max_rdclock/1000000.0); + tft_fg = TFT_GREEN; + sprintf(tmp_buff, "Read speed: %5.2f MHz", (float)tft_max_rdclock/1000000.0); TFT_print(tmp_buff, CENTER, LASTY+tempy); Wait(4000); @@ -1054,10 +1054,10 @@ void tft_demo() { if (run_gs_demo) { if (_demo_pass == 8) doprint = 0; // Change gray scale mode on every 2nd pass - gray_scale = _demo_pass & 1; + tft_gray_scale = _demo_pass & 1; // change display rotation if ((_demo_pass % 2) == 0) { - _bg = TFT_BLACK; + tft_bg = TFT_BLACK; TFT_setRotation(disp_rot); disp_rot++; disp_rot &= 3; @@ -1066,7 +1066,7 @@ void tft_demo() { else { if (_demo_pass == 4) doprint = 0; // change display rotation - _bg = TFT_BLACK; + tft_bg = TFT_BLACK; TFT_setRotation(disp_rot); disp_rot++; disp_rot &= 3; @@ -1078,7 +1078,7 @@ void tft_demo() { if (disp_rot == 3) sprintf(tmp_buff, "PORTRAIT FLIP"); if (disp_rot == 0) sprintf(tmp_buff, "LANDSCAPE FLIP"); printf("\r\n==========================================\r\nDisplay: %s: %s %d,%d %s\r\n\r\n", - dtype, tmp_buff, tft_width, tft_height, ((gray_scale) ? "Gray" : "Color")); + dtype, tmp_buff, tft_width, tft_height, ((tft_gray_scale) ? "Gray" : "Color")); } disp_header("Welcome to ESP32"); @@ -1241,7 +1241,7 @@ void app_main() // ==== Set maximum spi clock for display read ==== // operations, function 'find_rd_speed()' ==== // can be used after display initialization ==== - max_rdclock = 8000000; + tft_max_rdclock = 8000000; // =================================================== // ==================================================================== @@ -1310,7 +1310,7 @@ void app_main() ret=spi_lobo_bus_add_device(SPI_BUS, &buscfg, &devcfg, &spi); assert(ret==ESP_OK); printf("SPI: display device added to spi bus (%d)\r\n", SPI_BUS); - disp_spi = spi; + tft_disp_spi = spi; // ==== Test select/deselect ==== ret = spi_lobo_device_select(spi, 1); @@ -1328,7 +1328,7 @@ void app_main() ret=spi_lobo_bus_add_device(SPI_BUS, &buscfg, &tsdevcfg, &tsspi); assert(ret==ESP_OK); printf("SPI: touch screen device added to spi bus (%d)\r\n", SPI_BUS); - ts_spi = tsspi; + tft_ts_spi = tsspi; // ==== Test select/deselect ==== ret = spi_lobo_device_select(tsspi, 1); @@ -1353,8 +1353,8 @@ void app_main() #endif // ---- Detect maximum read speed ---- - max_rdclock = find_rd_speed(); - printf("SPI: Max rd speed = %u\r\n", max_rdclock); + tft_max_rdclock = find_rd_speed(); + printf("SPI: Max rd speed = %u\r\n", tft_max_rdclock); // ==== Set SPI clock used for display operations ==== spi_lobo_set_speed(spi, DEFAULT_SPI_CLOCK); @@ -1364,11 +1364,11 @@ void app_main() printf("Graphics demo started\r\n"); printf("---------------------\r\n"); - font_rotate = 0; - text_wrap = 0; - font_transparent = 0; - font_forceFixed = 0; - gray_scale = 0; + tft_font_rotate = 0; + tft_text_wrap = 0; + tft_font_transparent = 0; + tft_font_forceFixed = 0; + tft_gray_scale = 0; TFT_setGammaCurve(DEFAULT_GAMMA_CURVE); TFT_setRotation(PORTRAIT); TFT_setFont(DEFAULT_FONT, NULL); @@ -1391,18 +1391,18 @@ void app_main() // Is time set? If not, tm_year will be (1970 - 1900). if (tm_info->tm_year < (2016 - 1900)) { ESP_LOGI(tag, "Time is not set yet. Connecting to WiFi and getting time over NTP."); - _fg = TFT_CYAN; + tft_fg = TFT_CYAN; TFT_print("Time is not set yet", CENTER, CENTER); TFT_print("Connecting to WiFi", CENTER, LASTY+TFT_getfontheight()+2); TFT_print("Getting time over NTP", CENTER, LASTY+TFT_getfontheight()+2); - _fg = TFT_YELLOW; + tft_fg = TFT_YELLOW; TFT_print("Wait", CENTER, LASTY+TFT_getfontheight()+2); if (obtain_time()) { - _fg = TFT_GREEN; + tft_fg = TFT_GREEN; TFT_print("System time is set.", CENTER, LASTY); } else { - _fg = TFT_RED; + tft_fg = TFT_RED; TFT_print("ERROR.", CENTER, LASTY); } time(&time_now); @@ -1412,17 +1412,17 @@ void app_main() #endif disp_header("File system INIT"); - _fg = TFT_CYAN; + tft_fg = TFT_CYAN; TFT_print("Initializing SPIFFS...", CENTER, CENTER); // ==== Initialize the file system ==== printf("\r\n\n"); vfs_spiffs_register(); if (!spiffs_is_mounted) { - _fg = TFT_RED; + tft_fg = TFT_RED; TFT_print("SPIFFS not mounted !", CENTER, LASTY+TFT_getfontheight()+2); } else { - _fg = TFT_GREEN; + tft_fg = TFT_GREEN; TFT_print("SPIFFS Mounted.", CENTER, LASTY+TFT_getfontheight()+2); } Wait(-2000); From e145cba6d61d6879c6bea21b4853a54353084e54 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sun, 15 Sep 2019 16:14:23 -0400 Subject: [PATCH 14/39] Remaining fixups from open PRs. --- .gitignore | 1 + README.md | 4 ++-- components/spiffs/Kconfig | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index a0859ee..2e6be77 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ local/ *.elf *.map **/.DS_Store +mkspiffs/ \ No newline at end of file diff --git a/README.md b/README.md index 3ac2001..14b4fc2 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ * **getFontCharacters** Get all font's characters to buffer * **String write function**: * **TFT_print** Write text to display. - * Strings can be printed at **any angle**. Rotation of the displayed text depends on *font_ratate* variable (0~360) + * Strings can be printed at **any angle**. Rotation of the displayed text depends on *tft_font_rotate* variable (0~360) * if *font_transparent* variable is set to 1, no background pixels will be printed * If the text does not fit the screen/window width it will be clipped ( if *text_wrap=0* ), or continued on next line ( if *text_wrap=1* ) * Two special characters are allowed in strings: *\r* CR (0x0D), clears the display to EOL, *\n* LF (ox0A), continues to the new line, x=0 @@ -107,7 +107,7 @@ * **Global wariables** * **tft_orientation** current screen orientation - * **tft_font_ratate** current font rotate angle (0~395) + * **tft_font_rotate** current font rotate angle (0~395) * **tft_font_transparent** if not 0 draw fonts transparent * **tft_font_forceFixed** if not zero force drawing proportional fonts with fixed width * **tft_text_wrap** if not 0 wrap long text to the new line, else clip diff --git a/components/spiffs/Kconfig b/components/spiffs/Kconfig index e578fb7..7dfd5ac 100644 --- a/components/spiffs/Kconfig +++ b/components/spiffs/Kconfig @@ -2,7 +2,7 @@ menu "TFT SPIFFS" config SPIFFS_BASE_ADDR int "SPIFFS Base address" - range 1048576 33546240 + range 0x100000 0x1FFE000 default 0x180000 help Starting address of the SPIFFS area in ESP32 Flash @@ -14,7 +14,7 @@ config SPIFFS_SIZE config SPIFFS_LOG_BLOCK_SIZE int "SPIFFS Logical block size" - range 4098 65536 + range 4096 65536 default 8192 config SPIFFS_LOG_PAGE_SIZE From e098c86fbd949d8bb78daa2cf540c933936034e2 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sun, 15 Sep 2019 16:28:02 -0400 Subject: [PATCH 15/39] Range needs to be in base 10. --- components/spiffs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/spiffs/Kconfig b/components/spiffs/Kconfig index 7dfd5ac..5123800 100644 --- a/components/spiffs/Kconfig +++ b/components/spiffs/Kconfig @@ -2,7 +2,7 @@ menu "TFT SPIFFS" config SPIFFS_BASE_ADDR int "SPIFFS Base address" - range 0x100000 0x1FFE000 + range 1048576 33546240 default 0x180000 help Starting address of the SPIFFS area in ESP32 Flash From 3b1b357346c496535763b96639be29529f6d2d23 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sun, 15 Sep 2019 17:53:12 -0400 Subject: [PATCH 16/39] Fixup touchscreen config notes. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 14b4fc2..ea726e4 100644 --- a/README.md +++ b/README.md @@ -180,9 +180,11 @@ Configurations are available for: --- -**if you want to use the touch screen functions, set** `#define USE_TOUCH 1` in *tftspi.h* +#### Other config notes -Using *make menuconfig* **select tick rate 1000** ( → Component config → FreeRTOS → Tick rate (Hz) ) to get more accurate timings +Touch screen can be enabled in Components -> TFT Display as well. + +Using *idf.py menuconfig* **select tick rate 1000** ( → Component config → FreeRTOS → Tick rate (Hz) ) to get more accurate timings --- From a98a4484e2b66595255084f6d580252a819ad189 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Mon, 16 Sep 2019 07:45:07 -0400 Subject: [PATCH 17/39] Make X/Y case consistent with other globals. --- README.md | 10 +++++----- components/tft/tft.c | 44 ++++++++++++++++++++++---------------------- components/tft/tft.h | 4 ++-- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index ea726e4..d35ca3b 100644 --- a/README.md +++ b/README.md @@ -111,14 +111,14 @@ * **tft_font_transparent** if not 0 draw fonts transparent * **tft_font_forceFixed** if not zero force drawing proportional fonts with fixed width * **tft_text_wrap** if not 0 wrap long text to the new line, else clip - * **tft__fg** current foreground color for fonts - * **tft__bg** current background for non transparent fonts + * **tft_fg** current foreground color for fonts + * **tft_bg** current background for non transparent fonts * **tft_dispWin** current display clip window - * **tft__angleOffset** angle offset for arc, polygon and line by angle functions + * **tft_angleOffset** angle offset for arc, polygon and line by angle functions * **tft_image_debug** print debug messages during image decode if set to 1 * **tft_cfont** Currently used font structure - * **TFT_X** X position of the next character after TFT_print() function - * **TFT_Y** Y position of the next character after TFT_print() function + * **tft_x** X position of the next character after TFT_print() function + * **tft_y** Y position of the next character after TFT_print() function * **tft_tp_calx** touch screen X calibration constant * **tft_tp_caly** touch screen Y calibration constant * **tft_gray_scale** convert all colors to gray scale if set to 1 diff --git a/components/tft/tft.c b/components/tft/tft.c index bb16357..728731f 100644 --- a/components/tft/tft.c +++ b/components/tft/tft.c @@ -72,8 +72,8 @@ uint8_t tft_image_debug = 0; float tft_angleOffset = DEFAULT_ANGLE_OFFSET; -int TFT_X = 0; -int TFT_Y = 0; +int tft_x = 0; +int tft_y = 0; uint32_t tft_tp_calx = 7472920; uint32_t tft_tp_caly = 122224794; @@ -1750,8 +1750,8 @@ static void rotateChar(uint8_t c, int x, int y, int pos) { } disp_deselect(); // calculate x,y for the next char - TFT_X = (int)(x + ((pos+1) * tft_cfont.x_size * cos_radian)); - TFT_Y = (int)(y + ((pos+1) * tft_cfont.x_size * sin_radian)); + tft_x = (int)(x + ((pos+1) * tft_cfont.x_size * cos_radian)); + tft_y = (int)(y + ((pos+1) * tft_cfont.x_size * sin_radian)); } //---------------------- @@ -1931,10 +1931,10 @@ void TFT_print(char *st, int x, int y) { if ((x < LASTX) || (tft_font_rotate == 0)) TFT_OFFSET = 0; - if ((x >= LASTX) && (x < LASTY)) x = TFT_X + (x-LASTX); + if ((x >= LASTX) && (x < LASTY)) x = tft_x + (x-LASTX); else if (x > CENTER) x += tft_dispWin.x1; - if (y >= LASTY) y = TFT_Y + (y-LASTY); + if (y >= LASTY) y = tft_y + (y-LASTY); else if (y > CENTER) y += tft_dispWin.y1; // ** Get number of characters in string to print @@ -1958,8 +1958,8 @@ void TFT_print(char *st, int x, int y) { if (y < tft_dispWin.y1) y = tft_dispWin.y1; if ((x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return; - TFT_X = x; - TFT_Y = y; + tft_x = x; + tft_y = y; // ** Adjust y position tmph = tft_cfont.y_size; // font height @@ -1973,7 +1973,7 @@ void TFT_print(char *st, int x, int y) { } else TFT_OFFSET = 0; // fixed font; offset not needed - if ((TFT_Y + tmph - 1) > tft_dispWin.y2) return; + if ((tft_y + tmph - 1) > tft_dispWin.y2) return; int offset = TFT_OFFSET; @@ -1981,14 +1981,14 @@ void TFT_print(char *st, int x, int y) { ch = st[i]; // get string character if (ch == 0x0D) { // === '\r', erase to eol ==== - if ((!tft_font_transparent) && (tft_font_rotate==0)) _fillRect(TFT_X, TFT_Y, tft_dispWin.x2+1-TFT_X, tmph, tft_bg); + if ((!tft_font_transparent) && (tft_font_rotate==0)) _fillRect(tft_x, tft_y, tft_dispWin.x2+1-tft_x, tmph, tft_bg); } else if (ch == 0x0A) { // ==== '\n', new line ==== if (tft_cfont.bitmap == 1) { - TFT_Y += tmph + tft_font_line_space; - if (TFT_Y > (tft_dispWin.y2-tmph)) break; - TFT_X = tft_dispWin.x1; + tft_y += tmph + tft_font_line_space; + if (tft_y > (tft_dispWin.y2-tmph)) break; + tft_x = tft_dispWin.x1; } } @@ -2000,17 +2000,17 @@ void TFT_print(char *st, int x, int y) { } // check if character can be displayed in the current line - if ((TFT_X+tmpw) > (tft_dispWin.x2)) { + if ((tft_x+tmpw) > (tft_dispWin.x2)) { if (tft_text_wrap == 0) break; - TFT_Y += tmph + tft_font_line_space; - if (TFT_Y > (tft_dispWin.y2-tmph)) break; - TFT_X = tft_dispWin.x1; + tft_y += tmph + tft_font_line_space; + if (tft_y > (tft_dispWin.y2-tmph)) break; + tft_x = tft_dispWin.x1; } // Let's print the character if (tft_cfont.x_size == 0) { // == proportional font - if (tft_font_rotate == 0) TFT_X += printProportionalChar(TFT_X, TFT_Y) + 1; + if (tft_font_rotate == 0) tft_x += printProportionalChar(tft_x, tft_y) + 1; else { // rotated proportional font offset += rotatePropChar(x, y, offset); @@ -2022,15 +2022,15 @@ void TFT_print(char *st, int x, int y) { // == fixed font if ((ch < tft_cfont.offset) || ((ch-tft_cfont.offset) > tft_cfont.numchars)) ch = tft_cfont.offset; if (tft_font_rotate == 0) { - printChar(ch, TFT_X, TFT_Y); - TFT_X += tmpw; + printChar(ch, tft_x, tft_y); + tft_x += tmpw; } else rotateChar(ch, x, y, i); } else if (tft_cfont.bitmap == 2) { // == 7-segment font == - _draw7seg(TFT_X, TFT_Y, ch, tft_cfont.y_size, tft_cfont.x_size, tft_fg); - TFT_X += (tmpw + 2); + _draw7seg(tft_x, tft_y, ch, tft_cfont.y_size, tft_cfont.x_size, tft_fg); + tft_x += (tmpw + 2); } } } diff --git a/components/tft/tft.h b/components/tft/tft.h index 912b432..8249f20 100644 --- a/components/tft/tft.h +++ b/components/tft/tft.h @@ -53,8 +53,8 @@ extern uint8_t tft_image_debug; // print debug messages during image decode i extern Font tft_cfont; // Current font structure -extern int TFT_X; // X position of the next character after TFT_print() function -extern int TFT_Y; // Y position of the next character after TFT_print() function +extern int tft_x; // X position of the next character after TFT_print() function +extern int tft_y; // Y position of the next character after TFT_print() function extern uint32_t tft_tp_calx; // touch screen X calibration constant extern uint32_t tft_tp_caly; // touch screen Y calibration constant From 2017ad3051b24f5cfd9262614af2512d1bae5e32 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Mon, 16 Sep 2019 07:54:36 -0400 Subject: [PATCH 18/39] README tweaks. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d35ca3b..0d3ae2d 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,15 @@ --- -**This library must be built with the esp-idf release/v4.0 branch and xtensa toolchain** +**This library must be built with the esp-idf release/v4.0 branch.** +ESP-IDF 4.0 is currently in beta and instructions are found [here]( +https://docs.espressif.com/projects/esp-idf/en/v4.0-beta1/get-started/index.html) --- #### Features -* Full support for **ILI9341**, **ILI9488**, **ST7789V** and **ST7735** based TFT modules in 4-wire SPI mode. Support for other controllers will be added later +* Full support for **ILI9341**, **ILI9488**, **ST7789V** and **ST7735** based TFT modules in 4-wire SPI mode. * **18-bit (RGB)** color mode used * **SPI displays oriented SPI driver library** based on *spi-master* driver * Combined **DMA SPI** transfer mode and **direct SPI** for maximal speed From 855a17f9e3ee4db02c1e4da1693b325c48d21b31 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Mon, 16 Sep 2019 20:53:45 -0400 Subject: [PATCH 19/39] Expose RGB in config. Switch order around a bit. --- components/tft/Kconfig | 42 +++++++++++++++++++++++------------------ components/tft/tftspi.h | 9 +++++---- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/components/tft/Kconfig b/components/tft/Kconfig index 59e88c4..f716053 100644 --- a/components/tft/Kconfig +++ b/components/tft/Kconfig @@ -55,6 +55,30 @@ config TFT_DISPLAY_CONTROLLER_MODEL bool "ST7735B" endchoice +config TFT_DISPLAY_WIDTH + int "TFT display width in pixels." + default 240 + help + The smaller dimension (in portrait). + +config TFT_DISPLAY_HEIGHT + int "TFT display height in pixels." + default 320 + help + The smaller dimension (in portrait). + +config TFT_RGB_BGR + bool "RGB (Red Green Blue)" + default n + help + Is the display RGB rather than GBR? + +config TFT_ENABLE_TOUCH_SCREEN + bool "Enable Touch Screen" + default "n" + help + Enables touch screen features. + config TFT_INVERT_ROTATION1 bool "Invert rotation1." default n @@ -109,22 +133,4 @@ config TFT_PIN_NUM_BCKL help Optional. If not using a predefined display type, configure the blacklight pin here. -config TFT_ENABLE_TOUCH_SCREEN - bool "Enable Touch Screen" - default "n" - help - Enables touch screen features. - -config TFT_DISPLAY_WIDTH - int "TFT display width in pixels." - default 240 - help - The smaller dimension (in portrait). - -config TFT_DISPLAY_HEIGHT - int "TFT display height in pixels." - default 320 - help - The smaller dimension (in portrait). - endmenu diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index c8637ee..21b7f37 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -160,10 +160,6 @@ #define DISP_COLOR_BITS_24 0x66 //#define DISP_COLOR_BITS_16 0x55 // Do not use! -// ############################################# -// ### Set to 1 for some displays, ### -// for example the one on ESP-WROWER-KIT ### -// ############################################# #define TFT_INVERT_ROTATION 0 #if defined(CONFIG_TFT_INVERT_ROTATION1) @@ -177,7 +173,12 @@ // ### SET TO 0X08 FOR DISPLAYS WITH BGR MATRIX ### // ### For ESP-WROWER-KIT set to 0x00 ### // ################################################ + +#if defined(CONFIG_TFT_RGB_BGR) && CONFIG_TFT_RGB_BGR +#define TFT_RGB_BGR 0x00 +#else #define TFT_RGB_BGR 0x08 +#endif // ############################################################## // ### Define ESP32 SPI pins to which the display is attached ### From c94fd5a79787930f96479072531bb2fb64f78fdf Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Wed, 18 Sep 2019 20:27:13 -0400 Subject: [PATCH 20/39] use config to feed default dimensions --- components/tft/tftspi.c | 6 ++---- components/tft/tftspi.h | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/components/tft/tftspi.c b/components/tft/tftspi.c index 51ceba4..cf448ef 100644 --- a/components/tft/tftspi.c +++ b/components/tft/tftspi.c @@ -10,9 +10,7 @@ #include #include "tftspi.h" -#include "esp_system.h" #include "freertos/task.h" -#include "esp_heap_caps.h" #include "soc/spi_reg.h" #include "driver/gpio.h" @@ -26,8 +24,8 @@ uint8_t tft_gray_scale = 0; uint32_t tft_max_rdclock = 8000000; // Default display dimensions -int tft_width = CONFIG_TFT_DISPLAY_WIDTH; -int tft_height = CONFIG_TFT_DISPLAY_HEIGHT; +int tft_width = DEFAULT_TFT_DISPLAY_WIDTH; +int tft_height = DEFAULT_TFT_DISPLAY_HEIGHT; // Display type, DISP_TYPE_ILI9488 or DISP_TYPE_ILI9341 uint8_t tft_disp_type = DEFAULT_DISP_TYPE; diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index 21b7f37..4b10c0a 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -248,8 +248,8 @@ // ####################################################################### // Default display width (smaller dimension) and height (larger dimension) // ####################################################################### -#define DEFAULT_TFT_DISPLAY_WIDTH 240 -#define DEFAULT_TFT_DISPLAY_HEIGHT 320 +#define DEFAULT_TFT_DISPLAY_WIDTH CONFIG_TFT_DISPLAY_WIDTH +#define DEFAULT_TFT_DISPLAY_HEIGHT CONFIG_TFT_DISPLAY_HEIGHT // ####################################################################### #define DEFAULT_GAMMA_CURVE 0 From fe621a5d15f63f8706169c654ce07136980f6590 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Fri, 20 Sep 2019 07:41:01 -0400 Subject: [PATCH 21/39] Touch controller should be a choice. Remove config ifdefs. --- components/tft/Kconfig | 28 ++++++++++++++++------ components/tft/tftspi.h | 52 ++--------------------------------------- 2 files changed, 23 insertions(+), 57 deletions(-) diff --git a/components/tft/Kconfig b/components/tft/Kconfig index f716053..e77a610 100644 --- a/components/tft/Kconfig +++ b/components/tft/Kconfig @@ -73,11 +73,25 @@ config TFT_RGB_BGR help Is the display RGB rather than GBR? -config TFT_ENABLE_TOUCH_SCREEN - bool "Enable Touch Screen" - default "n" - help - Enables touch screen features. +config TFT_TOUCH_CONTROLLER + int + default 0 if TFT_TOUCH_CONTROLLER0 + default 1 if TFT_TOUCH_CONTROLLER1 + default 2 if TFT_TOUCH_CONTROLLER2 + + choice + prompt "Select touch controller." + default TFT_TOUCH_CONTROLLER0 + help + Select predefined display configuration + + config TFT_TOUCH_CONTROLLER0 + bool "None" + config TFT_TOUCH_CONTROLLER1 + bool "XPT2046" + config TFT_TOUCH_CONTROLLER2 + bool "STMPE610" + endchoice config TFT_INVERT_ROTATION1 bool "Invert rotation1." @@ -97,8 +111,8 @@ config TFT_PIN_NUM_MISO help If not using a predefined display type, configure the MISO pin here. -config TFT_PIN_NUM_SCK - int "GPIO for SCK (Serial Clock)" +config TFT_PIN_NUM_CLK + int "GPIO for CLK (SCK / Serial Clock)" default 18 help If not using a predefined display type, configure the SCK pin here. diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index 4b10c0a..e205b67 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -161,12 +161,7 @@ //#define DISP_COLOR_BITS_16 0x55 // Do not use! #define TFT_INVERT_ROTATION 0 - -#if defined(CONFIG_TFT_INVERT_ROTATION1) #define TFT_INVERT_ROTATION1 CONFIG_TFT_INVERT_ROTATION1 -#else -#define TFT_INVERT_ROTATION1 0 -#endif // ################################################ // ### SET TO 0X00 FOR DISPLAYS WITH RGB MATRIX ### @@ -174,7 +169,7 @@ // ### For ESP-WROWER-KIT set to 0x00 ### // ################################################ -#if defined(CONFIG_TFT_RGB_BGR) && CONFIG_TFT_RGB_BGR +#if CONFIG_TFT_RGB_BGR #define TFT_RGB_BGR 0x00 #else #define TFT_RGB_BGR 0x08 @@ -187,63 +182,20 @@ // The pins configured here are the native spi pins for HSPI interface // Any other valid pin combination can be used -#if defined(CONFIG_TFT_PIN_NUM_MISO) #define PIN_NUM_MISO CONFIG_TFT_PIN_NUM_MISO -#else -#define PIN_NUM_MISO 19 -#endif - -#if defined(CONFIG_TFT_PIN_NUM_MOSI) #define PIN_NUM_MOSI CONFIG_TFT_PIN_NUM_MOSI -#else -#define PIN_NUM_MOSI 23 -#endif - -#if defined(CONFIG_TFT_PIN_NUM_CLK) #define PIN_NUM_CLK CONFIG_TFT_PIN_NUM_CLK -#else -#define PIN_NUM_CLK 18 -#endif - -#if defined(CONFIG_TFT_PIN_NUM_CS) #define PIN_NUM_CS CONFIG_TFT_PIN_NUM_CS -#else -#define PIN_NUM_CS 5 -#endif - -#if defined(CONFIG_TFT_PIN_NUM_DC) #define PIN_NUM_DC CONFIG_TFT_PIN_NUM_DC -#else -#define PIN_NUM_DC 26 -#endif - -#if defined(CONFIG_TFT_PIN_NUM_TCS) #define PIN_NUM_TCS CONFIG_TFT_PIN_NUM_TCS -#else -#define PIN_NUM_TCS 25 -#endif - -#if defined(CONFIG_TFT_PIN_NUM_RST) #define PIN_NUM_RST CONFIG_TFT_PIN_NUM_RST -#else -#define PIN_NUM_RST 0 -#endif - -#if defined(CONFIG_TFT_PIN_NUM_BCKL) #define PIN_NUM_BCKL CONFIG_TFT_PIN_NUM_BCKL -#else -#define PIN_NUM_BCKL 0 -#endif #define PIN_BCKL_ON 0 // GPIO value for backlight ON #define PIN_BCKL_OFF 1 // GPIO value for backlight OFF // -------------------------------------------------------------- -#if defined(CONFIG_TFT_ENABLE_TOUCH_SCREEN) -#define USE_TOUCH CONFIG_TFT_ENABLE_TOUCH_SCREEN -#else -#define USE_TOUCH TOUCH_TYPE_NONE -#endif +#define USE_TOUCH CONFIG_TFT_TOUCH_CONTROLLER // ####################################################################### // Default display width (smaller dimension) and height (larger dimension) From c34976156d614a46a98534549fc9dcc21d74c6cb Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Sun, 22 Sep 2019 10:24:55 -0400 Subject: [PATCH 22/39] Fix touch config; hide unused menuconfigs --- components/spidriver/spi_master_lobo.c | 16 ---------------- components/tft/Kconfig | 4 ++++ components/tft/tft.c | 4 ++-- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/components/spidriver/spi_master_lobo.c b/components/spidriver/spi_master_lobo.c index a6bb3f0..9890bb0 100644 --- a/components/spidriver/spi_master_lobo.c +++ b/components/spidriver/spi_master_lobo.c @@ -60,28 +60,12 @@ Main driver's function is 'spi_lobo_transfer_data()' */ #include "freertos/FreeRTOS.h" #include -#include -#include "soc/gpio_sig_map.h" #include "soc/spi_reg.h" #include "soc/dport_reg.h" -#include "soc/rtc_cntl_reg.h" -#include "rom/ets_sys.h" -#include "esp_types.h" -#include "esp_attr.h" #include "esp_log.h" -#include "esp_err.h" #include "freertos/semphr.h" -#include "freertos/xtensa_api.h" -#include "freertos/task.h" -#include "freertos/ringbuf.h" -#include "soc/soc.h" -#include "soc/dport_reg.h" -#include "soc/uart_struct.h" #include "driver/uart.h" #include "driver/gpio.h" -#include "driver/periph_ctrl.h" -#include "esp_heap_caps.h" -#include "driver/periph_ctrl.h" #include "spi_master_lobo.h" diff --git a/components/tft/Kconfig b/components/tft/Kconfig index e77a610..a76d2e3 100644 --- a/components/tft/Kconfig +++ b/components/tft/Kconfig @@ -26,6 +26,8 @@ config TFT_PREDEFINED_DISPLAY_TYPE bool "M5Stack TFT Display" endchoice +if TFT_PREDEFINED_DISPLAY_TYPE0 + config TFT_DISPLAY_CONTROLLER_MODEL int default 0 if TFT_DISPLAY_CONTROLLER_ILI9341 @@ -147,4 +149,6 @@ config TFT_PIN_NUM_BCKL help Optional. If not using a predefined display type, configure the blacklight pin here. +endif + endmenu diff --git a/components/tft/tft.c b/components/tft/tft.c index 728731f..827890f 100644 --- a/components/tft/tft.c +++ b/components/tft/tft.c @@ -81,8 +81,8 @@ uint32_t tft_tp_caly = 122224794; dispWin_t tft_dispWin = { .x1 = 0, .y1 = 0, - .x2 = CONFIG_TFT_DISPLAY_WIDTH, - .y2 = CONFIG_TFT_DISPLAY_HEIGHT, + .x2 = DEFAULT_TFT_DISPLAY_WIDTH, + .y2 = DEFAULT_TFT_DISPLAY_HEIGHT, }; Font tft_cfont = { From cf347104a635a876fe9614ff465d5b9dec10f255 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Tue, 1 Oct 2019 20:52:07 -0400 Subject: [PATCH 23/39] Fix invertDisplay. resolves #1 --- components/tft/tft.c | 14 ++++++++++---- main/tft_demo.c | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/components/tft/tft.c b/components/tft/tft.c index 827890f..f97ffa0 100644 --- a/components/tft/tft.c +++ b/components/tft/tft.c @@ -2068,16 +2068,22 @@ void TFT_setRotation(uint8_t rot) { // Input: i 0 to disable inversion; non-zero to enable inversion //========================================== void TFT_invertDisplay(const uint8_t mode) { - if ( mode == INVERT_ON ) disp_spi_transfer_cmd(TFT_INVONN); - else disp_spi_transfer_cmd(TFT_INVOFF); + if (disp_select() == ESP_OK) { + if ( mode == INVERT_ON ) disp_spi_transfer_cmd(TFT_INVONN); + else disp_spi_transfer_cmd(TFT_INVOFF); + disp_deselect(); + } } // Select gamma curve // Input: gamma = 0~3 //================================== void TFT_setGammaCurve(uint8_t gm) { - uint8_t gamma_curve = 1 << (gm & 0x03); - disp_spi_transfer_cmd_data(TFT_CMD_GAMMASET, &gamma_curve, 1); + uint8_t gamma_curve = (uint8_t)1 << (gm & (uint8_t)0x03); + if (disp_select() == ESP_OK) { + disp_spi_transfer_cmd_data(TFT_CMD_GAMMASET, &gamma_curve, 1); + disp_deselect(); + } } //=========================================================== diff --git a/main/tft_demo.c b/main/tft_demo.c index 6663a2f..f0f5513 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -1425,6 +1425,7 @@ void app_main() tft_fg = TFT_GREEN; TFT_print("SPIFFS Mounted.", CENTER, LASTY+TFT_getfontheight()+2); } + Wait(-2000); //========= From dd5ecde923f991e10195fc308239a0501558f874 Mon Sep 17 00:00:00 2001 From: frhun <28605587+frhun@users.noreply.github.com> Date: Sat, 21 Mar 2020 17:56:22 +0100 Subject: [PATCH 24/39] Fix IDF 4.x compatibility Fixes: https://github.com/jeremyjh/ESP32_TFT_library/issues/2 --- components/spidriver/spi_master_lobo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/spidriver/spi_master_lobo.c b/components/spidriver/spi_master_lobo.c index 9890bb0..6aa7725 100644 --- a/components/spidriver/spi_master_lobo.c +++ b/components/spidriver/spi_master_lobo.c @@ -67,7 +67,7 @@ Main driver's function is 'spi_lobo_transfer_data()' #include "driver/uart.h" #include "driver/gpio.h" #include "spi_master_lobo.h" - +#include "driver/periph_ctrl.h" static spi_lobo_host_t *spihost[3] = {NULL}; From fd47bd935ec1b027520af45a3892a36bad28b7f8 Mon Sep 17 00:00:00 2001 From: frhun <28605587+frhun@users.noreply.github.com> Date: Sat, 21 Mar 2020 17:57:10 +0100 Subject: [PATCH 25/39] Add TTGO T-Display Adds the TTGO T-Display to the Kconfig system. Changes taken from: https://github.com/Xinyuan-LilyGO/TTGO-T-Display/issues/8 --- components/tft/Kconfig | 3 +++ components/tft/tftspi.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/components/tft/Kconfig b/components/tft/Kconfig index a76d2e3..cdea9bd 100644 --- a/components/tft/Kconfig +++ b/components/tft/Kconfig @@ -7,6 +7,7 @@ config TFT_PREDEFINED_DISPLAY_TYPE default 2 if TFT_PREDEFINED_DISPLAY_TYPE2 default 3 if TFT_PREDEFINED_DISPLAY_TYPE3 default 4 if TFT_PREDEFINED_DISPLAY_TYPE4 + default 5 if TFT_PREDEFINED_DISPLAY_TYPE5 choice prompt "Select predefined display configuration" @@ -24,6 +25,8 @@ config TFT_PREDEFINED_DISPLAY_TYPE bool "Adafruit TFT Feather Display" config TFT_PREDEFINED_DISPLAY_TYPE3 bool "M5Stack TFT Display" + config TFT_PREDEFINED_DISPLAY_TYPE5 + bool "TTGO T-DISPLAY (ST7789V)" endchoice if TFT_PREDEFINED_DISPLAY_TYPE0 diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index e205b67..bbdbd81 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -35,6 +35,8 @@ #define DISP_TYPE_ST7735R 4 #define DISP_TYPE_ST7735B 5 + + #if CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 1 // ** Set the correct configuration for ESP-WROVER-KIT v3 @@ -152,6 +154,32 @@ #define PIN_BCKL_ON 0 // GPIO value for backlight ON #define PIN_BCKL_OFF 1 // GPIO value for backlight OFF // -------------------------------------------------------- +#elif CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 5 +//CONFIG FOR TTGO T-DISPLAY +#define DEFAULT_DISP_TYPE DISP_TYPE_ST7789V +#define DEFAULT_TFT_DISPLAY_WIDTH 240 +#define DEFAULT_TFT_DISPLAY_HEIGHT 320 +#define DISP_COLOR_BITS_24 0x66 +#define DEFAULT_GAMMA_CURVE 0 +#define DEFAULT_SPI_CLOCK 20000000 +#define TFT_INVERT_ROTATION 0 +#define TFT_INVERT_ROTATION1 1 +#define TFT_RGB_BGR 0x00 + +#define USE_TOUCH TOUCH_TYPE_NONE + +#define PIN_NUM_MISO 0 // SPI MISO +#define PIN_NUM_MOSI 19 // SPI MOSI +#define PIN_NUM_CLK 18 // SPI CLOCK pin +#define PIN_NUM_CS 5 // Display CS pin +#define PIN_NUM_DC 16 // Display command/data pin +#define PIN_NUM_TCS 0 // Touch screen CS pin + +#define PIN_NUM_RST 23 // GPIO used for RESET control +#define PIN_NUM_BCKL 4 // GPIO used for backlight control +#define PIN_BCKL_ON 1 // GPIO value for backlight ON +#define PIN_BCKL_OFF 0 // GPIO value for backlight OFF +//END TTGO T_DISPLAY #else From 76208ed35a740e123c8de3a06c4867f9a2067bec Mon Sep 17 00:00:00 2001 From: frhun <28605587+frhun@users.noreply.github.com> Date: Sun, 22 Mar 2020 20:04:05 +0100 Subject: [PATCH 26/39] Adding default inversion option This adds a define, that the program can use, if the display needs to be inverted by default. --- components/tft/tftspi.h | 2 ++ main/tft_demo.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index bbdbd81..7f8b3fc 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -165,6 +165,8 @@ #define TFT_INVERT_ROTATION 0 #define TFT_INVERT_ROTATION1 1 #define TFT_RGB_BGR 0x00 +//To be used by user application for initialization +#define TFT_ALLWAYS_INVERTED #define USE_TOUCH TOUCH_TYPE_NONE diff --git a/main/tft_demo.c b/main/tft_demo.c index f0f5513..5616fca 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -1344,6 +1344,9 @@ void app_main() printf("SPI: display init...\r\n"); TFT_display_init(); +#ifdef TFT_ALLWAYS_INVERTED + TFT_invertDisplay(1); +#endif printf("OK\r\n"); #if USE_TOUCH == TOUCH_TYPE_STMPE610 stmpe610_Init(); From 2ba5509d4e8cc2243ccd121417c28f73d8a2eba9 Mon Sep 17 00:00:00 2001 From: frhun <28605587+frhun@users.noreply.github.com> Date: Sun, 22 Mar 2020 20:08:48 +0100 Subject: [PATCH 27/39] Adding coordinate offsets This adds offsets for displays that put 0;0 outside the actual screen area. The additional calculations completely dissapear when not defining any offsets, thus eliminating any potential performance hits for displays that do not need offsets. --- components/tft/tft.c | 38 +++++++++++++++++++------------------- components/tft/tftspi.c | 2 +- components/tft/tftspi.h | 20 ++++++++++++++++++-- main/tft_demo.c | 2 +- 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/components/tft/tft.c b/components/tft/tft.c index f97ffa0..e4498d5 100644 --- a/components/tft/tft.c +++ b/components/tft/tft.c @@ -79,10 +79,10 @@ uint32_t tft_tp_calx = 7472920; uint32_t tft_tp_caly = 122224794; dispWin_t tft_dispWin = { - .x1 = 0, - .y1 = 0, - .x2 = DEFAULT_TFT_DISPLAY_WIDTH, - .y2 = DEFAULT_TFT_DISPLAY_HEIGHT, + .x1 = TFT_STATIC_WIDTH_OFFSET, + .y1 = TFT_STATIC_HEIGHT_OFFSET, + .x2 = DEFAULT_TFT_DISPLAY_WIDTH + TFT_STATIC_WIDTH_OFFSET, + .y2 = DEFAULT_TFT_DISPLAY_HEIGHT + TFT_STATIC_HEIGHT_OFFSET, }; Font tft_cfont = { @@ -296,7 +296,7 @@ void TFT_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) { //================================== void TFT_fillScreen(color_t color) { - TFT_pushColorRep(0, 0, tft_width-1, tft_height-1, color, (uint32_t)(tft_height*tft_width)); + TFT_pushColorRep(TFT_STATIC_X_OFFSET, TFT_STATIC_Y_OFFSET, tft_width + TFT_STATIC_X_OFFSET -1, tft_height + TFT_STATIC_Y_OFFSET -1, color, (uint32_t)(tft_height*tft_width)); } //================================== @@ -2056,10 +2056,10 @@ void TFT_setRotation(uint8_t rot) { _tft_setRotation(rot); } - tft_dispWin.x1 = 0; - tft_dispWin.y1 = 0; - tft_dispWin.x2 = tft_width-1; - tft_dispWin.y2 = tft_height-1; + tft_dispWin.x1 = TFT_STATIC_X_OFFSET; + tft_dispWin.y1 = TFT_STATIC_Y_OFFSET; + tft_dispWin.x2 = tft_width + TFT_STATIC_X_OFFSET -1; + tft_dispWin.y2 = tft_height + TFT_STATIC_Y_OFFSET -1; TFT_fillScreen(tft_bg); } @@ -2157,13 +2157,13 @@ color_t HSBtoRGB(float _hue, float _sat, float _brightness) { //===================================================================== void TFT_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { - tft_dispWin.x1 = x1; - tft_dispWin.y1 = y1; - tft_dispWin.x2 = x2; - tft_dispWin.y2 = y2; + tft_dispWin.x1 = x1 + TFT_STATIC_X_OFFSET; + tft_dispWin.y1 = y1 + TFT_STATIC_Y_OFFSET; + tft_dispWin.x2 = x2 + TFT_STATIC_X_OFFSET; + tft_dispWin.y2 = y2 + TFT_STATIC_Y_OFFSET; - if (tft_dispWin.x2 >= tft_width) tft_dispWin.x2 = tft_width-1; - if (tft_dispWin.y2 >= tft_height) tft_dispWin.y2 = tft_height-1; + if (tft_dispWin.x2 >= tft_width + TFT_STATIC_X_OFFSET) tft_dispWin.x2 = tft_width + TFT_STATIC_X_OFFSET -1; + if (tft_dispWin.y2 >= tft_height + TFT_STATIC_Y_OFFSET) tft_dispWin.y2 = tft_height + TFT_STATIC_Y_OFFSET -1; if (tft_dispWin.x1 > tft_dispWin.x2) tft_dispWin.x1 = tft_dispWin.x2; if (tft_dispWin.y1 > tft_dispWin.y2) tft_dispWin.y1 = tft_dispWin.y2; } @@ -2171,10 +2171,10 @@ void TFT_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) //===================== void TFT_resetclipwin() { - tft_dispWin.x2 = tft_width-1; - tft_dispWin.y2 = tft_height-1; - tft_dispWin.x1 = 0; - tft_dispWin.y1 = 0; + tft_dispWin.x2 = tft_width + TFT_STATIC_X_OFFSET -1; + tft_dispWin.y2 = tft_height + TFT_STATIC_Y_OFFSET -1; + tft_dispWin.x1 = TFT_STATIC_X_OFFSET; + tft_dispWin.y1 = TFT_STATIC_Y_OFFSET; } //========================================================================== diff --git a/components/tft/tftspi.c b/components/tft/tftspi.c index cf448ef..d9e6e2b 100644 --- a/components/tft/tftspi.c +++ b/components/tft/tftspi.c @@ -936,7 +936,7 @@ void TFT_display_init() // Clear screen _tft_setRotation(PORTRAIT); - TFT_pushColorRep(0, 0, tft_width-1, tft_height-1, (color_t){0,0,0}, (uint32_t)(tft_height*tft_width)); + TFT_pushColorRep(TFT_STATIC_WIDTH_OFFSET, TFT_STATIC_HEIGHT_OFFSET, tft_width + TFT_STATIC_WIDTH_OFFSET -1, tft_height + TFT_STATIC_HEIGHT_OFFSET -1, (color_t){0,0,0}, (uint32_t)(tft_height*tft_width)); ///Enable backlight #if PIN_NUM_BCKL diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index 7f8b3fc..28ba2c8 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -157,8 +157,13 @@ #elif CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 5 //CONFIG FOR TTGO T-DISPLAY #define DEFAULT_DISP_TYPE DISP_TYPE_ST7789V -#define DEFAULT_TFT_DISPLAY_WIDTH 240 -#define DEFAULT_TFT_DISPLAY_HEIGHT 320 +#define DEFAULT_TFT_DISPLAY_WIDTH 135 +#define DEFAULT_TFT_DISPLAY_HEIGHT 240 + +//Need to be defined together so they can be swapped for x;y when rotating +#define TFT_STATIC_WIDTH_OFFSET 53 +#define TFT_STATIC_HEIGHT_OFFSET 40 + #define DISP_COLOR_BITS_24 0x66 #define DEFAULT_GAMMA_CURVE 0 #define DEFAULT_SPI_CLOCK 20000000 @@ -246,6 +251,17 @@ #endif // CONFIG_PREDEFINED_DISPLAY_TYPE +// Define offset generation, or ignore offsets if none are needed +#ifdef TFT_STATIC_WIDTH_OFFSET +#define TFT_STATIC_X_OFFSET (tft_orientation & 1 ? TFT_STATIC_HEIGHT_OFFSET : TFT_STATIC_WIDTH_OFFSET) +#define TFT_STATIC_Y_OFFSET (tft_orientation & 1 ? TFT_STATIC_WIDTH_OFFSET : TFT_STATIC_HEIGHT_OFFSET) +#else +#define TFT_STATIC_WIDTH_OFFSET 0 +#define TFT_STATIC_X_OFFSET 0 +#define TFT_STATIC_HEIGHT_OFFSET 0 +#define TFT_STATIC_Y_OFFSET 0 +#endif + // ############################################################## // #### Global variables #### diff --git a/main/tft_demo.c b/main/tft_demo.c index 5616fca..3b300c2 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -351,7 +351,7 @@ static void test_times() { tstart = clock(); for (int n=0; n<1000; n++) { if (gsline) memcpy(color_line, gsline, tft_width*3); - send_data(0, 40+(n&63), tft_dispWin.x2-tft_dispWin.x1, 40+(n&63), (uint32_t)(tft_dispWin.x2-tft_dispWin.x1+1), color_line); + send_data(0 + TFT_STATIC_X_OFFSET, 40+(n&63) + TFT_STATIC_Y_OFFSET, tft_dispWin.x2-tft_dispWin.x1 + TFT_STATIC_X_OFFSET , 40+(n&63) + TFT_STATIC_Y_OFFSET, (uint32_t)(tft_dispWin.x2-tft_dispWin.x1+1), color_line); wait_trans_finish(1); } t2 = clock() - tstart; From 8d7dc5a234988ea99bfded8b7335c1f5ab42853f Mon Sep 17 00:00:00 2001 From: frhun <28605587+frhun@users.noreply.github.com> Date: Wed, 25 Mar 2020 20:23:57 +0100 Subject: [PATCH 28/39] More direct color inversion option naming --- components/tft/tftspi.h | 2 +- main/tft_demo.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index 28ba2c8..fb689b0 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -171,7 +171,7 @@ #define TFT_INVERT_ROTATION1 1 #define TFT_RGB_BGR 0x00 //To be used by user application for initialization -#define TFT_ALLWAYS_INVERTED +#define TFT_START_COLORS_INVERTED #define USE_TOUCH TOUCH_TYPE_NONE diff --git a/main/tft_demo.c b/main/tft_demo.c index 3b300c2..d0fe482 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -1344,7 +1344,7 @@ void app_main() printf("SPI: display init...\r\n"); TFT_display_init(); -#ifdef TFT_ALLWAYS_INVERTED +#ifdef TFT_START_COLORS_INVERTED TFT_invertDisplay(1); #endif printf("OK\r\n"); From 17fc22649d294f0877d6c174c88f07e04cbedc0a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 24 Jun 2020 10:30:11 +1000 Subject: [PATCH 29/39] cmake: Declare nvs_flash as a dependency, instead of pushing absolute include path For more information about how component requirements are declared, see https://docs.espressif.com/projects/esp-idf/en/v4.0.1/api-guides/build-system.html#component-requirements Closes #8 --- main/CMakeLists.txt | 5 ++--- main/tft_demo.c | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index d5e3002..e647895 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,10 +1,9 @@ set(SOURCES tft_demo.c) idf_component_register( SRCS ${SOURCES} - INCLUDE_DIRS - ${CMAKE_CURRENT_LIST_DIR} - $ENV{IDF_PATH}/components REQUIRES tft spiffs + PRIV_REQUIRES + nvs_flash ) diff --git a/main/tft_demo.c b/main/tft_demo.c index d0fe482..3692f04 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -24,7 +24,7 @@ #include "freertos/event_groups.h" #include "esp_sntp.h" #include "esp_log.h" -#include "nvs_flash/include/nvs_flash.h" +#include "nvs_flash.h" #endif From 801643712291e3d380ca14c1d2cae60bc43949c2 Mon Sep 17 00:00:00 2001 From: James Hawtin Date: Tue, 30 Jun 2020 17:26:47 +0100 Subject: [PATCH 30/39] Adding support for the TTGO T-WRISTBAND support --- components/tft/Kconfig | 5 +++++ components/tft/tftspi.h | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/components/tft/Kconfig b/components/tft/Kconfig index cdea9bd..78ba419 100644 --- a/components/tft/Kconfig +++ b/components/tft/Kconfig @@ -8,6 +8,8 @@ config TFT_PREDEFINED_DISPLAY_TYPE default 3 if TFT_PREDEFINED_DISPLAY_TYPE3 default 4 if TFT_PREDEFINED_DISPLAY_TYPE4 default 5 if TFT_PREDEFINED_DISPLAY_TYPE5 + default 6 if TFT_PREDEFINED_DISPLAY_TYPE6 + choice prompt "Select predefined display configuration" @@ -27,6 +29,9 @@ config TFT_PREDEFINED_DISPLAY_TYPE bool "M5Stack TFT Display" config TFT_PREDEFINED_DISPLAY_TYPE5 bool "TTGO T-DISPLAY (ST7789V)" + config TFT_PREDEFINED_DISPLAY_TYPE6 + bool "TTGO T-WRISTBAND (ST7735)" + endchoice if TFT_PREDEFINED_DISPLAY_TYPE0 diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index fb689b0..282e3f9 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -188,6 +188,39 @@ #define PIN_BCKL_OFF 0 // GPIO value for backlight OFF //END TTGO T_DISPLAY +#elif CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 6 +//CONFIG FOR TTGO T-WRISTBAND +#define DEFAULT_DISP_TYPE DISP_TYPE_ST7735R +#define DEFAULT_TFT_DISPLAY_WIDTH 80 +#define DEFAULT_TFT_DISPLAY_HEIGHT 160 + +//Need to be defined together so they can be swapped for x;y when rotating +#define TFT_STATIC_WIDTH_OFFSET 26 +#define TFT_STATIC_HEIGHT_OFFSET 1 + +#define DISP_COLOR_BITS_24 0x66 +#define DEFAULT_GAMMA_CURVE 0 +#define DEFAULT_SPI_CLOCK 27000000 +#define TFT_INVERT_ROTATION 0 +#define TFT_INVERT_ROTATION1 1 +#define TFT_RGB_BGR 0x00 +//To be used by user application for initialization +#define TFT_START_COLORS_INVERTED + +#define USE_TOUCH TOUCH_TYPE_NONE + +#define PIN_NUM_MISO 0 // SPI MISO +#define PIN_NUM_MOSI 19 // SPI MOSI +#define PIN_NUM_CLK 18 // SPI CLOCK pin +#define PIN_NUM_CS 5 // Display CS pin +#define PIN_NUM_DC 23 // Display command/data pin +#define PIN_NUM_TCS 0 // Touch screen CS pin + +#define PIN_NUM_RST 26 // GPIO used for RESET control +#define PIN_NUM_BCKL 27 // GPIO used for backlight control +#define PIN_BCKL_ON 1 // GPIO value for backlight ON +#define PIN_BCKL_OFF 0 // GPIO value for backlight OFF +//END TTGO T_DISPLAY #else // Configuration for other boards, set the correct values for the display used From f4e07be511d1bf1c54ae657d3248c352de6b93b1 Mon Sep 17 00:00:00 2001 From: Marc Lepage Date: Wed, 8 Jul 2020 11:53:06 -0400 Subject: [PATCH 31/39] Fix const correctness in TFT functions Change string parameters to be const char * (instead of char*) and fix up related spacing and formatting. --- components/tft/tft.c | 30 +++++++++++++++--------------- components/tft/tft.h | 25 +++++++++++++------------ 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/components/tft/tft.c b/components/tft/tft.c index e4498d5..0214c41 100644 --- a/components/tft/tft.c +++ b/components/tft/tft.c @@ -1011,8 +1011,8 @@ void TFT_drawStar(int cx, int cy, int diameter, color_t color, bool fill, float // ================ Font and string functions ================================== -//-------------------------------------------------------- -static int load_file_font(const char * fontfile, int info) +//------------------------------------------------------- +static int load_file_font(const char *fontfile, int info) { int err = 0; char err_msg[256] = {'\0'}; @@ -1142,8 +1142,8 @@ static int load_file_font(const char * fontfile, int info) return err; } -//------------------------------------------------ -int compile_font_file(char *fontfile, uint8_t dbg) +//------------------------------------------------------ +int compile_font_file(const char *fontfile, uint8_t dbg) { int err = 0; char err_msg[128] = {'\0'}; @@ -1768,8 +1768,8 @@ static int _7seg_height() // Returns the string width in pixels. // Useful for positions strings on the screen. -//=============================== -int TFT_getStringWidth(char* str) +//===================================== +int TFT_getStringWidth(const char *str) { int strWidth = 0; @@ -1777,7 +1777,7 @@ int TFT_getStringWidth(char* str) else if (tft_cfont.x_size != 0) strWidth = strlen(str) * tft_cfont.x_size; // fixed width font else { // calculate the width of the string of proportional characters - char* tempStrptr = str; + char *tempStrptr = str; while (*tempStrptr != 0) { if (getCharPtr(*tempStrptr++)) { strWidth += (((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta) + 1); @@ -1788,8 +1788,8 @@ int TFT_getStringWidth(char* str) return strWidth; } -//=============================================== -void TFT_clearStringRect(int x, int y, char *str) +//===================================================== +void TFT_clearStringRect(int x, int y, const char *str) { int w = TFT_getStringWidth(str); int h = TFT_getfontheight(); @@ -1919,8 +1919,8 @@ static void _draw7seg(int16_t x, int16_t y, int8_t num, int16_t w, int16_t l, co } //============================================================================== -//====================================== -void TFT_print(char *st, int x, int y) { +//============================================ +void TFT_print(const char *st, int x, int y) { int stl, i, tmpw, tmph, fh; uint8_t ch; @@ -2372,8 +2372,8 @@ static UINT tjd_output ( // tft.jpgimage(X, Y, scale, file_name, buf, size] // X & Y can be < 0 ! -//================================================================================== -void TFT_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int size) +//======================================================================================== +void TFT_jpg_image(int x, int y, uint8_t scale, const char *fname, uint8_t *buf, int size) { JPGIODEV dev; struct stat sb; @@ -2469,8 +2469,8 @@ void TFT_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int s } -//==================================================================================== -int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int size) +//========================================================================================== +int TFT_bmp_image(int x, int y, uint8_t scale, const char *fname, uint8_t *imgbuf, int size) { FILE *fhndl = NULL; struct stat sb; diff --git a/components/tft/tft.h b/components/tft/tft.h index 8249f20..e5d4c01 100644 --- a/components/tft/tft.h +++ b/components/tft/tft.h @@ -447,7 +447,7 @@ void TFT_setFont(uint8_t font, const char *font_file); * height: pointer to returned font height */ //------------------------------------------- -int TFT_getfontsize(int *width, int* height); +int TFT_getfontsize(int *width, int *height); /* @@ -484,8 +484,8 @@ int TFT_getfontheight(); * LASTY, continues from last Y position; offset can be used: LASTY+n * */ -//------------------------------------- -void TFT_print(char *st, int x, int y); +//------------------------------------------- +void TFT_print(const char *st, int x, int y); /* * Set atributes for 7 segment vector font @@ -579,14 +579,15 @@ int TFT_compare_colors(color_t c1, color_t c2); * returns the string width in pixels. * Useful for positions strings on the screen. */ -//-------------------------------- -int TFT_getStringWidth(char* str); +//-------------------------------------- +int TFT_getStringWidth(const char *str); /* * Fills the rectangle occupied by string with current background color */ -void TFT_clearStringRect(int x, int y, char *str); +//------------------------------------------------------ +void TFT_clearStringRect(int x, int y, const char *str); /* * Converts the components of a color, as specified by the HSB model, @@ -622,8 +623,8 @@ color_t HSBtoRGB(float _hue, float _sat, float _brightness); * size: size of the memory buffer from which the image will be read; used if fname=NULL & buf!=NULL * */ -//----------------------------------------------------------------------------------- -void TFT_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int size); +//----------------------------------------------------------------------------------------- +void TFT_jpg_image(int x, int y, uint8_t scale, const char *fname, uint8_t *buf, int size); /* * Decodes and displays BMP image @@ -639,8 +640,8 @@ void TFT_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int s * size: size of the memory buffer from which the image will be read; used if fname=NULL & imgbuf!=NULL * */ -//------------------------------------------------------------------------------------- -int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int size); +//------------------------------------------------------------------------------------------- +int TFT_bmp_image(int x, int y, uint8_t scale, const char *fname, uint8_t *imgbuf, int size); /* * Get the touch panel coordinates. @@ -673,8 +674,8 @@ int TFT_read_touch(int *x, int* y, uint8_t raw); * err no on error * */ -//------------------------------------------------ -int compile_font_file(char *fontfile, uint8_t dbg); +//------------------------------------------------------- +int compile_font_file(const char *fontfile, uint8_t dbg); /* * Get all font's characters to buffer From 11d56bb3d1edb121839665d1b64fbab123a241a6 Mon Sep 17 00:00:00 2001 From: Marc Lepage Date: Wed, 8 Jul 2020 12:28:48 -0400 Subject: [PATCH 32/39] Fix typos in README Fix various typos and make punctuation consistent. --- README.md | 44 ++++++++++++++++++++++---------------------- components/tft/tft.h | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 0d3ae2d..ee141a9 100644 --- a/README.md +++ b/README.md @@ -16,35 +16,35 @@ https://docs.espressif.com/projects/esp-idf/en/v4.0-beta1/get-started/index.html * **SPI displays oriented SPI driver library** based on *spi-master* driver * Combined **DMA SPI** transfer mode and **direct SPI** for maximal speed * **Grayscale mode** can be selected during runtime which converts all colors to gray scale -* SPI speeds up to **40 MHz** are tested and works without problems +* SPI speeds up to **40 MHz** are tested and work without problems * **Demo application** included which demonstrates most of the library features * **Graphics drawing functions**: * **TFT_drawPixel** Draw pixel at given x,y coordinates * **TFT_drawLine** Draw line between two points - * **TFT_drawFastVLine**, **TFT_drawFastHLine** Draw vertical or horizontal line of given lenght + * **TFT_drawFastVLine**, **TFT_drawFastHLine** Draw vertical or horizontal line of given length * **TFT_drawLineByAngle** Draw line on screen from (x,y) point at given angle * **TFT_drawRect**, **TFT_fillRect** Draw rectangle on screen or fill given rectangular screen region with color * **TFT_drawRoundRect**, **TFT_fillRoundRect** Draw rectangle on screen or fill given rectangular screen region with color with rounded corners * **TFT_drawCircle**, **TFT_fillCircle** Draw or fill circle on screen * **TFT_drawEllipse**, **TFT_fillEllipse** Draw or fill ellipse on screen - * **TFT_drawTriangel**, **TFT_fillTriangle** Draw or fill triangle on screen + * **TFT_drawTriangle**, **TFT_fillTriangle** Draw or fill triangle on screen * **TFT_drawArc** Draw circle arc on screen, from ~ to given angles, with given thickness. Can be outlined with different color - * **TFT_drawPolygon** Draw poligon on screen with given number of sides (3~60). Can be outlined with different color and rotated by given angle. + * **TFT_drawPolygon** Draw polygon on screen with given number of sides (3~60). Can be outlined with different color and rotated by given angle * **Fonts**: - * **fixed** width and proportional fonts are supported; 8 fonts embeded + * **fixed** width and proportional fonts are supported; 8 fonts embedded * unlimited number of **fonts from file** * **7-segment vector font** with variable width/height is included (only numbers and few characters) - * Proportional fonts can be used in fixed width mode. + * Proportional fonts can be used in fixed width mode * Related functions: * **TFT_setFont** Set current font from one of embeded fonts or font file - * **TFT_getfontsize** Returns current font height & width in pixels. - * **TFT_getfontheight** Returns current font height in pixels. - * **set_7seg_font_atrib** Set atributes for 7 segment vector font + * **TFT_getfontsize** Returns current font height & width in pixels + * **TFT_getfontheight** Returns current font height in pixels + * **set_7seg_font_atrib** Set attributes for 7 segment vector font * **getFontCharacters** Get all font's characters to buffer * **String write function**: - * **TFT_print** Write text to display. + * **TFT_print** Write text to display * Strings can be printed at **any angle**. Rotation of the displayed text depends on *tft_font_rotate* variable (0~360) * if *font_transparent* variable is set to 1, no background pixels will be printed * If the text does not fit the screen/window width it will be clipped ( if *text_wrap=0* ), or continued on next line ( if *text_wrap=1* ) @@ -57,15 +57,15 @@ https://docs.espressif.com/projects/esp-idf/en/v4.0-beta1/get-started/index.html * *CENTER* centers the text verticaly * *BOTTOM* bottom justifies the text * *LASTY* continues from last Y position; offset can be used: *LASTY+n* - * **TFT_getStringWidth** Returns the string width in pixels based on current font characteristics. Useful for positioning strings on the screen. + * **TFT_getStringWidth** Returns the string width in pixels based on current font characteristics. Useful for positioning strings on the screen * **TFT_clearStringRect** Fills the rectangle occupied by string with current background color * **Images**: - * **TFT_jpg_image** Decodes and displays JPG images + * **TFT_jpg_image** Decodes and displays JPEG images * Limits: - * Baseline only. Progressive and Lossless JPEG format are not supported. + * Baseline only. Progressive and Lossless JPEG format are not supported * Image size: Up to 65520 x 65520 pixels - * Color space: YCbCr three components only. Gray scale image is not supported. - * Sampling factor: 4:4:4, 4:2:2 or 4:2:0. + * Color space: YCbCr three components only. Gray scale image is not supported + * Sampling factor: 4:4:4, 4:2:2 or 4:2:0 * Can display the image **from file** or **memory buffer** * Image can be **scaled** by factor 0 ~ 3 (1/1, 1/2, 1/4 or 1/8) * Image is displayed from X,Y position on screen/window: @@ -89,9 +89,9 @@ https://docs.espressif.com/projects/esp-idf/en/v4.0-beta1/get-started/index.html * **TFT_fillWindow** Fill *window* area with color * **Touch screen** supported (for now only **XPT2046** controllers) * **TFT_read_touch** Detect if touched and return X,Y coordinates. **Raw** touch screen or **calibrated** values can be returned. - * calibrated coordinates are adjusted for screen orientation. + * calibrated coordinates are adjusted for screen orientation * **Read from display memory** supported - * **TFT_readPixel** Read pixel color value from display GRAM at given x,y coordinates. + * **TFT_readPixel** Read pixel color value from display GRAM at given x,y coordinates * **TFT_readData** Read color data from rectangular screen area * **Other display functions**: * **TFT_fillScreen** Fill the whole screen with color @@ -101,13 +101,13 @@ https://docs.espressif.com/projects/esp-idf/en/v4.0-beta1/get-started/index.html * **disp_select()** Activate display's CS line * **disp_deselect()** Deactivate display's CS line * **find_rd_speed()** Find maximum spi clock for successful read from display RAM - * **TFT_display_init()** Perform display initialization sequence. Sets orientation to landscape; clears the screen. SPI interface must already be setup, *tft_disp_type*, *tft_width*, *tft_height* variables must be set. - * **HSBtoRGB** Converts the components of a color, as specified by the HSB model to an equivalent set of values for the default RGB model. + * **TFT_display_init()** Perform display initialization sequence. Sets orientation to landscape; clears the screen. SPI interface must already be set up, *tft_disp_type*, *tft_width*, *tft_height* variables must be set. + * **HSBtoRGB** Converts the components of a color, as specified by the HSB model to an equivalent set of values for the default RGB model * **TFT_setGammaCurve()** Select one of 4 Gamma curves * **compile_font_file** Function which compiles font c source file to font file which can be used in *TFT_setFont()* function to select external font. Created file have the same name as source file and extension *.fnt* -* **Global wariables** +* **Global variables** * **tft_orientation** current screen orientation * **tft_font_rotate** current font rotate angle (0~395) * **tft_font_transparent** if not 0 draw fonts transparent @@ -146,7 +146,7 @@ Full **demo application**, well documented, is included, please **analyze it** t | Any output pin | SCK | SPI clock input on Display module | | Any output pin | CS | SPI CS input on Display module | | Any output pin | DC | DC (data/command) input on Display module | -| Any output pin | TCS | Touch pannel CS input (if touch panel is used | +| Any output pin | TCS | Touch pannel CS input (if touch panel is used) | | Any output pin | RST | **optional**, reset input of the display module, if not used **pullup the reset input** to Vcc | | Any output pin | BL | **optional**, backlight input of the display module, if not used connect to +3.3V (or +5V) | | GND | GND | Power supply ground | @@ -253,7 +253,7 @@ Deploy the SPIFFS image as below to make the image and font examples work. #### Prepare **SPIFFS** image -*The demo uses some image and font files and it is necessary to flash the spiffs image* +*The demo uses some image and font files and it is necessary to flash the spiffs image.* **To flash already prepared image to flash** execute: diff --git a/components/tft/tft.h b/components/tft/tft.h index 8249f20..dcca528 100644 --- a/components/tft/tft.h +++ b/components/tft/tft.h @@ -488,7 +488,7 @@ int TFT_getfontheight(); void TFT_print(char *st, int x, int y); /* - * Set atributes for 7 segment vector font + * Set attributes for 7 segment vector font * == 7 segment font must be the current font to this function to have effect == * * Params: From 3351a89cef04980cb96637ab0d18fb94bbdf8125 Mon Sep 17 00:00:00 2001 From: Marc Lepage Date: Thu, 23 Jul 2020 11:23:28 -0400 Subject: [PATCH 33/39] Add const qualifier to temp pointer to fix warning warning: initialization discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers] --- components/tft/tft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/tft/tft.c b/components/tft/tft.c index 0214c41..557773f 100644 --- a/components/tft/tft.c +++ b/components/tft/tft.c @@ -1777,7 +1777,7 @@ int TFT_getStringWidth(const char *str) else if (tft_cfont.x_size != 0) strWidth = strlen(str) * tft_cfont.x_size; // fixed width font else { // calculate the width of the string of proportional characters - char *tempStrptr = str; + const char *tempStrptr = str; while (*tempStrptr != 0) { if (getCharPtr(*tempStrptr++)) { strWidth += (((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta) + 1); From 82427a32df0c71d104a858730b93fce01c826fc5 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Wed, 2 Jun 2021 09:36:07 -0400 Subject: [PATCH 34/39] Remove tftspi.h from demo tftspi.h is not meant to be a separate public interface because it is already included in `tft.h`. See discussion in #15; `tftspi.h` is not a useful interface on it is own; user programs should include only `tft.h`. --- main/tft_demo.c | 1 - 1 file changed, 1 deletion(-) diff --git a/main/tft_demo.c b/main/tft_demo.c index 3692f04..e353c24 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -14,7 +14,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "tftspi.h" #include "tft.h" #include "spiffs_vfs.h" From fac22b3664aa41d8ee8bb6a7c775c0b84e971cb1 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Wed, 2 Jun 2021 09:46:38 -0400 Subject: [PATCH 35/39] Update README Regarding Current Status --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index ee141a9..9ee8b5c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,13 @@ +## Not Maintained + +I'm not currently doing any ESP32 development at all, and so I'm unable to address issues in this library. +If anyone will volunteer to maintain the active fork I'm happy to link to it from this README. Just open a PR with the README changes. + +For my own project I ended up using [LVGL](https://github.com/lvgl/lv_port_esp32) instead of this library. + +The license of this project is in complete doubt because upstream [never released it under an open source license](https://github.com/loboris/ESP32_TFT_library/issues/80). + +Technically it is not open source and no one can use it in their project without risk of a copyright infringement, though loboris clearly intended for people to use it freely. ### TFT library for ESP32 From 9283df8c83370be9cfe55caaf9841c457ba558c9 Mon Sep 17 00:00:00 2001 From: Jeremy Huffman Date: Wed, 2 Jun 2021 09:51:01 -0400 Subject: [PATCH 36/39] tftspi.h doesn't need to include itself. fixes #19 --- components/tft/tftspi.h | 1 - 1 file changed, 1 deletion(-) diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h index 282e3f9..afde266 100644 --- a/components/tft/tftspi.h +++ b/components/tft/tftspi.h @@ -7,7 +7,6 @@ #ifndef _TFTSPI_H_ #define _TFTSPI_H_ -#include "tftspi.h" #include "spi_master_lobo.h" #include "sdkconfig.h" #include "stmpe610.h" From f401699ee986e03e519528422af66715de6d69bf Mon Sep 17 00:00:00 2001 From: Shubham Patil Date: Mon, 23 Jan 2023 23:12:57 +0530 Subject: [PATCH 37/39] Fixes for compiling with esp-idf v5.0 --- components/spidriver/CMakeLists.txt | 3 ++- components/spidriver/spi_master_lobo.c | 2 ++ components/tft/CMakeLists.txt | 3 ++- components/tft/tft.c | 2 +- components/tft/tftspi.c | 11 ++++++----- main/tft_demo.c | 16 ++++++++-------- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/components/spidriver/CMakeLists.txt b/components/spidriver/CMakeLists.txt index 2422429..1b3ee90 100644 --- a/components/spidriver/CMakeLists.txt +++ b/components/spidriver/CMakeLists.txt @@ -1,2 +1,3 @@ FILE(GLOB SOURCES *.c) -idf_component_register(SRCS ${SOURCES} INCLUDE_DIRS ".") +idf_component_register(SRCS ${SOURCES} INCLUDE_DIRS "." + PRIV_REQUIRES driver) diff --git a/components/spidriver/spi_master_lobo.c b/components/spidriver/spi_master_lobo.c index 6aa7725..9d31f0f 100644 --- a/components/spidriver/spi_master_lobo.c +++ b/components/spidriver/spi_master_lobo.c @@ -68,6 +68,8 @@ Main driver's function is 'spi_lobo_transfer_data()' #include "driver/gpio.h" #include "spi_master_lobo.h" #include "driver/periph_ctrl.h" +#include "soc/gpio_periph.h" +#include "rom/gpio.h" static spi_lobo_host_t *spihost[3] = {NULL}; diff --git a/components/tft/CMakeLists.txt b/components/tft/CMakeLists.txt index a5f8712..cc96dce 100644 --- a/components/tft/CMakeLists.txt +++ b/components/tft/CMakeLists.txt @@ -1,4 +1,5 @@ FILE(GLOB SOURCES *.c) idf_component_register(SRCS ${SOURCES} INCLUDE_DIRS "." - REQUIRES spidriver) + REQUIRES spidriver + PRIV_REQUIRES driver) diff --git a/components/tft/tft.c b/components/tft/tft.c index 557773f..842720f 100644 --- a/components/tft/tft.c +++ b/components/tft/tft.c @@ -2363,7 +2363,7 @@ static UINT tjd_output ( } else { wait_trans_finish(1); - printf("Data size error: %d jpg: (%d,%d,%d,%d) disp: (%d,%d,%d,%d)\r\n", len, left,top,right,bottom, dleft,dtop,dright,dbottom); + printf("Data size error: %lu jpg: (%d,%d,%d,%d) disp: (%d,%d,%d,%d)\r\n", (long unsigned)len, left,top,right,bottom, dleft,dtop,dright,dbottom); return 0; // stop decompression } diff --git a/components/tft/tftspi.c b/components/tft/tftspi.c index d9e6e2b..7677629 100644 --- a/components/tft/tftspi.c +++ b/components/tft/tftspi.c @@ -13,6 +13,7 @@ #include "freertos/task.h" #include "soc/spi_reg.h" #include "driver/gpio.h" +#include "rom/gpio.h" // ==================================================== @@ -601,14 +602,14 @@ uint32_t stmpe610_getID() void stmpe610_Init() { stmpe610_write_reg(STMPE610_REG_SYS_CTRL1, 0x02); // Software chip reset - vTaskDelay(10 / portTICK_RATE_MS); + vTaskDelay(10 / portTICK_PERIOD_MS); stmpe610_write_reg(STMPE610_REG_SYS_CTRL2, 0x04); // Temperature sensor clock off, GPIO clock off, touch clock on, ADC clock on stmpe610_write_reg(STMPE610_REG_INT_EN, 0x00); // Don't Interrupt on INT pin stmpe610_write_reg(STMPE610_REG_ADC_CTRL1, 0x48); // ADC conversion time = 80 clock ticks, 12-bit ADC, internal voltage refernce - vTaskDelay(2 / portTICK_RATE_MS); + vTaskDelay(2 / portTICK_PERIOD_MS); stmpe610_write_reg(STMPE610_REG_ADC_CTRL2, 0x01); // ADC speed 3.25MHz stmpe610_write_reg(STMPE610_REG_GPIO_AF, 0x00); // GPIO alternate function - OFF stmpe610_write_reg(STMPE610_REG_TSC_CFG, 0xE3); // Averaging 8, touch detect delay 1ms, panel driver settling time 1ms @@ -756,7 +757,7 @@ static void commandList(spi_lobo_device_handle_t spi, const uint8_t *addr) { if(ms) { ms = *addr++; // Read post-command delay time (ms) if(ms == 255) ms = 500; // If 255, delay for 500 ms - vTaskDelay(ms / portTICK_RATE_MS); + vTaskDelay(ms / portTICK_PERIOD_MS); } } } @@ -897,9 +898,9 @@ void TFT_display_init() #if PIN_NUM_RST //Reset the display gpio_set_level(PIN_NUM_RST, 0); - vTaskDelay(20 / portTICK_RATE_MS); + vTaskDelay(20 / portTICK_PERIOD_MS); gpio_set_level(PIN_NUM_RST, 1); - vTaskDelay(150 / portTICK_RATE_MS); + vTaskDelay(150 / portTICK_PERIOD_MS); #endif ret = disp_select(); diff --git a/main/tft_demo.c b/main/tft_demo.c index 3692f04..07b9894 100644 --- a/main/tft_demo.c +++ b/main/tft_demo.c @@ -131,7 +131,7 @@ static int obtain_time(void) //ESP_LOGI(tag, "Waiting for system time to be set... (%d/%d)", retry, retry_count); sprintf(tmp_buff, "Wait %0d/%d", retry, retry_count); TFT_print(tmp_buff, CENTER, LASTY); - vTaskDelay(500 / portTICK_RATE_MS); + vTaskDelay(500 / portTICK_PERIOD_MS); time(&time_now); tm_info = localtime(&time_now); } @@ -189,7 +189,7 @@ static int _checkTouch() int tx, ty; if (TFT_read_touch(&tx, &ty, 0)) { while (TFT_read_touch(&tx, &ty, 1)) { - vTaskDelay(20 / portTICK_RATE_MS); + vTaskDelay(20 / portTICK_PERIOD_MS); } return 1; } @@ -206,12 +206,12 @@ static int Wait(int ms) ms *= -1; } if (ms <= 50) { - vTaskDelay(ms / portTICK_RATE_MS); + vTaskDelay(ms / portTICK_PERIOD_MS); //if (_checkTouch()) return 0; } else { for (int n=0; n 50) return; - vTaskDelay(100 / portTICK_RATE_MS); + vTaskDelay(100 / portTICK_PERIOD_MS); } } else { doexit++; if (doexit == 2) update_header(NULL, "---"); if (doexit > 50) return; - vTaskDelay(100 / portTICK_RATE_MS); + vTaskDelay(100 / portTICK_PERIOD_MS); } } #endif @@ -1294,7 +1294,7 @@ void app_main() // ==================================================================================================================== - vTaskDelay(500 / portTICK_RATE_MS); + vTaskDelay(500 / portTICK_PERIOD_MS); printf("\r\n==============================\r\n"); printf("TFT display DEMO, LoBo 11/2017\r\n"); printf("==============================\r\n"); @@ -1350,7 +1350,7 @@ void app_main() printf("OK\r\n"); #if USE_TOUCH == TOUCH_TYPE_STMPE610 stmpe610_Init(); - vTaskDelay(10 / portTICK_RATE_MS); + vTaskDelay(10 / portTICK_PERIOD_MS); uint32_t tver = stmpe610_getID(); printf("STMPE touch initialized, ver: %04x - %02x\r\n", tver >> 8, tver & 0xFF); #endif From a9482e32f3d99ca88da6a8a0cf67ca50f64b6086 Mon Sep 17 00:00:00 2001 From: Shubham Patil Date: Mon, 31 Jul 2023 15:37:27 +0530 Subject: [PATCH 38/39] Use the newer spi_flash APIs for read/write/erase flash operations esp-idf v5.1 have deprecated the legacy spi_flash APIs, so using the newer APIs if esp-idf version is >= 5.0 --- components/mkspiffs/src/spiffs/esp_spiffs.c | 31 ++++++++++++++++++++- components/spiffs/CMakeLists.txt | 2 +- components/spiffs/esp_spiffs.c | 29 +++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/components/mkspiffs/src/spiffs/esp_spiffs.c b/components/mkspiffs/src/spiffs/esp_spiffs.c index 088480c..af15bf6 100644 --- a/components/mkspiffs/src/spiffs/esp_spiffs.c +++ b/components/mkspiffs/src/spiffs/esp_spiffs.c @@ -34,7 +34,12 @@ #include "spiffs.h" +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +#include +#include +#else #include +#endif s32_t esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst) { u32_t aaddr; @@ -63,7 +68,11 @@ s32_t esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst) { abuff = (u8_t *)(((ptrdiff_t)buff + (4 - 1)) & (u32_t)-4); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (esp_flash_read(NULL, (void *)abuff, aaddr, asize) != ESP_OK) { +#else if (spi_flash_read(aaddr, (void *)abuff, asize) != 0) { +#endif free(buff); return SPIFFS_ERR_INTERNAL; } @@ -72,7 +81,11 @@ s32_t esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst) { free(buff); } else { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (esp_flash_read(NULL, (void *)dst, addr, size) != ESP_OK) { +#else if (spi_flash_read(addr, (void *)dst, size) != 0) { +#endif return SPIFFS_ERR_INTERNAL; } } @@ -87,7 +100,7 @@ s32_t esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src) { u32_t asize; asize = size; - + // Align address to 4 byte aaddr = (addr + (4 - 1)) & -4; if (aaddr != addr) { @@ -107,21 +120,33 @@ s32_t esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src) { abuff = (u8_t *)(((ptrdiff_t)buff + (4 - 1)) & -4); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (esp_flash_read(NULL, (void *)abuff, aaddr, asize) != ESP_OK) { +#else if (spi_flash_read(aaddr, (void *)abuff, asize) != 0) { +#endif free(buff); return SPIFFS_ERR_INTERNAL; } memcpy(abuff + (addr - aaddr), src, size); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (esp_flash_write(NULL, (uint32_t *)abuff, aaddr, asize) != ESP_OK) { +#else if (spi_flash_write(aaddr, (uint32_t *)abuff, asize) != 0) { +#endif free(buff); return SPIFFS_ERR_INTERNAL; } free(buff); } else { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (esp_flash_write(NULL, (uint32_t *)src, addr, size) != ESP_OK) { +#else if (spi_flash_write(addr, (uint32_t *)src, size) != 0) { +#endif return SPIFFS_ERR_INTERNAL; } } @@ -130,7 +155,11 @@ s32_t esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src) { } s32_t IRAM_ATTR esp32_spi_flash_erase(u32_t addr, u32_t size) { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (esp_flash_erase_region(NULL, addr, size) != ESP_OK) { +#else if (spi_flash_erase_sector(addr >> 12) != 0) { +#endif return SPIFFS_ERR_INTERNAL; } diff --git a/components/spiffs/CMakeLists.txt b/components/spiffs/CMakeLists.txt index 487e50b..fc1f498 100644 --- a/components/spiffs/CMakeLists.txt +++ b/components/spiffs/CMakeLists.txt @@ -1,3 +1,3 @@ FILE(GLOB SOURCES *.c) idf_component_register(SRCS ${SOURCES} INCLUDE_DIRS "." - REQUIRES spi_flash) + REQUIRES vfs spi_flash) diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c index 57b8aa0..8487183 100644 --- a/components/spiffs/esp_spiffs.c +++ b/components/spiffs/esp_spiffs.c @@ -34,7 +34,12 @@ #include "spiffs.h" +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +#include +#include +#else #include +#endif s32_t IRAM_ATTR esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst) { u32_t aaddr; @@ -63,7 +68,11 @@ s32_t IRAM_ATTR esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst) { abuff = (u8_t *)(((ptrdiff_t)buff + (4 - 1)) & (u32_t)-4); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (esp_flash_read(NULL, (void *)abuff, aaddr, asize) != ESP_OK) { +#else if (spi_flash_read(aaddr, (void *)abuff, asize) != 0) { +#endif free(buff); return SPIFFS_ERR_INTERNAL; } @@ -72,7 +81,11 @@ s32_t IRAM_ATTR esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst) { free(buff); } else { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (esp_flash_read(NULL, (void *)dst, addr, size) != ESP_OK) { +#else if (spi_flash_read(addr, (void *)dst, size) != 0) { +#endif return SPIFFS_ERR_INTERNAL; } } @@ -107,21 +120,33 @@ s32_t IRAM_ATTR esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src) { abuff = (u8_t *)(((ptrdiff_t)buff + (4 - 1)) & -4); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (esp_flash_read(NULL, (void *)abuff, aaddr, asize) != ESP_OK) { +#else if (spi_flash_read(aaddr, (void *)abuff, asize) != 0) { +#endif free(buff); return SPIFFS_ERR_INTERNAL; } memcpy(abuff + (addr - aaddr), src, size); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (esp_flash_write(NULL, (uint32_t *)abuff, aaddr, asize) != ESP_OK) { +#else if (spi_flash_write(aaddr, (uint32_t *)abuff, asize) != 0) { +#endif free(buff); return SPIFFS_ERR_INTERNAL; } free(buff); } else { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (esp_flash_write(NULL, (uint32_t *)src, addr, size) != ESP_OK) { +#else if (spi_flash_write(addr, (uint32_t *)src, size) != 0) { +#endif return SPIFFS_ERR_INTERNAL; } } @@ -130,7 +155,11 @@ s32_t IRAM_ATTR esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src) { } s32_t IRAM_ATTR esp32_spi_flash_erase(u32_t addr, u32_t size) { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (esp_flash_erase_region(NULL, addr, size) != ESP_OK) { +#else if (spi_flash_erase_sector(addr >> 12) != 0) { +#endif return SPIFFS_ERR_INTERNAL; } From edbb14ce36501e3e8e072e1bb61460f372219532 Mon Sep 17 00:00:00 2001 From: Shubham Patil Date: Tue, 18 Jun 2024 10:33:11 +0530 Subject: [PATCH 39/39] Fix warnings when building with esp-idf v5.2.1 - typecast for fixing the type - remove IRAM_ATTR for getting rid of conflic warnings - fix use after free --- components/spidriver/spi_master_lobo.c | 2 +- components/spiffs/spiffs_vfs.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/components/spidriver/spi_master_lobo.c b/components/spidriver/spi_master_lobo.c index 9d31f0f..24eeebb 100644 --- a/components/spidriver/spi_master_lobo.c +++ b/components/spidriver/spi_master_lobo.c @@ -621,8 +621,8 @@ esp_err_t spi_lobo_bus_remove_device(spi_lobo_device_handle_t handle) if (spihost[handle->host_dev]->device[x] !=NULL) break; } if (x == NO_DEV) { - free(handle); spi_lobo_bus_free(handle->host_dev, 1); + free(handle); } else free(handle); diff --git a/components/spiffs/spiffs_vfs.c b/components/spiffs/spiffs_vfs.c index 0e52cf4..0b8a068 100644 --- a/components/spiffs/spiffs_vfs.c +++ b/components/spiffs/spiffs_vfs.c @@ -47,12 +47,12 @@ int spiffs_is_mounted = 0; QueueHandle_t spiffs_mutex = NULL; -static int IRAM_ATTR vfs_spiffs_open(const char *path, int flags, int mode); -static ssize_t IRAM_ATTR vfs_spiffs_write(int fd, const void *data, size_t size); -static ssize_t IRAM_ATTR vfs_spiffs_read(int fd, void * dst, size_t size); -static int IRAM_ATTR vfs_spiffs_fstat(int fd, struct stat * st); -static int IRAM_ATTR vfs_spiffs_close(int fd); -static off_t IRAM_ATTR vfs_spiffs_lseek(int fd, off_t size, int mode); +static int vfs_spiffs_open(const char *path, int flags, int mode); +static ssize_t vfs_spiffs_write(int fd, const void *data, size_t size); +static ssize_t vfs_spiffs_read(int fd, void * dst, size_t size); +static int vfs_spiffs_fstat(int fd, struct stat * st); +static int vfs_spiffs_close(int fd); +static off_t vfs_spiffs_lseek(int fd, off_t size, int mode); typedef struct { DIR dir; @@ -92,7 +92,7 @@ static u8_t *my_spiffs_cache; //---------------------------------------------------- void spiffs_fs_stat(uint32_t *total, uint32_t *used) { - if (SPIFFS_info(&fs, total, used) != SPIFFS_OK) { + if (SPIFFS_info(&fs, (u32_t *)total, (u32_t *)used) != SPIFFS_OK) { *total = 0; *used = 0; }