diff --git a/components/esp_lcd/include/esp_lcd_panel_io.h b/components/esp_lcd/include/esp_lcd_panel_io.h index c35cb18cfb..7b99bde3f1 100644 --- a/components/esp_lcd/include/esp_lcd_panel_io.h +++ b/components/esp_lcd/include/esp_lcd_panel_io.h @@ -19,6 +19,25 @@ typedef void *esp_lcd_spi_bus_handle_t; /*!< Type of LCD S typedef void *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 */ +/** + * @brief Transmit LCD command and receive corresponding parameters + * + * @note Commands sent by this function are short, so they are sent using polling transactions. + * The function does not return before the command tranfer is completed. + * If any queued transactions sent by `esp_lcd_panel_io_tx_color()` are still pending when this function is called, + * this function will wait until they are finished and the queue is empty before sending the command(s). + * + * @param[in] io LCD panel IO handle, which is created by other factory API like `esp_lcd_new_panel_io_spi()` + * @param[in] lcd_cmd The specific LCD command, set to -1 if no command needed + * @param[out] param Buffer for the command data + * @param[in] param_size Size of `param` buffer + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_NOT_SUPPORTED if read is not supported by transport + * - ESP_OK on success + */ +esp_err_t esp_lcd_panel_io_rx_param(esp_lcd_panel_io_handle_t io, int lcd_cmd, void *param, size_t param_size); + /** * @brief Transmit LCD command and corresponding parameters * @@ -125,6 +144,7 @@ typedef struct { int lcd_param_bits; /*!< Bit-width of LCD parameter */ 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; } esp_lcd_panel_io_i2c_config_t; diff --git a/components/esp_lcd/interface/esp_lcd_panel_io_interface.h b/components/esp_lcd/interface/esp_lcd_panel_io_interface.h index 2faa6fab26..9f2226587e 100644 --- a/components/esp_lcd/interface/esp_lcd_panel_io_interface.h +++ b/components/esp_lcd/interface/esp_lcd_panel_io_interface.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,6 +18,22 @@ typedef struct esp_lcd_panel_io_t esp_lcd_panel_io_t; /*!< Type of LCD panel IO * @brief LCD panel IO interface */ struct esp_lcd_panel_io_t { + /** + * @brief Transmit LCD command and receive corresponding parameters + * + * @note This is the panel-specific interface called by function `esp_lcd_panel_io_rx_param()`. + * + * @param[in] io LCD panel IO handle, which is created by other factory API like `esp_lcd_new_panel_io_spi()` + * @param[in] lcd_cmd The specific LCD command, set to -1 if no command needed + * @param[out] param Buffer for the command data + * @param[in] param_size Size of `param` buffer + * @return + * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_NOT_SUPPORTED if read is not supported by transport + * - ESP_OK on success + */ + esp_err_t (*rx_param)(esp_lcd_panel_io_t *io, int lcd_cmd, void *param, size_t param_size); + /** * @brief Transmit LCD command and corresponding parameters * diff --git a/components/esp_lcd/src/esp_lcd_panel_io.c b/components/esp_lcd/src/esp_lcd_panel_io.c index 1c45c65a0a..5ff040ec77 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io.c +++ b/components/esp_lcd/src/esp_lcd_panel_io.c @@ -10,6 +10,13 @@ static const char *TAG = "lcd_panel.io"; +esp_err_t esp_lcd_panel_io_rx_param(esp_lcd_panel_io_handle_t io, int lcd_cmd, void *param, size_t param_size) +{ + ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_ARG, TAG, "invalid panel io handle"); + ESP_RETURN_ON_FALSE(io->rx_param, ESP_ERR_NOT_SUPPORTED, TAG, "rx_param is not supported yet"); + return io->rx_param(io, lcd_cmd, param, param_size); +} + esp_err_t esp_lcd_panel_io_tx_param(esp_lcd_panel_io_handle_t io, int lcd_cmd, const void *param, size_t param_size) { ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_ARG, TAG, "invalid panel io handle"); diff --git a/components/esp_lcd/src/esp_lcd_panel_io_i2c.c b/components/esp_lcd/src/esp_lcd_panel_io_i2c.c index 30900ed105..bb05192e72 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_i2c.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_i2c.c @@ -22,6 +22,7 @@ static const char *TAG = "lcd_panel.io.i2c"; #define BYTESHIFT(VAR, IDX) (((VAR) >> ((IDX) * 8)) & 0xFF) 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); @@ -31,6 +32,7 @@ typedef struct { 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 @@ -52,10 +54,12 @@ esp_err_t esp_lcd_new_panel_io_i2c(esp_lcd_i2c_bus_handle_t bus, const esp_lcd_p 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; *ret_io = &(i2c_panel_io->base); @@ -76,6 +80,47 @@ static esp_err_t panel_io_i2c_del(esp_lcd_panel_io_t *io) return ret; } +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); + + i2c_cmd_handle_t cmd_link = i2c_cmd_link_create_static(i2c_panel_io->cmdlink_buffer, CMD_HANDLER_BUFFER_SIZE); + ESP_GOTO_ON_FALSE(cmd_link, ESP_ERR_NO_MEM, err, TAG, "no mem for i2c cmd link"); + ESP_GOTO_ON_ERROR(i2c_master_start(cmd_link), err, TAG, "issue start failed"); // start phase + ESP_GOTO_ON_ERROR(i2c_master_write_byte(cmd_link, (i2c_panel_io->dev_addr << 1) | I2C_MASTER_WRITE, true), err, TAG, "write address failed"); // address phase + if (send_param) { + if (i2c_panel_io->control_phase_enabled) { + ESP_GOTO_ON_ERROR(i2c_master_write_byte(cmd_link, i2c_panel_io->control_phase_cmd, true), + err, TAG, "write control phase failed"); // control phase + } + 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)) { + ESP_GOTO_ON_ERROR(i2c_master_write(cmd_link, cmds + (sizeof(cmds) - cmds_size), cmds_size, true), err, TAG, "write LCD cmd failed"); + } + } + + if (buffer) { + ESP_GOTO_ON_ERROR(i2c_master_start(cmd_link), err, TAG, "issue start failed"); // start phase + ESP_GOTO_ON_ERROR(i2c_master_write_byte(cmd_link, (i2c_panel_io->dev_addr << 1) | I2C_MASTER_READ, true), err, TAG, "write address failed"); // address phase + ESP_GOTO_ON_ERROR(i2c_master_read(cmd_link, buffer, buffer_size, I2C_MASTER_LAST_NACK), err, TAG, "read data failed"); + } + + ESP_GOTO_ON_ERROR(i2c_master_stop(cmd_link), err, TAG, "issue stop failed"); // stop phase + ESP_GOTO_ON_ERROR(i2c_master_cmd_begin(i2c_panel_io->i2c_bus_id, cmd_link, portMAX_DELAY), err, TAG, "i2c transaction failed"); + + i2c_cmd_link_delete_static(cmd_link); + + return ESP_OK; +err: + if (cmd_link) { + i2c_cmd_link_delete_static(cmd_link); + } + 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; @@ -85,8 +130,10 @@ static esp_err_t panel_io_i2c_tx_buffer(esp_lcd_panel_io_t *io, int lcd_cmd, con ESP_GOTO_ON_FALSE(cmd_link, ESP_ERR_NO_MEM, err, TAG, "no mem for i2c cmd link"); ESP_GOTO_ON_ERROR(i2c_master_start(cmd_link), err, TAG, "issue start failed"); // start phase ESP_GOTO_ON_ERROR(i2c_master_write_byte(cmd_link, (i2c_panel_io->dev_addr << 1) | I2C_MASTER_WRITE, true), err, TAG, "write address failed"); // address phase - ESP_GOTO_ON_ERROR(i2c_master_write_byte(cmd_link, is_param ? i2c_panel_io->control_phase_cmd : i2c_panel_io->control_phase_data, true), - err, TAG, "write control phase failed"); // control phase + if (i2c_panel_io->control_phase_enabled) { + ESP_GOTO_ON_ERROR(i2c_master_write_byte(cmd_link, is_param ? i2c_panel_io->control_phase_cmd : i2c_panel_io->control_phase_data, true), + err, TAG, "write control phase failed"); // control phase + } 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)) { @@ -116,6 +163,11 @@ err: 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);