diff --git a/.gitignore b/.gitignore index 3c4910f2..882d279a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Editor +.vscode + # Prerequisites *.d @@ -59,4 +62,4 @@ build # Kconfig files sdkconfig -sdkconfig.old \ No newline at end of file +sdkconfig.old diff --git a/CMakeLists.txt b/CMakeLists.txt index bccf670a..30cf07e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,10 @@ if(CONFIG_LV_TOUCH_CONTROLLER) list(APPEND SOURCES "lvgl_touch/l58.cpp") elseif(CONFIG_LV_TOUCH_CONTROLLER_GT911) list(APPEND SOURCES "lvgl_touch/gt911.c") + elseif(CONFIG_LV_TOUCH_CONTROLLER_TT21100) + list(APPEND SOURCES "lvgl_touch/tt21100.c") + elseif(CONFIG_LV_TOUCH_CONTROLLER_TMA445) + list(APPEND SOURCES "lvgl_touch/tma445.c") elseif(CONFIG_LV_TOUCH_CONTROLLER_STMPE610) list(APPEND SOURCES "lvgl_touch/stmpe610.c") elseif(CONFIG_LV_TOUCH_CONTROLLER_ADCRAW) @@ -95,6 +99,8 @@ idf_component_register(SRCS ${SOURCES} #CalEPD #sharp-lcd #espressif__esp_lcd_touch_gt911 + #espressif__esp_lcd_touch_tt21100 + touch_tma445 ) target_compile_definitions(${COMPONENT_LIB} PUBLIC "-DLV_LVGL_H_INCLUDE_SIMPLE") diff --git a/lvgl_helpers.c b/lvgl_helpers.c index 84285fca..42e4703c 100644 --- a/lvgl_helpers.c +++ b/lvgl_helpers.c @@ -19,9 +19,9 @@ #include "driver/i2c.h" #ifdef LV_LVGL_H_INCLUDE_SIMPLE -#include "src/lv_core/lv_refr.h" +#include "src/core/lv_refr.h" #else -#include "lvgl/src/lv_core/lv_refr.h" +#include "lvgl/src/core/lv_refr.h" #endif /********************* diff --git a/lvgl_helpers.h b/lvgl_helpers.h index ad66cd75..654cdae7 100644 --- a/lvgl_helpers.h +++ b/lvgl_helpers.h @@ -18,6 +18,12 @@ extern "C" { #include "lvgl_tft/disp_driver.h" #include "lvgl_touch/touch_driver.h" +#define DISPLAY_WIDTH 1024 +#define DISPLAY_HEIGHT 768 +/* Backward compatibility for LV_HOR_RES_MAX & LV_VER_RES_MAX */ +#define LV_HOR_RES_MAX 1024 +#define LV_VER_RES_MAX 768 + /********************* * DEFINES *********************/ diff --git a/lvgl_tft/epdiy_epaper.cpp b/lvgl_tft/epdiy_epaper.cpp index 7bc829e7..3cfcd68c 100644 --- a/lvgl_tft/epdiy_epaper.cpp +++ b/lvgl_tft/epdiy_epaper.cpp @@ -6,7 +6,7 @@ EpdiyHighlevelState hl; uint16_t flushcalls = 0; -uint8_t * framebuffer; +uint8_t *framebuffer; uint8_t temperature = 25; bool init = true; // MODE_DU: Fast monochrome | MODE_GC16 slow with 16 grayscales @@ -15,121 +15,131 @@ enum EpdDrawMode updateMode = MODE_DU; /* Display initialization routine */ void epdiy_init(void) { -// epd_init(&epd_board_v7, &ED097TC2, EPD_LUT_64K); - epd_init(&epd_board_v7, &EC060KH3, EPD_LUT_64K); + epd_init(&epd_board_v7, &ED060XC3, EPD_LUT_64K); // Set VCOM for boards that allow to set this in software (in mV). // This will print an error if unsupported. In this case, // set VCOM using the hardware potentiometer and delete this line. - epd_set_vcom(1760); - hl = epd_hl_init(EPD_BUILTIN_WAVEFORM); - framebuffer = epd_hl_get_framebuffer(&hl); - epd_poweron(); - //Clear all display in initialization to remove any ghosts - epd_fullclear(&hl, temperature); + epd_set_vcom(1760); + hl = epd_hl_init(EPD_BUILTIN_WAVEFORM); + framebuffer = epd_hl_get_framebuffer(&hl); + epd_poweron(); + // Clear all display in initialization to remove any ghosts + epd_fullclear(&hl, temperature); } -/* Suggested by @kisvegabor https://forum.lvgl.io/t/lvgl-port-to-be-used-with-epaper-displays/5630/26 - * @deprecated -*/ -void buf_area_to_framebuffer(const lv_area_t *area, const uint8_t *image_data) { +/* Suggested by @kisvegabor https://forum.lvgl.io/t/lvgl-port-to-be-used-with-epaper-displays/5630/26 + * @deprecated + */ +void buf_area_to_framebuffer(const lv_area_t *area, const uint8_t *image_data) +{ assert(framebuffer != NULL); uint8_t *fb_ptr = &framebuffer[area->y1 * epd_width() / 2 + area->x1 / 2]; lv_coord_t img_w = lv_area_get_width(area); - for(uint32_t y = area->y1; y < area->y2; y++) { - memcpy(fb_ptr, image_data, img_w / 2); - fb_ptr += epd_width() / 2; - image_data += img_w / 2; + for (uint32_t y = area->y1; y < area->y2; y++) + { + memcpy(fb_ptr, image_data, img_w / 2); + fb_ptr += epd_width() / 2; + image_data += img_w / 2; } } /* A copy from epd_copy_to_framebuffer with temporary lenght prediction */ -void buf_copy_to_framebuffer(EpdRect image_area, const uint8_t *image_data) { +void buf_copy_to_framebuffer(EpdRect image_area, const uint8_t *image_data) +{ assert(framebuffer != NULL); - int x, xx = image_area.x; - int y, yy = image_area.y; - int w = image_area.width; - int h = image_area.height; - const uint8_t ucCLRMask[3] = {0xc0,0x38,0x7}; - uint8_t uc, *s, *d; -// source data is one byte per pixel (RGB233) - for (y=yy; y<(yy + h); y++) { - int i = (xx+y) % 3; // which color filter is in use? - s = (uint8_t *)&image_data[(y-yy) * w]; - d = &framebuffer[(y * epd_width() / 2) + (xx / 2)]; - x = xx; - if (x & 1) { - uc = d[0] & 0xf0; // special case for odd starting pixel - if (s[0] & ucCLRMask[i]) - uc |= 0xf; - i++; - s++; - *d++ = uc; - x++; - if (i >= 3) i-=3; - } - for (; x<(xx + w); x+=2) { // work 2 pixels at a time - uc = 0; - if (s[0] & ucCLRMask[i]) uc |= 0xf; - i++; if (i >= 3) i -= 3; - if (s[1] & ucCLRMask[i]) uc |= 0xf0; - i++; if (i >= 3) i -= 3; - *d++ = uc; - s += 2; - } // for x - } // for y -} /* buf_copy_to_framebuffer() */ + + for (uint32_t i = 0; i < image_area.width * image_area.height; i++) + { + uint8_t val = (i % 2) ? (image_data[i / 2] & 0xF0) >> 4 : image_data[i / 2] & 0x0F; + int xx = image_area.x + i % image_area.width; + if (xx < 0 || xx >= epd_width()) + { + continue; + } + int yy = image_area.y + i / image_area.width; + if (yy < 0 || yy >= epd_height()) + { + continue; + } + uint8_t *buf_ptr = &framebuffer[yy * epd_width() / 2 + xx / 2]; + if (xx % 2) + { + *buf_ptr = (*buf_ptr & 0x0F) | (val << 4); + } + else + { + *buf_ptr = (*buf_ptr & 0xF0) | val; + } + } +} /* Required by LVGL. Sends the color_map to the screen with a partial update */ void epdiy_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) { -static int x1=65535,y1=65535,x2=-1,y2=-1; - - ++flushcalls; - uint16_t w = lv_area_get_width(area); - uint16_t h = lv_area_get_height(area); + static int x1 = 65535, y1 = 65535, x2 = -1, y2 = -1; + ++flushcalls; + uint16_t w = lv_area_get_width(area); + uint16_t h = lv_area_get_height(area); - EpdRect update_area = { - .x = (uint16_t)area->x1, - .y = (uint16_t)area->y1, - .width = w, - .height = h - }; + EpdRect update_area = { + .x = (uint16_t)area->x1, + .y = (uint16_t)area->y1, + .width = w, + .height = h}; - // capture the upper left and lower right corners - if (area->x1 < x1) x1 = area->x1; - if (area->y1 < y1) y1 = area->y1; - if (area->x2 > x2) x2 = area->x2; - if (area->y2 > y2) y2 = area->y2; + // capture the upper left and lower right corners + if (area->x1 < x1) + x1 = area->x1; + if (area->y1 < y1) + y1 = area->y1; + if (area->x2 > x2) + x2 = area->x2; + if (area->y2 > y2) + y2 = area->y2; + uint8_t *buf = (uint8_t *)color_map; - uint8_t* buf = (uint8_t *) color_map; - // Buffer debug - /* for (int index=0; index<400; index++) { - printf("%x ", buf[index]); - } */ - // This is the slower version that works good without leaving any white line - buf_copy_to_framebuffer(update_area, buf); + // This is the slower version that works good without leaving any white line + buf_copy_to_framebuffer(update_area, buf); - //Faster mode suggested in LVGL forum (Leaves ghosting&prints bad sections / experimental) NOTE: Do NOT use in production - //buf_area_to_framebuffer(area, buf); - if (lv_disp_flush_is_last(drv)) { // only send to e-paper when complete - update_area.x = x1; - update_area.y = y1; - update_area.width = (x2-x1)+1; - update_area.height = (y2-y1)+1; - epd_hl_update_area(&hl, updateMode, temperature, update_area); //update_area - x1 = y1 = 65535; x2 = y2 = -1; // reset update boundary - } - //printf("epdiy_flush %d x:%d y:%d w:%d h:%d\n", flushcalls,(uint16_t)area->x1,(uint16_t)area->y1,w,h); - /* Inform the graphics library that you are ready with the flushing */ - lv_disp_flush_ready(drv); // do this after the check for "is_last" + // Faster mode suggested in LVGL forum (Leaves ghosting&prints bad sections / experimental) NOTE: Do NOT use in production + // buf_area_to_framebuffer(area, buf); + if (lv_disp_flush_is_last(drv)) + { // only send to e-paper when complete + update_area.x = x1; + update_area.y = y1; + update_area.width = (x2 - x1) + 1; + update_area.height = (y2 - y1) + 1; + epd_hl_update_area(&hl, updateMode, temperature, update_area); // update_area + x1 = y1 = 65535; + x2 = y2 = -1; // reset update boundary + } + // printf("epdiy_flush %d x:%d y:%d w:%d h:%d\n", flushcalls,(uint16_t)area->x1,(uint16_t)area->y1,w,h); + /* Inform the graphics library that you are ready with the flushing */ + lv_disp_flush_ready(drv); } -/* +/* * Called for each pixel. Designed with the idea to fill the buffer directly, not to set each pixel, see LVGL Forum (buf_area_to_framebuffer) -*/ -void epdiy_set_px_cb(lv_disp_drv_t * disp_drv, uint8_t* buf, - lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, - lv_color_t color, lv_opa_t opa) + */ +void epdiy_set_px_cb(lv_disp_drv_t *disp_drv, uint8_t *buf, + lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa) { - buf[(y * buf_w)+x] = color.full; + // Test using RGB232 + int16_t epd_color = 255; + if ((int16_t)color.full < 254) + { + epd_color = (updateMode == MODE_DU) ? 0 : (int16_t)color.full / 3; + } + + // Instead of using epd_draw_pixel: Set pixel directly in *buf that comes afterwards in flush as *color_map + uint16_t idx = (int16_t)y * buf_w / 2 + (int16_t)x / 2; + if (x % 2) + { + buf[idx] = (buf[idx] & 0x0F) | (epd_color & 0xF0); + } + else + { + buf[idx] = (buf[idx] & 0xF0) | (epd_color >> 4); + } } diff --git a/lvgl_tft/epdiy_epaper_mono.cpp b/lvgl_tft/epdiy_epaper_kaleido.cpp similarity index 60% rename from lvgl_tft/epdiy_epaper_mono.cpp rename to lvgl_tft/epdiy_epaper_kaleido.cpp index 5f2185ed..7bc829e7 100644 --- a/lvgl_tft/epdiy_epaper_mono.cpp +++ b/lvgl_tft/epdiy_epaper_kaleido.cpp @@ -15,7 +15,8 @@ enum EpdDrawMode updateMode = MODE_DU; /* Display initialization routine */ void epdiy_init(void) { - epd_init(&epd_board_v7, &ED060XC3, EPD_LUT_64K); +// epd_init(&epd_board_v7, &ED097TC2, EPD_LUT_64K); + epd_init(&epd_board_v7, &EC060KH3, EPD_LUT_64K); // Set VCOM for boards that allow to set this in software (in mV). // This will print an error if unsupported. In this case, // set VCOM using the hardware potentiometer and delete this line. @@ -44,29 +45,45 @@ void buf_area_to_framebuffer(const lv_area_t *area, const uint8_t *image_data) { /* A copy from epd_copy_to_framebuffer with temporary lenght prediction */ void buf_copy_to_framebuffer(EpdRect image_area, const uint8_t *image_data) { assert(framebuffer != NULL); - - for (uint32_t i = 0; i < image_area.width * image_area.height; i++) { - uint8_t val = (i % 2) ? (image_data[i / 2] & 0xF0) >> 4 : image_data[i / 2] & 0x0F; - int xx = image_area.x + i % image_area.width; - if (xx < 0 || xx >= epd_width()) { - continue; - } - int yy = image_area.y + i / image_area.width; - if (yy < 0 || yy >= epd_height()) { - continue; - } - uint8_t *buf_ptr = &framebuffer[yy * epd_width() / 2 + xx / 2]; - if (xx % 2) { - *buf_ptr = (*buf_ptr & 0x0F) | (val << 4); - } else { - *buf_ptr = (*buf_ptr & 0xF0) | val; - } - } -} + int x, xx = image_area.x; + int y, yy = image_area.y; + int w = image_area.width; + int h = image_area.height; + const uint8_t ucCLRMask[3] = {0xc0,0x38,0x7}; + uint8_t uc, *s, *d; +// source data is one byte per pixel (RGB233) + for (y=yy; y<(yy + h); y++) { + int i = (xx+y) % 3; // which color filter is in use? + s = (uint8_t *)&image_data[(y-yy) * w]; + d = &framebuffer[(y * epd_width() / 2) + (xx / 2)]; + x = xx; + if (x & 1) { + uc = d[0] & 0xf0; // special case for odd starting pixel + if (s[0] & ucCLRMask[i]) + uc |= 0xf; + i++; + s++; + *d++ = uc; + x++; + if (i >= 3) i-=3; + } + for (; x<(xx + w); x+=2) { // work 2 pixels at a time + uc = 0; + if (s[0] & ucCLRMask[i]) uc |= 0xf; + i++; if (i >= 3) i -= 3; + if (s[1] & ucCLRMask[i]) uc |= 0xf0; + i++; if (i >= 3) i -= 3; + *d++ = uc; + s += 2; + } // for x + } // for y +} /* buf_copy_to_framebuffer() */ /* Required by LVGL. Sends the color_map to the screen with a partial update */ void epdiy_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) { +static int x1=65535,y1=65535,x2=-1,y2=-1; + ++flushcalls; uint16_t w = lv_area_get_width(area); uint16_t h = lv_area_get_height(area); @@ -78,6 +95,12 @@ void epdiy_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_ma .height = h }; + // capture the upper left and lower right corners + if (area->x1 < x1) x1 = area->x1; + if (area->y1 < y1) y1 = area->y1; + if (area->x2 > x2) x2 = area->x2; + if (area->y2 > y2) y2 = area->y2; + uint8_t* buf = (uint8_t *) color_map; // Buffer debug /* for (int index=0; index<400; index++) { @@ -88,18 +111,17 @@ void epdiy_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_ma //Faster mode suggested in LVGL forum (Leaves ghosting&prints bad sections / experimental) NOTE: Do NOT use in production //buf_area_to_framebuffer(area, buf); - if (lv_disp_flush_is_last(drv)) { - update_area = { - .x = 0, - .y = 0, - .width = epd_width(), - .height = epd_height() - }; - epd_hl_update_area(&hl, updateMode, temperature, update_area); //update_area + if (lv_disp_flush_is_last(drv)) { // only send to e-paper when complete + update_area.x = x1; + update_area.y = y1; + update_area.width = (x2-x1)+1; + update_area.height = (y2-y1)+1; + epd_hl_update_area(&hl, updateMode, temperature, update_area); //update_area + x1 = y1 = 65535; x2 = y2 = -1; // reset update boundary } //printf("epdiy_flush %d x:%d y:%d w:%d h:%d\n", flushcalls,(uint16_t)area->x1,(uint16_t)area->y1,w,h); /* Inform the graphics library that you are ready with the flushing */ - lv_disp_flush_ready(drv); + lv_disp_flush_ready(drv); // do this after the check for "is_last" } /* @@ -109,17 +131,5 @@ void epdiy_set_px_cb(lv_disp_drv_t * disp_drv, uint8_t* buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa) { - // Test using RGB232 - int16_t epd_color = 255; - if ((int16_t)color.full < 254) { - epd_color = (updateMode==MODE_DU) ? 0 : (int16_t)color.full / 3; - } - - //Instead of using epd_draw_pixel: Set pixel directly in *buf that comes afterwards in flush as *color_map - uint16_t idx = (int16_t)y * buf_w / 2 + (int16_t)x / 2; - if (x % 2) { - buf[idx] = (buf[idx] & 0x0F) | (epd_color & 0xF0); - } else { - buf[idx] = (buf[idx] & 0xF0) | (epd_color >> 4); - } + buf[(y * buf_w)+x] = color.full; } diff --git a/lvgl_touch/Kconfig b/lvgl_touch/Kconfig index 9ed4255a..af4c3ec8 100644 --- a/lvgl_touch/Kconfig +++ b/lvgl_touch/Kconfig @@ -11,6 +11,8 @@ menu "LVGL Touch controller" default 6 if LV_TOUCH_CONTROLLER_RA8875 default 7 if LV_TOUCH_CONTROLLER_L58 default 8 if LV_TOUCH_CONTROLLER_GT911 + default 9 if LV_TOUCH_CONTROLLER_TT21100 + default 10 if LV_TOUCH_CONTROLLER_TMA445 choice prompt "Select a touch panel controller model." default LV_TOUCH_CONTROLLER_NONE @@ -33,6 +35,12 @@ menu "LVGL Touch controller" config LV_TOUCH_CONTROLLER_GT911 select LV_TOUCH_DRIVER_PROTOCOL_I2C bool "GT911" + config LV_TOUCH_CONTROLLER_TT21100 + select LV_TOUCH_DRIVER_PROTOCOL_I2C + bool "TT21100" + config LV_TOUCH_CONTROLLER_TMA445 + select LV_TOUCH_DRIVER_PROTOCOL_I2C + bool "TMA445" config LV_TOUCH_CONTROLLER_STMPE610 select LV_TOUCH_DRIVER_PROTOCOL_SPI bool "STMPE610" @@ -529,6 +537,50 @@ menu "LVGL Touch controller" menu "Touchpanel GT911 Pin Assignments" depends on LV_TOUCH_CONTROLLER_GT911 + config LV_TOUCH_I2C_SDA + int + prompt "GPIO for SDA (I2C)" + range 0 39 if IDF_TARGET_ESP32 + range 0 43 if IDF_TARGET_ESP32S3 + + default 39 + help + Configure the I2C touchpanel SDA pin here. + + config LV_TOUCH_I2C_SCL + int "GPIO for clock signal SCL (I2C)" + range 0 39 if IDF_TARGET_ESP32 + range 0 43 if IDF_TARGET_ESP32S3 + + default 40 + help + Configure the I2C touchpanel SCL pin here. + endmenu +menu "Touchpanel TMA445 Pin Assignments" + depends on LV_TOUCH_CONTROLLER_TMA445 + + config LV_TOUCH_I2C_SDA + int + prompt "GPIO for SDA (I2C)" + range 0 39 if IDF_TARGET_ESP32 + range 0 43 if IDF_TARGET_ESP32S3 + + default 39 + help + Configure the I2C touchpanel SDA pin here. + + config LV_TOUCH_I2C_SCL + int "GPIO for clock signal SCL (I2C)" + range 0 39 if IDF_TARGET_ESP32 + range 0 43 if IDF_TARGET_ESP32S3 + + default 40 + help + Configure the I2C touchpanel SCL pin here. + endmenu +menu "Touchpanel TT21100 Pin Assignments" + depends on LV_TOUCH_CONTROLLER_TT21100 + config LV_TOUCH_I2C_SDA int prompt "GPIO for SDA (I2C)" diff --git a/lvgl_touch/gt911.h b/lvgl_touch/gt911.h index 96643b70..b298fe87 100644 --- a/lvgl_touch/gt911.h +++ b/lvgl_touch/gt911.h @@ -1,6 +1,6 @@ #ifndef __GT911_H /* -* Copyright © 2021 Sturnus Inc. +* Copyright © 2024 Fasani Corp. * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the “Software”), to deal in the Software diff --git a/lvgl_touch/tma445.c b/lvgl_touch/tma445.c new file mode 100644 index 00000000..56533c1a --- /dev/null +++ b/lvgl_touch/tma445.c @@ -0,0 +1,388 @@ +/* +* Copyright © 2024 Fasani Corp. + +* Permission is hereby granted, free of charge, to any person obtaining a copy of this +* software and associated documentation files (the “Software”), to deal in the Software +* without restriction, including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +* to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +#include +#include +#include +#include +#include +#include +#include "freertos/queue.h" +#include +#include "driver/gpio.h" +#include + +QueueHandle_t touch_queue = NULL; + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include +#else +#include +#endif +#include "tma445.h" + +#define TAG "TMA445" + +#ifdef CONFIG_LV_TOUCH_I2C_PORT_0 +#define I2C_PORT I2C_NUM_0 +#endif +#ifdef CONFIG_LV_TOUCH_I2C_PORT_1 +#define I2C_PORT I2C_NUM_1 +#endif + +#define TS_INT 3 +#define TS_RES -1 + +#define TOUCH_ADDR 0x24 +// Adjust to your desired level. Too large might record many seconds which is bad for UX +#define TOUCH_QUEUE_LENGTH 15 + +#define CY_REG_BASE 0x00 +#define GET_NUM_TOUCHES(x) ((x) & 0x0F) +#define GET_TOUCH1_ID(x) (((x) & 0xF0) >> 4) +#define GET_TOUCH2_ID(x) ((x) & 0x0F) +#define CY_XPOS 0 +#define CY_YPOS 1 +#define CY_MT_TCH1_IDX 0 +#define CY_MT_TCH2_IDX 1 +#define be16_to_cpu(x) (uint16_t)((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8)) + +#define CY_OPERATE_MODE 0x00 +#define CY_SOFT_RESET_MODE 0x01 +#define CY_LOW_POWER 0x04 // Non used for now +#define CY_HNDSHK_BIT 0x80 + +// I2C common protocol defines +#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */ +#define READ_BIT I2C_MASTER_READ /*!< I2C master read */ +#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/ +#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ +#define ACK_VAL 0x0 /*!< I2C ack value */ +#define NACK_VAL 0x1 /*!< I2C nack value */ + +static uint8_t sec_key[] = {0x00, 0xFF, 0xA5, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + +#define DELAY(ms) vTaskDelay(pdMS_TO_TICKS(ms)) +#define GET_BOOTLOADERMODE(reg) ((reg & 0x10) >> 4) + +/* TrueTouch Standard Product Gen3 (Txx3xx) interface definition */ +struct cyttsp_xydata { + uint8_t hst_mode; + uint8_t tt_mode; + uint8_t tt_stat; + uint16_t x1 __attribute__ ((packed)); + uint16_t y1 __attribute__ ((packed)); + uint8_t z1; + uint8_t touch12_id; + uint16_t x2 __attribute__ ((packed)); + uint16_t y2 __attribute__ ((packed)); + uint8_t z2; +}; + +struct cyttsp_xydata xy_data; + +struct cyttsp_bootloader_data { + uint8_t bl_file; + uint8_t bl_status; + uint8_t bl_error; + uint8_t blver_hi; + uint8_t blver_lo; + uint8_t bld_blver_hi; + uint8_t bld_blver_lo; + uint8_t ttspver_hi; + uint8_t ttspver_lo; + uint8_t appid_hi; + uint8_t appid_lo; + uint8_t appver_hi; + uint8_t appver_lo; + uint8_t cid_0; + uint8_t cid_1; + uint8_t cid_2; +}; + +static const char *LOGG = "LOG"; + +static void i2c_master_init() { + i2c_config_t conf = { }; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = CONFIG_LV_TOUCH_I2C_SDA; + conf.scl_io_num = CONFIG_LV_TOUCH_I2C_SCL; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = 100000; + i2c_param_config(I2C_NUM_0, &conf); + i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0); +} + +static esp_err_t i2c_master_read_slave_reg(i2c_port_t i2c_num, uint8_t i2c_addr, uint8_t i2c_reg, uint8_t* data_rd, size_t size) { + if (size == 0) { + return ESP_OK; + } + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + // first, send device address (indicating write) & register to be read + i2c_master_write_byte(cmd, ( i2c_addr << 1 ), ACK_CHECK_EN); + // send register we want + i2c_master_write_byte(cmd, i2c_reg, ACK_CHECK_EN); + // Send repeated start + i2c_master_start(cmd); + // now send device address (indicating read) & read data + i2c_master_write_byte(cmd, ( i2c_addr << 1 ) | READ_BIT, ACK_CHECK_EN); + if (size > 1) { + i2c_master_read(cmd, data_rd, size - 1, ACK_VAL); + } + i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +static esp_err_t i2c_master_write_slave_reg(i2c_port_t i2c_num, uint8_t i2c_addr, uint8_t i2c_reg, uint8_t* data_wr, size_t size) { + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + // first, send device address (indicating write) & register to be written + i2c_master_write_byte(cmd, ( i2c_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN); + // send register we want + i2c_master_write_byte(cmd, i2c_reg, ACK_CHECK_EN); + // write the data + i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +/* Read contents of a register +---------------------------------------------------------------------------*/ +esp_err_t i2c_read_reg( uint8_t reg, uint8_t *pdata, uint8_t count ) { + return( i2c_master_read_slave_reg( I2C_NUM_0, TOUCH_ADDR, reg, pdata, count ) ); +} + +/* Write value to specified register +---------------------------------------------------------------------------*/ +esp_err_t i2c_write_reg( uint8_t reg, uint8_t *pdata, uint8_t count ) { + return( i2c_master_write_slave_reg( I2C_NUM_0, TOUCH_ADDR, reg, pdata, count ) ); +} + +static QueueHandle_t gpio_evt_queue = NULL; + +static int _cyttsp_hndshk_n_write(uint8_t write_back) +{ + int retval = -1; + uint8_t hst_mode[1]; + uint8_t cmd[1]; + uint8_t tries = 0; + while (retval < 0 && tries++ < 20){ + DELAY(5); + retval = i2c_read_reg(0x00, &hst_mode, sizeof(hst_mode)); + if(retval < 0) { + printf("%s: bus read fail on handshake ret=%d, retries=%d\n", + __func__, retval, tries); + continue; + } + cmd[0] = hst_mode[0] & CY_HNDSHK_BIT ? + write_back & ~CY_HNDSHK_BIT : + write_back | CY_HNDSHK_BIT; + retval = i2c_write_reg(CY_REG_BASE, &cmd, sizeof(cmd)); + if(retval < 0) + printf("%s: bus write fail on handshake ret=%d, retries=%d\n", + __func__, retval, tries); + } + return retval; +} + +// 317 cyttsp.c +static int _cyttsp_hndshk() +{ + int retval = -1; + uint8_t hst_mode[1]; + uint8_t cmd[1]; + uint8_t tries = 0; + while (retval < 0 && tries++ < 20){ + DELAY(5); + retval = i2c_read_reg(0x00, &hst_mode, sizeof(hst_mode)); + if(retval < 0) { + printf("%s: bus read fail on handshake ret=%d, retries=%d\n", + __func__, retval, tries); + continue; + } + cmd[0] = hst_mode[0] & CY_HNDSHK_BIT ? + hst_mode[0] & ~CY_HNDSHK_BIT : + hst_mode[0] | CY_HNDSHK_BIT; + retval = i2c_write_reg(CY_REG_BASE, &cmd, sizeof(cmd)); + if(retval < 0) + printf("%s: bus write fail on handshake ret=%d, retries=%d\n", + __func__, retval, tries); + } + return retval; +} + +static void IRAM_ATTR gpio_isr_handler(void* arg) +{ + uint32_t gpio_num = (uint32_t) arg; + xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); +} + +static void touch_INT(void* arg) +{ + uint32_t io_num; + for (;;) { + if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) { + //printf("GPIO[%"PRIu32"] intr, val: %d\n", io_num, gpio_get_level(io_num)); + /* get event data from CYTTSP device */ + int retval; + retval = i2c_read_reg(CY_REG_BASE, &xy_data, sizeof(xy_data)); + if (retval < 0) { + printf("%s: Error, fail to read device on host interface bus\n", __func__); + } + /* provide flow control handshake */ + _cyttsp_hndshk(); + int touch = GET_NUM_TOUCHES(xy_data.tt_stat); + if (touch) { + xy_data.x1 = be16_to_cpu(xy_data.x1); + xy_data.y1 = be16_to_cpu(xy_data.y1); + if (xQueueSend( touch_queue, &xy_data, ( TickType_t ) 0 ) == pdTRUE) { + // The message was sent successfully + } else { + ESP_LOGW("touch INT", "Could not add xy_data in queue"); + } + } + } + } +} + +void resetTouch() { + gpio_set_level(TS_RES, 1); + DELAY(20); + gpio_set_level(TS_RES, 0); + DELAY(40); + gpio_set_level(TS_RES, 1); + DELAY(20); +} + +void touchStuff() { + // soft reset + esp_err_t err; + uint8_t softres[] = {0x01}; + err = i2c_write_reg(CY_REG_BASE, &softres, 1); + if (err != ESP_OK) { + printf("i2c_write_reg CY_SOFT_RESET_MODE FAILED \n"); + } + DELAY(50); + // write sec_key + err = i2c_write_reg(CY_REG_BASE, &sec_key, sizeof(sec_key)); + if (err != ESP_OK) { + printf("i2c_write_reg sec_key FAILED \n"); + } + printf("i2c_write_reg sec_key OK \n"); + DELAY(88); + + // Before this there is a part that reads sysinfo and sets some registers + // Maybe that is the key to start the engines faster? + uint8_t tries = 0; + struct cyttsp_bootloader_data bl_data = {}; + do { + DELAY(20); + i2c_read_reg(CY_REG_BASE, &bl_data, sizeof(bl_data)); + uint8_t *bl_data_p = (uint8_t *)&(bl_data); + /* for (int i = 0; i < sizeof(struct cyttsp_bootloader_data); i++) { + printf("bl_data[%d]=0x%x\n", i, bl_data_p[i]); + } + printf("bl_data status=0x%x\n", bl_data.bl_status); */ + } while (GET_BOOTLOADERMODE(bl_data.bl_status) && tries++ < 10); + + printf("bl_data status=0x%x\n", GET_BOOTLOADERMODE(bl_data.bl_status)); + + // Set OP mode + int retval; + uint8_t cmd = CY_OPERATE_MODE; + struct cyttsp_xydata xy_data; + printf("%s set operational mode\n",__func__); + memset(&(xy_data), 0, sizeof(xy_data)); + /* wait for TTSP Device to complete switch to Operational mode */ + DELAY(20); + retval = i2c_read_reg(CY_REG_BASE, &xy_data, sizeof(xy_data)); + printf("%s: hstmode:0x%x tt_mode:0x%x tt_stat:0x%x ", __func__, xy_data.hst_mode, xy_data.tt_mode, xy_data.tt_stat); +} + +/** + * @brief Initialize for TMA445 communication via I2C + * @retval None + * IMPORTANT: Adjust here to your panel resolution this is by default + * set to the Kindle 6" paperwhite x_max/y_max + */ +void tma445_init(uint8_t dev_addr) +{ + // It seems there is no need to RESET + #if TS_RES != -1 + gpio_set_direction(TS_RES, GPIO_MODE_OUTPUT); + gpio_set_pull_mode(TS_RES, GPIO_PULLUP_ONLY); + resetTouch(); + #endif + //zero-initialize the config structure. + gpio_config_t io_conf = {}; + io_conf.intr_type = GPIO_INTR_NEGEDGE; + io_conf.pin_bit_mask = TS_INT; + io_conf.mode = GPIO_MODE_INPUT; + //enable pull-up mode + io_conf.pull_up_en = 1; + gpio_config(&io_conf); + //create a queue to handle gpio event from isr + gpio_evt_queue = xQueueCreate(TOUCH_QUEUE_LENGTH, sizeof(uint8_t)); + touch_queue = xQueueCreate(TOUCH_QUEUE_LENGTH, sizeof(xy_data)); + + //i2c_master_init(); // I2C is already started by LVGL + + // Setup interrupt for this IO that goes low on the interrupt + gpio_set_intr_type(TS_INT, GPIO_INTR_NEGEDGE); + gpio_install_isr_service(0); + gpio_isr_handler_add(TS_INT, gpio_isr_handler, (void *)TS_INT); + // INT detection task + xTaskCreatePinnedToCore(touch_INT, TAG, configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); + + // Start I2C scanner task + //xTaskCreatePinnedToCore(i2cscanner, TAG, configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); + touchStuff(); +} + +/** + * @brief Get the touch screen X and Y positions values. Ignores multi touch + * @param drv: + * @param data: Store data here + * @retval Always false + */ +bool tma445_read(lv_indev_drv_t *drv, lv_indev_data_t *data) +{ + /* Read from the touch Queue */ + if( xQueueReceive(touch_queue, &xy_data, ( TickType_t ) 0 ) ) { + data->state = LV_INDEV_STATE_PR; + data->point.y = 768 - xy_data.x1; + data->point.x = xy_data.y1; + printf("qR X:%d Y:%d\n", data->point.x, data->point.y); + } else { + data->state = LV_INDEV_STATE_REL; + data->point.x = -1; + data->point.y = -1; + } + vTaskDelay(pdMS_TO_TICKS(1)); + return false; +} + + diff --git a/lvgl_touch/tma445.h b/lvgl_touch/tma445.h new file mode 100644 index 00000000..dc78f0ce --- /dev/null +++ b/lvgl_touch/tma445.h @@ -0,0 +1,53 @@ +#ifndef __TMA445_H +/* +* Copyright © 2024 Fasani Corp. + +* Permission is hereby granted, free of charge, to any person obtaining a copy of this +* software and associated documentation files (the “Software”), to deal in the Software +* without restriction, including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +* to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#define __TMA445_H + +#include +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize for GT911 communication via I2C + * @param dev_addr: Device address on communication Bus (I2C slave address of GT911). + * @retval None + */ +void tma445_init(uint8_t dev_addr); + +/** + * @brief Get the touch screen X and Y positions values. Ignores multi touch + * @param drv: + * @param data: Store data here + * @retval Always false + */ +bool tma445_read(lv_indev_drv_t *drv, lv_indev_data_t *data); + +#ifdef __cplusplus +} +#endif +#endif /* __GT911_H */ diff --git a/lvgl_touch/touch_driver.c b/lvgl_touch/touch_driver.c index 499f5cff..52488fbb 100644 --- a/lvgl_touch/touch_driver.c +++ b/lvgl_touch/touch_driver.c @@ -5,8 +5,6 @@ #include "touch_driver.h" #include "tp_spi.h" #include "tp_i2c.h" -// Is not being included in CMakeLists.txt (Research why) -#include "l58.h" void touch_driver_init(void) { @@ -18,6 +16,10 @@ void touch_driver_init(void) l58_init(); #elif defined (CONFIG_LV_TOUCH_CONTROLLER_GT911) gt911_init(0x5d); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_TT21100) + tt21100_init(0x24); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_TMA445) + tma445_init(0x24); #elif defined (CONFIG_LV_TOUCH_CONTROLLER_STMPE610) stmpe610_init(); #elif defined (CONFIG_LV_TOUCH_CONTROLLER_ADCRAW) @@ -41,6 +43,10 @@ bool touch_driver_read(lv_indev_drv_t *drv, lv_indev_data_t *data) res = l58_read(drv, data); #elif defined (CONFIG_LV_TOUCH_CONTROLLER_GT911) res = gt911_read(drv, data); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_TT21100) + res = tt21100_read(drv, data); +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_TMA445) + res = tma445_read(drv, data); #elif defined (CONFIG_LV_TOUCH_CONTROLLER_STMPE610) res = stmpe610_read(drv, data); #elif defined (CONFIG_LV_TOUCH_CONTROLLER_ADCRAW) diff --git a/lvgl_touch/touch_driver.h b/lvgl_touch/touch_driver.h index 44378db7..3868f074 100644 --- a/lvgl_touch/touch_driver.h +++ b/lvgl_touch/touch_driver.h @@ -32,8 +32,14 @@ extern "C" { #include "FT81x.h" #elif defined (CONFIG_LV_TOUCH_CONTROLLER_RA8875) #include "ra8875_touch.h" +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_L58) +#include "l58.h" #elif defined (CONFIG_LV_TOUCH_CONTROLLER_GT911) #include "gt911.h" +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_TT21100) +#include "tt21100.h" +#elif defined (CONFIG_LV_TOUCH_CONTROLLER_TMA445) +#include "tma445.h" #endif /********************* diff --git a/lvgl_touch/tt21100.c b/lvgl_touch/tt21100.c new file mode 100644 index 00000000..4c96402e --- /dev/null +++ b/lvgl_touch/tt21100.c @@ -0,0 +1,100 @@ +/* +* Copyright © 2024 Fasani Corp. +* +* LVGL for TrueTouch 21100 +* Permission is hereby granted, free of charge, to any person obtaining a copy of this +* software and associated documentation files (the “Software”), to deal in the Software +* without restriction, including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +* to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_lcd_touch_tt21100.h" +#include +#include "driver/i2c.h" + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include +#else +#include +#endif +#include "tt21100.h" + +#define TAG "TT21100" + +#ifdef CONFIG_LV_TOUCH_I2C_PORT_0 + #define I2C_PORT I2C_NUM_0 +#endif +#ifdef CONFIG_LV_TOUCH_I2C_PORT_1 + #define I2C_PORT I2C_NUM_1 +#endif + +esp_lcd_touch_handle_t tp; + +/** + * @brief Initialize for TT communication via I2C + * @retval None + */ +void tt21100_init(uint8_t dev_addr) { + //printf("init TT I2C SDA:%d SCL:%d\n",CONFIG_LV_TOUCH_I2C_SDA, CONFIG_LV_TOUCH_I2C_SCL); + // Setup up touch panel + esp_lcd_panel_io_handle_t tp_io_handle = NULL; + esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_TT21100_CONFIG(); + + esp_lcd_touch_config_t tp_cfg = { + .x_max = 1448, + .y_max = 1072, + .rst_gpio_num = 9, + .int_gpio_num = 3, + .levels = { + .reset = 0, + .interrupt = 0, + }, + .flags = { + .swap_xy = 0, + .mirror_x = 0, + .mirror_y = 1, + }, + }; + + esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)I2C_PORT, &tp_io_config, &tp_io_handle); + + esp_lcd_touch_new_i2c_tt21100(tp_io_handle, &tp_cfg, &tp); +} + +/** + * @brief Get the touch screen X and Y positions values. Ignores multi touch + * @param drv: + * @param data: Store data here + * @retval Always false + */ +bool tt21100_read(lv_indev_drv_t *drv, lv_indev_data_t *data) { + esp_lcd_touch_read_data(tp); + uint16_t touch_x[1]; + uint16_t touch_y[1]; + uint16_t touch_strength[1]; + uint8_t touch_cnt = 0; + bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, touch_x, touch_y, touch_strength, &touch_cnt, 1); + if (touchpad_pressed) { + data->state = LV_INDEV_STATE_PR; + data->point.x = touch_x[0]; + data->point.y = touch_y[0]; + printf("x:%d y:%d\n", (int)touch_x[0], (int)touch_y[0]); + } else { + data->state = LV_INDEV_STATE_REL; + data->point.x = -1; + data->point.y = -1; + } + return false; +} diff --git a/lvgl_touch/tt21100.h b/lvgl_touch/tt21100.h new file mode 100644 index 00000000..0de3b259 --- /dev/null +++ b/lvgl_touch/tt21100.h @@ -0,0 +1,50 @@ +/* +* Copyright © 2024 Fasani Corp. +* +* This is the touch driver that Eink Kaleido panel has for the models v1 & v2 +* Permission is hereby granted, free of charge, to any person obtaining a copy of this +* software and associated documentation files (the “Software”), to deal in the Software +* without restriction, including without limitation the rights to use, copy, modify, merge, +* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +* to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include +#ifdef LV_LVGL_H_INCLUDE_SIMPLE +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize for TT21100 communication via I2C + * @param dev_addr: Device address on communication Bus (I2C slave address of GT911). + * @retval None + */ +void tt21100_init(uint8_t dev_addr); + +/** + * @brief Get the touch screen X and Y positions values. Ignores multi touch + * @param drv: + * @param data: Store data here + * @retval Always false + */ +bool tt21100_read(lv_indev_drv_t *drv, lv_indev_data_t *data); + +#ifdef __cplusplus +} +#endif