feat(i2c): Add new API and implementation for I2C driver

This commit is contained in:
Cao Sen Miao
2023-08-03 12:29:44 +08:00
parent db4308888d
commit 4ef94fc0dc
52 changed files with 3351 additions and 605 deletions
+5 -2
View File
@@ -91,7 +91,10 @@ endif()
# I2C related source files
if(CONFIG_SOC_I2C_SUPPORTED)
list(APPEND srcs "i2c/i2c.c")
list(APPEND srcs "i2c/i2c.c"
"i2c/i2c_master.c"
"i2c/i2c_common.c"
)
endif()
# I2S related source files
@@ -216,7 +219,7 @@ else()
INCLUDE_DIRS ${includes}
PRIV_REQUIRES efuse esp_timer
REQUIRES esp_pm esp_ringbuf freertos soc hal esp_hw_support
LDFRAGMENTS linker.lf gptimer/linker.lf gpio/linker.lf twai/linker.lf)
LDFRAGMENTS linker.lf gptimer/linker.lf gpio/linker.lf twai/linker.lf i2c/linker.lf)
endif()
# If system needs to monitor USJ connection status, then usb_serial_jtag_connection_monitor object file has to be linked
+20
View File
@@ -486,4 +486,24 @@ menu "Driver Configurations"
Enabling this option can improve driver performance as well.
endmenu # LEDC Configuration
menu "I2C Configuration"
config I2C_ISR_IRAM_SAFE
bool "I2C ISR IRAM-Safe"
default n
help
Ensure the I2C interrupt is IRAM-Safe by allowing the interrupt handler to be
executable when the cache is disabled (e.g. SPI Flash write).
note: This cannot be used in the I2C legacy driver.
config I2C_ENABLE_DEBUG_LOG
bool "Enable I2C debug log"
default n
help
Wether to enable the debug log message for I2C driver.
Note that this option only controls the I2C driver log, will not affect other drivers.
note: This cannot be used in the I2C legacy driver.
endmenu # I2C Configurations
endmenu # Driver configurations
+30 -14
View File
@@ -632,7 +632,7 @@ static esp_err_t i2c_master_clear_bus(i2c_port_t i2c_num)
gpio_set_level(sda_io, 1); // STOP, SDA low -> high while SCL is HIGH
i2c_set_pin(i2c_num, sda_io, scl_io, 1, 1, I2C_MODE_MASTER);
#else
i2c_ll_master_clr_bus(i2c_context[i2c_num].hal.dev);
i2c_ll_master_clr_bus(i2c_context[i2c_num].hal.dev, I2C_CLR_BUS_SCL_NUM);
#endif
return ESP_OK;
}
@@ -650,7 +650,7 @@ static esp_err_t i2c_hw_fsm_reset(i2c_port_t i2c_num)
uint8_t filter_cfg;
i2c_hal_get_timing_config(&i2c_context[i2c_num].hal, &timing_config);
i2c_ll_get_filter(i2c_context[i2c_num].hal.dev, &filter_cfg);
i2c_ll_master_get_filter(i2c_context[i2c_num].hal.dev, &filter_cfg);
//to reset the I2C hw module, we need re-enable the hw
i2c_hw_disable(i2c_num);
@@ -661,7 +661,7 @@ static esp_err_t i2c_hw_fsm_reset(i2c_port_t i2c_num)
i2c_ll_disable_intr_mask(i2c_context[i2c_num].hal.dev, I2C_LL_INTR_MASK);
i2c_ll_clear_intr_mask(i2c_context[i2c_num].hal.dev, I2C_LL_INTR_MASK);
i2c_hal_set_timing_config(&i2c_context[i2c_num].hal, &timing_config);
i2c_ll_set_filter(i2c_context[i2c_num].hal.dev, filter_cfg);
i2c_ll_master_set_filter(i2c_context[i2c_num].hal.dev, filter_cfg);
#else
i2c_ll_master_fsm_rst(i2c_context[i2c_num].hal.dev);
i2c_master_clear_bus(i2c_num);
@@ -767,7 +767,7 @@ esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf)
{
i2c_hal_master_init(&(i2c_context[i2c_num].hal));
//Default, we enable hardware filter
i2c_ll_set_filter(i2c_context[i2c_num].hal.dev, I2C_FILTER_CYC_NUM_DEF);
i2c_ll_master_set_filter(i2c_context[i2c_num].hal.dev, I2C_FILTER_CYC_NUM_DEF);
i2c_hal_set_bus_timing(&(i2c_context[i2c_num].hal), i2c_conf->master.clk_speed, src_clk, s_get_src_clk_freq(src_clk));
}
i2c_ll_update(i2c_context[i2c_num].hal.dev);
@@ -802,7 +802,7 @@ esp_err_t i2c_filter_enable(i2c_port_t i2c_num, uint8_t cyc_num)
ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_NUM_ERROR_STR);
ESP_RETURN_ON_FALSE(p_i2c_obj[i2c_num] != NULL, ESP_FAIL, I2C_TAG, I2C_DRIVER_ERR_STR);
I2C_ENTER_CRITICAL(&(i2c_context[i2c_num].spinlock));
i2c_ll_set_filter(i2c_context[i2c_num].hal.dev, cyc_num);
i2c_ll_master_set_filter(i2c_context[i2c_num].hal.dev, cyc_num);
i2c_ll_update(i2c_context[i2c_num].hal.dev);
I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock));
return ESP_OK;
@@ -812,7 +812,7 @@ esp_err_t i2c_filter_disable(i2c_port_t i2c_num)
{
ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_NUM_ERROR_STR);
I2C_ENTER_CRITICAL(&(i2c_context[i2c_num].spinlock));
i2c_ll_set_filter(i2c_context[i2c_num].hal.dev, 0);
i2c_ll_master_set_filter(i2c_context[i2c_num].hal.dev, 0);
i2c_ll_update(i2c_context[i2c_num].hal.dev);
I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock));
return ESP_OK;
@@ -825,7 +825,7 @@ esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time
ESP_RETURN_ON_FALSE((setup_time <= I2C_SCL_RSTART_SETUP_TIME_V) && (setup_time > 0), ESP_ERR_INVALID_ARG, I2C_TAG, I2C_TIMING_VAL_ERR_STR);
I2C_ENTER_CRITICAL(&(i2c_context[i2c_num].spinlock));
i2c_ll_set_start_timing(i2c_context[i2c_num].hal.dev, setup_time, hold_time);
i2c_ll_master_set_start_timing(i2c_context[i2c_num].hal.dev, setup_time, hold_time);
i2c_ll_update(i2c_context[i2c_num].hal.dev);
I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock));
return ESP_OK;
@@ -847,7 +847,7 @@ esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time)
ESP_RETURN_ON_FALSE((hold_time <= I2C_SCL_STOP_HOLD_TIME_V) && (hold_time > 0), ESP_ERR_INVALID_ARG, I2C_TAG, I2C_TIMING_VAL_ERR_STR);
I2C_ENTER_CRITICAL(&(i2c_context[i2c_num].spinlock));
i2c_ll_set_stop_timing(i2c_context[i2c_num].hal.dev, setup_time, hold_time);
i2c_ll_master_set_stop_timing(i2c_context[i2c_num].hal.dev, setup_time, hold_time);
i2c_ll_update(i2c_context[i2c_num].hal.dev);
I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock));
return ESP_OK;
@@ -1411,8 +1411,8 @@ static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num, BaseType_t
}
hw_cmd.byte_num = fifo_fill;
i2c_ll_write_txfifo(i2c_context[i2c_num].hal.dev, write_pr, fifo_fill);
i2c_ll_write_cmd_reg(i2c_context[i2c_num].hal.dev, hw_cmd, p_i2c->cmd_idx);
i2c_ll_write_cmd_reg(i2c_context[i2c_num].hal.dev, hw_end_cmd, p_i2c->cmd_idx + 1);
i2c_ll_master_write_cmd_reg(i2c_context[i2c_num].hal.dev, hw_cmd, p_i2c->cmd_idx);
i2c_ll_master_write_cmd_reg(i2c_context[i2c_num].hal.dev, hw_end_cmd, p_i2c->cmd_idx + 1);
i2c_ll_master_enable_tx_it(i2c_context[i2c_num].hal.dev);
p_i2c->cmd_idx = 0;
if (i2c_cmd_is_single_byte(cmd) || cmd->total_bytes == cmd->bytes_used) {
@@ -1428,13 +1428,13 @@ static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num, BaseType_t
fifo_fill = MIN(remaining_bytes, SOC_I2C_FIFO_LEN);
p_i2c->rx_cnt = fifo_fill;
hw_cmd.byte_num = fifo_fill;
i2c_ll_write_cmd_reg(i2c_context[i2c_num].hal.dev, hw_cmd, p_i2c->cmd_idx);
i2c_ll_write_cmd_reg(i2c_context[i2c_num].hal.dev, hw_end_cmd, p_i2c->cmd_idx + 1);
i2c_ll_master_write_cmd_reg(i2c_context[i2c_num].hal.dev, hw_cmd, p_i2c->cmd_idx);
i2c_ll_master_write_cmd_reg(i2c_context[i2c_num].hal.dev, hw_end_cmd, p_i2c->cmd_idx + 1);
i2c_ll_master_enable_rx_it(i2c_context[i2c_num].hal.dev);
p_i2c->status = I2C_STATUS_READ;
break;
} else {
i2c_ll_write_cmd_reg(i2c_context[i2c_num].hal.dev, hw_cmd, p_i2c->cmd_idx);
i2c_ll_master_write_cmd_reg(i2c_context[i2c_num].hal.dev, hw_cmd, p_i2c->cmd_idx);
}
p_i2c->cmd_idx++;
p_i2c->cmd_link.head = p_i2c->cmd_link.head->next;
@@ -1444,7 +1444,7 @@ static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num, BaseType_t
}
}
i2c_ll_update(i2c_context[i2c_num].hal.dev);
i2c_ll_trans_start(i2c_context[i2c_num].hal.dev);
i2c_ll_master_trans_start(i2c_context[i2c_num].hal.dev);
return;
}
@@ -1643,3 +1643,19 @@ int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t *data, size_t max_size, Ti
return max_size - size_rem;
}
#endif
/**
* @brief This function will be called during start up, to check that this legacy i2c driver is not running along with the new I2C driver
*/
__attribute__((constructor))
static void check_i2c_driver_conflict(void)
{
// This function was declared as weak here. The new I2C driver has the implementation.
// So if the new I2C driver is not linked in, then `i2c_acquire_bus_handle()` should be NULL at runtime.
extern __attribute__((weak)) esp_err_t i2c_acquire_bus_handle(int port_num, void *i2c_new_bus, int mode);
if ((void *)i2c_acquire_bus_handle != NULL) {
ESP_EARLY_LOGE(I2C_TAG, "CONFLICT! driver_ng is not allowed to be used with this old driver");
abort();
}
ESP_EARLY_LOGW(I2C_TAG, "This driver is an old driver, please migrate your application code to adapt `driver/i2c_master.h`");
}
+230
View File
@@ -0,0 +1,230 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdio.h>
#include "sdkconfig.h"
#include "esp_types.h"
#if CONFIG_I2C_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "esp_log.h"
#include "esp_check.h"
#include "esp_pm.h"
#include "freertos/FreeRTOS.h"
#include "hal/i2c_hal.h"
#include "hal/gpio_hal.h"
#include "esp_private/periph_ctrl.h"
#include "esp_rom_gpio.h"
#include "i2c_private.h"
#include "driver/gpio.h"
#include "soc/clk_tree_defs.h"
#include "soc/i2c_periph.h"
#include "esp_clk_tree.h"
#include "clk_ctrl_os.h"
static const char *TAG = "i2c.common";
typedef struct i2c_platform_t {
_lock_t mutex; // platform level mutex lock.
i2c_bus_handle_t buses[SOC_I2C_NUM]; // array of I2C bus instances.
uint32_t count[SOC_I2C_NUM]; // reference count used to protect group install/uninstall.
} i2c_platform_t;
static i2c_platform_t s_i2c_platform = {}; // singleton platform
esp_err_t i2c_acquire_bus_handle(i2c_port_num_t port_num, i2c_bus_handle_t *i2c_new_bus, i2c_bus_mode_t mode)
{
#if CONFIG_I2C_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
bool new_bus = false;
i2c_bus_t *bus = NULL;
esp_err_t ret = ESP_OK;
if (!s_i2c_platform.buses[port_num]) {
new_bus = true;
bus = heap_caps_calloc(1, sizeof(i2c_bus_t), I2C_MEM_ALLOC_CAPS);
if (bus) {
s_i2c_platform.buses[port_num] = bus;
bus->port_num = port_num;
bus->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
bus->bus_mode = mode;
// Enable the I2C module
periph_module_enable(i2c_periph_signal[port_num].module);
periph_module_reset(i2c_periph_signal[port_num].module);
i2c_hal_init(&bus->hal, port_num);
}
} else {
ESP_LOGE(TAG, "I2C bus id(%d) has already been acquired", port_num);
bus = s_i2c_platform.buses[port_num];
ret = ESP_ERR_INVALID_STATE;
}
if (bus) {
s_i2c_platform.count[port_num]++;
}
if (new_bus) {
ESP_LOGD(TAG, "new bus(%d) at %p", port_num, bus);
}
*i2c_new_bus = bus;
return ret;
}
bool i2c_bus_occupied(i2c_port_num_t port_num)
{
bool bus_occupied = false;
_lock_acquire(&s_i2c_platform.mutex);
if (s_i2c_platform.buses[port_num]) {
bus_occupied = true;
}
_lock_release(&s_i2c_platform.mutex);
return bus_occupied;
}
uint8_t i2c_release_bus_handle(i2c_bus_handle_t i2c_bus)
{
int port_num = i2c_bus->port_num;
i2c_clock_source_t clk_src = i2c_bus->clk_src;
bool do_deinitialize = false;
_lock_acquire(&s_i2c_platform.mutex);
if (s_i2c_platform.buses[port_num]) {
s_i2c_platform.count[port_num]--;
if (s_i2c_platform.count[port_num] == 0) {
do_deinitialize = true;
s_i2c_platform.buses[port_num] = NULL;
// Disable I2C module
periph_module_disable(i2c_periph_signal[port_num].module);
free(i2c_bus);
}
}
_lock_release(&s_i2c_platform.mutex);
switch (clk_src) {
#if SOC_I2C_SUPPORT_RTC
case I2C_CLK_SRC_RC_FAST:
periph_rtc_dig_clk8m_disable();
break;
#endif // SOC_I2C_SUPPORT_RTC
default:
break;
}
if (do_deinitialize) {
ESP_LOGD(TAG, "delete bus %d", port_num);
}
return s_i2c_platform.count[port_num];
}
esp_err_t i2c_select_periph_clock(i2c_bus_handle_t handle, i2c_clock_source_t clk_src)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "I2C empty controller handle");
uint32_t periph_src_clk_hz = 0;
bool clock_selection_conflict = 0;
portENTER_CRITICAL(&handle->spinlock);
if (handle->clk_src == 0) {
handle->clk_src = clk_src;
} else {
clock_selection_conflict = (handle->clk_src != clk_src);
}
portEXIT_CRITICAL(&handle->spinlock);
ESP_RETURN_ON_FALSE(!clock_selection_conflict, ESP_ERR_INVALID_STATE, TAG,
"group clock conflict, already is %d but attempt to %d", handle->clk_src, clk_src);
// TODO: [clk_tree] to use a generic clock enable/disable or acquire/release function for all clock source
#if SOC_I2C_SUPPORT_RTC
if (clk_src == I2C_CLK_SRC_RC_FAST) {
// RC_FAST clock is not enabled automatically on start up, we enable it here manually.
// Note there's a ref count in the enable/disable function, we must call them in pair in the driver.
periph_rtc_dig_clk8m_enable();
}
#endif // SOC_I2C_SUPPORT_RTC
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &periph_src_clk_hz), TAG, "i2c get clock frequency error");
handle->clk_src_freq_hz = periph_src_clk_hz;
#if CONFIG_PM_ENABLE
bool need_pm_lock = true;
// to make the I2C work reliable, the source clock must stay alive and unchanged
// driver will create different pm lock for that purpose, according to different clock source
esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP;
#if SOC_I2C_SUPPORT_RTC
if (clk_src == I2C_CLK_SRC_RC_FAST) {
// I2C use fifo, which connected to APB, so we cannot use I2C either when in light sleep.
need_pm_lock = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2C_SUPPORT_RTC
#if SOC_I2C_SUPPORT_APB
if (clk_src == I2C_CLK_SRC_APB) {
// APB clock frequency can be changed during DFS
pm_lock_type = ESP_PM_APB_FREQ_MAX;
}
#endif // SOC_I2C_SUPPORT_APB
if (need_pm_lock) {
sprintf(handle->pm_lock_name, "I2C_%d", handle->port_num); // e.g. PORT_0
ret = esp_pm_lock_create(pm_lock_type, 0, handle->pm_lock_name, &handle->pm_lock);
ESP_RETURN_ON_ERROR(ret, TAG, "create pm lock failed");
}
#endif // CONFIG_PM_ENABLE
ESP_LOGD(TAG, "bus clock source frequency: %"PRIu32"hz", periph_src_clk_hz);
return ret;
}
esp_err_t i2c_common_set_pins(i2c_bus_handle_t handle)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "I2C empty controller handle");
int port_id = handle->port_num;
// SDA pin configurations
gpio_config_t sda_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT_OUTPUT_OD,
.pull_down_en = false,
.pull_up_en = handle->pull_up_enable ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE,
.pin_bit_mask = 1ULL << handle->sda_num,
};
#if CONFIG_IDF_TARGET_ESP32
// on esp32, must enable internal pull-up
sda_conf.pull_up_en = GPIO_PULLUP_ENABLE;
#endif
ESP_RETURN_ON_ERROR(gpio_config(&sda_conf), TAG, "config GPIO failed");
ESP_RETURN_ON_ERROR(gpio_set_level(handle->sda_num, 1), TAG, "i2c sda pin set level failed");
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[handle->sda_num], PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(handle->sda_num, i2c_periph_signal[port_id].sda_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(handle->sda_num, i2c_periph_signal[port_id].sda_in_sig, 0);
// SCL pin configurations
gpio_config_t scl_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT_OUTPUT_OD,
.pull_down_en = false,
.pull_up_en = handle->pull_up_enable ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE,
.pin_bit_mask = 1ULL << handle->scl_num,
};
#if CONFIG_IDF_TARGET_ESP32
// on esp32, must enable internal pull-up
scl_conf.pull_up_en = GPIO_PULLUP_ENABLE;
#endif
ESP_RETURN_ON_ERROR(gpio_config(&scl_conf), TAG, "config GPIO failed");
ESP_RETURN_ON_ERROR(gpio_set_level(handle->scl_num, 1), TAG, "i2c scl pin set level failed");
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[handle->scl_num], PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(handle->scl_num, i2c_periph_signal[port_id].scl_out_sig, 0, 0);
esp_rom_gpio_connect_in_signal(handle->scl_num, i2c_periph_signal[port_id].scl_in_sig, 0);
return ret;
}
File diff suppressed because it is too large Load Diff
+209
View File
@@ -0,0 +1,209 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdatomic.h>
#include <sys/queue.h>
#include "esp_err.h"
#include "driver/i2c_types.h"
#include "hal/i2c_hal.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "freertos/ringbuf.h"
#include "esp_pm.h"
#ifdef __cplusplus
extern "C" {
#endif
#if CONFIG_I2C_ISR_IRAM_SAFE
#define I2C_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define I2C_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT)
#endif
// I2C driver object is per-mode, the interrupt source is shared between modes
#if CONFIG_I2C_ISR_IRAM_SAFE
#define I2C_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LOWMED)
#else
#define I2C_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED)
#endif
#define I2C_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
#define I2C_PM_LOCK_NAME_LEN_MAX 16
#define I2C_STATIC_OPERATION_ARRAY_MAX 6
#define ACK_VAL 0
#define NACK_VAL 1
#define I2C_TRANS_READ_COMMAND(ack_value) {.ack_val = (ack_value), .op_code = I2C_LL_CMD_READ}
#define I2C_TRANS_WRITE_COMMAND(ack_check) {.ack_en = (ack_check), .op_code = I2C_LL_CMD_WRITE}
#define I2C_TRANS_STOP_COMMAND {.op_code = I2C_LL_CMD_STOP}
#define I2C_TRANS_START_COMMAND {.op_code = I2C_LL_CMD_RESTART}
typedef struct i2c_bus_t i2c_bus_t;
typedef struct i2c_master_bus_t i2c_master_bus_t;
typedef struct i2c_bus_t *i2c_bus_handle_t;
typedef struct i2c_master_dev_t i2c_master_dev_t;
typedef enum {
I2C_BUS_MODE_MASTER = 0,
I2C_BUS_MODE_SLAVE = 1,
} i2c_bus_mode_t;
typedef enum {
I2C_SLAVE_FIFO = 0,
I2C_SLAVE_NONFIFO = 1,
} i2c_slave_fifo_mode_t;
enum {
I2C_TRANS_QUEUE_READY,
I2C_TRANS_QUEUE_PROGRESS,
I2C_TRANS_QUEUE_COMPLETE,
I2C_TRANS_QUEUE_MAX,
};
typedef struct {
i2c_ll_hw_cmd_t hw_cmd; // I2C command, defined by hardware
uint8_t *data; // Pointer to data
uint16_t bytes_used; // I2C data has been used
size_t total_bytes; // Total bytes to be transferred
} i2c_operation_t;
typedef struct {
uint32_t device_address; // Address of I2C device
i2c_operation_t *ops; // Pointer to I2C operation structure
size_t cmd_count; // Record how many I2C hardware commands in one transaction
} i2c_transaction_t;
struct i2c_bus_t {
i2c_port_num_t port_num; // Port(Bus) ID, index from 0
portMUX_TYPE spinlock; // To protect pre-group register level concurrency access
i2c_hal_context_t hal; // Hal layer for each port(bus)
i2c_clock_source_t clk_src; // Record the port clock source
uint32_t clk_src_freq_hz; // Record the clock source frequency
int sda_num; // SDA pin number
int scl_num; // SCL pin number
bool pull_up_enable; // Disable pull-ups
intr_handle_t intr_handle; // I2C interrupt handle
esp_pm_lock_handle_t pm_lock; // power manange lock
#if CONFIG_PM_ENABLE
char pm_lock_name[I2C_PM_LOCK_NAME_LEN_MAX]; // pm lock name
#endif
i2c_bus_mode_t bus_mode; // I2C bus mode
};
typedef struct i2c_master_device_list {
i2c_master_dev_t *device;
SLIST_ENTRY(i2c_master_device_list) next;
} i2c_master_device_list_t;
struct i2c_master_bus_t {
i2c_bus_t *base; // bus base class
SemaphoreHandle_t bus_lock_mux; // semaphore to lock bus process
int cmd_idx; //record current command index, for master mode
_Atomic i2c_master_status_t status; // record current command status, for master mode
i2c_master_event_t event; // record current i2c bus event
int rx_cnt; // record current read index, for master mode
i2c_transaction_t i2c_trans; // Pointer to I2C transfer structure
i2c_operation_t i2c_ops[I2C_STATIC_OPERATION_ARRAY_MAX]; // I2C operation array
_Atomic uint16_t trans_idx; // Index of I2C transaction command.
SemaphoreHandle_t cmd_semphr; // Semaphore between task and interrupt, using for synchronizing ISR and I2C task.
uint32_t read_buf_pos; // Read buffer position
bool contains_read; // Whether command array includes read operation, true: yes, otherwise, false.
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.
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
bool async_break; // break transaction loop flag.
i2c_addr_bit_len_t addr_10bits_bus; // Slave address is 10 bits.
size_t queue_size; // I2C transaction queue size.
size_t num_trans_inflight; // Indicates the number of transactions that are undergoing but not recycled to ready_queue
size_t num_trans_inqueue; // Indicates the number of transaction in queue transaction.
void* queues_storage; // storage of transaction queues
bool sent_all; // true if the queue transaction is sent
bool in_progress; // true if current transaction is in progress
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_transaction_t i2c_trans_pool[]; // I2C transaction pool.
};
struct i2c_master_dev_t {
i2c_master_bus_t *master_bus; // I2C master bus base class
uint16_t device_address; // I2C device address
uint32_t scl_speed_hz; // SCL clock frequency
i2c_addr_bit_len_t addr_10bits; // Whether I2C device is a 10-bits address device.
i2c_master_callback_t on_trans_done; // I2C master transaction done callback.
void *user_ctx; // Callback user context
};
/**
* @brief Acquire I2C bus handle
*
* @param port_num I2C port number.
* @return
* - ESP_OK: Acquire bus handle successfully.
* - ESP_ERR_INVALID_ARG: Argument error.
* - ESP_ERR_INVALID_STATE: Acquire bus invalid state because bus has already acquired.
*/
esp_err_t i2c_acquire_bus_handle(i2c_port_num_t port_num, i2c_bus_handle_t *i2c_new_bus, i2c_bus_mode_t mode);
/**
* @brief Release I2C bus handle
*
* @param i2c_bus I2C bus handle, returned from `i2c_acquire_bus_handle`
* @return ESP_OK: If release successfully
*/
uint8_t i2c_release_bus_handle(i2c_bus_handle_t i2c_bus);
/**
* @brief Set clock source for I2C peripheral
*
* @param handle I2C bus handle
* @param clk_src Clock source
* @return
* - ESP_OK: Set clock source successfully
* - ESP_ERR_NOT_SUPPORTED: Set clock source failed because the clk_src is not supported
* - ESP_ERR_INVALID_STATE: Set clock source failed because the clk_src is different from other I2C controller
* - ESP_FAIL: Set clock source failed because of other error
*/
esp_err_t i2c_select_periph_clock(i2c_bus_handle_t handle, i2c_clock_source_t clk_src);
/**
* @brief Set I2C SCL/SDA pins
*
* @param handle I2C bus handle
* @return
* - ESP_OK: I2C set SCL/SDA pins successfully.
* - ESP_ERR_INVALID_ARG: Argument error.
* - Otherwise: Set SCL/SDA IOs error.
*/
esp_err_t i2c_common_set_pins(i2c_bus_handle_t handle);
/**
* @brief Check whether I2C bus is occupied
*
* @param port_num I2C port number.
* @return true: occupied, otherwise, false.
*/
bool i2c_bus_occupied(i2c_port_num_t port_num);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,213 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "driver/i2c_types.h"
#include "hal/gpio_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief I2C master bus specific configurations
*/
typedef struct {
i2c_port_num_t i2c_port; /*!< I2C port number, `-1` for auto selecting */
gpio_num_t sda_io_num; /*!< GPIO number of I2C SDA signal, pulled-up internally */
gpio_num_t scl_io_num; /*!< GPIO number of I2C SCL signal, pulled-up internally */
i2c_clock_source_t clk_source; /*!< Clock source of I2C master bus, channels in the same group must use the same clock source */
uint8_t glitch_ignore_cnt; /*!< If the glitch period on the line is less than this value, it can be filtered out, typically value is 7 (unit: I2C module clock cycle)*/
int intr_priority; /*!< I2C interrupt priority, if set to 0, driver will select the default priority (1,2,3). */
size_t trans_queue_depth; /*!< Depth of internal transfer queue, increase this value can support more transfers pending in the background, only valid in asynchronous transaction. (Typically max_device_num * per_transaction)*/
struct {
uint32_t enable_internal_pullup:1; /*!< Enable internal pullups */
} flags;
} i2c_master_bus_config_t;
/**
* @brief I2C device configuration
*/
typedef struct {
i2c_addr_bit_len_t dev_addr_length; /*!< Select the address length of the slave device. */
uint16_t device_address; /*!< I2C device raw address. (The 7/10 bit address without read/write bit) */
uint32_t scl_speed_hz; /*!< I2C SCL line frequency. */
} i2c_device_config_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
* @note When CONFIG_I2C_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
* The variables used in the function should be in the SRAM as well.
*/
typedef struct {
i2c_master_callback_t on_trans_done; /*!< I2C master transaction finish callback */
} i2c_master_event_callbacks_t;
/**
* @brief Allocate an I2C master bus
*
* @param[in] bus_config I2C master bus configuration.
* @param[out] ret_bus_handle I2C bus handle
* @return
* - ESP_OK: I2C master bus initialized successfully.
* - ESP_ERR_INVALID_ARG: I2C bus initialization failed because of invalid argument.
* - ESP_ERR_NO_MEM: Create I2C bus failed because of out of memory.
* - ESP_ERR_NOT_FOUND: No more free bus.
*/
esp_err_t i2c_new_master_bus(const i2c_master_bus_config_t *bus_config, i2c_master_bus_handle_t *ret_bus_handle);
/**
* @brief Add I2C master BUS device.
*
* @param[in] bus_handle I2C bus handle.
* @param[in] dev_config device config.
* @param[out] ret_handle device handle.
* @return
* - ESP_OK: Create I2C master device successfully.
* - ESP_ERR_INVALID_ARG: I2C bus initialization failed because of invalid argument.
* - ESP_ERR_NO_MEM: Create I2C bus failed because of out of memory.
*/
esp_err_t i2c_master_bus_add_device(i2c_master_bus_handle_t bus_handle, const i2c_device_config_t *dev_config, i2c_master_dev_handle_t *ret_handle);
/**
* @brief Deinitialize the I2C master bus and delete the handle.
*
* @param[in] bus_handle I2C bus handle.
* @return
* - ESP_OK: Delete I2C bus success, otherwise, failed.
* - Otherwise: Some module delete failed.
*/
esp_err_t i2c_del_master_bus(i2c_master_bus_handle_t bus_handle);
/**
* @brief I2C master bus delete device
*
* @param handle i2c device handle
* @return
* - ESP_OK: If device is successfully deleted.
*/
esp_err_t i2c_master_bus_rm_device(i2c_master_dev_handle_t handle);
/**
* @brief Perform a write transaction on the I2C bus.
* The transaction will be undergoing until it finishes or it reaches
* the timeout provided.
*
* @note If a callback was registered with `i2c_master_register_event_callbacks`, the transaction will be asynchronous, and thus, this function will return directly, without blocking.
* You will get finish information from callback. Besides, data buffer should always be completely prepared when callback is registered, otherwise, the data will get corrupt.
*
* @param[in] i2c_dev I2C master device handle that created by `i2c_master_bus_add_device`.
* @param[in] write_buffer Data bytes to send on the I2C bus.
* @param[in] write_size Size, in bytes, of the write buffer.
* @param[in] 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_transmit(i2c_master_dev_handle_t i2c_dev, const uint8_t *write_buffer, size_t write_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
* the timeout provided.
*
* @note If a callback was registered with `i2c_master_register_event_callbacks`, the transaction will be asynchronous, and thus, this function will return directly, without blocking.
* You will get finish information from callback. Besides, data buffer should always be completely prepared when callback is registered, otherwise, the data will get corrupt.
*
* @param[in] i2c_dev I2C master device handle that created by `i2c_master_bus_add_device`.
* @param[in] write_buffer Data bytes to send on the I2C bus.
* @param[in] write_size Size, in bytes, of the write buffer.
* @param[out] read_buffer Data bytes received from i2c bus.
* @param[in] read_size Size, in bytes, of the read buffer.
* @param[in] xfer_timeout_ms Wait timeout, in ms. Note: -1 means wait forever.
* @return
* - ESP_OK: I2C master transmit-receive 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_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);
/**
* @brief Perform a read transaction on the I2C bus.
* The transaction will be undergoing until it finishes or it reaches
* the timeout provided.
*
* @note If a callback was registered with `i2c_master_register_event_callbacks`, the transaction will be asynchronous, and thus, this function will return directly, without blocking.
* You will get finish information from callback. Besides, data buffer should always be completely prepared when callback is registered, otherwise, the data will get corrupt.
*
* @param[in] i2c_dev I2C master device handle that created by `i2c_master_bus_add_device`.
* @param[out] read_buffer Data bytes received from i2c bus.
* @param[in] read_size Size, in bytes, of the read buffer.
* @param[in] xfer_timeout_ms Wait timeout, in ms. Note: -1 means wait forever.
* @return
* - ESP_OK: I2C master receive success
* - ESP_ERR_INVALID_ARG: I2c master receive 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_receive(i2c_master_dev_handle_t i2c_dev, uint8_t *read_buffer, size_t read_size, int xfer_timeout_ms);
/**
* @brief Probe I2C address, if address is correct and ACK is received, this function will return ESP_OK.
*
* @param[in] i2c_dev I2C master device handle that created by `i2c_master_bus_add_device`.
* @param[in] xfer_timeout_ms Wait timeout, in ms. Note: -1 means wait forever (Not recommended in this function).
* @return
* - ESP_OK: I2C device probe successfully
* - ESP_ERR_TIMEOUT: Operation timeout(larger than xfer_timeout_ms) because the bus is busy or hardware crash.
*/
// esp_err_t i2c_master_probe(i2c_master_dev_handle_t i2c_dev, int xfer_timeout_ms);
esp_err_t i2c_master_probe(i2c_master_bus_handle_t i2c_master, uint16_t address, int xfer_timeout_ms);
/**
* @brief Register I2C transaction callbacks for a master device
*
* @note User can deregister a previously registered callback by calling this function and setting the callback member in the `cbs` structure to NULL.
* @note When CONFIG_I2C_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
* The variables used in the function should be in the SRAM as well. The `user_data` should also reside in SRAM.
* @note If the callback is used for helping asynchronous transaction. On the same bus, only one device can be used for performing asynchronous operation.
*
* @param[in] i2c_dev I2C master device handle that created by `i2c_master_bus_add_device`.
* @param[in] cbs Group of callback functions
* @param[in] user_data User data, which will be passed to callback functions directly
* @return
* - ESP_OK: Set I2C transaction callbacks successfully
* - ESP_ERR_INVALID_ARG: Set I2C transaction callbacks failed because of invalid argument
* - ESP_FAIL: Set I2C transaction callbacks failed because of other error
*/
esp_err_t i2c_master_register_event_callbacks(i2c_master_dev_handle_t i2c_dev, const i2c_master_event_callbacks_t *cbs, void *user_data);
/**
* @brief Reset the I2C master bus.
*
* @param handle I2C bus handle.
* @return
* - ESP_OK: Reset succeed.
* - ESP_ERR_INVALID_ARG: I2C master bus handle is not initialized.
* - Otherwise: Reset failed.
*/
esp_err_t i2c_master_bus_reset(i2c_master_bus_handle_t handle);
/**
* @brief Wait for all pending I2C transactions done
*
* @param[in] i2c_master I2C bus handle
* @param[in] timeout_ms Wait timeout, in ms. Specially, -1 means to wait forever.
* @return
* - ESP_OK: Flush transactions successfully
* - ESP_ERR_INVALID_ARG: Flush transactions failed because of invalid argument
* - ESP_ERR_TIMEOUT: Flush transactions failed because of timeout
* - ESP_FAIL: Flush transactions failed because of other error
*/
esp_err_t i2c_master_wait_all_done(i2c_master_bus_handle_t i2c_master, int timeout_ms);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,74 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "hal/i2c_types.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief I2C port number.
*/
typedef int i2c_port_num_t;
/**
* @brief Enumeration for I2C fsm status.
*/
typedef enum {
I2C_STATUS_READ, /*!< read status for current master command */
I2C_STATUS_WRITE, /*!< write status for current master command */
I2C_STATUS_START, /*!< Start status for current master command */
I2C_STATUS_STOP, /*!< stop status for current master command */
I2C_STATUS_IDLE, /*!< idle status for current master command */
I2C_STATUS_ACK_ERROR, /*!< ack error status for current master command */
I2C_STATUS_DONE, /*!< I2C command done */
I2C_STATUS_TIMEOUT, /*!< I2C bus status error, and operation timeout */
} i2c_master_status_t;
typedef enum {
I2C_EVENT_ALIVE, /*!< i2c bus in alive status.*/
I2C_EVENT_DONE, /*!< i2c bus transaction done */
I2C_EVENT_NACK, /*!< i2c bus nack */
} i2c_master_event_t;
/**
* @brief Type of I2C master bus handle
*/
typedef struct i2c_master_bus_t *i2c_master_bus_handle_t;
/**
* @brief Type of I2C master bus device handle
*/
typedef struct i2c_master_dev_t *i2c_master_dev_handle_t;
/**
* @brief Data type used in I2C event callback
*/
typedef struct {
i2c_master_event_t event; /**< The I2C hardware event that I2C callback is called. */
} i2c_master_event_data_t;
/**
* @brief An callback for I2C transaction.
*
* @param[in] i2c_dev Handle for I2C device.
* @param[out] evt_data I2C capture event data, fed by driver
* @param[in] arg User data, set in `i2c_master_register_event_callbacks()`
*
* @return Whether a high priority task has been waken up by this function
*/
typedef bool (*i2c_master_callback_t)(i2c_master_dev_handle_t i2c_dev, const i2c_master_event_data_t *evt_data, void *arg);
#ifdef __cplusplus
}
#endif
+8
View File
@@ -0,0 +1,8 @@
[mapping:i2c_driver]
archive: libdriver.a
entries:
if I2C_ISR_IRAM_SAFE = y:
i2c_master: s_i2c_send_command_async (noflash)
i2c_master: s_i2c_write_command (noflash)
i2c_master: s_i2c_read_command (noflash)
i2c_master: s_i2c_start_end_command (noflash)
@@ -0,0 +1,131 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "unity.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "soc/gpio_periph.h"
#include "soc/clk_tree_defs.h"
#include "soc/soc_caps.h"
#include "hal/gpio_hal.h"
#include "hal/uart_ll.h"
#include "esp_private/periph_ctrl.h"
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "esp_rom_gpio.h"
#include "esp_log.h"
#include "test_utils.h"
static const char TAG[] = "test-i2c";
#define TEST_I2C_SCL_PIN 2
#define TEST_I2C_SDA_PIN 4
#define TEST_I2C_SCL_FREQ (100 * 1000)
#define TEST_I2C_PORT 0
#define SLAVE_ADDR 0x58
TEST_CASE("I2C bus install-uninstall test", "[i2c]")
{
i2c_master_bus_config_t i2c_mst_config_1 = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = TEST_I2C_PORT,
.scl_io_num = TEST_I2C_SCL_PIN,
.sda_io_num = TEST_I2C_SDA_PIN,
};
i2c_master_bus_handle_t i2c_mst_handle1;
#if SOC_I2C_NUM > 1
i2c_master_bus_config_t i2c_mst_config_2 = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = 1,
.scl_io_num = TEST_I2C_SCL_PIN,
.sda_io_num = TEST_I2C_SDA_PIN,
};
i2c_master_bus_handle_t i2c_mst_handle2;
#endif
// Install master bus 0
ESP_LOGI(TAG, "Initialize bus0");
TEST_ESP_OK(i2c_new_master_bus(&i2c_mst_config_1, &i2c_mst_handle1));
#if SOC_I2C_NUM > 1
// Install master bus 1
ESP_LOGI(TAG, "Initialize bus1");
TEST_ESP_OK(i2c_new_master_bus(&i2c_mst_config_2, &i2c_mst_handle2));
#endif
// Install master bus 0 again
ESP_LOGI(TAG, "Initialize bus0 again");
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, i2c_new_master_bus(&i2c_mst_config_1, &i2c_mst_handle1));
ESP_LOGI(TAG, "Delete bus0");
TEST_ESP_OK(i2c_del_master_bus(i2c_mst_handle1));
#if SOC_I2C_NUM > 1
ESP_LOGI(TAG, "Delete bus1");
TEST_ESP_OK(i2c_del_master_bus(i2c_mst_handle2));
#endif
}
TEST_CASE("I2C driver memory leaking check", "[i2c]")
{
i2c_master_bus_config_t i2c_mst_config_1 = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = TEST_I2C_PORT,
.scl_io_num = TEST_I2C_SCL_PIN,
.sda_io_num = TEST_I2C_SDA_PIN,
};
i2c_master_bus_handle_t bus_handle;
int size = esp_get_free_heap_size();
for (uint32_t i = 0; i <= 5; i++) {
TEST_ESP_OK(i2c_new_master_bus(&i2c_mst_config_1, &bus_handle));
vTaskDelay(10 / portTICK_PERIOD_MS);
TEST_ESP_OK(i2c_del_master_bus(bus_handle));
}
TEST_ASSERT_INT_WITHIN(300, size, esp_get_free_heap_size());
}
TEST_CASE("I2C device add & remove check", "[i2c]")
{
i2c_master_bus_config_t i2c_mst_config_1 = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = TEST_I2C_PORT,
.scl_io_num = TEST_I2C_SCL_PIN,
.sda_io_num = TEST_I2C_SDA_PIN,
};
i2c_master_bus_handle_t bus_handle;
TEST_ESP_OK(i2c_new_master_bus(&i2c_mst_config_1, &bus_handle));
i2c_device_config_t dev_cfg_1 = {
.scl_speed_hz = 100*1000,
.device_address = 0x10,
};
i2c_master_dev_handle_t dev_1;
i2c_master_bus_add_device(bus_handle, &dev_cfg_1, &dev_1);
i2c_device_config_t dev_cfg_2 = {
.scl_speed_hz = 100*1000,
.device_address = 0x20,
};
i2c_master_dev_handle_t dev_2;
i2c_master_bus_add_device(bus_handle, &dev_cfg_2, &dev_2);
i2c_device_config_t dev_cfg_3 = {
.scl_speed_hz = 100*1000,
.device_address = 0x30,
};
i2c_master_dev_handle_t dev_3;
i2c_master_bus_add_device(bus_handle, &dev_cfg_3, &dev_3);
i2c_master_bus_rm_device(dev_1);
i2c_master_bus_rm_device(dev_2);
i2c_master_bus_rm_device(dev_3);
TEST_ESP_OK(i2c_del_master_bus(bus_handle));
}
@@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.supported_targets
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
'iram_safe',
],
indirect=True,
)
def test_i2c(dut: Dut) -> None:
dut.run_all_single_board_cases()
@@ -0,0 +1,6 @@
CONFIG_PM_ENABLE=y
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_I2C_ISR_IRAM_SAFE=y
@@ -0,0 +1,22 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS
"$ENV{IDF_PATH}/tools/unit-test-app/components"
)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(legacy_i2c_test)
if(CONFIG_COMPILER_DUMP_RTL_FILES)
add_custom_target(check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dirs ${CMAKE_BINARY_DIR}/esp-idf/driver/,${CMAKE_BINARY_DIR}/esp-idf/hal/
--elf-file ${CMAKE_BINARY_DIR}/legacy_i2c_test.elf
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata
--exit-code
DEPENDS ${elf}
)
endif()
@@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
@@ -0,0 +1,6 @@
set(srcs "test_app_main.c"
"test_i2c.c"
)
idf_component_register(SRCS ${srcs}
WHOLE_ARCHIVE)
@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "unity_test_utils_memory.h"
#include "esp_heap_caps.h"
// Some resources are lazy allocated in I2C driver, so we reserved this threshold when checking memory leak
// A better way to check a potential memory leak is running a same case by twice, for the second time, the memory usage delta should be zero
#define LEAKS (400)
void setUp(void)
{
unity_utils_record_free_mem();
}
void tearDown(void)
{
unity_utils_evaluate_leaks_direct(LEAKS);
}
void app_main(void)
{
// ___ ____ ____ _____ _
// |_ _|___ \ / ___| |_ _|__ ___| |_
// | | __) | | | |/ _ \/ __| __|
// | | / __/| |___ | | __/\__ \ |_
// |___|_____|\____| |_|\___||___/\__|
printf(" ___ ____ ____ _____ _ \n");
printf("|_ _|___ \\ / ___| |_ _|__ ___| |_ \n");
printf(" | | __) | | | |/ _ \\/ __| __|\n");
printf(" | | / __/| |___ | | __/\\__ \\ |_ \n");
printf("|___|_____|\\____| |_|\\___||___/\\__|\n");
unity_run_menu();
}
@@ -15,7 +15,7 @@ from pytest_embedded import Dut
],
indirect=True,
)
def test_i2c(dut: Dut) -> None:
def test_i2c_legacy(dut: Dut) -> None:
dut.run_all_single_board_cases()
@@ -33,7 +33,7 @@ def test_i2c(dut: Dut) -> None:
],
indirect=True
)
def test_i2c_multi_dev(case_tester) -> None: # type: ignore
def test_i2c_multi_dev_legacy(case_tester) -> None: # type: ignore
for case in case_tester.test_menu:
if case.attributes.get('test_env', 'generic_multi_device') == 'generic_multi_device':
case_tester.run_multi_dev_case(case=case, reset=True)
@@ -0,0 +1,2 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n
@@ -0,0 +1,5 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
@@ -0,0 +1,2 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n