diff --git a/dependencies.lock b/dependencies.lock index 30a4559..764e12b 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -1,4 +1,19 @@ dependencies: + atanisoft/esp_lcd_touch_xpt2046: + component_hash: + 7e6381b67b6e487379118368b8e91624dc87036ef5734818de1db9eb35697998 + dependencies: + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: private + version: '>=1.0.4' + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.6 espressif/cmake_utilities: component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f @@ -25,13 +40,50 @@ dependencies: registry_url: https://components.espressif.com/ type: service version: 2.0.1 + espressif/esp_lcd_touch: + component_hash: + 3f85a7d95af876f1a6ecca8eb90a81614890d0f03a038390804e5a77e2caf862 + dependencies: + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com + type: service + version: 1.2.1 + espressif/esp_lvgl_port: + component_hash: + f872401524cb645ee6ff1c9242d44fb4ddcfd4d37d7be8b9ed3f4e85a404efcd + dependencies: + - name: idf + require: private + version: '>=5.1' + - name: lvgl/lvgl + registry_url: https://components.espressif.com + require: public + version: '>=8,<10' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.7.0 idf: source: type: idf version: 5.5.1 + lvgl/lvgl: + component_hash: + 17e68bfd21f0edf4c3ee838e2273da840bf3930e5dbc3bfa6c1190c3aed41f9f + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 9.4.0 direct_dependencies: +- atanisoft/esp_lcd_touch_xpt2046 - espressif/esp_lcd_ili9341 +- espressif/esp_lvgl_port - idf -manifest_hash: 5c04a0422ee419cb472e6df05c9d4292985ae0cd069ef8404da0161609cc967c +- lvgl/lvgl +manifest_hash: d2b549f17124d3d38e4add4614fb10956ada1a576e4c5f6a623ea3756bcd3ba5 target: esp32 version: 2.0.0 diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 40f5b7d..6bebada 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,3 +1,4 @@ idf_component_register(SRCS "spincoat-plater-firmware.c" "dshot_esc_encoder.c" "display.c" - PRIV_REQUIRES esp_driver_rmt esp_driver_gpio esp_driver_uart esp_driver_spi esp_lcd unity + PRIV_REQUIRES esp_driver_rmt esp_driver_gpio esp_driver_uart esp_driver_spi + esp_lcd unity lvgl esp_lvgl_port esp_lcd_touch_xpt2046 INCLUDE_DIRS ".") diff --git a/main/Kconfig b/main/Kconfig index f9df9c7..88df847 100644 --- a/main/Kconfig +++ b/main/Kconfig @@ -9,36 +9,64 @@ menu "Pin Mapping Configuration" default 27 help This is the pin used for receiving UART telemetry from the ESC. - config TFT_MISO_PIN + config TFT_MISO int "SPI LCD MISO pin" default 12 help This is the pin for MISO on the SPI LCD. - config TFT_MOSI_PIN + config TFT_MOSI int "SPI LCD MOSI pin" default 13 help This is the pin for MOSI on the SPI LCD. - config TFT_SCKL_PIN + config TFT_SCKL int "SPI LCD SCKL pin" default 14 help This is the pin for SCKL on the SPI LCD. - config TFT_CS_PIN + config TFT_CS int "SPI LCD CS pin" default 15 help This is the pin for CS on the SPI LCD. - config TFT_DC_PIN + config TFT_DC int "SPI LCD DC pin" default 2 help This is the pin for DC on the SPI LCD. - config TFT_BL_PIN + config TFT_BL int "SPI LCD BL (backlight) pin" default 21 help This is the pin for backlight control on the SPI LCD. + + + config TOUCH_IRQ + int "SPI touchscreen IRQ pin" + default 36 + help + This is the pin for IRQ on the SPI touchscreen. + config TOUCH_MOSI + int "SPI touchscreen MOSI pin" + default 32 + help + This is the pin for MOSI on the SPI touchscreen. + config TOUCH_MISO + int "SPI touchscreen MISO pin" + default 39 + help + This is the pin for MISO on the SPI touchscreen. + config TOUCH_CLK + int "SPI touchscreen CLK pin" + default 25 + help + This is the pin for CLK on the SPI touchscreen. + config TOUCH_CS + int "SPI touchscreen CS pin" + default 33 + help + This is the pin for CS on the SPI touchscreen. + config TFT_HRES int "The horizontal resolution of the TFT display" default 320 diff --git a/main/display.c b/main/display.c index cffae3d..9bfd549 100644 --- a/main/display.c +++ b/main/display.c @@ -18,9 +18,17 @@ #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_commands.h" #include "esp_lcd_ili9341.h" +#include "esp_lcd_touch_xpt2046.h" + +#include "lvgl.h" +#include "esp_lvgl_port.h" SemaphoreHandle_t refresh_finish = NULL; +esp_lcd_panel_io_handle_t io_handle = NULL; +esp_lcd_panel_handle_t panel_handle = NULL; +esp_lcd_touch_handle_t tp = NULL; + static const char * TAG = "spincoat-plater-firmware/display"; /** @@ -36,29 +44,108 @@ IRAM_ATTR static bool notify_refresh_ready(esp_lcd_panel_io_handle_t panel_io, e } /** - * Draws a test bitmap of stripes of colors to the LCD. + * Callback for LVGL pointer input device, it reads from the esp_lcd_touch driver and updates + * the LVGL internal state for event handling. */ -void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle) -{ - refresh_finish = xSemaphoreCreateBinary(); - TEST_ASSERT_NOT_NULL(refresh_finish); +void touch_driver_read(lv_indev_t * indev, lv_indev_data_t * data) { + ESP_ERROR_CHECK(esp_lcd_touch_read_data(tp)); - uint16_t row_line = TFT_VRES / TFT_BPP; - uint8_t byte_per_pixel = TFT_BPP / 8; - uint8_t *color = (uint8_t *)heap_caps_calloc(1, row_line * TFT_VRES * byte_per_pixel, MALLOC_CAP_DMA); - TEST_ASSERT_NOT_NULL(color); + uint16_t x[1]; + uint16_t y[1]; + uint16_t strength[1]; + uint8_t count = 0; - for (int j = 0; j < TFT_BPP; j++) { - for (int i = 0; i < row_line * TFT_HRES ; i++) { - for (int k = 0; k < byte_per_pixel; k++) { - color[i * byte_per_pixel + k] = (SPI_SWAP_DATA_TX(BIT(j), TFT_BPP) >> (k * 8)) & 0xff; - } - } - TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, j * row_line, TFT_HRES , (j + 1) * row_line, color)); - xSemaphoreTake(refresh_finish, portMAX_DELAY); + bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, x, y, strength, &count, 1); + + if(touchpad_pressed == true) { + //ESP_LOGI(TAG, "Touchpad pressed from LVGL..\n"); + data->point.x = x[0]; + data->point.y = y[0]; + data->state = LV_INDEV_STATE_PRESSED; + } else { + data->state = LV_INDEV_STATE_RELEASED; } - free(color); - vSemaphoreDelete(refresh_finish); +} + +/** + * Callback for LVGL button events. + */ +static void btn_event_cb(lv_event_t * e) { + lv_event_code_t code = lv_event_get_code(e); + + //ESP_LOGI(TAG, "Button callback fired."); + + if(code == LV_EVENT_CLICKED) { + ESP_LOGI(TAG, "LVGL button pressed."); + } +} + +/** + * Draws a test UI + */ +void lv_example_btn_1(void) +{ + lv_obj_t * label; + + lv_obj_t * btn1 = lv_btn_create(lv_scr_act()); + lv_obj_add_event_cb(btn1, btn_event_cb, LV_EVENT_ALL, NULL); + lv_obj_set_width(btn1, 120); + lv_obj_set_height(btn1, 100); + lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -40); + + label = lv_label_create(btn1); + lv_label_set_text(label, "Button"); + lv_obj_center(label); + + lv_obj_t * btn2 = lv_btn_create(lv_scr_act()); + lv_obj_add_event_cb(btn2, btn_event_cb, LV_EVENT_ALL, NULL); + lv_obj_align(btn2, LV_ALIGN_CENTER, 0, 40); + lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE); + lv_obj_set_height(btn2, LV_SIZE_CONTENT); + + label = lv_label_create(btn2); + lv_label_set_text(label, "Toggle"); + lv_obj_center(label); +} + + +/** + * Initializes the SPI bus and starts communication with xpt2046 touch controller. + */ +void init_touchscreen_xpt2046(void) { + + ESP_LOGI(TAG, "Initialize touchscreen SPI bus"); + + const spi_bus_config_t ts_bus_config = { + .mosi_io_num = CONFIG_TOUCH_MOSI, + .miso_io_num = CONFIG_TOUCH_MISO, + .sclk_io_num = CONFIG_TOUCH_CLK, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 4096, + }; + + ESP_ERROR_CHECK(spi_bus_initialize(TOUCH_SPI_HOST, &ts_bus_config, SPI_DMA_CH_AUTO)); + + esp_lcd_panel_io_handle_t tp_io_handle = NULL; + esp_lcd_panel_io_spi_config_t tp_io_config = ESP_LCD_TOUCH_IO_SPI_XPT2046_CONFIG(CONFIG_TOUCH_CS); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(TOUCH_SPI_HOST, &tp_io_config, &tp_io_handle)); + + esp_lcd_touch_config_t tp_cfg = { + .x_max = TFT_HRES, + .y_max = TFT_VRES, + .rst_gpio_num = -1, + .int_gpio_num = -1, + .flags = { + .swap_xy = 1, + .mirror_x = 0, + .mirror_y = 0, + }, + }; + + ESP_LOGI(TAG, "Initialize touch controller XPT2046"); + ESP_ERROR_CHECK(esp_lcd_touch_new_spi_xpt2046(tp_io_handle, &tp_cfg, &tp)); + ESP_LOGI(TAG, "Finished initialization of XPT2046 touch driver"); } /** @@ -68,7 +155,7 @@ void init_spi_lcd(void) { ESP_LOGI(TAG, "Turn on backlight"); gpio_config_t io_conf = { - .pin_bit_mask = (1ULL << GPIO_TFT_BL), + .pin_bit_mask = (1ULL << CONFIG_TFT_BL), .mode = GPIO_MODE_OUTPUT, .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, @@ -76,22 +163,21 @@ void init_spi_lcd(void) { }; gpio_config(&io_conf); - gpio_set_level(GPIO_TFT_BL, 1); + gpio_set_level(CONFIG_TFT_BL, 1); ESP_LOGI(TAG, "Initialize SPI bus"); - const spi_bus_config_t bus_config = ILI9341_PANEL_BUS_SPI_CONFIG(GPIO_TFT_SCKL, - GPIO_TFT_MOSI, TFT_HRES * 80 * TFT_BPP / 8); + const spi_bus_config_t bus_config = ILI9341_PANEL_BUS_SPI_CONFIG(CONFIG_TFT_SCKL, + CONFIG_TFT_MOSI, TFT_HRES * 80 * TFT_BPP / 8); TEST_ESP_OK(spi_bus_initialize(LCD_SPI_HOST, &bus_config, SPI_DMA_CH_AUTO)); ESP_LOGI(TAG, "Install panel IO"); - esp_lcd_panel_io_handle_t io_handle = NULL; - const esp_lcd_panel_io_spi_config_t io_config = ILI9341_PANEL_IO_SPI_CONFIG(GPIO_TFT_CS, GPIO_TFT_DC, + const esp_lcd_panel_io_spi_config_t io_config = ILI9341_PANEL_IO_SPI_CONFIG(CONFIG_TFT_CS, CONFIG_TFT_DC, notify_refresh_ready, NULL); TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_HOST, &io_config, &io_handle)); ESP_LOGI(TAG, "Install ili9341 panel driver"); - esp_lcd_panel_handle_t panel_handle = NULL; + const esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = -1, // Shared with Touch reset #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) @@ -113,15 +199,59 @@ void init_spi_lcd(void) { TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); #endif - ESP_LOGI(TAG, "Finished init of spi LCD."); - ESP_LOGI(TAG, "Drawing bitmap.");; - test_draw_bitmap(panel_handle); - vTaskDelay(pdMS_TO_TICKS(3000)); - - // Tear it back down, move this into a function to clean up after ourselves if it's ever needed. - ESP_LOGI(TAG, "Destroying and cleaning up LCD/SPI handles."); - gpio_reset_pin(GPIO_TFT_BL); - TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); - TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); - TEST_ESP_OK(spi_bus_free(LCD_SPI_HOST)); + ESP_LOGI(TAG, "Finished init of spi LCD"); +} + +/** + * Initializes the LVGL display and sets everything up so that LVGL can draw to the TFT LCD. + */ +void init_lvgl_display(void) { + static lv_disp_t * disp_handle = NULL; + + const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG(); + TEST_ESP_OK(lvgl_port_init(&lvgl_cfg)); + + const lvgl_port_display_cfg_t disp_cfg = { + .io_handle = io_handle, + .panel_handle = panel_handle, + .buffer_size = LVGL_BUF_SIZE, + .double_buffer = true, + .hres = TFT_HRES, + .vres = TFT_VRES, + .monochrome = false, + .color_format = LV_COLOR_FORMAT_RGB565, + .rotation = { + .swap_xy = true, + .mirror_x = false, + .mirror_y = false, + }, + .flags = { + .buff_dma = false, + .swap_bytes = false, + } + }; + disp_handle = lvgl_port_add_disp(&disp_cfg); +} + + +/** + * Initializes the LVGL input driver as a pointer type so that LVGL can read input and dispatch + * UI events. + */ +void init_input(void) { + lv_indev_t * indev = lv_indev_create(); + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_read_cb(indev, touch_driver_read); +} + +/** + * Main function that completely initializes the TFT LCD, the touchscreen, the LVGL display, + * input device and draws the UI. + */ +void init_display(void) { + init_spi_lcd(); + init_touchscreen_xpt2046(); + init_lvgl_display(); + init_input(); + lv_example_btn_1(); } diff --git a/main/display.h b/main/display.h index f393d53..66f57ce 100644 --- a/main/display.h +++ b/main/display.h @@ -4,17 +4,14 @@ #include "esp_lcd_panel_io.h" -#define LCD_SPI_HOST SPI2_HOST +#define LCD_SPI_HOST SPI2_HOST +#define TOUCH_SPI_HOST SPI3_HOST -#define GPIO_TFT_MISO CONFIG_TFT_MISO_PIN -#define GPIO_TFT_MOSI CONFIG_TFT_MOSI_PIN -#define GPIO_TFT_SCKL CONFIG_TFT_SCKL_PIN -#define GPIO_TFT_CS CONFIG_TFT_CS_PIN -#define GPIO_TFT_DC CONFIG_TFT_DC_PIN -#define GPIO_TFT_BL CONFIG_TFT_BL_PIN // Backlight -#define TFT_HRES CONFIG_TFT_HRES -#define TFT_VRES CONFIG_TFT_VRES -#define TFT_BPP CONFIG_TFT_BPP +#define TFT_HRES CONFIG_TFT_HRES +#define TFT_VRES CONFIG_TFT_VRES +#define TFT_BPP CONFIG_TFT_BPP + +#define LVGL_BUF_SIZE TFT_VRES * TFT_HRES / 10 * (TFT_BPP / 8) /** * Draws a test bitmap of stripes of colors to the LCD. @@ -22,6 +19,6 @@ void test_draw_bitmap(esp_lcd_panel_handle_t panel_handle); /** - * Initializes the SPI LCD in preparation for writing graphics to it. + * Initializes the display TFT and touchscreen. */ -void init_spi_lcd(void); +void init_display(void); diff --git a/main/idf_component.yml b/main/idf_component.yml index d4cd4fd..a3a1e9e 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -15,3 +15,6 @@ dependencies: # # All dependencies of `main` are public by default. # public: true espressif/esp_lcd_ili9341: ^2.0.0 + lvgl/lvgl: ^9.4.0 + espressif/esp_lvgl_port: ^2.3.0 + atanisoft/esp_lcd_touch_xpt2046: ^1.0.2 diff --git a/main/spincoat-plater-firmware.c b/main/spincoat-plater-firmware.c index 8337c22..8156ae4 100644 --- a/main/spincoat-plater-firmware.c +++ b/main/spincoat-plater-firmware.c @@ -206,7 +206,7 @@ void parse_telemetry(void) { } void app_main(void) { - init_spi_lcd(); + init_display(); init_rmt_esc_tx(); throttle.throttle = 300;