From d27579848ad09dcf4553dcc1ce48aa1b2f9620fa Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Sun, 28 Jan 2024 16:34:25 +0100 Subject: [PATCH] SD card support (#23) ### General - Added support for SD cards in `HadwareConfig` - Properly disabled PC build for now (I was still getting error emails) - Updated `README.md` with a device compatibility table ### T-Deck: - Implemented SD card support - Logging message cleanup - Updated `config,h` with various new settings - Reduced stack depth from `8096` to `5000` --- .github/workflows/pc.yml | 24 ------ .github/workflows/pc.yml.disabled | 24 ++++++ README.md | 22 +++-- boards/lilygo_tdeck/CMakeLists.txt | 2 +- boards/lilygo_tdeck/bootstrap.c | 40 +++++++-- boards/lilygo_tdeck/config.h | 58 +++++++++++++ boards/lilygo_tdeck/display.c | 108 +++++++---------------- boards/lilygo_tdeck/keyboard.c | 13 ++- boards/lilygo_tdeck/lilygo_tdeck.c | 5 +- boards/lilygo_tdeck/lvgl.c | 21 +++-- boards/lilygo_tdeck/sdcard.c | 132 +++++++++++++++++++++++++++++ boards/lilygo_tdeck/touch.c | 19 +++-- docs/ideas.md | 2 +- tactility/src/hardware.c | 12 ++- tactility/src/hardware_config.h | 4 +- tactility/src/sdcard.c | 82 ++++++++++++++++++ tactility/src/sdcard.h | 31 +++++++ 17 files changed, 457 insertions(+), 142 deletions(-) delete mode 100644 .github/workflows/pc.yml create mode 100644 .github/workflows/pc.yml.disabled create mode 100644 boards/lilygo_tdeck/sdcard.c create mode 100644 tactility/src/sdcard.c create mode 100644 tactility/src/sdcard.h diff --git a/.github/workflows/pc.yml b/.github/workflows/pc.yml deleted file mode 100644 index 9516998b..00000000 --- a/.github/workflows/pc.yml +++ /dev/null @@ -1,24 +0,0 @@ -#name: Build -#on: [push] -#jobs: -# Build-PC: -# runs-on: ubuntu-latest -# steps: -# - name: Checkout repo -# uses: actions/checkout@v2 -# with: -# submodules: recursive -# - uses: libsdl-org/setup-sdl@main -# id: sdl -# with: -# install-linux-dependencies: true -# version: 2-latest -# version-sdl-image: 2-latest -# - name: Configure Project -# uses: threeal/cmake-action@v1.3.0 -# - name: Prepare Project -# run: cmake -S ./ -B build -# - name: Build Project -# env: -# USE_SDL_WITH_NAMESPACE: true -# run: cmake --build build diff --git a/.github/workflows/pc.yml.disabled b/.github/workflows/pc.yml.disabled new file mode 100644 index 00000000..89b6bdae --- /dev/null +++ b/.github/workflows/pc.yml.disabled @@ -0,0 +1,24 @@ +name: Build +on: [push] +jobs: + Build-PC: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v2 + with: + submodules: recursive + - uses: libsdl-org/setup-sdl@main + id: sdl + with: + install-linux-dependencies: true + version: 2-latest + version-sdl-image: 2-latest + - name: Configure Project + uses: threeal/cmake-action@v1.3.0 + - name: Prepare Project + run: cmake -S ./ -B build + - name: Build Project + env: + USE_SDL_WITH_NAMESPACE: true + run: cmake --build build diff --git a/README.md b/README.md index 11259cd8..ee78f4a2 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,26 @@ and [here](https://components.espressif.com/components?q=esp_lcd_touch) ### Devices +Most hardware configurations should work, but it might require you to set up the drivers yourself. + Predefined configurations are available for: -- LilyGo T-Deck (see [lilygo.cc](https://www.lilygo.cc/products/t-deck), [AliExpress](https://www.aliexpress.com/item/1005005692235592.html)) -- Waveshare S3 Touch LCD 4.3 ([docs](https://www.waveshare.com/wiki/ESP32-S3-Touch-LCD-4.3)) -- Yellow Board 2432S024C: a 2.4" display with capacitive touch (see AliExpress [1](https://www.aliexpress.com/item/1005005902429049.html), [2](https://www.aliexpress.com/item/1005005865107357.html)) -- (more will follow) +| Device | Screen&Touch | SD card | Other | +|------------------------------------------|--------------|---------|----------| +| [LilyGo T-Deck][tdeck] | ✅ | ✅ | Keyboard | +| [Waveshare S3 Touch][waveshare_s3_touch] | ✅ | ⏳ | | +| Yellow Board 2432S024C (\*) | ✅ | ⏳ | | -Other configurations can be supported, but they require you to set up the drivers yourself. +- ✅: Capable and implemented +- ⏳: Capable but not yet implemented +- ❌: Not capable + +(*) Note: Only the capacitive version is supported. See AliExpress [here][2432s024c_1] and [here][2432s024c_2]. + +[tdeck]: https://www.lilygo.cc/products/t-deck +[waveshare_s3_touch]: https://www.aliexpress.com/item/1005005692235592.html +[2432s024c_1]: https://www.aliexpress.com/item/1005005902429049.html +[2432s024c_2]: https://www.aliexpress.com/item/1005005865107357.html ## Guide diff --git a/boards/lilygo_tdeck/CMakeLists.txt b/boards/lilygo_tdeck/CMakeLists.txt index ce35f140..56fd7fc8 100644 --- a/boards/lilygo_tdeck/CMakeLists.txt +++ b/boards/lilygo_tdeck/CMakeLists.txt @@ -1,7 +1,7 @@ idf_component_register( SRC_DIRS "." INCLUDE_DIRS "." - REQUIRES esp_lvgl_port esp_lcd esp_lcd_touch_gt911 driver + REQUIRES esp_lvgl_port esp_lcd esp_lcd_touch_gt911 driver vfs fatfs ) target_link_libraries(${COMPONENT_LIB} ${IDF_TARGET_NAME} tactility) \ No newline at end of file diff --git a/boards/lilygo_tdeck/bootstrap.c b/boards/lilygo_tdeck/bootstrap.c index 5fa157a6..7dace3b1 100644 --- a/boards/lilygo_tdeck/bootstrap.c +++ b/boards/lilygo_tdeck/bootstrap.c @@ -1,14 +1,14 @@ #include "config.h" -#include "keyboard.h" #include "kernel.h" +#include "keyboard.h" #include "log.h" +#include #define TAG "tdeck_bootstrap" -lv_disp_t* tdeck_init_display(); +lv_disp_t* tdeck_display_init(); static bool tdeck_power_on() { - ESP_LOGI(TAG, "power on"); gpio_config_t device_power_signal_config = { .pin_bit_mask = BIT64(TDECK_POWERON_GPIO), .mode = GPIO_MODE_OUTPUT, @@ -42,9 +42,27 @@ static bool init_i2c() { && i2c_driver_install(TDECK_I2C_BUS_HANDLE, i2c_conf.mode, 0, 0, 0) == ESP_OK; } +static bool init_spi() { + spi_bus_config_t bus_config = { + .sclk_io_num = TDECK_SPI_PIN_SCLK, + .mosi_io_num = TDECK_SPI_PIN_MOSI, + .miso_io_num = TDECK_SPI_PIN_MISO, + .quadwp_io_num = -1, // Quad SPI LCD driver is not yet supported + .quadhd_io_num = -1, // Quad SPI LCD driver is not yet supported + .max_transfer_sz = TDECK_SPI_TRANSFER_SIZE_LIMIT, + }; + + if (spi_bus_initialize(TDECK_SPI_HOST, &bus_config, SPI_DMA_CH_AUTO) != ESP_OK) { + return false; + } else { + return true; + } +} + bool tdeck_bootstrap() { + ESP_LOGI(TAG, "Power on"); if (!tdeck_power_on()) { - TT_LOG_E(TAG, "failed to power on device"); + TT_LOG_E(TAG, "Power on failed"); } /** @@ -56,11 +74,19 @@ bool tdeck_bootstrap() { * By reading from I2C until it succeeds and to then init the driver. * It doesn't work, because it never recovers from the error. */ - TT_LOG_I(TAG, "waiting after power-on"); - tt_delay_ms(2000); + TT_LOG_I(TAG, "Waiting after power-on"); + tt_delay_ms(TDECK_POWERON_DELAY); + TT_LOG_I(TAG, "Init I2C"); if (!init_i2c()) { - TT_LOG_E(TAG, "failed to init I2C"); + TT_LOG_E(TAG, "Init I2C failed"); + return false; + } + + TT_LOG_I(TAG, "Init SPI"); + if (!init_spi()) { + TT_LOG_E(TAG, "Init SPI failed"); + return false; } keyboard_wait_for_response(); diff --git a/boards/lilygo_tdeck/config.h b/boards/lilygo_tdeck/config.h index d99fec1b..d52239dd 100644 --- a/boards/lilygo_tdeck/config.h +++ b/boards/lilygo_tdeck/config.h @@ -3,5 +3,63 @@ #include "driver/i2c.h" #include "driver/gpio.h" +// Main bus, used by GT911 touch hardware and keyboard #define TDECK_I2C_BUS_HANDLE (0) + +// SPI +#define TDECK_SPI_HOST SPI2_HOST +#define TDECK_SPI_PIN_SCLK GPIO_NUM_40 +#define TDECK_SPI_PIN_MOSI GPIO_NUM_41 +#define TDECK_SPI_PIN_MISO GPIO_NUM_38 +#define TDECK_SPI_TRANSFER_SIZE_LIMIT (TDECK_LCD_HORIZONTAL_RESOLUTION * TDECK_LCD_SPI_TRANSFER_HEIGHT * (TDECK_LCD_BITS_PER_PIXEL / 8)) + +// Power on #define TDECK_POWERON_GPIO GPIO_NUM_10 +#define TDECK_POWERON_DELAY 2000 // milliseconds - see bootstrap.c for explanation + +// Display +#define TDECK_LCD_SPI_HOST SPI2_HOST +#define TDECK_LCD_PIN_CS GPIO_NUM_12 +#define TDECK_LCD_PIN_DC GPIO_NUM_11 // RS +#define TDECK_LCD_PIN_BACKLIGHT GPIO_NUM_42 +#define TDECK_LCD_SPI_FREQUENCY 40000000 +#define TDECK_LCD_HORIZONTAL_RESOLUTION 320 +#define TDECK_LCD_VERTICAL_RESOLUTION 240 +#define TDECK_LCD_BITS_PER_PIXEL 16 +#define TDECK_LCD_DRAW_BUFFER_HEIGHT (TDECK_LCD_VERTICAL_RESOLUTION / 10) +#define TDECK_LCD_SPI_TRANSFER_HEIGHT (TDECK_LCD_VERTICAL_RESOLUTION / 10) + +// LVGL +// The minimum task stack seems to be about 3500, but that crashes the wifi app in some scenarios +#define TDECK_LVGL_TASK_STACK_DEPTH 4000 + +// Dipslay backlight (PWM) +#define TDECK_LCD_BACKLIGHT_LEDC_TIMER LEDC_TIMER_0 +#define TDECK_LCD_BACKLIGHT_LEDC_MODE LEDC_LOW_SPEED_MODE +#define TDECK_LCD_BACKLIGHT_LEDC_OUTPUT_IO TDECK_LCD_PIN_BACKLIGHT +#define TDECK_LCD_BACKLIGHT_LEDC_CHANNEL LEDC_CHANNEL_0 +#define TDECK_LCD_BACKLIGHT_LEDC_DUTY_RES LEDC_TIMER_8_BIT +#define TDECK_LCD_BACKLIGHT_LEDC_DUTY (191) +#define TDECK_LCD_BACKLIGHT_LEDC_FREQUENCY (1000) + +// Touch (GT911) +#define TDECK_TOUCH_I2C_BUS_HANDLE TDECK_I2C_BUS_HANDLE +#define TDECK_TOUCH_X_MAX 240 +#define TDECK_TOUCH_Y_MAX 320 +#define TDECK_TOUCH_PIN_INT GPIO_NUM_16 + +// SD Card +#define TDECK_SDCARD_SPI_HOST SPI2_HOST +#define TDECK_SDCARD_PIN_CS GPIO_NUM_39 +#define TDECK_SDCARD_SPI_FREQUENCY 800000U +#define TDECK_SDCARD_FORMAT_ON_MOUNT_FAILED false +#define TDECK_SDCARD_MAX_OPEN_FILES 4 +#define TDECK_SDCARD_ALLOC_UNIT_SIZE (16 * 1024) +#define TDECK_SDCARD_STATUS_CHECK_ENALBED false + +// Keyboard +#define TDECK_KEYBOARD_I2C_BUS_HANDLE TDECK_I2C_BUS_HANDLE +#define TDECK_KEYBOARD_SLAVE_ADDRESS 0x55 + +// Lora (optional) +#define TDECK_RADIO_PIN_CS GPIO_NUM_9 diff --git a/boards/lilygo_tdeck/display.c b/boards/lilygo_tdeck/display.c index 091ba550..4e1a5ba7 100644 --- a/boards/lilygo_tdeck/display.c +++ b/boards/lilygo_tdeck/display.c @@ -1,87 +1,44 @@ +#include "config.h" #include "driver/ledc.h" #include "driver/spi_master.h" #include "esp_err.h" #include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_vendor.h" -#include "esp_log.h" #include "esp_lvgl_port.h" +#include "log.h" #define TAG "tdeck_display" -#define LCD_SPI_HOST SPI2_HOST -#define LCD_PIN_SCLK GPIO_NUM_40 -#define LCD_PIN_MOSI GPIO_NUM_41 -#define LCD_PIN_MISO GPIO_NUM_38 -#define LCD_PIN_CS GPIO_NUM_12 -#define LCD_PIN_DC GPIO_NUM_11 // RS -#define LCD_PIN_BACKLIGHT GPIO_NUM_42 - -#define LCD_SPI_FREQUENCY 40000000 -#define LCD_HORIZONTAL_RESOLUTION 320 -#define LCD_VERTICAL_RESOLUTION 240 -#define LCD_BITS_PER_PIXEL 16 -#define LCD_DRAW_BUFFER_HEIGHT (LCD_VERTICAL_RESOLUTION / 10) -#define LCD_SPI_TRANSFER_HEIGHT (LCD_VERTICAL_RESOLUTION / 10) - -// Backlight PWM -#define LCD_BACKLIGHT_LEDC_TIMER LEDC_TIMER_0 -#define LCD_BACKLIGHT_LEDC_MODE LEDC_LOW_SPEED_MODE -#define LCD_BACKLIGHT_LEDC_OUTPUT_IO LCD_PIN_BACKLIGHT -#define LCD_BACKLIGHT_LEDC_CHANNEL LEDC_CHANNEL_0 -#define LCD_BACKLIGHT_LEDC_DUTY_RES LEDC_TIMER_8_BIT -#define LCD_BACKLIGHT_LEDC_DUTY (191) -#define LCD_BACKLIGHT_LEDC_FREQUENCY (1000) - void tdeck_enable_backlight() { - ESP_LOGI(TAG, "enable backlight"); - ledc_timer_config_t ledc_timer = { - .speed_mode = LCD_BACKLIGHT_LEDC_MODE, - .timer_num = LCD_BACKLIGHT_LEDC_TIMER, - .duty_resolution = LCD_BACKLIGHT_LEDC_DUTY_RES, - .freq_hz = LCD_BACKLIGHT_LEDC_FREQUENCY, - .clk_cfg = LEDC_AUTO_CLK + .speed_mode = TDECK_LCD_BACKLIGHT_LEDC_MODE, + .timer_num = TDECK_LCD_BACKLIGHT_LEDC_TIMER, + .duty_resolution = TDECK_LCD_BACKLIGHT_LEDC_DUTY_RES, + .freq_hz = TDECK_LCD_BACKLIGHT_LEDC_FREQUENCY, + .clk_cfg = LEDC_AUTO_CLK }; ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); ledc_channel_config_t ledc_channel = { - .speed_mode = LCD_BACKLIGHT_LEDC_MODE, - .channel = LCD_BACKLIGHT_LEDC_CHANNEL, - .timer_sel = LCD_BACKLIGHT_LEDC_TIMER, - .intr_type = LEDC_INTR_DISABLE, - .gpio_num = LCD_BACKLIGHT_LEDC_OUTPUT_IO, - .duty = 0, // Set duty to 0% - .hpoint = 0 + .speed_mode = TDECK_LCD_BACKLIGHT_LEDC_MODE, + .channel = TDECK_LCD_BACKLIGHT_LEDC_CHANNEL, + .timer_sel = TDECK_LCD_BACKLIGHT_LEDC_TIMER, + .intr_type = LEDC_INTR_DISABLE, + .gpio_num = TDECK_LCD_BACKLIGHT_LEDC_OUTPUT_IO, + .duty = 0, // Set duty to 0% + .hpoint = 0 }; ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); - ESP_ERROR_CHECK(ledc_set_duty(LCD_BACKLIGHT_LEDC_MODE, LCD_BACKLIGHT_LEDC_CHANNEL, LCD_BACKLIGHT_LEDC_DUTY)); + ESP_ERROR_CHECK(ledc_set_duty(TDECK_LCD_BACKLIGHT_LEDC_MODE, TDECK_LCD_BACKLIGHT_LEDC_CHANNEL, TDECK_LCD_BACKLIGHT_LEDC_DUTY)); } -lv_disp_t* tdeck_init_display() { - ESP_LOGI(TAG, "creating display"); - - int max_transfer_size = LCD_HORIZONTAL_RESOLUTION * LCD_SPI_TRANSFER_HEIGHT * (LCD_BITS_PER_PIXEL / 8); - - spi_bus_config_t bus_config = { - .sclk_io_num = LCD_PIN_SCLK, - .mosi_io_num = LCD_PIN_MOSI, - .miso_io_num = LCD_PIN_MISO, - .quadwp_io_num = -1, // Quad SPI LCD driver is not yet supported - .quadhd_io_num = -1, // Quad SPI LCD driver is not yet supported - .max_transfer_sz = max_transfer_size, - }; - - if (spi_bus_initialize(LCD_SPI_HOST, &bus_config, SPI_DMA_CH_AUTO) != ESP_OK) { - ESP_LOGD(TAG, "spi bus init failed"); - return false; - } - +lv_disp_t* tdeck_display_init() { const esp_lcd_panel_io_spi_config_t panel_io_config = { - .cs_gpio_num = LCD_PIN_CS, - .dc_gpio_num = LCD_PIN_DC, + .cs_gpio_num = TDECK_LCD_PIN_CS, + .dc_gpio_num = TDECK_LCD_PIN_DC, .spi_mode = 0, - .pclk_hz = LCD_SPI_FREQUENCY, + .pclk_hz = TDECK_LCD_SPI_FREQUENCY, .trans_queue_depth = 10, .on_color_trans_done = NULL, .user_ctx = NULL, @@ -98,17 +55,16 @@ lv_disp_t* tdeck_init_display() { }; esp_lcd_panel_io_handle_t io_handle; - if (esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_HOST, &panel_io_config, &io_handle) != ESP_OK) { - ESP_LOGD(TAG, "failed to create panel IO"); + if (esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TDECK_LCD_SPI_HOST, &panel_io_config, &io_handle) != ESP_OK) { + TT_LOG_E(TAG, "failed to create panel IO"); return false; } - ESP_LOGI(TAG, "install driver"); const esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = GPIO_NUM_NC, .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, .data_endian = LCD_RGB_DATA_ENDIAN_BIG, - .bits_per_pixel = LCD_BITS_PER_PIXEL, + .bits_per_pixel = TDECK_LCD_BITS_PER_PIXEL, .flags = { .reset_active_high = 0 }, @@ -117,47 +73,47 @@ lv_disp_t* tdeck_init_display() { esp_lcd_panel_handle_t panel_handle; if (esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle) != ESP_OK) { - ESP_LOGD(TAG, "failed to create panel"); + TT_LOG_E(TAG, "failed to create panel"); return false; } if (esp_lcd_panel_reset(panel_handle) != ESP_OK) { - ESP_LOGD(TAG, "failed to reset panel"); + TT_LOG_E(TAG, "failed to reset panel"); return false; } if (esp_lcd_panel_init(panel_handle) != ESP_OK) { - ESP_LOGD(TAG, "failed to init panel"); + TT_LOG_E(TAG, "failed to init panel"); return false; } if (esp_lcd_panel_invert_color(panel_handle, true) != ESP_OK) { - ESP_LOGD(TAG, "failed to init panel"); + TT_LOG_E(TAG, "failed to init panel"); return false; } if (esp_lcd_panel_swap_xy(panel_handle, true) != ESP_OK) { - ESP_LOGD(TAG, "failed to init panel"); + TT_LOG_E(TAG, "failed to init panel"); return false; } if (esp_lcd_panel_mirror(panel_handle, true, false) != ESP_OK) { - ESP_LOGD(TAG, "failed to init panel"); + TT_LOG_E(TAG, "failed to init panel"); return false; } if (esp_lcd_panel_disp_on_off(panel_handle, true) != ESP_OK) { - ESP_LOGD(TAG, "failed to turn display on"); + TT_LOG_E(TAG, "failed to turn display on"); return false; } const lvgl_port_display_cfg_t disp_cfg = { .io_handle = io_handle, .panel_handle = panel_handle, - .buffer_size = LCD_HORIZONTAL_RESOLUTION * LCD_DRAW_BUFFER_HEIGHT * (LCD_BITS_PER_PIXEL / 8), + .buffer_size = TDECK_LCD_HORIZONTAL_RESOLUTION * TDECK_LCD_DRAW_BUFFER_HEIGHT * (TDECK_LCD_BITS_PER_PIXEL / 8), .double_buffer = true, // Disable to free up SPIRAM - .hres = LCD_HORIZONTAL_RESOLUTION, - .vres = LCD_VERTICAL_RESOLUTION, + .hres = TDECK_LCD_HORIZONTAL_RESOLUTION, + .vres = TDECK_LCD_VERTICAL_RESOLUTION, .monochrome = false, .rotation = { .swap_xy = true, diff --git a/boards/lilygo_tdeck/keyboard.c b/boards/lilygo_tdeck/keyboard.c index fa049a9d..d9ad7a33 100644 --- a/boards/lilygo_tdeck/keyboard.c +++ b/boards/lilygo_tdeck/keyboard.c @@ -6,7 +6,6 @@ #include #define TAG "tdeck_keyboard" -#define KEYBOARD_SLAVE_ADDRESS 0x55 typedef struct { lv_indev_drv_t* driver; @@ -15,8 +14,8 @@ typedef struct { static inline esp_err_t keyboard_i2c_read(uint8_t* output) { return i2c_master_read_from_device( - TDECK_I2C_BUS_HANDLE, - KEYBOARD_SLAVE_ADDRESS, + TDECK_KEYBOARD_I2C_BUS_HANDLE, + TDECK_KEYBOARD_SLAVE_ADDRESS, output, 1, configTICK_RATE_HZ / 10 @@ -24,7 +23,7 @@ static inline esp_err_t keyboard_i2c_read(uint8_t* output) { } void keyboard_wait_for_response() { - TT_LOG_I(TAG, "wake await..."); + TT_LOG_I(TAG, "Waiting for keyboard response..."); bool awake = false; uint8_t read_buffer = 0x00; do { @@ -33,7 +32,7 @@ void keyboard_wait_for_response() { tt_delay_ms(100); } } while (!awake); - TT_LOG_I(TAG, "awake"); + TT_LOG_I(TAG, "Keyboard responded"); } /** @@ -55,11 +54,11 @@ static void keyboard_read_callback(TT_UNUSED struct _lv_indev_drv_t* indev_drv, if (keyboard_i2c_read(&read_buffer) == ESP_OK) { if (read_buffer == 0 && read_buffer != last_buffer) { - TT_LOG_I(TAG, "released %d", last_buffer); + TT_LOG_I(TAG, "Released %d", last_buffer); data->key = last_buffer; data->state = LV_INDEV_STATE_RELEASED; } else if (read_buffer != 0) { - TT_LOG_I(TAG, "pressed %d", read_buffer); + TT_LOG_I(TAG, "Pressed %d", read_buffer); data->key = read_buffer; data->state = LV_INDEV_STATE_PRESSED; } diff --git a/boards/lilygo_tdeck/lilygo_tdeck.c b/boards/lilygo_tdeck/lilygo_tdeck.c index ebc9ef10..5d61be3b 100644 --- a/boards/lilygo_tdeck/lilygo_tdeck.c +++ b/boards/lilygo_tdeck/lilygo_tdeck.c @@ -4,7 +4,10 @@ bool tdeck_bootstrap(); bool tdeck_init_lvgl(); +extern SdCard tdeck_sdcard; + const HardwareConfig lilygo_tdeck = { .bootstrap = &tdeck_bootstrap, - .init_lvgl = &tdeck_init_lvgl + .init_lvgl = &tdeck_init_lvgl, + .sdcard = &tdeck_sdcard }; diff --git a/boards/lilygo_tdeck/lvgl.c b/boards/lilygo_tdeck/lvgl.c index 5804b4ca..67e3d363 100644 --- a/boards/lilygo_tdeck/lvgl.c +++ b/boards/lilygo_tdeck/lvgl.c @@ -1,3 +1,4 @@ +#include "config.h" #include "esp_lvgl_port.h" #include "keyboard.h" #include "log.h" @@ -6,7 +7,7 @@ #define TAG "tdeck_lvgl" -lv_disp_t* tdeck_init_display(); +lv_disp_t* tdeck_display_init(); void tdeck_enable_backlight(); bool tdeck_init_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle); @@ -18,29 +19,33 @@ bool tdeck_init_lvgl() { // Init LVGL Port library const lvgl_port_cfg_t lvgl_cfg = { - .task_priority = ThreadPriorityHigh, - .task_stack = 8096, + .task_priority = THREAD_PRIORITY_RENDER, + .task_stack = TDECK_LVGL_TASK_STACK_DEPTH , .task_affinity = -1, // core pinning .task_max_sleep_ms = 500, .timer_period_ms = 5 }; + TT_LOG_D(TAG, "LVGL port init"); if (lvgl_port_init(&lvgl_cfg) != ESP_OK) { - TT_LOG_E(TAG, "lvgl port init failed"); + TT_LOG_E(TAG, "LVGL port init failed"); return false; } // Add display - display = tdeck_init_display(); + TT_LOG_D(TAG, "Creating display"); + display = tdeck_display_init(); if (display == NULL) { - TT_LOG_E(TAG, "failed to add display"); + TT_LOG_E(TAG, "Creating display failed"); return false; } // Add touch + TT_LOG_D(TAG, "Creating touch"); if (!tdeck_init_touch(&touch_io_handle, &touch_handle)) { + TT_LOG_E(TAG, "Creating touch failed"); return false; } @@ -49,9 +54,10 @@ bool tdeck_init_lvgl() { .handle = touch_handle, }; + TT_LOG_D(TAG, "Adding touch"); lv_indev_t _Nullable* touch_indev = lvgl_port_add_touch(&touch_cfg); if (touch_indev == NULL) { - TT_LOG_E(TAG, "failed to add touch to lvgl"); + TT_LOG_E(TAG, "Adding touch failed"); return false; } @@ -60,6 +66,7 @@ bool tdeck_init_lvgl() { keyboard_alloc(display); + TT_LOG_D(TAG, "enabling backlight"); tdeck_enable_backlight(); return true; diff --git a/boards/lilygo_tdeck/sdcard.c b/boards/lilygo_tdeck/sdcard.c new file mode 100644 index 00000000..47ba1a6f --- /dev/null +++ b/boards/lilygo_tdeck/sdcard.c @@ -0,0 +1,132 @@ +#include "sdcard.h" +#include "check.h" +#include "log.h" +#include "config.h" + +#include "esp_vfs_fat.h" +#include "sdmmc_cmd.h" + +#define TAG "tdeck_sdcard" + +typedef struct { + const char* mount_point; + sdmmc_card_t* card; +} MountData; + +/** + * Before we can initialize the sdcard's SPI communications, we have to set all + * other SPI pins on the board high. + * See https://github.com/espressif/esp-idf/issues/1597 + * See https://github.com/Xinyuan-LilyGO/T-Deck/blob/master/examples/UnitTest/UnitTest.ino + * @return success result + */ +static bool sdcard_init() { + TT_LOG_D(TAG, "init"); + + gpio_config_t config = { + .pin_bit_mask = BIT64(TDECK_SDCARD_PIN_CS) | BIT64(TDECK_RADIO_PIN_CS) | BIT64(TDECK_LCD_PIN_CS), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + + if (gpio_config(&config) != ESP_OK) { + TT_LOG_E(TAG, "GPIO init failed"); + return false; + } + + if (gpio_set_level(TDECK_SDCARD_PIN_CS, 1) != ESP_OK) { + TT_LOG_E(TAG, "Failed to set board CS pin high"); + return false; + } + + if (gpio_set_level(TDECK_RADIO_PIN_CS, 1) != ESP_OK) { + TT_LOG_E(TAG, "Failed to set radio CS pin high"); + return false; + } + + if (gpio_set_level(TDECK_LCD_PIN_CS, 1) != ESP_OK) { + TT_LOG_E(TAG, "Failed to set TFT CS pin high"); + return false; + } + + return true; +} + +static void* sdcard_mount(const char* mount_point) { + TT_LOG_I(TAG, "Mounting %s", mount_point); + + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = TDECK_SDCARD_FORMAT_ON_MOUNT_FAILED, + .max_files = TDECK_SDCARD_MAX_OPEN_FILES, + .allocation_unit_size = TDECK_SDCARD_ALLOC_UNIT_SIZE, + .disk_status_check_enable = TDECK_SDCARD_STATUS_CHECK_ENALBED + }; + + sdmmc_card_t* card; + + // Init without card detect (CD) and write protect (WD) + sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); + slot_config.gpio_cs = TDECK_SDCARD_PIN_CS; + slot_config.host_id = TDECK_SDCARD_SPI_HOST; + + sdmmc_host_t host = SDSPI_HOST_DEFAULT(); + // The following value is from T-Deck repo's UnitTest.ino project: + // https://github.com/Xinyuan-LilyGO/T-Deck/blob/master/examples/UnitTest/UnitTest.ino + // Observation: Using this automatically sets the bus to 20MHz + host.max_freq_khz = TDECK_SDCARD_SPI_FREQUENCY; + esp_err_t ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card); + + if (ret != ESP_OK) { + if (ret == ESP_FAIL) { + TT_LOG_E(TAG, "Mounting failed. Ensure the card is formatted with FAT."); + } else { + TT_LOG_E(TAG, "Mounting failed (%s)", esp_err_to_name(ret)); + } + return NULL; + } + + MountData* data = malloc(sizeof(MountData)); + *data = (MountData) { + .card = card, + .mount_point = mount_point + }; + + return data; +} + +static void* sdcard_init_and_mount(const char* mount_point) { + if (!sdcard_init()) { + TT_LOG_E(TAG, "Failed to set SPI CS pins high. This is a pre-requisite for mounting."); + return NULL; + } + + MountData* data = sdcard_mount(mount_point); + if (data == NULL) { + TT_LOG_E(TAG, "Mount failed for %s", mount_point); + return NULL; + } + + sdmmc_card_print_info(stdout, data->card); + + return data; +} + +static void sdcard_unmount(void* context) { + MountData* data = (MountData*)context; + TT_LOG_I(TAG, "Unmounting %s", data->mount_point); + + tt_assert(data != NULL); + if (esp_vfs_fat_sdcard_unmount(data->mount_point, data->card) != ESP_OK) { + TT_LOG_E(TAG, "Unmount failed for %s", data->mount_point); + } + + free(data); +} + +SdCard tdeck_sdcard = { + .mount = &sdcard_init_and_mount, + .unmount = &sdcard_unmount, + .mount_behaviour = SDCARD_MOUNT_BEHAVIOUR_AT_BOOT +}; diff --git a/boards/lilygo_tdeck/touch.c b/boards/lilygo_tdeck/touch.c index c20fae39..5106dcd0 100644 --- a/boards/lilygo_tdeck/touch.c +++ b/boards/lilygo_tdeck/touch.c @@ -1,27 +1,30 @@ #include "config.h" #include "esp_err.h" -#include "esp_lcd_touch_gt911.h" #include "esp_lcd_panel_io_interface.h" +#include "esp_lcd_touch_gt911.h" #include "log.h" #include #define TAG "tdeck_touch" bool tdeck_init_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle) { - TT_LOG_I(TAG, "creating touch"); - const esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); - if (esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TDECK_I2C_BUS_HANDLE, &touch_io_config, io_handle) != ESP_OK) { + if ( + esp_lcd_new_panel_io_i2c( + (esp_lcd_i2c_bus_handle_t)TDECK_TOUCH_I2C_BUS_HANDLE, + &touch_io_config, + io_handle + ) != ESP_OK + ) { TT_LOG_E(TAG, "touch io i2c creation failed"); return false; } - TT_LOG_I(TAG, "create_touch"); esp_lcd_touch_config_t config = { - .x_max = 240, - .y_max = 320, + .x_max = TDECK_TOUCH_X_MAX, + .y_max = TDECK_TOUCH_Y_MAX, .rst_gpio_num = GPIO_NUM_NC, - .int_gpio_num = GPIO_NUM_16, + .int_gpio_num = TDECK_TOUCH_PIN_INT, .levels = { .reset = 0, .interrupt = 0, diff --git a/docs/ideas.md b/docs/ideas.md index 1c8560d8..016240f4 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -1,5 +1,4 @@ # TODOs -- Make `desktop` app listen to changes in `app_manifest_registry` - Update `view_port` to use `ViewPort` as handle externally and `ViewPortData` internally - Replace FreeRTOS semaphore from `Loader` with internal `Mutex` - Create unit tests for `tactility-core` and `tactility` (PC-only for now) @@ -7,6 +6,7 @@ - Thread is broken: `tt_thread_join()` always hangs because `tt_thread_cleanup_tcb_event()` is not automatically called. This is normally done by a hook in `FreeRTOSConfig.h` but that seems to not work with ESP32. I should investigate task cleanup hooks further. +- Set DPI in sdkconfig for Waveshare display # Core Ideas - Make a HAL? It would mainly be there to support PC development. It's a lot of effort for supporting what's effectively a dev-only feature. diff --git a/tactility/src/hardware.c b/tactility/src/hardware.c index 5ef43855..15ff0931 100644 --- a/tactility/src/hardware.c +++ b/tactility/src/hardware.c @@ -1,16 +1,22 @@ #include "hardware_i.h" -#include "check.h" -#include "lvgl.h" - #define TAG "hardware" +typedef struct { + bool initialized; +} Hardware; + void tt_hardware_init(const HardwareConfig* config) { if (config->bootstrap != NULL) { TT_LOG_I(TAG, "Bootstrapping"); tt_check(config->bootstrap(), "bootstrap failed"); } + if (config->sdcard != NULL) { + TT_LOG_I(TAG, "Mounting sdcard"); + tt_sdcard_mount(config->sdcard); + } + tt_check(config->init_lvgl, "lvlg init not set"); tt_check(config->init_lvgl(), "lvgl init failed"); } diff --git a/tactility/src/hardware_config.h b/tactility/src/hardware_config.h index 44d7daab..5215dcfd 100644 --- a/tactility/src/hardware_config.h +++ b/tactility/src/hardware_config.h @@ -1,8 +1,8 @@ #pragma once #include "tactility_core.h" +#include "sdcard.h" -// Forward declarations typedef bool (*Bootstrap)(); typedef bool (*InitLvgl)(); @@ -10,5 +10,5 @@ typedef struct { // Optional bootstrapping method (e.g. to turn peripherals on) const Bootstrap _Nullable bootstrap; const InitLvgl init_lvgl; - + const SdCard* _Nullable sdcard; } HardwareConfig; diff --git a/tactility/src/sdcard.c b/tactility/src/sdcard.c new file mode 100644 index 00000000..71583406 --- /dev/null +++ b/tactility/src/sdcard.c @@ -0,0 +1,82 @@ +#include "sdcard.h" + +#include "mutex.h" +#include "tactility_core.h" + +#define TAG "sdcard" + +Mutex* mutex = NULL; + +typedef struct { + const SdCard* sdcard; + void* context; +} MountData; + +MountData data = { + .sdcard = NULL, + .context = NULL +}; + +static void sdcard_ensure_mutex() { + if (mutex == NULL) { + mutex = tt_mutex_alloc(MutexTypeRecursive); + } +} + +static bool sdcard_lock(uint32_t timeout_ticks) { + sdcard_ensure_mutex(); + return tt_mutex_acquire(mutex, timeout_ticks) == TtStatusOk; +} + +static void sdcard_unlock() { + tt_mutex_release(mutex); +} + +bool tt_sdcard_mount(const SdCard* sdcard) { + TT_LOG_I(TAG, "Mounting"); + + if (data.sdcard != NULL) { + TT_LOG_E(TAG, "Failed to mount: already mounted"); + return false; + } + + if (sdcard_lock(100)) { + void* context = sdcard->mount(TT_SDCARD_MOUNT_POINT); + data = (MountData) { + .context = context, + .sdcard = sdcard + }; + sdcard_unlock(); + return data.context != NULL; + } else { + TT_LOG_E(TAG, "Failed to lock"); + return false; + } +} + +bool tt_sdcard_is_mounted() { + return data.context != NULL; +} + +bool tt_sdcard_unmount(uint32_t timeout_ticks) { + TT_LOG_I(TAG, "Unmounting"); + bool result = false; + + if (sdcard_lock(timeout_ticks)) { + if (data.sdcard != NULL) { + data.sdcard->unmount(data.context); + data = (MountData) { + .context = NULL, + .sdcard = NULL + }; + result = true; + } else { + TT_LOG_E(TAG, "Can't unmount: nothing mounted"); + } + sdcard_unlock(); + } else { + TT_LOG_E(TAG, "Failed to lock in %lu ticks", timeout_ticks); + } + + return result; +} diff --git a/tactility/src/sdcard.h b/tactility/src/sdcard.h new file mode 100644 index 00000000..9a9de7f6 --- /dev/null +++ b/tactility/src/sdcard.h @@ -0,0 +1,31 @@ +#pragma once + +#include "tactility_core.h" + +#define TT_SDCARD_MOUNT_POINT "/sdcard" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* (*SdcardMount)(const char* mount_path); +typedef void (*SdcardUnmount)(void*); + +typedef enum { + SDCARD_MOUNT_BEHAVIOUR_AT_BOOT, /** Only mount at boot */ + SDCARD_MOUNT_BEHAVIOUR_ANYTIME /** Mount/dismount any time */ +} SdcardMountBehaviour; + +typedef struct { + SdcardMount mount; + SdcardUnmount unmount; + SdcardMountBehaviour mount_behaviour; +} SdCard; + +bool tt_sdcard_mount(const SdCard* sdcard); +bool tt_sdcard_is_mounted(); +bool tt_sdcard_unmount(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file