From 992d8bc5f20556adb62571d55a1517a9848d4b51 Mon Sep 17 00:00:00 2001 From: "C.S.M" Date: Wed, 19 Jun 2024 11:47:14 +0800 Subject: [PATCH] feat(i2c_master): Add an api for retrieveing handle via port, Closes https://github.com/espressif/esp-idf/issues/13968 --- components/esp_driver_i2c/i2c_common.c | 2 +- components/esp_driver_i2c/i2c_master.c | 22 ++++++++++++ components/esp_driver_i2c/i2c_private.h | 8 +++++ .../include/esp_private/i2c_platform.h | 36 +++++++++++++++++++ .../i2c_test_apps/main/test_i2c_common.c | 17 +++++++++ docs/en/api-reference/peripherals/i2c.rst | 21 +++++++++++ 6 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 components/esp_driver_i2c/include/esp_private/i2c_platform.h diff --git a/components/esp_driver_i2c/i2c_common.c b/components/esp_driver_i2c/i2c_common.c index 8c3f86952e..19af0a2977 100644 --- a/components/esp_driver_i2c/i2c_common.c +++ b/components/esp_driver_i2c/i2c_common.c @@ -127,7 +127,7 @@ static esp_err_t s_i2c_bus_handle_acquire(i2c_port_num_t port_num, i2c_bus_handl return ret; } -static bool i2c_bus_occupied(i2c_port_num_t port_num) +bool i2c_bus_occupied(i2c_port_num_t port_num) { return s_i2c_platform.buses[port_num] != NULL; } diff --git a/components/esp_driver_i2c/i2c_master.c b/components/esp_driver_i2c/i2c_master.c index 66d2454a0d..68fdd60cbe 100644 --- a/components/esp_driver_i2c/i2c_master.c +++ b/components/esp_driver_i2c/i2c_master.c @@ -46,6 +46,15 @@ static const char *TAG = "i2c.master"; #define I2C_FIFO_LEN(port_num) (SOC_I2C_FIFO_LEN) #endif +// Use the platform to same master bus handle +typedef struct i2c_master_bus_platform_t i2c_master_bus_platform_t; + +struct i2c_master_bus_platform_t { + i2c_master_bus_handle_t handle[SOC_I2C_NUM]; +}; + +static i2c_master_bus_platform_t s_platform; + static esp_err_t s_i2c_master_clear_bus(i2c_bus_handle_t handle) { #if !SOC_I2C_SUPPORT_HW_CLR_BUS @@ -989,6 +998,7 @@ esp_err_t i2c_new_master_bus(const i2c_master_bus_config_t *bus_config, i2c_mast xSemaphoreGive(i2c_master->cmd_semphr); *ret_bus_handle = i2c_master; + s_platform.handle[i2c_port_num] = i2c_master; return ESP_OK; err: @@ -1075,6 +1085,18 @@ esp_err_t i2c_master_bus_reset(i2c_master_bus_handle_t bus_handle) return ESP_OK; } +esp_err_t i2c_master_get_bus_handle(i2c_port_num_t port_num, i2c_master_bus_handle_t *ret_handle) +{ + ESP_RETURN_ON_FALSE((port_num < SOC_I2C_NUM), ESP_ERR_INVALID_ARG, TAG, "invalid i2c port number"); + if (i2c_bus_occupied(port_num) == false) { + ESP_LOGE(TAG, "this port has not been initialized, please initialize it first"); + return ESP_ERR_INVALID_STATE; + } else { + *ret_handle = s_platform.handle[port_num]; + } + 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"); diff --git a/components/esp_driver_i2c/i2c_private.h b/components/esp_driver_i2c/i2c_private.h index ae33a26ea6..a78dfdd067 100644 --- a/components/esp_driver_i2c/i2c_private.h +++ b/components/esp_driver_i2c/i2c_private.h @@ -252,6 +252,14 @@ esp_err_t i2c_select_periph_clock(i2c_bus_handle_t handle, soc_module_clk_t clk_ */ esp_err_t i2c_common_set_pins(i2c_bus_handle_t handle); +/** + * @brief Check whether bus is acquired + * + * @param port_num number of port + * @return true if the bus is occupied, false if the bus is not occupied. +*/ +bool i2c_bus_occupied(i2c_port_num_t port_num); + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_i2c/include/esp_private/i2c_platform.h b/components/esp_driver_i2c/include/esp_private/i2c_platform.h new file mode 100644 index 0000000000..f18cf53c99 --- /dev/null +++ b/components/esp_driver_i2c/include/esp_private/i2c_platform.h @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "driver/i2c_types.h" +#include "hal/gpio_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Retrieves the I2C master bus handle for a specified I2C port number. + * + * This function retrieves the I2C master bus handle for the + * given I2C port number. Please make sure the handle has already been initialized, and this + * function would simply returns the existing handle. Note that the returned handle still can't be used concurrently + * + * @param port_num I2C port number for which the handle is to be retrieved. + * @param ret_handle Pointer to a variable where the retrieved handle will be stored. + * @return + * - ESP_OK: Success. The handle is retrieved successfully. + * - ESP_ERR_INVALID_ARG: Invalid argument, such as invalid port number + * - ESP_ERR_INVALID_STATE: Invalid state, such as the I2C port is not initialized. + */ +esp_err_t i2c_master_get_bus_handle(i2c_port_num_t port_num, i2c_master_bus_handle_t *ret_handle); + +#ifdef __cplusplus +} +#endif 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 45ec47b06b..91296a6eed 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 @@ -19,6 +19,7 @@ #include "esp_private/periph_ctrl.h" #include "driver/gpio.h" #include "driver/i2c_master.h" +#include "esp_private/i2c_platform.h" #include "esp_rom_gpio.h" #include "esp_log.h" #include "test_utils.h" @@ -345,3 +346,19 @@ TEST_CASE("I2C master transaction receive check nack return value", "[i2c]") TEST_ESP_ERR(ESP_ERR_INVALID_STATE, i2c_master_receive(dev_handle, data_rd, DATA_LENGTH, -1)); _test_i2c_del_bus_device(bus_handle, dev_handle); } + +TEST_CASE("Test get handle with known port", "[i2c]") +{ + i2c_master_bus_handle_t handle; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, i2c_master_get_bus_handle(10, &handle)); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, i2c_master_get_bus_handle(0, &handle)); + + i2c_master_bus_handle_t bus_handle; + i2c_master_dev_handle_t dev_handle; + _test_i2c_new_bus_device(&bus_handle, &dev_handle); + TEST_ESP_OK(i2c_master_get_bus_handle(0, &handle)); + + // Check the handle retrieved is as same as original handle + TEST_ASSERT((uint32_t)bus_handle == (uint32_t)handle); + _test_i2c_del_bus_device(bus_handle, dev_handle); +} diff --git a/docs/en/api-reference/peripherals/i2c.rst b/docs/en/api-reference/peripherals/i2c.rst index c1084ffeb4..c0918c7dc7 100644 --- a/docs/en/api-reference/peripherals/i2c.rst +++ b/docs/en/api-reference/peripherals/i2c.rst @@ -150,6 +150,27 @@ Once the :cpp:type:`i2c_device_config_t` structure is populated with mandatory p i2c_master_dev_handle_t dev_handle; ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle)); +Get I2C master handle via port +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Given that i2c master handle has been initialized in some module(e.g. an audio module), an another module(e.g. a video module) is not convenient to reused the handle. We have a helper function, :cpp:func:`i2c_master_get_bus_handle` for getting the initialized handle via port. However, please make sure the handle has already been initialized ahead of time. Otherwise an error would be reported. + +.. code:: c + + // Source File 1 + #include "driver/i2c_master.h" + i2c_master_bus_handle_t bus_handle; + i2c_master_bus_config_t i2c_mst_config = { + ... // same as others + }; + ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle)); + + // Source File 2 + #include "esp_private/i2c_platform.h" + #include "driver/i2c_master.h" + i2c_master_bus_handle_t handle; + ESP_ERROR_CHECK(i2c_master_get_bus_handle(0, &handle)); + .. only:: SOC_LP_I2C_SUPPORTED Install I2C master bus with LP I2C Peripheral