From 197f9163832b753764c5e1e161d8d58af4d28ba9 Mon Sep 17 00:00:00 2001 From: Slamy Date: Wed, 22 Jun 2022 21:10:21 +0200 Subject: [PATCH 1/3] fixed misaligned data transfer for ssd1306 (cherry picked from commit 46a2bf8ac9d309b80d8d6e7f3a0a8813593b6644) (cherry picked from commit b95304829af55174c0c8ac1b9c561e0319896d5b) --- components/esp_lcd/src/esp_lcd_panel_io_i2c.c | 13 +++++++++---- components/esp_lcd/src/esp_lcd_panel_ssd1306.c | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) 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 bb05192e72..17f5605fc5 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_i2c.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_i2c.c @@ -134,10 +134,15 @@ static esp_err_t panel_io_i2c_tx_buffer(esp_lcd_panel_io_t *io, int lcd_cmd, con 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)) { - ESP_GOTO_ON_ERROR(i2c_master_write(cmd_link, cmds + (sizeof(cmds) - cmds_size), cmds_size, true), err, TAG, "write LCD cmd failed"); + + // some displays don't want any additional commands on data transfers + if (lcd_cmd != -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)) { + 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) { diff --git a/components/esp_lcd/src/esp_lcd_panel_ssd1306.c b/components/esp_lcd/src/esp_lcd_panel_ssd1306.c index 9e56a1548b..323c1faac9 100644 --- a/components/esp_lcd/src/esp_lcd_panel_ssd1306.c +++ b/components/esp_lcd/src/esp_lcd_panel_ssd1306.c @@ -167,7 +167,7 @@ static esp_err_t panel_ssd1306_draw_bitmap(esp_lcd_panel_t *panel, int x_start, }, 2); // transfer frame buffer size_t len = (y_end - y_start) * (x_end - x_start) * ssd1306->bits_per_pixel / 8; - esp_lcd_panel_io_tx_color(io, 0, color_data, len); + esp_lcd_panel_io_tx_color(io, -1, color_data, len); return ESP_OK; } From 28954a635d876f0e3e1cc73cd566be94fc65f308 Mon Sep 17 00:00:00 2001 From: Vilem Zavodny Date: Mon, 18 Jul 2022 08:38:41 +0200 Subject: [PATCH 2/3] esp_lcd: Fix code style. (cherry picked from commit 18f46959e4c83d6dd01db53d26388d5d3b4e3d99) --- components/esp_lcd/src/esp_lcd_panel_io_i2c.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 17f5605fc5..49c35d3634 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_i2c.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_i2c.c @@ -125,6 +125,7 @@ static esp_err_t panel_io_i2c_tx_buffer(esp_lcd_panel_io_t *io, int lcd_cmd, con { 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"); @@ -136,7 +137,7 @@ static esp_err_t panel_io_i2c_tx_buffer(esp_lcd_panel_io_t *io, int lcd_cmd, con } // some displays don't want any additional commands on data transfers - if (lcd_cmd != -1) + 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; From d072873d5effe6ae3aaf753e078f0d4112594239 Mon Sep 17 00:00:00 2001 From: Vilem Zavodny Date: Wed, 13 Jul 2022 10:40:51 +0200 Subject: [PATCH 3/3] esp_lcd: Add RX into SPI lcd panel. --- components/esp_lcd/include/esp_lcd_panel_io.h | 2 +- components/esp_lcd/src/esp_lcd_panel_io_spi.c | 94 ++++++++++++++++--- 2 files changed, 84 insertions(+), 12 deletions(-) diff --git a/components/esp_lcd/include/esp_lcd_panel_io.h b/components/esp_lcd/include/esp_lcd_panel_io.h index 57f27ac96a..2f2c613f1a 100644 --- a/components/esp_lcd/include/esp_lcd_panel_io.h +++ b/components/esp_lcd/include/esp_lcd_panel_io.h @@ -47,7 +47,7 @@ esp_err_t esp_lcd_panel_io_rx_param(esp_lcd_panel_io_handle_t io, int lcd_cmd, v * 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 + * @param[in] lcd_cmd The specific LCD command (set to -1 if no command needed - only in SPI and I2C) * @param[in] param Buffer that holds the command specific parameters, set to NULL if no parameter is needed for the command * @param[in] param_size Size of `param` in memory, in bytes, set to zero if no parameter is needed for the command * @return diff --git a/components/esp_lcd/src/esp_lcd_panel_io_spi.c b/components/esp_lcd/src/esp_lcd_panel_io_spi.c index 71a2b6b86b..d6c640dde3 100644 --- a/components/esp_lcd/src/esp_lcd_panel_io_spi.c +++ b/components/esp_lcd/src/esp_lcd_panel_io_spi.c @@ -22,6 +22,7 @@ static const char *TAG = "lcd_panel.io.spi"; +static esp_err_t panel_io_spi_rx_param(esp_lcd_panel_io_t *io, int lcd_cmd, void *param, size_t param_size); static esp_err_t panel_io_spi_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, const void *param, size_t param_size); static esp_err_t panel_io_spi_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, const void *color, size_t color_size); static esp_err_t panel_io_spi_del(esp_lcd_panel_io_t *io); @@ -65,7 +66,6 @@ esp_err_t esp_lcd_new_panel_io_spi(esp_lcd_spi_bus_handle_t bus, const esp_lcd_p ESP_GOTO_ON_FALSE(spi_panel_io, ESP_ERR_NO_MEM, err, TAG, "no mem for spi panel io"); spi_device_interface_config_t devcfg = { - // currently the driver only supports TX path, so half duplex is enough .flags = SPI_DEVICE_HALFDUPLEX | (io_config->flags.lsb_first ? SPI_DEVICE_TXBIT_LSBFIRST : 0), .clock_speed_hz = io_config->pclk_hz, .mode = io_config->spi_mode, @@ -96,6 +96,7 @@ esp_err_t esp_lcd_new_panel_io_spi(esp_lcd_spi_bus_handle_t bus, const esp_lcd_p spi_panel_io->lcd_param_bits = io_config->lcd_param_bits; spi_panel_io->dc_gpio_num = io_config->dc_gpio_num; spi_panel_io->queue_size = io_config->trans_queue_depth; + spi_panel_io->base.rx_param = panel_io_spi_rx_param; spi_panel_io->base.tx_param = panel_io_spi_tx_param; spi_panel_io->base.tx_color = panel_io_spi_tx_color; spi_panel_io->base.del = panel_io_spi_del; @@ -170,36 +171,45 @@ static esp_err_t panel_io_spi_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons spi_transaction_t *spi_trans = NULL; lcd_spi_trans_descriptor_t *lcd_trans = NULL; esp_lcd_panel_io_spi_t *spi_panel_io = __containerof(io, esp_lcd_panel_io_spi_t, base); + bool send_cmd = (lcd_cmd >= 0); + + spi_device_acquire_bus(spi_panel_io->spi_dev, portMAX_DELAY); // before issue a polling transaction, need to wait queued transactions finished - for (size_t i = 0; i < spi_panel_io->num_trans_inflight; i++) { + size_t num_trans_inflight = spi_panel_io->num_trans_inflight; + for (size_t i = 0; i < num_trans_inflight; i++) { ret = spi_device_get_trans_result(spi_panel_io->spi_dev, &spi_trans, portMAX_DELAY); ESP_GOTO_ON_ERROR(ret, err, TAG, "recycle spi transactions failed"); + spi_panel_io->num_trans_inflight--; } - spi_panel_io->num_trans_inflight = 0; lcd_trans = &spi_panel_io->trans_pool[0]; memset(lcd_trans, 0, sizeof(lcd_spi_trans_descriptor_t)); spi_lcd_prepare_cmd_buffer(spi_panel_io, &lcd_cmd); lcd_trans->base.user = spi_panel_io; - lcd_trans->flags.dc_gpio_level = !spi_panel_io->flags.dc_data_level; // set D/C line to command mode - lcd_trans->base.length = spi_panel_io->lcd_cmd_bits; - lcd_trans->base.tx_buffer = &lcd_cmd; + lcd_trans->base.flags |= SPI_TRANS_CS_KEEP_ACTIVE; if (spi_panel_io->flags.octal_mode) { // use 8 lines for transmitting command, address and data lcd_trans->base.flags |= (SPI_TRANS_MULTILINE_CMD | SPI_TRANS_MULTILINE_ADDR | SPI_TRANS_MODE_OCT); } - if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary - lcd_trans->base.cmd = !spi_panel_io->flags.dc_data_level; + + if (send_cmd) { + lcd_trans->flags.dc_gpio_level = !spi_panel_io->flags.dc_data_level; // set D/C line to command mode + if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary + lcd_trans->base.cmd = !spi_panel_io->flags.dc_data_level; + } + lcd_trans->base.length = spi_panel_io->lcd_cmd_bits; + lcd_trans->base.tx_buffer = &lcd_cmd; + // command is short, using polling mode + ret = spi_device_polling_transmit(spi_panel_io->spi_dev, &lcd_trans->base); + ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (polling) command failed"); } - // command is short, using polling mode - ret = spi_device_polling_transmit(spi_panel_io->spi_dev, &lcd_trans->base); - ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (polling) command failed"); if (param && param_size) { spi_lcd_prepare_param_buffer(spi_panel_io, param, param_size); lcd_trans->flags.dc_gpio_level = spi_panel_io->flags.dc_data_level; // set D/C line to data mode lcd_trans->base.length = param_size * 8; // transaction length is in bits lcd_trans->base.tx_buffer = param; + lcd_trans->base.flags &= ~SPI_TRANS_CS_KEEP_ACTIVE; if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary lcd_trans->base.cmd = spi_panel_io->flags.dc_data_level; } @@ -209,6 +219,68 @@ static esp_err_t panel_io_spi_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons } err: + spi_device_release_bus(spi_panel_io->spi_dev); + + return ret; +} + +static esp_err_t panel_io_spi_rx_param(esp_lcd_panel_io_t *io, int lcd_cmd, void *param, size_t param_size) +{ + esp_err_t ret = ESP_OK; + spi_transaction_t *spi_trans = NULL; + lcd_spi_trans_descriptor_t *lcd_trans = NULL; + esp_lcd_panel_io_spi_t *spi_panel_io = __containerof(io, esp_lcd_panel_io_spi_t, base); + bool send_cmd = (lcd_cmd >= 0); + + spi_device_acquire_bus(spi_panel_io->spi_dev, portMAX_DELAY); + + // before issue a polling transaction, need to wait queued transactions finished + size_t num_trans_inflight = spi_panel_io->num_trans_inflight; + for (size_t i = 0; i < num_trans_inflight; i++) { + ret = spi_device_get_trans_result(spi_panel_io->spi_dev, &spi_trans, portMAX_DELAY); + ESP_GOTO_ON_ERROR(ret, err, TAG, "recycle spi transactions failed"); + spi_panel_io->num_trans_inflight--; + } + lcd_trans = &spi_panel_io->trans_pool[0]; + memset(lcd_trans, 0, sizeof(lcd_spi_trans_descriptor_t)); + spi_lcd_prepare_cmd_buffer(spi_panel_io, &lcd_cmd); + lcd_trans->base.user = spi_panel_io; + lcd_trans->base.flags |= SPI_TRANS_CS_KEEP_ACTIVE; + if (spi_panel_io->flags.octal_mode) { + // use 8 lines for transmitting command, address and data + lcd_trans->base.flags |= (SPI_TRANS_MULTILINE_CMD | SPI_TRANS_MULTILINE_ADDR | SPI_TRANS_MODE_OCT); + } + + if (send_cmd) { + lcd_trans->flags.dc_gpio_level = !spi_panel_io->flags.dc_data_level; // set D/C line to command mode + if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary + lcd_trans->base.cmd = !spi_panel_io->flags.dc_data_level; + } + lcd_trans->base.length = spi_panel_io->lcd_cmd_bits; + lcd_trans->base.tx_buffer = &lcd_cmd; + // command is short, using polling mode + ret = spi_device_polling_transmit(spi_panel_io->spi_dev, &lcd_trans->base); + ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (polling) command failed"); + } + + if (param && param_size) { + lcd_trans->flags.dc_gpio_level = spi_panel_io->flags.dc_data_level; // set D/C line to data mode + lcd_trans->base.length = 0; + lcd_trans->base.tx_buffer = NULL; + lcd_trans->base.rxlength = param_size * 8; // Read length in bits + lcd_trans->base.rx_buffer = param; + lcd_trans->base.flags &= ~SPI_TRANS_CS_KEEP_ACTIVE; + if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary + lcd_trans->base.cmd = spi_panel_io->flags.dc_data_level; + } + // parameter is usually short, using polling mode + ret = spi_device_polling_transmit(spi_panel_io->spi_dev, &lcd_trans->base); + ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (polling) param failed"); + } + +err: + spi_device_release_bus(spi_panel_io->spi_dev); + return ret; }