diff --git a/components/esp_driver_i2c/i2c_master.c b/components/esp_driver_i2c/i2c_master.c index 91e44952d57a..258adc8e1b9f 100644 --- a/components/esp_driver_i2c/i2c_master.c +++ b/components/esp_driver_i2c/i2c_master.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,7 +149,7 @@ static bool s_i2c_write_command(i2c_master_bus_handle_t i2c_master, i2c_operatio i2c_master->trans_idx++; } portENTER_CRITICAL_SAFE(&handle->spinlock); - if (i2c_master->asnyc_trans == false) { + if (i2c_master->async_trans == false) { i2c_hal_master_trans_start(hal); } else { i2c_master->async_break = true; @@ -163,7 +163,7 @@ static bool s_i2c_write_command(i2c_master_bus_handle_t i2c_master, i2c_operatio 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; - if (i2c_master->asnyc_trans == false) { + if (i2c_master->async_trans == false) { i2c_hal_master_trans_start(hal); } else { i2c_master->async_break = true; @@ -172,7 +172,7 @@ static bool s_i2c_write_command(i2c_master_bus_handle_t i2c_master, i2c_operatio i2c_master->cmd_idx++; i2c_master->trans_idx++; i2c_master->i2c_trans.cmd_count--; - if (i2c_master->asnyc_trans == false) { + if (i2c_master->async_trans == false) { if (xPortInIsrContext()) { xSemaphoreGiveFromISR(i2c_master->cmd_semphr, do_yield); } else { @@ -230,7 +230,7 @@ static bool s_i2c_read_command(i2c_master_bus_handle_t i2c_master, i2c_operation } i2c_master->trans_idx++; i2c_master->i2c_trans.cmd_count--; - if (i2c_master->asnyc_trans == false) { + if (i2c_master->async_trans == false) { if (xPortInIsrContext()) { xSemaphoreGiveFromISR(i2c_master->cmd_semphr, do_yield); } else { @@ -242,7 +242,7 @@ static bool s_i2c_read_command(i2c_master_bus_handle_t i2c_master, i2c_operation portENTER_CRITICAL_SAFE(&handle->spinlock); i2c_ll_master_write_cmd_reg(hal->dev, hw_cmd, i2c_master->cmd_idx); i2c_ll_master_write_cmd_reg(hal->dev, hw_end_cmd, i2c_master->cmd_idx + 1); - if (i2c_master->asnyc_trans == false) { + if (i2c_master->async_trans == false) { i2c_hal_master_trans_start(hal); } else { i2c_master->async_break = true; @@ -256,7 +256,7 @@ static bool s_i2c_read_command(i2c_master_bus_handle_t i2c_master, i2c_operation portEXIT_CRITICAL_SAFE(&handle->spinlock); i2c_master->status = I2C_STATUS_READ; portENTER_CRITICAL_SAFE(&handle->spinlock); - if (i2c_master->asnyc_trans == false) { + if (i2c_master->async_trans == false) { i2c_hal_master_trans_start(hal); } else { i2c_master->async_break = true; @@ -361,7 +361,7 @@ static void s_i2c_start_end_command(i2c_master_bus_handle_t i2c_master, i2c_oper *address_fill += sizeof(addr_write); portEXIT_CRITICAL_SAFE(&i2c_master->base->spinlock); } - if (i2c_master->asnyc_trans == false) { + if (i2c_master->async_trans == false) { if (xPortInIsrContext()) { xSemaphoreGiveFromISR(i2c_master->cmd_semphr, do_yield); } else { @@ -438,7 +438,10 @@ static void s_i2c_send_command_async(i2c_master_bus_handle_t i2c_master, BaseTyp i2c_master->trans_finish = true; i2c_master->in_progress = false; if (i2c_master->queue_trans) { + xSemaphoreTakeFromISR(i2c_master->bus_lock_mux, do_yield); i2c_master->new_queue = true; + i2c_master->ops_cur_size--; + xSemaphoreGiveFromISR(i2c_master->bus_lock_mux, do_yield); xQueueSendFromISR(i2c_master->trans_queues[I2C_TRANS_QUEUE_COMPLETE], &i2c_master->i2c_trans, do_yield); } i2c_master->sent_all = true; @@ -497,7 +500,7 @@ static esp_err_t s_i2c_transaction_start(i2c_master_dev_handle_t i2c_dev, int xf i2c_ll_rxfifo_rst(hal->dev); i2c_ll_enable_intr_mask(hal->dev, I2C_LL_MASTER_EVENT_INTR); portEXIT_CRITICAL(&i2c_master->base->spinlock); - if (i2c_master->asnyc_trans == true) { + if (i2c_master->async_trans == true) { s_i2c_send_command_async(i2c_master, NULL); } else { s_i2c_send_commands(i2c_master, ticks_to_wait); @@ -579,7 +582,7 @@ static void IRAM_ATTR i2c_master_isr_handler_default(void *arg) i2c_isr_receive_handler(i2c_master); } - if (i2c_master->asnyc_trans) { + if (i2c_master->async_trans) { i2c_master_dev_handle_t i2c_dev = NULL; i2c_master_device_list_t *device_item; @@ -668,22 +671,7 @@ static esp_err_t i2c_master_bus_destroy(i2c_master_bus_handle_t bus_handle) if (i2c_master->queues_storage) { free(i2c_master->queues_storage); } - if (i2c_master->i2c_anyc_ops) { - for (int i = 0; i < i2c_master->index; i++) { - if (i2c_master->i2c_anyc_ops[i]) { - free(i2c_master->i2c_anyc_ops[i]); - } - } - free(i2c_master->i2c_anyc_ops); - } - if (i2c_master->anyc_write_buffer) { - for (int i = 0; i < i2c_master->index; i++) { - if (i2c_master->anyc_write_buffer[i]) { - free(i2c_master->anyc_write_buffer[i]); - } - } - free(i2c_master->anyc_write_buffer); - } + free(i2c_master->i2c_async_ops); for (int i = 0; i < I2C_TRANS_QUEUE_MAX; i++) { if (i2c_master->trans_queues[i]) { vQueueDelete(i2c_master->trans_queues[i]); @@ -702,50 +690,70 @@ static esp_err_t i2c_master_bus_destroy(i2c_master_bus_handle_t bus_handle) static esp_err_t s_i2c_asynchronous_transaction(i2c_master_dev_handle_t i2c_dev, i2c_operation_t *i2c_ops, size_t ops_dim, int timeout_ms) { - if (i2c_dev->master_bus->sent_all == true && i2c_dev->master_bus->num_trans_inqueue == 0) { - memcpy(i2c_dev->master_bus->i2c_ops, i2c_ops, sizeof(i2c_operation_t) * ops_dim); - i2c_dev->master_bus->addr_10bits_bus = i2c_dev->addr_10bits; - i2c_dev->master_bus->i2c_trans = (i2c_transaction_t) { + i2c_master_bus_t *i2c_master = i2c_dev->master_bus; + if (i2c_master->sent_all == true && i2c_master->num_trans_inqueue == 0) { + memcpy(i2c_master->i2c_ops, i2c_ops, sizeof(i2c_operation_t) * ops_dim); + i2c_master->addr_10bits_bus = i2c_dev->addr_10bits; + i2c_master->i2c_trans = (i2c_transaction_t) { .device_address = i2c_dev->device_address, - .ops = i2c_dev->master_bus->i2c_ops, + .ops = i2c_master->i2c_ops, .cmd_count = ops_dim, }; - i2c_dev->master_bus->sent_all = false; - i2c_dev->master_bus->trans_finish = false; - i2c_dev->master_bus->queue_trans = false; + i2c_master->sent_all = false; + i2c_master->trans_finish = false; + i2c_master->queue_trans = false; ESP_RETURN_ON_ERROR(s_i2c_transaction_start(i2c_dev, timeout_ms), TAG, "I2C transaction failed"); } else { - i2c_dev->master_bus->i2c_anyc_ops[i2c_dev->master_bus->index] = (i2c_operation_t(*))heap_caps_calloc(1, sizeof(i2c_operation_t) * 6, I2C_MEM_ALLOC_CAPS); - memcpy(i2c_dev->master_bus->i2c_anyc_ops[i2c_dev->master_bus->index], i2c_ops, sizeof(i2c_operation_t) * ops_dim); + xSemaphoreTake(i2c_master->bus_lock_mux, portMAX_DELAY); + // Check whether operation pool has extra space. + bool ops_pool = (i2c_master->ops_cur_size != i2c_master->queue_size); + i2c_operation_t *ops_current; + + if (ops_pool) { + i2c_master->ops_cur_size++; + memcpy(&i2c_master->i2c_async_ops[i2c_master->ops_prepare_idx], i2c_ops, sizeof(i2c_operation_t) * ops_dim); + // Clear unused memory + uint8_t unused_dim = I2C_STATIC_OPERATION_ARRAY_MAX - ops_dim; + if (unused_dim != 0) { + memset(&i2c_master->i2c_async_ops[i2c_master->ops_prepare_idx] + sizeof(i2c_operation_t) * ops_dim, 0, sizeof(i2c_operation_t) * unused_dim); + } + // Record current operation and feed to transaction queue. + ops_current = &i2c_master->i2c_async_ops[i2c_master->ops_prepare_idx][0]; + i2c_master->ops_prepare_idx = (i2c_master->ops_prepare_idx + 1) % i2c_master->queue_size; + } + + xSemaphoreGive(i2c_master->bus_lock_mux); + ESP_RETURN_ON_FALSE(ops_pool == true, ESP_ERR_INVALID_STATE, TAG, "ops list is full, please increase your trans_queue_depth"); + i2c_transaction_t i2c_queue_pre; - if (i2c_dev->master_bus->num_trans_inflight < i2c_dev->master_bus->queue_size) { - ESP_RETURN_ON_FALSE(xQueueReceive(i2c_dev->master_bus->trans_queues[I2C_TRANS_QUEUE_READY], &i2c_queue_pre, portMAX_DELAY) == pdTRUE, ESP_FAIL, TAG, "no transaction in the ready queue"); + if (i2c_master->num_trans_inflight < i2c_master->queue_size) { + ESP_RETURN_ON_FALSE(xQueueReceive(i2c_master->trans_queues[I2C_TRANS_QUEUE_READY], &i2c_queue_pre, portMAX_DELAY) == pdTRUE, ESP_FAIL, TAG, "no transaction in the ready queue"); } else { - ESP_RETURN_ON_FALSE(xQueueReceive(i2c_dev->master_bus->trans_queues[I2C_TRANS_QUEUE_COMPLETE], &i2c_queue_pre, portMAX_DELAY) == pdTRUE, ESP_FAIL, TAG, "recycle transaction from done queue failed"); - i2c_dev->master_bus->num_trans_inflight--; + ESP_RETURN_ON_FALSE(xQueueReceive(i2c_master->trans_queues[I2C_TRANS_QUEUE_COMPLETE], &i2c_queue_pre, portMAX_DELAY) == pdTRUE, ESP_FAIL, TAG, "recycle transaction from done queue failed"); + i2c_master->num_trans_inflight--; } i2c_queue_pre = (i2c_transaction_t) { .device_address = i2c_dev->device_address, - .ops = i2c_dev->master_bus->i2c_anyc_ops[i2c_dev->master_bus->index], + .ops = ops_current, .cmd_count = ops_dim, }; - i2c_dev->master_bus->index++; - if (xQueueSend(i2c_dev->master_bus->trans_queues[I2C_TRANS_QUEUE_PROGRESS], &i2c_queue_pre, portMAX_DELAY) == pdTRUE) { - i2c_dev->master_bus->num_trans_inflight++; - i2c_dev->master_bus->num_trans_inqueue++; - if (i2c_dev->master_bus->sent_all == true) { + if (xQueueSend(i2c_master->trans_queues[I2C_TRANS_QUEUE_PROGRESS], &i2c_queue_pre, portMAX_DELAY) == pdTRUE) { + i2c_master->num_trans_inflight++; + i2c_master->num_trans_inqueue++; + if (i2c_master->sent_all == true) { // Oh no, you cannot get the queue from ISR, so you get queue here. - ESP_RETURN_ON_FALSE(xQueueReceive(i2c_dev->master_bus->trans_queues[I2C_TRANS_QUEUE_PROGRESS], &i2c_queue_pre, portMAX_DELAY) == pdTRUE, ESP_FAIL, TAG, "get trans from progress queue failed"); - i2c_dev->master_bus->num_trans_inflight--; - i2c_dev->master_bus->num_trans_inqueue--; - i2c_dev->master_bus->sent_all = false; - i2c_dev->master_bus->trans_finish = false; - i2c_dev->master_bus->queue_trans = false; + ESP_RETURN_ON_FALSE(xQueueReceive(i2c_master->trans_queues[I2C_TRANS_QUEUE_PROGRESS], &i2c_queue_pre, portMAX_DELAY) == pdTRUE, ESP_FAIL, TAG, "get trans from progress queue failed"); + i2c_master->ops_cur_size--; + i2c_master->num_trans_inflight--; + i2c_master->num_trans_inqueue--; + i2c_master->sent_all = false; + i2c_master->trans_finish = false; + i2c_master->queue_trans = false; ESP_RETURN_ON_ERROR(s_i2c_transaction_start(i2c_dev, timeout_ms), TAG, "I2C transaction failed"); } } else { - ESP_RETURN_ON_FALSE(xQueueSend(i2c_dev->master_bus->trans_queues[I2C_TRANS_QUEUE_READY], &i2c_queue_pre, 0) == pdTRUE, ESP_ERR_INVALID_STATE, TAG, "ready queue full"); + ESP_RETURN_ON_FALSE(xQueueSend(i2c_master->trans_queues[I2C_TRANS_QUEUE_READY], &i2c_queue_pre, 0) == pdTRUE, ESP_ERR_INVALID_STATE, TAG, "ready queue full"); } } @@ -818,10 +826,9 @@ esp_err_t i2c_new_master_bus(const i2c_master_bus_config_t *bus_config, i2c_mast xSemaphoreTake(i2c_master->bus_lock_mux, portMAX_DELAY); SLIST_INIT(&i2c_master->device_list); xSemaphoreGive(i2c_master->bus_lock_mux); - // Initialize the queue if (bus_config->trans_queue_depth) { - i2c_master->asnyc_trans = true; + i2c_master->async_trans = true; i2c_master->sent_all = true; i2c_master->trans_finish = true; i2c_master->new_queue = true; @@ -843,8 +850,10 @@ esp_err_t i2c_new_master_bus(const i2c_master_bus_config_t *bus_config, i2c_mast ESP_ERR_INVALID_STATE, TAG, "ready queue full"); } - i2c_master->i2c_anyc_ops = (i2c_operation_t(**))heap_caps_calloc(bus_config->trans_queue_depth, sizeof(i2c_operation_t*), I2C_MEM_ALLOC_CAPS); - i2c_master->anyc_write_buffer = (uint8_t**)heap_caps_calloc(bus_config->trans_queue_depth, sizeof(uint8_t*), I2C_MEM_ALLOC_CAPS); + i2c_master->i2c_async_ops = (i2c_operation_t(*)[I2C_STATIC_OPERATION_ARRAY_MAX])heap_caps_calloc(bus_config->trans_queue_depth, sizeof(*i2c_master->i2c_async_ops), I2C_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(i2c_master->i2c_async_ops, ESP_ERR_NO_MEM, TAG, "no mem for operations"); + i2c_master->ops_prepare_idx = 0; + } int isr_flags = I2C_INTR_ALLOC_FLAG; if (bus_config->intr_priority) { @@ -949,21 +958,15 @@ esp_err_t i2c_master_transmit(i2c_master_dev_handle_t i2c_dev, const uint8_t *wr 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"); - if (i2c_dev->master_bus->asnyc_trans == false) { - i2c_operation_t i2c_ops[] = { - {.hw_cmd = I2C_TRANS_START_COMMAND}, - {.hw_cmd = I2C_TRANS_WRITE_COMMAND(false), .data = (uint8_t *)write_buffer, .total_bytes = write_size}, - {.hw_cmd = I2C_TRANS_STOP_COMMAND}, - }; + i2c_operation_t i2c_ops[] = { + {.hw_cmd = I2C_TRANS_START_COMMAND}, + {.hw_cmd = I2C_TRANS_WRITE_COMMAND(false), .data = (uint8_t *)write_buffer, .total_bytes = write_size}, + {.hw_cmd = I2C_TRANS_STOP_COMMAND}, + }; + + 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 { - i2c_dev->master_bus->anyc_write_buffer[i2c_dev->master_bus->index] = (uint8_t*)heap_caps_calloc(1, sizeof(uint8_t) * write_size, I2C_MEM_ALLOC_CAPS); - memcpy(i2c_dev->master_bus->anyc_write_buffer[i2c_dev->master_bus->index], write_buffer, write_size); - i2c_operation_t i2c_ops[] = { - {.hw_cmd = I2C_TRANS_START_COMMAND}, - {.hw_cmd = I2C_TRANS_WRITE_COMMAND(false), .data = (uint8_t *)i2c_dev->master_bus->anyc_write_buffer[i2c_dev->master_bus->index], .total_bytes = write_size}, - {.hw_cmd = I2C_TRANS_STOP_COMMAND}, - }; 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; @@ -975,28 +978,18 @@ esp_err_t i2c_master_transmit_receive(i2c_master_dev_handle_t i2c_dev, const uin ESP_RETURN_ON_FALSE((write_buffer != NULL) && (write_size > 0), ESP_ERR_INVALID_ARG, TAG, "i2c transmit buffer or size invalid"); ESP_RETURN_ON_FALSE((read_buffer != NULL) && (read_size > 0), ESP_ERR_INVALID_ARG, TAG, "i2c receive buffer or size invalid"); - if (i2c_dev->master_bus->asnyc_trans == false) { - i2c_operation_t i2c_ops[] = { - {.hw_cmd = I2C_TRANS_START_COMMAND}, - {.hw_cmd = I2C_TRANS_WRITE_COMMAND(false), .data = (uint8_t *)write_buffer, .total_bytes = write_size}, - {.hw_cmd = I2C_TRANS_START_COMMAND}, - {.hw_cmd = I2C_TRANS_READ_COMMAND(ACK_VAL), .data = read_buffer, .total_bytes = read_size - 1}, - {.hw_cmd = I2C_TRANS_READ_COMMAND(NACK_VAL), .data = (read_buffer + read_size - 1), .total_bytes = 1}, - {.hw_cmd = I2C_TRANS_STOP_COMMAND}, - }; + i2c_operation_t i2c_ops[] = { + {.hw_cmd = I2C_TRANS_START_COMMAND}, + {.hw_cmd = I2C_TRANS_WRITE_COMMAND(false), .data = (uint8_t *)write_buffer, .total_bytes = write_size}, + {.hw_cmd = I2C_TRANS_START_COMMAND}, + {.hw_cmd = I2C_TRANS_READ_COMMAND(ACK_VAL), .data = read_buffer, .total_bytes = read_size - 1}, + {.hw_cmd = I2C_TRANS_READ_COMMAND(NACK_VAL), .data = (read_buffer + read_size - 1), .total_bytes = 1}, + {.hw_cmd = I2C_TRANS_STOP_COMMAND}, + }; + + 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 { - i2c_dev->master_bus->anyc_write_buffer[i2c_dev->master_bus->index] = (uint8_t*)heap_caps_calloc(1, sizeof(uint8_t) * write_size, I2C_MEM_ALLOC_CAPS); - memcpy(i2c_dev->master_bus->anyc_write_buffer[i2c_dev->master_bus->index], write_buffer, write_size); - - i2c_operation_t i2c_ops[] = { - {.hw_cmd = I2C_TRANS_START_COMMAND}, - {.hw_cmd = I2C_TRANS_WRITE_COMMAND(false), .data = (uint8_t *)i2c_dev->master_bus->anyc_write_buffer[i2c_dev->master_bus->index], .total_bytes = write_size}, - {.hw_cmd = I2C_TRANS_START_COMMAND}, - {.hw_cmd = I2C_TRANS_READ_COMMAND(ACK_VAL), .data = read_buffer, .total_bytes = read_size - 1}, - {.hw_cmd = I2C_TRANS_READ_COMMAND(NACK_VAL), .data = (read_buffer + read_size - 1), .total_bytes = 1}, - {.hw_cmd = I2C_TRANS_STOP_COMMAND}, - }; 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; @@ -1014,7 +1007,7 @@ esp_err_t i2c_master_receive(i2c_master_dev_handle_t i2c_dev, uint8_t *read_buff {.hw_cmd = I2C_TRANS_STOP_COMMAND}, }; - if (i2c_dev->master_bus->asnyc_trans == false) { + 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"); @@ -1069,7 +1062,7 @@ esp_err_t i2c_master_register_event_callbacks(i2c_master_dev_handle_t i2c_dev, c { ESP_RETURN_ON_FALSE(i2c_dev != NULL, ESP_ERR_INVALID_ARG, TAG, "i2c handle not initialized"); - if (i2c_dev->master_bus->asnyc_trans == false) { + if (i2c_dev->master_bus->async_trans == false) { ESP_LOGE(TAG, "I2C transaction queue is not initialized, so you can't use callback here, please resister the bus again with trans_queue_depth != 0"); return ESP_ERR_INVALID_STATE; } diff --git a/components/esp_driver_i2c/i2c_private.h b/components/esp_driver_i2c/i2c_private.h index 546aae170a76..de7bcbe8a6e2 100644 --- a/components/esp_driver_i2c/i2c_private.h +++ b/components/esp_driver_i2c/i2c_private.h @@ -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 */ @@ -136,7 +136,7 @@ struct i2c_master_bus_t { uint32_t read_len_static; // Read static buffer length uint32_t w_r_size; // The size send/receive last time. bool trans_over_buffer; // Data length is more than hardware fifo length, needs interrupt. - bool asnyc_trans; // asynchronous transaction, true after callback is installed. + bool async_trans; // asynchronous transaction, true after callback is installed. volatile bool trans_done; // transaction command finish SLIST_HEAD(i2c_master_device_list_head, i2c_master_device_list) device_list; // I2C device (instance) list // asnyc trans members @@ -151,11 +151,11 @@ struct i2c_master_bus_t { bool trans_finish; // true if current command has been sent out. bool queue_trans; // true if current transaction is in queue bool new_queue; // true if allow a new queue transaction - size_t index; // transaction index QueueHandle_t trans_queues[I2C_TRANS_QUEUE_MAX]; // transaction queues. StaticQueue_t trans_queue_structs[I2C_TRANS_QUEUE_MAX]; // memory to store the static structure for trans_queues - i2c_operation_t **i2c_anyc_ops; // pointer to asynchronous operation. - uint8_t **anyc_write_buffer; // pointer to asynchronous write buffer. + i2c_operation_t (*i2c_async_ops)[I2C_STATIC_OPERATION_ARRAY_MAX]; // pointer to asynchronous operation(s). + uint32_t ops_prepare_idx; // Index for the operations can be written into `i2c_async_ops` array. + uint32_t ops_cur_size; // Indicates how many operations have already put in `i2c_async_ops`. i2c_transaction_t i2c_trans_pool[]; // I2C transaction pool. }; diff --git a/components/esp_driver_i2c/test_apps/i2c_test_apps/main/test_i2c_common.c b/components/esp_driver_i2c/test_apps/i2c_test_apps/main/test_i2c_common.c index cc238cc0a6e2..964892fb4948 100644 --- a/components/esp_driver_i2c/test_apps/i2c_test_apps/main/test_i2c_common.c +++ b/components/esp_driver_i2c/test_apps/i2c_test_apps/main/test_i2c_common.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: Unlicense OR CC0-1.0 */ @@ -148,3 +148,84 @@ TEST_CASE("I2C master probe device test", "[i2c]") TEST_ESP_ERR(i2c_master_probe(bus_handle, 0x55, -1), ESP_ERR_NOT_FOUND); TEST_ESP_OK(i2c_del_master_bus(bus_handle)); } + +#define LENGTH 48 + +static IRAM_ATTR bool test_master_tx_done_callback(i2c_master_dev_handle_t i2c_dev, const i2c_master_event_data_t *evt_data, void *arg) +{ + return true; +} + +/******************************************************************************* + * + * This test aim to test I2C non-blocking transaction function. Several things have been + * done in this test for testing its memory/concurrency issues. + * + * 1. See the depth of queue is 37, but the number of transaction is 42, that means some + * queue must be reused. + * 2. There are some delay randomly set there, for testing the concurency or any I2C state + * might be met. + ******************************************************************************* +*/ +TEST_CASE("I2C master transaction non-blocking mode with large amount of transaction", "[i2c]") +{ + i2c_master_bus_config_t i2c_bus_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, + .glitch_ignore_cnt = 7, + .trans_queue_depth = 37, + .flags.enable_internal_pullup = true, + }; + + i2c_master_bus_handle_t bus_handle; + + TEST_ESP_OK(i2c_new_master_bus(&i2c_bus_config, &bus_handle)); + + i2c_device_config_t dev_cfg = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = 0x58, + .scl_speed_hz = 400000, + }; + + i2c_master_dev_handle_t dev_handle; + TEST_ESP_OK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle)); + + i2c_master_event_callbacks_t cbs = { + .on_trans_done = test_master_tx_done_callback, + }; + i2c_master_register_event_callbacks(dev_handle, &cbs, NULL); + + uint32_t cnt = 7; + uint8_t *buf[6]; + for (int i = 0; i < 6; i++) { + buf[i] = (uint8_t*)heap_caps_calloc(1, LENGTH, MALLOC_CAP_8BIT); + for (int j = 0; j < LENGTH; j++) { + buf[i][j] = i + j; + } + } + while (cnt--) { + i2c_master_transmit(dev_handle, buf[0], LENGTH, -1); + esp_rom_delay_us(1000); + i2c_master_transmit(dev_handle, buf[1], LENGTH, -1); + esp_rom_delay_us(500); + i2c_master_transmit(dev_handle, buf[2], LENGTH, -1); + esp_rom_delay_us(200); + i2c_master_transmit(dev_handle, buf[3], LENGTH, -1); + esp_rom_delay_us(400); + i2c_master_transmit(dev_handle, buf[4], LENGTH, -1); + esp_rom_delay_us(700); + i2c_master_transmit(dev_handle, buf[5], LENGTH, -1); + esp_rom_delay_us(200); + } + + i2c_master_bus_wait_all_done(bus_handle, -1); + for (int i = 0; i < 6; i++) { + if (buf[i]) { + free(buf[i]); + } + } + TEST_ESP_OK(i2c_master_bus_rm_device(dev_handle)); + TEST_ESP_OK(i2c_del_master_bus(bus_handle)); +}