├── icons ├── git.png ├── facebook.png ├── linkedin.png ├── twitter.png ├── whatsapp.png ├── youtube.png ├── instagram.png └── vkontakte.png ├── images ├── git.png ├── esp32.bmp ├── esp32.jpeg ├── twitter.png ├── youtube.png ├── esp32_ro.bmp ├── esp_logo.png ├── facebook.png ├── instagram.png ├── linkedin.png ├── vkontakte.png └── whatsapp.png ├── font ├── ILGH16XB.FNT ├── ILGH24XB.FNT ├── ILGH32XB.FNT ├── ILMH16XB.FNT ├── ILMH24XB.FNT ├── ILMH32XB.FNT └── LATIN32B.FNT ├── main ├── CMakeLists.txt ├── component.mk ├── idf_component.yml ├── decode_png.h ├── decode_jpeg.h ├── decode_png.c ├── bmpfile.h ├── pngle.h ├── decode_jpeg.c ├── decode_jpeg_v5.c └── pngle.c ├── components └── ili9340 │ ├── component.mk │ ├── CMakeLists.txt │ ├── fontx.h │ ├── ili9340.h │ ├── Kconfig.projbuild │ ├── fontx.c │ └── ili9340.c ├── partitions.csv ├── sdkconfig.defaults ├── CMakeLists.txt ├── .github └── workflows │ └── espidf-compile.yml ├── LICENSE └── README.md /icons/git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/icons/git.png -------------------------------------------------------------------------------- /images/git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/images/git.png -------------------------------------------------------------------------------- /font/ILGH16XB.FNT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/font/ILGH16XB.FNT -------------------------------------------------------------------------------- /font/ILGH24XB.FNT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/font/ILGH24XB.FNT -------------------------------------------------------------------------------- /font/ILGH32XB.FNT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/font/ILGH32XB.FNT -------------------------------------------------------------------------------- /font/ILMH16XB.FNT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/font/ILMH16XB.FNT -------------------------------------------------------------------------------- /font/ILMH24XB.FNT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/font/ILMH24XB.FNT -------------------------------------------------------------------------------- /font/ILMH32XB.FNT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/font/ILMH32XB.FNT -------------------------------------------------------------------------------- /font/LATIN32B.FNT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/font/LATIN32B.FNT -------------------------------------------------------------------------------- /icons/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/icons/facebook.png -------------------------------------------------------------------------------- /icons/linkedin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/icons/linkedin.png -------------------------------------------------------------------------------- /icons/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/icons/twitter.png -------------------------------------------------------------------------------- /icons/whatsapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/icons/whatsapp.png -------------------------------------------------------------------------------- /icons/youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/icons/youtube.png -------------------------------------------------------------------------------- /images/esp32.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/images/esp32.bmp -------------------------------------------------------------------------------- /images/esp32.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/images/esp32.jpeg -------------------------------------------------------------------------------- /images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/images/twitter.png -------------------------------------------------------------------------------- /images/youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/images/youtube.png -------------------------------------------------------------------------------- /icons/instagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/icons/instagram.png -------------------------------------------------------------------------------- /icons/vkontakte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/icons/vkontakte.png -------------------------------------------------------------------------------- /images/esp32_ro.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/images/esp32_ro.bmp -------------------------------------------------------------------------------- /images/esp_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/images/esp_logo.png -------------------------------------------------------------------------------- /images/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/images/facebook.png -------------------------------------------------------------------------------- /images/instagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/images/instagram.png -------------------------------------------------------------------------------- /images/linkedin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/images/linkedin.png -------------------------------------------------------------------------------- /images/vkontakte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/images/vkontakte.png -------------------------------------------------------------------------------- /images/whatsapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-ili9340/HEAD/images/whatsapp.png -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(srcs "main.c" "decode_png.c" "decode_jpeg_v5.c" "pngle.c") 2 | 3 | idf_component_register(SRCS ${srcs} INCLUDE_DIRS ".") 4 | -------------------------------------------------------------------------------- /main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # "main" pseudo-component makefile. 3 | # 4 | # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) 5 | -------------------------------------------------------------------------------- /main/idf_component.yml: -------------------------------------------------------------------------------- 1 | ## IDF Component Manager Manifest File 2 | dependencies: 3 | esp_jpeg: 4 | version: "^1.0.0" 5 | rules: 6 | - if: "idf_version >=5.0" 7 | -------------------------------------------------------------------------------- /components/ili9340/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # "main" pseudo-component makefile. 3 | # 4 | # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) 5 | -------------------------------------------------------------------------------- /components/ili9340/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(srcs "ili9340.c" "fontx.c") 2 | 3 | idf_component_register(SRCS "${srcs}" 4 | PRIV_REQUIRES driver 5 | INCLUDE_DIRS ".") 6 | -------------------------------------------------------------------------------- /main/decode_png.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "pngle.h" 5 | 6 | void png_init(pngle_t *pngle, uint32_t w, uint32_t h); 7 | void png_draw(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t rgba[4]); 8 | void png_finish(pngle_t *pngle); 9 | 10 | -------------------------------------------------------------------------------- /partitions.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild 3 | nvs, data, nvs, 0x9000, 0x6000, 4 | phy_init, data, phy, 0xf000, 0x1000, 5 | factory, app, factory, 0x10000, 1M, 6 | #storage, data, spiffs, , 0xF0000, 7 | storage0, data, spiffs, , 0x20000, 8 | storage1, data, spiffs, , 0x10000, 9 | storage2, data, spiffs, , 0xB0000, 10 | -------------------------------------------------------------------------------- /sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | # 2 | # Partition Table 3 | # 4 | CONFIG_PARTITION_TABLE_CUSTOM=y 5 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 6 | CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" 7 | 8 | # 9 | # Disable Task Watchdog Timer 10 | # 11 | CONFIG_ESP_TASK_WDT_EN=n 12 | 13 | # 14 | # ESP32-specific 15 | # 16 | CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y 17 | CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 18 | 19 | # 20 | # ESP32S2-specific 21 | # 22 | CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y 23 | CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ=240 24 | 25 | # 26 | # ESP32S3-specific 27 | # 28 | CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y 29 | CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240 30 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(ili9340) 7 | 8 | # Create a SPIFFS image from the contents of the 'font' directory 9 | # that fits the partition named 'storage'. FLASH_IN_PROJECT indicates that 10 | # the generated image should be flashed when the entire project is flashed to 11 | # the target with 'idf.py -p PORT flash 12 | spiffs_create_partition_image(storage0 font FLASH_IN_PROJECT) 13 | spiffs_create_partition_image(storage1 icons FLASH_IN_PROJECT) 14 | spiffs_create_partition_image(storage2 images FLASH_IN_PROJECT) 15 | -------------------------------------------------------------------------------- /.github/workflows/espidf-compile.yml: -------------------------------------------------------------------------------- 1 | name: esp-idf-ci-action 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - "main/**" 7 | - "components/**" 8 | 9 | push: 10 | paths: 11 | - "main/**" 12 | - "components/**" 13 | 14 | workflow_dispatch: 15 | repository_dispatch: 16 | 17 | jobs: 18 | build: 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | idf_ver: 23 | - release-v5.0 24 | - release-v5.1 25 | - release-v5.2 26 | - release-v5.3 27 | idf_target: 28 | - esp32 29 | - esp32s2 30 | - esp32c3 31 | - esp32s3 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout repo 35 | uses: actions/checkout@v4 36 | with: 37 | submodules: 'recursive' 38 | - name: Build Application with ESP-IDF 39 | uses: espressif/esp-idf-ci-action@v1 40 | with: 41 | esp_idf_version: ${{ matrix.idf_ver }} 42 | target: ${{ matrix.idf_target }} 43 | path: '.' 44 | -------------------------------------------------------------------------------- /main/decode_jpeg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "esp_err.h" 4 | 5 | #if 0 6 | typedef struct __attribute__((__packed__)) { 7 | uint8_t red; 8 | uint8_t green; 9 | uint8_t blue; 10 | } pixel_jpeg; 11 | #endif 12 | 13 | //rgb565 format 14 | typedef uint16_t pixel_jpeg; 15 | 16 | /** 17 | * @brief Decode the jpeg ``image.jpg`` embedded into the program file into pixel data. 18 | * 19 | * @param pixels A pointer to a pointer for an array of rows, which themselves are an array of pixels. 20 | * Effectively, you can get the pixel data by doing ``decode_jpeg(&myPixels); pixelval=myPixels[ypos][xpos];`` 21 | * @return - ESP_ERR_NOT_SUPPORTED if image is malformed or a progressive jpeg file 22 | * - ESP_ERR_NO_MEM if out of memory 23 | * - ESP_OK on succesful decode 24 | */ 25 | 26 | esp_err_t decode_jpeg(pixel_jpeg ***pixels, char * file, int screenWidth, int screenHeight, int * imageWidth, int * imageHeight); 27 | 28 | /** 29 | * @brief Release image memory. 30 | * 31 | */ 32 | 33 | esp_err_t release_image(pixel_jpeg ***pixels, int screenWidth, int screenHeight); 34 | 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 nopnop2002 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /components/ili9340/fontx.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_FONTX_H_ 2 | #define MAIN_FONTX_H_ 3 | #define FontxGlyphBufSize (32*32/8) 4 | 5 | typedef struct { 6 | const char *path; 7 | char fxname[10]; 8 | bool opened; 9 | bool valid; 10 | bool is_ank; 11 | uint8_t w; 12 | uint8_t h; 13 | uint16_t fsz; 14 | uint8_t bc; 15 | FILE *file; 16 | } FontxFile; 17 | 18 | void AaddFontx(FontxFile *fx, const char *path); 19 | void InitFontx(FontxFile *fxs, const char *f0, const char *f1); 20 | bool OpenFontx(FontxFile *fx); 21 | void CloseFontx(FontxFile *fx); 22 | void DumpFontx(FontxFile *fxs); 23 | uint8_t getFortWidth(FontxFile *fx); 24 | uint8_t getFortHeight(FontxFile *fx); 25 | bool GetFontx(FontxFile *fxs, uint8_t ascii , uint8_t *pGlyph, uint8_t *pw, uint8_t *ph); 26 | void Font2Bitmap(uint8_t *fonts, uint8_t *line, uint8_t w, uint8_t h, uint8_t inverse); 27 | void UnderlineBitmap(uint8_t *line, uint8_t w, uint8_t h); 28 | void ReversBitmap(uint8_t *line, uint8_t w, uint8_t h); 29 | void ShowFont(uint8_t *fonts, uint8_t pw, uint8_t ph); 30 | void ShowBitmap(uint8_t *bitmap, uint8_t pw, uint8_t ph); 31 | uint8_t RotateByte(uint8_t ch); 32 | 33 | // UTF8 to SJIS table 34 | // https://www.mgo-tec.com/blog-entry-utf8sjis01.html 35 | //#define Utf8Sjis "Utf8Sjis.tbl" 36 | //uint16_t UTF2SJIS(spiffs_file fd, uint8_t *utf8); 37 | //int String2SJIS(spiffs_file fd, unsigned char *str_in, size_t stlen, uint16_t *sjis, size_t ssize); 38 | #endif /* MAIN_FONTX_H_ */ 39 | 40 | -------------------------------------------------------------------------------- /main/decode_png.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "decode_png.h" 4 | #include "pngle.h" 5 | #include "esp_log.h" 6 | 7 | void png_init(pngle_t *pngle, uint32_t w, uint32_t h) 8 | { 9 | ESP_LOGD(__FUNCTION__, "png_init w=%"PRIu32" h=%"PRIu32, w, h); 10 | ESP_LOGD(__FUNCTION__, "screenWidth=%d screenHeight=%d", pngle->screenWidth, pngle->screenHeight); 11 | pngle->imageWidth = w; 12 | pngle->imageHeight = h; 13 | pngle->reduction = false; 14 | pngle->scale_factor = 1.0; 15 | 16 | // Calculate Reduction 17 | if (pngle->screenWidth < pngle->imageWidth || pngle->screenHeight < pngle->imageHeight) { 18 | pngle->reduction = true; 19 | double factorWidth = (double)pngle->screenWidth / (double)pngle->imageWidth; 20 | double factorHeight = (double)pngle->screenHeight / (double)pngle->imageHeight; 21 | pngle->scale_factor = factorWidth; 22 | if (factorHeight < factorWidth) pngle->scale_factor = factorHeight; 23 | pngle->imageWidth = pngle->imageWidth * pngle->scale_factor; 24 | pngle->imageHeight = pngle->imageHeight * pngle->scale_factor; 25 | } 26 | ESP_LOGD(__FUNCTION__, "reduction=%d scale_factor=%f", pngle->reduction, pngle->scale_factor); 27 | ESP_LOGD(__FUNCTION__, "imageWidth=%d imageHeight=%d", pngle->imageWidth, pngle->imageHeight); 28 | 29 | } 30 | 31 | #define rgb565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)) 32 | 33 | void png_draw(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t rgba[4]) 34 | { 35 | ESP_LOGD(__FUNCTION__, "png_draw x=%"PRIu32" y=%"PRIu32" w=%"PRIu32" h=%"PRIu32, x,y,w,h); 36 | #if 0 37 | uint8_t r = rgba[0]; 38 | uint8_t g = rgba[1]; 39 | uint8_t b = rgba[2]; 40 | #endif 41 | 42 | // image reduction 43 | uint32_t _x = x; 44 | uint32_t _y = y; 45 | if (pngle->reduction) { 46 | _x = x * pngle->scale_factor; 47 | _y = y * pngle->scale_factor; 48 | } 49 | if (_y < pngle->screenHeight && _x < pngle->screenWidth) { 50 | #if 0 51 | pngle->pixels[_y][_x].red = rgba[0]; 52 | pngle->pixels[_y][_x].green = rgba[1]; 53 | pngle->pixels[_y][_x].blue = rgba[2]; 54 | #endif 55 | pngle->pixels[_y][_x] = rgb565(rgba[0], rgba[1], rgba[2]); 56 | } 57 | 58 | } 59 | 60 | void png_finish(pngle_t *pngle) { 61 | ESP_LOGD(__FUNCTION__, "png_finish"); 62 | } 63 | -------------------------------------------------------------------------------- /main/bmpfile.h: -------------------------------------------------------------------------------- 1 | #ifndef __bmpfile_h__ 2 | #define __bmpfile_h__ 3 | 4 | typedef struct { 5 | uint8_t magic[2]; /* the magic number used to identify the BMP file: 6 | 0x42 0x4D (Hex code points for B and M). 7 | The following entries are possible: 8 | BM - Windows 3.1x, 95, NT, ... etc 9 | BA - OS/2 Bitmap Array 10 | CI - OS/2 Color Icon 11 | CP - OS/2 Color Pointer 12 | IC - OS/2 Icon 13 | PT - OS/2 Pointer. */ 14 | uint32_t filesz; /* the size of the BMP file in bytes */ 15 | uint16_t creator1; /* reserved. */ 16 | uint16_t creator2; /* reserved. */ 17 | uint32_t offset; /* the offset, i.e. starting address, 18 | of the byte where the bitmap data can be found. */ 19 | } bmp_header_t; 20 | 21 | typedef struct { 22 | uint32_t header_sz; /* the size of this header (40 bytes) */ 23 | uint32_t width; /* the bitmap width in pixels */ 24 | uint32_t height; /* the bitmap height in pixels */ 25 | uint16_t nplanes; /* the number of color planes being used. 26 | Must be set to 1. */ 27 | uint16_t depth; /* the number of bits per pixel, 28 | which is the color depth of the image. 29 | Typical values are 1, 4, 8, 16, 24 and 32. */ 30 | uint32_t compress_type; /* the compression method being used. 31 | See also bmp_compression_method_t. */ 32 | uint32_t bmp_bytesz; /* the image size. This is the size of the raw bitmap 33 | data (see below), and should not be confused 34 | with the file size. */ 35 | uint32_t hres; /* the horizontal resolution of the image. 36 | (pixel per meter) */ 37 | uint32_t vres; /* the vertical resolution of the image. 38 | (pixel per meter) */ 39 | uint32_t ncolors; /* the number of colors in the color palette, 40 | or 0 to default to 2n. */ 41 | uint32_t nimpcolors; /* the number of important colors used, 42 | or 0 when every color is important; 43 | generally ignored. */ 44 | } bmp_dib_v3_header_t; 45 | 46 | typedef struct { 47 | bmp_header_t header; 48 | bmp_dib_v3_header_t dib; 49 | } bmpfile_t; 50 | 51 | #endif /* __bmpfile_h__ */ 52 | -------------------------------------------------------------------------------- /main/pngle.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * MIT License 3 | * 4 | * Copyright (c) 2019 kikuchan 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef __PNGLE_H__ 26 | #define __PNGLE_H__ 27 | 28 | #include 29 | #include 30 | #include "miniz.h" 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | #ifndef MIN 37 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 38 | #endif 39 | 40 | #ifdef PNGLE_DEBUG 41 | #define debug_printf(...) fprintf(stderr, __VA_ARGS__) 42 | #else 43 | #define debug_printf(...) ((void)0) 44 | #endif 45 | 46 | //#define PNGLE_NO_GAMMA_CORRECTION 47 | 48 | #if 0 49 | typedef struct __attribute__((__packed__)) { 50 | uint8_t red; 51 | uint8_t green; 52 | uint8_t blue; 53 | } pixel_png; 54 | #endif 55 | 56 | //rgb565 format 57 | typedef uint16_t pixel_png; 58 | 59 | typedef enum { 60 | PNGLE_STATE_ERROR = -2, 61 | PNGLE_STATE_EOF = -1, 62 | PNGLE_STATE_INITIAL = 0, 63 | 64 | PNGLE_STATE_FIND_CHUNK_HEADER, 65 | PNGLE_STATE_HANDLE_CHUNK, 66 | PNGLE_STATE_CRC, 67 | } pngle_state_t; 68 | 69 | 70 | typedef enum { 71 | // Supported chunks 72 | // Filter chunk names by following command to (re)generate hex constants; 73 | // % perl -ne 'chomp; s/.*\s*\/\/\s*//; print "\tPNGLE_CHUNK_$_ = 0x" . unpack("H*") . "UL, // $_\n";' 74 | PNGLE_CHUNK_IHDR = 0x49484452UL, // IHDR 75 | PNGLE_CHUNK_PLTE = 0x504c5445UL, // PLTE 76 | PNGLE_CHUNK_IDAT = 0x49444154UL, // IDAT 77 | PNGLE_CHUNK_IEND = 0x49454e44UL, // IEND 78 | PNGLE_CHUNK_tRNS = 0x74524e53UL, // tRNS 79 | PNGLE_CHUNK_gAMA = 0x67414d41UL, // gAMA 80 | } pngle_chunk_t; 81 | 82 | typedef struct _pngle_ihdr_t { 83 | uint32_t width; 84 | uint32_t height; 85 | uint8_t depth; 86 | uint8_t color_type; 87 | uint8_t compression; 88 | uint8_t filter; 89 | uint8_t interlace; 90 | } pngle_ihdr_t; 91 | 92 | typedef unsigned int UINT; 93 | 94 | typedef struct pngle pngle_t; 95 | 96 | typedef void (*pngle_init_callback_t)(pngle_t *pngle, uint32_t w, uint32_t h); 97 | typedef void (*pngle_draw_callback_t)(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t rgba[4]); 98 | typedef void (*pngle_done_callback_t)(pngle_t *pngle); 99 | 100 | struct pngle { 101 | pngle_ihdr_t hdr; 102 | 103 | uint_fast8_t channels; // 0 indicates IHDR hasn't been processed yet 104 | 105 | // PLTE chunk 106 | size_t n_palettes; 107 | uint8_t *palette; 108 | 109 | // tRNS chunk 110 | size_t n_trans_palettes; 111 | uint8_t *trans_palette; 112 | 113 | // parser state (reset on every chunk header) 114 | pngle_state_t state; 115 | uint32_t chunk_type; 116 | uint32_t chunk_remain; 117 | mz_ulong crc32; 118 | 119 | // decompression state (reset on IHDR) 120 | tinfl_decompressor inflator; // 11000 bytes 121 | uint8_t lz_buf[TINFL_LZ_DICT_SIZE]; // 32768 bytes 122 | uint8_t *next_out; // NULL indicates IDAT hasn't been processed yet 123 | size_t avail_out; 124 | 125 | // scanline decoder (reset on every set_interlace_pass() call) 126 | uint8_t *scanline_ringbuf; 127 | size_t scanline_ringbuf_size; 128 | size_t scanline_ringbuf_cidx; 129 | int_fast8_t scanline_remain_bytes_to_render; 130 | int_fast8_t filter_type; 131 | uint32_t drawing_x; 132 | uint32_t drawing_y; 133 | 134 | // interlace 135 | uint_fast8_t interlace_pass; 136 | 137 | const char *error; 138 | 139 | #ifndef PNGLE_NO_GAMMA_CORRECTION 140 | uint8_t *gamma_table; 141 | double display_gamma; 142 | #endif 143 | 144 | pngle_init_callback_t init_callback; 145 | pngle_draw_callback_t draw_callback; 146 | pngle_done_callback_t done_callback; 147 | 148 | void *user_data; 149 | uint16_t screenWidth; 150 | uint16_t screenHeight; 151 | uint16_t imageWidth; 152 | uint16_t imageHeight; 153 | pixel_png **pixels; 154 | bool reduction; 155 | double scale_factor; 156 | }; 157 | 158 | 159 | // ---------------- 160 | // Basic interfaces 161 | // ---------------- 162 | pngle_t *pngle_new(uint16_t width, uint16_t height); 163 | void pngle_destroy(pngle_t *pngle, uint16_t width, uint16_t height); 164 | void pngle_reset(pngle_t *pngle); // clear its internal state (not applied to pngle_set_* functions) 165 | const char *pngle_error(pngle_t *pngle); 166 | int pngle_feed(pngle_t *pngle, const void *buf, size_t len); // returns -1: On error, 0: Need more data, n: n bytes eaten 167 | 168 | uint32_t pngle_get_width(pngle_t *pngle); 169 | uint32_t pngle_get_height(pngle_t *pngle); 170 | 171 | void pngle_set_init_callback(pngle_t *png, pngle_init_callback_t callback); 172 | void pngle_set_draw_callback(pngle_t *png, pngle_draw_callback_t callback); 173 | void pngle_set_done_callback(pngle_t *png, pngle_done_callback_t callback); 174 | 175 | void pngle_set_display_gamma(pngle_t *pngle, double display_gamma); // enables gamma correction by specifying display gamma, typically 2.2. No effect when gAMA chunk is missing 176 | 177 | void pngle_set_user_data(pngle_t *pngle, void *user_data); 178 | void *pngle_get_user_data(pngle_t *pngle); 179 | 180 | 181 | // Get IHDR information 182 | pngle_ihdr_t *pngle_get_ihdr(pngle_t *pngle); 183 | 184 | 185 | #ifdef __cplusplus 186 | } 187 | #endif 188 | 189 | #endif /* __PNGLE_H__ */ 190 | -------------------------------------------------------------------------------- /components/ili9340/ili9340.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_ILI9340_H_ 2 | #define MAIN_ILI9340_H_ 3 | 4 | #include "driver/spi_master.h" 5 | #include "fontx.h" 6 | 7 | #define rgb565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)) 8 | 9 | #define RED rgb565(255, 0, 0) // 0xf800 10 | #define GREEN rgb565( 0, 255, 0) // 0x07e0 11 | #define BLUE rgb565( 0, 0, 255) // 0x001f 12 | #define BLACK rgb565( 0, 0, 0) // 0x0000 13 | #define WHITE rgb565(255, 255, 255) // 0xffff 14 | #define GRAY rgb565(128, 128, 128) // 0x8410 15 | #define YELLOW rgb565(255, 255, 0) // 0xFFE0 16 | #define CYAN rgb565( 0, 156, 209) // 0x04FA 17 | #define PURPLE rgb565(128, 0, 128) // 0x8010 18 | 19 | typedef enum {DIRECTION0, DIRECTION90, DIRECTION180, DIRECTION270} DIRECTION; 20 | 21 | typedef struct { 22 | uint16_t _model; 23 | uint16_t _width; 24 | uint16_t _height; 25 | uint16_t _offsetx; 26 | uint16_t _offsety; 27 | uint16_t _font_direction; 28 | uint16_t _font_fill; 29 | uint16_t _font_fill_color; 30 | uint16_t _font_underline; 31 | uint16_t _font_underline_color; 32 | int16_t _dc; 33 | int16_t _bl; 34 | int16_t _irq; 35 | spi_device_handle_t _TFT_Handle; 36 | spi_device_handle_t _XPT_Handle; 37 | bool _use_frame_buffer; 38 | bool _use_frame_buffer_evacuate; 39 | uint16_t *_frame_buffer; 40 | bool _calibration; 41 | int16_t _min_xp; // Minimum xp calibration 42 | int16_t _min_yp; // Minimum yp calibration 43 | int16_t _max_xp; // Maximum xp calibration 44 | int16_t _max_yp; // Maximum yp calibration 45 | int16_t _check1_xp; // increasing direction 46 | int16_t _check1_yp; // increasing direction 47 | int16_t _check2_xp; // increasing direction 48 | int16_t _check2_yp; // increasing direction 49 | int16_t _min_xc; // Minimum x coordinate 50 | int16_t _min_yc; // Minimum y coordinate 51 | int16_t _max_xc; // Maximum x coordinate 52 | int16_t _max_yc; // Maximum y coordinate 53 | } TFT_t; 54 | 55 | void spi_clock_speed(int speed); 56 | void spi_master_init(TFT_t * dev, int16_t TFT_MOSI, int16_t TFT_SCLK, int16_t TFT_CS, int16_t GPIO_DC, int16_t GPIO_RESET, int16_t GPIO_BL, 57 | int16_t XPT_MISO, int16_t XPT_CS, int16_t XPT_IRQ, int16_t XPT_SCLK, int16_t XPT_MOSI); 58 | bool spi_master_write_byte(spi_device_handle_t SPIHandle, const uint8_t* Data, size_t DataLength); 59 | bool spi_master_write_comm_byte(TFT_t * dev, uint8_t cmd); 60 | bool spi_master_write_comm_word(TFT_t * dev, uint16_t cmd); 61 | bool spi_master_write_data_byte(TFT_t * dev, uint8_t data); 62 | bool spi_master_write_data_word(TFT_t * dev, uint16_t data); 63 | bool spi_master_write_addr(TFT_t * dev, uint16_t addr1, uint16_t addr2); 64 | bool spi_master_write_color(TFT_t * dev, uint16_t color, uint16_t size); 65 | bool spi_master_write_colors(TFT_t * dev, uint16_t * colors, uint16_t size); 66 | 67 | void delayMS(int ms); 68 | void lcdWriteRegisterWord(TFT_t * dev, uint16_t addr, uint16_t data); 69 | void lcdWriteRegisterByte(TFT_t * dev, uint8_t addr, uint16_t data); 70 | void lcdInit(TFT_t * dev, uint16_t model, int width, int height, int offsetx, int offsety); 71 | bool lcdIsFrameBuffer(TFT_t * dev); 72 | void lcdDisableFrameBuffer(TFT_t * dev); 73 | void lcdResumeFrameBuffer(TFT_t * dev); 74 | void lcdGetFrameBuffer(TFT_t * dev, uint16_t *buffer); 75 | void lcdSetFrameBuffer(TFT_t * dev, uint16_t *buffer); 76 | void lcdDrawPixel(TFT_t * dev, uint16_t x, uint16_t y, uint16_t color); 77 | void lcdDrawMultiPixels(TFT_t * dev, uint16_t x, uint16_t y, uint16_t size, uint16_t * colors); 78 | void lcdDrawFillRect(TFT_t * dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); 79 | void lcdDrawFillRect2(TFT_t * dev, uint16_t x0, uint16_t y0, uint16_t size, uint16_t color); 80 | void lcdDisplayOff(TFT_t * dev); 81 | void lcdDisplayOn(TFT_t * dev); 82 | void lcdInversionOff(TFT_t * dev); 83 | void lcdInversionOn(TFT_t * dev); 84 | void lcdBGRFilter(TFT_t * dev); 85 | void lcdFillScreen(TFT_t * dev, uint16_t color); 86 | void lcdDrawLine(TFT_t * dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); 87 | void lcdDrawRect(TFT_t * dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); 88 | void lcdDrawRect2(TFT_t * dev, uint16_t x0, uint16_t y0, uint16_t size, uint16_t color); 89 | void lcdDrawRectAngle(TFT_t * dev, uint16_t xc, uint16_t yc, uint16_t w, uint16_t h, uint16_t angle, uint16_t color); 90 | void lcdDrawTriangle(TFT_t * dev, uint16_t xc, uint16_t yc, uint16_t w, uint16_t h, uint16_t angle, uint16_t color); 91 | void lcdDrawRegularPolygon(TFT_t * dev, uint16_t xc, uint16_t yc, uint16_t n, uint16_t r, uint16_t angle, uint16_t color); 92 | void lcdDrawCircle(TFT_t * dev, uint16_t x0, uint16_t y0, uint16_t r, uint16_t color); 93 | void lcdDrawFillCircle(TFT_t * dev, uint16_t x0, uint16_t y0, uint16_t r, uint16_t color); 94 | void lcdDrawRoundRect(TFT_t * dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t r, uint16_t color); 95 | void lcdDrawArrow(TFT_t * dev, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t w, uint16_t color); 96 | void lcdDrawFillArrow(TFT_t * dev, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t w, uint16_t color); 97 | int lcdDrawChar(TFT_t * dev, FontxFile *fx, uint16_t x, uint16_t y, uint8_t ascii, uint16_t color); 98 | int lcdDrawString(TFT_t * dev, FontxFile *fx, uint16_t x, uint16_t y, uint8_t * ascii, uint16_t color); 99 | int lcdDrawCode(TFT_t * dev, FontxFile *fx, uint16_t x,uint16_t y,uint8_t code,uint16_t color); 100 | //int lcdDrawSJISChar(TFT_t * dev, FontxFile *fx, uint16_t x, uint16_t y, uint16_t sjis, uint16_t color); 101 | //int lcdDrawUTF8Char(TFT_t * dev, FontxFile *fx, uint16_t x, uint16_t y, uint8_t *utf8, uint16_t color); 102 | //int lcdDrawUTF8String(TFT_t * dev, FontxFile *fx, uint16_t x, uint16_t y, unsigned char *utfs, uint16_t color); 103 | void lcdSetFontDirection(TFT_t * dev, uint16_t); 104 | void lcdSetFontFill(TFT_t * dev, uint16_t color); 105 | void lcdUnsetFontFill(TFT_t * dev); 106 | void lcdSetFontUnderLine(TFT_t * dev, uint16_t color); 107 | void lcdUnsetFontUnderLine(TFT_t * dev); 108 | void lcdBacklightOff(TFT_t * dev); 109 | void lcdBacklightOn(TFT_t * dev); 110 | void lcdSetScrollArea(TFT_t * dev, uint16_t tfa, uint16_t vsa, uint16_t bfa); 111 | void lcdResetScrollArea(TFT_t * dev, uint16_t vsa); 112 | void lcdScroll(TFT_t * dev, uint16_t vsp); 113 | void lcdDrawFinish(TFT_t *dev); 114 | int xptGetit(TFT_t * dev, int cmd); 115 | bool touch_getxy(TFT_t *dev, int *xp, int *yp); 116 | #endif /* MAIN_ILI9340_H_ */ 117 | 118 | -------------------------------------------------------------------------------- /main/decode_jpeg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "decode_jpeg.h" 3 | #include "esp32/rom/tjpgd.h" 4 | #include "esp_log.h" 5 | 6 | //Data that is passed from the decoder function to the infunc/outfunc functions. 7 | typedef struct { 8 | pixel_jpeg **outData; // Array of IMAGE_H pointers to arrays of 16-bit pixel values 9 | int screenWidth; // Width of the screen 10 | int screenHeight; // Height of the screen 11 | FILE* fp; // File pointer of jpeg file 12 | } JpegDev; 13 | 14 | 15 | //Input function for jpeg decoder. Just returns bytes from the inData field of the JpegDev structure. 16 | static UINT infunc(JDEC *decoder, BYTE *buf, UINT len) { 17 | JpegDev *jd = (JpegDev *) decoder->device; 18 | ESP_LOGD(__FUNCTION__, "infunc len=%d fp=%p", len, jd->fp); 19 | int rlen; 20 | if (buf != NULL) { /* Read nd bytes from the input strem */ 21 | rlen = fread(buf, 1, len, jd->fp); 22 | ESP_LOGD(__FUNCTION__, "rlen=%d",rlen); 23 | } else { /* Skip nd bytes on the input stream */ 24 | ESP_LOGD(__FUNCTION__, "buff is NULL"); 25 | fseek(jd->fp, len, SEEK_CUR); 26 | rlen = len; 27 | } 28 | return rlen; 29 | } 30 | 31 | #define rgb565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)) 32 | 33 | //Output function. Re-encodes the RGB888 data from the decoder as big-endian RGB565 and 34 | //stores it in the outData array of the JpegDev structure. 35 | static UINT outfunc(JDEC *decoder, void *bitmap, JRECT *rect) { 36 | JpegDev *jd = (JpegDev *) decoder->device; 37 | uint8_t *in = (uint8_t *) bitmap; 38 | ESP_LOGD(__FUNCTION__, "rect->top=%d rect->bottom=%d", rect->top, rect->bottom); 39 | ESP_LOGD(__FUNCTION__, "rect->left=%d rect->right=%d", rect->left, rect->right); 40 | ESP_LOGD(__FUNCTION__, "jd->screenWidth=%d jd->screenHeight=%d", jd->screenWidth, jd->screenHeight); 41 | 42 | for (int y = rect->top; y <= rect->bottom; y++) { 43 | for (int x = rect->left; x <= rect->right; x++) { 44 | 45 | if (y < jd->screenHeight && x < jd->screenWidth) { 46 | #if 0 47 | jd->outData[y][x].red = in[0]; 48 | jd->outData[y][x].green = in[1]; 49 | jd->outData[y][x].blue = in[2]; 50 | #endif 51 | jd->outData[y][x] = rgb565(in[0], in[1], in[2]); 52 | } 53 | 54 | in += 3; 55 | } 56 | } 57 | return 1; 58 | } 59 | 60 | // Specifies scaling factor N for output. The output image is descaled to 1 / 2 ^ N (N = 0 to 3). 61 | // When scaling feature is disabled (JD_USE_SCALE == 0), it must be 0. 62 | uint8_t getScale(uint16_t screenWidth, uint16_t screenHeight, uint16_t imageWidth, uint16_t imageHeight) { 63 | if (screenWidth >= imageWidth && screenHeight >= imageHeight) return 0; 64 | 65 | double scaleWidth = (double)imageWidth / (double)screenWidth; 66 | double scaleHeight = (double)imageHeight / (double)screenHeight; 67 | ESP_LOGD(__FUNCTION__, "scaleWidth=%f scaleHeight=%f", scaleWidth, scaleHeight); 68 | double scale = scaleWidth; 69 | if (scaleWidth < scaleHeight) scale = scaleHeight; 70 | ESP_LOGD(__FUNCTION__, "scale=%f", scale); 71 | if (scale <= 2.0) return 1; 72 | if (scale <= 4.0) return 2; 73 | return 3; 74 | 75 | } 76 | 77 | //Size of the work space for the jpeg decoder. 78 | #define WORKSZ 3100 79 | 80 | //Decode the embedded image into pixel lines that can be used with the rest of the logic. 81 | esp_err_t decode_jpeg(pixel_jpeg ***pixels, char * file, uint16_t width, uint16_t height, uint16_t * imageWidth, uint16_t * imageHeight) { 82 | char *work = NULL; 83 | int r; 84 | JDEC decoder; 85 | JpegDev jd; 86 | *pixels = NULL; 87 | esp_err_t ret = ESP_OK; 88 | 89 | 90 | //Alocate pixel memory. Each line is an array of IMAGE_W 16-bit pixels; the `*pixels` array itself contains pointers to these lines. 91 | *pixels = calloc(height, sizeof(pixel_jpeg *)); 92 | if (*pixels == NULL) { 93 | ESP_LOGE(__FUNCTION__, "Error allocating memory for lines"); 94 | ret = ESP_ERR_NO_MEM; 95 | goto err; 96 | } 97 | for (int i = 0; i < height; i++) { 98 | (*pixels)[i] = malloc(width * sizeof(pixel_jpeg)); 99 | if ((*pixels)[i] == NULL) { 100 | ESP_LOGE(__FUNCTION__, "Error allocating memory for line %d", i); 101 | ret = ESP_ERR_NO_MEM; 102 | goto err; 103 | } 104 | } 105 | 106 | //Allocate the work space for the jpeg decoder. 107 | work = calloc(WORKSZ, 1); 108 | if (work == NULL) { 109 | ESP_LOGE(__FUNCTION__, "Cannot allocate workspace"); 110 | ret = ESP_ERR_NO_MEM; 111 | goto err; 112 | } 113 | 114 | 115 | //Populate fields of the JpegDev struct. 116 | jd.outData = *pixels; 117 | jd.screenWidth = width; 118 | jd.screenHeight = height; 119 | jd.fp = fopen(file, "rb"); 120 | if (jd.fp == NULL) { 121 | ESP_LOGW(__FUNCTION__, "Image file not found [%s]", file); 122 | ret = ESP_ERR_NOT_FOUND; 123 | goto err; 124 | } 125 | ESP_LOGD(__FUNCTION__, "jd.fp=%p", jd.fp); 126 | 127 | //Prepare and decode the jpeg. 128 | r = jd_prepare(&decoder, infunc, work, WORKSZ, (void *) &jd); 129 | if (r != JDR_OK) { 130 | ESP_LOGE(__FUNCTION__, "Image decoder: jd_prepare failed (%d)", r); 131 | ret = ESP_ERR_NOT_SUPPORTED; 132 | goto err; 133 | } 134 | ESP_LOGD(__FUNCTION__, "decoder.width=%d decoder.height=%d", decoder.width, decoder.height); 135 | 136 | //Calculate Scaling factor 137 | uint8_t scale = getScale(width, height, decoder.width, decoder.height); 138 | ESP_LOGD(__FUNCTION__, "scale=%d", scale); 139 | 140 | //Calculate image size 141 | double factor = 1.0; 142 | if (scale == 1) factor = 0.5; 143 | if (scale == 2) factor = 0.25; 144 | if (scale == 3) factor = 0.125; 145 | ESP_LOGD(__FUNCTION__, "factor=%f",factor); 146 | *imageWidth = (double)decoder.width * factor; 147 | *imageHeight = (double)decoder.height * factor; 148 | ESP_LOGD(__FUNCTION__, "imageWidth=%d imageHeight=%d", *imageWidth, *imageHeight); 149 | 150 | 151 | r = jd_decomp(&decoder, outfunc, scale); 152 | if (r != JDR_OK) { 153 | ESP_LOGE(__FUNCTION__, "Image decoder: jd_decode failed (%d)", r); 154 | ret = ESP_ERR_NOT_SUPPORTED; 155 | goto err; 156 | } 157 | 158 | //All done! Free the work area (as we don't need it anymore) and return victoriously. 159 | free(work); 160 | fclose(jd.fp); 161 | return ret; 162 | 163 | //Something went wrong! Exit cleanly, de-allocating everything we allocated. 164 | err: 165 | fclose(jd.fp); 166 | if (*pixels != NULL) { 167 | for (int i = 0; i < height; i++) { 168 | free((*pixels)[i]); 169 | } 170 | free(*pixels); 171 | } 172 | free(work); 173 | return ret; 174 | } 175 | 176 | 177 | esp_err_t release_image(pixel_jpeg ***pixels, uint16_t width, uint16_t height) { 178 | if (*pixels != NULL) { 179 | for (int i = 0; i < height; i++) { 180 | free((*pixels)[i]); 181 | } 182 | free(*pixels); 183 | } 184 | return ESP_OK; 185 | } 186 | 187 | -------------------------------------------------------------------------------- /main/decode_jpeg_v5.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "freertos/FreeRTOS.h" 4 | #include "freertos/task.h" 5 | #include "decode_jpeg.h" 6 | #include "esp_rom_caps.h" 7 | #include "esp_log.h" 8 | 9 | #if CONFIG_JD_USE_ROM 10 | /* When supported in ROM, use ROM functions */ 11 | #if defined(ESP_ROM_HAS_JPEG_DECODE) 12 | #include "rom/tjpgd.h" 13 | #define JPEG "rom tjpgd" 14 | /* The ROM code of TJPGD is older and has different return type in decode callback */ 15 | typedef unsigned int jpeg_decode_out_t; 16 | #else 17 | #error Using JPEG decoder from ROM is not supported for selected target. Please select external code in menuconfig. 18 | #endif 19 | 20 | #else 21 | /* When Tiny JPG Decoder is not in ROM or selected external code */ 22 | #include "tjpgd.h" 23 | #define JPEG "external tjpgd" 24 | /* The TJPGD outside the ROM code is newer and has different return type in decode callback */ 25 | typedef int jpeg_decode_out_t; 26 | #endif 27 | 28 | //Data that is passed from the decoder function to the infunc/outfunc functions. 29 | typedef struct { 30 | pixel_jpeg **outData; // Array of IMAGE_H pointers to arrays of 16-bit pixel values 31 | int screenWidth; // Width of the screen 32 | int screenHeight; // Height of the screen 33 | FILE* fp; // File pointer of jpeg file 34 | } JpegDev; 35 | 36 | 37 | //Input function for jpeg decoder. Just returns bytes from the inData field of the JpegDev structure. 38 | static unsigned int infunc(JDEC *decoder, uint8_t *buf, unsigned int len) { 39 | JpegDev *jd = (JpegDev *) decoder->device; 40 | ESP_LOGD(__FUNCTION__, "infunc len=%d fp=%p", len, jd->fp); 41 | int rlen; 42 | if (buf != NULL) { /* Read nd bytes from the input strem */ 43 | rlen = fread(buf, 1, len, jd->fp); 44 | ESP_LOGD(__FUNCTION__, "rlen=%d",rlen); 45 | } else { /* Skip nd bytes on the input stream */ 46 | ESP_LOGD(__FUNCTION__, "buff is NULL"); 47 | fseek(jd->fp, len, SEEK_CUR); 48 | rlen = len; 49 | } 50 | return rlen; 51 | } 52 | 53 | #define rgb565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)) 54 | 55 | //Output function. Re-encodes the RGB888 data from the decoder as big-endian RGB565 and 56 | //stores it in the outData array of the JpegDev structure. 57 | static jpeg_decode_out_t outfunc(JDEC *decoder, void *bitmap, JRECT *rect) { 58 | JpegDev *jd = (JpegDev *) decoder->device; 59 | uint8_t *in = (uint8_t *) bitmap; 60 | ESP_LOGD(__FUNCTION__, "rect->top=%d rect->bottom=%d", rect->top, rect->bottom); 61 | ESP_LOGD(__FUNCTION__, "rect->left=%d rect->right=%d", rect->left, rect->right); 62 | ESP_LOGD(__FUNCTION__, "jd->screenWidth=%d jd->screenHeight=%d", jd->screenWidth, jd->screenHeight); 63 | 64 | for (int y = rect->top; y <= rect->bottom; y++) { 65 | for (int x = rect->left; x <= rect->right; x++) { 66 | 67 | if (y < jd->screenHeight && x < jd->screenWidth) { 68 | #if 0 69 | jd->outData[y][x].red = in[0]; 70 | jd->outData[y][x].green = in[1]; 71 | jd->outData[y][x].blue = in[2]; 72 | #endif 73 | jd->outData[y][x] = rgb565(in[0], in[1], in[2]); 74 | } 75 | 76 | in += 3; 77 | } 78 | } 79 | return 1; 80 | } 81 | 82 | // Specifies scaling factor N for output. The output image is descaled to 1 / 2 ^ N (N = 0 to 3). 83 | // When scaling feature is disabled (JD_USE_SCALE == 0), it must be 0. 84 | uint8_t getScale(int screenWidth, int screenHeight, uint16_t decodeWidth, uint16_t decodeHeight) { 85 | if (screenWidth >= decodeWidth && screenHeight >= decodeHeight) return 0; 86 | 87 | double scaleWidth = (double)decodeWidth / (double)screenWidth; 88 | double scaleHeight = (double)decodeHeight / (double)screenHeight; 89 | ESP_LOGD(__FUNCTION__, "scaleWidth=%f scaleHeight=%f", scaleWidth, scaleHeight); 90 | double scale = scaleWidth; 91 | if (scaleWidth < scaleHeight) scale = scaleHeight; 92 | ESP_LOGD(__FUNCTION__, "scale=%f", scale); 93 | if (scale <= 2.0) return 1; 94 | if (scale <= 4.0) return 2; 95 | return 3; 96 | 97 | } 98 | 99 | //Decode the embedded image into pixel lines that can be used with the rest of the logic. 100 | esp_err_t decode_jpeg(pixel_jpeg ***pixels, char * file, int screenWidth, int screenHeight, int * imageWidth, int * imageHeight) { 101 | char *work = NULL; 102 | JDEC decoder; 103 | JpegDev jd; 104 | *pixels = NULL; 105 | esp_err_t ret = ESP_OK; 106 | jd.fp = NULL; 107 | 108 | ESP_LOGW(__FUNCTION__, "v5 version. JPEG Decoder is %s", JPEG); 109 | //Alocate pixel memory. Each line is an array of IMAGE_W 16-bit pixels; the `*pixels` array itself contains pointers to these lines. 110 | *pixels = calloc(screenHeight, sizeof(pixel_jpeg *)); 111 | if (*pixels == NULL) { 112 | ESP_LOGE(__FUNCTION__, "Error allocating memory for lines"); 113 | ret = ESP_ERR_NO_MEM; 114 | goto err; 115 | } 116 | for (int i = 0; i < screenHeight; i++) { 117 | (*pixels)[i] = malloc(screenWidth * sizeof(pixel_jpeg)); 118 | if ((*pixels)[i] == NULL) { 119 | ESP_LOGE(__FUNCTION__, "Error allocating memory for line %d", i); 120 | ret = ESP_ERR_NO_MEM; 121 | goto err; 122 | } 123 | } 124 | 125 | //Allocate the work space for the jpeg decoder. 126 | uint32_t free_heap_size = esp_get_free_heap_size(); 127 | ESP_LOGI(__FUNCTION__, "esp_get_free_heap_size=%"PRIu32, free_heap_size); 128 | //uint32_t jd_work_size = free_heap_size/2; 129 | 130 | //Get the largest free block of memory able to be allocated with the given capabilities. 131 | size_t largest_free_block = heap_caps_get_largest_free_block(MALLOC_CAP_EXEC); 132 | ESP_LOGI(__FUNCTION__, "largest_free_block=%d", largest_free_block); 133 | uint32_t jd_work_size = largest_free_block; 134 | ESP_LOGI(__FUNCTION__, "jd_work_size=%"PRIu32, jd_work_size); 135 | work = calloc(jd_work_size, 1); 136 | if (work == NULL) { 137 | ESP_LOGE(__FUNCTION__, "Cannot allocate workspace"); 138 | ret = ESP_ERR_NO_MEM; 139 | goto err; 140 | } 141 | 142 | //Populate fields of the JpegDev struct. 143 | jd.outData = *pixels; 144 | jd.screenWidth = screenWidth; 145 | jd.screenHeight = screenHeight; 146 | jd.fp = fopen(file, "rb"); 147 | if (jd.fp == NULL) { 148 | ESP_LOGW(__FUNCTION__, "Image file not found [%s]", file); 149 | ret = ESP_ERR_NOT_FOUND; 150 | goto err; 151 | } 152 | ESP_LOGD(__FUNCTION__, "jd.fp=%p", jd.fp); 153 | 154 | //Prepare and decode the jpeg. 155 | JRESULT res; 156 | res = jd_prepare(&decoder, infunc, work, jd_work_size, &jd); 157 | if (res != JDR_OK) { 158 | ESP_LOGE(__FUNCTION__, "Image decoder: jd_prepare failed (%d)", res); 159 | ret = ESP_ERR_NOT_SUPPORTED; 160 | goto err; 161 | } 162 | ESP_LOGD(__FUNCTION__, "decoder.width=%d decoder.height=%d", decoder.width, decoder.height); 163 | 164 | //Calculate Scaling factor 165 | uint8_t scale = getScale(screenWidth, screenHeight, decoder.width, decoder.height); 166 | ESP_LOGD(__FUNCTION__, "scale=%d", scale); 167 | 168 | //Calculate image size 169 | double factor = 1.0; 170 | if (scale == 1) factor = 0.5; 171 | if (scale == 2) factor = 0.25; 172 | if (scale == 3) factor = 0.125; 173 | ESP_LOGD(__FUNCTION__, "factor=%f",factor); 174 | *imageWidth = (double)decoder.width * factor; 175 | *imageHeight = (double)decoder.height * factor; 176 | ESP_LOGD(__FUNCTION__, "imageWidth=%d imageHeight=%d", *imageWidth, *imageHeight); 177 | 178 | 179 | res = jd_decomp(&decoder, outfunc, scale); 180 | if (res != JDR_OK) { 181 | ESP_LOGE(__FUNCTION__, "Image decoder: jd_decode failed (%d)", res); 182 | ret = ESP_ERR_NOT_SUPPORTED; 183 | goto err; 184 | } 185 | 186 | //All done! Free the work area (as we don't need it anymore) and return victoriously. 187 | free(work); 188 | fclose(jd.fp); 189 | return ret; 190 | 191 | //Something went wrong! Exit cleanly, de-allocating everything we allocated. 192 | err: 193 | ESP_LOGD(__FUNCTION__, "start err"); 194 | if (*pixels != NULL) { 195 | for (int i = 0; i < screenHeight; i++) { 196 | if ((*pixels)[i]) free((*pixels)[i]); 197 | } 198 | free(*pixels); 199 | } 200 | free(work); 201 | if (jd.fp) fclose(jd.fp); 202 | ESP_LOGD(__FUNCTION__, "done err"); 203 | return ret; 204 | } 205 | 206 | 207 | esp_err_t release_image(pixel_jpeg ***pixels, int screenWidth, int screenHeight) { 208 | if (*pixels != NULL) { 209 | for (int i = 0; i < screenHeight; i++) { 210 | free((*pixels)[i]); 211 | } 212 | free(*pixels); 213 | } 214 | return ESP_OK; 215 | } 216 | -------------------------------------------------------------------------------- /components/ili9340/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "TFT Configuration" 2 | 3 | config GPIO_RANGE_MAX 4 | int 5 | default 39 if IDF_TARGET_ESP32 6 | default 46 if IDF_TARGET_ESP32S2 7 | default 48 if IDF_TARGET_ESP32S3 8 | default 18 if IDF_TARGET_ESP32C2 9 | default 19 if IDF_TARGET_ESP32C3 10 | default 30 if IDF_TARGET_ESP32C6 11 | 12 | choice DRIVER 13 | prompt "Display Driver" 14 | default ILI9341 15 | help 16 | Select Display Driver. 17 | config ILI9225 18 | bool "ILI9225" 19 | help 20 | Display Driver is ILI9225. 21 | config ILI9340 22 | bool "ILI9340" 23 | help 24 | Display Driver is ILI9340. 25 | config ILI9341 26 | bool "ILI9341" 27 | help 28 | Display Driver is ILI9341. 29 | config ST7735 30 | bool "ST7735" 31 | help 32 | Display Driver is ST7735. 33 | config ST7789 34 | bool "ST7789" 35 | help 36 | Display Driver is ST7789. 37 | config ST7796 38 | bool "ST7796" 39 | help 40 | Display Driver is ST7796S. 41 | endchoice 42 | 43 | config WIDTH 44 | int "SCREEN WIDTH" 45 | range 0 999 46 | default 320 47 | help 48 | The width resolution of the screen. 49 | 50 | config HEIGHT 51 | int "SCREEN HEIGHT" 52 | range 0 999 53 | default 240 54 | help 55 | The height resolution of the screen. 56 | 57 | config OFFSETX 58 | int "GRAM X OFFSET" 59 | range 0 99 60 | default 0 61 | help 62 | When your TFT have offset(X), set it. 63 | 64 | config OFFSETY 65 | int "GRAM Y OFFSET" 66 | range 0 99 67 | default 0 68 | help 69 | When your TFT have offset(Y), set it. 70 | 71 | config MOSI_GPIO 72 | int "MOSI GPIO number" 73 | range 0 GPIO_RANGE_MAX 74 | default 23 if IDF_TARGET_ESP32 75 | default 35 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 76 | default 0 # C3 and others 77 | help 78 | GPIO number (IOxx) to SPI MOSI. 79 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to MOSI. 80 | On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs. 81 | On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs. 82 | 83 | config SCLK_GPIO 84 | int "SCLK GPIO number" 85 | range 0 GPIO_RANGE_MAX 86 | default 18 if IDF_TARGET_ESP32 87 | default 36 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 88 | default 1 # C3 and others 89 | help 90 | GPIO number (IOxx) to SPI SCLK. 91 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to SCLK. 92 | On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs. 93 | On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs. 94 | 95 | config TFT_CS_GPIO 96 | int "CS GPIO number" 97 | range 0 GPIO_RANGE_MAX 98 | default 14 if IDF_TARGET_ESP32 99 | default 34 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 100 | default 2 # C3 and others 101 | help 102 | GPIO number (IOxx) to TFT CS. 103 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to CS. 104 | On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs. 105 | On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs. 106 | 107 | config DC_GPIO 108 | int "DC GPIO number" 109 | range 0 GPIO_RANGE_MAX 110 | default 27 if IDF_TARGET_ESP32 111 | default 37 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 112 | default 3 # C3 and others 113 | help 114 | GPIO number (IOxx) to SPI DC. 115 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to DC. 116 | On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs. 117 | On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs. 118 | 119 | config RESET_GPIO 120 | int "RESET GPIO number" 121 | range -1 GPIO_RANGE_MAX 122 | default 33 if IDF_TARGET_ESP32 123 | default 38 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 124 | default 4 # C3 and others 125 | help 126 | GPIO number (IOxx) to RESET. 127 | When it is -1, the RESET isn't performed. 128 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to RESET. 129 | On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs. 130 | On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs. 131 | 132 | config BL_GPIO 133 | int "BACKLIGHT GPIO number" 134 | range -1 GPIO_RANGE_MAX 135 | default 32 if IDF_TARGET_ESP32 136 | default 33 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 137 | default 5 # C3 and others 138 | help 139 | GPIO number (IOxx) to BACKLIGHT. 140 | When it is -1, BACKLIGHT isn't performed. 141 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to BACKLIGHT. 142 | On the ESP32, GPIOs 35-39 are input-only so cannot be used as outputs. 143 | On the ESP32-S2, GPIO 46 is input-only so cannot be used as outputs. 144 | 145 | config INVERSION 146 | bool "Enable Display Inversion" 147 | default false 148 | help 149 | Enable Display Inversion. 150 | 151 | config RGB_COLOR 152 | bool "Change BGR filter to RGB filter" 153 | default false 154 | help 155 | Change BGR color filter to RGB color filter. 156 | 157 | config FRAME_BUFFER 158 | bool "Enable Frame Buffer" 159 | depends on !IDF_TARGET_ESP32C2 160 | default false 161 | help 162 | Enable Frame Buffer. 163 | 164 | choice XPT2046 165 | prompt "XPT2046 Touch Contoller" 166 | default XPT2046_DISABLE 167 | help 168 | Select XPT2046 Touch Contoller. 169 | config XPT2046_DISABLE 170 | bool "Disable Touch Contoller" 171 | config XPT2046_ENABLE_SAME_BUS 172 | bool "Enable Touch Contoller using the same SPI bus as TFT" 173 | help 174 | Enable Touch Contoller using the same SPI bus as TFT. 175 | config XPT2046_ENABLE_DIFF_BUS 176 | bool "Enable Touch Contoller using a different SPI bus than TFT" 177 | help 178 | Enable Touch Contoller using a different SPI bus than TFT. 179 | endchoice 180 | 181 | choice SPI_HOST 182 | depends on XPT2046_DISABLE || XPT2046_ENABLE_SAME 183 | prompt "SPI peripheral that controls this bus" 184 | default SPI2_HOST 185 | help 186 | Select SPI peripheral that controls this bus. 187 | config SPI2_HOST 188 | bool "SPI2_HOST" 189 | help 190 | Use SPI2_HOST. This is also called HSPI_HOST. 191 | config SPI3_HOST 192 | depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 193 | bool "SPI3_HOST" 194 | help 195 | USE SPI3_HOST. This is also called VSPI_HOST 196 | endchoice 197 | 198 | 199 | config XPT_MISO_GPIO 200 | depends on XPT2046_ENABLE_SAME_BUS || XPT2046_ENABLE_DIFF_BUS 201 | int "XPT2046 MISO GPIO number" 202 | range 0 GPIO_RANGE_MAX 203 | default 19 if IDF_TARGET_ESP32 204 | default 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 205 | default 6 # C3 and others 206 | help 207 | GPIO number (IOxx) to XPT2046 MISO. 208 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to MISO. 209 | 210 | config XPT_CS_GPIO 211 | depends on XPT2046_ENABLE_SAME_BUS || XPT2046_ENABLE_DIFF_BUS 212 | int "XPT2046 CS GPIO number" 213 | range 0 GPIO_RANGE_MAX 214 | default 21 if IDF_TARGET_ESP32 215 | default 2 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 216 | default 7 # C3 and others 217 | help 218 | GPIO number (IOxx) to XPT2046 CS. 219 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to CS. 220 | 221 | config XPT_IRQ_GPIO 222 | depends on XPT2046_ENABLE_SAME_BUS || XPT2046_ENABLE_DIFF_BUS 223 | int "XPT2046 IRQ GPIO number" 224 | range 0 GPIO_RANGE_MAX 225 | default 22 if IDF_TARGET_ESP32 226 | default 3 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 227 | default 8 # C3 and others 228 | help 229 | GPIO number (IOxx) to XPT2046 IRQ. 230 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to IRQ. 231 | 232 | config XPT_SCLK_GPIO 233 | depends on XPT2046_ENABLE_DIFF_BUS 234 | int "XPT2046 SCLK GPIO number" 235 | range 0 GPIO_RANGE_MAX 236 | default 25 if IDF_TARGET_ESP32 237 | default 4 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 238 | default 9 # C3 and others 239 | help 240 | GPIO number (IOxx) to XPT2046 CLK. 241 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to IRQ. 242 | 243 | config XPT_MOSI_GPIO 244 | depends on XPT2046_ENABLE_DIFF_BUS 245 | int "XPT2046 MOSI GPIO number" 246 | range 0 GPIO_RANGE_MAX 247 | default 26 if IDF_TARGET_ESP32 248 | default 5 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 249 | default 10 # C3 and others 250 | help 251 | GPIO number (IOxx) to XPT2046 CLK. 252 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to IRQ. 253 | 254 | config XPT_ACCURACY 255 | depends on XPT2046_ENABLE_SAME_BUS || XPT2046_ENABLE_DIFF_BUS 256 | int "Touch position accuracy" 257 | range 2 10 258 | default 5 259 | help 260 | Tolerance of touch position. 261 | If the difference from the previous position is within this range, it is considered valid. 262 | The higher the value, the less accurate the position, but the less responsive it is. 263 | 264 | config SAVE_CALIBRATION 265 | depends on XPT2046_ENABLE_SAME_BUS || XPT2046_ENABLE_DIFF_BUS 266 | bool "Save calibration data to NVS" 267 | default false 268 | help 269 | Save calibration data to NVS. 270 | 271 | config XPT_CHECK 272 | depends on XPT2046_ENABLE_SAME_BUS || XPT2046_ENABLE_DIFF_BUS 273 | bool "Enable XPT2046 Touch Check" 274 | default false 275 | help 276 | Enable XPT2046 Touch Check. 277 | 278 | endmenu 279 | -------------------------------------------------------------------------------- /components/ili9340/fontx.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "esp_err.h" 8 | #include "esp_log.h" 9 | //#include "esp_spiffs.h" 10 | 11 | #include "fontx.h" 12 | 13 | #define FontxDebug 0 // for Debug 14 | 15 | // フォントファイルパスを構造体に保存 16 | void AddFontx(FontxFile *fx, const char *path) 17 | { 18 | memset(fx, 0, sizeof(FontxFile)); 19 | fx->path = path; 20 | fx->opened = false; 21 | } 22 | 23 | // フォント構造体を初期化 24 | void InitFontx(FontxFile *fxs, const char *f0, const char *f1) 25 | { 26 | AddFontx(&fxs[0], f0); 27 | AddFontx(&fxs[1], f1); 28 | } 29 | 30 | // フォントファイルをOPEN 31 | bool OpenFontx(FontxFile *fx) 32 | { 33 | FILE *f; 34 | if(!fx->opened){ 35 | if(FontxDebug)printf("[openFont]fx->path=[%s]\n",fx->path); 36 | f = fopen(fx->path, "r"); 37 | if(FontxDebug)printf("[openFont]fopen=%p\n",f); 38 | if (f == NULL) { 39 | fx->valid = false; 40 | printf("Fontx:%s not found.\n",fx->path); 41 | return fx->valid ; 42 | } 43 | fx->opened = true; 44 | fx->file = f; 45 | char buf[18]; 46 | if (fread(buf, 1, sizeof(buf), fx->file) != sizeof(buf)) { 47 | fx->valid = false; 48 | printf("Fontx:%s not FONTX format.\n",fx->path); 49 | fclose(fx->file); 50 | return fx->valid ; 51 | } 52 | 53 | if(FontxDebug) { 54 | for(int i=0;ifxname, &buf[6], 8); 59 | fx->w = buf[14]; 60 | fx->h = buf[15]; 61 | fx->is_ank = (buf[16] == 0); 62 | fx->bc = buf[17]; 63 | fx->fsz = (fx->w + 7)/8 * fx->h; 64 | if(fx->fsz > FontxGlyphBufSize){ 65 | printf("Fontx:%s is too big font size.\n",fx->path); 66 | fx->valid = false; 67 | fclose(fx->file); 68 | return fx->valid ; 69 | } 70 | fx->valid = true; 71 | } 72 | return fx->valid; 73 | } 74 | 75 | // フォントファイルをCLOSE 76 | void CloseFontx(FontxFile *fx) 77 | { 78 | if(fx->opened){ 79 | fclose(fx->file); 80 | fx->opened = false; 81 | } 82 | } 83 | 84 | // フォント構造体の表示 85 | void DumpFontx(FontxFile *fxs) 86 | { 87 | for(int i=0;i<2;i++) { 88 | printf("fxs[%d]->path=%s\n",i,fxs[i].path); 89 | printf("fxs[%d]->opened=%d\n",i,fxs[i].opened); 90 | printf("fxs[%d]->fxname=%s\n",i,fxs[i].fxname); 91 | printf("fxs[%d]->valid=%d\n",i,fxs[i].valid); 92 | printf("fxs[%d]->is_ank=%d\n",i,fxs[i].is_ank); 93 | printf("fxs[%d]->w=%d\n",i,fxs[i].w); 94 | printf("fxs[%d]->h=%d\n",i,fxs[i].h); 95 | printf("fxs[%d]->fsz=%d\n",i,fxs[i].fsz); 96 | printf("fxs[%d]->bc=%d\n",i,fxs[i].bc); 97 | } 98 | } 99 | 100 | uint8_t getFortWidth(FontxFile *fx) { 101 | printf("fx->w=%d\n",fx->w); 102 | return(fx->w); 103 | } 104 | 105 | uint8_t getFortHeight(FontxFile *fx) { 106 | printf("fx->h=%d\n",fx->h); 107 | return(fx->h); 108 | } 109 | 110 | 111 | /* 112 | フォントファイルからフォントパターンを取り出す 113 | 114 | フォントの並び(16X16ドット) 115 | 00000000 01111111 116 | 12345678 90123456 117 | 01 pGlyph[000] pGlyph[001] 118 | 02 pGlyph[002] pGlyph[003] 119 | 03 pGlyph[004] pGlyph[005] 120 | 04 pGlyph[006] pGlyph[007] 121 | 05 pGlyph[008] pGlyph[009] 122 | 06 pGlyph[010] pGlyph[011] 123 | 07 pGlyph[012] pGlyph[013] 124 | 08 pGlyph[014] pGlyph[015] 125 | 09 pGlyph[016] pGlyph[017] 126 | 10 pGlyph[018] pGlyph[019] 127 | 11 pGlyph[020] pGlyph[021] 128 | 12 pGlyph[022] pGlyph[023] 129 | 13 pGlyph[024] pGlyph[025] 130 | 14 pGlyph[026] pGlyph[027] 131 | 15 pGlyph[028] pGlyph[029] 132 | 16 pGlyph[030] pGlyph[031] 133 | 134 | フォントの並び(24X24ドット) 135 | 00000000 01111111 11122222 136 | 12345678 90123456 78901234 137 | 01 pGlyph[000] pGlyph[001] pGlyph[002] 138 | 02 pGlyph[003] pGlyph[004] pGlyph[005] 139 | 03 pGlyph[006] pGlyph[007] pGlyph[008] 140 | 04 pGlyph[009] pGlyph[010] pGlyph[011] 141 | 05 pGlyph[012] pGlyph[013] pGlyph[014] 142 | 06 pGlyph[015] pGlyph[016] pGlyph[017] 143 | 07 pGlyph[018] pGlyph[019] pGlyph[020] 144 | 08 pGlyph[021] pGlyph[022] pGlyph[023] 145 | 09 pGlyph[024] pGlyph[025] pGlyph[026] 146 | 10 pGlyph[027] pGlyph[028] pGlyph[029] 147 | 11 pGlyph[030] pGlyph[031] pGlyph[032] 148 | 12 pGlyph[033] pGlyph[034] pGlyph[035] 149 | 13 pGlyph[036] pGlyph[037] pGlyph[038] 150 | 14 pGlyph[039] pGlyph[040] pGlyph[041] 151 | 15 pGlyph[042] pGlyph[043] pGlyph[044] 152 | 16 pGlyph[045] pGlyph[046] pGlyph[047] 153 | 17 pGlyph[048] pGlyph[049] pGlyph[050] 154 | 18 pGlyph[051] pGlyph[052] pGlyph[053] 155 | 19 pGlyph[054] pGlyph[055] pGlyph[056] 156 | 20 pGlyph[057] pGlyph[058] pGlyph[059] 157 | 21 pGlyph[060] pGlyph[061] pGlyph[062] 158 | 22 pGlyph[063] pGlyph[064] pGlyph[065] 159 | 23 pGlyph[066] pGlyph[067] pGlyph[068] 160 | 24 pGlyph[069] pGlyph[070] pGlyph[071] 161 | 162 | フォントの並び(32X32ドット) 163 | 00000000 01111111 11122222 22222333 164 | 12345678 90123456 78901234 56789012 165 | 01 pGlyph[000] pGlyph[001] pGlyph[002] pGlyph[003] 166 | 02 pGlyph[004] pGlyph[005] pGlyph[006] pGlyph[007] 167 | 03 pGlyph[008] pGlyph[009] pGlyph[010] pGlyph[011] 168 | 04 pGlyph[012] pGlyph[013] pGlyph[014] pGlyph[015] 169 | 05 pGlyph[016] pGlyph[017] pGlyph[018] pGlyph[019] 170 | 06 pGlyph[020] pGlyph[021] pGlyph[022] pGlyph[023] 171 | 07 pGlyph[024] pGlyph[025] pGlyph[026] pGlyph[027] 172 | 08 pGlyph[028] pGlyph[029] pGlyph[030] pGlyph[031] 173 | 09 pGlyph[032] pGlyph[033] pGlyph[034] pGlyph[035] 174 | 10 pGlyph[036] pGlyph[037] pGlyph[038] pGlyph[039] 175 | 11 pGlyph[040] pGlyph[041] pGlyph[042] pGlyph[043] 176 | 12 pGlyph[044] pGlyph[045] pGlyph[046] pGlyph[047] 177 | 13 pGlyph[048] pGlyph[049] pGlyph[050] pGlyph[051] 178 | 14 pGlyph[052] pGlyph[053] pGlyph[054] pGlyph[055] 179 | 15 pGlyph[056] pGlyph[057] pGlyph[058] pGlyph[059] 180 | 16 pGlyph[060] pGlyph[061] pGlyph[062] pGlyph[063] 181 | 17 pGlyph[064] pGlyph[065] pGlyph[066] pGlyph[067] 182 | 18 pGlyph[068] pGlyph[069] pGlyph[070] pGlyph[071] 183 | 19 pGlyph[072] pGlyph[073] pGlyph[074] pGlyph[075] 184 | 20 pGlyph[076] pGlyph[077] pGlyph[078] pGlyph[079] 185 | 21 pGlyph[080] pGlyph[081] pGlyph[082] pGlyph[083] 186 | 22 pGlyph[084] pGlyph[085] pGlyph[086] pGlyph[087] 187 | 23 pGlyph[088] pGlyph[089] pGlyph[090] pGlyph[091] 188 | 24 pGlyph[092] pGlyph[093] pGlyph[094] pGlyph[095] 189 | 25 pGlyph[096] pGlyph[097] pGlyph[098] pGlyph[099] 190 | 26 pGlyph[100] pGlyph[101] pGlyph[102] pGlyph[103] 191 | 27 pGlyph[104] pGlyph[105] pGlyph[106] pGlyph[107] 192 | 28 pGlyph[108] pGlyph[109] pGlyph[110] pGlyph[111] 193 | 29 pGlyph[112] pGlyph[113] pGlyph[114] pGlyph[115] 194 | 30 pGlyph[116] pGlyph[117] pGlyph[118] pGlyph[119] 195 | 31 pGlyph[120] pGlyph[121] pGlyph[122] pGlyph[123] 196 | 32 pGlyph[124] pGlyph[125] pGlyph[127] pGlyph[128] 197 | 198 | */ 199 | 200 | bool GetFontx(FontxFile *fxs, uint8_t ascii , uint8_t *pGlyph, uint8_t *pw, uint8_t *ph) 201 | { 202 | 203 | int i; 204 | uint32_t offset; 205 | 206 | if(FontxDebug)printf("[GetFontx]ascii=0x%x\n",ascii); 207 | for(i=0; i<2; i++){ 208 | //for(i=0; i<1; i++){ 209 | if(!OpenFontx(&fxs[i])) continue; 210 | if(FontxDebug)printf("[GetFontx]openFontxFile[%d] ok\n",i); 211 | 212 | //if(ascii < 0xFF){ 213 | if(fxs[i].is_ank){ 214 | if(FontxDebug)printf("[GetFontx]fxs.is_ank fxs.fsz=%d\n",fxs[i].fsz); 215 | offset = 17 + ascii * fxs[i].fsz; 216 | if(FontxDebug)printf("[GetFontx]offset=%"PRIu32"\n",offset); 217 | if(fseek(fxs[i].file, offset, SEEK_SET)) { 218 | printf("Fontx:seek(%"PRIu32") failed.\n",offset); 219 | return false; 220 | } 221 | if(fread(pGlyph, 1, fxs[i].fsz, fxs[i].file) != fxs[i].fsz) { 222 | printf("Fontx:fread failed.\n"); 223 | return false; 224 | } 225 | if(pw) *pw = fxs[i].w; 226 | if(ph) *ph = fxs[i].h; 227 | return true; 228 | } 229 | //} 230 | } 231 | return false; 232 | } 233 | 234 | 235 | /* 236 | フォントパターンをビットマップイメージに変換する 237 | 238 | fonts(16X16ドット) 239 | 00000000 01111111 240 | 12345678 90123456 241 | 01 pGlyph[000] pGlyph[001] 242 | 02 pGlyph[002] pGlyph[003] 243 | 03 pGlyph[004] pGlyph[005] 244 | 04 pGlyph[006] pGlyph[007] 245 | 05 pGlyph[008] pGlyph[009] 246 | 06 pGlyph[010] pGlyph[011] 247 | 07 pGlyph[012] pGlyph[013] 248 | 08 pGlyph[014] pGlyph[015] 249 | 09 pGlyph[016] pGlyph[017] 250 | 10 pGlyph[018] pGlyph[019] 251 | 11 pGlyph[020] pGlyph[021] 252 | 12 pGlyph[022] pGlyph[023] 253 | 13 pGlyph[024] pGlyph[025] 254 | 14 pGlyph[026] pGlyph[027] 255 | 15 pGlyph[028] pGlyph[029] 256 | 16 pGlyph[030] pGlyph[031] 257 | 258 | line[32*4] 259 | 01 line[000] line[001] line[002] .... line[014] line[015] line[016-031](Not use) 260 | | 261 | 07 line[000] line[001] line[002] .... line[014] line[015] line[016-031](Not use) 262 | 263 | 08 line[032] line[033] line[034] .... line[046] line[047] line[048-063](Not use) 264 | | 265 | 16 line[032] line[033] line[034] .... line[046] line[047] line[048-063](Not use) 266 | 267 | 268 | 269 | fonts(24X24ドット) 270 | 00000000 01111111 11122222 271 | 12345678 90123456 78901234 272 | 01 pGlyph[000] pGlyph[001] pGlyph[002] 273 | 02 pGlyph[003] pGlyph[004] pGlyph[005] 274 | 03 pGlyph[006] pGlyph[007] pGlyph[008] 275 | 04 pGlyph[009] pGlyph[010] pGlyph[011] 276 | 05 pGlyph[012] pGlyph[013] pGlyph[014] 277 | 06 pGlyph[015] pGlyph[016] pGlyph[017] 278 | 07 pGlyph[018] pGlyph[019] pGlyph[020] 279 | 08 pGlyph[021] pGlyph[022] pGlyph[023] 280 | 09 pGlyph[024] pGlyph[025] pGlyph[026] 281 | 10 pGlyph[027] pGlyph[028] pGlyph[029] 282 | 11 pGlyph[030] pGlyph[031] pGlyph[032] 283 | 12 pGlyph[033] pGlyph[034] pGlyph[035] 284 | 13 pGlyph[036] pGlyph[037] pGlyph[038] 285 | 14 pGlyph[039] pGlyph[040] pGlyph[041] 286 | 15 pGlyph[042] pGlyph[043] pGlyph[044] 287 | 16 pGlyph[045] pGlyph[046] pGlyph[047] 288 | 17 pGlyph[048] pGlyph[049] pGlyph[050] 289 | 18 pGlyph[051] pGlyph[052] pGlyph[053] 290 | 19 pGlyph[054] pGlyph[055] pGlyph[056] 291 | 20 pGlyph[057] pGlyph[058] pGlyph[059] 292 | 21 pGlyph[060] pGlyph[061] pGlyph[062] 293 | 22 pGlyph[063] pGlyph[064] pGlyph[065] 294 | 23 pGlyph[066] pGlyph[067] pGlyph[068] 295 | 24 pGlyph[069] pGlyph[070] pGlyph[071] 296 | 297 | line[32*4] 298 | 01 line[000] line[001] line[002] .... line[022] line[023] line[024-031](Not use) 299 | | 300 | 08 line[000] line[001] line[002] .... line[022] line[023] line[024-031](Not use) 301 | 302 | 09 line[032] line[033] line[034] .... line[054] line[055] line[056-063](Not use) 303 | | 304 | 16 line[032] line[033] line[034] .... line[054] line[055] line[056-063](Not use) 305 | 306 | 17 line[064] line[065] line[066] .... line[086] line[087] line[088-095](Not use) 307 | | 308 | 24 line[064] line[065] line[066] .... line[086] line[087] line[088-095](Not use) 309 | 310 | 311 | fonts(32X32ドット) 312 | 00000000 01111111 11122222 22222333 313 | 12345678 90123456 78901234 56789012 314 | 01 pGlyph[000] pGlyph[001] pGlyph[002] pGlyph[003] 315 | 02 pGlyph[004] pGlyph[005] pGlyph[006] pGlyph[007] 316 | 03 pGlyph[008] pGlyph[009] pGlyph[010] pGlyph[011] 317 | 04 pGlyph[012] pGlyph[013] pGlyph[014] pGlyph[015] 318 | 05 pGlyph[016] pGlyph[017] pGlyph[018] pGlyph[019] 319 | 06 pGlyph[020] pGlyph[021] pGlyph[022] pGlyph[023] 320 | 07 pGlyph[024] pGlyph[025] pGlyph[026] pGlyph[027] 321 | 08 pGlyph[028] pGlyph[029] pGlyph[030] pGlyph[031] 322 | 09 pGlyph[032] pGlyph[033] pGlyph[034] pGlyph[035] 323 | 10 pGlyph[036] pGlyph[037] pGlyph[038] pGlyph[039] 324 | 11 pGlyph[040] pGlyph[041] pGlyph[042] pGlyph[043] 325 | 12 pGlyph[044] pGlyph[045] pGlyph[046] pGlyph[047] 326 | 13 pGlyph[048] pGlyph[049] pGlyph[050] pGlyph[051] 327 | 14 pGlyph[052] pGlyph[053] pGlyph[054] pGlyph[055] 328 | 15 pGlyph[056] pGlyph[057] pGlyph[058] pGlyph[059] 329 | 16 pGlyph[060] pGlyph[061] pGlyph[062] pGlyph[063] 330 | 17 pGlyph[064] pGlyph[065] pGlyph[066] pGlyph[067] 331 | 18 pGlyph[068] pGlyph[069] pGlyph[070] pGlyph[071] 332 | 19 pGlyph[072] pGlyph[073] pGlyph[074] pGlyph[075] 333 | 20 pGlyph[076] pGlyph[077] pGlyph[078] pGlyph[079] 334 | 21 pGlyph[080] pGlyph[081] pGlyph[082] pGlyph[083] 335 | 22 pGlyph[084] pGlyph[085] pGlyph[086] pGlyph[087] 336 | 23 pGlyph[088] pGlyph[089] pGlyph[090] pGlyph[091] 337 | 24 pGlyph[092] pGlyph[093] pGlyph[094] pGlyph[095] 338 | 25 pGlyph[096] pGlyph[097] pGlyph[098] pGlyph[099] 339 | 26 pGlyph[100] pGlyph[101] pGlyph[102] pGlyph[103] 340 | 27 pGlyph[104] pGlyph[105] pGlyph[106] pGlyph[107] 341 | 28 pGlyph[108] pGlyph[109] pGlyph[110] pGlyph[111] 342 | 29 pGlyph[112] pGlyph[113] pGlyph[114] pGlyph[115] 343 | 30 pGlyph[116] pGlyph[117] pGlyph[118] pGlyph[119] 344 | 31 pGlyph[120] pGlyph[121] pGlyph[122] pGlyph[123] 345 | 32 pGlyph[124] pGlyph[125] pGlyph[127] pGlyph[128] 346 | 347 | line[32*4] 348 | 01 line[000] line[001] line[002] .... line[030] line[031] 349 | | 350 | 08 line[000] line[001] line[002] .... line[030] line[031] 351 | 352 | 09 line[032] line[033] line[034] .... line[062] line[063] 353 | | 354 | 16 line[032] line[033] line[034] .... line[062] line[063] 355 | 356 | 17 line[064] line[065] line[066] .... line[094] line[095] 357 | | 358 | 24 line[064] line[065] line[066] .... line[094] line[095] 359 | 360 | 25 line[096] line[097] line[098] .... line[126] line[127] 361 | | 362 | 32 line[096] line[097] line[098] .... line[126] line[127] 363 | 364 | */ 365 | void Font2Bitmap(uint8_t *fonts, uint8_t *line, uint8_t w, uint8_t h, uint8_t inverse) { 366 | int x,y; 367 | for(y=0; y<(h/8); y++){ 368 | for(x=0; x> (x % 8))) line[linep] = line[linep] + (1 << mask); 381 | } 382 | mask--; 383 | if (mask < 0) mask = 7; 384 | fontp += (w + 7)/8; 385 | } 386 | 387 | if (inverse) { 388 | for(y=0; y<(h/8); y++){ 389 | for(x=0; x> (x % 8))) { 429 | printf("*"); 430 | } else { 431 | printf("."); 432 | } 433 | } 434 | printf("\n"); 435 | fpos=fpos+(pw+7)/8; 436 | } 437 | printf("\n"); 438 | } 439 | 440 | // Bitmapの表示 441 | void ShowBitmap(uint8_t *bitmap, uint8_t pw, uint8_t ph) { 442 | int x,y,fpos; 443 | printf("[ShowBitmap pw=%d ph=%d]\n",pw,ph); 444 | #if 0 445 | for (y=0;y<(ph+7)/8;y++) { 446 | for (x=0;x> fpos); 458 | if (bitmap[x+(y/8)*32] & (0x80 >> fpos)) { 459 | printf("*"); 460 | } else { 461 | printf("."); 462 | } 463 | } 464 | printf("\n"); 465 | fpos++; 466 | if (fpos > 7) fpos = 0; 467 | } 468 | printf("\n"); 469 | } 470 | 471 | 472 | // 8ビットデータを反転 473 | uint8_t RotateByte(uint8_t ch1) { 474 | uint8_t ch2 = 0; 475 | int j; 476 | for (j=0;j<8;j++) { 477 | ch2 = (ch2 << 1) + (ch1 & 0x01); 478 | ch1 = ch1 >> 1; 479 | } 480 | return ch2; 481 | } 482 | 483 | 484 | #if 0 485 | // UTF code(3Byte) を SJIS Code(2 Byte) に変換 486 | // https://www.mgo-tec.com/blog-entry-utf8sjis01.html 487 | uint16_t UTF2SJIS(spiffs_file fd, uint8_t *utf8) { 488 | 489 | uint32_t offset = 0; 490 | uint32_t ret; 491 | uint32_t UTF8uint = utf8[0]*256*256 + utf8[1]*256 + utf8[2]; 492 | 493 | if(utf8[0]>=0xC2 && utf8[0]<=0xD1){ //0xB0からS_JISコード実データ。0x00-0xAFまではライセンス文ヘッダなのでそれをカット。 494 | offset = ((utf8[0]*256 + utf8[1])-0xC2A2)*2 + 0xB0; //文字"¢" UTF8コード C2A2~、S_jisコード8191 495 | }else if(utf8[0]==0xE2 && utf8[1]>=0x80){ 496 | offset = (UTF8uint-0xE28090)*2 + 0x1EEC; //文字"‐" UTF8コード E28090~、S_jisコード815D 497 | }else if(utf8[0]==0xE3 && utf8[1]>=0x80){ 498 | offset = (UTF8uint-0xE38080)*2 + 0x9DCC; //スペース UTF8コード E38080~、S_jisコード8140 499 | }else if(utf8[0]==0xE4 && utf8[1]>=0x80){ 500 | offset = (UTF8uint-0xE4B880)*2 + 0x11CCC; //文字"一" UTF8コード E4B880~、S_jisコード88EA 501 | }else if(utf8[0]==0xE5 && utf8[1]>=0x80){ 502 | offset = (UTF8uint-0xE58085)*2 + 0x12BCC; //文字"倅" UTF8コード E58085~、S_jisコード98E4 503 | }else if(utf8[0]==0xE6 && utf8[1]>=0x80){ 504 | offset = (UTF8uint-0xE6808E)*2 + 0x1AAC2; //文字"怎" UTF8コード E6808E~、S_jisコード9C83 505 | }else if(utf8[0]==0xE7 && utf8[1]>=0x80){ 506 | offset = (UTF8uint-0xE78081)*2 + 0x229A6; //文字"瀁" UTF8コード E78081~、S_jisコードE066 507 | }else if(utf8[0]==0xE8 && utf8[1]>=0x80){ 508 | offset = (UTF8uint-0xE88080)*2 + 0x2A8A4; //文字"耀" UTF8コード E88080~、S_jisコード9773 509 | }else if(utf8[0]==0xE9 && utf8[1]>=0x80){ 510 | offset = (UTF8uint-0xE98080)*2 + 0x327A4; //文字"退" UTF8コード E98080~、S_jisコード91DE 511 | }else if(utf8[0]>=0xEF && utf8[1]>=0xBC){ 512 | offset = (UTF8uint-0xEFBC81)*2 + 0x3A6A4; //文字"!" UTF8コード EFBC81~、S_jisコード8149 513 | if(utf8[0]==0xEF && utf8[1]==0xBD && utf8[2]==0x9E){ 514 | offset = 0x3A8DE; // "~" UTF8コード EFBD9E、S_jisコード8160 515 | } 516 | } 517 | 518 | if(FontxDebug)printf("[UTF2SJIS] offset=%d\n",offset); 519 | char buf[2]; 520 | ret = SPIFFS_lseek(&fs, fd, offset, SPIFFS_SEEK_SET); 521 | if(FontxDebug)printf("[UTF2SJIS] lseek ret=%d\n",ret); 522 | if (ret != offset) { 523 | printf("UTF2SJIS:seek(%u) failed.\n",offset); 524 | return 0; 525 | } 526 | if (SPIFFS_read(&fs, fd, buf, sizeof(buf)) != sizeof(buf)) { 527 | printf("UTF2SJIS:read failed.\n"); 528 | return 0; 529 | } 530 | if(FontxDebug)printf("[UTF2SJIS] sjis=0x%x%x\n",buf[0],buf[1]); 531 | return buf[0]*256+buf[1]; 532 | } 533 | 534 | 535 | // UTFを含む文字列をSJISに変換 536 | int String2SJIS(spiffs_file fd, unsigned char *str_in, size_t stlen, 537 | uint16_t *sjis, size_t ssize) { 538 | int i; 539 | uint8_t sp; 540 | uint8_t c1 = 0; 541 | uint8_t c2 = 0; 542 | uint8_t utf8[3]; 543 | uint16_t sjis2; 544 | int spos = 0; 545 | 546 | for(i=0;i 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "esp_log.h" 32 | #include "miniz.h" 33 | #include "pngle.h" 34 | 35 | #define PNGLE_ERROR(s) (pngle->error = (s), pngle->state = PNGLE_STATE_ERROR, -1) 36 | #define PNGLE_CALLOC(a, b, name) (debug_printf("[pngle] Allocating %zu bytes for %s\n", (size_t)(a) * (size_t)(b), (name)), calloc((size_t)(a), (size_t)(b))) 37 | 38 | #define PNGLE_UNUSED(x) (void)(x) 39 | 40 | // magic 41 | static const uint8_t png_sig[] = { 137, 80, 78, 71, 13, 10, 26, 10 }; 42 | static uint32_t interlace_off_x[8] = { 0, 0, 4, 0, 2, 0, 1, 0 }; 43 | static uint32_t interlace_off_y[8] = { 0, 0, 0, 4, 0, 2, 0, 1 }; 44 | static uint32_t interlace_div_x[8] = { 1, 8, 8, 4, 4, 2, 2, 1 }; 45 | static uint32_t interlace_div_y[8] = { 1, 8, 8, 8, 4, 4, 2, 2 }; 46 | 47 | 48 | static inline uint8_t read_uint8(const uint8_t *p) 49 | { 50 | return *p; 51 | } 52 | 53 | static inline uint32_t read_uint32(const uint8_t *p) 54 | { 55 | return (p[0] << 24) 56 | | (p[1] << 16) 57 | | (p[2] << 8) 58 | | (p[3] << 0) 59 | ; 60 | } 61 | 62 | static inline uint32_t U32_CLAMP_ADD(uint32_t a, uint32_t b, uint32_t top) 63 | { 64 | uint32_t v = a + b; 65 | if (v < a) return top; // uint32 overflow 66 | if (v > top) return top; // clamp 67 | return v; 68 | } 69 | 70 | 71 | void pngle_reset(pngle_t *pngle) 72 | { 73 | if (!pngle) return ; 74 | 75 | pngle->state = PNGLE_STATE_INITIAL; 76 | pngle->error = "No error"; 77 | 78 | if (pngle->scanline_ringbuf) free(pngle->scanline_ringbuf); 79 | if (pngle->palette) free(pngle->palette); 80 | if (pngle->trans_palette) free(pngle->trans_palette); 81 | #ifndef PNGLE_NO_GAMMA_CORRECTION 82 | if (pngle->gamma_table) free(pngle->gamma_table); 83 | #endif 84 | 85 | pngle->scanline_ringbuf = NULL; 86 | pngle->palette = NULL; 87 | pngle->trans_palette = NULL; 88 | #ifndef PNGLE_NO_GAMMA_CORRECTION 89 | pngle->gamma_table = NULL; 90 | #endif 91 | 92 | pngle->channels = 0; // indicates IHDR hasn't been processed yet 93 | pngle->next_out = NULL; // indicates IDAT hasn't been processed yet 94 | 95 | // clear them just in case... 96 | memset(&pngle->hdr, 0, sizeof(pngle->hdr)); 97 | pngle->n_palettes = 0; 98 | pngle->n_trans_palettes = 0; 99 | 100 | tinfl_init(&pngle->inflator); 101 | } 102 | 103 | pngle_t *pngle_new(uint16_t width, uint16_t height) 104 | { 105 | pngle_t *pngle = (pngle_t *)PNGLE_CALLOC(1, sizeof(pngle_t), "pngle_t"); 106 | if (!pngle) return NULL; 107 | 108 | pngle_reset(pngle); 109 | 110 | pngle->pixels = NULL; 111 | 112 | //Alocate pixel memory. Each line is an array of IMAGE_W 16-bit pixels; the `*pixels` array itself contains pointers to these lines. 113 | ESP_LOGD(__FUNCTION__, "height=%d sizeof(pixel_png *)=%d", height, sizeof(pixel_png *)); 114 | pngle->pixels = calloc(height, sizeof(pixel_png *)); 115 | if (pngle->pixels == NULL) { 116 | ESP_LOGE(__FUNCTION__, "Error allocating memory for lines"); 117 | //ret = ESP_ERR_NO_MEM; 118 | goto err; 119 | } 120 | ESP_LOGD(__FUNCTION__, "width=%d sizeof(pixel_png)=%d", width, sizeof(pixel_png)); 121 | for (int i = 0; i < height; i++) { 122 | (pngle->pixels)[i] = malloc(width * sizeof(pixel_png)); 123 | if ((pngle->pixels)[i] == NULL) { 124 | ESP_LOGE(__FUNCTION__, "Error allocating memory for line %d", i); 125 | //ret = ESP_ERR_NO_MEM; 126 | goto err; 127 | } 128 | } 129 | 130 | pngle->screenWidth = width; 131 | pngle->screenHeight = height; 132 | return pngle; 133 | 134 | err: 135 | //Something went wrong! Exit cleanly, de-allocating everything we allocated. 136 | if (pngle->pixels != NULL) { 137 | for (int i = 0; i < height; i++) { 138 | if ((pngle->pixels)[i]) free((pngle->pixels)[i]); 139 | } 140 | free(pngle->pixels); 141 | } 142 | return NULL; 143 | } 144 | 145 | void pngle_destroy(pngle_t *pngle, uint16_t width, uint16_t height) 146 | { 147 | if (pngle) { 148 | if (pngle->pixels != NULL) { 149 | for (int i = 0; i < height; i++) { 150 | free((pngle->pixels)[i]); 151 | } 152 | free(pngle->pixels); 153 | } 154 | pngle_reset(pngle); 155 | free(pngle); 156 | } 157 | } 158 | 159 | const char *pngle_error(pngle_t *pngle) 160 | { 161 | if (!pngle) return "Uninitialized"; 162 | return pngle->error; 163 | } 164 | 165 | uint32_t pngle_get_width(pngle_t *pngle) 166 | { 167 | if (!pngle) return 0; 168 | return pngle->hdr.width; 169 | } 170 | 171 | uint32_t pngle_get_height(pngle_t *pngle) 172 | { 173 | if (!pngle) return 0; 174 | return pngle->hdr.height; 175 | } 176 | 177 | pngle_ihdr_t *pngle_get_ihdr(pngle_t *pngle) 178 | { 179 | if (!pngle) return NULL; 180 | if (pngle->channels == 0) return NULL; 181 | return &pngle->hdr; 182 | } 183 | 184 | 185 | static int is_trans_color(pngle_t *pngle, uint16_t *value, size_t n) 186 | { 187 | if (pngle->n_trans_palettes != 1) return 0; // false (none or indexed) 188 | 189 | for (size_t i = 0; i < n; i++) { 190 | if (value[i] != (pngle->trans_palette[i * 2 + 0] * 0x100 + pngle->trans_palette[i * 2 + 1])) return 0; // false 191 | } 192 | return 1; // true 193 | } 194 | 195 | static inline void scanline_ringbuf_push(pngle_t *pngle, uint8_t value) 196 | { 197 | pngle->scanline_ringbuf[pngle->scanline_ringbuf_cidx] = value; 198 | pngle->scanline_ringbuf_cidx = (pngle->scanline_ringbuf_cidx + 1) % pngle->scanline_ringbuf_size; 199 | } 200 | 201 | static inline uint16_t get_value(pngle_t *pngle, size_t *ridx, int *bitcount, int depth) 202 | { 203 | uint16_t v; 204 | 205 | switch (depth) { 206 | case 1: 207 | case 2: 208 | case 4: 209 | if (*bitcount >= 8) { 210 | *bitcount = 0; 211 | *ridx = (*ridx + 1) % pngle->scanline_ringbuf_size; 212 | } 213 | *bitcount += depth; 214 | uint8_t mask = ((1UL << depth) - 1); 215 | uint8_t shift = (8 - *bitcount); 216 | return (pngle->scanline_ringbuf[*ridx] >> shift) & mask; 217 | 218 | case 8: 219 | v = pngle->scanline_ringbuf[*ridx]; 220 | *ridx = (*ridx + 1) % pngle->scanline_ringbuf_size; 221 | return v; 222 | 223 | case 16: 224 | v = pngle->scanline_ringbuf[*ridx]; 225 | *ridx = (*ridx + 1) % pngle->scanline_ringbuf_size; 226 | 227 | v = v * 0x100 + pngle->scanline_ringbuf[*ridx]; 228 | *ridx = (*ridx + 1) % pngle->scanline_ringbuf_size; 229 | return v; 230 | } 231 | 232 | return 0; 233 | } 234 | 235 | static int pngle_draw_pixels(pngle_t *pngle, size_t scanline_ringbuf_xidx) 236 | { 237 | uint16_t v[4]; // MAX_CHANNELS 238 | int bitcount = 0; 239 | uint8_t pixel_depth = (pngle->hdr.color_type & 1) ? 8 : pngle->hdr.depth; 240 | uint16_t maxval = (1UL << pixel_depth) - 1; 241 | 242 | int n_pixels = pngle->hdr.depth == 16 ? 1 : (8 / pngle->hdr.depth); 243 | 244 | for (; n_pixels-- > 0 && pngle->drawing_x < pngle->hdr.width; pngle->drawing_x = U32_CLAMP_ADD(pngle->drawing_x, interlace_div_x[pngle->interlace_pass], pngle->hdr.width)) { 245 | for (uint_fast8_t c = 0; c < pngle->channels; c++) { 246 | v[c] = get_value(pngle, &scanline_ringbuf_xidx, &bitcount, pngle->hdr.depth); 247 | } 248 | 249 | // color type: 0000 0111 250 | // ^-- indexed color (palette) 251 | // ^--- Color 252 | // ^---- Alpha channel 253 | 254 | if (pngle->hdr.color_type & 2) { 255 | // color 256 | if (pngle->hdr.color_type & 1) { 257 | // indexed color: type 3 258 | 259 | // lookup palette info 260 | uint16_t pidx = v[0]; 261 | if (pidx >= pngle->n_palettes) return PNGLE_ERROR("Color index is out of range"); 262 | 263 | v[0] = pngle->palette[pidx * 3 + 0]; 264 | v[1] = pngle->palette[pidx * 3 + 1]; 265 | v[2] = pngle->palette[pidx * 3 + 2]; 266 | 267 | // tRNS as an indexed alpha value table (for color type 3) 268 | v[3] = pidx < pngle->n_trans_palettes ? pngle->trans_palette[pidx] : maxval; 269 | } else { 270 | // true color: 2, and 6 271 | v[3] = (pngle->hdr.color_type & 4) ? v[3] : is_trans_color(pngle, v, 3) ? 0 : maxval; 272 | } 273 | } else { 274 | // alpha, tRNS, or opaque 275 | v[3] = (pngle->hdr.color_type & 4) ? v[1] : is_trans_color(pngle, v, 1) ? 0 : maxval; 276 | 277 | // monochrome 278 | v[1] = v[2] = v[0]; 279 | } 280 | 281 | if (pngle->draw_callback) { 282 | uint8_t rgba[4] = { 283 | (v[0] * 255 + maxval / 2) / maxval, 284 | (v[1] * 255 + maxval / 2) / maxval, 285 | (v[2] * 255 + maxval / 2) / maxval, 286 | (v[3] * 255 + maxval / 2) / maxval 287 | }; 288 | 289 | #ifndef PNGLE_NO_GAMMA_CORRECTION 290 | if (pngle->gamma_table) { 291 | for (int i = 0; i < 3; i++) { 292 | rgba[i] = pngle->gamma_table[v[i]]; 293 | } 294 | } 295 | #endif 296 | 297 | pngle->draw_callback(pngle, pngle->drawing_x, pngle->drawing_y 298 | , MIN(interlace_div_x[pngle->interlace_pass] - interlace_off_x[pngle->interlace_pass], pngle->hdr.width - pngle->drawing_x) 299 | , MIN(interlace_div_y[pngle->interlace_pass] - interlace_off_y[pngle->interlace_pass], pngle->hdr.height - pngle->drawing_y) 300 | , rgba 301 | ); 302 | } 303 | } 304 | 305 | return 0; 306 | } 307 | 308 | static inline int paeth(int a, int b, int c) 309 | { 310 | int p = a + b - c; 311 | int pa = abs(p - a); 312 | int pb = abs(p - b); 313 | int pc = abs(p - c); 314 | 315 | if (pa <= pb && pa <= pc) return a; 316 | if (pb <= pc) return b; 317 | return c; 318 | } 319 | 320 | static int set_interlace_pass(pngle_t *pngle, uint_fast8_t pass) 321 | { 322 | pngle->interlace_pass = pass; 323 | 324 | uint_fast8_t bytes_per_pixel = (pngle->channels * pngle->hdr.depth + 7) / 8; // 1 if depth <= 8 325 | size_t scanline_pixels = (pngle->hdr.width - interlace_off_x[pngle->interlace_pass] + interlace_div_x[pngle->interlace_pass] - 1) / interlace_div_x[pngle->interlace_pass]; 326 | size_t scanline_stride = (scanline_pixels * pngle->channels * pngle->hdr.depth + 7) / 8; 327 | 328 | pngle->scanline_ringbuf_size = scanline_stride + bytes_per_pixel * 2; // 2 rooms for c/x and a 329 | 330 | if (pngle->scanline_ringbuf) free(pngle->scanline_ringbuf); 331 | if ((pngle->scanline_ringbuf = PNGLE_CALLOC(pngle->scanline_ringbuf_size, 1, "scanline ringbuf")) == NULL) return PNGLE_ERROR("Insufficient memory"); 332 | 333 | pngle->drawing_x = interlace_off_x[pngle->interlace_pass]; 334 | pngle->drawing_y = interlace_off_y[pngle->interlace_pass]; 335 | pngle->filter_type = -1; 336 | 337 | pngle->scanline_ringbuf_cidx = 0; 338 | pngle->scanline_remain_bytes_to_render = -1; 339 | 340 | return 0; 341 | } 342 | 343 | static int setup_gamma_table(pngle_t *pngle, uint32_t png_gamma) 344 | { 345 | #ifndef PNGLE_NO_GAMMA_CORRECTION 346 | if (pngle->gamma_table) free(pngle->gamma_table); 347 | 348 | if (pngle->display_gamma <= 0) return 0; // disable gamma correction 349 | if (png_gamma == 0) return 0; 350 | 351 | uint8_t pixel_depth = (pngle->hdr.color_type & 1) ? 8 : pngle->hdr.depth; 352 | uint16_t maxval = (1UL << pixel_depth) - 1; 353 | 354 | pngle->gamma_table = PNGLE_CALLOC(1, maxval + 1, "gamma table"); 355 | if (!pngle->gamma_table) return PNGLE_ERROR("Insufficient memory"); 356 | 357 | for (int i = 0; i < maxval + 1; i++) { 358 | pngle->gamma_table[i] = (uint8_t)floor(pow(i / (double)maxval, 100000.0 / png_gamma / pngle->display_gamma) * 255.0 + 0.5); 359 | } 360 | debug_printf("[pngle] gamma value = %d\n", png_gamma); 361 | #else 362 | PNGLE_UNUSED(pngle); 363 | PNGLE_UNUSED(png_gamma); 364 | #endif 365 | return 0; 366 | } 367 | 368 | 369 | static int pngle_on_data(pngle_t *pngle, const uint8_t *p, int len) 370 | { 371 | const uint8_t *ep = p + len; 372 | 373 | uint_fast8_t bytes_per_pixel = (pngle->channels * pngle->hdr.depth + 7) / 8; // 1 if depth <= 8 374 | 375 | while (p < ep) { 376 | if (pngle->drawing_x >= pngle->hdr.width) { 377 | // New row 378 | pngle->drawing_x = interlace_off_x[pngle->interlace_pass]; 379 | pngle->drawing_y = U32_CLAMP_ADD(pngle->drawing_y, interlace_div_y[pngle->interlace_pass], pngle->hdr.height); 380 | pngle->filter_type = -1; // Indicate new line 381 | } 382 | 383 | if (pngle->drawing_x >= pngle->hdr.width || pngle->drawing_y >= pngle->hdr.height) { 384 | if (pngle->interlace_pass == 0 || pngle->interlace_pass >= 7) return len; // Do nothing further 385 | 386 | // Interlace: Next pass 387 | if (set_interlace_pass(pngle, pngle->interlace_pass + 1) < 0) return -1; 388 | debug_printf("[pngle] interlace pass changed to: %d\n", pngle->interlace_pass); 389 | 390 | continue; // This is required because "No filter type bytes are present in an empty pass". 391 | } 392 | 393 | if (pngle->filter_type < 0) { 394 | if (*p > 4) { 395 | debug_printf("[pngle] Invalid filter type is found; 0x%02x\n", *p); 396 | return PNGLE_ERROR("Invalid filter type is found"); 397 | } 398 | 399 | pngle->filter_type = (int_fast8_t)*p++; // 0 - 4 400 | 401 | // push sentinel bytes for new line 402 | for (uint_fast8_t i = 0; i < bytes_per_pixel; i++) { 403 | scanline_ringbuf_push(pngle, 0); 404 | } 405 | 406 | continue; 407 | } 408 | 409 | size_t cidx = pngle->scanline_ringbuf_cidx; 410 | size_t bidx = (pngle->scanline_ringbuf_cidx + bytes_per_pixel) % pngle->scanline_ringbuf_size; 411 | size_t aidx = (pngle->scanline_ringbuf_cidx + pngle->scanline_ringbuf_size - bytes_per_pixel) % pngle->scanline_ringbuf_size; 412 | // debug_printf("[pngle] cidx = %zd, bidx = %zd, aidx = %zd\n", cidx, bidx, aidx); 413 | 414 | uint8_t c = pngle->scanline_ringbuf[cidx]; // left-up 415 | uint8_t b = pngle->scanline_ringbuf[bidx]; // up 416 | uint8_t a = pngle->scanline_ringbuf[aidx]; // left 417 | uint8_t x = *p++; // target 418 | // debug_printf("[pngle] c = 0x%02x, b = 0x%02x, a = 0x%02x, x = 0x%02x\n", c, b, a, x); 419 | 420 | // Reverse the filter 421 | switch (pngle->filter_type) { 422 | case 0: break; // None 423 | case 1: x += a; break; // Sub 424 | case 2: x += b; break; // Up 425 | case 3: x += (a + b) / 2; break; // Average 426 | case 4: x += paeth(a, b, c); break; // Paeth 427 | } 428 | 429 | scanline_ringbuf_push(pngle, x); // updates scanline_ringbuf_cidx 430 | 431 | if (pngle->scanline_remain_bytes_to_render < 0) pngle->scanline_remain_bytes_to_render = bytes_per_pixel; 432 | if (--pngle->scanline_remain_bytes_to_render == 0) { 433 | size_t xidx = (pngle->scanline_ringbuf_cidx + pngle->scanline_ringbuf_size - bytes_per_pixel) % pngle->scanline_ringbuf_size; 434 | 435 | if (pngle_draw_pixels(pngle, xidx) < 0) return -1; 436 | 437 | pngle->scanline_remain_bytes_to_render = -1; // reset 438 | } 439 | } 440 | 441 | return len; 442 | } 443 | 444 | 445 | static int pngle_handle_chunk(pngle_t *pngle, const uint8_t *buf, size_t len) 446 | { 447 | size_t consume = 0; 448 | 449 | switch (pngle->chunk_type) { 450 | case PNGLE_CHUNK_IHDR: 451 | // parse IHDR 452 | consume = 13; 453 | if (len < consume) return 0; 454 | 455 | debug_printf("[pngle] Parse IHDR\n"); 456 | 457 | pngle->hdr.width = read_uint32(buf + 0); 458 | pngle->hdr.height = read_uint32(buf + 4); 459 | pngle->hdr.depth = read_uint8 (buf + 8); 460 | pngle->hdr.color_type = read_uint8 (buf + 9); 461 | pngle->hdr.compression = read_uint8 (buf + 10); 462 | pngle->hdr.filter = read_uint8 (buf + 11); 463 | pngle->hdr.interlace = read_uint8 (buf + 12); 464 | 465 | 466 | debug_printf("[pngle] width : %d\n", pngle->hdr.width ); 467 | debug_printf("[pngle] height : %d\n", pngle->hdr.height ); 468 | debug_printf("[pngle] depth : %d\n", pngle->hdr.depth ); 469 | debug_printf("[pngle] color_type : %d\n", pngle->hdr.color_type ); 470 | debug_printf("[pngle] compression: %d\n", pngle->hdr.compression); 471 | debug_printf("[pngle] filter : %d\n", pngle->hdr.filter ); 472 | debug_printf("[pngle] interlace : %d\n", pngle->hdr.interlace ); 473 | 474 | /* 475 | Color Allowed Interpretation channels 476 | Type Bit Depths 477 | 478 | 0 1,2,4,8,16 Each pixel is a grayscale sample. 1 channels (Brightness) 479 | 480 | 2 8,16 Each pixel is an R,G,B triple. 3 channels (R, G, B) 481 | 482 | 3 1,2,4,8 Each pixel is a palette index; 1 channels (palette info) 483 | a PLTE chunk must appear. 484 | 485 | 4 8,16 Each pixel is a grayscale sample, 2 channels (Brightness, Alpha) 486 | followed by an alpha sample. 487 | 488 | 6 8,16 Each pixel is an R,G,B triple, 4 channels (R, G, B, Alpha) 489 | followed by an alpha sample. 490 | */ 491 | // 111 492 | // ^-- indexed color (palette) 493 | // ^--- Color 494 | // ^---- Alpha channel 495 | 496 | switch (pngle->hdr.color_type) { 497 | case 0: pngle->channels = 1; if (pngle->hdr.depth != 1 && pngle->hdr.depth != 2 && pngle->hdr.depth != 4 && pngle->hdr.depth != 8 && pngle->hdr.depth != 16) return PNGLE_ERROR("Invalid bit depth"); break; // grayscale 498 | case 2: pngle->channels = 3; if ( pngle->hdr.depth != 8 && pngle->hdr.depth != 16) return PNGLE_ERROR("Invalid bit depth"); break; // truecolor 499 | case 3: pngle->channels = 1; if (pngle->hdr.depth != 1 && pngle->hdr.depth != 2 && pngle->hdr.depth != 4 && pngle->hdr.depth != 8 ) return PNGLE_ERROR("Invalid bit depth"); break; // indexed color 500 | case 4: pngle->channels = 2; if ( pngle->hdr.depth != 8 && pngle->hdr.depth != 16) return PNGLE_ERROR("Invalid bit depth"); break; // grayscale + alpha 501 | case 6: pngle->channels = 4; if ( pngle->hdr.depth != 8 && pngle->hdr.depth != 16) return PNGLE_ERROR("Invalid bit depth"); break; // truecolor + alpha 502 | default: 503 | return PNGLE_ERROR("Incorrect IHDR info"); 504 | } 505 | 506 | if (pngle->hdr.compression != 0) return PNGLE_ERROR("Unsupported compression type in IHDR"); 507 | if (pngle->hdr.filter != 0) return PNGLE_ERROR("Unsupported filter type in IHDR"); 508 | 509 | // interlace 510 | if (set_interlace_pass(pngle, pngle->hdr.interlace ? 1 : 0) < 0) return -1; 511 | 512 | // callback 513 | if (pngle->init_callback) pngle->init_callback(pngle, pngle->hdr.width, pngle->hdr.height); 514 | 515 | break; 516 | 517 | case PNGLE_CHUNK_IDAT: 518 | // parse & decode IDAT chunk 519 | if (len < 1) return 0; 520 | 521 | debug_printf("[pngle] Reading IDAT (len %zd / chunk remain %u)\n", len, pngle->chunk_remain); 522 | 523 | size_t in_bytes = len; 524 | size_t out_bytes = pngle->avail_out; 525 | 526 | //debug_printf("[pngle] in_bytes %zd, out_bytes %zd, next_out %p\n", in_bytes, out_bytes, pngle->next_out); 527 | 528 | // XXX: tinfl_decompress always requires (next_out - lz_buf + avail_out) == TINFL_LZ_DICT_SIZE 529 | tinfl_status status = tinfl_decompress(&pngle->inflator, (const mz_uint8 *)buf, &in_bytes, pngle->lz_buf, (mz_uint8 *)pngle->next_out, &out_bytes, TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_PARSE_ZLIB_HEADER); 530 | 531 | //debug_printf("[pngle] tinfl_decompress\n"); 532 | //debug_printf("[pngle] => in_bytes %zd, out_bytes %zd, next_out %p, status %d\n", in_bytes, out_bytes, pngle->next_out, status); 533 | 534 | if (status < TINFL_STATUS_DONE) { 535 | // Decompression failed. 536 | debug_printf("[pngle] tinfl_decompress() failed with status %d!\n", status); 537 | return PNGLE_ERROR("Failed to decompress the IDAT stream"); 538 | } 539 | 540 | pngle->next_out += out_bytes; 541 | pngle->avail_out -= out_bytes; 542 | 543 | // debug_printf("[pngle] => avail_out %zd, next_out %p\n", pngle->avail_out, pngle->next_out); 544 | 545 | if (status == TINFL_STATUS_DONE || pngle->avail_out == 0) { 546 | // Output buffer is full, or decompression is done, so write buffer to output file. 547 | // XXX: This is the only chance to process the buffer. 548 | uint8_t *read_ptr = pngle->lz_buf; 549 | size_t n = TINFL_LZ_DICT_SIZE - (size_t)pngle->avail_out; 550 | 551 | // pngle_on_data() usually returns n, otherwise -1 on error 552 | if (pngle_on_data(pngle, read_ptr, n) < 0) return -1; 553 | 554 | // XXX: tinfl_decompress always requires (next_out - lz_buf + avail_out) == TINFL_LZ_DICT_SIZE 555 | pngle->next_out = pngle->lz_buf; 556 | pngle->avail_out = TINFL_LZ_DICT_SIZE; 557 | } 558 | 559 | consume = in_bytes; 560 | break; 561 | 562 | case PNGLE_CHUNK_PLTE: 563 | consume = 3; 564 | if (len < consume) return 0; 565 | 566 | memcpy(pngle->palette + pngle->n_palettes * 3, buf, 3); 567 | 568 | debug_printf("[pngle] PLTE[%zd]: (%d, %d, %d)\n" 569 | , pngle->n_palettes 570 | , pngle->palette[pngle->n_palettes * 3 + 0] 571 | , pngle->palette[pngle->n_palettes * 3 + 1] 572 | , pngle->palette[pngle->n_palettes * 3 + 2] 573 | ); 574 | 575 | pngle->n_palettes++; 576 | 577 | break; 578 | 579 | case PNGLE_CHUNK_IEND: 580 | consume = 0; 581 | break; 582 | 583 | case PNGLE_CHUNK_tRNS: 584 | switch (pngle->hdr.color_type) { 585 | case 3: consume = 1; break; 586 | case 0: consume = 2 * 1; break; 587 | case 2: consume = 2 * 3; break; 588 | default: 589 | return PNGLE_ERROR("tRNS chunk is prohibited on the color type"); 590 | } 591 | if (len < consume) return 0; 592 | 593 | memcpy(pngle->trans_palette + pngle->n_trans_palettes, buf, consume); 594 | 595 | pngle->n_trans_palettes++; 596 | 597 | break; 598 | 599 | case PNGLE_CHUNK_gAMA: 600 | consume = 4; 601 | if (len < consume) return 0; 602 | 603 | if (setup_gamma_table(pngle, read_uint32(buf)) < 0) return -1; 604 | 605 | break; 606 | 607 | default: 608 | // unknown chunk 609 | consume = len; 610 | 611 | debug_printf("[pngle] Unknown chunk; %zd bytes discarded\n", consume); 612 | break; 613 | } 614 | 615 | return consume; 616 | } 617 | 618 | static int pngle_feed_internal(pngle_t *pngle, const uint8_t *buf, size_t len) 619 | { 620 | if (!pngle) return -1; 621 | 622 | switch (pngle->state) { 623 | case PNGLE_STATE_ERROR: 624 | return -1; 625 | 626 | case PNGLE_STATE_EOF: 627 | return len; 628 | 629 | case PNGLE_STATE_INITIAL: 630 | // find PNG header 631 | if (len < sizeof(png_sig)) return 0; 632 | 633 | if (memcmp(png_sig, buf, sizeof(png_sig))) return PNGLE_ERROR("Incorrect PNG signature"); 634 | 635 | debug_printf("[pngle] PNG signature found\n"); 636 | 637 | pngle->state = PNGLE_STATE_FIND_CHUNK_HEADER; 638 | return sizeof(png_sig); 639 | 640 | case PNGLE_STATE_FIND_CHUNK_HEADER: 641 | if (len < 8) return 0; 642 | 643 | pngle->chunk_remain = read_uint32(buf); 644 | pngle->chunk_type = read_uint32(buf + 4); 645 | 646 | pngle->crc32 = mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)(buf + 4), 4); 647 | 648 | debug_printf("[pngle] Chunk '%.4s' len %u\n", buf + 4, pngle->chunk_remain); 649 | 650 | pngle->state = PNGLE_STATE_HANDLE_CHUNK; 651 | 652 | // initialize & sanity check 653 | switch (pngle->chunk_type) { 654 | case PNGLE_CHUNK_IHDR: 655 | if (pngle->chunk_remain != 13) return PNGLE_ERROR("Invalid IHDR chunk size"); 656 | if (pngle->channels != 0) return PNGLE_ERROR("Multiple IHDR chunks are not allowed"); 657 | break; 658 | 659 | case PNGLE_CHUNK_IDAT: 660 | if (pngle->chunk_remain <= 0) return PNGLE_ERROR("Invalid IDAT chunk size"); 661 | if (pngle->channels == 0) return PNGLE_ERROR("No IHDR chunk is found"); 662 | if (pngle->hdr.color_type == 3 && pngle->palette == NULL) return PNGLE_ERROR("No PLTE chunk is found"); 663 | 664 | if (pngle->next_out == NULL) { 665 | // Very first IDAT 666 | pngle->next_out = pngle->lz_buf; 667 | pngle->avail_out = TINFL_LZ_DICT_SIZE; 668 | } 669 | break; 670 | 671 | case PNGLE_CHUNK_PLTE: 672 | if (pngle->chunk_remain <= 0) return PNGLE_ERROR("Invalid PLTE chunk size"); 673 | if (pngle->channels == 0) return PNGLE_ERROR("No IHDR chunk is found"); 674 | if (pngle->palette) return PNGLE_ERROR("Too many PLTE chunk"); 675 | 676 | switch (pngle->hdr.color_type) { 677 | case 3: // indexed color 678 | break; 679 | case 2: // truecolor 680 | case 6: // truecolor + alpha 681 | // suggested palettes 682 | break; 683 | default: 684 | return PNGLE_ERROR("PLTE chunk is prohibited on the color type"); 685 | } 686 | 687 | if (pngle->chunk_remain % 3) return PNGLE_ERROR("Invalid PLTE chunk size"); 688 | if (pngle->chunk_remain / 3 > MIN(256, (1UL << pngle->hdr.depth))) return PNGLE_ERROR("Too many palettes in PLTE"); 689 | if ((pngle->palette = PNGLE_CALLOC(pngle->chunk_remain / 3, 3, "palette")) == NULL) return PNGLE_ERROR("Insufficient memory"); 690 | pngle->n_palettes = 0; 691 | break; 692 | 693 | case PNGLE_CHUNK_IEND: 694 | if (pngle->next_out == NULL) return PNGLE_ERROR("No IDAT chunk is found"); 695 | if (pngle->chunk_remain > 0) return PNGLE_ERROR("Invalid IEND chunk size"); 696 | break; 697 | 698 | case PNGLE_CHUNK_tRNS: 699 | if (pngle->chunk_remain <= 0) return PNGLE_ERROR("Invalid tRNS chunk size"); 700 | if (pngle->channels == 0) return PNGLE_ERROR("No IHDR chunk is found"); 701 | if (pngle->trans_palette) return PNGLE_ERROR("Too many tRNS chunk"); 702 | 703 | switch (pngle->hdr.color_type) { 704 | case 3: // indexed color 705 | if (pngle->chunk_remain > (1UL << pngle->hdr.depth)) return PNGLE_ERROR("Too many palettes in tRNS"); 706 | break; 707 | case 0: // grayscale 708 | if (pngle->chunk_remain != 2) return PNGLE_ERROR("Invalid tRNS chunk size"); 709 | break; 710 | case 2: // truecolor 711 | if (pngle->chunk_remain != 6) return PNGLE_ERROR("Invalid tRNS chunk size"); 712 | break; 713 | 714 | default: 715 | return PNGLE_ERROR("tRNS chunk is prohibited on the color type"); 716 | } 717 | if ((pngle->trans_palette = PNGLE_CALLOC(pngle->chunk_remain, 1, "trans palette")) == NULL) return PNGLE_ERROR("Insufficient memory"); 718 | pngle->n_trans_palettes = 0; 719 | break; 720 | 721 | default: 722 | break; 723 | } 724 | 725 | return 8; 726 | 727 | case PNGLE_STATE_HANDLE_CHUNK: 728 | len = MIN(len, pngle->chunk_remain); 729 | 730 | int consumed = pngle_handle_chunk(pngle, buf, len); 731 | 732 | if (consumed > 0) { 733 | if (pngle->chunk_remain < (uint32_t)consumed) return PNGLE_ERROR("Chunk data has been consumed too much"); 734 | 735 | pngle->chunk_remain -= consumed; 736 | pngle->crc32 = mz_crc32(pngle->crc32, (const mz_uint8 *)buf, consumed); 737 | } 738 | if (pngle->chunk_remain <= 0) pngle->state = PNGLE_STATE_CRC; 739 | 740 | return consumed; 741 | 742 | case PNGLE_STATE_CRC: 743 | if (len < 4) return 0; 744 | 745 | uint32_t crc32 = read_uint32(buf); 746 | 747 | if (crc32 != pngle->crc32) { 748 | debug_printf("[pngle] CRC: %08x vs %08x => NG\n", crc32, (uint32_t)pngle->crc32); 749 | return PNGLE_ERROR("CRC mismatch"); 750 | } 751 | 752 | debug_printf("[pngle] CRC: %08x vs %08x => OK\n", crc32, (uint32_t)pngle->crc32); 753 | pngle->state = PNGLE_STATE_FIND_CHUNK_HEADER; 754 | 755 | // XXX: 756 | if (pngle->chunk_type == PNGLE_CHUNK_IEND) { 757 | pngle->state = PNGLE_STATE_EOF; 758 | if (pngle->done_callback) pngle->done_callback(pngle); 759 | debug_printf("[pngle] DONE\n"); 760 | } 761 | 762 | return 4; 763 | 764 | default: 765 | break; 766 | } 767 | 768 | return PNGLE_ERROR("Invalid state"); 769 | } 770 | 771 | int pngle_feed(pngle_t *pngle, const void *buf, size_t len) 772 | { 773 | size_t pos = 0; 774 | pngle_state_t last_state = pngle->state; 775 | 776 | while (pos < len) { 777 | int r = pngle_feed_internal(pngle, (const uint8_t *)buf + pos, len - pos); 778 | if (r < 0) return r; // error 779 | 780 | if (r == 0 && last_state == pngle->state) break; 781 | last_state = pngle->state; 782 | 783 | pos += r; 784 | } 785 | 786 | return pos; 787 | } 788 | 789 | void pngle_set_display_gamma(pngle_t *pngle, double display_gamma) 790 | { 791 | if (!pngle) return ; 792 | #ifndef PNGLE_NO_GAMMA_CORRECTION 793 | pngle->display_gamma = display_gamma; 794 | #else 795 | PNGLE_UNUSED(display_gamma); 796 | #endif 797 | } 798 | 799 | void pngle_set_init_callback(pngle_t *pngle, pngle_init_callback_t callback) 800 | { 801 | if (!pngle) return ; 802 | pngle->init_callback = callback; 803 | } 804 | 805 | void pngle_set_draw_callback(pngle_t *pngle, pngle_draw_callback_t callback) 806 | { 807 | if (!pngle) return ; 808 | pngle->draw_callback = callback; 809 | } 810 | 811 | void pngle_set_done_callback(pngle_t *pngle, pngle_done_callback_t callback) 812 | { 813 | if (!pngle) return ; 814 | pngle->done_callback = callback; 815 | } 816 | 817 | void pngle_set_user_data(pngle_t *pngle, void *user_data) 818 | { 819 | if (!pngle) return ; 820 | pngle->user_data = user_data; 821 | } 822 | 823 | void *pngle_get_user_data(pngle_t *pngle) 824 | { 825 | if (!pngle) return NULL; 826 | return pngle->user_data; 827 | } 828 | 829 | /* vim: set ts=4 sw=4 noexpandtab: */ 830 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp-idf-ili9340 2 | SPI TFT and XPT2046 touch screen controller driver for esp-idf. 3 | 4 | # Software requirements 5 | ESP-IDF V5.0 or later. 6 | ESP-IDF V4.4 release branch reached EOL in July 2024. 7 | 8 | __Note for ESP32-C6__ 9 | ESP-IDF V5.1 is required when using ESP32-C6. 10 | 11 | # Installation 12 | 13 | ``` 14 | git clone https://github.com/nopnop2002/esp-idf-ili9340 15 | cd esp-idf-ili9340/ 16 | idf.py set-target {esp32/esp32s2/esp32s3/esp32c2/esp32c3/esp32c6} 17 | idf.py menuconfig 18 | idf.py flash 19 | ``` 20 | 21 | __Note for ESP32-S2/ESP32-C2__ 22 | The tjpgd library is not included in the ESP32-S2/ESP32-C2 ROM. 23 | However, you can use [this](https://components.espressif.com/components/espressif/esp_jpeg) IDF component registry. 24 | JPEG files can be displayed. 25 | 26 | __Note for ESP32-C2__ 27 | ESP32-C2 has less SRAM, so JPEG and PNG may not be displayed on higher resolution TFTs. 28 | ``` 29 | E (256560) decode_jpeg: Error allocating memory for line 251 30 | E (260630) pngle_new: Error allocating memory for line 160 31 | ``` 32 | 33 | __Note for ESP32-S2__ 34 | ESP32-S2 has less SRAM, but some ESP32-S2 have PSRAM. 35 | If your SoC has PSRAM, you can avoid running out of memory by enabling PSRAM. 36 | ![config-psram](https://github.com/user-attachments/assets/a71b320d-f5fb-4cd5-9466-f191b8704d40) 37 | 38 | 39 | # Configuration 40 | You have to set this config value with menuconfig. 41 | - CONFIG_WIDTH 42 | - CONFIG_HEIGHT 43 | - CONFIG_OFFSETX 44 | - CONFIG_OFFSETY 45 | - CONFIG_MOSI_GPIO 46 | - CONFIG_SCLK_GPIO 47 | - CONFIG_CS_GPIO 48 | - CONFIG_DC_GPIO 49 | - CONFIG_RESET_GPIO 50 | - CONFIG_BL_GPIO 51 | __GPIO of ESP32 cannot supply too much current.__ 52 | __TFT backlight becomes brighter when powered by an external power source.__ 53 | 54 | __TFT MISO is not use.__ 55 | 56 | ![config-menu](https://user-images.githubusercontent.com/6020549/101022538-db9fcb00-35b4-11eb-95a9-ad84a880c7fb.jpg) 57 | 58 | --- 59 | 60 | # Tested TFT 61 | - M5Stack 62 | - Shenzhen Jingcal Intelligent 3.5" ST7796 320x480 (ESP32-3248S035) 63 | - Shenzhen Jingcal Intelligent 2.8" ILI9341 240x320 (ESP32-2432S028R) 64 | - 4.0" ST7796 320x480 65 | - 3.2" ILI9341 240x320 66 | - 2.8" ILI9341 240x320 67 | - 2.8" ST7789 240x320 68 | - 2.4" ILI9341 240x320 69 | - 2.4" ST7789 240x320 70 | - 2.2" ILI9340 240x320 71 | - 2.0" ILI9225 176x220 72 | - 1.8" ST7735 128x160 73 | - 1.77" ST7735 128x160 74 | - 1.44" ST7735 128x128 75 | - 0.96" ST7735 80x160 76 | 77 | 78 | ### Generic Product 79 | ![spi-tft](https://user-images.githubusercontent.com/6020549/59512687-3098de80-8ef4-11e9-9759-530ffe47d659.JPG) 80 | 81 | __A note about RESET__ 82 | Pull Up of the RESET pin may be required. I inserted a 100 ohm resistor between Vcc and RESET. 83 | 84 | ### Shenzhen Jingcal Intelligent Product 85 | They can use touch screens. 86 | ![Shenzhen_Jingcal_Intelligent](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/eda884bb-9fac-40ea-8396-d77bb0de074b) 87 | 88 | __ESP32-2432S032/ESP32-4827S043/ESP32-8048S043 doesn't work because it's an RGB panel, not SPI.__ 89 | 90 | --- 91 | 92 | # M5Stack 93 | 94 | ![config-m5stack](https://user-images.githubusercontent.com/6020549/101022626-f96d3000-35b4-11eb-8c60-a8a4896306d9.jpg) 95 | 96 | ![M5Stick-1](https://user-images.githubusercontent.com/6020549/57977479-7d43e380-7a34-11e9-99b6-6028500436e8.JPG) 97 | ![M5Stack-2](https://user-images.githubusercontent.com/6020549/59008264-f7fa6480-8864-11e9-92f6-ffbd979fa75e.JPG) 98 | ![M5Stick-3](https://user-images.githubusercontent.com/6020549/57977481-7d43e380-7a34-11e9-9bc6-8e415aa52c52.JPG) 99 | ![M5Stick-4](https://user-images.githubusercontent.com/6020549/57977482-7d43e380-7a34-11e9-8188-653cb02f3ba0.JPG) 100 | ![M5Stick-5](https://user-images.githubusercontent.com/6020549/57977483-7ddc7a00-7a34-11e9-936e-4d97b1411610.JPG) 101 | ![M5Stick-6](https://user-images.githubusercontent.com/6020549/57977484-7ddc7a00-7a34-11e9-8750-52db073c96c2.JPG) 102 | ![M5Stick-7](https://user-images.githubusercontent.com/6020549/57977485-7e751080-7a34-11e9-95f9-ffb12879d1b0.JPG) 103 | ![M5Stick-8](https://user-images.githubusercontent.com/6020549/57977486-7e751080-7a34-11e9-9ac8-c546f248fdec.JPG) 104 | ![M5Stick-9](https://user-images.githubusercontent.com/6020549/57977487-7e751080-7a34-11e9-9a3e-6a0bd7359efb.JPG) 105 | ![M5Stick-10](https://user-images.githubusercontent.com/6020549/57977488-7e751080-7a34-11e9-9a12-e2b70334604d.JPG) 106 | ![M5Stick-11](https://user-images.githubusercontent.com/6020549/57977489-7f0da700-7a34-11e9-9ea3-c0420a785a3e.JPG) 107 | 108 | BMP file 109 | ![M5Stack-BMP](https://user-images.githubusercontent.com/6020549/78413964-dcaee000-7654-11ea-88f2-e70662d761e1.JPG) 110 | 111 | JPEG file(Cannot be displayed on ESP32S2) 112 | ![M5Stack-JPEG](https://user-images.githubusercontent.com/6020549/78413968-e0426700-7654-11ea-9040-0fdfd0f2de2e.JPG) 113 | 114 | PNG file 115 | ![M5Stack-PNG](https://user-images.githubusercontent.com/6020549/78613610-40c8e280-78a7-11ea-95b0-a89ce14dc196.JPG) 116 | 117 | PNG icon 118 | ![M5Stack-ICON](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/120af5a4-9b31-476a-a9db-258c2d0072e7) 119 | 120 | --- 121 | 122 | # 4.0" ST7796S 320x480 123 | 124 | ![config-st7796](https://user-images.githubusercontent.com/6020549/101022680-0e49c380-35b5-11eb-914f-f522b33bb49a.jpg) 125 | 126 | Left:4.0" Right:2.4" 127 | ![4_0_st779s](https://user-images.githubusercontent.com/6020549/85913118-e7040300-b86c-11ea-87fd-fdcd97c5bf26.JPG) 128 | 129 | --- 130 | 131 | # Shenzhen Jingcal Intelligent 3.5" ST7796 320x480 132 | Vendor part number is ESP32-3248S035. 133 | 134 | ![config-ESP32-3248S035](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/68749d5a-9047-4725-bc06-281f37e44539) 135 | ![ESP32-3248S035-1](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/ee2b7eb5-2627-4bbf-b7a5-8a31005cf22d) 136 | ![ESP32-3248S035-2](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/56d7bc3b-ab03-4486-bd51-bc16d6138d1c) 137 | 138 | --- 139 | 140 | # 3.2" ILI9341 240x320 141 | # 2.8" ILI9341 240x320 142 | # 2.4" ILI9341 240x320 143 | 144 | ![config-ili9341](https://user-images.githubusercontent.com/6020549/101022718-1b66b280-35b5-11eb-8103-7bbb1f0a7a30.jpg) 145 | 146 | Left:3.2" Right:2.4" 147 | ![3_2_ili9341](https://user-images.githubusercontent.com/6020549/85909286-0b9fb100-b854-11ea-9935-942fe7344f25.JPG) 148 | 149 | Left:2.8" Right:2.4" 150 | ![screen_2-8_240x320](https://user-images.githubusercontent.com/6020549/59007633-3f332600-8862-11e9-8d8f-bbfb303456f4.JPG) 151 | 152 | --- 153 | 154 | # 2.8" ST7789 240x320 155 | # 2.4" ST7789 240x320 156 | 157 | ![config_st7789_2-4_240x320](https://github.com/user-attachments/assets/0ce336db-8a51-4b76-b268-0dda76559f9a) 158 | 159 | Left:2.4" Right:2.8" 160 | ![screen_st7789_240x320](https://github.com/user-attachments/assets/ccb5335d-2800-4300-a91e-182e07772c3d) 161 | 162 | --- 163 | 164 | # Shenzhen Jingcal Intelligent 2.8" ILI9341 240x320 165 | Vendor part number is ESP32-2432S028R. 166 | 167 | ![config-ESP32-2432S028R-1](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/317e365c-3c1a-4c6f-9c14-f07feacbd4d8) 168 | ![ESP32-2432S028R-1](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/e9e4b502-4063-49b2-b58a-d60aeccdd380) 169 | ![ESP32-2432S028R-2](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/d6d6cc27-463a-4ad5-a739-441d6ebdc037) 170 | 171 | 172 | --- 173 | 174 | # 2.2" ILI9340 240x320 175 | 176 | ![config-ili9340](https://user-images.githubusercontent.com/6020549/101022746-2588b100-35b5-11eb-8eaa-1f86cc4c9f93.jpg) 177 | 178 | Left:2.2" Right:2.4" 179 | ![screen_2-2_240x320](https://user-images.githubusercontent.com/6020549/59007758-d304f200-8862-11e9-8d61-fa4b734f4b9a.JPG) 180 | 181 | --- 182 | 183 | # 2.0" ILI9225 176x220 184 | 185 | ![config-ili9225](https://user-images.githubusercontent.com/6020549/101022807-3a654480-35b5-11eb-9e3d-b2eb5e396f08.jpg) 186 | 187 | Left:2.0" Right:2.4" 188 | ![2_0_ili9225](https://user-images.githubusercontent.com/6020549/85910092-004e8480-b858-11ea-98f9-2ba9efe5880a.JPG) 189 | 190 | --- 191 | 192 | # 1.8" ST7735 128x160 193 | 194 | ![config-st7735-128x160-1](https://user-images.githubusercontent.com/6020549/101023253-dc852c80-35b5-11eb-97c8-4873200f85e7.jpg) 195 | 196 | Left:1.8" Right:2.4" 197 | ![1_8_st7735](https://user-images.githubusercontent.com/6020549/85910349-3cceb000-b859-11ea-98a0-ecda6870d5aa.JPG) 198 | 199 | --- 200 | 201 | # 1.8" ST7735 128x160 202 | 203 | ![config-st7735-128x160-2](https://user-images.githubusercontent.com/6020549/101023297-ec9d0c00-35b5-11eb-924b-a4aefc0dc9f2.jpg) 204 | ![screen_1-8_128x160-12](https://user-images.githubusercontent.com/6020549/59007829-21b28c00-8863-11e9-945d-91f1c2fa2d14.JPG) 205 | ![screen_1-8_128x160-13](https://user-images.githubusercontent.com/6020549/77221889-4cbb6180-6b91-11ea-9e3f-97d9ddafb82d.JPG) 206 | 207 | --- 208 | 209 | # 1.8" ST7735 128x160 210 | 211 | ![config-st7735-128x160-3](https://user-images.githubusercontent.com/6020549/101023341-f6267400-35b5-11eb-8835-7ea3278ba6eb.jpg) 212 | ![screen_1-8_128x160-21](https://user-images.githubusercontent.com/6020549/59007852-3858e300-8863-11e9-8f03-ce4ae9eb652b.JPG) 213 | ![screen_1-8_128x160-31](https://user-images.githubusercontent.com/6020549/59970608-b9271700-95a5-11e9-94f8-062740fe135c.JPG) 214 | 215 | --- 216 | 217 | # 1.77" ST7735 128x160 218 | 219 | ![config-st7735-128x160-4](https://user-images.githubusercontent.com/6020549/101023366-01799f80-35b6-11eb-8608-96bab43d909f.jpg) 220 | 221 | Left:1.77" Right:1.8" 222 | ![screen_1-77_128x160](https://user-images.githubusercontent.com/6020549/59970576-bd066980-95a4-11e9-9f4a-88d69733f034.JPG) 223 | 224 | --- 225 | 226 | # 1.44" ST7735 128x160 227 | 228 | ![config-st7735-128x160-5](https://user-images.githubusercontent.com/6020549/101023395-0a6a7100-35b6-11eb-887c-3ebfc93bb0eb.jpg) 229 | 230 | Left:1.44" Right:2.0" 231 | ![screen_1-8_128x160-2](https://user-images.githubusercontent.com/6020549/59007892-63433700-8863-11e9-86a7-1b92e8b15efd.JPG) 232 | 233 | --- 234 | 235 | # 1.44" ST7735 128x128 236 | ![config-st7735-128x128](https://user-images.githubusercontent.com/6020549/101023420-16eec980-35b6-11eb-9c75-ca90a8b7f99a.jpg) 237 | ![screen_1-44_128x128-12](https://user-images.githubusercontent.com/6020549/59007915-7eae4200-8863-11e9-901f-037cbc0baed2.JPG) 238 | ![screen_1-44_128x128-13](https://user-images.githubusercontent.com/6020549/77222202-775ae980-6b94-11ea-8eed-0f6829833da8.JPG) 239 | 240 | __GRAM Offset may be different__ 241 | 242 | --- 243 | 244 | # 0.96" ST7735 80x160 245 | 246 | ![config-st7735-80x160](https://user-images.githubusercontent.com/6020549/101023455-240bb880-35b6-11eb-9008-e69c59efb825.jpg) 247 | 248 | Left:1.44" Right:0.96" 249 | ![screen_0-96_80x160-3](https://user-images.githubusercontent.com/6020549/59007940-9ede0100-8863-11e9-85ba-6eca86b6f441.JPG) 250 | 251 | --- 252 | 253 | # JPEG Decoder 254 | The ESP-IDF component includes Tiny JPEG Decompressor. 255 | The document of Tiny JPEG Decompressor is [here](http://elm-chan.org/fsw/tjpgd/00index.html). 256 | This can reduce the image to 1/2 1/4 1/8. 257 | 258 | --- 259 | 260 | # PNG Decoder 261 | The ESP-IDF component includes part of the miniz library, such as mz_crc32. 262 | But it doesn't support all of the miniz. 263 | The document of miniz library is [here](https://github.com/richgel999/miniz). 264 | 265 | And I ported the pngle library from [here](https://github.com/kikuchan/pngle). 266 | This can reduce the image to any size. 267 | 268 | --- 269 | 270 | # Font File 271 | This project uses the following as default fonts: 272 | - font/ILGH16XB.FNT // 8x16Dot Gothic 273 | - font/ILGH24XB.FNT // 12x24Dot Gothic 274 | - font/ILGH32XB.FNT // 16x32Dot Gothic 275 | - font/ILMH16XB.FNT // 8x16Dot Mincyo 276 | - font/ILMH24XB.FNT // 12x24Dot Mincyo 277 | - font/ILMH32XB.FNT // 16x32Dot Mincyo 278 | 279 | From 0x00 to 0x7f, the characters image of Alphanumeric are stored. 280 | From 0x80 to 0xff, the characters image of Japanese are stored. 281 | Changing this file will change the font. 282 | 283 | You can add your original fonts. 284 | The format of the font file is the FONTX format. 285 | Your font file is put in font directory. 286 | Your font file is uploaded to SPIFFS partition using meke flash. 287 | 288 | Please refer [this](http://elm-chan.org/docs/dosv/fontx_e.html) page about FONTX format. 289 | 290 | --- 291 | 292 | # Font File Editor(FONTX Editor) 293 | [There](http://elm-chan.org/fsw/fontxedit.zip) is a font file editor. 294 | This can be done on Windows 10. 295 | Developer page is [here](http://elm-chan.org/fsw_e.html). 296 | 297 | ![fontx-editor-1](https://github.com/user-attachments/assets/76a8c96f-74c3-4583-a4f1-5664f0e81f3a) 298 | 299 | # Convert from BDF font to FONTX font 300 | step1) 301 | download Font File Editor(FONTX Editor) from [here](http://elm-chan.org/fsw_e.html). 302 | 303 | step2) 304 | download BDF font file from Internet. 305 | I downloaded from [here](https://github.com/fcambus/spleen). 306 | fontxedit.exe can __ONLY__ import Monospaced bitmap fonts file. 307 | Monospaced bitmap fonts can also be downloaded [here](https://github.com/Tecate/bitmap-fonts). 308 | 309 | step3) 310 | import the BDF font file into your fontxedit.exe. 311 | this tool can convert from BDF to FONTX. 312 | ![fontx-editor-2](https://github.com/user-attachments/assets/3353bf23-01f0-455d-8c9c-b56d55b4dc9c) 313 | 314 | step4) 315 | adjust font size. 316 | ![fontx-editor-3](https://github.com/user-attachments/assets/0a99fb0b-1725-472e-8310-ca57362ae6d1) 317 | 318 | step5) 319 | check font pattern. 320 | when you have made any changes, press the apply button. 321 | ![fontx-editor-4](https://github.com/user-attachments/assets/44b8ed95-0c3e-4507-87fa-b94c3c2349de) 322 | 323 | step6) 324 | save as .fnt file from your fontedit.exe. 325 | ![fontx-editor-5](https://github.com/user-attachments/assets/db5b62a8-3a61-49aa-8505-b906067f1111) 326 | 327 | step7) 328 | upload your font file to $HOME/esp-idf-ili9340/font directory. 329 | 330 | step8) 331 | add font to use 332 | ``` 333 | FontxFile fx32L[2]; 334 | InitFontx(fx32L,"/spiffs/LATIN32B.FNT",""); // 16x32Dot LATIN 335 | ``` 336 | Font file that From 0x00 to 0x7f, the characters image of Standard ASCII are stored. 337 | ![M5Statck-Font-1](https://user-images.githubusercontent.com/6020549/132110943-98e8f481-8840-4e96-b649-f525aa427826.JPG) 338 | 339 | Font file that From 0x80 to 0xff, the characters image of Japanese are stored. 340 | ![M5Statck-Font-2](https://user-images.githubusercontent.com/6020549/132110947-d161678f-0424-4934-91b6-22eb12a57435.JPG) 341 | 342 | Font file that From 0x80 to 0xff, the characters image of Latin are stored. 343 | ![M5Statck-Font-3](https://user-images.githubusercontent.com/6020549/132110951-e7faaaa9-cdf2-429d-8eea-d20fe0748524.JPG) 344 | 345 | u8g2 library contains many BDF fonts. 346 | This is a 24x24 font converted from emoticons21.bdf. 347 | ![bdf-font-1](https://github.com/user-attachments/assets/0c5470df-ab99-4b73-b3ac-624b6e6a5230) 348 | 349 | This is a 16x16 font converted from Scroll-o-Sprites.bdf. 350 | ![bdf-font-2](https://github.com/user-attachments/assets/41dd693d-cb3d-4928-b213-49bf7cad4f6a) 351 | 352 | # Convert from TTF font to FONTX font 353 | step1) 354 | Download WFONTX64.exe from [here](https://github.com/nemuisan/WFONTX64/releases). 355 | Developer page is [here](https://github.com/nemuisan/WFONTX64). 356 | 357 | step2) 358 | Select ttf font. 359 | ___Please note that if you select a proportional font, some fonts may not convert correctly.___ 360 | If you select a proportional font, some fonts will need to be modified using fontxedit.exe. 361 | Monospaced fonts can be converted correctly. 362 | You can find Monospaced fonts [here](https://en.wikipedia.org/wiki/List_of_monospaced_typefaces). 363 | ![WFONTX64-1](https://github.com/user-attachments/assets/2193a3c4-021c-48e6-8486-2ce500bdac36) 364 | 365 | step3) 366 | Enter Height, Width, FontX2 name. 367 | Specify half of Height for Width. 368 | Specify your favorite font name in the FontX2 name field using up to 8 characters. 369 | ![WFONTX64-2](https://github.com/user-attachments/assets/c87a9ec9-8e28-4d34-8475-60b15a47fb22) 370 | 371 | step4) 372 | Specify the file name to save. 373 | ![WFONTX64-3](https://github.com/user-attachments/assets/9715d4bf-e460-41a6-9a4b-38c0f10020f7) 374 | 375 | step5) 376 | Specify the font style as required. 377 | ![WFONTX64-4](https://github.com/user-attachments/assets/0ff3072d-6a78-48ae-b855-60c692f8d771) 378 | 379 | step6) 380 | Press the RUN button to convert TTF fonts to FONTX format. 381 | ![WFONTX64-5](https://github.com/user-attachments/assets/d9797e3d-1fd6-4504-b161-c1280f1242c0) 382 | 383 | step7) 384 | upload your font file to $HOME/esp-idf-ili9340/font directory. 385 | 386 | step8) 387 | add font to use 388 | ``` 389 | FontxFile fx16G[2]; 390 | FontxFile fx24G[2]; 391 | FontxFile fx32G[2]; 392 | //InitFontx(fx16G,"/spiffs/ILGH16XB.FNT",""); // 8x16Dot Gothic 393 | //InitFontx(fx24G,"/spiffs/ILGH24XB.FNT",""); // 12x24Dot Gothic 394 | //InitFontx(fx32G,"/spiffs/ILGH32XB.FNT",""); // 16x32Dot Gothic 395 | InitFontx(fx16G,"/spiffs/Gigi16.FNT",""); // 8x16Dot Gigi 396 | InitFontx(fx24G,"/spiffs/Gigi24.FNT",""); // 12x24Dot Gigi 397 | InitFontx(fx32G,"/spiffs/Gigi32.FNT",""); // 16x32Dot Gigi 398 | 399 | ``` 400 | 401 | ![ttf_font](https://github.com/user-attachments/assets/9f01959b-b083-4a67-806f-86909630ad32) 402 | 403 | # How to add your color 404 | Change here. 405 | ``` 406 | #define RED rgb565(255, 0, 0) // 0xf800 407 | #define GREEN rgb565( 0, 255, 0) // 0x07e0 408 | #define BLUE rgb565( 0, 0, 255) // 0x001f 409 | #define BLACK rgb565( 0, 0, 0) // 0x0000 410 | #define WHITE rgb565(255, 255, 255) // 0xffff 411 | #define GRAY rgb565(128, 128, 128) // 0x8410 412 | #define YELLOW rgb565(255, 255, 0) // 0xFFE0 413 | #define CYAN rgb565( 0, 156, 209) // 0x04FA 414 | #define PURPLE rgb565(128, 0, 128) // 0x8010 415 | ``` 416 | 417 | --- 418 | 419 | # XPT2046 Touch Screen 420 | A library of XPT2046 Touch Screen is included in this project. 421 | There is a TFT equipped with XPT2046. 422 | ![XPT2046-3](https://user-images.githubusercontent.com/6020549/144333924-5236bff3-3f4d-4be4-8e99-b6e31878e4f3.jpg) 423 | 424 | XPT2046 shares the TFT and SPI bus. 425 | Use the menu to enable XPT2046. 426 | ![config-xpt2046-1](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/61323301-4b6a-4012-84f8-ee95dadb2db0) 427 | 428 | - Touch position accuacy 429 | The coordinates read from XPT2046 are physical coordinates. 430 | Physical coordinates are converted to logical coordinates. 431 | Then draw using logical coordinates. 432 | In TouchPenTest, when you touch the screen, a circle is drawn at the touched position, but if you touch the same position as the previous time, it is not drawn. 433 | This value is the threshold that determines whether the touch location is the same as the previous touch location. 434 | Decreasing this value will make the position more accurate, but less responsive. 435 | Increasing this value will make the position more inaccurate but more responsive. 436 | 437 | 438 | ### HR2046 Chip 439 | There is a TFT equipped with HR2046. 440 | XPT2046 and HR2046 are very similar. But HR2046 does not work properly. 441 | ![XPT2046-2](https://user-images.githubusercontent.com/6020549/144332571-717f33b1-df03-4a0a-9a23-c7c99b9d4d32.JPG) 442 | 443 | 444 | ### Wiring for XPT2046 445 | 446 | |TFT||ESP32|ESP32-S2/S3|ESP32-C2/C3|| 447 | |:-:|:-:|:-:|:-:|:-:|:-:| 448 | |VCC|--|3.3V|3.3V|3V3|| 449 | |GND|--|GND|GND|GND|| 450 | |CS|--|GPIO14|GPIO34|GPIO2|| 451 | |RES|--|GPIO33|GPIO41|GPIO4|(*1)| 452 | |D/C|--|GPIO27|GPIO40|GPIO3|(*1)| 453 | |MOSI|--|GPIO23|GPIO35|GPIO0|(*1) (*2)| 454 | |SCK|--|GPIO18|GPIO36|GPIO1|(*1) (*2)| 455 | |LED|--|3.3V|3.3V|3.3V|(*1) (*3)| 456 | |MISO|--|N/C|N/C|N/C|| 457 | |T_CLK|--|GPIO18|GPIO36|GPIO1|(*1) (*2)| 458 | |T_CS|--|GPIO21|GPIO38|GPIO7|(*1) (*4)| 459 | |T_DIN|--|GPIO23|GPIO35|GPIO0|(*1) (*2)| 460 | |T_OUT|--|GPIO19|GPIO37|GPIO6|(*1) (*4)| 461 | |T_IRQ|--|GPIO22|GPIO39|GPIO8|(*1) (*4)| 462 | 463 | (*1) You can change it to any gpio using menuconfig. But some gpio's are input only. 464 | 465 | (*2) These are shared by TFT and XPT2046. 466 | 467 | (*3) It can be controlled using gpio. However, GPIO of ESP32 cannot supply too much current. TFT backlight becomes brighter when powered by an external power source. 468 | 469 | (*4) I found that there are limits to the GPIOs that can be used as touch panel controls. 470 | 471 | ### Check if XPT2046 works properly 472 | You can check if XPT2046 works properly. 473 | ![config-xpt2046-2](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/4fe1f528-c443-4e93-a22f-e85f9f02ef84) 474 | 475 | If you touch it at this time, the touched coordinates will be displayed. 476 | If there is no touch for 10 seconds, it will end. 477 | ![TouchPosition-1](https://user-images.githubusercontent.com/6020549/147400381-cdd61863-c619-45e0-95ee-2aba593b22da.jpg) 478 | 479 | Move the touch-pen vertically and horizontally to check the X and Y coordinates. 480 | What you get here is the physical coordinates. 481 | See [here](https://github.com/nopnop2002/esp-idf-ili9340/issues/39) about physical coordinates. 482 | ![TouchPosition-2](https://user-images.githubusercontent.com/6020549/147400385-20b035b4-653a-4605-9d36-6d325a1f68a3.jpg) 483 | 484 | ### Using ESP32-3248S035 485 | This module also has an XPT2046. 486 | ![ESP32-3248S035-2](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/56d7bc3b-ab03-4486-bd51-bc16d6138d1c) 487 | ![ESP32-3248S035-3](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/bed91135-fd74-4789-949e-f9c8398b565f) 488 | 489 | XPT2046 uses the same SPI bus as TFT. 490 | XPT2046's SCLK and MOSI use the same GPIO as the TFT. 491 | ![config-ESP32-3248S035-2](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/7388f072-8e4f-43c0-b8c4-545bf774f0db) 492 | 493 | ### Using ESP32-2432S028R 494 | This module also has an XPT2046. 495 | ![ESP32-2432S028R-2](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/af351771-6c08-4949-b541-1c9d35e97c8e) 496 | ![ESP32-2432S028R-3](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/4cdd0bed-a248-4a69-83aa-5c44e2f2e02d) 497 | 498 | XPT2046 uses a different SPI bus than TFT. 499 | XPT2046's SCLK and MOSI use separate GPIOs from the TFT. 500 | ![config-ESP32-2432S028R-2](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/65679e0c-c08f-4dca-bc2a-13f9f52dadd4) 501 | 502 | ### Calibration 503 | Keep touching the point. 504 | ![XPT2046-1](https://user-images.githubusercontent.com/6020549/145127021-6f311ab1-0def-4577-9212-39b3848756ae.JPG) 505 | ![XPT2046-2](https://user-images.githubusercontent.com/6020549/145127026-be6496bc-90d6-48f7-8a0e-da7c1a7ac618.JPG) 506 | 507 | ### Draw with touch 508 | If there is no touch for 10 seconds, it will end. 509 | ![TouchPen-1](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/83c25554-aa45-49cd-b8d7-6eda9303b31d) 510 | 511 | ### Button with touch 512 | You can only enter up to 15 characters. 513 | If there is no touch for 10 seconds, it will end. 514 | ![TouchKeyboard](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/fdba50c7-e349-4fba-8b70-5d50a96aa708) 515 | 516 | ### Move with touch 517 | If there is no touch for 10 seconds, it will end. 518 | ![TouchMove-1](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/d6cd524d-b576-4109-bcf7-98e26f36f99c) 519 | ![TouchMove-2](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/7be77525-9f61-4deb-9025-f502ab6df76a) 520 | 521 | ### Menu with Touch 522 | If there is no touch for 10 seconds, it will end. 523 | ![TouchMenu-1](https://github.com/user-attachments/assets/bf57ac5a-65a1-4081-b172-01e7771cef06) 524 | ![TouchMenu-2](https://github.com/user-attachments/assets/69ed7f60-cf73-4c7e-ac24-a1122ec71a0d) 525 | 526 | ### Select with touch 527 | If there is no touch for 10 seconds, it will end. 528 | I borrowed the icon from [here](https://www.flaticon.com/packs/social-media-343). 529 | ![TouchIcon-1](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/e0aa7955-adad-4b33-93aa-a283585e2cf1) 530 | ![TouchIcon-2](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/e909df95-1a02-4fa6-a973-b6baf24c9c20) 531 | 532 | ### Save calibration data to NVS 533 | 534 | Write calibration data to NVS. 535 | Read calibration data from NVS when starting the firmware and use it. 536 | If you use the same TFT, you don't need to calibrate again. 537 | To clear the calibration data recorded in NVS, execute the following command. 538 | ``` 539 | idf.py erase_flash 540 | ``` 541 | 542 | ![config-xpt2046-4](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/27bc35da-db6a-460d-ba8a-311d4bb72637) 543 | 544 | # SPI Clock speed 545 | According to the datasheet, the minimum SPI clock cycles for each driver are as follows: 546 | Maximum SPI clock frequency is the reciprocal of this. 547 | |Driver|minimum SPI clock cycle|maximum SPI clock frequency| 548 | |:-:|:-:|:-:| 549 | |ILI9225|100ns|10MHz| 550 | |ILI9340|66ns|15MHz| 551 | |ILI9341|100ns|10MHz| 552 | |ST7735|66ns|15MHz| 553 | |ST7796|66ns|15MHz| 554 | 555 | The SPI clock frequency used by this project is 10MHz. 556 | Higher SPI clock frequencies can be specified using ```spi_clock_speed()```. 557 | When using higher SPI clock frequencies, you need to be careful about the length of the wire cable. 558 | ``` 559 | int speed = 15000000; // 15MHz 560 | spi_clock_speed(speed); 561 | spi_master_init(&dev, CONFIG_MOSI_GPIO, CONFIG_SCLK_GPIO, CONFIG_TFT_CS_GPIO, CONFIG_DC_GPIO, 562 | CONFIG_RESET_GPIO, CONFIG_BL_GPIO, XPT_MISO_GPIO, XPT_CS_GPIO, XPT_IRQ_GPIO, XPT_SCLK_GPIO, XPT_MOSI_GPIO); 563 | lcdInit(&dev, model, CONFIG_WIDTH, CONFIG_HEIGHT, CONFIG_OFFSETX, CONFIG_OFFSETY); 564 | ``` 565 | 566 | # SPI BUS selection 567 | ![config-spi-bus](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/bdfd8436-31c7-435f-b21d-3d4b8e5350d3) 568 | 569 | The ESP32 series has three SPI BUSs. 570 | SPI1_HOST is used for communication with Flash memory. 571 | You can use SPI2_HOST and SPI3_HOST freely. 572 | When you use SDSPI(SD Card via SPI), SDSPI uses SPI2_HOST BUS. 573 | When using this module at the same time as SDSPI or other SPI device using SPI2_HOST, it needs to be changed to SPI3_HOST. 574 | When you don't use SDSPI, both SPI2_HOST and SPI3_HOST will work. 575 | Previously it was called HSPI_HOST / VSPI_HOST, but now it is called SPI2_HOST / SPI3_HOST. 576 | 577 | # Using Frame Buffer 578 | ![config_framebuffer](https://github.com/user-attachments/assets/21c3b031-0796-4227-b48e-47bb7537e3b8) 579 | 580 | When FrameBuffer is enabled, all output will be stored in the internal FrameBuffer and reflected to the device with ```lcdDrawFinish```. 581 | If you don't use FrameBuffer, ```lcdDrawFinish``` does nothing. 582 | If your main purpose is to display text, it's well worth using FrameBuffer. 583 | If your main purpose is to display images, there is no value in using FrameBuffer. 584 | Enabling FrameBuffer does not make image display faster. 585 | This is because image analysis takes time. 586 | 587 | This option requires large RAM space. 588 | If your SoC has PSRAM, you can avoid running out of memory by enabling PSRAM. 589 | ![config-psram](https://github.com/user-attachments/assets/a71b320d-f5fb-4cd5-9466-f191b8704d40) 590 | 591 | Benchmarking using ESP32-S2@240 & 2.4 inch TFT 592 | 593 | ||Disable Frame Buffer|Enable Frame Buffer| 594 | |:-:|:-:|:-:| 595 | |FillTest|1390|1450| 596 | |ColorBarTest|150|160| 597 | |ArrowTest|330|160| 598 | |LineTest|2670|180| 599 | |CircleTest|2390|190| 600 | |RoundRectTest|2430|180| 601 | |DirectionTest|500|160| 602 | |HorizontalTest|1110|170| 603 | |VerticalTest|1100|180| 604 | |FillRectTest|300|180| 605 | |ColorTest|370|170| 606 | |CodeTest|1760|180| 607 | |BMPTest|3000|2810| 608 | |JPEGTest|2760|2760| 609 | |PNGTest|2940|2960| 610 | |IconTest|740|870| 611 | 612 | # How to use this component in your project 613 | Create idf_component.yml in the same directory as main.c. 614 | ``` 615 | YourProject --+-- CMakeLists.txt 616 | +-- main --+-- main.c 617 | +-- CMakeLists.txt 618 | +-- idf_component.yml 619 | ``` 620 | 621 | Contents of idf_component.yml. 622 | ``` 623 | dependencies: 624 | nopnop2002/ili9340: 625 | path: components/ili9340/ 626 | git: https://github.com/nopnop2002/esp-idf-ili9340.git 627 | ``` 628 | 629 | When you build a projects esp-idf will automaticly fetch repository to managed_components dir and link with your code. 630 | ``` 631 | YourProject --+-- CMakeLists.txt 632 | +-- main --+-- main.c 633 | | +-- CMakeLists.txt 634 | | +-- idf_component.yml 635 | +-- managed_components ----- nopnop2002__ili9340 636 | ``` 637 | 638 | --- 639 | 640 | # Using SPI TFT Adapter 641 | 642 | I purchased this adapter on AliExpress. 643 | It comes with an ESP32 and costs $4. 644 | ![14pin-adapter-1](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/f73208ee-2c41-4541-bffd-e731e14cbff3) 645 | ![14pin-adapter-2](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/a989ec43-41f4-4929-9352-6d148f1d6e60) 646 | 647 | However, this adapter's T_IRQ is not connected anywhere. 648 | To use the touch panel, you need to add a jumper. 649 | ![14pin-adapter-3](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/fba70ca5-1ab1-4f0c-8962-1b4efb195b35) 650 | 651 | As a result, the GPIO will be: 652 | |TFT||ESP32|| 653 | |:-:|:-:|:-:|:-:| 654 | |VCC|--|3.3V|| 655 | |GND|--|GND|| 656 | |CS|--|GPIO15|| 657 | |RES|--|GPIO04|| 658 | |D/C|--|GPIO02|| 659 | |MOSI|--|GPIO23|| 660 | |SCK|--|GPIO18|| 661 | |LED|--|3.3V|*1| 662 | |MISO|--|N/C|| 663 | |T_CLK|--|GPIO18|| 664 | |T_CS|--|GPIO05|| 665 | |T_DIN|--|GPIO23|| 666 | |T_OUT|--|GPIO19|| 667 | |T_IRQ|--|GPIO21|| 668 | 669 | (*1) BL_EN is directly connected to 3.3V, so it cannot control BackLight. 670 | 671 | When not using the touch panel. 672 | ![config-14pin-adapter-1](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/b9d4d2a1-5e58-439f-b2ef-69a85e1810ae) 673 | 674 | When using the touch panel. 675 | ![config-14pin-adapter-2](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/5b7664e2-c1c1-4cfa-9979-59856ea37d42) 676 | 677 | ![14pin-adapter-4](https://github.com/nopnop2002/esp-idf-ili9340/assets/6020549/9a098ae6-aa09-4ee5-9568-18f06260e4a9) 678 | 679 | 680 | --- 681 | 682 | # Reference 683 | A TFT shield like this can be used with ESP-IDF. 684 | ![TFT-Shield](https://user-images.githubusercontent.com/6020549/104253960-10a71380-54b9-11eb-8789-a12c2c769ab4.JPG) 685 | 686 | It can be attached directly to this ESP32. 687 | ![tzt-d1-esp32s3](https://github.com/nopnop2002/esp-idf-parallel-tft/assets/6020549/ba6772f1-c8b7-4e7d-9d0a-0fb8fb8cbea5) 688 | 689 | https://github.com/nopnop2002/esp-idf-parallel-tft 690 | 691 | -------------------------------------------------------------------------------- /components/ili9340/ili9340.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "freertos/FreeRTOS.h" 7 | #include "freertos/task.h" 8 | 9 | #include 10 | #include 11 | #include "rom/ets_sys.h" // ets_get_cpu_frequency() 12 | #include "esp_log.h" 13 | 14 | #include "ili9340.h" 15 | 16 | #define TAG "ILI9340" 17 | #define _DEBUG_ 0 18 | 19 | #if CONFIG_SPI2_HOST 20 | #define TFT_ID SPI2_HOST 21 | #elif CONFIG_SPI3_HOST 22 | #define TFT_ID SPI3_HOST 23 | #else 24 | #define TFT_ID SPI2_HOST // When not to use menuconfig 25 | #define XPT_ID SPI3_HOST // When not to use menuconfig 26 | #endif 27 | 28 | #define SPI_DEFAULT_FREQUENCY SPI_MASTER_FREQ_10M; // 10MHz 29 | 30 | //static const int TFT_MOSI = 23; 31 | //static const int TFT_SCLK = 18; 32 | 33 | static const int SPI_Command_Mode = 0; 34 | static const int SPI_Data_Mode = 1; 35 | //static const int SPI_Frequency = SPI_MASTER_FREQ_10M; 36 | //static const int SPI_Frequency = SPI_MASTER_FREQ_20M; 37 | //static const int SPI_Frequency = SPI_MASTER_FREQ_26M; 38 | //static const int SPI_Frequency = SPI_MASTER_FREQ_40M; 39 | 40 | int clock_speed_hz = SPI_DEFAULT_FREQUENCY; 41 | 42 | #if CONFIG_XPT2046_ENABLE_SAME_BUS || CONFIG_XPT2046_ENABLE_DIFF_BUS 43 | static const int XPT_Frequency = 1*1000*1000; 44 | //static const int XPT_Frequency = 2*1000*1000; 45 | //static const int XPT_Frequency = 4*1000*1000; 46 | 47 | //#define XPT_MISO 19 48 | //#define XPT_CS 4 49 | //#define XPT_IRQ 5 50 | #endif 51 | 52 | void spi_clock_speed(int speed) { 53 | ESP_LOGI(TAG, "SPI clock speed=%d MHz", speed/1000000); 54 | clock_speed_hz = speed; 55 | } 56 | 57 | void spi_master_init(TFT_t * dev, int16_t TFT_MOSI, int16_t TFT_SCLK, int16_t TFT_CS, int16_t GPIO_DC, int16_t GPIO_RESET, int16_t GPIO_BL, 58 | int16_t XPT_MISO, int16_t XPT_CS, int16_t XPT_IRQ, int16_t XPT_SCLK, int16_t XPT_MOSI) 59 | { 60 | esp_err_t ret; 61 | 62 | ESP_LOGI(TAG, "TFT_MOSI=%d",TFT_MOSI); 63 | ESP_LOGI(TAG, "TFT_SCLK=%d",TFT_SCLK); 64 | ESP_LOGI(TAG, "TFT_CS=%d",TFT_CS); 65 | gpio_reset_pin( TFT_CS ); 66 | gpio_set_direction( TFT_CS, GPIO_MODE_OUTPUT ); 67 | //gpio_set_level( TFT_CS, 0 ); 68 | gpio_set_level( TFT_CS, 1 ); 69 | 70 | ESP_LOGI(TAG, "GPIO_DC=%d",GPIO_DC); 71 | gpio_reset_pin( GPIO_DC ); 72 | gpio_set_direction( GPIO_DC, GPIO_MODE_OUTPUT ); 73 | gpio_set_level( GPIO_DC, 0 ); 74 | 75 | ESP_LOGI(TAG, "GPIO_RESET=%d",GPIO_RESET); 76 | if ( GPIO_RESET >= 0 ) { 77 | gpio_reset_pin( GPIO_RESET ); 78 | gpio_set_direction( GPIO_RESET, GPIO_MODE_OUTPUT ); 79 | gpio_set_level( GPIO_RESET, 0 ); 80 | vTaskDelay( pdMS_TO_TICKS( 100 ) ); 81 | gpio_set_level( GPIO_RESET, 1 ); 82 | } 83 | 84 | ESP_LOGI(TAG, "GPIO_BL=%d",GPIO_BL); 85 | if ( GPIO_BL >= 0 ) { 86 | gpio_reset_pin( GPIO_BL ); 87 | gpio_set_direction( GPIO_BL, GPIO_MODE_OUTPUT ); 88 | gpio_set_level( GPIO_BL, 0 ); 89 | } 90 | 91 | #if CONFIG_XPT2046_ENABLE_SAME_BUS 92 | spi_bus_config_t tft_buscfg = { 93 | .sclk_io_num = TFT_SCLK, 94 | .mosi_io_num = TFT_MOSI, 95 | .miso_io_num = XPT_MISO, 96 | .quadwp_io_num = -1, 97 | .quadhd_io_num = -1 98 | }; 99 | #else 100 | spi_bus_config_t tft_buscfg = { 101 | .sclk_io_num = TFT_SCLK, 102 | .mosi_io_num = TFT_MOSI, 103 | .miso_io_num = -1, 104 | .quadwp_io_num = -1, 105 | .quadhd_io_num = -1 106 | }; 107 | #endif 108 | 109 | ret = spi_bus_initialize( TFT_ID, &tft_buscfg, SPI_DMA_CH_AUTO ); 110 | ESP_LOGI(TAG, "spi_bus_initialize(TFT) ret=%d TFT_ID=%d",ret, TFT_ID); 111 | assert(ret==ESP_OK); 112 | 113 | spi_device_interface_config_t tft_devcfg={ 114 | //.clock_speed_hz = SPI_Frequency, 115 | .clock_speed_hz = clock_speed_hz, 116 | .spics_io_num = TFT_CS, 117 | .queue_size = 7, 118 | .flags = SPI_DEVICE_NO_DUMMY, 119 | }; 120 | 121 | spi_device_handle_t tft_handle; 122 | ret = spi_bus_add_device( TFT_ID, &tft_devcfg, &tft_handle); 123 | ESP_LOGD(TAG, "spi_bus_add_device=%d",ret); 124 | assert(ret==ESP_OK); 125 | dev->_dc = GPIO_DC; 126 | dev->_bl = GPIO_BL; 127 | dev->_TFT_Handle = tft_handle; 128 | 129 | #if CONFIG_XPT2046_ENABLE_DIFF_BUS 130 | ESP_LOGI(TAG, "XPT_SCLK=%d",XPT_SCLK); 131 | ESP_LOGI(TAG, "XPT_MOSI=%d",XPT_MOSI); 132 | ESP_LOGI(TAG, "XPT_MISO=%d",XPT_MISO); 133 | spi_bus_config_t xpt_buscfg = { 134 | .sclk_io_num = XPT_SCLK, 135 | .mosi_io_num = XPT_MOSI, 136 | .miso_io_num = XPT_MISO, 137 | .quadwp_io_num = -1, 138 | .quadhd_io_num = -1 139 | }; 140 | 141 | ret = spi_bus_initialize( XPT_ID, &xpt_buscfg, SPI_DMA_CH_AUTO ); 142 | ESP_LOGI(TAG, "spi_bus_initialize(XPT) ret=%d XPT_ID=%d",ret, XPT_ID); 143 | assert(ret==ESP_OK); 144 | #endif 145 | 146 | #if CONFIG_XPT2046_ENABLE_SAME_BUS || CONFIG_XPT2046_ENABLE_DIFF_BUS 147 | ESP_LOGI(TAG, "XPT_CS=%d",XPT_CS); 148 | gpio_reset_pin( XPT_CS ); 149 | gpio_set_direction( XPT_CS, GPIO_MODE_OUTPUT ); 150 | gpio_set_level( XPT_CS, 1 ); 151 | 152 | // set the IRQ as a input 153 | ESP_LOGI(TAG, "XPT_IRQ=%d",XPT_IRQ); 154 | gpio_config_t io_conf = {}; 155 | io_conf.intr_type = GPIO_INTR_DISABLE; 156 | io_conf.pin_bit_mask = (1ULL<_XPT_Handle = xpt_handle; 179 | dev->_irq = XPT_IRQ; 180 | dev->_calibration = true; 181 | #endif 182 | } 183 | 184 | 185 | bool spi_master_write_byte(spi_device_handle_t SPIHandle, const uint8_t* Data, size_t DataLength) 186 | { 187 | spi_transaction_t SPITransaction; 188 | esp_err_t ret; 189 | 190 | if ( DataLength > 0 ) { 191 | memset( &SPITransaction, 0, sizeof( spi_transaction_t ) ); 192 | SPITransaction.length = DataLength * 8; 193 | SPITransaction.tx_buffer = Data; 194 | #if 1 195 | ret = spi_device_transmit( SPIHandle, &SPITransaction ); 196 | #endif 197 | #if 0 198 | ret = spi_device_polling_transmit( SPIHandle, &SPITransaction ); 199 | #endif 200 | assert(ret==ESP_OK); 201 | } 202 | 203 | return true; 204 | } 205 | 206 | bool spi_master_write_comm_byte(TFT_t * dev, uint8_t cmd) 207 | { 208 | static uint8_t Byte = 0; 209 | Byte = cmd; 210 | gpio_set_level( dev->_dc, SPI_Command_Mode ); 211 | return spi_master_write_byte( dev->_TFT_Handle, &Byte, 1 ); 212 | } 213 | 214 | bool spi_master_write_comm_word(TFT_t * dev, uint16_t cmd) 215 | { 216 | static uint8_t Byte[2]; 217 | Byte[0] = (cmd >> 8) & 0xFF; 218 | Byte[1] = cmd & 0xFF; 219 | gpio_set_level( dev->_dc, SPI_Command_Mode ); 220 | return spi_master_write_byte( dev->_TFT_Handle, Byte, 2 ); 221 | } 222 | 223 | 224 | bool spi_master_write_data_byte(TFT_t * dev, uint8_t data) 225 | { 226 | static uint8_t Byte = 0; 227 | Byte = data; 228 | gpio_set_level( dev->_dc, SPI_Data_Mode ); 229 | return spi_master_write_byte( dev->_TFT_Handle, &Byte, 1 ); 230 | } 231 | 232 | 233 | bool spi_master_write_data_word(TFT_t * dev, uint16_t data) 234 | { 235 | static uint8_t Byte[2]; 236 | Byte[0] = (data >> 8) & 0xFF; 237 | Byte[1] = data & 0xFF; 238 | gpio_set_level( dev->_dc, SPI_Data_Mode ); 239 | return spi_master_write_byte( dev->_TFT_Handle, Byte, 2); 240 | } 241 | 242 | bool spi_master_write_addr(TFT_t * dev, uint16_t addr1, uint16_t addr2) 243 | { 244 | static uint8_t Byte[4]; 245 | Byte[0] = (addr1 >> 8) & 0xFF; 246 | Byte[1] = addr1 & 0xFF; 247 | Byte[2] = (addr2 >> 8) & 0xFF; 248 | Byte[3] = addr2 & 0xFF; 249 | gpio_set_level( dev->_dc, SPI_Data_Mode ); 250 | return spi_master_write_byte( dev->_TFT_Handle, Byte, 4); 251 | } 252 | 253 | bool spi_master_write_color(TFT_t * dev, uint16_t color, uint16_t size) 254 | { 255 | static uint8_t Byte[1024]; 256 | int index = 0; 257 | for(int i=0;i> 8) & 0xFF; 259 | Byte[index++] = color & 0xFF; 260 | } 261 | gpio_set_level( dev->_dc, SPI_Data_Mode ); 262 | return spi_master_write_byte( dev->_TFT_Handle, Byte, size*2); 263 | } 264 | 265 | // Add 202001 266 | bool spi_master_write_colors(TFT_t * dev, uint16_t * colors, uint16_t size) 267 | { 268 | static uint8_t Byte[1024]; 269 | int index = 0; 270 | for(int i=0;i> 8) & 0xFF; 272 | Byte[index++] = colors[i] & 0xFF; 273 | } 274 | gpio_set_level( dev->_dc, SPI_Data_Mode ); 275 | return spi_master_write_byte( dev->_TFT_Handle, Byte, size*2); 276 | } 277 | 278 | 279 | void delayMS(int ms) { 280 | int _ms = ms + (portTICK_PERIOD_MS - 1); 281 | TickType_t xTicksToDelay = _ms / portTICK_PERIOD_MS; 282 | ESP_LOGD(TAG, "ms=%d _ms=%d portTICK_PERIOD_MS=%"PRIu32" xTicksToDelay=%"PRIu32,ms,_ms,portTICK_PERIOD_MS,xTicksToDelay); 283 | vTaskDelay(xTicksToDelay); 284 | } 285 | 286 | 287 | void lcdWriteRegisterWord(TFT_t * dev, uint16_t addr, uint16_t data) 288 | { 289 | spi_master_write_comm_word(dev, addr); 290 | spi_master_write_data_word(dev, data); 291 | } 292 | 293 | 294 | void lcdWriteRegisterByte(TFT_t * dev, uint8_t addr, uint16_t data) 295 | { 296 | spi_master_write_comm_byte(dev, addr); 297 | spi_master_write_data_word(dev, data); 298 | } 299 | 300 | 301 | 302 | void lcdInit(TFT_t * dev, uint16_t model, int width, int height, int offsetx, int offsety) 303 | { 304 | dev->_model = model; 305 | dev->_width = width; 306 | dev->_height = height; 307 | dev->_offsetx = offsetx; 308 | dev->_offsety = offsety; 309 | dev->_font_direction = DIRECTION0; 310 | dev->_font_fill = false; 311 | dev->_font_underline = false; 312 | 313 | if (dev->_model == 0x7789 || dev->_model == 0x7796) { 314 | if (dev->_model == 0x7789) ESP_LOGI(TAG,"Your TFT is ST7789"); 315 | if (dev->_model == 0x7796) ESP_LOGI(TAG,"Your TFT is ST7796"); 316 | ESP_LOGI(TAG,"Screen width:%d",width); 317 | ESP_LOGI(TAG,"Screen height:%d",height); 318 | spi_master_write_comm_byte(dev, 0xC0); //Power Control 1 319 | spi_master_write_data_byte(dev, 0x10); 320 | spi_master_write_data_byte(dev, 0x10); 321 | 322 | spi_master_write_comm_byte(dev, 0xC1); //Power Control 2 323 | spi_master_write_data_byte(dev, 0x41); 324 | 325 | spi_master_write_comm_byte(dev, 0xC5); //VCOM Control 1 326 | spi_master_write_data_byte(dev, 0x00); 327 | spi_master_write_data_byte(dev, 0x22); 328 | spi_master_write_data_byte(dev, 0x80); 329 | spi_master_write_data_byte(dev, 0x40); 330 | 331 | spi_master_write_comm_byte(dev, 0x36); //Memory Access Control 332 | spi_master_write_data_byte(dev, 0x48); //Right top start, BGR color filter panel 333 | //spi_master_write_data_byte(dev, 0x68); //Right top start, BGR color filter panel 334 | 335 | spi_master_write_comm_byte(dev, 0xB0); //Interface Mode Control 336 | spi_master_write_data_byte(dev, 0x00); 337 | 338 | spi_master_write_comm_byte(dev, 0xB1); //Frame Rate Control 339 | spi_master_write_data_byte(dev, 0xB0); 340 | spi_master_write_data_byte(dev, 0x11); 341 | 342 | spi_master_write_comm_byte(dev, 0xB4); //Display Inversion Control 343 | spi_master_write_data_byte(dev, 0x02); 344 | 345 | spi_master_write_comm_byte(dev, 0xB6); //Display Function Control 346 | spi_master_write_data_byte(dev, 0x02); 347 | spi_master_write_data_byte(dev, 0x02); 348 | spi_master_write_data_byte(dev, 0x3B); 349 | 350 | spi_master_write_comm_byte(dev, 0xB7); //Entry Mode Set 351 | spi_master_write_data_byte(dev, 0xC6); 352 | 353 | spi_master_write_comm_byte(dev, 0x3A); //Interface Pixel Format 354 | spi_master_write_data_byte(dev, 0x55); 355 | 356 | spi_master_write_comm_byte(dev, 0xF7); //Adjust Control 3 357 | spi_master_write_data_byte(dev, 0xA9); 358 | spi_master_write_data_byte(dev, 0x51); 359 | spi_master_write_data_byte(dev, 0x2C); 360 | spi_master_write_data_byte(dev, 0x82); 361 | 362 | if (dev->_model == 0x7789) { 363 | spi_master_write_comm_byte(dev, 0x21); //Display Inversion ON 364 | } 365 | 366 | spi_master_write_comm_byte(dev, 0x11); //Sleep Out 367 | delayMS(120); 368 | 369 | spi_master_write_comm_byte(dev, 0x29); //Display ON 370 | } // endif 0x7789/0x7796 371 | 372 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7735) { 373 | if (dev->_model == 0x9340) ESP_LOGI(TAG,"Your TFT is ILI9340"); 374 | if (dev->_model == 0x9341) ESP_LOGI(TAG,"Your TFT is ILI9341"); 375 | if (dev->_model == 0x7735) ESP_LOGI(TAG,"Your TFT is ST7735"); 376 | ESP_LOGI(TAG,"Screen width:%d",width); 377 | ESP_LOGI(TAG,"Screen height:%d",height); 378 | spi_master_write_comm_byte(dev, 0xC0); //Power Control 1 379 | spi_master_write_data_byte(dev, 0x23); 380 | 381 | spi_master_write_comm_byte(dev, 0xC1); //Power Control 2 382 | spi_master_write_data_byte(dev, 0x10); 383 | 384 | spi_master_write_comm_byte(dev, 0xC5); //VCOM Control 1 385 | spi_master_write_data_byte(dev, 0x3E); 386 | spi_master_write_data_byte(dev, 0x28); 387 | 388 | spi_master_write_comm_byte(dev, 0xC7); //VCOM Control 2 389 | spi_master_write_data_byte(dev, 0x86); 390 | 391 | spi_master_write_comm_byte(dev, 0x36); //Memory Access Control 392 | spi_master_write_data_byte(dev, 0x08); //Right top start, BGR color filter panel 393 | //spi_master_write_data_byte(dev, 0x00); //Right top start, RGB color filter panel 394 | 395 | spi_master_write_comm_byte(dev, 0x3A); //Pixel Format Set 396 | spi_master_write_data_byte(dev, 0x55); //65K color: 16-bit/pixel 397 | 398 | spi_master_write_comm_byte(dev, 0x20); //Display Inversion OFF 399 | 400 | spi_master_write_comm_byte(dev, 0xB1); //Frame Rate Control 401 | spi_master_write_data_byte(dev, 0x00); 402 | spi_master_write_data_byte(dev, 0x18); 403 | 404 | spi_master_write_comm_byte(dev, 0xB6); //Display Function Control 405 | spi_master_write_data_byte(dev, 0x08); 406 | spi_master_write_data_byte(dev, 0xA2); // REV:1 GS:0 SS:0 SM:0 407 | spi_master_write_data_byte(dev, 0x27); 408 | spi_master_write_data_byte(dev, 0x00); 409 | 410 | spi_master_write_comm_byte(dev, 0x26); //Gamma Set 411 | spi_master_write_data_byte(dev, 0x01); 412 | 413 | spi_master_write_comm_byte(dev, 0xE0); //Positive Gamma Correction 414 | spi_master_write_data_byte(dev, 0x0F); 415 | spi_master_write_data_byte(dev, 0x31); 416 | spi_master_write_data_byte(dev, 0x2B); 417 | spi_master_write_data_byte(dev, 0x0C); 418 | spi_master_write_data_byte(dev, 0x0E); 419 | spi_master_write_data_byte(dev, 0x08); 420 | spi_master_write_data_byte(dev, 0x4E); 421 | spi_master_write_data_byte(dev, 0xF1); 422 | spi_master_write_data_byte(dev, 0x37); 423 | spi_master_write_data_byte(dev, 0x07); 424 | spi_master_write_data_byte(dev, 0x10); 425 | spi_master_write_data_byte(dev, 0x03); 426 | spi_master_write_data_byte(dev, 0x0E); 427 | spi_master_write_data_byte(dev, 0x09); 428 | spi_master_write_data_byte(dev, 0x00); 429 | 430 | spi_master_write_comm_byte(dev, 0xE1); //Negative Gamma Correction 431 | spi_master_write_data_byte(dev, 0x00); 432 | spi_master_write_data_byte(dev, 0x0E); 433 | spi_master_write_data_byte(dev, 0x14); 434 | spi_master_write_data_byte(dev, 0x03); 435 | spi_master_write_data_byte(dev, 0x11); 436 | spi_master_write_data_byte(dev, 0x07); 437 | spi_master_write_data_byte(dev, 0x31); 438 | spi_master_write_data_byte(dev, 0xC1); 439 | spi_master_write_data_byte(dev, 0x48); 440 | spi_master_write_data_byte(dev, 0x08); 441 | spi_master_write_data_byte(dev, 0x0F); 442 | spi_master_write_data_byte(dev, 0x0C); 443 | spi_master_write_data_byte(dev, 0x31); 444 | spi_master_write_data_byte(dev, 0x36); 445 | spi_master_write_data_byte(dev, 0x0F); 446 | 447 | spi_master_write_comm_byte(dev, 0x11); //Sleep Out 448 | delayMS(120); 449 | 450 | spi_master_write_comm_byte(dev, 0x29); //Display ON 451 | } // endif 0x9340/0x9341/0x7735 452 | 453 | if (dev->_model == 0x9225) { 454 | ESP_LOGI(TAG,"Your TFT is ILI9225"); 455 | ESP_LOGI(TAG,"Screen width:%d",width); 456 | ESP_LOGI(TAG,"Screen height:%d",height); 457 | lcdWriteRegisterByte(dev, 0x10, 0x0000); // Set SAP,DSTB,STB 458 | lcdWriteRegisterByte(dev, 0x11, 0x0000); // Set APON,PON,AON,VCI1EN,VC 459 | lcdWriteRegisterByte(dev, 0x12, 0x0000); // Set BT,DC1,DC2,DC3 460 | lcdWriteRegisterByte(dev, 0x13, 0x0000); // Set GVDD 461 | lcdWriteRegisterByte(dev, 0x14, 0x0000); // Set VCOMH/VCOML voltage 462 | delayMS(40); 463 | 464 | // Power-on sequence 465 | lcdWriteRegisterByte(dev, 0x11, 0x0018); // Set APON,PON,AON,VCI1EN,VC 466 | lcdWriteRegisterByte(dev, 0x12, 0x6121); // Set BT,DC1,DC2,DC3 467 | lcdWriteRegisterByte(dev, 0x13, 0x006F); // Set GVDD 468 | lcdWriteRegisterByte(dev, 0x14, 0x495F); // Set VCOMH/VCOML voltage 469 | lcdWriteRegisterByte(dev, 0x10, 0x0800); // Set SAP,DSTB,STB 470 | delayMS(10); 471 | lcdWriteRegisterByte(dev, 0x11, 0x103B); // Set APON,PON,AON,VCI1EN,VC 472 | delayMS(50); 473 | 474 | lcdWriteRegisterByte(dev, 0x01, 0x011C); // set the display line number and display direction 475 | lcdWriteRegisterByte(dev, 0x02, 0x0100); // set 1 line inversion 476 | lcdWriteRegisterByte(dev, 0x03, 0x1030); // set GRAM write direction and BGR=1. 477 | lcdWriteRegisterByte(dev, 0x07, 0x0000); // Display off 478 | lcdWriteRegisterByte(dev, 0x08, 0x0808); // set the back porch and front porch 479 | lcdWriteRegisterByte(dev, 0x0B, 0x1100); // set the clocks number per line 480 | lcdWriteRegisterByte(dev, 0x0C, 0x0000); // CPU interface 481 | //lcdWriteRegisterByte(dev, 0x0F, 0x0D01); // Set Osc 482 | lcdWriteRegisterByte(dev, 0x0F, 0x0801); // Set Osc 483 | lcdWriteRegisterByte(dev, 0x15, 0x0020); // Set VCI recycling 484 | lcdWriteRegisterByte(dev, 0x20, 0x0000); // RAM Address 485 | lcdWriteRegisterByte(dev, 0x21, 0x0000); // RAM Address 486 | 487 | // Set GRAM area 488 | lcdWriteRegisterByte(dev, 0x30, 0x0000); 489 | lcdWriteRegisterByte(dev, 0x31, 0x00DB); 490 | lcdWriteRegisterByte(dev, 0x32, 0x0000); 491 | lcdWriteRegisterByte(dev, 0x33, 0x0000); 492 | lcdWriteRegisterByte(dev, 0x34, 0x00DB); 493 | lcdWriteRegisterByte(dev, 0x35, 0x0000); 494 | lcdWriteRegisterByte(dev, 0x36, 0x00AF); 495 | lcdWriteRegisterByte(dev, 0x37, 0x0000); 496 | lcdWriteRegisterByte(dev, 0x38, 0x00DB); 497 | lcdWriteRegisterByte(dev, 0x39, 0x0000); 498 | 499 | // Adjust GAMMA Curve 500 | lcdWriteRegisterByte(dev, 0x50, 0x0000); 501 | lcdWriteRegisterByte(dev, 0x51, 0x0808); 502 | lcdWriteRegisterByte(dev, 0x52, 0x080A); 503 | lcdWriteRegisterByte(dev, 0x53, 0x000A); 504 | lcdWriteRegisterByte(dev, 0x54, 0x0A08); 505 | lcdWriteRegisterByte(dev, 0x55, 0x0808); 506 | lcdWriteRegisterByte(dev, 0x56, 0x0000); 507 | lcdWriteRegisterByte(dev, 0x57, 0x0A00); 508 | lcdWriteRegisterByte(dev, 0x58, 0x0710); 509 | lcdWriteRegisterByte(dev, 0x59, 0x0710); 510 | 511 | lcdWriteRegisterByte(dev, 0x07, 0x0012); 512 | delayMS(50); // Delay 50ms 513 | lcdWriteRegisterByte(dev, 0x07, 0x1017); 514 | } // endif 0x9225 515 | 516 | if(dev->_bl >= 0) { 517 | gpio_set_level( dev->_bl, 1 ); 518 | } 519 | 520 | dev->_use_frame_buffer = false; 521 | #if CONFIG_FRAME_BUFFER 522 | ESP_LOGI(TAG, "MALLOC_CAP_DEFAULT: %d bytes", heap_caps_get_free_size(MALLOC_CAP_DEFAULT)); 523 | ESP_LOGI(TAG, "MALLOC_CAP_INTERNAL: %d bytes", heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); 524 | ESP_LOGI(TAG, "MALLOC_CAP_SPIRAM: %d bytes", heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); 525 | ESP_LOGI(TAG, "Free heap size: %"PRIu32, esp_get_free_heap_size()); 526 | dev->_frame_buffer = heap_caps_malloc(sizeof(uint16_t)*width*height, MALLOC_CAP_DEFAULT); 527 | if (dev->_frame_buffer == NULL) { 528 | ESP_LOGE(TAG, "heap_caps_malloc fail. Frame buffer is not available."); 529 | } else { 530 | ESP_LOGI(TAG, "heap_caps_malloc success. Frame buffer is available."); 531 | dev->_use_frame_buffer = true; 532 | } 533 | #endif 534 | 535 | } 536 | 537 | // Get framebuffer status 538 | bool lcdIsFrameBuffer(TFT_t * dev) { 539 | return dev->_use_frame_buffer; 540 | } 541 | 542 | // Disable framebuffer 543 | void lcdDisableFrameBuffer(TFT_t * dev) { 544 | dev->_use_frame_buffer_evacuate = dev->_use_frame_buffer; 545 | dev->_use_frame_buffer = false; 546 | } 547 | 548 | // Resume framebuffer 549 | void lcdResumeFrameBuffer(TFT_t * dev) { 550 | dev->_use_frame_buffer = dev->_use_frame_buffer_evacuate; 551 | } 552 | 553 | // Get frame buffer 554 | void lcdGetFrameBuffer(TFT_t * dev, uint16_t *buffer) { 555 | if (dev->_use_frame_buffer) { 556 | memcpy((char *)buffer, (char *)dev->_frame_buffer, sizeof(uint16_t)*dev->_width*dev->_height); 557 | } 558 | } 559 | 560 | // Set frame buffer 561 | void lcdSetFrameBuffer(TFT_t * dev, uint16_t *buffer) { 562 | if (dev->_use_frame_buffer) { 563 | memcpy((char *)dev->_frame_buffer, (char *)buffer, sizeof(uint16_t)*dev->_width*dev->_height); 564 | } 565 | } 566 | 567 | // Draw pixel 568 | // x:X coordinate 569 | // y:Y coordinate 570 | // color:color 571 | void lcdDrawPixel(TFT_t * dev, uint16_t x, uint16_t y, uint16_t color){ 572 | if (x >= dev->_width) return; 573 | if (y >= dev->_height) return; 574 | 575 | if (dev->_use_frame_buffer) { 576 | dev->_frame_buffer[y*dev->_width+x] = color; 577 | } else { 578 | uint16_t _x = x + dev->_offsetx; 579 | uint16_t _y = y + dev->_offsety; 580 | 581 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7789 || dev->_model == 0x7796) { 582 | spi_master_write_comm_byte(dev, 0x2A); // set column(x) address 583 | spi_master_write_addr(dev, _x, _x); 584 | spi_master_write_comm_byte(dev, 0x2B); // set Page(y) address 585 | spi_master_write_addr(dev, _y, _y); 586 | spi_master_write_comm_byte(dev, 0x2C); // Memory Write 587 | spi_master_write_data_word(dev, color); 588 | } // endif 0x9340/0x9341/0x7789/0x7796 589 | 590 | if (dev->_model == 0x7735) { 591 | spi_master_write_comm_byte(dev, 0x2A); // set column(x) address 592 | spi_master_write_data_word(dev, _x); 593 | spi_master_write_data_word(dev, _x); 594 | spi_master_write_comm_byte(dev, 0x2B); // set Page(y) address 595 | spi_master_write_data_word(dev, _y); 596 | spi_master_write_data_word(dev, _y); 597 | spi_master_write_comm_byte(dev, 0x2C); // Memory Write 598 | spi_master_write_data_word(dev, color); 599 | } // endif 0x7735 600 | 601 | if (dev->_model == 0x9225) { 602 | lcdWriteRegisterByte(dev, 0x20, _x); 603 | lcdWriteRegisterByte(dev, 0x21, _y); 604 | spi_master_write_comm_byte(dev, 0x22); // Memory Write 605 | spi_master_write_data_word(dev, color); 606 | } // endif 0x9225 607 | 608 | } // endif dev->_use_frame_buffer 609 | } 610 | 611 | // Add 202001 612 | // Draw multi pixel 613 | // x:X coordinate 614 | // y:Y coordinate 615 | // size:Number of colors 616 | // colors:colors 617 | void lcdDrawMultiPixels(TFT_t * dev, uint16_t x, uint16_t y, uint16_t size, uint16_t * colors) { 618 | if (x+size > dev->_width) return; 619 | if (y >= dev->_height) return; 620 | 621 | if (dev->_use_frame_buffer) { 622 | uint16_t _x1 = x; 623 | uint16_t _x2 = _x1 + (size-1); 624 | uint16_t _y1 = y; 625 | uint16_t _y2 = _y1; 626 | int16_t index = 0; 627 | for (int16_t j = _y1; j <= _y2; j++){ 628 | for(int16_t i = _x1; i <= _x2; i++){ 629 | dev->_frame_buffer[j*dev->_width+i] = colors[index++]; 630 | } 631 | } 632 | } else { 633 | uint16_t _x1 = x + dev->_offsetx; 634 | uint16_t _x2 = _x1 + (size-1); 635 | uint16_t _y1 = y + dev->_offsety; 636 | uint16_t _y2 = _y1; 637 | 638 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7789 || dev->_model == 0x7796) { 639 | spi_master_write_comm_byte(dev, 0x2A); // set column(x) address 640 | spi_master_write_addr(dev, _x1, _x2); 641 | spi_master_write_comm_byte(dev, 0x2B); // set Page(y) address 642 | spi_master_write_addr(dev, _y1, _y2); 643 | spi_master_write_comm_byte(dev, 0x2C); // Memory Write 644 | spi_master_write_colors(dev, colors, size); 645 | } // endif 0x9340/0x9341/0x7789/0x7796 646 | 647 | if (dev->_model == 0x7735) { 648 | spi_master_write_comm_byte(dev, 0x2A); // set column(x) address 649 | spi_master_write_data_word(dev, _x1); 650 | spi_master_write_data_word(dev, _x2); 651 | spi_master_write_comm_byte(dev, 0x2B); // set Page(y) address 652 | spi_master_write_data_word(dev, _y1); 653 | spi_master_write_data_word(dev, _y2); 654 | spi_master_write_comm_byte(dev, 0x2C); // Memory Write 655 | spi_master_write_colors(dev, colors, size); 656 | } // endif 0x7735 657 | 658 | if (dev->_model == 0x9225) { 659 | lcdWriteRegisterByte(dev, 0x20, _x1); // set Horizontal address 660 | lcdWriteRegisterByte(dev, 0x21, _y1); // set Vertical address 661 | spi_master_write_comm_byte(dev, 0x22); // Memory Write 662 | spi_master_write_colors(dev, colors, size); 663 | } // endif 0x9225 664 | } // endif dev->_use_frame_buffer 665 | } 666 | 667 | // Draw rectangle of filling 668 | // x1:Start X coordinate 669 | // y1:Start Y coordinate 670 | // x2:End X coordinate 671 | // y2:End Y coordinate 672 | // color:color 673 | void lcdDrawFillRect(TFT_t * dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { 674 | if (x1 >= dev->_width) return; 675 | if (x2 >= dev->_width) x2=dev->_width-1; 676 | if (y1 >= dev->_height) return; 677 | if (y2 >= dev->_height) y2=dev->_height-1; 678 | 679 | ESP_LOGD(TAG,"offset(x)=%d offset(y)=%d",dev->_offsetx,dev->_offsety); 680 | 681 | if (dev->_use_frame_buffer) { 682 | for (int16_t j = y1; j <= y2; j++){ 683 | for(int16_t i = x1; i <= x2; i++){ 684 | dev->_frame_buffer[j*dev->_width+i] = color; 685 | } 686 | } 687 | } else { 688 | uint16_t _x1 = x1 + dev->_offsetx; 689 | uint16_t _x2 = x2 + dev->_offsetx; 690 | uint16_t _y1 = y1 + dev->_offsety; 691 | uint16_t _y2 = y2 + dev->_offsety; 692 | 693 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7789 || dev->_model == 0x7796) { 694 | spi_master_write_comm_byte(dev, 0x2A); // set column(x) address 695 | spi_master_write_addr(dev, _x1, _x2); 696 | spi_master_write_comm_byte(dev, 0x2B); // set Page(y) address 697 | spi_master_write_addr(dev, _y1, _y2); 698 | spi_master_write_comm_byte(dev, 0x2C); // Memory Write 699 | for(int i=_x1;i<=_x2;i++) { 700 | uint16_t size = _y2-_y1+1; 701 | spi_master_write_color(dev, color, size); 702 | } 703 | } // endif 0x9340/0x9341/0x7789/0x7796 704 | 705 | if (dev->_model == 0x7735) { 706 | spi_master_write_comm_byte(dev, 0x2A); // set column(x) address 707 | spi_master_write_data_word(dev, _x1); 708 | spi_master_write_data_word(dev, _x2); 709 | spi_master_write_comm_byte(dev, 0x2B); // set Page(y) address 710 | spi_master_write_data_word(dev, _y1); 711 | spi_master_write_data_word(dev, _y2); 712 | spi_master_write_comm_byte(dev, 0x2C); // Memory Write 713 | for(int i=_x1;i<=_x2;i++) { 714 | uint16_t size = _y2-_y1+1; 715 | spi_master_write_color(dev, color, size); 716 | } 717 | } // endif 0x7735 718 | 719 | if (dev->_model == 0x9225) { 720 | for(int _y=_y1;_y<=_y2;_y++){ 721 | lcdWriteRegisterByte(dev, 0x20, _x1); // set Horizontal address 722 | lcdWriteRegisterByte(dev, 0x21, _y); // set Vertical address 723 | spi_master_write_comm_byte(dev, 0x22); // Memory Write 724 | uint16_t size = _x2-_x1+1; 725 | spi_master_write_color(dev, color, size); 726 | } 727 | } // endif 0x9225 728 | } // endif dev->_use_frame_buffer 729 | } 730 | 731 | // Draw square of filling 732 | // x0:Center X coordinate 733 | // y0:Center Y coordinate 734 | // size:Square size 735 | // color:color 736 | void lcdDrawFillRect2(TFT_t * dev, uint16_t x0, uint16_t y0, uint16_t size, uint16_t color) { 737 | uint16_t x1 = x0-size; 738 | uint16_t y1 = y0-size; 739 | uint16_t x2 = x0+size; 740 | uint16_t y2 = y0+size; 741 | lcdDrawFillRect(dev, x1, y1, x2, y2, color); 742 | } 743 | 744 | // Display OFF 745 | void lcdDisplayOff(TFT_t * dev) { 746 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7735 || dev->_model == 0x7789 || dev->_model == 0x7796) { 747 | spi_master_write_comm_byte(dev, 0x28); 748 | } // endif 0x9340/0x9341/0x7735/0x7789/0x7796 749 | 750 | if (dev->_model == 0x9225) { 751 | lcdWriteRegisterByte(dev, 0x07, 0x1014); 752 | } // endif 0x9225 753 | 754 | } 755 | 756 | // Display ON 757 | void lcdDisplayOn(TFT_t * dev) { 758 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7735 || dev->_model == 0x7789 || dev->_model == 0x7796) { 759 | spi_master_write_comm_byte(dev, 0x29); 760 | } // endif 0x9340/0x9341/0x7735/0x7789/0x7796 761 | 762 | if (dev->_model == 0x9225) { 763 | lcdWriteRegisterByte(dev, 0x07, 0x1017); 764 | } // endif 0x9225 765 | 766 | } 767 | 768 | // Display Inversion OFF 769 | void lcdInversionOff(TFT_t * dev) { 770 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7735 || dev->_model == 0x7789 || dev->_model == 0x7796) { 771 | spi_master_write_comm_byte(dev, 0x20); 772 | } // endif 0x9340/0x9341/0x7735/0x7789/0x7796 773 | 774 | if (dev->_model == 0x9225) { 775 | lcdWriteRegisterByte(dev, 0x07, 0x1017); 776 | } // endif 0x9225 777 | } 778 | 779 | // Display Inversion ON 780 | void lcdInversionOn(TFT_t * dev) { 781 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7735 || dev->_model == 0x7789 || dev->_model == 0x7796) { 782 | spi_master_write_comm_byte(dev, 0x21); 783 | } // endif 0x9340/0x9341/0x7735/0x7789/0x7796 784 | 785 | if (dev->_model == 0x9225) { 786 | lcdWriteRegisterByte(dev, 0x07, 0x1013); 787 | } // endif 0x9225 788 | } 789 | 790 | // Change Memory Access Control 791 | void lcdBGRFilter(TFT_t * dev) { 792 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7735 || dev->_model == 0x7789 || dev->_model == 0x7796) { 793 | spi_master_write_comm_byte(dev, 0x36); //Memory Access Control 794 | spi_master_write_data_byte(dev, 0x00); //Right top start, RGB color filter panel 795 | } // endif 0x9340/0x9341/0x7735/0x7789/0x7796 796 | 797 | if (dev->_model == 0x9225) { 798 | lcdWriteRegisterByte(dev, 0x03, 0x0030); // set GRAM write direction and BGR=0. 799 | } // endif 0x9225 800 | } 801 | // Fill screen 802 | // color:color 803 | void lcdFillScreen(TFT_t * dev, uint16_t color) { 804 | lcdDrawFillRect(dev, 0, 0, dev->_width-1, dev->_height-1, color); 805 | } 806 | 807 | // Draw line 808 | // x1:Start X coordinate 809 | // y1:Start Y coordinate 810 | // x2:End X coordinate 811 | // y2:End Y coordinate 812 | // color:color 813 | void lcdDrawLine(TFT_t * dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { 814 | int i; 815 | int dx,dy; 816 | int sx,sy; 817 | int E; 818 | 819 | /* distance between two points */ 820 | dx = ( x2 > x1 ) ? x2 - x1 : x1 - x2; 821 | dy = ( y2 > y1 ) ? y2 - y1 : y1 - y2; 822 | 823 | /* direction of two point */ 824 | sx = ( x2 > x1 ) ? 1 : -1; 825 | sy = ( y2 > y1 ) ? 1 : -1; 826 | 827 | /* inclination < 1 */ 828 | if ( dx > dy ) { 829 | E = -dx; 830 | for ( i = 0 ; i <= dx ; i++ ) { 831 | lcdDrawPixel(dev, x1, y1, color); 832 | x1 += sx; 833 | E += 2 * dy; 834 | if ( E >= 0 ) { 835 | y1 += sy; 836 | E -= 2 * dx; 837 | } 838 | } 839 | 840 | /* inclination >= 1 */ 841 | } else { 842 | E = -dy; 843 | for ( i = 0 ; i <= dy ; i++ ) { 844 | lcdDrawPixel(dev, x1, y1, color); 845 | y1 += sy; 846 | E += 2 * dx; 847 | if ( E >= 0 ) { 848 | x1 += sx; 849 | E -= 2 * dy; 850 | } 851 | } 852 | } 853 | } 854 | 855 | // Draw rectangle 856 | // x1:Start X coordinate 857 | // y1:Start Y coordinate 858 | // x2:End X coordinate 859 | // y2:End Y coordinate 860 | // color:color 861 | void lcdDrawRect(TFT_t * dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { 862 | lcdDrawLine(dev, x1, y1, x2, y1, color); 863 | lcdDrawLine(dev, x2, y1, x2, y2, color); 864 | lcdDrawLine(dev, x2, y2, x1, y2, color); 865 | lcdDrawLine(dev, x1, y2, x1, y1, color); 866 | } 867 | 868 | // x0:Center X coordinate 869 | // y0:Center Y coordinate 870 | // size:Square size 871 | // color:color 872 | void lcdDrawRect2(TFT_t * dev, uint16_t x0, uint16_t y0, uint16_t size, uint16_t color) { 873 | lcdDrawLine(dev, x0-size, y0-size, x0+size, y0-size, color); 874 | lcdDrawLine(dev, x0+size, y0-size, x0+size, y0+size, color); 875 | lcdDrawLine(dev, x0+size, y0+size, x0-size, y0+size, color); 876 | lcdDrawLine(dev, x0-size, y0+size, x0-size, y0-size, color); 877 | } 878 | 879 | 880 | // Draw rectangle with angle 881 | // xc:Center X coordinate 882 | // yc:Center Y coordinate 883 | // w:Width of rectangle 884 | // h:Height of rectangle 885 | // angle:Angle of rectangle 886 | // color:color 887 | 888 | //When the origin is (0, 0), the point (x1, y1) after rotating the point (x, y) by the angle is obtained by the following calculation. 889 | // x1 = x * cos(angle) - y * sin(angle) 890 | // y1 = x * sin(angle) + y * cos(angle) 891 | void lcdDrawRectAngle(TFT_t * dev, uint16_t xc, uint16_t yc, uint16_t w, uint16_t h, uint16_t angle, uint16_t color) { 892 | double xd,yd,rd; 893 | int x1,y1; 894 | int x2,y2; 895 | int x3,y3; 896 | int x4,y4; 897 | rd = -angle * M_PI / 180.0; 898 | xd = 0.0 - w/2; 899 | yd = h/2; 900 | x1 = (int)(xd * cos(rd) - yd * sin(rd) + xc); 901 | y1 = (int)(xd * sin(rd) + yd * cos(rd) + yc); 902 | 903 | yd = 0.0 - yd; 904 | x2 = (int)(xd * cos(rd) - yd * sin(rd) + xc); 905 | y2 = (int)(xd * sin(rd) + yd * cos(rd) + yc); 906 | 907 | xd = w/2; 908 | yd = h/2; 909 | x3 = (int)(xd * cos(rd) - yd * sin(rd) + xc); 910 | y3 = (int)(xd * sin(rd) + yd * cos(rd) + yc); 911 | 912 | yd = 0.0 - yd; 913 | x4 = (int)(xd * cos(rd) - yd * sin(rd) + xc); 914 | y4 = (int)(xd * sin(rd) + yd * cos(rd) + yc); 915 | 916 | lcdDrawLine(dev, x1, y1, x2, y2, color); 917 | lcdDrawLine(dev, x1, y1, x3, y3, color); 918 | lcdDrawLine(dev, x2, y2, x4, y4, color); 919 | lcdDrawLine(dev, x3, y3, x4, y4, color); 920 | } 921 | 922 | 923 | // Draw triangle 924 | // xc:Center X coordinate 925 | // yc:Center Y coordinate 926 | // w:Width of triangle 927 | // h:Height of triangle 928 | // angle:Angle of triangle 929 | // color:color 930 | 931 | //When the origin is (0, 0), the point (x1, y1) after rotating the point (x, y) by the angle is obtained by the following calculation. 932 | // x1 = x * cos(angle) - y * sin(angle) 933 | // y1 = x * sin(angle) + y * cos(angle) 934 | void lcdDrawTriangle(TFT_t * dev, uint16_t xc, uint16_t yc, uint16_t w, uint16_t h, uint16_t angle, uint16_t color) { 935 | double xd,yd,rd; 936 | int x1,y1; 937 | int x2,y2; 938 | int x3,y3; 939 | rd = -angle * M_PI / 180.0; 940 | xd = 0.0; 941 | yd = h/2; 942 | x1 = (int)(xd * cos(rd) - yd * sin(rd) + xc); 943 | y1 = (int)(xd * sin(rd) + yd * cos(rd) + yc); 944 | 945 | xd = w/2; 946 | yd = 0.0 - yd; 947 | x2 = (int)(xd * cos(rd) - yd * sin(rd) + xc); 948 | y2 = (int)(xd * sin(rd) + yd * cos(rd) + yc); 949 | 950 | xd = 0.0 - w/2; 951 | x3 = (int)(xd * cos(rd) - yd * sin(rd) + xc); 952 | y3 = (int)(xd * sin(rd) + yd * cos(rd) + yc); 953 | 954 | lcdDrawLine(dev, x1, y1, x2, y2, color); 955 | lcdDrawLine(dev, x1, y1, x3, y3, color); 956 | lcdDrawLine(dev, x2, y2, x3, y3, color); 957 | } 958 | 959 | // Draw regular polygon 960 | // xc:Center X coordinate 961 | // yc:Center Y coordinate 962 | // n:Number of slides 963 | // r:radius 964 | // angle:Angle of regular polygon 965 | // color:color 966 | void lcdDrawRegularPolygon(TFT_t * dev, uint16_t xc, uint16_t yc, uint16_t n, uint16_t r, uint16_t angle, uint16_t color) 967 | { 968 | double xd,yd,rd; 969 | int x1,y1; 970 | int x2,y2; 971 | int i; 972 | 973 | rd = -angle * M_PI / 180.0; 974 | for(i=0;iy || err>x) err+=++y*2+1; 1010 | } while(y<0); 1011 | } 1012 | 1013 | // Draw circle of filling 1014 | // x0:Central X coordinate 1015 | // y0:Central Y coordinate 1016 | // r:radius 1017 | // color:color 1018 | void lcdDrawFillCircle(TFT_t * dev, uint16_t x0, uint16_t y0, uint16_t r, uint16_t color) { 1019 | int x; 1020 | int y; 1021 | int err; 1022 | int old_err; 1023 | int ChangeX; 1024 | 1025 | x=0; 1026 | y=-r; 1027 | err=2-2*r; 1028 | ChangeX=1; 1029 | do{ 1030 | if(ChangeX) { 1031 | lcdDrawLine(dev, x0-x, y0-y, x0-x, y0+y, color); 1032 | lcdDrawLine(dev, x0+x, y0-y, x0+x, y0+y, color); 1033 | } // endif 1034 | ChangeX=(old_err=err)<=x; 1035 | if (ChangeX) err+=++x*2+1; 1036 | if (old_err>y || err>x) err+=++y*2+1; 1037 | } while(y<=0); 1038 | } 1039 | 1040 | // Draw rectangle with round corner 1041 | // x1:Start X coordinate 1042 | // y1:Start Y coordinate 1043 | // x2:End X coordinate 1044 | // y2:End Y coordinate 1045 | // r:radius 1046 | // color:color 1047 | void lcdDrawRoundRect(TFT_t * dev, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t r, uint16_t color) { 1048 | int x; 1049 | int y; 1050 | int err; 1051 | int old_err; 1052 | unsigned char temp; 1053 | 1054 | if(x1>x2) { 1055 | temp=x1; x1=x2; x2=temp; 1056 | } // endif 1057 | 1058 | if(y1>y2) { 1059 | temp=y1; y1=y2; y2=temp; 1060 | } // endif 1061 | 1062 | ESP_LOGD(TAG, "x1=%d x2=%d delta=%d r=%d",x1, x2, x2-x1, r); 1063 | ESP_LOGD(TAG, "y1=%d y2=%d delta=%d r=%d",y1, y2, y2-y1, r); 1064 | if (x2-x1 < r) return; // Add 20190517 1065 | if (y2-y1 < r) return; // Add 20190517 1066 | 1067 | x=0; 1068 | y=-r; 1069 | err=2-2*r; 1070 | 1071 | do{ 1072 | if(x) { 1073 | lcdDrawPixel(dev, x1+r-x, y1+r+y, color); 1074 | lcdDrawPixel(dev, x2-r+x, y1+r+y, color); 1075 | lcdDrawPixel(dev, x1+r-x, y2-r-y, color); 1076 | lcdDrawPixel(dev, x2-r+x, y2-r-y, color); 1077 | } // endif 1078 | if ((old_err=err)<=x) err+=++x*2+1; 1079 | if (old_err>y || err>x) err+=++y*2+1; 1080 | } while(y<0); 1081 | 1082 | ESP_LOGD(TAG, "x1+r=%d x2-r=%d",x1+r, x2-r); 1083 | lcdDrawLine(dev, x1+r, y1, x2-r, y1, color); 1084 | lcdDrawLine(dev, x1+r, y2, x2-r, y2, color); 1085 | ESP_LOGD(TAG, "y1+r=%d y2-r=%d",y1+r, y2-r); 1086 | lcdDrawLine(dev, x1, y1+r, x1, y2-r, color); 1087 | lcdDrawLine(dev, x2, y1+r, x2, y2-r, color); 1088 | } 1089 | 1090 | // Draw arrow 1091 | // x0:Start X coordinate 1092 | // y0:Start Y coordinate 1093 | // x1:End X coordinate 1094 | // y1:End Y coordinate 1095 | // w:Width of the botom 1096 | // color:color 1097 | // Thanks http://k-hiura.cocolog-nifty.com/blog/2010/11/post-2a62.html 1098 | void lcdDrawArrow(TFT_t * dev, uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t w,uint16_t color) { 1099 | double Vx= x1 - x0; 1100 | double Vy= y1 - y0; 1101 | double v = sqrt(Vx*Vx+Vy*Vy); 1102 | //printf("v=%f\n",v); 1103 | double Ux= Vx/v; 1104 | double Uy= Vy/v; 1105 | 1106 | uint16_t L[2],R[2]; 1107 | L[0]= x1 - Uy*w - Ux*v; 1108 | L[1]= y1 + Ux*w - Uy*v; 1109 | R[0]= x1 + Uy*w - Ux*v; 1110 | R[1]= y1 - Ux*w - Uy*v; 1111 | //printf("L=%d-%d R=%d-%d\n",L[0],L[1],R[0],R[1]); 1112 | 1113 | //lcdDrawLine(x0,y0,x1,y1,color); 1114 | lcdDrawLine(dev, x1, y1, L[0], L[1], color); 1115 | lcdDrawLine(dev, x1, y1, R[0], R[1], color); 1116 | lcdDrawLine(dev, L[0], L[1], R[0], R[1], color); 1117 | } 1118 | 1119 | 1120 | // Draw arrow of filling 1121 | // x0:Start X coordinate 1122 | // y0:Start Y coordinate 1123 | // x1:End X coordinate 1124 | // y1:End Y coordinate 1125 | // w:Width of the botom 1126 | // color:color 1127 | void lcdDrawFillArrow(TFT_t * dev, uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t w,uint16_t color) { 1128 | double Vx= x1 - x0; 1129 | double Vy= y1 - y0; 1130 | double v = sqrt(Vx*Vx+Vy*Vy); 1131 | //printf("v=%f\n",v); 1132 | double Ux= Vx/v; 1133 | double Uy= Vy/v; 1134 | 1135 | uint16_t L[2],R[2]; 1136 | L[0]= x1 - Uy*w - Ux*v; 1137 | L[1]= y1 + Ux*w - Uy*v; 1138 | R[0]= x1 + Uy*w - Ux*v; 1139 | R[1]= y1 - Ux*w - Uy*v; 1140 | //printf("L=%d-%d R=%d-%d\n",L[0],L[1],R[0],R[1]); 1141 | 1142 | lcdDrawLine(dev, x0, y0, x1, y1, color); 1143 | lcdDrawLine(dev, x1, y1, L[0], L[1], color); 1144 | lcdDrawLine(dev, x1, y1, R[0], R[1], color); 1145 | lcdDrawLine(dev, L[0], L[1], R[0], R[1], color); 1146 | 1147 | int ww; 1148 | for(ww=w-1;ww>0;ww--) { 1149 | L[0]= x1 - Uy*ww - Ux*v; 1150 | L[1]= y1 + Ux*ww - Uy*v; 1151 | R[0]= x1 + Uy*ww - Ux*v; 1152 | R[1]= y1 - Ux*ww - Uy*v; 1153 | //printf("Fill>L=%d-%d R=%d-%d\n",L[0],L[1],R[0],R[1]); 1154 | lcdDrawLine(dev, x1, y1, L[0], L[1], color); 1155 | lcdDrawLine(dev, x1, y1, R[0], R[1], color); 1156 | } 1157 | } 1158 | 1159 | // Draw ASCII character 1160 | // x:X coordinate 1161 | // y:Y coordinate 1162 | // ascii:ascii code 1163 | // color:color 1164 | int lcdDrawChar(TFT_t * dev, FontxFile *fxs, uint16_t x, uint16_t y, uint8_t ascii, uint16_t color) { 1165 | uint16_t xx,yy,bit,ofs; 1166 | unsigned char fonts[128]; // font pattern 1167 | unsigned char pw, ph; 1168 | int h,w; 1169 | uint16_t mask; 1170 | bool rc; 1171 | 1172 | if(_DEBUG_)printf("_font_direction=%d\n",dev->_font_direction); 1173 | rc = GetFontx(fxs, ascii, fonts, &pw, &ph); 1174 | if(_DEBUG_)printf("GetFontx rc=%d pw=%d ph=%d\n",rc,pw,ph); 1175 | if (!rc) return 0; 1176 | 1177 | int16_t xd1 = 0; 1178 | int16_t yd1 = 0; 1179 | int16_t xd2 = 0; 1180 | int16_t yd2 = 0; 1181 | int16_t xss = 0; 1182 | int16_t yss = 0; 1183 | int16_t xsd = 0; 1184 | int16_t ysd = 0; 1185 | int16_t next = 0; 1186 | int16_t x0 = 0; 1187 | int16_t x1 = 0; 1188 | int16_t y0 = 0; 1189 | int16_t y1 = 0; 1190 | if (dev->_font_direction == 0) { 1191 | xd1 = +1; 1192 | yd1 = +1; //-1; 1193 | xd2 = 0; 1194 | yd2 = 0; 1195 | xss = x; 1196 | yss = y - (ph - 1); 1197 | xsd = 1; 1198 | ysd = 0; 1199 | next = x + pw; 1200 | 1201 | x0 = x; 1202 | y0 = y - (ph-1); 1203 | x1 = x + (pw-1); 1204 | y1 = y; 1205 | } else if (dev->_font_direction == 2) { 1206 | xd1 = -1; 1207 | yd1 = -1; //+1; 1208 | xd2 = 0; 1209 | yd2 = 0; 1210 | xss = x; 1211 | yss = y + ph + 1; 1212 | xsd = 1; 1213 | ysd = 0; 1214 | next = x - pw; 1215 | 1216 | x0 = x - (pw-1); 1217 | y0 = y; 1218 | x1 = x; 1219 | y1 = y + (ph-1); 1220 | } else if (dev->_font_direction == 1) { 1221 | xd1 = 0; 1222 | yd1 = 0; 1223 | xd2 = -1; 1224 | yd2 = +1; //-1; 1225 | xss = x + ph; 1226 | yss = y; 1227 | xsd = 0; 1228 | ysd = 1; 1229 | next = y + pw; //y - pw; 1230 | 1231 | x0 = x; 1232 | y0 = y; 1233 | x1 = x + (ph-1); 1234 | y1 = y + (pw-1); 1235 | } else if (dev->_font_direction == 3) { 1236 | xd1 = 0; 1237 | yd1 = 0; 1238 | xd2 = +1; 1239 | yd2 = -1; //+1; 1240 | xss = x - (ph - 1); 1241 | yss = y; 1242 | xsd = 0; 1243 | ysd = 1; 1244 | next = y - pw; //y + pw; 1245 | 1246 | x0 = x - (ph-1); 1247 | y0 = y - (pw-1); 1248 | x1 = x; 1249 | y1 = y; 1250 | } 1251 | 1252 | if (dev->_font_fill) lcdDrawFillRect(dev, x0, y0, x1, y1, dev->_font_fill_color); 1253 | 1254 | int bits; 1255 | if(_DEBUG_)printf("xss=%d yss=%d\n",xss,yss); 1256 | ofs = 0; 1257 | yy = yss; 1258 | xx = xss; 1259 | for(h=0;h_font_fill) lcdDrawPixel(dev, xx, yy, dev->_font_fill_color); 1274 | } 1275 | if (h == (ph-2) && dev->_font_underline) 1276 | lcdDrawPixel(dev, xx, yy, dev->_font_underline_color); 1277 | if (h == (ph-1) && dev->_font_underline) 1278 | lcdDrawPixel(dev, xx, yy, dev->_font_underline_color); 1279 | xx = xx + xd1; 1280 | yy = yy + yd2; 1281 | mask = mask >> 1; 1282 | } 1283 | ofs++; 1284 | } 1285 | yy = yy + yd1; 1286 | xx = xx + xd2; 1287 | } 1288 | 1289 | if (next < 0) next = 0; 1290 | return next; 1291 | } 1292 | 1293 | int lcdDrawString(TFT_t * dev, FontxFile *fx, uint16_t x, uint16_t y, uint8_t * ascii, uint16_t color) { 1294 | int length = strlen((char *)ascii); 1295 | if(_DEBUG_)printf("lcdDrawString length=%d\n",length); 1296 | for(int i=0;i_font_direction == 0) 1299 | x = lcdDrawChar(dev, fx, x, y, ascii[i], color); 1300 | if (dev->_font_direction == 1) 1301 | y = lcdDrawChar(dev, fx, x, y, ascii[i], color); 1302 | if (dev->_font_direction == 2) 1303 | x = lcdDrawChar(dev, fx, x, y, ascii[i], color); 1304 | if (dev->_font_direction == 3) 1305 | y = lcdDrawChar(dev, fx, x, y, ascii[i], color); 1306 | } 1307 | if (dev->_font_direction == 0) return x; 1308 | if (dev->_font_direction == 2) return x; 1309 | if (dev->_font_direction == 1) return y; 1310 | if (dev->_font_direction == 3) return y; 1311 | return 0; 1312 | } 1313 | 1314 | 1315 | // Draw character using code 1316 | // x:X coordinate 1317 | // y:Y coordinate 1318 | // code:character code 1319 | // color:color 1320 | int lcdDrawCode(TFT_t * dev, FontxFile *fx, uint16_t x,uint16_t y,uint8_t code,uint16_t color) { 1321 | if(_DEBUG_)printf("code=%x x=%d y=%d\n",code,x,y); 1322 | if (dev->_font_direction == 0) 1323 | x = lcdDrawChar(dev, fx, x, y, code, color); 1324 | if (dev->_font_direction == 1) 1325 | y = lcdDrawChar(dev, fx, x, y, code, color); 1326 | if (dev->_font_direction == 2) 1327 | x = lcdDrawChar(dev, fx, x, y, code, color); 1328 | if (dev->_font_direction == 3) 1329 | y = lcdDrawChar(dev, fx, x, y, code, color); 1330 | if (dev->_font_direction == 0) return x; 1331 | if (dev->_font_direction == 2) return x; 1332 | if (dev->_font_direction == 1) return y; 1333 | if (dev->_font_direction == 3) return y; 1334 | return 0; 1335 | } 1336 | 1337 | #if 0 1338 | // Draw SJIS character 1339 | // x:X coordinate 1340 | // y:Y coordinate 1341 | // sjis:SJIS code 1342 | // color:color 1343 | int lcdDrawSJISChar(TFT_t * dev, FontxFile *fxs, uint16_t x,uint16_t y,uint16_t sjis,uint16_t color) { 1344 | uint16_t xx,yy,bit,ofs; 1345 | unsigned char fonts[128]; // font pattern 1346 | unsigned char pw, ph; 1347 | int h,w; 1348 | uint16_t mask; 1349 | bool rc; 1350 | 1351 | if(_DEBUG_)printf("_font_direction=%d\n",dev->_font_direction); 1352 | //sjis = UTF2SJIS(utf8); 1353 | //if(_DEBUG_)printf("sjis=%04x\n",sjis); 1354 | 1355 | rc = GetFontx(fxs, sjis, fonts, &pw, &ph); // SJIS -> Font pattern 1356 | if(_DEBUG_)printf("GetFontx rc=%d pw=%d ph=%d\n",rc,pw,ph); 1357 | if (!rc) return 0; 1358 | 1359 | uint16_t xd1, yd1; 1360 | uint16_t xd2, yd2; 1361 | uint16_t xss, yss; 1362 | uint16_t xsd, ysd; 1363 | int next; 1364 | if (dev->_font_direction == 0) { 1365 | xd1 = +1; 1366 | yd1 = -1; 1367 | xd2 = 0; 1368 | yd2 = 0; 1369 | xss = x; 1370 | yss = y + ph - 1; 1371 | xsd = 1; 1372 | ysd = 0; 1373 | next = x + pw; 1374 | } else if (dev->_font_direction == 2) { 1375 | xd1 = -1; 1376 | yd1 = +1; 1377 | xd2 = 0; 1378 | yd2 = 0; 1379 | xss = x; 1380 | yss = y - ph + 1; 1381 | xsd = 1; 1382 | ysd = 0; 1383 | next = x - pw; 1384 | } else if (dev->_font_direction == 1) { 1385 | xd1 = 0; 1386 | yd1 = 0; 1387 | xd2 = -1; 1388 | yd2 = -1; 1389 | xss = x + ph; 1390 | yss = y; 1391 | xsd = 0; 1392 | ysd = 1; 1393 | next = y - pw; 1394 | } else if (dev->_font_direction == 3) { 1395 | xd1 = 0; 1396 | yd1 = 0; 1397 | xd2 = +1; 1398 | yd2 = +1; 1399 | xss = x - ph - 1; 1400 | yss = y; 1401 | xsd = 0; 1402 | ysd = 1; 1403 | next = y + pw; 1404 | } 1405 | 1406 | int bits; 1407 | if(_DEBUG_)printf("xss=%d yss=%d\n",xss,yss); 1408 | ofs = 0; 1409 | yy = yss; 1410 | xx = xss; 1411 | for(h=0;h_font_fill) lcdDrawPixel(dev, xx, yy, dev->_font_fill_color); 1426 | } 1427 | if (h == (ph-2) && dev->_font_underline) 1428 | lcdDrawPixel(xx, yy, dev->_font_underline_color); 1429 | if (h == (ph-1) && dev->_font_underline) 1430 | lcdDrawPixel(xx, yy, dev->_font_underline_color); 1431 | xx = xx + xd1; 1432 | yy = yy + yd2; 1433 | mask = mask >> 1; 1434 | } 1435 | ofs++; 1436 | } 1437 | yy = yy + yd1; 1438 | xx = xx + xd2; 1439 | } 1440 | 1441 | if (next < 0) next = 0; 1442 | return next; 1443 | } 1444 | 1445 | // Draw UTF8 character 1446 | // x:X coordinate 1447 | // y:Y coordinate 1448 | // utf8:UTF8 code 1449 | // color:color 1450 | int lcdDrawUTF8Char(TFT_t * dev, FontxFile *fx, uint16_t x,uint16_t y,uint8_t *utf8,uint16_t color) { 1451 | uint16_t sjis[1]; 1452 | 1453 | sjis[0] = UTF2SJIS(utf8); 1454 | if(_DEBUG_)printf("sjis=%04x\n",sjis[0]); 1455 | return lcdDrawSJISChar(dev, fx, x, y, sjis[0], color); 1456 | } 1457 | 1458 | // Draw UTF8 string 1459 | // x:X coordinate 1460 | // y:Y coordinate 1461 | // utfs:UTF8 string 1462 | // color:color 1463 | int lcdDrawUTF8String(TFT_t * dev, FontxFile *fx, uint16_t x, uint16_t y, unsigned char *utfs, uint16_t color) { 1464 | 1465 | int i; 1466 | int spos; 1467 | uint16_t sjis[64]; 1468 | spos = String2SJIS(utfs, strlen((char *)utfs), sjis, 64); 1469 | if(_DEBUG_)printf("spos=%d\n",spos); 1470 | for(i=0;i_font_direction == 0) 1473 | x = lcdDrawSJISChar(dev, fx, x, y, sjis[i], color); 1474 | if (dev->_font_direction == 1) 1475 | y = lcdDrawSJISChar(dev, fx, x, y, sjis[i], color); 1476 | if (dev->_font_direction == 2) 1477 | x = lcdDrawSJISChar(dev, fx, x, y, sjis[i], color); 1478 | if (dev->_font_direction == 3) 1479 | y = lcdDrawSJISChar(dev, fx, x, y, sjis[i], color); 1480 | } 1481 | if (dev->_font_direction == 0) return x; 1482 | if (dev->_font_direction == 2) return x; 1483 | if (dev->_font_direction == 1) return y; 1484 | if (dev->_font_direction == 3) return y; 1485 | return 0; 1486 | } 1487 | #endif 1488 | 1489 | // Set font direction 1490 | // dir:Direction 1491 | void lcdSetFontDirection(TFT_t * dev, uint16_t dir) { 1492 | dev->_font_direction = dir; 1493 | } 1494 | 1495 | // Set font filling 1496 | // color:fill color 1497 | void lcdSetFontFill(TFT_t * dev, uint16_t color) { 1498 | dev->_font_fill = true; 1499 | dev->_font_fill_color = color; 1500 | } 1501 | 1502 | // UnSet font filling 1503 | void lcdUnsetFontFill(TFT_t * dev) { 1504 | dev->_font_fill = false; 1505 | } 1506 | 1507 | // Set font underline 1508 | // color:frame color 1509 | void lcdSetFontUnderLine(TFT_t * dev, uint16_t color) { 1510 | dev->_font_underline = true; 1511 | dev->_font_underline_color = color; 1512 | } 1513 | 1514 | // UnSet font underline 1515 | void lcdUnsetFontUnderLine(TFT_t * dev) { 1516 | dev->_font_underline = false; 1517 | } 1518 | 1519 | // Backlight OFF 1520 | void lcdBacklightOff(TFT_t * dev) { 1521 | if(dev->_bl >= 0) { 1522 | gpio_set_level( dev->_bl, 0 ); 1523 | } 1524 | } 1525 | 1526 | // Backlight ON 1527 | void lcdBacklightOn(TFT_t * dev) { 1528 | if(dev->_bl >= 0) { 1529 | gpio_set_level( dev->_bl, 1 ); 1530 | } 1531 | } 1532 | 1533 | // Vertical Scrolling Definition 1534 | // tfa:Top Fixed Area 1535 | // vsa:Vertical Scrolling Area 1536 | // bfa:Bottom Fixed Area 1537 | void lcdSetScrollArea(TFT_t * dev, uint16_t tfa, uint16_t vsa, uint16_t bfa){ 1538 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7789 || dev->_model == 0x7796) { 1539 | spi_master_write_comm_byte(dev, 0x33); // Vertical Scrolling Definition 1540 | spi_master_write_data_word(dev, tfa); 1541 | spi_master_write_data_word(dev, vsa); 1542 | spi_master_write_data_word(dev, bfa); 1543 | //spi_master_write_comm_byte(dev, 0x12); // Partial Mode ON 1544 | } // endif 0x9340/0x9341/0x7789/0x7796 1545 | 1546 | if (dev->_model == 0x9225) { 1547 | lcdWriteRegisterByte(dev, 0x31, vsa); // Specify scroll end and step at the scroll display 1548 | lcdWriteRegisterByte(dev, 0x32, tfa); // Specify scroll start and step at the scroll display 1549 | #if 0 1550 | spi_master_write_comm_byte(dev, 0x31); // Specify scroll end address at the scroll display 1551 | spi_master_write_data_word(dev, vsa); 1552 | spi_master_write_comm_byte(dev, 0x32); // Specify scroll start address at the scroll display 1553 | spi_master_write_data_word(dev, tfa); 1554 | #endif 1555 | } // endif 0x9225 1556 | } 1557 | 1558 | void lcdResetScrollArea(TFT_t * dev, uint16_t vsa){ 1559 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7789 || dev->_model == 0x7796) { 1560 | spi_master_write_comm_byte(dev, 0x33); // Vertical Scrolling Definition 1561 | spi_master_write_data_word(dev, 0); 1562 | //spi_master_write_data_word(dev, 0x140); 1563 | spi_master_write_data_word(dev, vsa); 1564 | spi_master_write_data_word(dev, 0); 1565 | } // endif 0x9340/0x9341/0x7789/0x7796 1566 | 1567 | if (dev->_model == 0x9225) { 1568 | lcdWriteRegisterByte(dev, 0x31, 0x0); // Specify scroll end and step at the scroll display 1569 | lcdWriteRegisterByte(dev, 0x32, 0x0); // Specify scroll start and step at the scroll display 1570 | //lcdWriteRegisterByte(dev, 0x31, vsa); // Specify scroll end and step at the scroll display 1571 | //lcdWriteRegisterByte(dev, 0x32, tfa); // Specify scroll start and step at the scroll display 1572 | } // endif 0x9225 1573 | } 1574 | 1575 | // Vertical Scrolling Start Address 1576 | // vsp:Vertical Scrolling Start Address 1577 | void lcdScroll(TFT_t * dev, uint16_t vsp){ 1578 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7789 || dev->_model == 0x7796) { 1579 | spi_master_write_comm_byte(dev, 0x37); // Vertical Scrolling Start Address 1580 | spi_master_write_data_word(dev, vsp); 1581 | } // endif 0x9340/0x9341/0x7789/0x7796 1582 | 1583 | if (dev->_model == 0x9225) { 1584 | lcdWriteRegisterByte(dev, 0x33, vsp); // Vertical Scrolling Start Address 1585 | #if 0 1586 | spi_master_write_comm_byte(dev, 0x33); // Vertical Scrolling Start Address 1587 | spi_master_write_data_word(dev, vsp); 1588 | #endif 1589 | } // endif 0x9225 1590 | } 1591 | 1592 | #define MAX_LEN 3 1593 | #define XPT_START 0x80 1594 | #define XPT_XPOS 0x50 1595 | #define XPT_YPOS 0x10 1596 | #define XPT_8BIT 0x80 1597 | #define XPT_SER 0x04 1598 | #define XPT_DEF 0x03 1599 | 1600 | 1601 | int xptGetit(TFT_t * dev, int cmd){ 1602 | char rbuf[MAX_LEN]; 1603 | char wbuf[MAX_LEN]; 1604 | 1605 | memset(wbuf, 0, sizeof(rbuf)); 1606 | memset(rbuf, 0, sizeof(rbuf)); 1607 | wbuf[0] = cmd; 1608 | spi_transaction_t SPITransaction; 1609 | esp_err_t ret; 1610 | 1611 | memset( &SPITransaction, 0, sizeof( spi_transaction_t ) ); 1612 | SPITransaction.length = MAX_LEN * 8; 1613 | SPITransaction.tx_buffer = wbuf; 1614 | SPITransaction.rx_buffer = rbuf; 1615 | #if 1 1616 | ret = spi_device_transmit( dev->_XPT_Handle, &SPITransaction ); 1617 | #else 1618 | ret = spi_device_polling_transmit( dev->_XPT_Handle, &SPITransaction ); 1619 | #endif 1620 | assert(ret==ESP_OK); 1621 | ESP_LOGD(TAG, "rbuf[0]=%02x rbuf[1]=%02x rbuf[2]=%02x", rbuf[0], rbuf[1], rbuf[2]); 1622 | // 12bit Conversion 1623 | //int pos = (rbuf[1]<<8)+rbuf[2]; 1624 | int pos = (rbuf[1]<<4)+(rbuf[2]>>4); 1625 | return(pos); 1626 | } 1627 | 1628 | bool touch_getxy(TFT_t *dev, int *xp, int *yp) { 1629 | int level = gpio_get_level(dev->_irq); 1630 | ESP_LOGD(__FUNCTION__, "gpio_get_level=%d", level); 1631 | if (level == 1) return false; // Not touched 1632 | *xp = xptGetit(dev, (XPT_START | XPT_XPOS | XPT_SER) ); 1633 | *yp = xptGetit(dev, (XPT_START | XPT_YPOS | XPT_SER) ); 1634 | return true; 1635 | } 1636 | 1637 | // Draw Frame Buffer 1638 | void lcdDrawFinish(TFT_t *dev) 1639 | { 1640 | if (dev->_use_frame_buffer == false) return; 1641 | 1642 | uint16_t _x1 = dev->_offsetx; 1643 | uint16_t _x2 = dev->_offsetx+dev->_width-1; 1644 | uint16_t _y1 = dev->_offsety; 1645 | uint16_t _y2 = dev->_offsety+dev->_height-1; 1646 | ESP_LOGD(TAG,"_x1=%d _x2=%d _y1=%d _y2=%d",_x1, _x2, _y1, _y2); 1647 | 1648 | if (dev->_model == 0x9340 || dev->_model == 0x9341 || dev->_model == 0x7789 || dev->_model == 0x7796) { 1649 | spi_master_write_comm_byte(dev, 0x2A); // set column(x) address 1650 | spi_master_write_addr(dev, _x1, _x2); 1651 | spi_master_write_comm_byte(dev, 0x2B); // set Page(y) address 1652 | spi_master_write_addr(dev, _y1, _y2); 1653 | spi_master_write_comm_byte(dev, 0x2C); // Memory Write 1654 | 1655 | uint32_t size = dev->_width*dev->_height; 1656 | uint16_t *image = dev->_frame_buffer; 1657 | while (size > 0) { 1658 | // 1024 bytes per time. 1659 | uint16_t bs = (size > 1024) ? 1024 : size; 1660 | spi_master_write_colors(dev, image, bs); 1661 | size -= bs; 1662 | image += bs; 1663 | } 1664 | } // endif 0x9340/0x9341/0x7789/0x7796 1665 | 1666 | if (dev->_model == 0x7735) { 1667 | spi_master_write_comm_byte(dev, 0x2A); // set column(x) address 1668 | spi_master_write_data_word(dev, _x1); 1669 | spi_master_write_data_word(dev, _x2); 1670 | spi_master_write_comm_byte(dev, 0x2B); // set Page(y) address 1671 | spi_master_write_data_word(dev, _y1); 1672 | spi_master_write_data_word(dev, _y2); 1673 | spi_master_write_comm_byte(dev, 0x2C); // Memory Write 1674 | 1675 | uint32_t size = dev->_width*dev->_height; 1676 | uint16_t *image = dev->_frame_buffer; 1677 | while (size > 0) { 1678 | // 1024 bytes per time. 1679 | uint16_t bs = (size > 1024) ? 1024 : size; 1680 | spi_master_write_colors(dev, image, bs); 1681 | size -= bs; 1682 | image += bs; 1683 | } 1684 | } // endif 0x7735 1685 | 1686 | if (dev->_model == 0x9225) { 1687 | uint16_t *image = dev->_frame_buffer; 1688 | for(int _y0=_y1;_y0<=_y2;_y0++){ 1689 | lcdWriteRegisterByte(dev, 0x20, _x1); // set Horizontal address 1690 | lcdWriteRegisterByte(dev, 0x21, _y0); // set Vertical address 1691 | spi_master_write_comm_byte(dev, 0x22); // Memory Write 1692 | // _width bytes per time. 1693 | uint16_t bs = dev->_width; 1694 | spi_master_write_colors(dev, image, bs); 1695 | image += bs; 1696 | } 1697 | } // endif 0x9225 1698 | return; 1699 | } 1700 | --------------------------------------------------------------------------------