Merge branch 'feature/esp_lcd_spi_rx_v4.4' into 'release/v4.4'

Add support SPI rx into LCD component (backport v4.4)

See merge request espressif/esp-idf!19122
This commit is contained in:
Jiang Jiang Jian
2022-08-04 14:41:03 +08:00
4 changed files with 95 additions and 17 deletions

View File

@@ -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). * 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] 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 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 * @param[in] param_size Size of `param` in memory, in bytes, set to zero if no parameter is needed for the command
* @return * @return

View File

@@ -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; esp_err_t ret = ESP_OK;
lcd_panel_io_i2c_t *i2c_panel_io = __containerof(io, lcd_panel_io_i2c_t, base); 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); 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_FALSE(cmd_link, ESP_ERR_NO_MEM, err, TAG, "no mem for i2c cmd link");
@@ -134,11 +135,16 @@ 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), 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 err, TAG, "write control phase failed"); // control phase
} }
// 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)}; 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; size_t cmds_size = i2c_panel_io->lcd_cmd_bits / 8;
if (cmds_size > 0 && cmds_size <= sizeof(cmds)) { 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"); 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) { if (buffer) {
ESP_GOTO_ON_ERROR(i2c_master_write(cmd_link, buffer, buffer_size, true), err, TAG, "write data failed"); ESP_GOTO_ON_ERROR(i2c_master_write(cmd_link, buffer, buffer_size, true), err, TAG, "write data failed");

View File

@@ -22,6 +22,7 @@
static const char *TAG = "lcd_panel.io.spi"; 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_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_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); 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"); 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 = { 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), .flags = SPI_DEVICE_HALFDUPLEX | (io_config->flags.lsb_first ? SPI_DEVICE_TXBIT_LSBFIRST : 0),
.clock_speed_hz = io_config->pclk_hz, .clock_speed_hz = io_config->pclk_hz,
.mode = io_config->spi_mode, .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->lcd_param_bits = io_config->lcd_param_bits;
spi_panel_io->dc_gpio_num = io_config->dc_gpio_num; spi_panel_io->dc_gpio_num = io_config->dc_gpio_num;
spi_panel_io->queue_size = io_config->trans_queue_depth; 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_param = panel_io_spi_tx_param;
spi_panel_io->base.tx_color = panel_io_spi_tx_color; spi_panel_io->base.tx_color = panel_io_spi_tx_color;
spi_panel_io->base.del = panel_io_spi_del; 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; spi_transaction_t *spi_trans = NULL;
lcd_spi_trans_descriptor_t *lcd_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); 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 // 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); 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"); 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]; lcd_trans = &spi_panel_io->trans_pool[0];
memset(lcd_trans, 0, sizeof(lcd_spi_trans_descriptor_t)); memset(lcd_trans, 0, sizeof(lcd_spi_trans_descriptor_t));
spi_lcd_prepare_cmd_buffer(spi_panel_io, &lcd_cmd); spi_lcd_prepare_cmd_buffer(spi_panel_io, &lcd_cmd);
lcd_trans->base.user = spi_panel_io; 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.flags |= SPI_TRANS_CS_KEEP_ACTIVE;
lcd_trans->base.length = spi_panel_io->lcd_cmd_bits;
lcd_trans->base.tx_buffer = &lcd_cmd;
if (spi_panel_io->flags.octal_mode) { if (spi_panel_io->flags.octal_mode) {
// use 8 lines for transmitting command, address and data // 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); 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 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.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 // command is short, using polling mode
ret = spi_device_polling_transmit(spi_panel_io->spi_dev, &lcd_trans->base); 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"); ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (polling) command failed");
}
if (param && param_size) { if (param && param_size) {
spi_lcd_prepare_param_buffer(spi_panel_io, 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->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.length = param_size * 8; // transaction length is in bits
lcd_trans->base.tx_buffer = param; 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 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.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: 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; return ret;
} }

View File

@@ -167,7 +167,7 @@ static esp_err_t panel_ssd1306_draw_bitmap(esp_lcd_panel_t *panel, int x_start,
}, 2); }, 2);
// transfer frame buffer // transfer frame buffer
size_t len = (y_end - y_start) * (x_end - x_start) * ssd1306->bits_per_pixel / 8; 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; return ESP_OK;
} }