From eefcb722e038ae6c794d5f37191d32d3418f1d4f Mon Sep 17 00:00:00 2001 From: Chen Chen Date: Tue, 12 May 2026 15:01:03 +0800 Subject: [PATCH] feat(lcd): add configurable timeout for lcd i2c panel Closes https://github.com/espressif/esp-idf/issues/18542 Closes https://github.com/espressif/esp-idf/pull/18543 --- components/esp_lcd/i2c/esp_lcd_panel_io_i2c.c | 12 ++++++++---- components/esp_lcd/include/esp_lcd_io_i2c.h | 1 + .../test_apps/i2c_lcd/main/test_i2c_lcd_panel.cpp | 3 ++- docs/en/api-reference/peripherals/lcd/i2c_lcd.rst | 1 + docs/zh_CN/api-reference/peripherals/lcd/i2c_lcd.rst | 1 + 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/components/esp_lcd/i2c/esp_lcd_panel_io_i2c.c b/components/esp_lcd/i2c/esp_lcd_panel_io_i2c.c index 68c4b461e05..b290602da1c 100644 --- a/components/esp_lcd/i2c/esp_lcd_panel_io_i2c.c +++ b/components/esp_lcd/i2c/esp_lcd_panel_io_i2c.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -46,6 +46,7 @@ typedef struct { 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() + int transaction_timeout_ms; // I2C xfer timeout passed to i2c_master_* (-1 = infinite) } lcd_panel_io_i2c_t; esp_err_t esp_lcd_new_panel_io_i2c(i2c_master_bus_handle_t bus, const esp_lcd_panel_io_i2c_config_t *io_config, esp_lcd_panel_io_handle_t *ret_io) @@ -58,6 +59,7 @@ esp_err_t esp_lcd_new_panel_io_i2c(i2c_master_bus_handle_t bus, const esp_lcd_pa 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"); + ESP_GOTO_ON_FALSE(io_config->transaction_timeout_ms >= -1, ESP_ERR_INVALID_ARG, err, TAG, "invalid transaction_timeout_ms"); // leak detection of i2c_panel_io because saving i2c_panel_io->base address ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-malloc-leak") i2c_panel_io = calloc(1, sizeof(lcd_panel_io_i2c_t)); @@ -78,6 +80,8 @@ esp_err_t esp_lcd_new_panel_io_i2c(i2c_master_bus_handle_t bus, const esp_lcd_pa 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; + /* transaction_timeout_ms == 0: omitted or zero-init, keep legacy infinite wait (same as -1). */ + i2c_panel_io->transaction_timeout_ms = (io_config->transaction_timeout_ms == 0) ? -1 : io_config->transaction_timeout_ms; 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; @@ -142,9 +146,9 @@ static esp_err_t panel_io_i2c_rx_buffer(esp_lcd_panel_io_t *io, int lcd_cmd, voi 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"); + ESP_GOTO_ON_ERROR(i2c_master_transmit_receive(i2c_panel_io->i2c_handle, write_buffer, write_size, buffer, buffer_size, i2c_panel_io->transaction_timeout_ms), err, TAG, "i2c transaction failed"); } else { - ESP_GOTO_ON_ERROR(i2c_master_receive(i2c_panel_io->i2c_handle, buffer, buffer_size, -1), err, TAG, "i2c transaction failed"); + ESP_GOTO_ON_ERROR(i2c_master_receive(i2c_panel_io->i2c_handle, buffer, buffer_size, i2c_panel_io->transaction_timeout_ms), err, TAG, "i2c transaction failed"); } return ESP_OK; @@ -190,7 +194,7 @@ static esp_err_t panel_io_i2c_tx_buffer(esp_lcd_panel_io_t *io, int lcd_cmd, con {.write_buffer = lcd_buffer, .buffer_size = lcd_buffer_size}, }; - ESP_GOTO_ON_ERROR(i2c_master_multi_buffer_transmit(i2c_panel_io->i2c_handle, lcd_i2c_buffer, sizeof(lcd_i2c_buffer) / sizeof(i2c_master_transmit_multi_buffer_info_t), -1), err, TAG, "i2c transaction failed"); + ESP_GOTO_ON_ERROR(i2c_master_multi_buffer_transmit(i2c_panel_io->i2c_handle, lcd_i2c_buffer, sizeof(lcd_i2c_buffer) / sizeof(i2c_master_transmit_multi_buffer_info_t), i2c_panel_io->transaction_timeout_ms), err, TAG, "i2c transaction failed"); if (!is_param) { // trans done callback if (i2c_panel_io->on_color_trans_done) { diff --git a/components/esp_lcd/include/esp_lcd_io_i2c.h b/components/esp_lcd/include/esp_lcd_io_i2c.h index 4a0c2bac0dc..cf3216a06ef 100644 --- a/components/esp_lcd/include/esp_lcd_io_i2c.h +++ b/components/esp_lcd/include/esp_lcd_io_i2c.h @@ -31,6 +31,7 @@ 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 */ + int transaction_timeout_ms; /*!< Timeout for each I2C transfer in ms, 0/-1: wait forever, >0: finite timeout */ } esp_lcd_panel_io_i2c_config_t; /** diff --git a/components/esp_lcd/test_apps/i2c_lcd/main/test_i2c_lcd_panel.cpp b/components/esp_lcd/test_apps/i2c_lcd/main/test_i2c_lcd_panel.cpp index fbe26049ac0..8de401b3c84 100644 --- a/components/esp_lcd/test_apps/i2c_lcd/main/test_i2c_lcd_panel.cpp +++ b/components/esp_lcd/test_apps/i2c_lcd/main/test_i2c_lcd_panel.cpp @@ -56,7 +56,8 @@ TEST_CASE("lcd_panel_with_i2c_interface_(ssd1306)", "[lcd]") .flags = { .dc_low_on_data = false, // According to SSD1306 datasheet, DC=0 means command, DC=1 means data .disable_control_phase = false, // Control phase is used - } + }, + .transaction_timeout_ms = 0, // 0 keeps the legacy infinite wait behavior }; TEST_ESP_OK(esp_lcd_new_panel_io_i2c(bus_handle, &io_config, &io_handle)); diff --git a/docs/en/api-reference/peripherals/lcd/i2c_lcd.rst b/docs/en/api-reference/peripherals/lcd/i2c_lcd.rst index 8e71d8e897f..4da3738f063 100644 --- a/docs/en/api-reference/peripherals/lcd/i2c_lcd.rst +++ b/docs/en/api-reference/peripherals/lcd/i2c_lcd.rst @@ -23,6 +23,7 @@ I2C Interfaced LCD - :cpp:member:`esp_lcd_panel_io_i2c_config_t::dev_addr` sets the I2C device address of the LCD controller chip. The LCD driver uses this address to communicate with the LCD controller chip. - :cpp:member:`esp_lcd_panel_io_i2c_config_t::scl_speed_hz` sets the I2C clock frequency in Hz. The value should not exceed the range recommended in the LCD spec. - :cpp:member:`esp_lcd_panel_io_i2c_config_t::lcd_cmd_bits` and :cpp:member:`esp_lcd_panel_io_i2c_config_t::lcd_param_bits` set the bit width of the command and parameter recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance. + - :cpp:member:`esp_lcd_panel_io_i2c_config_t::transaction_timeout_ms` sets the timeout (in milliseconds) for each underlying I2C transfer. Setting this to 0 or -1 means to wait indefinitely. If a positive value is specified, panel IO calls will return ``ESP_ERR_TIMEOUT`` when the timeout is reached. This is useful for cases like shared buses or when a slave device could potentially hang the bus. .. code-block:: c diff --git a/docs/zh_CN/api-reference/peripherals/lcd/i2c_lcd.rst b/docs/zh_CN/api-reference/peripherals/lcd/i2c_lcd.rst index d21ae7b0875..d2132018dcc 100644 --- a/docs/zh_CN/api-reference/peripherals/lcd/i2c_lcd.rst +++ b/docs/zh_CN/api-reference/peripherals/lcd/i2c_lcd.rst @@ -23,6 +23,7 @@ I2C 接口的 LCD - :cpp:member:`esp_lcd_panel_io_i2c_config_t::dev_addr` 设置 LCD 控制器芯片的 I2C 设备地址。LCD 驱动程序使用此地址与 LCD 控制器芯片通信。 - :cpp:member:`esp_lcd_panel_io_i2c_config_t::scl_speed_hz` 设置 I2C 时钟频率 (Hz)。该值不应超过 LCD 规格书中推荐的范围。 - :cpp:member:`esp_lcd_panel_io_i2c_config_t::lcd_cmd_bits` 和 :cpp:member:`esp_lcd_panel_io_i2c_config_t::lcd_param_bits` 分别设置 LCD 控制器芯片可识别的命令及参数的位宽。不同芯片对位宽要求不同,请提前参阅 LCD 规格书。 + - :cpp:member:`esp_lcd_panel_io_i2c_config_t::transaction_timeout_ms` 设置每次底层 I2C 传输的超时时间(毫秒)。设为 0 或 -1 时表示无限等待;如指定为正值,则面板 IO 相关调用会在超时后返回 ``ESP_ERR_TIMEOUT``,适用于共享总线或总线可能被从设备挂死的场景。 .. code-block:: c