Unable to get valid IO Expander handle for ESP32-S3 and LCD using TCA9554 IO Expander

karunt
Posts: 93
Joined: Sat Apr 03, 2021 7:58 am

Unable to get valid IO Expander handle for ESP32-S3 and LCD using TCA9554 IO Expander

Postby karunt » Tue Oct 08, 2024 9:20 pm

Setup: Driving LCD (without touchscreen) with an Adafruit Qualia ESP32-S3 board which uses a TCA9554 I/O expander. LCD screen uses ST7701 panel driver. Attached schematics show connection between ESP32-S3, I/O expander and LCD screen.

Trying to run the rgb_avoid_tearing example from ESP-IOT-SOLUTION with this setup. For some reason, I'm unable to get a valid io_handle for the I/O expander.

Code as follows:

Code: Select all

#define CONFIG_EXAMPLE_LCD_RGB_BOUNCE_BUFFER_HEIGHT 10
#define ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_NEW 0x3F

#define EXAMPLE_LCD_H_RES               480
#define EXAMPLE_LCD_V_RES               480
#define EXAMPLE_LCD_BIT_PER_PIXEL       (16)
#define EXAMPLE_RGB_BIT_PER_PIXEL       (16)
#define EXAMPLE_RGB_DATA_WIDTH          (16)
#define EXAMPLE_RGB_BOUNCE_BUFFER_SIZE  (EXAMPLE_LCD_H_RES * CONFIG_EXAMPLE_LCD_RGB_BOUNCE_BUFFER_HEIGHT)
#define EXAMPLE_LCD_IO_RGB_DISP         (-1)             // -1 if not used

#define EXAMPLE_LCD_IO_RGB_VSYNC        42
#define EXAMPLE_LCD_IO_RGB_HSYNC        41
#define EXAMPLE_LCD_IO_RGB_DE           2
#define EXAMPLE_LCD_IO_RGB_PCLK         1
#define EXAMPLE_LCD_IO_RGB_DATA0        40
#define EXAMPLE_LCD_IO_RGB_DATA1        39
#define EXAMPLE_LCD_IO_RGB_DATA2        38
#define EXAMPLE_LCD_IO_RGB_DATA3        0
#define EXAMPLE_LCD_IO_RGB_DATA4        45
#define EXAMPLE_LCD_IO_RGB_DATA5        48
#define EXAMPLE_LCD_IO_RGB_DATA6        47
#define EXAMPLE_LCD_IO_RGB_DATA7        21
#define EXAMPLE_LCD_IO_RGB_DATA8        14
#define EXAMPLE_LCD_IO_RGB_DATA9        13
#define EXAMPLE_LCD_IO_RGB_DATA10       12
#define EXAMPLE_LCD_IO_RGB_DATA11       11
#define EXAMPLE_LCD_IO_RGB_DATA12       10
#define EXAMPLE_LCD_IO_RGB_DATA13       9
#define EXAMPLE_LCD_IO_RGB_DATA14       46
#define EXAMPLE_LCD_IO_RGB_DATA15       3

#define EXAMPLE_LCD_IO_SPI_CS           (IO_EXPANDER_PIN_NUM_5)
#define EXAMPLE_LCD_IO_SPI_SCL          (IO_EXPANDER_PIN_NUM_4)
#define EXAMPLE_LCD_IO_SPI_SDA          (IO_EXPANDER_PIN_NUM_12)
#define EXAMPLE_LCD_IO_RST              (IO_EXPANDER_PIN_NUM_6)
#define EXAMPLE_PIN_NUM_BK_LIGHT        (IO_EXPANDER_PIN_NUM_9)
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL   1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL  !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL

IRAM_ATTR static bool rgb_lcd_on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx)
{
    return lvgl_port_notify_rgb_vsync();
}

