diff --git a/components/esp_driver_i2c/i2c_master.c b/components/esp_driver_i2c/i2c_master.c index 4ee7c2b409..afed2c9be4 100644 --- a/components/esp_driver_i2c/i2c_master.c +++ b/components/esp_driver_i2c/i2c_master.c @@ -193,14 +193,30 @@ static bool s_i2c_write_command(i2c_master_bus_handle_t i2c_master, i2c_operatio i2c_master->async_break = true; } } else { - i2c_master->cmd_idx++; - i2c_master->trans_idx++; - i2c_master->i2c_trans.cmd_count--; - if (i2c_master->async_trans == false) { - if (xPortInIsrContext()) { - xSemaphoreGiveFromISR(i2c_master->cmd_semphr, do_yield); + // Handle consecutive i2c write operations + i2c_operation_t next_transaction = i2c_master->i2c_trans.ops[i2c_master->trans_idx + 1]; + if (next_transaction.hw_cmd.op_code == I2C_LL_CMD_WRITE) { + portENTER_CRITICAL_SAFE(&handle->spinlock); + i2c_ll_master_write_cmd_reg(hal->dev, hw_end_cmd, i2c_master->cmd_idx + 1); + portEXIT_CRITICAL_SAFE(&handle->spinlock); + i2c_master->cmd_idx = 0; + i2c_master->trans_idx++; + i2c_master->i2c_trans.cmd_count--; + if (i2c_master->async_trans == false) { + i2c_hal_master_trans_start(hal); } else { - xSemaphoreGive(i2c_master->cmd_semphr); + i2c_master->async_break = true; + } + } else { + i2c_master->cmd_idx++; + i2c_master->trans_idx++; + i2c_master->i2c_trans.cmd_count--; + if (i2c_master->async_trans == false) { + if (xPortInIsrContext()) { + xSemaphoreGiveFromISR(i2c_master->cmd_semphr, do_yield); + } else { + xSemaphoreGive(i2c_master->cmd_semphr); + } } } } @@ -1057,23 +1073,45 @@ esp_err_t i2c_master_bus_reset(i2c_master_bus_handle_t bus_handle) return ESP_OK; } +esp_err_t i2c_master_multi_buffer_transmit(i2c_master_dev_handle_t i2c_dev, i2c_master_transmit_multi_buffer_info_t *buffer_info_array, size_t array_size, int xfer_timeout_ms) +{ + ESP_RETURN_ON_FALSE(i2c_dev != NULL, ESP_ERR_INVALID_ARG, TAG, "i2c handle not initialized"); + ESP_RETURN_ON_FALSE(array_size <= (SOC_I2C_CMD_REG_NUM - 2), ESP_ERR_INVALID_ARG, TAG, "i2c command list cannot contain so many commands"); + ESP_RETURN_ON_FALSE(buffer_info_array != NULL, ESP_ERR_INVALID_ARG, TAG, "buffer info array is empty"); + + size_t op_index = 0; + i2c_operation_t i2c_ops[SOC_I2C_CMD_REG_NUM] = {}; + i2c_ops[op_index++].hw_cmd.op_code = I2C_LL_CMD_RESTART; + for (int i = 0; i < array_size; i++) { + if (buffer_info_array[i].buffer_size == 0) { + continue; + } + i2c_ops[op_index].hw_cmd.ack_en = i2c_dev->ack_check_disable ? false : true; + i2c_ops[op_index].hw_cmd.op_code = I2C_LL_CMD_WRITE; + i2c_ops[op_index].data = (uint8_t*)buffer_info_array[i].write_buffer; + i2c_ops[op_index].total_bytes = buffer_info_array[i].buffer_size; + i2c_ops[op_index].bytes_used = 0; + op_index++; + } + + i2c_ops[op_index++].hw_cmd.op_code = I2C_LL_CMD_STOP; + if (i2c_dev->master_bus->async_trans == false) { + ESP_RETURN_ON_ERROR(s_i2c_synchronous_transaction(i2c_dev, i2c_ops, op_index, xfer_timeout_ms), TAG, "I2C transaction failed"); + } else { + ESP_RETURN_ON_ERROR(s_i2c_asynchronous_transaction(i2c_dev, i2c_ops, op_index, xfer_timeout_ms), TAG, "I2C transaction failed"); + } + return ESP_OK; +} + esp_err_t i2c_master_transmit(i2c_master_dev_handle_t i2c_dev, const uint8_t *write_buffer, size_t write_size, int xfer_timeout_ms) { ESP_RETURN_ON_FALSE(i2c_dev != NULL, ESP_ERR_INVALID_ARG, TAG, "i2c handle not initialized"); ESP_RETURN_ON_FALSE((write_buffer != NULL) && (write_size > 0), ESP_ERR_INVALID_ARG, TAG, "i2c transmit buffer or size invalid"); - i2c_operation_t i2c_ops[] = { - {.hw_cmd = I2C_TRANS_START_COMMAND}, - {.hw_cmd = I2C_TRANS_WRITE_COMMAND(i2c_dev->ack_check_disable ? false : true), .data = (uint8_t *)write_buffer, .total_bytes = write_size}, - {.hw_cmd = I2C_TRANS_STOP_COMMAND}, + i2c_master_transmit_multi_buffer_info_t buffer_info[1] = { + {.write_buffer = (uint8_t*)write_buffer, .buffer_size = write_size}, }; - - if (i2c_dev->master_bus->async_trans == false) { - ESP_RETURN_ON_ERROR(s_i2c_synchronous_transaction(i2c_dev, i2c_ops, DIM(i2c_ops), xfer_timeout_ms), TAG, "I2C transaction failed"); - } else { - ESP_RETURN_ON_ERROR(s_i2c_asynchronous_transaction(i2c_dev, i2c_ops, DIM(i2c_ops), xfer_timeout_ms), TAG, "I2C transaction failed"); - } - return ESP_OK; + return i2c_master_multi_buffer_transmit(i2c_dev, buffer_info, 1, xfer_timeout_ms); } esp_err_t i2c_master_transmit_receive(i2c_master_dev_handle_t i2c_dev, const uint8_t *write_buffer, size_t write_size, uint8_t *read_buffer, size_t read_size, int xfer_timeout_ms) diff --git a/components/esp_driver_i2c/include/driver/i2c_master.h b/components/esp_driver_i2c/include/driver/i2c_master.h index 795493f25d..a7b1a0d4c7 100644 --- a/components/esp_driver_i2c/include/driver/i2c_master.h +++ b/components/esp_driver_i2c/include/driver/i2c_master.h @@ -49,6 +49,14 @@ typedef struct { } flags; /*!< I2C device config flags */ } i2c_device_config_t; +/** + * @brief I2C master transmit buffer information structure + */ +typedef struct { + uint8_t *write_buffer; /*!< Pointer to buffer to be written. */ + size_t buffer_size; /*!< Size of data to be written. */ +} i2c_master_transmit_multi_buffer_info_t; + /** * @brief Group of I2C master callbacks, can be used to get status during transaction or doing other small things. But take care potential concurrency issues. * @note The callbacks are all running under ISR context @@ -123,6 +131,24 @@ esp_err_t i2c_master_bus_rm_device(i2c_master_dev_handle_t handle); */ esp_err_t i2c_master_transmit(i2c_master_dev_handle_t i2c_dev, const uint8_t *write_buffer, size_t write_size, int xfer_timeout_ms); +/** + * @brief Transmit multiple buffers of data over an I2C bus. + * + * This function transmits multiple buffers of data over an I2C bus using the specified I2C master device handle. + * It takes in an array of buffer information structures along with the size of the array and a transfer timeout value in milliseconds. + * + * @param i2c_dev I2C master device handle that created by `i2c_master_bus_add_device`. + * @param buffer_info_array Pointer to buffer information array. + * @param array_size size of buffer information array. + * @param xfer_timeout_ms Wait timeout, in ms. Note: -1 means wait forever. + * + * @return + * - ESP_OK: I2C master transmit success + * - ESP_ERR_INVALID_ARG: I2C master transmit parameter invalid. + * - ESP_ERR_TIMEOUT: Operation timeout(larger than xfer_timeout_ms) because the bus is busy or hardware crash. + */ +esp_err_t i2c_master_multi_buffer_transmit(i2c_master_dev_handle_t i2c_dev, i2c_master_transmit_multi_buffer_info_t *buffer_info_array, size_t array_size, int xfer_timeout_ms); + /** * @brief Perform a write-read transaction on the I2C bus. * The transaction will be undergoing until it finishes or it reaches diff --git a/components/esp_driver_i2c/test_apps/i2c_test_apps/main/test_i2c_multi.c b/components/esp_driver_i2c/test_apps/i2c_test_apps/main/test_i2c_multi.c index 652bd53793..e167ce62d6 100644 --- a/components/esp_driver_i2c/test_apps/i2c_test_apps/main/test_i2c_multi.c +++ b/components/esp_driver_i2c/test_apps/i2c_test_apps/main/test_i2c_multi.c @@ -140,7 +140,7 @@ TEST_CASE_MULTIPLE_DEVICES("I2C master write slave test", "[i2c][test_env=generi static void i2c_master_write_test_large_write_small_read(void) { - uint8_t data_wr[DATA_LENGTH] = { 0 }; + uint8_t data_wr[35] = { 0 }; // IDFCI-2151 Temporarily make up this test. int i; i2c_master_bus_config_t i2c_mst_config = { @@ -166,12 +166,12 @@ static void i2c_master_write_test_large_write_small_read(void) unity_wait_for_signal("i2c slave init finish"); unity_send_signal("master write"); - for (i = 0; i < DATA_LENGTH; i++) { + for (i = 0; i < 35; i++) { data_wr[i] = i; } disp_buf(data_wr, i); - TEST_ESP_OK(i2c_master_transmit(dev_handle, data_wr, DATA_LENGTH, -1)); + TEST_ESP_OK(i2c_master_transmit(dev_handle, data_wr, 35, -1)); unity_wait_for_signal("ready to delete"); TEST_ESP_OK(i2c_master_bus_rm_device(dev_handle)); @@ -613,6 +613,108 @@ static void slave_init_for_probe(void) TEST_CASE_MULTIPLE_DEVICES("I2C master probe slave test", "[i2c][test_env=generic_multi_device][timeout=150]", master_probe_slave, slave_init_for_probe); +static void i2c_master_write_multi_buffer_test(void) +{ + uint8_t data_wr[DATA_LENGTH]; + for (int i = 0; i < DATA_LENGTH; i++) { + data_wr[i] = i; + } + uint8_t data_wr2[DATA_LENGTH]; + for (int i = 0; i < DATA_LENGTH; i++) { + data_wr2[i] = i + 0x05; + } + uint8_t data_wr3[DATA_LENGTH]; + for (int i = 0; i < DATA_LENGTH; i++) { + data_wr3[i] = i + 0xf; + } + + i2c_master_transmit_multi_buffer_info_t buffer_info[3] = { + {.write_buffer = data_wr, .buffer_size = DATA_LENGTH}, + {.write_buffer = data_wr2, .buffer_size = DATA_LENGTH}, + {.write_buffer = data_wr3, .buffer_size = DATA_LENGTH}, + }; + + i2c_master_bus_config_t i2c_mst_config = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .i2c_port = TEST_I2C_PORT, + .scl_io_num = I2C_MASTER_SCL_IO, + .sda_io_num = I2C_MASTER_SDA_IO, + .flags.enable_internal_pullup = true, + }; + i2c_master_bus_handle_t bus_handle; + + TEST_ESP_OK(i2c_new_master_bus(&i2c_mst_config, &bus_handle)); + + i2c_device_config_t dev_cfg = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = 0x58, + .scl_speed_hz = 100000, + }; + + i2c_master_dev_handle_t dev_handle; + TEST_ESP_OK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle)); + + unity_wait_for_signal("i2c slave init finish"); + + unity_send_signal("master write"); + + disp_buf(data_wr, DATA_LENGTH); + disp_buf(data_wr2, DATA_LENGTH); + disp_buf(data_wr3, DATA_LENGTH); + TEST_ESP_OK(i2c_master_multi_buffer_transmit(dev_handle, buffer_info, sizeof(buffer_info) / sizeof(i2c_master_transmit_multi_buffer_info_t), -1)); + unity_wait_for_signal("ready to delete"); + TEST_ESP_OK(i2c_master_bus_rm_device(dev_handle)); + + TEST_ESP_OK(i2c_del_master_bus(bus_handle)); +} + +static void i2c_slave_read_multi_buffer_test(void) +{ + uint8_t data_rd[DATA_LENGTH * 3] = {0}; + + i2c_slave_config_t i2c_slv_config = { + .addr_bit_len = I2C_ADDR_BIT_LEN_7, + .clk_source = I2C_CLK_SRC_DEFAULT, + .i2c_port = TEST_I2C_PORT, + .send_buf_depth = 512, + .scl_io_num = I2C_SLAVE_SCL_IO, + .sda_io_num = I2C_SLAVE_SDA_IO, + .slave_addr = 0x58, + }; + + i2c_slave_dev_handle_t slave_handle; + TEST_ESP_OK(i2c_new_slave_device(&i2c_slv_config, &slave_handle)); + + s_receive_queue = xQueueCreate(1, sizeof(i2c_slave_rx_done_event_data_t)); + i2c_slave_event_callbacks_t cbs = { + .on_recv_done = test_i2c_rx_done_callback, + }; + ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(slave_handle, &cbs, s_receive_queue)); + + i2c_slave_rx_done_event_data_t rx_data; + TEST_ESP_OK(i2c_slave_receive(slave_handle, data_rd, DATA_LENGTH * 3)); + + unity_send_signal("i2c slave init finish"); + + unity_wait_for_signal("master write"); + xQueueReceive(s_receive_queue, &rx_data, pdMS_TO_TICKS(10000)); + disp_buf(data_rd, DATA_LENGTH * 3); + for (int i = 0; i < DATA_LENGTH; i++) { + TEST_ASSERT(data_rd[i] == i); + } + for (int i = DATA_LENGTH; i < DATA_LENGTH * 2; i++) { + TEST_ASSERT(data_rd[i] == ((i - DATA_LENGTH) + 0x05)); + } + for (int i = DATA_LENGTH * 2; i < DATA_LENGTH * 3; i++) { + TEST_ASSERT(data_rd[i] == ((i - DATA_LENGTH * 2) + 0x0f)); + } + vQueueDelete(s_receive_queue); + unity_send_signal("ready to delete"); + TEST_ESP_OK(i2c_del_slave_device(slave_handle)); +} + +TEST_CASE_MULTIPLE_DEVICES("I2C master write slave with multi buffer api test", "[i2c][test_env=generic_multi_device][timeout=150]", i2c_master_write_multi_buffer_test, i2c_slave_read_multi_buffer_test); + #if SOC_HP_I2C_NUM > 1 // Now chips with multiple I2C controllers are up to 2, can test more ports when we have more I2C controllers. static void i2c_master_write_test_more_port(void) diff --git a/components/esp_lcd/i2c/esp_lcd_panel_io_i2c_v2.c b/components/esp_lcd/i2c/esp_lcd_panel_io_i2c_v2.c index e8a50d8ee4..b423753b8d 100644 --- a/components/esp_lcd/i2c/esp_lcd_panel_io_i2c_v2.c +++ b/components/esp_lcd/i2c/esp_lcd_panel_io_i2c_v2.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -149,32 +149,40 @@ 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); - 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"); + uint8_t control_phase_byte = 0; + size_t control_phase_size = 0; 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; + control_phase_byte = is_param ? i2c_panel_io->control_phase_cmd : i2c_panel_io->control_phase_data; + control_phase_size = 1; } + uint8_t *cmd_buffer = NULL; + size_t cmd_buffer_size = 0; // 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; + cmd_buffer = cmds + (sizeof(cmds) - cmds_size); + cmd_buffer_size = cmds_size; } } + uint8_t *lcd_buffer = NULL; + size_t lcd_buffer_size = 0; if (buffer) { - memcpy(write_buffer + write_size, buffer, buffer_size); - write_size += buffer_size; + lcd_buffer = (uint8_t*)buffer; + lcd_buffer_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); + i2c_master_transmit_multi_buffer_info_t lcd_i2c_buffer[3] = { + {.write_buffer = &control_phase_byte, .buffer_size = control_phase_size}, + {.write_buffer = cmd_buffer, .buffer_size = cmd_buffer_size}, + {.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"); if (!is_param) { // trans done callback if (i2c_panel_io->on_color_trans_done) { @@ -184,9 +192,6 @@ static esp_err_t panel_io_i2c_tx_buffer(esp_lcd_panel_io_t *io, int lcd_cmd, con return ESP_OK; err: - if (write_buffer) { - free(write_buffer); - } return ret; } diff --git a/docs/en/api-reference/peripherals/i2c.rst b/docs/en/api-reference/peripherals/i2c.rst index 6de33a4ddc..aa194c7c2d 100644 --- a/docs/en/api-reference/peripherals/i2c.rst +++ b/docs/en/api-reference/peripherals/i2c.rst @@ -267,6 +267,42 @@ Simple example for writing data to slave: ESP_ERROR_CHECK(i2c_master_transmit(dev_handle, data_wr, DATA_LENGTH, -1)); + +I2C master write also supports transmit multi buffer in one transaction. Take following transaction as a simple example: + +.. code:: c + + uint8_t control_phase_byte = 0; + size_t control_phase_size = 0; + if (/*condition*/) { + control_phase_byte = 1; + control_phase_size = 1; + } + + uint8_t *cmd_buffer = NULL; + size_t cmd_buffer_size = 0; + if (/*condition*/) { + uint8_t cmds[4] = {BYTESHIFT(lcd_cmd, 3), BYTESHIFT(lcd_cmd, 2), BYTESHIFT(lcd_cmd, 1), BYTESHIFT(lcd_cmd, 0)}; + cmd_buffer = cmds; + cmd_buffer_size = 4; + } + + uint8_t *lcd_buffer = NULL; + size_t lcd_buffer_size = 0; + if (buffer) { + lcd_buffer = (uint8_t*)buffer; + lcd_buffer_size = buffer_size; + } + + i2c_master_transmit_multi_buffer_info_t lcd_i2c_buffer[3] = { + {.write_buffer = &control_phase_byte, .buffer_size = control_phase_size}, + {.write_buffer = cmd_buffer, .buffer_size = cmd_buffer_size}, + {.write_buffer = lcd_buffer, .buffer_size = lcd_buffer_size}, + }; + + i2c_master_multi_buffer_transmit(handle, lcd_i2c_buffer, sizeof(lcd_i2c_buffer) / sizeof(i2c_master_transmit_multi_buffer_info_t), -1); + + I2C Master Read ~~~~~~~~~~~~~~~