From 99a7f90874e0c0502ba218dc596f71975bd8c168 Mon Sep 17 00:00:00 2001 From: Tomoyuki Sakurai Date: Sun, 15 Sep 2024 14:41:35 +0700 Subject: [PATCH] feat: support in i2cdev --- components/i2cdev/CMakeLists.txt | 9 +- components/i2cdev/Kconfig | 16 +- components/i2cdev/i2cdev.c | 216 +------------------------ components/i2cdev/i2cdev.h | 27 +++- components/i2cdev/i2cdev_legacy.c | 205 +++++++++++++++++++++++ components/i2cdev/i2cdev_legacy.h | 17 ++ components/i2cdev/i2cdev_lock.h | 39 +++++ components/i2cdev/i2cdev_master.c | 197 ++++++++++++++++++++++ components/i2cdev/i2cdev_master.h | 17 ++ examples/i2cdev/default/CMakeLists.txt | 1 + examples/i2cdev/default/main/main.c | 13 +- 11 files changed, 537 insertions(+), 220 deletions(-) create mode 100644 components/i2cdev/i2cdev_legacy.c create mode 100644 components/i2cdev/i2cdev_legacy.h create mode 100644 components/i2cdev/i2cdev_lock.h create mode 100644 components/i2cdev/i2cdev_master.c create mode 100644 components/i2cdev/i2cdev_master.h diff --git a/components/i2cdev/CMakeLists.txt b/components/i2cdev/CMakeLists.txt index 585cfb20..112fccc2 100644 --- a/components/i2cdev/CMakeLists.txt +++ b/components/i2cdev/CMakeLists.txt @@ -4,8 +4,15 @@ else() set(req driver freertos esp_idf_lib_helpers) endif() +set(srcs "i2cdev.c") +if(CONFIG_I2CDEV_USING_LEGACY_I2C) + list(APPEND srcs "i2cdev_legacy.c") +else() + list(APPEND srcs "i2cdev_master.c") +endif() + idf_component_register( - SRCS i2cdev.c + SRCS "${srcs}" INCLUDE_DIRS . REQUIRES ${req} ) diff --git a/components/i2cdev/Kconfig b/components/i2cdev/Kconfig index aaff31d3..1aaf6be9 100644 --- a/components/i2cdev/Kconfig +++ b/components/i2cdev/Kconfig @@ -4,14 +4,20 @@ config I2CDEV_TIMEOUT int "I2C transaction timeout, milliseconds" default 1000 range 10 5000 - + config I2CDEV_NOLOCK bool "Disable the use of mutexes" default n help Attention! After enabling this option, all I2C device - drivers will become non-thread safe. + drivers will become non-thread safe. Use this option if you need to access your I2C devices - from interrupt handlers. - -endmenu \ No newline at end of file + from interrupt handlers. + +config I2CDEV_USING_LEGACY_I2C + bool "Use legacy driver/i2c.h" + default y + help + Use the legacy "driver/i2c.h" instead of "driver/i2c_master.h" + introduced in esp-idf 5.2. +endmenu diff --git a/components/i2cdev/i2cdev.c b/components/i2cdev/i2cdev.c index 7f1bba28..c132a15c 100644 --- a/components/i2cdev/i2cdev.c +++ b/components/i2cdev/i2cdev.c @@ -37,78 +37,19 @@ #include #include "i2cdev.h" -static const char *TAG = "i2cdev"; - -typedef struct { - SemaphoreHandle_t lock; - i2c_config_t config; - bool installed; -} i2c_port_state_t; - -static i2c_port_state_t states[I2C_NUM_MAX]; - -#if CONFIG_I2CDEV_NOLOCK -#define SEMAPHORE_TAKE(port) -#else -#define SEMAPHORE_TAKE(port) do { \ - if (!xSemaphoreTake(states[port].lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT))) \ - { \ - ESP_LOGE(TAG, "Could not take port mutex %d", port); \ - return ESP_ERR_TIMEOUT; \ - } \ - } while (0) -#endif +#include "i2cdev_legacy.h" +// #include "i2cdev_master.h" -#if CONFIG_I2CDEV_NOLOCK -#define SEMAPHORE_GIVE(port) -#else -#define SEMAPHORE_GIVE(port) do { \ - if (!xSemaphoreGive(states[port].lock)) \ - { \ - ESP_LOGE(TAG, "Could not give port mutex %d", port); \ - return ESP_FAIL; \ - } \ - } while (0) -#endif +static const char *TAG = "i2cdev"; esp_err_t i2cdev_init() { - memset(states, 0, sizeof(states)); - -#if !CONFIG_I2CDEV_NOLOCK - for (int i = 0; i < I2C_NUM_MAX; i++) - { - states[i].lock = xSemaphoreCreateMutex(); - if (!states[i].lock) - { - ESP_LOGE(TAG, "Could not create port mutex %d", i); - return ESP_FAIL; - } - } -#endif - - return ESP_OK; + return _i2cdev_init(); } esp_err_t i2cdev_done() { - for (int i = 0; i < I2C_NUM_MAX; i++) - { - if (!states[i].lock) continue; - - if (states[i].installed) - { - SEMAPHORE_TAKE(i); - i2c_driver_delete(i); - states[i].installed = false; - SEMAPHORE_GIVE(i); - } -#if !CONFIG_I2CDEV_NOLOCK - vSemaphoreDelete(states[i].lock); -#endif - states[i].lock = NULL; - } - return ESP_OK; + return _i2cdev_done(); } esp_err_t i2c_dev_create_mutex(i2c_dev_t *dev) @@ -173,160 +114,19 @@ esp_err_t i2c_dev_give_mutex(i2c_dev_t *dev) return ESP_OK; } -inline static bool cfg_equal(const i2c_config_t *a, const i2c_config_t *b) -{ - return a->scl_io_num == b->scl_io_num - && a->sda_io_num == b->sda_io_num -#if HELPER_TARGET_IS_ESP32 - && a->master.clk_speed == b->master.clk_speed -#elif HELPER_TARGET_IS_ESP8266 - && ((a->clk_stretch_tick && a->clk_stretch_tick == b->clk_stretch_tick) - || (!a->clk_stretch_tick && b->clk_stretch_tick == I2CDEV_MAX_STRETCH_TIME) - ) // see line 232 -#endif - && a->scl_pullup_en == b->scl_pullup_en - && a->sda_pullup_en == b->sda_pullup_en; -} - -static esp_err_t i2c_setup_port(const i2c_dev_t *dev) -{ - if (dev->port >= I2C_NUM_MAX) return ESP_ERR_INVALID_ARG; - - esp_err_t res; - if (!cfg_equal(&dev->cfg, &states[dev->port].config) || !states[dev->port].installed) - { - ESP_LOGD(TAG, "Reconfiguring I2C driver on port %d", dev->port); - i2c_config_t temp; - memcpy(&temp, &dev->cfg, sizeof(i2c_config_t)); - temp.mode = I2C_MODE_MASTER; - - // Driver reinstallation - if (states[dev->port].installed) - { - i2c_driver_delete(dev->port); - states[dev->port].installed = false; - } -#if HELPER_TARGET_IS_ESP32 -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) - // See https://github.com/espressif/esp-idf/issues/10163 - if ((res = i2c_driver_install(dev->port, temp.mode, 0, 0, 0)) != ESP_OK) - return res; - if ((res = i2c_param_config(dev->port, &temp)) != ESP_OK) - return res; -#else - if ((res = i2c_param_config(dev->port, &temp)) != ESP_OK) - return res; - if ((res = i2c_driver_install(dev->port, temp.mode, 0, 0, 0)) != ESP_OK) - return res; -#endif -#endif -#if HELPER_TARGET_IS_ESP8266 - // Clock Stretch time, depending on CPU frequency - temp.clk_stretch_tick = dev->timeout_ticks ? dev->timeout_ticks : I2CDEV_MAX_STRETCH_TIME; - if ((res = i2c_driver_install(dev->port, temp.mode)) != ESP_OK) - return res; - if ((res = i2c_param_config(dev->port, &temp)) != ESP_OK) - return res; -#endif - states[dev->port].installed = true; - - memcpy(&states[dev->port].config, &temp, sizeof(i2c_config_t)); - ESP_LOGD(TAG, "I2C driver successfully reconfigured on port %d", dev->port); - } -#if HELPER_TARGET_IS_ESP32 - int t; - if ((res = i2c_get_timeout(dev->port, &t)) != ESP_OK) - return res; - // Timeout cannot be 0 - uint32_t ticks = dev->timeout_ticks ? dev->timeout_ticks : I2CDEV_MAX_STRETCH_TIME; - if ((ticks != t) && (res = i2c_set_timeout(dev->port, ticks)) != ESP_OK) - return res; - ESP_LOGD(TAG, "Timeout: ticks = %" PRIu32 " (%" PRIu32 " usec) on port %d", dev->timeout_ticks, dev->timeout_ticks / 80, dev->port); -#endif - - return ESP_OK; -} - esp_err_t i2c_dev_probe(const i2c_dev_t *dev, i2c_dev_type_t operation_type) { - if (!dev) return ESP_ERR_INVALID_ARG; - - SEMAPHORE_TAKE(dev->port); - - esp_err_t res = i2c_setup_port(dev); - if (res == ESP_OK) - { - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, dev->addr << 1 | (operation_type == I2C_DEV_READ ? 1 : 0), true); - i2c_master_stop(cmd); - - res = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)); - - i2c_cmd_link_delete(cmd); - } - - SEMAPHORE_GIVE(dev->port); - - return res; + return _i2c_dev_probe(dev, operation_type); } esp_err_t i2c_dev_read(const i2c_dev_t *dev, const void *out_data, size_t out_size, void *in_data, size_t in_size) { - if (!dev || !in_data || !in_size) return ESP_ERR_INVALID_ARG; - - SEMAPHORE_TAKE(dev->port); - - esp_err_t res = i2c_setup_port(dev); - if (res == ESP_OK) - { - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - if (out_data && out_size) - { - i2c_master_start(cmd); - i2c_master_write_byte(cmd, dev->addr << 1, true); - i2c_master_write(cmd, (void *)out_data, out_size, true); - } - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (dev->addr << 1) | 1, true); - i2c_master_read(cmd, in_data, in_size, I2C_MASTER_LAST_NACK); - i2c_master_stop(cmd); - - res = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)); - if (res != ESP_OK) - ESP_LOGE(TAG, "Could not read from device [0x%02x at %d]: %d (%s)", dev->addr, dev->port, res, esp_err_to_name(res)); - - i2c_cmd_link_delete(cmd); - } - - SEMAPHORE_GIVE(dev->port); - return res; + return _i2c_dev_read(dev, out_data, out_size, in_data, in_size); } esp_err_t i2c_dev_write(const i2c_dev_t *dev, const void *out_reg, size_t out_reg_size, const void *out_data, size_t out_size) { - if (!dev || !out_data || !out_size) return ESP_ERR_INVALID_ARG; - - SEMAPHORE_TAKE(dev->port); - - esp_err_t res = i2c_setup_port(dev); - if (res == ESP_OK) - { - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, dev->addr << 1, true); - if (out_reg && out_reg_size) - i2c_master_write(cmd, (void *)out_reg, out_reg_size, true); - i2c_master_write(cmd, (void *)out_data, out_size, true); - i2c_master_stop(cmd); - res = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)); - if (res != ESP_OK) - ESP_LOGE(TAG, "Could not write to device [0x%02x at %d]: %d (%s)", dev->addr, dev->port, res, esp_err_to_name(res)); - i2c_cmd_link_delete(cmd); - } - - SEMAPHORE_GIVE(dev->port); - return res; + return _i2c_dev_write(dev, out_reg, out_reg_size, out_data, out_size); } esp_err_t i2c_dev_read_reg(const i2c_dev_t *dev, uint8_t reg, void *in_data, size_t in_size) diff --git a/components/i2cdev/i2cdev.h b/components/i2cdev/i2cdev.h index ce972a6b..5b83ec1a 100644 --- a/components/i2cdev/i2cdev.h +++ b/components/i2cdev/i2cdev.h @@ -35,12 +35,17 @@ #ifndef __I2CDEV_H__ #define __I2CDEV_H__ -#include #include #include #include #include +#if CONFIG_I2CDEV_USING_LEGACY_I2C +#include +#else +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -68,12 +73,23 @@ extern "C" { typedef struct { i2c_port_t port; //!< I2C port number - i2c_config_t cfg; //!< I2C driver configuration - uint8_t addr; //!< Unshifted address - SemaphoreHandle_t mutex; //!< Device mutex +#if defined(CONFIG_I2CDEV_USING_LEGACY_I2C) + i2c_config_t cfg; //!< I2C driver configuration (i2c.h only) uint32_t timeout_ticks; /*!< HW I2C bus timeout (stretch time), in ticks. 80MHz APB clock ticks for ESP-IDF, CPU ticks for ESP8266. - When this value is 0, I2CDEV_MAX_STRETCH_TIME will be used */ + When this value is 0, I2CDEV_MAX_STRETCH_TIME will be used + (i2c.h only) */ +#else + i2c_master_bus_handle_t bus; //!< I2C master bus handle (i2c_master.h only) + i2c_master_dev_handle_t device; //!< I2C master bus device handle + //(i2c_master.h only) + i2c_device_config_t device_config; //!< I2C device configuration (i2c_master.h only) + i2c_master_bus_config_t master_bus_config; /*! < I2C master bus specific + configurations (i2c_master.h + only) */ +#endif + uint8_t addr; //!< Unshifted address + SemaphoreHandle_t mutex; //!< Device mutex } i2c_dev_t; /** @@ -97,6 +113,7 @@ esp_err_t i2cdev_init(); /** * @brief Finish work with library * + * Delete Deinitialize the I2C master bus. When the legacy i2c.h is used, * Uninstall i2c drivers. * * @return ESP_OK on success diff --git a/components/i2cdev/i2cdev_legacy.c b/components/i2cdev/i2cdev_legacy.c new file mode 100644 index 00000000..50530dc0 --- /dev/null +++ b/components/i2cdev/i2cdev_legacy.c @@ -0,0 +1,205 @@ +#include +#include + +#include "i2cdev_legacy.h" +#include "i2cdev_lock.h" + +static const char *TAG = "i2cdev"; + +static i2c_port_state_t states[I2C_NUM_MAX]; + +inline static bool cfg_equal(const i2c_config_t *a, const i2c_config_t *b) +{ + return a->scl_io_num == b->scl_io_num + && a->sda_io_num == b->sda_io_num +#if HELPER_TARGET_IS_ESP32 + && a->master.clk_speed == b->master.clk_speed +#elif HELPER_TARGET_IS_ESP8266 + && ((a->clk_stretch_tick && a->clk_stretch_tick == b->clk_stretch_tick) + || (!a->clk_stretch_tick && b->clk_stretch_tick == I2CDEV_MAX_STRETCH_TIME) + ) // see line 232 +#endif + && a->scl_pullup_en == b->scl_pullup_en + && a->sda_pullup_en == b->sda_pullup_en; +} + +static esp_err_t i2c_setup_port(const i2c_dev_t *dev) +{ + if (dev->port >= I2C_NUM_MAX) return ESP_ERR_INVALID_ARG; + + esp_err_t res; + if (!cfg_equal(&dev->cfg, &states[dev->port].config) || !states[dev->port].installed) + { + ESP_LOGD(TAG, "Reconfiguring I2C driver on port %d", dev->port); + i2c_config_t temp; + memcpy(&temp, &dev->cfg, sizeof(i2c_config_t)); + temp.mode = I2C_MODE_MASTER; + + // Driver reinstallation + if (states[dev->port].installed) + { + i2c_driver_delete(dev->port); + states[dev->port].installed = false; + } +#if HELPER_TARGET_IS_ESP32 +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) + // See https://github.com/espressif/esp-idf/issues/10163 + if ((res = i2c_driver_install(dev->port, temp.mode, 0, 0, 0)) != ESP_OK) + return res; + if ((res = i2c_param_config(dev->port, &temp)) != ESP_OK) + return res; +#else + if ((res = i2c_param_config(dev->port, &temp)) != ESP_OK) + return res; + if ((res = i2c_driver_install(dev->port, temp.mode, 0, 0, 0)) != ESP_OK) + return res; +#endif +#endif +#if HELPER_TARGET_IS_ESP8266 + // Clock Stretch time, depending on CPU frequency + temp.clk_stretch_tick = dev->timeout_ticks ? dev->timeout_ticks : I2CDEV_MAX_STRETCH_TIME; + if ((res = i2c_driver_install(dev->port, temp.mode)) != ESP_OK) + return res; + if ((res = i2c_param_config(dev->port, &temp)) != ESP_OK) + return res; +#endif + states[dev->port].installed = true; + + memcpy(&states[dev->port].config, &temp, sizeof(i2c_config_t)); + ESP_LOGD(TAG, "I2C driver successfully reconfigured on port %d", dev->port); + } +#if HELPER_TARGET_IS_ESP32 + int t; + if ((res = i2c_get_timeout(dev->port, &t)) != ESP_OK) + return res; + // Timeout cannot be 0 + uint32_t ticks = dev->timeout_ticks ? dev->timeout_ticks : I2CDEV_MAX_STRETCH_TIME; + if ((ticks != t) && (res = i2c_set_timeout(dev->port, ticks)) != ESP_OK) + return res; + ESP_LOGD(TAG, "Timeout: ticks = %" PRIu32 " (%" PRIu32 " usec) on port %d", dev->timeout_ticks, dev->timeout_ticks / 80, dev->port); +#endif + + return ESP_OK; +} + +esp_err_t _i2cdev_init() +{ + memset(states, 0, sizeof(states)); + +#if !CONFIG_I2CDEV_NOLOCK + for (int i = 0; i < I2C_NUM_MAX; i++) + { + states[i].lock = xSemaphoreCreateMutex(); + if (!states[i].lock) + { + ESP_LOGE(TAG, "Could not create port mutex %d", i); + return ESP_FAIL; + } + } +#endif + + return ESP_OK; +} + +esp_err_t _i2cdev_done() +{ + for (int i = 0; i < I2C_NUM_MAX; i++) + { + if (!states[i].lock) continue; + + if (states[i].installed) + { + SEMAPHORE_TAKE(i); + i2c_driver_delete(i); + states[i].installed = false; + SEMAPHORE_GIVE(i); + } +#if !CONFIG_I2CDEV_NOLOCK + vSemaphoreDelete(states[i].lock); +#endif + states[i].lock = NULL; + } + return ESP_OK; +} + +esp_err_t _i2c_dev_probe(const i2c_dev_t *dev, i2c_dev_type_t operation_type) +{ + if (!dev) return ESP_ERR_INVALID_ARG; + + SEMAPHORE_TAKE(dev->port); + + esp_err_t res = i2c_setup_port(dev); + if (res == ESP_OK) + { + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, dev->addr << 1 | (operation_type == I2C_DEV_READ ? 1 : 0), true); + i2c_master_stop(cmd); + + res = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)); + + i2c_cmd_link_delete(cmd); + } + + SEMAPHORE_GIVE(dev->port); + + return res; +} + +esp_err_t _i2c_dev_read(const i2c_dev_t *dev, const void *out_data, size_t out_size, void *in_data, size_t in_size) +{ + if (!dev || !in_data || !in_size) return ESP_ERR_INVALID_ARG; + + SEMAPHORE_TAKE(dev->port); + + esp_err_t res = i2c_setup_port(dev); + if (res == ESP_OK) + { + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + if (out_data && out_size) + { + i2c_master_start(cmd); + i2c_master_write_byte(cmd, dev->addr << 1, true); + i2c_master_write(cmd, (void *)out_data, out_size, true); + } + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (dev->addr << 1) | 1, true); + i2c_master_read(cmd, in_data, in_size, I2C_MASTER_LAST_NACK); + i2c_master_stop(cmd); + + res = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)); + if (res != ESP_OK) + ESP_LOGE(TAG, "Could not read from device [0x%02x at %d]: %d (%s)", dev->addr, dev->port, res, esp_err_to_name(res)); + + i2c_cmd_link_delete(cmd); + } + + SEMAPHORE_GIVE(dev->port); + return res; +} + +esp_err_t _i2c_dev_write(const i2c_dev_t *dev, const void *out_reg, size_t out_reg_size, const void *out_data, size_t out_size) +{ + if (!dev || !out_data || !out_size) return ESP_ERR_INVALID_ARG; + + SEMAPHORE_TAKE(dev->port); + + esp_err_t res = i2c_setup_port(dev); + if (res == ESP_OK) + { + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, dev->addr << 1, true); + if (out_reg && out_reg_size) + i2c_master_write(cmd, (void *)out_reg, out_reg_size, true); + i2c_master_write(cmd, (void *)out_data, out_size, true); + i2c_master_stop(cmd); + res = i2c_master_cmd_begin(dev->port, cmd, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT)); + if (res != ESP_OK) + ESP_LOGE(TAG, "Could not write to device [0x%02x at %d]: %d (%s)", dev->addr, dev->port, res, esp_err_to_name(res)); + i2c_cmd_link_delete(cmd); + } + + SEMAPHORE_GIVE(dev->port); + return res; +} diff --git a/components/i2cdev/i2cdev_legacy.h b/components/i2cdev/i2cdev_legacy.h new file mode 100644 index 00000000..9ba0c571 --- /dev/null +++ b/components/i2cdev/i2cdev_legacy.h @@ -0,0 +1,17 @@ +#if !defined __I2CDEV_LEGACY_H__ +#define __I2CDEV_LEGACY_H__ + +#include +#include "i2cdev.h" + +esp_err_t _i2cdev_init(); + +esp_err_t _i2cdev_done(); + +esp_err_t _i2c_dev_probe(); + +esp_err_t _i2c_dev_read(const i2c_dev_t *dev, const void *out_data, size_t out_size, void *in_data, size_t in_size); + +esp_err_t _i2c_dev_write(const i2c_dev_t *dev, const void *out_reg, size_t out_reg_size, const void *out_data, size_t out_size); + +#endif // __I2CDEV_LEGACY_H__ diff --git a/components/i2cdev/i2cdev_lock.h b/components/i2cdev/i2cdev_lock.h new file mode 100644 index 00000000..ebb45254 --- /dev/null +++ b/components/i2cdev/i2cdev_lock.h @@ -0,0 +1,39 @@ +#if !defined(__I2CDEV_LOCK_H__) +#define __I2CDEV_LOCK_H__ + +#if CONFIG_I2CDEV_NOLOCK +#define SEMAPHORE_GIVE(port) +#else +#define SEMAPHORE_GIVE(port) do { \ + if (!xSemaphoreGive(states[port].lock)) \ + { \ + ESP_LOGE(TAG, "Could not give port mutex %d", port); \ + return ESP_FAIL; \ + } \ + } while (0) +#endif + +#if CONFIG_I2CDEV_NOLOCK +#define SEMAPHORE_TAKE(port) +#else +#define SEMAPHORE_TAKE(port) do { \ + if (!xSemaphoreTake(states[port].lock, pdMS_TO_TICKS(CONFIG_I2CDEV_TIMEOUT))) \ + { \ + ESP_LOGE(TAG, "Could not take port mutex %d", port); \ + return ESP_ERR_TIMEOUT; \ + } \ + } while (0) +#endif + +typedef struct { + SemaphoreHandle_t lock; +#if defined(CONFIG_I2CDEV_USING_LEGACY_I2C) + i2c_config_t config; +#else + i2c_master_bus_config_t config; + i2c_master_bus_handle_t bus; +#endif + bool installed; +} i2c_port_state_t; + +#endif diff --git a/components/i2cdev/i2cdev_master.c b/components/i2cdev/i2cdev_master.c new file mode 100644 index 00000000..d895a970 --- /dev/null +++ b/components/i2cdev/i2cdev_master.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include "i2cdev.h" +#include "i2cdev_lock.h" + +#define PROBE_XFER_TIMEOUT_MS (100) + +static const char *TAG = "i2cdev"; + +static i2c_port_state_t states[I2C_NUM_MAX]; + +inline static bool cfg_equal(const i2c_master_bus_config_t *a, const i2c_master_bus_config_t *b) +{ + return a->scl_io_num == b->scl_io_num + && a->sda_io_num == b->sda_io_num + && a->flags.enable_internal_pullup == b->flags.enable_internal_pullup; +} + +static esp_err_t i2c_setup_device(i2c_dev_t *dev) +{ + return i2c_master_bus_add_device(dev->bus, &dev->device_config, &dev->device); +} + +static esp_err_t i2c_setup_port(i2c_dev_t *dev) +{ + if (dev->port >= I2C_NUM_MAX) return ESP_ERR_INVALID_ARG; + + esp_err_t res; + if (!cfg_equal(&dev->master_bus_config, &states[dev->port].config) || !states[dev->port].installed) + { + ESP_LOGD(TAG, "Reconfiguring I2C driver on port %d", dev->port); + i2c_master_bus_config_t temp; + memcpy(&temp, &dev->master_bus_config, sizeof(i2c_master_bus_config_t)); + + // Driver reinstallation + if (states[dev->port].installed) + { + if (!dev->bus) + { + ESP_LOGW(TAG, "The state of I2C port %d is `installed`, but bus handle is NULL)", dev->port); + } + else + { + i2c_del_master_bus(dev->bus); + } + states[dev->port].installed = false; + } + if ((res = i2c_new_master_bus(&dev->master_bus_config, &dev->bus)) != ESP_OK) + return res; + states[dev->port].bus = dev->bus; + states[dev->port].installed = true; + + memcpy(&states[dev->port].config, &temp, sizeof(i2c_master_bus_config_t)); + ESP_LOGD(TAG, "I2C driver successfully reconfigured on port %d", dev->port); + } + return i2c_setup_device(dev); +} + +esp_err_t _i2cdev_init() +{ + memset(states, 0, sizeof(states)); + +#if !CONFIG_I2CDEV_NOLOCK + for (int i = 0; i < I2C_NUM_MAX; i++) + { + states[i].lock = xSemaphoreCreateMutex(); + if (!states[i].lock) + { + ESP_LOGE(TAG, "Could not create port mutex %d", i); + return ESP_FAIL; + } + } +#endif + + return ESP_OK; +} + +esp_err_t _i2cdev_done() +{ + for (int i = 0; i < I2C_NUM_MAX; i++) + { + if (!states[i].lock) continue; + + if (states[i].installed) + { + SEMAPHORE_TAKE(i); + if (!states[i].bus) + { + ESP_LOGW(TAG, "The state of I2C port %d is `installed`, but bus handle is NULL)", i); + } + else + { + i2c_del_master_bus(states[i].bus); + } + states[i].installed = false; + SEMAPHORE_GIVE(i); + } +#if !CONFIG_I2CDEV_NOLOCK + vSemaphoreDelete(states[i].lock); +#endif + states[i].lock = NULL; + } + return ESP_OK; +} + +esp_err_t _i2c_dev_probe(i2c_dev_t *dev, i2c_dev_type_t operation_type) +{ + if (!dev) return ESP_ERR_INVALID_ARG; + + SEMAPHORE_TAKE(dev->port); + + esp_err_t res = i2c_setup_port(dev); + if (res == ESP_OK) + { + res = i2c_master_probe(dev->bus, dev->device_config.device_address, CONFIG_I2CDEV_TIMEOUT); + } + + SEMAPHORE_GIVE(dev->port); + + return res; +} + +esp_err_t _i2c_dev_read(i2c_dev_t *dev, const void *out_data, size_t out_size, void *in_data, size_t in_size) +{ + if (!dev || !in_data || !in_size) return ESP_ERR_INVALID_ARG; + + SEMAPHORE_TAKE(dev->port); + + esp_err_t res = i2c_setup_port(dev); + if (res == ESP_OK) + { + + if (out_data && out_size) + { + res = i2c_master_transmit_receive(dev->device, (void *)out_data, out_size, in_data, in_size, CONFIG_I2CDEV_TIMEOUT); + } + else + { + res = i2c_master_receive(dev->device, (void *)out_data, out_size, CONFIG_I2CDEV_TIMEOUT); + } + if (res != ESP_OK) + { + ESP_LOGE(TAG, "Could not read from device [0x%02x at %d]: %d (%s)", dev->addr, dev->port, res, esp_err_to_name(res)); + } + } + + SEMAPHORE_GIVE(dev->port); + return res; +} + +esp_err_t _i2c_dev_write(i2c_dev_t *dev, const void *out_reg, size_t out_reg_size, const void *out_data, size_t out_size) +{ + if (!dev || !out_data || !out_size) return ESP_ERR_INVALID_ARG; + uint8_t *write_buffer = NULL; + size_t write_buffer_size = 0; + esp_err_t res = ESP_FAIL; + bool with_register_address = out_reg && out_reg_size; + + SEMAPHORE_TAKE(dev->port); + + write_buffer_size = with_register_address ? out_reg_size + out_size : out_size; + write_buffer = malloc(write_buffer_size); + if (!write_buffer) + { + ESP_LOGE(TAG, "_i2c_dev_write: malloc() failed: size %d", write_buffer_size); + res = ESP_ERR_NO_MEM; + goto fail; + } + res = i2c_setup_port(dev); + if (res != ESP_OK) + { + ESP_LOGE(TAG, "i2c_setup_port(): %s", esp_err_to_name(res)); + goto fail; + } + + if (with_register_address) + { + memcpy(write_buffer, out_reg, out_reg_size); + memcpy(write_buffer + out_reg_size, out_data, out_size); + + } + else + { + memcpy(write_buffer, out_data, out_size); + } + res = i2c_master_transmit(dev->device, write_buffer, out_reg_size + out_size, CONFIG_I2CDEV_TIMEOUT); + if (res != ESP_OK) + { + ESP_LOGE(TAG, "Could not write to device [0x%02x at %d]: %d (%s)", dev->addr, dev->port, res, esp_err_to_name(res)); + goto fail; + } +fail: + free(write_buffer); + SEMAPHORE_GIVE(dev->port); + return res; +} diff --git a/components/i2cdev/i2cdev_master.h b/components/i2cdev/i2cdev_master.h new file mode 100644 index 00000000..c73087cd --- /dev/null +++ b/components/i2cdev/i2cdev_master.h @@ -0,0 +1,17 @@ +#if !defined __I2CDEV_MASTER_H__ +#define __I2CDEV_MASTER_H__ + +#include +#include "i2cdev.h" + +esp_err_t _i2cdev_init(); + +esp_err_t _i2cdev_done(); + +esp_err_t _i2c_dev_probe(i2c_dev_t *dev, i2c_dev_type_t operation_type) + +esp_err_t _i2c_dev_read(const i2c_dev_t *dev, const void *out_data, size_t out_size, void *in_data, size_t in_size); + +esp_err_t _i2c_dev_write(const i2c_dev_t *dev, const void *out_reg, size_t out_reg_size, const void *out_data, size_t out_size); + +#endif // __I2CDEV_LEGACY_H__ diff --git a/examples/i2cdev/default/CMakeLists.txt b/examples/i2cdev/default/CMakeLists.txt index 088d6e62..2205ab37 100644 --- a/examples/i2cdev/default/CMakeLists.txt +++ b/examples/i2cdev/default/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.5) set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) +set(COMPONENTS main driver i2cdev) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(i2c_scanner) diff --git a/examples/i2cdev/default/main/main.c b/examples/i2cdev/default/main/main.c index cc7c85ab..ab5a9024 100644 --- a/examples/i2cdev/default/main/main.c +++ b/examples/i2cdev/default/main/main.c @@ -6,9 +6,20 @@ void task(void *ignore) { i2c_dev_t dev = { 0 }; +#if CONFIG_I2CDEV_USING_LEGACY_I2C dev.cfg.sda_io_num = CONFIG_EXAMPLE_I2C_MASTER_SDA; dev.cfg.scl_io_num = CONFIG_EXAMPLE_I2C_MASTER_SCL; -#if HELPER_TARGET_IS_ESP32 +#else + dev.master_bus_config.sda_io_num = CONFIG_EXAMPLE_I2C_MASTER_SDA; + dev.master_bus_config.scl_io_num = CONFIG_EXAMPLE_I2C_MASTER_SCL; + dev.master_bus_config.i2c_port = -1; + dev.master_bus_config.clk_source = I2C_CLK_SRC_DEFAULT; + dev.master_bus_config.intr_priority = 0; + dev.master_bus_config.glitch_ignore_cnt = 7; + dev.master_bus_config.trans_queue_depth = 0; + dev.master_bus_config.flags.enable_internal_pullup = false; +#endif +#if HELPER_TARGET_IS_ESP32 && CONFIG_I2CDEV_USING_LEGACY_I2C dev.cfg.master.clk_speed = CONFIG_EXAMPLE_I2C_CLOCK_HZ; // 100kHz #endif while (1)