static const st7701_lcd_init_cmd_t lcd_init_cmds[] = {
//  {cmd, { data }, data_size, delay_ms}
    {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
    {0xC0, (uint8_t []){0x3B, 0x00}, 2, 0},
    {0xC1, (uint8_t []){0x0D, 0x02}, 2, 0},
    {0xC2, (uint8_t []){0x31, 0x05}, 2, 0},
    {0xCD, (uint8_t []){0x00}, 1, 0},
    {0xB0, (uint8_t []){0x00, 0x11, 0x18, 0x0E, 0x11, 0x06, 0x07, 0x08, 0x07, 0x22, 0x04, 0x12, 0x0F, 0xAA, 0x31, 0x18}, 16, 0},
    {0xB1, (uint8_t []){0x00, 0x11, 0x19, 0x0E, 0x12, 0x07, 0x08, 0x08, 0x08, 0x22, 0x04, 0x11, 0x11, 0xA9, 0x32, 0x18}, 16, 0},
    {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
    {0xB0, (uint8_t []){0x60}, 1, 0},
    {0xB1, (uint8_t []){0x32}, 1, 0},
    {0xB2, (uint8_t []){0x07}, 1, 0},
    {0xB3, (uint8_t []){0x80}, 1, 0},
    {0xB5, (uint8_t []){0x49}, 1, 0},
    {0xB7, (uint8_t []){0x85}, 1, 0},
    {0xB8, (uint8_t []){0x21}, 1, 0},
    {0xC1, (uint8_t []){0x78}, 1, 0},
    {0xC2, (uint8_t []){0x78}, 1, 0},
    {0xE0, (uint8_t []){0x00, 0x1B, 0x02}, 3, 0},
    {0xE1, (uint8_t []){0x08, 0xA0, 0x00, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x44, 0x44}, 11, 0},
    {0xE2, (uint8_t []){0x11, 0x11, 0x44, 0x44, 0xED, 0xA0, 0x00, 0x00, 0xEC, 0xA0, 0x00, 0x00}, 12, 0},
    {0xE3, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
    {0xE4, (uint8_t []){0x44, 0x44}, 2, 0},
    {0xE5, (uint8_t []){0x0A, 0xE9, 0xD8, 0xA0, 0x0C, 0xEB, 0xD8, 0xA0, 0x0E, 0xED, 0xD8, 0xA0, 0x10, 0xEF, 0xD8, 0xA0}, 16, 0},
    {0xE6, (uint8_t []){0x00, 0x00, 0x11, 0x11}, 4, 0},
    {0xE7, (uint8_t []){0x44, 0x44}, 2, 0},
    {0xE8, (uint8_t []){0x09, 0xE8, 0xD8, 0xA0, 0x0B, 0xEA, 0xD8, 0xA0, 0x0D, 0xEC, 0xD8, 0xA0, 0x0F, 0xEE, 0xD8, 0xA0}, 16, 0},
    {0xEB, (uint8_t []){0x02, 0x00, 0xE4, 0xE4, 0x88, 0x00, 0x40}, 7, 0},
    {0xEC, (uint8_t []){0x3C, 0x00}, 2, 0},
    {0xED, (uint8_t []){0xAB, 0x89, 0x76, 0x54, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x45, 0x67, 0x98, 0xBA}, 16, 0},
    {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
    {0xE5, (uint8_t []){0xE4}, 1, 0},
    {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
    {0x11, (uint8_t []){0x00}, 0, 120},
    {0x29, (uint8_t []){0x00}, 0, 0},
};

void display_start()
{
    //Define io_expander handle as suggested in https://github.com/espressif/esp-bsp/blob/master/components/io_expander/
    //esp_io_expander_tca9554/README.md
    esp_io_expander_handle_t io_expander_tft = NULL;
    esp_io_expander_new_i2c_tca9554(I2C_NUM_0, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_NEW, &io_expander_tft);
    
    //Set pin 0 and pin 1 with output dircetion and low level
    esp_io_expander_set_dir(io_expander_tft, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_OUTPUT);
    esp_io_expander_set_level(io_expander_tft, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 0);

    esp_io_expander_print_state(io_expander_tft);
    if (EXAMPLE_PIN_NUM_BK_LIGHT >= 0) {
        ESP_LOGI(TAG, "Turn off LCD backlight");
        gpio_config_t bk_gpio_config = {
            .mode = GPIO_MODE_OUTPUT,
            .pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
        };
        ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
    }

    ESP_LOGI(TAG, "Install 3-wire SPI panel IO");
    spi_line_config_t line_config = {
        .cs_io_type = IO_TYPE_EXPANDER,
        .cs_expander_pin = EXAMPLE_LCD_IO_SPI_CS,
        .scl_io_type = IO_TYPE_EXPANDER,
        .scl_gpio_num = EXAMPLE_LCD_IO_SPI_SCL,
        .sda_io_type = IO_TYPE_EXPANDER,
        .sda_gpio_num = EXAMPLE_LCD_IO_SPI_SDA,
        .io_expander = io_expander_tft,
    };
    esp_lcd_panel_io_3wire_spi_config_t io_config = ST7701_PANEL_IO_3WIRE_SPI_CONFIG(line_config, 0);
    esp_lcd_panel_io_handle_t io_handle = NULL;
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_3wire_spi(&io_config, &io_handle));

    ESP_LOGI(TAG, "Install ST7701 panel driver");
    esp_lcd_panel_handle_t lcd_handle = NULL;
    esp_lcd_rgb_panel_config_t rgb_config = {
        .clk_src = LCD_CLK_SRC_DEFAULT,
        .psram_trans_align = 64,
        .data_width = EXAMPLE_RGB_DATA_WIDTH,
        .bits_per_pixel = EXAMPLE_RGB_BIT_PER_PIXEL,
        .de_gpio_num = EXAMPLE_LCD_IO_RGB_DE,
        .pclk_gpio_num = EXAMPLE_LCD_IO_RGB_PCLK,
        .vsync_gpio_num = EXAMPLE_LCD_IO_RGB_VSYNC,
        .hsync_gpio_num = EXAMPLE_LCD_IO_RGB_HSYNC,
        .disp_gpio_num = EXAMPLE_LCD_IO_RGB_DISP,
        .data_gpio_nums = {
            EXAMPLE_LCD_IO_RGB_DATA0,
            EXAMPLE_LCD_IO_RGB_DATA1,
            EXAMPLE_LCD_IO_RGB_DATA2,
            EXAMPLE_LCD_IO_RGB_DATA3,
            EXAMPLE_LCD_IO_RGB_DATA4,
            EXAMPLE_LCD_IO_RGB_DATA5,
            EXAMPLE_LCD_IO_RGB_DATA6,
            EXAMPLE_LCD_IO_RGB_DATA7,
            EXAMPLE_LCD_IO_RGB_DATA8,
            EXAMPLE_LCD_IO_RGB_DATA9,
            EXAMPLE_LCD_IO_RGB_DATA10,
            EXAMPLE_LCD_IO_RGB_DATA11,
            EXAMPLE_LCD_IO_RGB_DATA12,
            EXAMPLE_LCD_IO_RGB_DATA13,
            EXAMPLE_LCD_IO_RGB_DATA14,
            EXAMPLE_LCD_IO_RGB_DATA15,
        },
        .timings = ST7701_480_480_PANEL_60HZ_RGB_TIMING(),
        .flags.fb_in_psram = 1,
        .num_fbs = LVGL_PORT_LCD_RGB_BUFFER_NUMS,
        .bounce_buffer_size_px = EXAMPLE_RGB_BOUNCE_BUFFER_SIZE,
    };
    //rgb_config.timings.h_res = EXAMPLE_LCD_H_RES;
    //rgb_config.timings.v_res = EXAMPLE_LCD_V_RES;
    st7701_vendor_config_t vendor_config = {
        .rgb_config = &rgb_config,
        .init_cmds = lcd_init_cmds,      // Uncomment these line if use custom initialization commands
        .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(lcd_init_cmds[0]),
        .flags = {
            .auto_del_panel_io = 0,         /**
                                             * Set to 1 if panel IO is no longer needed after LCD initialization.
                                             * If the panel IO pins are sharing other pins of the RGB interface to save GPIOs,
                                             * Please set it to 1 to release the pins.
                                             */
            .mirror_by_cmd = 1,             // Set to 0 if `auto_del_panel_io` is enabled
        },
    };
    const esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = EXAMPLE_LCD_IO_RST,
        .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
        .bits_per_pixel = EXAMPLE_LCD_BIT_PER_PIXEL,
        .vendor_config = &vendor_config,
    };
    ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &lcd_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_reset(lcd_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_init(lcd_handle));
    esp_lcd_panel_disp_on_off(lcd_handle, true);

    esp_lcd_touch_handle_t tp_handle = NULL;
    ESP_ERROR_CHECK(lvgl_port_init(lcd_handle, tp_handle));

    esp_lcd_rgb_panel_event_callbacks_t cbs = {
#if EXAMPLE_RGB_BOUNCE_BUFFER_SIZE > 0
        .on_bounce_frame_finish = rgb_lcd_on_vsync_event,
#else
        .on_vsync = rgb_lcd_on_vsync_event,
#endif
    };
    esp_lcd_rgb_panel_register_event_callbacks(lcd_handle, &cbs, NULL);

    if (EXAMPLE_PIN_NUM_BK_LIGHT >= 0) {
        ESP_LOGI(TAG, "Turn on LCD backlight");
        gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
    }

    ESP_LOGI(TAG, "Display LVGL demos");
    // Lock the mutex due to the LVGL APIs are not thread-safe
    if (lvgl_port_lock(-1)) {
        // lv_demo_stress();
        // lv_demo_benchmark();
        //lv_demo_music();
        // lv_demo_widgets();

        // Release the mutex
        lvgl_port_unlock();
    }
}
This is what my output looks like after successfully flashing the code to ESP32-S3. It goes into panic abort mode.

Code: Select all

W (313) spi_flash: Detected size(16384k) larger than the size in the binary image header(2048k). Using the size in the binary image header.  
W (327) i2c: This driver is an old driver, please migrate your application code to adapt `driver/i2c_master.h`
I (337) sleep: Configure to isolate all GPIO pins in sleep state
I (344) sleep: Enable automatic switching of GPIO sleep configuration
I (351) app_start: Starting scheduler on CPU0
I (356) app_start: Starting scheduler on CPU1
I (356) main_task: Started on CPU0
I (366) main_task: Calling app_main()
[b]E (406) i2c: i2c_master_cmd_begin(1478): i2c driver not installed
E (416) tca9554: write_direction_reg(134): Write direction reg failed
E (416) tca9554: reset(152): Write dir reg failed
E (426) tca9554: esp_io_expander_new_i2c_tca9554(82): Reset failed
E (436) io_expander: esp_io_expander_set_dir(35): Invalid handle
E (436) io_expander: esp_io_expander_set_level(63): Invalid handle
E (446) io_expander: esp_io_expander_print_state(131): Invalid handle
I (456) display_example: Turn off LCD backlight
E (456) gpio: GPIO_PIN mask error[/b]
ESP_ERROR_CHECK failed: esp_err_t 0x102 (ESP_ERR_INVALID_ARG) at 0x420087fb
0x420087fb: display_start at C:/Users/User1/example/receiver/main/display.c:127 (discriminator 1)

file: "./main/display.c" line 127
func: display_start
expression: gpio_config(&bk_gpio_config)

abort() was called at PC 0x4037a4e3 on core 0
0x4037a4e3: _esp_error_check_failed at C:/Users/User1/ESP/esp-idf/components/esp_system/esp_err.c:50



Backtrace: 0x403758ee:0x3fca2160 0x4037a4ed:0x3fca2180 0x40380952:0x3fca21a0 0x4037a4e3:0x3fca2210 0x420087fb:0x3fca2240 0x42008787:0x3fca23a0 0x42048def:0x3fca23c0 0x4037cb71:0x3fca23f0
0x403758ee: panic_abort at C:/Users/User1/ESP/esp-idf/components/esp_system/panic.c:452

0x4037a4ed: esp_system_abort at C:/Users/User1/ESP/esp-idf/components/esp_system/port/esp_system_chip.c:93

0x40380952: abort at C:/Users/User1/ESP/esp-idf/components/newlib/abort.c:38

0x4037a4e3: _esp_error_check_failed at C:/Users/User1/ESP/esp-idf/components/esp_system/esp_err.c:50

0x420087fb: display_start at C:/Users/User1/example/receiver/main/display.c:127 (discriminator 1)

0x42008787: app_main at C:/Users/User1/example/receiver/main/main.c:5

0x42048def: main_task at C:/Users/User1/ESP/esp-idf/components/freertos/app_startup.c:208 (discriminator 13)

0x4037cb71: vPortTaskWrapper at C:/Users/User1/ESP/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:162
Functions in the esp_io_expander managed component seem to not register a valid I/O expander handle. The error is being thrown by the function i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, TickType_t ticks_to_wait) in component driver/i2c.c. The following line of code:

Code: Select all

ESP_RETURN_ON_FALSE(p_i2c_obj[i2c_num] != NULL, ESP_ERR_INVALID_STATE, I2C_TAG, I2C_DRIVER_NOT_INSTALL_ERR_STR)
throws the i2c driver not installed error. Array p_i2c_obj has a length of 8.

I'm lost on how to address the issue and would appreciate some help.
Attachments
AdaFruit Qualia Board Connections to ESP32-S3.png
Qualia ESP32-S3 schematics showing IO Expander and LCD
AdaFruit Qualia Board Connections to ESP32-S3.png (580.87 KiB) Viewed 620 times

Who is online

Users browsing this forum: No registered users and 11 guests