diff --git a/components/esp_lcd/.build-test-rules.yml b/components/esp_lcd/.build-test-rules.yml index eb513846ae..cda8b07ea9 100644 --- a/components/esp_lcd/.build-test-rules.yml +++ b/components/esp_lcd/.build-test-rules.yml @@ -8,6 +8,13 @@ components/esp_lcd/test_apps/i2c_lcd: temporary: true reason: insufficient runners +components/esp_lcd/test_apps/i2c_lcd_legacy: + disable: + - if: SOC_I2C_SUPPORTED != 1 + disable_test: + - if: IDF_TARGET not in ["esp32c3"] + temporary: true + reason: insufficient runners components/esp_lcd/test_apps/i80_lcd: disable: diff --git a/components/esp_lcd/CMakeLists.txt b/components/esp_lcd/CMakeLists.txt index e761342d15..733cfdd727 100644 --- a/components/esp_lcd/CMakeLists.txt +++ b/components/esp_lcd/CMakeLists.txt @@ -1,13 +1,14 @@ set(srcs "src/esp_lcd_common.c" "src/esp_lcd_panel_io.c" - "src/esp_lcd_panel_io_i2c.c" + "src/esp_lcd_panel_io_i2c_v1.c" + "src/esp_lcd_panel_io_i2c_v2.c" "src/esp_lcd_panel_io_spi.c" "src/esp_lcd_panel_nt35510.c" "src/esp_lcd_panel_ssd1306.c" "src/esp_lcd_panel_st7789.c" "src/esp_lcd_panel_ops.c") set(includes "include" "interface") -set(priv_requires "driver" "esp_mm" "esp_psram") +set(priv_requires "esp_mm" "esp_psram") if(CONFIG_SOC_I2S_LCD_I80_VARIANT) list(APPEND srcs "src/esp_lcd_panel_io_i2s.c") @@ -20,4 +21,5 @@ endif() idf_component_register(SRCS ${srcs} INCLUDE_DIRS ${includes} PRIV_REQUIRES ${priv_requires} + REQUIRES driver LDFRAGMENTS linker.lf) diff --git a/components/esp_lcd/include/esp_lcd_panel_io.h b/components/esp_lcd/include/esp_lcd_panel_io.h index de7b434b5f..a2dcd3912e 100644 --- a/components/esp_lcd/include/esp_lcd_panel_io.h +++ b/components/esp_lcd/include/esp_lcd_panel_io.h @@ -10,13 +10,15 @@ #include "esp_lcd_types.h" #include "soc/soc_caps.h" #include "hal/lcd_types.h" +#include "hal/i2c_types.h" +#include "driver/i2c_types.h" #ifdef __cplusplus extern "C" { #endif typedef void *esp_lcd_spi_bus_handle_t; /*!< Type of LCD SPI bus handle */ -typedef void *esp_lcd_i2c_bus_handle_t; /*!< Type of LCD I2C bus handle */ +typedef uint32_t esp_lcd_i2c_bus_handle_t; /*!< Type of LCD I2C bus handle */ typedef struct esp_lcd_i80_bus_t *esp_lcd_i80_bus_handle_t; /*!< Type of LCD intel 8080 bus handle */ /** @@ -171,10 +173,43 @@ typedef struct { unsigned int dc_low_on_data: 1; /*!< If this flag is enabled, DC line = 0 means transfer data, DC line = 1 means transfer command; vice versa */ unsigned int disable_control_phase: 1; /*!< If this flag is enabled, the control phase isn't used */ } flags; /*!< Extra flags to fine-tune the I2C device */ + uint32_t scl_speed_hz; /*!< I2C LCD SCL frequency (hz) */ } esp_lcd_panel_io_i2c_config_t; /** - * @brief Create LCD panel IO handle, for I2C interface + * @brief Create LCD panel IO handle, for I2C interface in legacy implementation + * + * @param[in] bus I2C bus handle, (in uint32_t) + * @param[in] io_config IO configuration, for I2C interface + * @param[out] ret_io Returned IO handle + * + * @note Please don't call this function in your project directly. Please call `esp_lcd_new_panel_to_i2c` instead. + * + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_NO_MEM if out of memory + * - ESP_OK on success + */ +esp_err_t esp_lcd_new_panel_io_i2c_v1(uint32_t bus, const esp_lcd_panel_io_i2c_config_t *io_config, esp_lcd_panel_io_handle_t *ret_io); + +/** + * @brief Create LCD panel IO handle, for I2C interface in new implementation + * + * @param[in] bus I2C bus handle, (in i2c_master_dev_handle_t) + * @param[in] io_config IO configuration, for I2C interface + * @param[out] ret_io Returned IO handle + * + * @note Please don't call this function in your project directly. Please call `esp_lcd_new_panel_to_i2c` instead. + * + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_NO_MEM if out of memory + * - ESP_OK on success + */ +esp_err_t esp_lcd_new_panel_io_i2c_v2(i2c_master_bus_handle_t bus, const esp_lcd_panel_io_i2c_config_t *io_config, esp_lcd_panel_io_handle_t *ret_io); + +/** + * @brief Create LCD panel IO handle * * @param[in] bus I2C bus handle * @param[in] io_config IO configuration, for I2C interface @@ -184,7 +219,9 @@ typedef struct { * - ESP_ERR_NO_MEM if out of memory * - ESP_OK on success */ -esp_err_t esp_lcd_new_panel_io_i2c(esp_lcd_i2c_bus_handle_t bus, const esp_lcd_panel_io_i2c_config_t *io_config, esp_lcd_panel_io_handle_t *ret_io); +#define esp_lcd_new_panel_io_i2c(bus, io_config, ret_io) _Generic((bus), \ + i2c_master_bus_handle_t : esp_lcd_new_panel_io_i2c_v2, \ + default : esp_lcd_new_panel_io_i2c_v1) (bus, io_config, ret_io) \ #if SOC_LCD_I80_SUPPORTED /** diff --git a/components/esp_lcd/src/esp_lcd_panel_io_i2c.c b/components/esp_lcd/src/esp_lcd_panel_io_i2c_v1.c similarity index 96% rename from components/esp_lcd/src/esp_lcd_panel_io_i2c.c rename to components/esp_lcd/src/esp_lcd_panel_io_i2c_v1.c index 5f04d27121..6ad986cee8 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_i2c.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_i2c_v1.c @@ -45,7 +45,7 @@ typedef struct { uint8_t cmdlink_buffer[]; // pre-alloc I2C command link buffer, to be reused in all transactions } lcd_panel_io_i2c_t; -esp_err_t esp_lcd_new_panel_io_i2c(esp_lcd_i2c_bus_handle_t bus, const esp_lcd_panel_io_i2c_config_t *io_config, esp_lcd_panel_io_handle_t *ret_io) +esp_err_t esp_lcd_new_panel_io_i2c_v1(uint32_t bus, const esp_lcd_panel_io_i2c_config_t *io_config, esp_lcd_panel_io_handle_t *ret_io) { #if CONFIG_LCD_ENABLE_DEBUG_LOG esp_log_level_set(TAG, ESP_LOG_DEBUG); @@ -54,10 +54,11 @@ esp_err_t esp_lcd_new_panel_io_i2c(esp_lcd_i2c_bus_handle_t bus, const esp_lcd_p lcd_panel_io_i2c_t *i2c_panel_io = NULL; ESP_GOTO_ON_FALSE(io_config && ret_io, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); ESP_GOTO_ON_FALSE(io_config->control_phase_bytes * 8 > io_config->dc_bit_offset, ESP_ERR_INVALID_ARG, err, TAG, "D/C bit exceeds control bytes"); + ESP_GOTO_ON_FALSE(io_config->scl_speed_hz == 0, ESP_ERR_INVALID_ARG, err, TAG, "scl_speed_hz is not need to set in legacy i2c_lcd driver"); i2c_panel_io = calloc(1, sizeof(lcd_panel_io_i2c_t) + CMD_HANDLER_BUFFER_SIZE); // expand zero-length array cmdlink_buffer ESP_GOTO_ON_FALSE(i2c_panel_io, ESP_ERR_NO_MEM, err, TAG, "no mem for i2c panel io"); - i2c_panel_io->i2c_bus_id = (uint32_t)bus; + i2c_panel_io->i2c_bus_id = bus; i2c_panel_io->lcd_cmd_bits = io_config->lcd_cmd_bits; i2c_panel_io->lcd_param_bits = io_config->lcd_param_bits; i2c_panel_io->on_color_trans_done = io_config->on_color_trans_done; @@ -84,7 +85,7 @@ static esp_err_t panel_io_i2c_del(esp_lcd_panel_io_t *io) esp_err_t ret = ESP_OK; lcd_panel_io_i2c_t *i2c_panel_io = __containerof(io, lcd_panel_io_i2c_t, base); - ESP_LOGD(TAG, "del lcd panel io spi @%p", i2c_panel_io); + ESP_LOGD(TAG, "del lcd panel i2c @%p", i2c_panel_io); free(i2c_panel_io); return ret; } diff --git a/components/esp_lcd/src/esp_lcd_panel_io_i2c_v2.c b/components/esp_lcd/src/esp_lcd_panel_io_i2c_v2.c new file mode 100644 index 0000000000..3bab7b8329 --- /dev/null +++ b/components/esp_lcd/src/esp_lcd_panel_io_i2c_v2.c @@ -0,0 +1,207 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "sdkconfig.h" +#if CONFIG_LCD_ENABLE_DEBUG_LOG +// The local log level must be defined before including esp_log.h +// Set the maximum log level for this source file +#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#endif +#include "esp_lcd_panel_io_interface.h" +#include "esp_lcd_panel_io.h" +#include "driver/i2c_master.h" +#include "driver/gpio.h" +#include "esp_log.h" +#include "esp_check.h" +#include "freertos/FreeRTOS.h" +#include "esp_heap_caps.h" +static const char *TAG = "lcd_panel.io.i2c"; + +#define BYTESHIFT(VAR, IDX) (((VAR) >> ((IDX) * 8)) & 0xFF) +#define CONTROL_PHASE_LENGTH (1) +#define CMD_LENGTH (4) + +static esp_err_t panel_io_i2c_del(esp_lcd_panel_io_t *io); +static esp_err_t panel_io_i2c_rx_param(esp_lcd_panel_io_t *io, int lcd_cmd, void *param, size_t param_size); +static esp_err_t panel_io_i2c_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, const void *param, size_t param_size); +static esp_err_t panel_io_i2c_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, const void *color, size_t color_size); +static esp_err_t panel_io_i2c_register_event_callbacks(esp_lcd_panel_io_handle_t io, const esp_lcd_panel_io_callbacks_t *cbs, void *user_ctx); + +typedef struct { + esp_lcd_panel_io_t base; // Base class of generic lcd panel io + i2c_master_dev_handle_t i2c_handle; // I2C master driver handle. + uint32_t dev_addr; // Device address + int lcd_cmd_bits; // Bit width of LCD command + int lcd_param_bits; // Bit width of LCD parameter + bool control_phase_enabled; // Is control phase enabled + uint32_t control_phase_cmd; // control byte when transferring command + uint32_t control_phase_data; // control byte when transferring data + esp_lcd_panel_io_color_trans_done_cb_t on_color_trans_done; // User register's callback, invoked when color data trans done + void *user_ctx; // User's private data, passed directly to callback on_color_trans_done() +} lcd_panel_io_i2c_t; + +esp_err_t esp_lcd_new_panel_io_i2c_v2(i2c_master_bus_handle_t bus, const esp_lcd_panel_io_i2c_config_t *io_config, esp_lcd_panel_io_handle_t *ret_io) +{ +#if CONFIG_LCD_ENABLE_DEBUG_LOG + esp_log_level_set(TAG, ESP_LOG_DEBUG); +#endif + esp_err_t ret = ESP_OK; + lcd_panel_io_i2c_t *i2c_panel_io = NULL; + i2c_master_dev_handle_t i2c_handle = NULL; + ESP_GOTO_ON_FALSE(io_config && ret_io, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + ESP_GOTO_ON_FALSE(io_config->control_phase_bytes * 8 > io_config->dc_bit_offset, ESP_ERR_INVALID_ARG, err, TAG, "D/C bit exceeds control bytes"); + i2c_panel_io = calloc(1, sizeof(lcd_panel_io_i2c_t)); + ESP_GOTO_ON_FALSE(i2c_panel_io, ESP_ERR_NO_MEM, err, TAG, "no mem for i2c panel io"); + + i2c_device_config_t i2c_lcd_cfg = { + .device_address = io_config->dev_addr, + .scl_speed_hz = io_config->scl_speed_hz, + }; + ESP_GOTO_ON_ERROR(i2c_master_bus_add_device(bus, &i2c_lcd_cfg, &i2c_handle), err, TAG, "i2c add device fail"); + + i2c_panel_io->i2c_handle = i2c_handle; + i2c_panel_io->lcd_cmd_bits = io_config->lcd_cmd_bits; + i2c_panel_io->lcd_param_bits = io_config->lcd_param_bits; + i2c_panel_io->on_color_trans_done = io_config->on_color_trans_done; + i2c_panel_io->user_ctx = io_config->user_ctx; + i2c_panel_io->control_phase_enabled = (!io_config->flags.disable_control_phase); + i2c_panel_io->control_phase_data = (!io_config->flags.dc_low_on_data) << (io_config->dc_bit_offset); + i2c_panel_io->control_phase_cmd = (io_config->flags.dc_low_on_data) << (io_config->dc_bit_offset); + i2c_panel_io->dev_addr = io_config->dev_addr; + i2c_panel_io->base.del = panel_io_i2c_del; + i2c_panel_io->base.rx_param = panel_io_i2c_rx_param; + i2c_panel_io->base.tx_param = panel_io_i2c_tx_param; + i2c_panel_io->base.tx_color = panel_io_i2c_tx_color; + i2c_panel_io->base.register_event_callbacks = panel_io_i2c_register_event_callbacks; + *ret_io = &(i2c_panel_io->base); + ESP_LOGD(TAG, "new i2c lcd panel io @%p", i2c_panel_io); + + return ESP_OK; +err: + if (i2c_panel_io) { + free(i2c_panel_io); + } + return ret; +} + +static esp_err_t panel_io_i2c_del(esp_lcd_panel_io_t *io) +{ + esp_err_t ret = ESP_OK; + lcd_panel_io_i2c_t *i2c_panel_io = __containerof(io, lcd_panel_io_i2c_t, base); + + ESP_LOGD(TAG, "del lcd panel io i2c @%p", i2c_panel_io); + ESP_ERROR_CHECK(i2c_master_bus_rm_device(i2c_panel_io->i2c_handle)); + free(i2c_panel_io); + return ret; +} + +static esp_err_t panel_io_i2c_register_event_callbacks(esp_lcd_panel_io_handle_t io, const esp_lcd_panel_io_callbacks_t *cbs, void *user_ctx) +{ + lcd_panel_io_i2c_t *i2c_panel_io = __containerof(io, lcd_panel_io_i2c_t, base); + + if(i2c_panel_io->on_color_trans_done != NULL) { + ESP_LOGW(TAG, "Callback on_color_trans_done was already set and now it was owerwritten!"); + } + + i2c_panel_io->on_color_trans_done = cbs->on_color_trans_done; + i2c_panel_io->user_ctx = user_ctx; + + return ESP_OK; +} + +static esp_err_t panel_io_i2c_rx_buffer(esp_lcd_panel_io_t *io, int lcd_cmd, void *buffer, size_t buffer_size) +{ + esp_err_t ret = ESP_OK; + lcd_panel_io_i2c_t *i2c_panel_io = __containerof(io, lcd_panel_io_i2c_t, base); + bool send_param = (lcd_cmd >= 0); + + int write_size = 0; + uint8_t write_buffer[CONTROL_PHASE_LENGTH + CMD_LENGTH] = {0}; + + if (send_param) { + if (i2c_panel_io->control_phase_enabled) { + write_buffer[0] = i2c_panel_io->control_phase_cmd; + write_size += 1; + + } + uint8_t cmds[4] = {BYTESHIFT(lcd_cmd, 3), BYTESHIFT(lcd_cmd, 2), BYTESHIFT(lcd_cmd, 1), BYTESHIFT(lcd_cmd, 0)}; + size_t cmds_size = i2c_panel_io->lcd_cmd_bits / 8; + if (cmds_size > 0 && cmds_size <= sizeof(cmds)) { + memcpy(write_buffer + write_size, cmds + (sizeof(cmds) - cmds_size), cmds_size); + write_size += cmds_size; + } + } + + ESP_GOTO_ON_ERROR(i2c_master_transmit_receive(i2c_panel_io->i2c_handle, write_buffer, write_size, buffer, buffer_size, -1), err, TAG, "i2c transaction failed"); + return ESP_OK; +err: + return ret; +} + +static esp_err_t panel_io_i2c_tx_buffer(esp_lcd_panel_io_t *io, int lcd_cmd, const void *buffer, size_t buffer_size, bool is_param) +{ + esp_err_t ret = ESP_OK; + lcd_panel_io_i2c_t *i2c_panel_io = __containerof(io, lcd_panel_io_i2c_t, base); + bool send_param = (lcd_cmd >= 0); + int write_size = 0; + uint8_t *write_buffer = (uint8_t*)heap_caps_malloc(CONTROL_PHASE_LENGTH + CMD_LENGTH + buffer_size, MALLOC_CAP_8BIT); + ESP_GOTO_ON_FALSE(write_buffer, ESP_ERR_NO_MEM, err, TAG, "no mem for write buffer"); + + if (i2c_panel_io->control_phase_enabled) { + write_buffer[0] = is_param ? i2c_panel_io->control_phase_cmd : i2c_panel_io->control_phase_data; + write_size += 1; + } + + // some displays don't want any additional commands on data transfers + if (send_param) + { + uint8_t cmds[4] = {BYTESHIFT(lcd_cmd, 3), BYTESHIFT(lcd_cmd, 2), BYTESHIFT(lcd_cmd, 1), BYTESHIFT(lcd_cmd, 0)}; + size_t cmds_size = i2c_panel_io->lcd_cmd_bits / 8; + if (cmds_size > 0 && cmds_size <= sizeof(cmds)) { + memcpy(write_buffer + write_size, cmds + (sizeof(cmds) - cmds_size), cmds_size); + write_size += cmds_size; + } + } + + if (buffer) { + memcpy(write_buffer + write_size, buffer, buffer_size); + write_size += buffer_size; + } + + ESP_GOTO_ON_ERROR(i2c_master_transmit(i2c_panel_io->i2c_handle, write_buffer, write_size, -1), err, TAG, "i2c transaction failed"); + free(write_buffer); + if (!is_param) { + // trans done callback + if (i2c_panel_io->on_color_trans_done) { + i2c_panel_io->on_color_trans_done(&(i2c_panel_io->base), NULL, i2c_panel_io->user_ctx); + } + } + + return ESP_OK; +err: + if (write_buffer) { + free(write_buffer); + } + return ret; +} + +static esp_err_t panel_io_i2c_rx_param(esp_lcd_panel_io_t *io, int lcd_cmd, void *param, size_t param_size) +{ + return panel_io_i2c_rx_buffer(io, lcd_cmd, param, param_size); +} + +static esp_err_t panel_io_i2c_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, const void *param, size_t param_size) +{ + return panel_io_i2c_tx_buffer(io, lcd_cmd, param, param_size, true); +} + +static esp_err_t panel_io_i2c_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, const void *color, size_t color_size) +{ + return panel_io_i2c_tx_buffer(io, lcd_cmd, color, color_size, false); +} diff --git a/components/esp_lcd/test_apps/i2c_lcd/main/test_i2c_lcd_panel.c b/components/esp_lcd/test_apps/i2c_lcd/main/test_i2c_lcd_panel.c index f0653a286a..19884dee57 100644 --- a/components/esp_lcd/test_apps/i2c_lcd/main/test_i2c_lcd_panel.c +++ b/components/esp_lcd/test_apps/i2c_lcd/main/test_i2c_lcd_panel.c @@ -1,12 +1,12 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include "unity.h" -#include "driver/i2c.h" +#include "driver/i2c_master.h" #include "driver/gpio.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_vendor.h" @@ -26,26 +26,27 @@ TEST_CASE("lcd_panel_with_i2c_interface_(ssd1306)", "[lcd]") } }; - i2c_config_t conf = { - .mode = I2C_MODE_MASTER, + i2c_master_bus_config_t i2c_bus_conf = { + .clk_source = I2C_CLK_SRC_DEFAULT, .sda_io_num = TEST_I2C_SDA_GPIO, .scl_io_num = TEST_I2C_SCL_GPIO, - .sda_pullup_en = GPIO_PULLUP_ENABLE, - .scl_pullup_en = GPIO_PULLUP_ENABLE, - .master.clk_speed = TEST_LCD_PIXEL_CLOCK_HZ, + .i2c_port = -1, }; - TEST_ESP_OK(i2c_param_config(TEST_I2C_HOST_ID, &conf)); - TEST_ESP_OK(i2c_driver_install(TEST_I2C_HOST_ID, I2C_MODE_MASTER, 0, 0, 0)); + + i2c_master_bus_handle_t bus_handle; + TEST_ESP_OK(i2c_new_master_bus(&i2c_bus_conf, &bus_handle)); esp_lcd_panel_io_handle_t io_handle = NULL; esp_lcd_panel_io_i2c_config_t io_config = { .dev_addr = TEST_I2C_DEV_ADDR, + .scl_speed_hz = TEST_LCD_PIXEL_CLOCK_HZ, .control_phase_bytes = 1, // According to SSD1306 datasheet .dc_bit_offset = 6, // According to SSD1306 datasheet .lcd_cmd_bits = 8, // According to SSD1306 datasheet .lcd_param_bits = 8, // According to SSD1306 datasheet }; - TEST_ESP_OK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TEST_I2C_HOST_ID, &io_config, &io_handle)); + + TEST_ESP_OK(esp_lcd_new_panel_io_i2c(bus_handle, &io_config, &io_handle)); esp_lcd_panel_handle_t panel_handle = NULL; esp_lcd_panel_dev_config_t panel_config = { @@ -66,5 +67,5 @@ TEST_CASE("lcd_panel_with_i2c_interface_(ssd1306)", "[lcd]") TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); - TEST_ESP_OK(i2c_driver_delete(TEST_I2C_HOST_ID)); + TEST_ESP_OK(i2c_del_master_bus(bus_handle)); } diff --git a/components/esp_lcd/test_apps/i2c_lcd_legacy/CMakeLists.txt b/components/esp_lcd/test_apps/i2c_lcd_legacy/CMakeLists.txt new file mode 100644 index 0000000000..9c62f764dc --- /dev/null +++ b/components/esp_lcd/test_apps/i2c_lcd_legacy/CMakeLists.txt @@ -0,0 +1,5 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(legacy_i2c_lcd_panel_test) diff --git a/components/esp_lcd/test_apps/i2c_lcd_legacy/README.md b/components/esp_lcd/test_apps/i2c_lcd_legacy/README.md new file mode 100644 index 0000000000..de65dcc35b --- /dev/null +++ b/components/esp_lcd/test_apps/i2c_lcd_legacy/README.md @@ -0,0 +1,4 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | + +This test app is used to test LCDs with I2C interface. diff --git a/components/esp_lcd/test_apps/i2c_lcd_legacy/main/CMakeLists.txt b/components/esp_lcd/test_apps/i2c_lcd_legacy/main/CMakeLists.txt new file mode 100644 index 0000000000..b969d9dbf9 --- /dev/null +++ b/components/esp_lcd/test_apps/i2c_lcd_legacy/main/CMakeLists.txt @@ -0,0 +1,7 @@ +set(srcs "test_app_main.c" + "test_i2c_lcd_legacy_panel.c") + +# In order for the cases defined by `TEST_CASE` to be linked into the final elf, +# the component can be registered as WHOLE_ARCHIVE +idf_component_register(SRCS ${srcs} + WHOLE_ARCHIVE) diff --git a/components/esp_lcd/test_apps/i2c_lcd_legacy/main/test_app_main.c b/components/esp_lcd/test_apps/i2c_lcd_legacy/main/test_app_main.c new file mode 100644 index 0000000000..826e43ddb5 --- /dev/null +++ b/components/esp_lcd/test_apps/i2c_lcd_legacy/main/test_app_main.c @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "esp_heap_caps.h" + +// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (-300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +static void check_leak(size_t before_free, size_t after_free, const char *type) +{ + ssize_t delta = after_free - before_free; + printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); + TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); +} + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT"); + check_leak(before_free_32bit, after_free_32bit, "32BIT"); +} + +void app_main(void) +{ + // ___ ____ ____ _ ____ ____ _____ _ + // |_ _|___ \ / ___| | | / ___| _ \ |_ _|__ ___| |_ + // | | __) | | | | | | | | | | | |/ _ \/ __| __| + // | | / __/| |___ | |__| |___| |_| | | | __/\__ \ |_ + // |___|_____|\____| |_____\____|____/ |_|\___||___/\__| + printf(" ___ ____ ____ _ ____ ____ _____ _\r\n"); + printf("|_ _|___ \\ / ___| | | / ___| _ \\ |_ _|__ ___| |_\r\n"); + printf(" | | __) | | | | | | | | | | | |/ _ \\/ __| __|\r\n"); + printf(" | | / __/| |___ | |__| |___| |_| | | | __/\\__ \\ |_\r\n"); + printf("|___|_____|\\____| |_____\\____|____/ |_|\\___||___/\\__|\r\n"); + unity_run_menu(); +} diff --git a/components/esp_lcd/test_apps/i2c_lcd_legacy/main/test_i2c_board.h b/components/esp_lcd/test_apps/i2c_lcd_legacy/main/test_i2c_board.h new file mode 100644 index 0000000000..f108031ca9 --- /dev/null +++ b/components/esp_lcd/test_apps/i2c_lcd_legacy/main/test_i2c_board.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define TEST_LCD_H_RES 128 +#define TEST_LCD_V_RES 64 + +#define TEST_I2C_SDA_GPIO 0 +#define TEST_I2C_SCL_GPIO 2 + +#define TEST_I2C_HOST_ID 0 + +#define TEST_I2C_DEV_ADDR 0x3C + +#define TEST_LCD_PIXEL_CLOCK_HZ (400 * 1000) + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_lcd/test_apps/i2c_lcd_legacy/main/test_i2c_lcd_legacy_panel.c b/components/esp_lcd/test_apps/i2c_lcd_legacy/main/test_i2c_lcd_legacy_panel.c new file mode 100644 index 0000000000..4a55b60b25 --- /dev/null +++ b/components/esp_lcd/test_apps/i2c_lcd_legacy/main/test_i2c_lcd_legacy_panel.c @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "unity.h" +#include "driver/i2c.h" +#include "driver/gpio.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_panel_ops.h" +#include "esp_system.h" +#include "test_i2c_board.h" + +TEST_CASE("lcd_panel_with_i2c_interface legacy_(ssd1306)", "[lcd]") +{ + const uint8_t pattern[][16] = {{ + 0x00, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x00, + 0x00, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x00 + }, + { + 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81, + 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 + } + }; + + i2c_config_t conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = TEST_I2C_SDA_GPIO, + .scl_io_num = TEST_I2C_SCL_GPIO, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = TEST_LCD_PIXEL_CLOCK_HZ, + }; + TEST_ESP_OK(i2c_param_config(TEST_I2C_HOST_ID, &conf)); + TEST_ESP_OK(i2c_driver_install(TEST_I2C_HOST_ID, I2C_MODE_MASTER, 0, 0, 0)); + + esp_lcd_panel_io_handle_t io_handle = NULL; + esp_lcd_panel_io_i2c_config_t io_config = { + .dev_addr = TEST_I2C_DEV_ADDR, + .control_phase_bytes = 1, // According to SSD1306 datasheet + .dc_bit_offset = 6, // According to SSD1306 datasheet + .lcd_cmd_bits = 8, // According to SSD1306 datasheet + .lcd_param_bits = 8, // According to SSD1306 datasheet + }; + TEST_ESP_OK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TEST_I2C_HOST_ID, &io_config, &io_handle)); + + esp_lcd_panel_handle_t panel_handle = NULL; + esp_lcd_panel_dev_config_t panel_config = { + .bits_per_pixel = 1, + .reset_gpio_num = -1, + }; + TEST_ESP_OK(esp_lcd_new_panel_ssd1306(io_handle, &panel_config, &panel_handle)); + TEST_ESP_OK(esp_lcd_panel_reset(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_init(panel_handle)); + // turn on display + TEST_ESP_OK(esp_lcd_panel_disp_on_off(panel_handle, true)); + + for (int i = 0; i < TEST_LCD_H_RES / 16; i++) { + for (int j = 0; j < TEST_LCD_V_RES / 8; j++) { + TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, i * 16, j * 8, i * 16 + 16, j * 8 + 8, pattern[i & 0x01])); + } + } + + TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); + TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); + TEST_ESP_OK(i2c_driver_delete(TEST_I2C_HOST_ID)); +} diff --git a/components/esp_lcd/test_apps/i2c_lcd_legacy/pytest_i2c_lcd_legacy.py b/components/esp_lcd/test_apps/i2c_lcd_legacy/pytest_i2c_lcd_legacy.py new file mode 100644 index 0000000000..a2e09316a6 --- /dev/null +++ b/components/esp_lcd/test_apps/i2c_lcd_legacy/pytest_i2c_lcd_legacy.py @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32c3 +@pytest.mark.i2c_oled +@pytest.mark.parametrize( + 'config', + [ + 'release', + ], + indirect=True, +) +def test_i2c_lcd_legacy(dut: Dut) -> None: + dut.run_all_single_board_cases() diff --git a/components/esp_lcd/test_apps/i2c_lcd_legacy/sdkconfig.ci.release b/components/esp_lcd/test_apps/i2c_lcd_legacy/sdkconfig.ci.release new file mode 100644 index 0000000000..3cff15d49e --- /dev/null +++ b/components/esp_lcd/test_apps/i2c_lcd_legacy/sdkconfig.ci.release @@ -0,0 +1,3 @@ +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/components/esp_lcd/test_apps/i2c_lcd_legacy/sdkconfig.defaults b/components/esp_lcd/test_apps/i2c_lcd_legacy/sdkconfig.defaults new file mode 100644 index 0000000000..ccc43c6fdf --- /dev/null +++ b/components/esp_lcd/test_apps/i2c_lcd_legacy/sdkconfig.defaults @@ -0,0 +1,5 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +# CONFIG_ESP_TASK_WDT_INIT is not set +CONFIG_FREERTOS_HZ=1000