diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index e87ca79..0b26c50 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -12,9 +12,9 @@ jobs: build: strategy: matrix: - idf_ver: ["release-v5.1", "release-v5.2", "release-v5.3"] - idf_target: ["esp32", "esp32s2", "esp32c3", "esp32s3"] - runs-on: ubuntu-20.04 + idf_ver: ["release-v5.4", "release-v5.5"] + idf_target: ["esp32", "esp32s2", "esp32c3", "esp32s3", "esp32c6", "esp32p4"] + runs-on: ubuntu-latest container: espressif/idf:${{ matrix.idf_ver }} steps: - uses: actions/checkout@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e14044..d8a0e2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # ChangeLog +## v2.0.0 - 2025-07-13 + +### Breaking Changes: + +* break(repo): depend on esp-idf v5.4 or later (arduino-esp32 v3.2.0 or later) +* break(repo): remove legacy header files and macros which are marked as deprecated +* break(repo): support i2c-ng driver and not compatible with old i2c driver + ## v1.1.1 - 2025-07-07 ### Enhancements: diff --git a/CMakeLists.txt b/CMakeLists.txt index 350d52b..17f6b5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,10 +12,3 @@ idf_component_register( REQUIRES driver ) - -target_compile_options(${COMPONENT_LIB} - PUBLIC - -Wno-missing-field-initializers - PRIVATE - $<$:-std=gnu++17> -) diff --git a/README.md b/README.md index 6ebc43c..0317216 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ **Latest Espressif Component Version**: [![Espressif Release](https://components.espressif.com/components/espressif/esp32_io_expander/badge.svg)](https://components.espressif.com/components/espressif/esp32_io_expander) -# ESP32_IO_Expander +# ESP IO Expander ## Overview @@ -17,7 +17,7 @@ ## Table of Contents -- [ESP32\_IO\_Expander](#esp32_io_expander) +- [ESP IO Expander](#esp-io-expander) - [Overview](#overview) - [Table of Contents](#table-of-contents) - [Supported Drivers](#supported-drivers) @@ -41,9 +41,9 @@ | **Driver** | **Version** | | ---------------------------------------------------------------------------------------------------- | ----------- | | [esp_io_expander](https://components.espressif.com/components/espressif/esp_io_expander) | 1.0.1 | -| [TCA95XX_8BIT](https://components.espressif.com/components/espressif/esp_io_expander_tca9554) | 1.0.1 | -| [TCA95XX_16BIT](https://components.espressif.com/components/espressif/esp_io_expander_tca95xx_16bit) | 1.0.0 | -| [HT8574](https://components.espressif.com/components/espressif/esp_io_expander_ht8574) | 1.0.0 | +| [TCA95XX_8BIT](https://components.espressif.com/components/espressif/esp_io_expander_tca9554) | 2.0.1 | +| [TCA95XX_16BIT](https://components.espressif.com/components/espressif/esp_io_expander_tca95xx_16bit) | 2.0.0 | +| [HT8574](https://components.espressif.com/components/espressif/esp_io_expander_ht8574) | 2.0.0 | | CH422G | x | ## How to Use @@ -52,10 +52,10 @@ #### Dependencies and Versions -| **Dependency** | **Version** | -| ------------------------------------------------------------------ | -------------------- | -| [esp-idf](https://github.com/espressif/esp-idf) | >= 5.1 | -| [esp-lib-utils](https://github.com/esp-arduino-libs/esp-lib-utils) | >= 0.1.0 && <= 0.2.0 | +| **Dependency** | **Version** | +| ------------------------------------------------------------------ | ----------- | +| [esp-idf](https://github.com/espressif/esp-idf) | >= 5.4 | +| [esp-lib-utils](https://github.com/esp-arduino-libs/esp-lib-utils) | 0.2.* | #### Adding to Project @@ -75,10 +75,10 @@ Since `ESP32_IO_Expander` depends on the `esp-lib-utils` library which implement #### Dependencies and Versions -| **Dependency** | **Version** | -| ------------------------------------------------------------------ | -------------------- | -| [arduino-esp32](https://github.com/espressif/arduino-esp32) | >= v3.0.0 | -| [esp-lib-utils](https://github.com/esp-arduino-libs/esp-lib-utils) | >= 0.1.0 && <= 0.2.0 | +| **Dependency** | **Version** | +| ------------------------------------------------------------------ | ------------------- | +| [arduino-esp32](https://github.com/espressif/arduino-esp32) | >= v3.2.0 | +| [esp-lib-utils](https://github.com/esp-arduino-libs/esp-lib-utils) | >= 0.2.0 && < 0.3.0 | #### Installing the Library diff --git a/examples/ch422g/ch422g.ino b/examples/ch422g/ch422g.ino index 89709e1..2b46819 100644 --- a/examples/ch422g/ch422g.ino +++ b/examples/ch422g/ch422g.ino @@ -41,7 +41,6 @@ #define EXAMPLE_I2C_SDA_PIN (8) #define EXAMPLE_I2C_SCL_PIN (9) -#define EXAMPLE_I2C_ADDR (ESP_IO_EXPANDER_I2C_CH422G_ADDRESS) esp_expander::CH422G *expander = NULL; @@ -50,7 +49,7 @@ void setup() { delay(1000); Serial.println("Test begin"); - expander = new esp_expander::CH422G(EXAMPLE_I2C_SCL_PIN, EXAMPLE_I2C_SDA_PIN, EXAMPLE_I2C_ADDR); + expander = new esp_expander::CH422G(EXAMPLE_I2C_SCL_PIN, EXAMPLE_I2C_SDA_PIN); expander->init(); expander->begin(); diff --git a/idf_component.yml b/idf_component.yml index 4ad0e50..b38e0dd 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,10 +1,11 @@ -version: "1.1.1" +version: "2.0.0" description: ESP32_IO_Expander is a library designed for driving IO expander chips using ESP SoCs url: https://github.com/esp-arduino-libs/ESP32_IO_Expander repository: https://github.com/esp-arduino-libs/ESP32_IO_Expander.git issues: https://github.com/esp-arduino-libs/ESP32_IO_Expander/issues dependencies: - idf: ">=5.1" + idf: ">=5.4" + espressif/esp-lib-utils: version: "0.2.*" - public: true + public: false diff --git a/library.properties b/library.properties index 4065fba..4872903 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ESP32_IO_Expander -version=1.1.1 +version=2.0.0 author=espressif maintainer=espressif sentence=ESP32_IO_Expander is a library designed for driving IO expander chips using ESP SoCs diff --git a/micropython.cmake b/micropython.cmake index f0caa53..ed63811 100644 --- a/micropython.cmake +++ b/micropython.cmake @@ -13,11 +13,5 @@ target_sources(usermod_esp_io_expander INTERFACE ${SRCS_C} ${SRCS_CXX}) # Add the current directory as an include directory. target_include_directories(usermod_esp_io_expander INTERFACE ${SRC_DIR}) -# Add compile options. Since the target is not created by `idf_component_register()`, we need to add the `ESP_PLATFORM` define manually. -target_compile_options(usermod_esp_io_expander - INTERFACE - -Wno-missing-field-initializers -DESP_PLATFORM $<$:-std=gnu++17> -) - # Link our INTERFACE library to the usermod target. target_link_libraries(usermod INTERFACE usermod_esp_io_expander) diff --git a/src/ESP_IOExpander.h b/src/ESP_IOExpander.h deleted file mode 100644 index 7212f7d..0000000 --- a/src/ESP_IOExpander.h +++ /dev/null @@ -1,16 +0,0 @@ - -/* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * This file is just to keep the compatibility with the old version of the library. Please use the file `chip/esp_expander_base.hpp` instead. - */ - -#pragma once - -#warning "This file is deprecated. Please use the file `chip/esp_expander_base.hpp` instead." - -#include "chip/esp_expander_base.hpp" diff --git a/src/ESP_IOExpander_Library.h b/src/ESP_IOExpander_Library.h deleted file mode 100644 index fb9fa32..0000000 --- a/src/ESP_IOExpander_Library.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * This file is just to keep the compatibility with the old version of the library. Please use the file `esp_io_expander.hpp` instead. - */ - -#pragma once - -#warning "This file is deprecated. Please use the file `esp_io_expander.hpp` instead." - -#include "esp_io_expander.hpp" diff --git a/src/base/esp_io_expander.h b/src/base/esp_io_expander.h deleted file mode 100644 index 0f26e86..0000000 --- a/src/base/esp_io_expander.h +++ /dev/null @@ -1,16 +0,0 @@ - -/* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * This file is just to keep the compatibility with the old version of the library. Please use the file `port/esp_io_expander.h` instead. - */ - -#pragma once - -#warning "This file is deprecated. Please use the file `port/esp_io_expander.h` instead." - -#include "port/esp_io_expander.h" diff --git a/src/chip/esp_expander_ht8574.hpp b/src/chip/esp_expander_ht8574.hpp deleted file mode 100644 index e7b68ce..0000000 --- a/src/chip/esp_expander_ht8574.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "esp_expander_base.hpp" - -namespace esp_expander { - -/** - * @brief The HT8574 IO expander device class - * - * @note This class is a derived class of `esp_expander::Base`, user can use it directly - */ -class HT8574: public Base { -public: - /** - * @brief Construct a HT8574 device. With this function, call `init()` will initialize I2C by using the host - * configuration. - * - * @param[in] scl_io I2C SCL pin number - * @param[in] sda_io I2C SDA pin number - * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - */ - HT8574(int scl_io, int sda_io, uint8_t address): Base(scl_io, sda_io, address) {} - - /** - * @brief Construct a HT8574 device. With this function, call `init()` will not initialize I2C, and users should - * initialize it manually. - * - * @param[in] host_id I2C host ID. - * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - */ - HT8574(int host_id, uint8_t address): Base(host_id, address) {} - - /** - * @brief Construct a HT8574 device. - * - * @param[in] config Configuration for the object - */ - HT8574(const Config &config): Base(config) {} - - /** - * @deprecated Deprecated and will be removed in the next major version. Please use other constructors instead. - */ - [[deprecated("Deprecated and will be removed in the next major version. Please use other constructors instead.")]] - HT8574(i2c_port_t id, uint8_t address, int scl_io, int sda_io): Base(id, address, scl_io, sda_io) {} - - /** - * @brief Desutruct object. This function will call `del()` to delete the object. - */ - ~HT8574() override; - - /** - * @brief Begin object - * - * @note This function typically calls `esp_io_expander_new_i2c_*()` to create the IO expander handle. - * @note The driver initialization by default sets CH422G's IO0-7 to output high-level mode. - * - * @return true if success, otherwise false - */ - bool begin(void) override; -}; - -} // namespace esp_expander - -/** - * @deprecated Deprecated and will be removed in the next major version. Please use `esp_expander::HT8574` instead. - */ -typedef esp_expander::HT8574 ESP_IOExpander_HT8574 __attribute__((deprecated("Deprecated and will be removed in the next major version. Please use `esp_expander::HT8574` instead."))); diff --git a/src/chip/esp_expander_tca95xx_8bit.hpp b/src/chip/esp_expander_tca95xx_8bit.hpp deleted file mode 100644 index 0946a3f..0000000 --- a/src/chip/esp_expander_tca95xx_8bit.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "esp_expander_base.hpp" - -namespace esp_expander { - -/** - * @brief The TCA95XX_8BIT IO expander device class - * - * @note This class is a derived class of `esp_expander::Base`, user can use it directly - */ -class TCA95XX_8BIT: public Base { -public: - /** - * @brief Construct a TCA95XX_8BIT device. With this function, call `init()` will initialize I2C by using the host - * configuration. - * - * @param[in] scl_io I2C SCL pin number - * @param[in] sda_io I2C SDA pin number - * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - */ - TCA95XX_8BIT(int scl_io, int sda_io, uint8_t address): Base(scl_io, sda_io, address) {} - - /** - * @brief Construct a TCA95XX_8BIT device. With this function, call `init()` will not initialize I2C, and users should - * initialize it manually. - * - * @param[in] host_id I2C host ID. - * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - */ - TCA95XX_8BIT(int host_id, uint8_t address): Base(host_id, address) {} - - /** - * @brief Construct a TCA95XX_8BIT device. - * - * @param[in] config Configuration for the object - */ - TCA95XX_8BIT(const Config &config): Base(config) {} - - /** - * @deprecated Deprecated and will be removed in the next major version. Please use other constructors instead. - */ - [[deprecated("Deprecated and will be removed in the next major version. Please use other constructors instead.")]] - TCA95XX_8BIT(i2c_port_t id, uint8_t address, int scl_io, int sda_io): Base(id, address, scl_io, sda_io) {} - - /** - * @brief Desutruct object. This function will call `del()` to delete the object. - */ - ~TCA95XX_8BIT() override; - - /** - * @brief Begin object - * - * @note This function typically calls `esp_io_expander_new_i2c_*()` to create the IO expander handle. - * @note This function sets all pins to inpurt mode by default. - * - * @return true if success, otherwise false - */ - bool begin(void) override; -}; - -} // namespace esp_expander - -/** - * @deprecated Deprecated and will be removed in the next major version. Please use `esp_expander::TCA95XX_8BIT` instead. - */ -typedef esp_expander::TCA95XX_8BIT ESP_IOExpander_TCA95xx_8bit __attribute__((deprecated("Deprecated and will be removed in the next major version. Please use `esp_expander::TCA95XX_8BIT` instead."))); diff --git a/src/chip/esp_expander_base.cpp b/src/esp_expander_base.cpp similarity index 50% rename from src/chip/esp_expander_base.cpp rename to src/esp_expander_base.cpp index 1ad2262..7679343 100644 --- a/src/chip/esp_expander_base.cpp +++ b/src/esp_expander_base.cpp @@ -5,8 +5,7 @@ */ #include "inttypes.h" -#include "driver/i2c.h" -#include "esp_expander_utils.h" +#include "private/esp_expander_utils.h" #include "esp_expander_base.hpp" // Check whether it is a valid pin number @@ -14,183 +13,197 @@ namespace esp_expander { -void Base::Config::convertPartialToFull(void) +void Base::Config::convertPartialToFull() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); if (isHostConfigValid() && std::holds_alternative(host.value())) { #if ESP_UTILS_CONF_LOG_LEVEL == ESP_UTILS_LOG_LEVEL_DEBUG - printHostConfig(); + dumpHost(); #endif // ESP_UTILS_LOG_LEVEL_DEBUG auto &config = std::get(host.value()); host = HostFullConfig{ - .mode = I2C_MODE_MASTER, - .sda_io_num = config.sda_io_num, - .scl_io_num = config.scl_io_num, - .sda_pullup_en = config.sda_pullup_en, - .scl_pullup_en = config.scl_pullup_en, - .master = { - .clk_speed = static_cast(config.clk_speed), + .i2c_port = static_cast(host_id), + .sda_io_num = static_cast(config.sda_io_num), + .scl_io_num = static_cast(config.scl_io_num), + .clk_source = I2C_CLK_SRC_DEFAULT, + .glitch_ignore_cnt = 7, + .flags = { + .enable_internal_pullup = config.enable_internal_pullup, }, - .clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL, }; } - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); + if (std::holds_alternative(device)) { + auto &config = std::get(device); + device = DeviceFullConfig{ + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = static_cast(config.address), + .scl_speed_hz = static_cast(config.freq_hz), + }; + } } -void Base::Config::printHostConfig(void) const +void Base::Config::dumpHost() const { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); if (!isHostConfigValid()) { ESP_UTILS_LOGI("\n\t{Host config}[skipped]"); - goto end; + return; } if (std::holds_alternative(host.value())) { auto &config = std::get(host.value()); ESP_UTILS_LOGI( "\n\t{Host config}[full]\n" - "\t\t-> [host_id]: %d\n" - "\t\t-> [mode]: %d\n" + "\t\t-> [i2c_port]: %d\n" "\t\t-> [sda_io_num]: %d\n" "\t\t-> [scl_io_num]: %d\n" - "\t\t-> [sda_pullup_en]: %d\n" - "\t\t-> [scl_pullup_en]: %d\n" - "\t\t-> [master.clk_speed]: %d\n" - "\t\t-> [clk_flags]: %d" - , static_cast(host_id) - , static_cast(config.mode) + "\t\t-> [clk_source]: %d\n" + "\t\t-> [glitch_ignore_cnt]: %d\n" + "\t\t-> [intr_priority]: %d\n" + "\t\t-> [trans_queue_depth]: %d\n" + "\t\t-> [flags]:\n" + "\t\t\t-> [enable_internal_pullup]: %d\n" + "\t\t\t-> [allow_pd]: %d\n" + , static_cast(config.i2c_port) , static_cast(config.sda_io_num) , static_cast(config.scl_io_num) - , static_cast(config.sda_pullup_en) - , static_cast(config.scl_pullup_en) - , static_cast(config.master.clk_speed) - , static_cast(config.clk_flags) + , static_cast(config.clk_source) + , static_cast(config.glitch_ignore_cnt) + , static_cast(config.intr_priority) + , static_cast(config.trans_queue_depth) + , static_cast(config.flags.enable_internal_pullup) + , static_cast(config.flags.allow_pd) ); } else { auto &config = std::get(host.value()); ESP_UTILS_LOGI( "\n\t{Host config}[partial]\n" - "\t\t-> [host_id]: %d\n" + "\t\t-> [id]: %d\n" "\t\t-> [sda_io_num]: %d\n" "\t\t-> [scl_io_num]: %d\n" - "\t\t-> [sda_pullup_en]: %d\n" - "\t\t-> [scl_pullup_en]: %d\n" - "\t\t-> [clk_speed]: %d" + "\t\t-> [enable_internal_pullup]: %d\n" , static_cast(host_id) , static_cast(config.sda_io_num) , static_cast(config.scl_io_num) - , static_cast(config.sda_pullup_en) - , static_cast(config.scl_pullup_en) - , static_cast(config.clk_speed) + , static_cast(config.enable_internal_pullup) ); } - -end: - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); } -void Base::Config::printDeviceConfig(void) const +void Base::Config::dumpDevice() const { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - - ESP_UTILS_LOGI( - "\n\t{Device config}[partial]\n" - "\t\t-> [host_id]: %d\n" - "\t\t-> [address]: 0x%02X" - , static_cast(host_id) - , static_cast(device.address) - ); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); + if (std::holds_alternative(device)) { + auto &config = std::get(device); + ESP_UTILS_LOGI( + "\n\t{Device config}[full]\n" + "\t\t-> [dev_addr_length]: %d\n" + "\t\t-> [device_address]: 0x%02X\n" + "\t\t-> [scl_speed_hz]: %d\n" + "\t\t-> [scl_wait_us]: %d\n" + "\t\t-> [flags]:\n" + "\t\t\t-> [disable_ack_check]: %d\n" + , static_cast(config.dev_addr_length) + , static_cast(config.device_address) + , static_cast(config.scl_speed_hz) + , static_cast(config.scl_wait_us) + , static_cast(config.flags.disable_ack_check) + ); + } else { + auto &config = std::get(device); + ESP_UTILS_LOGI( + "\n\t{Device config}[partial]\n" + "\t\t-> [address]: 0x%02X\n" + "\t\t-> [freq_hz]: %d\n" + , static_cast(config.address) + , static_cast(config.freq_hz) + ); + } } bool Base::configHostSkipInit(bool skip_init) { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::INIT), false, "Should be called before `init()`"); - _is_host_skip_init = skip_init; - - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); + flags_.is_host_skip_init = skip_init; return true; } -bool Base::init(void) +bool Base::init() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::INIT), false, "Already initialized"); // Convert the partial configuration to full configuration - _config.convertPartialToFull(); + config_.convertPartialToFull(); #if ESP_UTILS_CONF_LOG_LEVEL == ESP_UTILS_LOG_LEVEL_DEBUG - _config.printHostConfig(); - _config.printDeviceConfig(); + config_.dumpHost(); + config_.dumpDevice(); #endif // ESP_UTILS_LOG_LEVEL_DEBUG - // Initialize the I2C host if not skipped - if (!isHostSkipInit()) { - i2c_port_t host_id = static_cast(getConfig().host_id); - ESP_UTILS_CHECK_ERROR_RETURN( - i2c_param_config(host_id, getHostFullConfig()), false, "I2C param config failed" - ); + // Initialize the I2C host if not initialized manually and host config is valid + if (!flags_.is_host_skip_init && config_.isHostConfigValid()) { + ESP_UTILS_LOGD("Try to init I2C host(%d)", static_cast(config_.host_id)); + + auto host_config = getHostFullConfig(); + ESP_UTILS_CHECK_NULL_RETURN(host_config, false, "Host config is not valid"); + ESP_UTILS_CHECK_ERROR_RETURN( - i2c_driver_install(host_id, getHostFullConfig()->mode, 0, 0, 0), false, "I2C driver install failed" + i2c_new_master_bus(host_config, &host_handle_), false, "I2C new master bus failed" ); - ESP_UTILS_LOGD("Init I2C host(%d)", static_cast(host_id)); + ESP_UTILS_LOGD("Init I2C host(%d)(@%p)", static_cast(config_.host_id), host_handle_); } setState(State::INIT); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } -bool Base::reset(void) +bool Base::reset() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_reset(device_handle), false, "Reset failed"); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } -bool Base::del(void) +bool Base::del() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); if (device_handle != nullptr) { ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_del(device_handle), false, "Delete failed"); + ESP_UTILS_LOGD("Delete device @%p", device_handle); device_handle = nullptr; - ESP_UTILS_LOGD("Delete @%p", device_handle); } - if (isOverState(State::INIT) && !isHostSkipInit()) { - i2c_port_t host_id = static_cast(getConfig().host_id); - ESP_UTILS_CHECK_ERROR_RETURN(i2c_driver_delete(host_id), false, "I2C driver delete failed"); - ESP_UTILS_LOGD("Delete I2C host(%d)", static_cast(host_id)); + if (host_handle_ != nullptr) { + if (!flags_.is_host_skip_init && config_.isHostConfigValid()) { + ESP_UTILS_CHECK_ERROR_RETURN(i2c_del_master_bus(host_handle_), false, "I2C driver delete failed"); + } + ESP_UTILS_LOGD("Delete host @%p", host_handle_); + host_handle_ = nullptr; } setState(State::DEINIT); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } bool Base::pinMode(uint8_t pin, uint8_t mode) { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); @@ -201,14 +214,12 @@ bool Base::pinMode(uint8_t pin, uint8_t mode) esp_io_expander_dir_t dir = (mode == INPUT) ? IO_EXPANDER_INPUT : IO_EXPANDER_OUTPUT; ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_set_dir(device_handle, BIT64(pin), dir), false, "Set dir failed"); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } bool Base::digitalWrite(uint8_t pin, uint8_t value) { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); @@ -219,14 +230,12 @@ bool Base::digitalWrite(uint8_t pin, uint8_t value) esp_io_expander_set_level(device_handle, BIT64(pin), value), false, "Set level failed" ); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } int Base::digitalRead(uint8_t pin) { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); @@ -238,14 +247,12 @@ int Base::digitalRead(uint8_t pin) esp_io_expander_get_level(device_handle, BIT64(pin), &level), -1, "Get level failed" ); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return (level & BIT64(pin)) ? HIGH : LOW; } bool Base::multiPinMode(uint32_t pin_mask, uint8_t mode) { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); @@ -255,14 +262,12 @@ bool Base::multiPinMode(uint32_t pin_mask, uint8_t mode) esp_io_expander_dir_t dir = (mode == INPUT) ? IO_EXPANDER_INPUT : IO_EXPANDER_OUTPUT; ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_set_dir(device_handle, pin_mask, dir), false, "Set dir failed"); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } bool Base::multiDigitalWrite(uint32_t pin_mask, uint8_t value) { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); @@ -270,14 +275,12 @@ bool Base::multiDigitalWrite(uint32_t pin_mask, uint8_t value) ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_set_level(device_handle, pin_mask, value), false, "Set level failed"); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } int64_t Base::multiDigitalRead(uint32_t pin_mask) { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); @@ -286,31 +289,53 @@ int64_t Base::multiDigitalRead(uint32_t pin_mask) uint32_t level = 0; ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_get_level(device_handle, pin_mask, &level), false, "Get level failed"); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return level; } -bool Base::printStatus(void) const +bool Base::printStatus() const { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_print_state(device_handle), false, "Print state failed"); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } +Base::DeviceFullConfig *Base::getDeviceFullConfig() +{ + if (std::holds_alternative(config_.device)) { + config_.convertPartialToFull(); + } + + return &std::get(config_.device); +} + Base::HostFullConfig *Base::getHostFullConfig() { - if (std::holds_alternative(_config.host.value())) { - _config.convertPartialToFull(); + ESP_UTILS_CHECK_FALSE_RETURN(config_.isHostConfigValid(), nullptr, "Host config is not valid"); + + if (std::holds_alternative(config_.host.value())) { + config_.convertPartialToFull(); + } + + return &std::get(config_.host.value()); +} + +Base::HostHandle Base::getHostHandle() +{ + if (host_handle_ != nullptr) { + return host_handle_; } - return &std::get(_config.host.value()); + ESP_UTILS_LOGD("Try to get I2C host(%d) handle", static_cast(config_.host_id)); + ESP_UTILS_CHECK_ERROR_RETURN( + i2c_master_get_bus_handle(static_cast(config_.host_id), &host_handle_), + nullptr, "I2C master bus handle get failed" + ); + + return host_handle_; } } // namespace esp_expander diff --git a/src/chip/esp_expander_base.hpp b/src/esp_expander_base.hpp similarity index 71% rename from src/chip/esp_expander_base.hpp rename to src/esp_expander_base.hpp index 957a620..2874d49 100644 --- a/src/chip/esp_expander_base.hpp +++ b/src/esp_expander_base.hpp @@ -8,21 +8,21 @@ #include #include -#include "driver/i2c.h" +#include "driver/i2c_master.h" #include "port/esp_io_expander.h" // Refer to `esp32-hal-gpio.h` in Arduino -#ifndef INPUT -#define INPUT 0x01 +#ifndef INPUT +# define INPUT 0x01 #endif -#ifndef OUTPUT -#define OUTPUT 0x03 +#ifndef OUTPUT +# define OUTPUT 0x03 #endif -#ifndef LOW -#define LOW 0x0 +#ifndef LOW +# define LOW 0x0 #endif -#ifndef HIGH -#define HIGH 0x1 +#ifndef HIGH +# define HIGH 0x1 #endif namespace esp_expander { @@ -38,39 +38,42 @@ class Base { /** * Here are some default values for I2C bus */ - constexpr static int I2C_HOST_ID_DEFAULT = static_cast(I2C_NUM_0); - constexpr static int I2C_CLK_SPEED_DEFAULT = 400 * 1000; + static constexpr int I2C_HOST_ID_DEFAULT = static_cast(I2C_NUM_0); + static constexpr int I2C_CLK_SPEED_DEFAULT = 400 * 1000; + static constexpr bool I2C_ENABLE_INTERNAL_PULLUP_DEFAULT = true; + using HostHandle = i2c_master_bus_handle_t; using DeviceHandle = esp_io_expander_handle_t; struct HostPartialConfig { int sda_io_num = -1; int scl_io_num = -1; - bool sda_pullup_en = GPIO_PULLUP_ENABLE; - bool scl_pullup_en = GPIO_PULLUP_ENABLE; - int clk_speed = I2C_CLK_SPEED_DEFAULT; + bool enable_internal_pullup = I2C_ENABLE_INTERNAL_PULLUP_DEFAULT; }; - using HostFullConfig = i2c_config_t; + using HostFullConfig = i2c_master_bus_config_t; using HostConfig = std::variant; - struct DeviceConfig { - uint8_t address = 0; + struct DevicePartialConfig { + int address = -1; + int freq_hz = I2C_CLK_SPEED_DEFAULT; }; + using DeviceFullConfig = i2c_device_config_t; + using DeviceConfig = std::variant; /** * @brief Configuration for Base object */ struct Config { - void convertPartialToFull(void); - void printHostConfig(void) const; - void printDeviceConfig(void) const; + void convertPartialToFull(); + void dumpHost() const; + void dumpDevice() const; - bool isHostConfigValid(void) const + bool isHostConfigValid() const { return host.has_value(); } - int host_id = I2C_HOST_ID_DEFAULT; /*!< I2C host ID */ + int host_id = I2C_HOST_ID_DEFAULT; std::optional host; /*!< I2C host configuration */ DeviceConfig device = {}; /*!< I2C device configuration */ }; @@ -91,38 +94,36 @@ class Base { * * @param[in] scl_io I2C SCL pin number * @param[in] sda_io I2C SDA pin number - * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. + * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS*`. */ - Base(int scl_io, int sda_io, uint8_t address): - _config{ + Base(int scl_io, int sda_io, uint8_t address) + : config_{ .host_id = I2C_HOST_ID_DEFAULT, .host = HostPartialConfig{ .sda_io_num = sda_io, .scl_io_num = scl_io, }, - .device = DeviceConfig{ - .address = address + .device = DevicePartialConfig{ + .address = address, } } - { - } + {} /** * @brief Construct a base device. With this function, call `init()` will not initialize I2C, and users should * initialize it manually. * * @param[in] host_id I2C host ID. - * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. + * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS*`. */ - Base(int host_id, uint8_t address): - _config{ + Base(int host_id, uint8_t address) + : config_{ .host_id = host_id, - .device = DeviceConfig{ - .address = address + .device = DevicePartialConfig{ + .address = address, } } - { - } + {} // *INDENT-ON* /** @@ -130,7 +131,9 @@ class Base { * * @param[in] config Configuration for the object */ - Base(const Config &config): _config(config) {} + Base(const Config &config) + : config_(config) + {} /** * @brief Virtual desutruct object. @@ -157,26 +160,26 @@ class Base { * * @return true if success, otherwise false */ - bool init(void); + bool init(); /** * @brief Begin object * * @note This function typically calls `esp_io_expander_new_i2c_*()` to create the IO expander handle. */ - virtual bool begin(void) = 0; + virtual bool begin() = 0; /** * @brief Reset object * * @return true if success, otherwise false */ - bool reset(void); + bool reset(); /** * @brief Delete object */ - bool del(void); + bool del(); /** * @brief Set pin mode @@ -247,7 +250,14 @@ class Base { * * @return Pin levels, every bit represents a pin (HIGH / LOW) */ - bool printStatus(void) const; + bool printStatus() const; + + /** + * @brief Get host handle + * + * @return Host handle + */ + HostHandle getHostHandle(); /** * @brief Check if the driver has reached or passed the specified state @@ -258,7 +268,7 @@ class Base { */ bool isOverState(State state) const { - return (_state >= state); + return (state_ >= state); } /** @@ -266,9 +276,9 @@ class Base { * * @return IO expander Configuration */ - const Config &getConfig(void) const + const Config &getConfig() const { - return _config; + return config_; } /** @@ -276,49 +286,30 @@ class Base { * * @return Device handle if success, otherwise nullptr */ - DeviceHandle getDeviceHandle(void) const + DeviceHandle getDeviceHandle() const { return device_handle; } - // TODO: Remove in the next major version - Base(i2c_port_t id, uint8_t address, int scl_io, int sda_io): - Base(scl_io, sda_io, address) - { - _config.host_id = id; - } - [[deprecated("Deprecated and will be removed in the next major version. Please use `getDeviceHandle()` instead.")]] - esp_io_expander_handle_t getHandle(void) const - { - return getDeviceHandle(); - } - protected: - bool isHostSkipInit(void) const - { - return !_config.isHostConfigValid() || _is_host_skip_init; - } - void setState(State state) { - _state = state; + state_ = state; } + DeviceFullConfig *getDeviceFullConfig(); + DeviceHandle device_handle = nullptr; private: HostFullConfig *getHostFullConfig(); - State _state = State::DEINIT; - bool _is_host_skip_init = false; - Config _config = {}; + Config config_ = {}; + State state_ = State::DEINIT; + HostHandle host_handle_ = nullptr; + struct { + int is_host_skip_init : 1; + } flags_ = {}; }; } // namespace esp_expander - -/** - * @brief Alias for backward compatibility - * - * @deprecated Use `esp_expander::Base` instead - */ -using ESP_IOExpander [[deprecated("Use `esp_expander::Base` instead.")]] = esp_expander::Base; diff --git a/src/chip/esp_expander_ch422g.cpp b/src/esp_expander_ch422g.cpp similarity index 65% rename from src/chip/esp_expander_ch422g.cpp rename to src/esp_expander_ch422g.cpp index dcf3223..fa50891 100644 --- a/src/chip/esp_expander_ch422g.cpp +++ b/src/esp_expander_ch422g.cpp @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "esp_expander_utils.h" +#include "private/esp_expander_utils.h" #include "port/esp_io_expander_ch422g.h" #include "esp_expander_ch422g.hpp" @@ -12,16 +12,14 @@ namespace esp_expander { CH422G::~CH422G() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_EXIT(del(), "Delete failed"); - - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); } -bool CH422G::begin(void) +bool CH422G::begin() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::BEGIN), false, "Already begun"); @@ -30,23 +28,20 @@ bool CH422G::begin(void) ESP_UTILS_CHECK_FALSE_RETURN(init(), false, "Init failed"); } + auto device_config = getDeviceFullConfig(); ESP_UTILS_CHECK_ERROR_RETURN( - esp_io_expander_new_i2c_ch422g( - static_cast(getConfig().host_id), getConfig().device.address, &device_handle - ), false, "Create CH422G failed" + esp_io_expander_new_i2c_ch422g(getHostHandle(), device_config, &device_handle), false, "Create CH422G failed" ); ESP_UTILS_LOGD("Create CH422G @%p", device_handle); setState(State::BEGIN); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } -bool CH422G::enableOC_OpenDrain(void) +bool CH422G::enableOC_OpenDrain() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); @@ -54,14 +49,12 @@ bool CH422G::enableOC_OpenDrain(void) esp_io_expander_ch422g_set_oc_open_drain(device_handle), false, "Set OC open-drain failed" ); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } -bool CH422G::enableOC_PushPull(void) +bool CH422G::enableOC_PushPull() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); @@ -69,14 +62,12 @@ bool CH422G::enableOC_PushPull(void) esp_io_expander_ch422g_set_oc_push_pull(device_handle), false, "Set OC push-pull failed" ); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } -bool CH422G::enableAllIO_Input(void) +bool CH422G::enableAllIO_Input() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); @@ -84,14 +75,12 @@ bool CH422G::enableAllIO_Input(void) esp_io_expander_ch422g_set_all_input(device_handle), false, "Set all input failed" ); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } -bool CH422G::enableAllIO_Output(void) +bool CH422G::enableAllIO_Output() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); @@ -99,14 +88,12 @@ bool CH422G::enableAllIO_Output(void) esp_io_expander_ch422g_set_all_output(device_handle), false, "Set all output failed" ); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } -bool CH422G::enterSleep(void) +bool CH422G::enterSleep() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); @@ -114,14 +101,12 @@ bool CH422G::enterSleep(void) esp_io_expander_ch422g_enter_sleep(device_handle), false, "Enter sleep failed" ); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } -bool CH422G::exitSleep(void) +bool CH422G::exitSleep() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); @@ -129,8 +114,6 @@ bool CH422G::exitSleep(void) esp_io_expander_ch422g_exit_sleep(device_handle), false, "Exit sleep failed" ); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } diff --git a/src/chip/esp_expander_ch422g.hpp b/src/esp_expander_ch422g.hpp similarity index 68% rename from src/chip/esp_expander_ch422g.hpp rename to src/esp_expander_ch422g.hpp index c438bde..5907b18 100644 --- a/src/chip/esp_expander_ch422g.hpp +++ b/src/esp_expander_ch422g.hpp @@ -27,31 +27,31 @@ class CH422G: public Base { * * @param[in] scl_io I2C SCL pin number * @param[in] sda_io I2C SDA pin number - * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. + * @param[in] address I2C device 7-bit address. This parameter is unused and just for compatibility. */ - CH422G(int scl_io, int sda_io, uint8_t address): Base(scl_io, sda_io, address) {} + CH422G(int scl_io, int sda_io, uint8_t address = 0) + : Base(scl_io, sda_io, address) + {} /** * @brief Construct a CH422G device. With this function, call `init()` will not initialize I2C, and users should * initialize it manually. * * @param[in] host_id I2C host ID. - * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. + * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS*`. */ - CH422G(int host_id, uint8_t address): Base(host_id, address) {} + CH422G(int host_id, uint8_t address = 0) + : Base(host_id, address) + {} /** * @brief Construct a CH422G device. * * @param[in] config Configuration for the object */ - CH422G(const Config &config): Base(config) {} - - /** - * @deprecated Deprecated and will be removed in the next major version. Please use other constructors instead. - */ - [[deprecated("Deprecated and will be removed in the next major version. Please use other constructors instead.")]] - CH422G(i2c_port_t id, uint8_t address, int scl_io, int sda_io): Base(id, address, scl_io, sda_io) {} + CH422G(const Config &config) + : Base(config) + {} /** * @brief Desutruct object. This function will call `del()` to delete the object. @@ -61,26 +61,26 @@ class CH422G: public Base { /** * @brief Begin object * - * @note This function typically calls `esp_io_expander_new_i2c_*()` to create the IO expander handle. + * @note This function typically calls `esp_io_expander_new_i2c_ch422g()` to create the IO expander handle. * @note This function sets all IO0-7 pins to output high-level mode by default. * * @return true if success, otherwise false */ - bool begin(void) override; + bool begin() override; /** * @brief Enable OC0-OC3 output open-drain * * @return true if success, otherwise false */ - bool enableOC_OpenDrain(void); + bool enableOC_OpenDrain(); /** * @brief Enable OC0-OC3 output push-pull (default mode when power-on) * * @return true if success, otherwise false */ - bool enableOC_PushPull(void); + bool enableOC_PushPull(); /** * @brief Enable IO0-7 input mode @@ -91,33 +91,28 @@ class CH422G: public Base { * * @return true if success, otherwise false */ - bool enableAllIO_Input(void); + bool enableAllIO_Input(); /** * @brief Enable IO0-7 output mode * * @return true if success, otherwise false */ - bool enableAllIO_Output(void); + bool enableAllIO_Output(); /** * @brief Enter sleep mode * * @return true if success, otherwise false */ - bool enterSleep(void); + bool enterSleep(); /** * @brief Exit sleep mode * * @return true if success, otherwise false */ - bool exitSleep(void); + bool exitSleep(); }; } // namespace esp_expander - -/** - * @deprecated Deprecated and will be removed in the next major version. Please use `esp_expander::CH422G` instead. - */ -typedef esp_expander::CH422G ESP_IOExpander_CH422G __attribute__((deprecated("Deprecated and will be removed in the next major version. Please use `esp_expander::CH422G` instead."))); diff --git a/src/chip/esp_expander_ht8574.cpp b/src/esp_expander_ht8574.cpp similarity index 64% rename from src/chip/esp_expander_ht8574.cpp rename to src/esp_expander_ht8574.cpp index 837f299..0e6de92 100644 --- a/src/chip/esp_expander_ht8574.cpp +++ b/src/esp_expander_ht8574.cpp @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "esp_expander_utils.h" +#include "private/esp_expander_utils.h" #include "port/esp_io_expander_ht8574.h" #include "esp_expander_ht8574.hpp" @@ -12,16 +12,14 @@ namespace esp_expander { HT8574::~HT8574() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_EXIT(del(), "Delete failed"); - - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); } -bool HT8574::begin(void) +bool HT8574::begin() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::BEGIN), false, "Already begun"); @@ -30,17 +28,14 @@ bool HT8574::begin(void) ESP_UTILS_CHECK_FALSE_RETURN(init(), false, "Init failed"); } + auto device_config = getDeviceFullConfig(); ESP_UTILS_CHECK_ERROR_RETURN( - esp_io_expander_new_i2c_ht8574( - static_cast(getConfig().host_id), getConfig().device.address, &device_handle - ), false, "Create HT8574 failed" + esp_io_expander_new_i2c_ht8574(getHostHandle(), device_config, &device_handle), false, "Create HT8574 failed" ); ESP_UTILS_LOGD("Create HT8574 @%p", device_handle); setState(State::BEGIN); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } diff --git a/src/esp_expander_ht8574.hpp b/src/esp_expander_ht8574.hpp new file mode 100644 index 0000000..b1eeba2 --- /dev/null +++ b/src/esp_expander_ht8574.hpp @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_expander_base.hpp" + +namespace esp_expander { + +/** + * @brief The HT8574 IO expander device class + * + * @note This class is a derived class of `esp_expander::Base`, user can use it directly + */ +class HT8574: public Base { +public: + // /** + // * @brief Construct a HT8574 device. With this function, call `init()` will initialize I2C by using the host + // * configuration. + // * + // * @param[in] scl_io I2C SCL pin number + // * @param[in] sda_io I2C SDA pin number + // * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_XXX`. + // */ + // HT8574(int scl_io, int sda_io, uint8_t address) + // : Base(scl_io, sda_io, address) + // {} + + // /** + // * @brief Construct a HT8574 device. With this function, call `init()` will not initialize I2C, and users should + // * initialize it manually. + // * + // * @param[in] host_handle I2C host handle, which is created by `i2c_new_master_bus()`. + // * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_XXX`. + // */ + // HT8574(HostHandle host_handle, uint8_t address) + // : Base(host_handle, address) + // {} + + // /** + // * @brief Construct a HT8574 device. + // * + // * @param[in] config Configuration for the object + // */ + // HT8574(const Config &config) + // : Base(config) + // {} + + using Base::Base; + + /** + * @brief Desutruct object. This function will call `del()` to delete the object. + */ + ~HT8574() override; + + /** + * @brief Begin object + * + * @note This function typically calls `esp_io_expander_new_i2c_ht8574()` to create the IO expander handle. + * + * @return true if success, otherwise false + */ + bool begin() override; +}; + +} // namespace esp_expander diff --git a/src/chip/esp_expander_tca95xx_16bit.cpp b/src/esp_expander_tca95xx_16bit.cpp similarity index 63% rename from src/chip/esp_expander_tca95xx_16bit.cpp rename to src/esp_expander_tca95xx_16bit.cpp index 68f4702..30faf13 100644 --- a/src/chip/esp_expander_tca95xx_16bit.cpp +++ b/src/esp_expander_tca95xx_16bit.cpp @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "esp_expander_utils.h" +#include "private/esp_expander_utils.h" #include "port/esp_io_expander_tca95xx_16bit.h" #include "esp_expander_tca95xx_16bit.hpp" @@ -12,16 +12,14 @@ namespace esp_expander { TCA95XX_16BIT::~TCA95XX_16BIT() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_EXIT(del(), "Delete failed"); - - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); } -bool TCA95XX_16BIT::begin(void) +bool TCA95XX_16BIT::begin() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::BEGIN), false, "Already begun"); @@ -30,17 +28,15 @@ bool TCA95XX_16BIT::begin(void) ESP_UTILS_CHECK_FALSE_RETURN(init(), false, "Init failed"); } + auto device_config = getDeviceFullConfig(); ESP_UTILS_CHECK_ERROR_RETURN( - esp_io_expander_new_i2c_tca95xx_16bit( - static_cast(getConfig().host_id), getConfig().device.address, &device_handle - ), false, "Create TCA95XX_16BIT failed" + esp_io_expander_new_i2c_tca95xx_16bit(getHostHandle(), device_config, &device_handle), false, + "Create TCA95XX_16BIT failed" ); ESP_UTILS_LOGD("Create TCA95XX_16BIT @%p", device_handle); setState(State::BEGIN); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } diff --git a/src/chip/esp_expander_tca95xx_16bit.hpp b/src/esp_expander_tca95xx_16bit.hpp similarity index 54% rename from src/chip/esp_expander_tca95xx_16bit.hpp rename to src/esp_expander_tca95xx_16bit.hpp index 94f4c35..dbfdf5d 100644 --- a/src/chip/esp_expander_tca95xx_16bit.hpp +++ b/src/esp_expander_tca95xx_16bit.hpp @@ -23,31 +23,35 @@ class TCA95XX_16BIT: public Base { * * @param[in] scl_io I2C SCL pin number * @param[in] sda_io I2C SDA pin number - * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. + * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C_TCA9539_ADDRESS_XX` or + * `ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_XXX`. */ - TCA95XX_16BIT(int scl_io, int sda_io, uint8_t address): Base(scl_io, sda_io, address) {} + // TCA95XX_16BIT(int scl_io, int sda_io, uint8_t address) + // : Base(scl_io, sda_io, address) + // {} /** * @brief Construct a TCA95XX_16BIT device. With this function, call `init()` will not initialize I2C, and users * should initialize it manually. * - * @param[in] host_id I2C host ID. - * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. + * @param[in] host_handle I2C host handle, which is created by `i2c_new_master_bus()`. + * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C_TCA9539_ADDRESS_XX` or + * `ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_XXX`. */ - TCA95XX_16BIT(int host_id, uint8_t address): Base(host_id, address) {} + // TCA95XX_16BIT(HostHandle host_handle, uint8_t address) + // : Base(host_handle, address) + // {} /** * @brief Construct a TCA95XX_16BIT device. * * @param[in] config Configuration for the object */ - TCA95XX_16BIT(const Config &config): Base(config) {} + // TCA95XX_16BIT(const Config &config) + // : Base(config) + // {} - /** - * @deprecated Deprecated and will be removed in the next major version. Please use other constructors instead. - */ - [[deprecated("Deprecated and will be removed in the next major version. Please use other constructors instead.")]] - TCA95XX_16BIT(i2c_port_t id, uint8_t address, int scl_io, int sda_io): Base(id, address, scl_io, sda_io) {} + using Base::Base; /** * @brief Desutruct object. This function will call `del()` to delete the object. @@ -57,17 +61,12 @@ class TCA95XX_16BIT: public Base { /** * @brief Begin object * - * @note This function typically calls `esp_io_expander_new_i2c_*()` to create the IO expander handle. - * @note This function sets all pins to inpurt mode by default. + * @note This function typically calls `esp_io_expander_new_i2c_tca95xx_16bit()` to create the IO expander handle. + * @note This function sets all pins to input mode by default. * * @return true if success, otherwise false */ - bool begin(void) override; + bool begin() override; }; } // namespace esp_expander - -/** - * @deprecated Deprecated and will be removed in the next major version. Please use `esp_expander::TCA95XX_16BIT` instead. - */ -typedef esp_expander::TCA95XX_16BIT ESP_IOExpander_TCA95xx_16bit __attribute__((deprecated("Deprecated and will be removed in the next major version. Please use `esp_expander::TCA95XX_16BIT` instead."))); diff --git a/src/chip/esp_expander_tca95xx_8bit.cpp b/src/esp_expander_tca95xx_8bit.cpp similarity index 64% rename from src/chip/esp_expander_tca95xx_8bit.cpp rename to src/esp_expander_tca95xx_8bit.cpp index 60ebdca..ec4de04 100644 --- a/src/chip/esp_expander_tca95xx_8bit.cpp +++ b/src/esp_expander_tca95xx_8bit.cpp @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "esp_expander_utils.h" +#include "private/esp_expander_utils.h" #include "port/esp_io_expander_tca9554.h" #include "esp_expander_tca95xx_8bit.hpp" @@ -12,16 +12,14 @@ namespace esp_expander { TCA95XX_8BIT::~TCA95XX_8BIT() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_EXIT(del(), "Delete failed"); - - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); } -bool TCA95XX_8BIT::begin(void) +bool TCA95XX_8BIT::begin() { - ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS(); ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::BEGIN), false, "Already begun"); @@ -30,17 +28,14 @@ bool TCA95XX_8BIT::begin(void) ESP_UTILS_CHECK_FALSE_RETURN(init(), false, "Init failed"); } + auto device_config = getDeviceFullConfig(); ESP_UTILS_CHECK_ERROR_RETURN( - esp_io_expander_new_i2c_tca9554( - static_cast(getConfig().host_id), getConfig().device.address, &device_handle - ), false, "Create TCA95XX_8BIT failed" + esp_io_expander_new_i2c_tca9554(getHostHandle(), device_config, &device_handle), false, "Create TCA95XX_8BIT failed" ); ESP_UTILS_LOGD("Create TCA95XX_8BIT @%p", device_handle); setState(State::BEGIN); - ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); - return true; } diff --git a/src/esp_expander_tca95xx_8bit.hpp b/src/esp_expander_tca95xx_8bit.hpp new file mode 100644 index 0000000..2e24a29 --- /dev/null +++ b/src/esp_expander_tca95xx_8bit.hpp @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_expander_base.hpp" + +namespace esp_expander { + +/** + * @brief The TCA95XX_8BIT IO expander device class + * + * @note This class is a derived class of `esp_expander::Base`, user can use it directly + */ +class TCA95XX_8BIT: public Base { +public: + // /** + // * @brief Construct a TCA95XX_8BIT device. With this function, call `init()` will initialize I2C by using the host + // * configuration. + // * + // * @param[in] scl_io I2C SCL pin number + // * @param[in] sda_io I2C SDA pin number + // * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_XXX` or + // * `ESP_IO_EXPANDER_I2C_TCA9554A_ADDRESS_XXX`. + // */ + // TCA95XX_8BIT(int scl_io, int sda_io, uint8_t address) + // : Base(scl_io, sda_io, address) + // {} + + // /** + // * @brief Construct a TCA95XX_8BIT device. With this function, call `init()` will not initialize I2C, and users should + // * initialize it manually. + // * + // * @param[in] host_handle I2C host handle, which is created by `i2c_new_master_bus()`. + // * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_XXX` or + // * `ESP_IO_EXPANDER_I2C_TCA9554A_ADDRESS_XXX`. + // */ + // TCA95XX_8BIT(HostHandle host_handle, uint8_t address) + // : Base(host_handle, address) + // {} + + // /** + // * @brief Construct a TCA95XX_8BIT device. + // * + // * @param[in] config Configuration for the object + // */ + // TCA95XX_8BIT(const Config &config) + // : Base(config) + // {} + + using Base::Base; + + /** + * @brief Desutruct object. This function will call `del()` to delete the object. + */ + ~TCA95XX_8BIT() override; + + /** + * @brief Begin object + * + * @note This function typically calls `esp_io_expander_new_i2c_tca9554()` to create the IO expander handle. + * @note This function sets all pins to input mode by default. + * + * @return true if success, otherwise false + */ + bool begin() override; +}; + +} // namespace esp_expander diff --git a/src/esp_io_expander.hpp b/src/esp_io_expander.hpp index e9a2062..66493de 100644 --- a/src/esp_io_expander.hpp +++ b/src/esp_io_expander.hpp @@ -14,8 +14,8 @@ #include "port/esp_io_expander_tca95xx_16bit.h" /* Wrapper classes */ -#include "chip/esp_expander_base.hpp" -#include "chip/esp_expander_ch422g.hpp" -#include "chip/esp_expander_ht8574.hpp" -#include "chip/esp_expander_tca95xx_8bit.hpp" -#include "chip/esp_expander_tca95xx_16bit.hpp" +#include "esp_expander_base.hpp" +#include "esp_expander_ch422g.hpp" +#include "esp_expander_ht8574.hpp" +#include "esp_expander_tca95xx_8bit.hpp" +#include "esp_expander_tca95xx_16bit.hpp" diff --git a/src/port/esp_io_expander.c b/src/port/esp_io_expander.c index f442168..fd0147f 100644 --- a/src/port/esp_io_expander.c +++ b/src/port/esp_io_expander.c @@ -13,12 +13,13 @@ #include "esp_io_expander.h" -#include "esp_expander_utils.h" +#include "private/esp_expander_utils.h" #define VALID_IO_COUNT(handle) ((handle)->config.io_count <= IO_COUNT_MAX ? (handle)->config.io_count : IO_COUNT_MAX) /** * @brief Register type + * */ typedef enum { REG_INPUT = 0, diff --git a/src/port/esp_io_expander.h b/src/port/esp_io_expander.h index a01f066..9fc0ecb 100644 --- a/src/port/esp_io_expander.h +++ b/src/port/esp_io_expander.h @@ -20,16 +20,22 @@ extern "C" { #endif +#define ESP_IO_EXPANDER_VER_MAJOR (1) +#define ESP_IO_EXPANDER_VER_MINOR (0) +#define ESP_IO_EXPANDER_VER_PATCH (1) + #define IO_COUNT_MAX (sizeof(uint32_t) * 8) /** * @brief IO Expander Device Type + * */ typedef struct esp_io_expander_s esp_io_expander_t; typedef esp_io_expander_t *esp_io_expander_handle_t; /** * @brief IO Expander Pin Num + * */ typedef enum { IO_EXPANDER_PIN_NUM_0 = (1ULL << 0), @@ -68,6 +74,7 @@ typedef enum { /** * @brief IO Expander Pin direction + * */ typedef enum { IO_EXPANDER_INPUT, /*!< Input direction */ @@ -76,6 +83,7 @@ typedef enum { /** * @brief IO Expander Configuration Type + * */ typedef struct { uint8_t io_count; /*!< Count of device's IO, must be less or equal than `IO_COUNT_MAX` */ diff --git a/src/port/esp_io_expander_ch422g.c b/src/port/esp_io_expander_ch422g.c index f16fd02..88cb417 100644 --- a/src/port/esp_io_expander_ch422g.c +++ b/src/port/esp_io_expander_ch422g.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,27 +7,25 @@ #include #include #include - -#include "driver/i2c.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "esp_bit_defs.h" #include "esp_check.h" #include "esp_log.h" - #include "esp_io_expander.h" #include "esp_io_expander_ch422g.h" +#include "private/esp_expander_utils.h" -#include "esp_expander_utils.h" - -/* Timeout of each I2C communication */ -#define I2C_TIMEOUT_MS (10) +/* I2C communication related */ +#define I2C_TIMEOUT_MS (1000) #define IO_COUNT (12) /* Register address */ -#define CH422G_REG_WR_SET (0x48 >> 1) -#define CH422G_REG_WR_OC (0x46 >> 1) -#define CH422G_REG_WR_IO (0x70 >> 1) -#define CH422G_REG_RD_IO (0x4D >> 1) +#define CH422G_REG_ADDR_WR_SET (0x48 >> 1) +#define CH422G_REG_ADDR_WR_OC (0x46 >> 1) +#define CH422G_REG_ADDR_WR_IO (0x70 >> 1) +#define CH422G_REG_ADDR_RD_IO (0x4D >> 1) /* Default register value when reset */ // *INDENT-OFF* @@ -47,13 +45,24 @@ #define REG_WR_SET_BIT_OD_EN (1U << 2) #define REG_WR_SET_BIT_SLEEP (1U << 3) +#define DIR_OUT_VALUE (0xFFF) +#define DIR_IN_VALUE (0xF00) + +/* Register type */ +enum { + CH422G_REG_TYPE_WR_SET = 0, + CH422G_REG_TYPE_WR_OC, + CH422G_REG_TYPE_WR_IO, + CH422G_REG_TYPE_RD_IO, + CH422G_REG_TYPE_NUM, +}; + /** * @brief Device Structure Type */ typedef struct { esp_io_expander_t base; - i2c_port_t i2c_num; - uint32_t i2c_address; + i2c_master_dev_handle_t i2c_handles[CH422G_REG_TYPE_NUM]; struct { uint8_t wr_set; uint8_t wr_oc; @@ -62,6 +71,12 @@ typedef struct { } esp_io_expander_ch422g_t; static const char *TAG = "ch422g"; +static const int REG_TYPE_ADDR[CH422G_REG_TYPE_NUM] = { + [CH422G_REG_TYPE_WR_SET] = CH422G_REG_ADDR_WR_SET, + [CH422G_REG_TYPE_WR_OC] = CH422G_REG_ADDR_WR_OC, + [CH422G_REG_TYPE_WR_IO] = CH422G_REG_ADDR_WR_IO, + [CH422G_REG_TYPE_RD_IO] = CH422G_REG_ADDR_RD_IO, +}; static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value); static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t value); @@ -71,19 +86,27 @@ static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *v static esp_err_t reset(esp_io_expander_t *handle); static esp_err_t del(esp_io_expander_t *handle); -esp_err_t esp_io_expander_new_i2c_ch422g(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle) +esp_err_t esp_io_expander_new_i2c_ch422g( + i2c_master_bus_handle_t i2c_bus, const i2c_device_config_t *i2c_dev_cfg, esp_io_expander_handle_t *handle_ret +) { ESP_LOGI(TAG, "version: %d.%d.%d", ESP_IO_EXPANDER_CH422G_VER_MAJOR, ESP_IO_EXPANDER_CH422G_VER_MINOR, ESP_IO_EXPANDER_CH422G_VER_PATCH); - ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "Invalid i2c num"); - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); + ESP_RETURN_ON_FALSE(handle_ret != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid handle_ret"); esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)calloc(1, sizeof(esp_io_expander_ch422g_t)); ESP_RETURN_ON_FALSE(ch422g, ESP_ERR_NO_MEM, TAG, "Malloc failed"); + // Add new I2C devices + esp_err_t ret = ESP_OK; + i2c_device_config_t temp_cfg = *i2c_dev_cfg; + for (int i = 0; i < CH422G_REG_TYPE_NUM; i++) { + temp_cfg.device_address = REG_TYPE_ADDR[i] << 1; + ret = i2c_master_bus_add_device(i2c_bus, &temp_cfg, &ch422g->i2c_handles[i]); + ESP_GOTO_ON_ERROR(ret, err, TAG, "Add new I2C device(%d) failed(%s)", REG_TYPE_ADDR[i], esp_err_to_name(ret)); + } + ch422g->base.config.io_count = IO_COUNT; - ch422g->i2c_num = i2c_num; - ch422g->i2c_address = i2c_address; ch422g->regs.wr_set = REG_WR_SET_DEFAULT_VAL; ch422g->regs.wr_oc = REG_WR_OC_DEFAULT_VAL; ch422g->regs.wr_io = REG_WR_IO_DEFAULT_VAL; @@ -95,14 +118,21 @@ esp_err_t esp_io_expander_new_i2c_ch422g(i2c_port_t i2c_num, uint32_t i2c_addres ch422g->base.del = del; ch422g->base.reset = reset; - esp_err_t ret = ESP_OK; /* Reset configuration and register status */ ESP_GOTO_ON_ERROR(reset(&ch422g->base), err, TAG, "Reset failed"); - *handle = &ch422g->base; + *handle_ret = &ch422g->base; return ESP_OK; + err: - free(ch422g); + if (ch422g != NULL) { + for (int i = 0; i < CH422G_REG_TYPE_NUM; i++) { + if (ch422g->i2c_handles[i]) { + i2c_master_bus_rm_device(ch422g->i2c_handles[i]); + } + } + free(ch422g); + } return ret; } @@ -113,9 +143,8 @@ esp_err_t esp_io_expander_ch422g_set_oc_open_drain(esp_io_expander_handle_t hand // WR-SET ESP_RETURN_ON_ERROR( - i2c_master_write_to_device( - ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) - ), TAG, "Write WR_SET reg failed" + i2c_master_transmit(ch422g->i2c_handles[CH422G_REG_TYPE_WR_SET], &data, sizeof(data), I2C_TIMEOUT_MS), + TAG, "Write WR_SET reg failed" ); ch422g->regs.wr_set = data; @@ -129,9 +158,8 @@ esp_err_t esp_io_expander_ch422g_set_oc_push_pull(esp_io_expander_handle_t handl // WR-SET ESP_RETURN_ON_ERROR( - i2c_master_write_to_device( - ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) - ), TAG, "Write WR_SET reg failed" + i2c_master_transmit(ch422g->i2c_handles[CH422G_REG_TYPE_WR_SET], &data, sizeof(data), I2C_TIMEOUT_MS), + TAG, "Write WR_SET reg failed" ); ch422g->regs.wr_set = data; @@ -145,12 +173,11 @@ esp_err_t esp_io_expander_ch422g_set_all_input(esp_io_expander_handle_t handle) // WR-SET ESP_RETURN_ON_ERROR( - i2c_master_write_to_device( - ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) - ), TAG, "Write WR_SET reg failed" + i2c_master_transmit(ch422g->i2c_handles[CH422G_REG_TYPE_WR_SET], &data, sizeof(data), I2C_TIMEOUT_MS), + TAG, "Write WR_SET reg failed" ); ch422g->regs.wr_set = data; - // Delay 1ms to wait for the IO expander to switch to input mode + // Delay 2ms to wait for the IO expander to switch to input mode vTaskDelay(pdMS_TO_TICKS(2)); return ESP_OK; @@ -163,9 +190,8 @@ esp_err_t esp_io_expander_ch422g_set_all_output(esp_io_expander_handle_t handle) // WR-SET ESP_RETURN_ON_ERROR( - i2c_master_write_to_device( - ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) - ), TAG, "Write WR_SET reg failed" + i2c_master_transmit(ch422g->i2c_handles[CH422G_REG_TYPE_WR_SET], &data, sizeof(data), I2C_TIMEOUT_MS), + TAG, "Write WR_SET reg failed" ); ch422g->regs.wr_set = data; @@ -179,9 +205,8 @@ esp_err_t esp_io_expander_ch422g_enter_sleep(esp_io_expander_handle_t handle) // WR-SET ESP_RETURN_ON_ERROR( - i2c_master_write_to_device( - ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) - ), TAG, "Write WR_SET reg failed" + i2c_master_transmit(ch422g->i2c_handles[CH422G_REG_TYPE_WR_SET], &data, sizeof(data), I2C_TIMEOUT_MS), + TAG, "Write WR_SET reg failed" ); ch422g->regs.wr_set = data; @@ -195,9 +220,8 @@ esp_err_t esp_io_expander_ch422g_exit_sleep(esp_io_expander_handle_t handle) // WR-SET ESP_RETURN_ON_ERROR( - i2c_master_write_to_device( - ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) - ), TAG, "Write WR_SET reg failed" + i2c_master_transmit(ch422g->i2c_handles[CH422G_REG_TYPE_WR_SET], &data, sizeof(data), I2C_TIMEOUT_MS), + TAG, "Write WR_SET reg failed" ); ch422g->regs.wr_set = data; @@ -210,7 +234,7 @@ static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value uint8_t temp = 0; ESP_RETURN_ON_ERROR( - i2c_master_read_from_device(ch422g->i2c_num, CH422G_REG_RD_IO, &temp, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), + i2c_master_transmit(ch422g->i2c_handles[CH422G_REG_TYPE_RD_IO], &temp, sizeof(temp), I2C_TIMEOUT_MS), TAG, "Read RD-IO reg failed" ); *value = temp; @@ -228,7 +252,7 @@ static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t valu // WR-OC if (wr_oc_data) { ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_OC, &wr_oc_data, sizeof(wr_oc_data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), + i2c_master_transmit(ch422g->i2c_handles[CH422G_REG_TYPE_WR_OC], &wr_oc_data, sizeof(wr_oc_data), I2C_TIMEOUT_MS), TAG, "Write WR-OC reg failed" ); ch422g->regs.wr_oc = wr_oc_data; @@ -237,7 +261,7 @@ static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t valu // WR-IO if (wr_io_data) { ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_IO, &wr_io_data, sizeof(wr_io_data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), + i2c_master_transmit(ch422g->i2c_handles[CH422G_REG_TYPE_WR_IO], &wr_io_data, sizeof(wr_io_data), I2C_TIMEOUT_MS), TAG, "Write WR-IO reg failed" ); ch422g->regs.wr_io = wr_io_data; @@ -269,17 +293,14 @@ static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t v // WR-SET ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write WR_SET reg failed" + i2c_master_transmit(ch422g->i2c_handles[CH422G_REG_TYPE_WR_SET], &data, sizeof(data), I2C_TIMEOUT_MS), TAG, + "Write WR_SET reg failed" ); ch422g->regs.wr_set = data; return ESP_OK; } -#define DIR_OUT_VALUE (0xFFF) -#define DIR_IN_VALUE (0xF00) - static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *value) { esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); @@ -301,6 +322,11 @@ static esp_err_t del(esp_io_expander_t *handle) { esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); + for (int i = 0; i < CH422G_REG_TYPE_NUM; i++) { + if (ch422g->i2c_handles[i]) { + i2c_master_bus_rm_device(ch422g->i2c_handles[i]); + } + } free(ch422g); return ESP_OK; } diff --git a/src/port/esp_io_expander_ch422g.h b/src/port/esp_io_expander_ch422g.h index a312edf..65e0a67 100644 --- a/src/port/esp_io_expander_ch422g.h +++ b/src/port/esp_io_expander_ch422g.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,49 +7,109 @@ #pragma once #include - -#include "driver/i2c.h" #include "esp_err.h" - +#include "driver/i2c_master.h" #include "esp_io_expander.h" #ifdef __cplusplus extern "C" { #endif -#define ESP_IO_EXPANDER_CH422G_VER_MAJOR (0) -#define ESP_IO_EXPANDER_CH422G_VER_MINOR (1) +#define ESP_IO_EXPANDER_CH422G_VER_MAJOR (2) +#define ESP_IO_EXPANDER_CH422G_VER_MINOR (0) #define ESP_IO_EXPANDER_CH422G_VER_PATCH (0) /** - * @brief Create a new ch422g IO expander driver + * @brief Create a CH422G IO expander object * - * @note The I2C communication should be initialized before use this function - * - * @param i2c_num: I2C port num - * @param i2c_address: I2C address of chip - * @param handle: IO expander handle + * @param[in] i2c_bus I2C bus handle. Obtained from `i2c_new_master_bus()` + * @param[in] i2c_dev_cfg I2C device configuration. + * @param[out] handle_ret Handle to created IO expander object * * @return * - ESP_OK: Success, otherwise returns ESP_ERR_xxx */ -esp_err_t esp_io_expander_new_i2c_ch422g(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle); +esp_err_t esp_io_expander_new_i2c_ch422g( + i2c_master_bus_handle_t i2c_bus, const i2c_device_config_t *i2c_dev_cfg, esp_io_expander_handle_t *handle_ret +); /** * @brief I2C address of the ch422g. Just to keep the same with other IO expanders, but it is ignored. */ #define ESP_IO_EXPANDER_I2C_CH422G_ADDRESS (0x24) +/** + * @brief Macro to create a I2C device configuration for CH422G + * + * @param[in] address I2C device address + * @param[in] freq_hz I2C bus frequency + * @return I2C device configuration + */ +#define ESP_IO_EXPANDER_I2C_CH422G_DEVICE_CFG(address, freq_hz) \ + { \ + .dev_addr_length = I2C_ADDR_BIT_LEN_7, \ + .device_address = address, \ + .scl_speed_hz = freq_hz, \ + } + +/** + * @brief Set CH422G to open-drain output mode + * + * @param[in] handle IO expander handle + * @return + * - ESP_OK: Success + * - Others: Failure + */ esp_err_t esp_io_expander_ch422g_set_oc_open_drain(esp_io_expander_handle_t handle); +/** + * @brief Set CH422G to push-pull output mode + * + * @param[in] handle IO expander handle + * @return + * - ESP_OK: Success + * - Others: Failure + */ esp_err_t esp_io_expander_ch422g_set_oc_push_pull(esp_io_expander_handle_t handle); +/** + * @brief Set all CH422G pins to input mode + * + * @param[in] handle IO expander handle + * @return + * - ESP_OK: Success + * - Others: Failure + */ esp_err_t esp_io_expander_ch422g_set_all_input(esp_io_expander_handle_t handle); +/** + * @brief Set all CH422G pins to output mode + * + * @param[in] handle IO expander handle + * @return + * - ESP_OK: Success + * - Others: Failure + */ esp_err_t esp_io_expander_ch422g_set_all_output(esp_io_expander_handle_t handle); +/** + * @brief Make CH422G enter sleep mode + * + * @param[in] handle IO expander handle + * @return + * - ESP_OK: Success + * - Others: Failure + */ esp_err_t esp_io_expander_ch422g_enter_sleep(esp_io_expander_handle_t handle); +/** + * @brief Make CH422G exit sleep mode + * + * @param[in] handle IO expander handle + * @return + * - ESP_OK: Success + * - Others: Failure + */ esp_err_t esp_io_expander_ch422g_exit_sleep(esp_io_expander_handle_t handle); #ifdef __cplusplus diff --git a/src/port/esp_io_expander_ht8574.c b/src/port/esp_io_expander_ht8574.c index 840992f..7bee2f7 100644 --- a/src/port/esp_io_expander_ht8574.c +++ b/src/port/esp_io_expander_ht8574.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,19 +7,15 @@ #include #include #include - -#include "driver/i2c.h" #include "esp_bit_defs.h" #include "esp_check.h" #include "esp_log.h" - #include "esp_io_expander.h" #include "esp_io_expander_ht8574.h" +#include "private/esp_expander_utils.h" -#include "esp_expander_utils.h" - -/* Timeout of each I2C communication */ -#define I2C_TIMEOUT_MS (10) +/* I2C communication related */ +#define I2C_TIMEOUT_MS (1000) #define IO_COUNT (8) @@ -29,11 +25,11 @@ /** * @brief Device Structure Type + * */ typedef struct { esp_io_expander_t base; - i2c_port_t i2c_num; - uint32_t i2c_address; + i2c_master_dev_handle_t i2c_handle; struct { uint8_t direction; uint8_t output; @@ -50,20 +46,25 @@ static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *v static esp_err_t reset(esp_io_expander_t *handle); static esp_err_t del(esp_io_expander_t *handle); -esp_err_t esp_io_expander_new_i2c_ht8574(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle) +esp_err_t esp_io_expander_new_i2c_ht8574( + i2c_master_bus_handle_t i2c_bus, const i2c_device_config_t *i2c_dev_cfg, esp_io_expander_handle_t *handle_ret +) { ESP_LOGI(TAG, "version: %d.%d.%d", ESP_IO_EXPANDER_HT8574_VER_MAJOR, ESP_IO_EXPANDER_HT8574_VER_MINOR, ESP_IO_EXPANDER_HT8574_VER_PATCH); - ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "Invalid i2c num"); - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); + ESP_RETURN_ON_FALSE(handle_ret != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid handle_ret"); + // Allocate memory for driver object esp_io_expander_ht8574_t *ht8574 = (esp_io_expander_ht8574_t *)calloc(1, sizeof(esp_io_expander_ht8574_t)); - ESP_RETURN_ON_FALSE(ht8574, ESP_ERR_NO_MEM, TAG, "Malloc failed"); + ESP_RETURN_ON_FALSE(ht8574 != NULL, ESP_ERR_NO_MEM, TAG, "Malloc failed"); + // Add new I2C device + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_ERROR(i2c_master_bus_add_device(i2c_bus, i2c_dev_cfg, &ht8574->i2c_handle), err, TAG, "Add new I2C device failed"); + + // Initialize device structure ht8574->base.config.io_count = IO_COUNT; ht8574->base.config.flags.dir_out_bit_zero = 1; - ht8574->i2c_num = i2c_num; - ht8574->i2c_address = i2c_address; ht8574->base.read_input_reg = read_input_reg; ht8574->base.write_output_reg = write_output_reg; ht8574->base.read_output_reg = read_output_reg; @@ -72,27 +73,28 @@ esp_err_t esp_io_expander_new_i2c_ht8574(i2c_port_t i2c_num, uint32_t i2c_addres ht8574->base.del = del; ht8574->base.reset = reset; - esp_err_t ret = ESP_OK; - /* Reset configuration and register status */ + // Reset configuration and register status ESP_GOTO_ON_ERROR(reset(&ht8574->base), err, TAG, "Reset failed"); - *handle = &ht8574->base; + *handle_ret = &ht8574->base; return ESP_OK; + err: - free(ht8574); + if (ht8574 != NULL) { + if (ht8574->i2c_handle != NULL) { + i2c_master_bus_rm_device(ht8574->i2c_handle); + } + free(ht8574); + } return ret; } static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value) { esp_io_expander_ht8574_t *ht8574 = (esp_io_expander_ht8574_t *)__containerof(handle, esp_io_expander_ht8574_t, base); - uint8_t temp = 0; - // *INDENT-OFF* - ESP_RETURN_ON_ERROR( - i2c_master_read_from_device(ht8574->i2c_num, ht8574->i2c_address, &temp, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Read input reg failed"); - // *INDENT-ON* + + ESP_RETURN_ON_ERROR(i2c_master_receive(ht8574->i2c_handle, &temp, sizeof(temp), I2C_TIMEOUT_MS), TAG, "Read input reg failed"); *value = temp; return ESP_OK; } @@ -101,11 +103,9 @@ static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t valu { esp_io_expander_ht8574_t *ht8574 = (esp_io_expander_ht8574_t *)__containerof(handle, esp_io_expander_ht8574_t, base); value &= 0xff; - uint8_t data = (uint8_t)value; - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(ht8574->i2c_num, ht8574->i2c_address, &data, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write output reg failed"); + + ESP_RETURN_ON_ERROR(i2c_master_transmit(ht8574->i2c_handle, &data, sizeof(data), I2C_TIMEOUT_MS), TAG, "Write output reg failed"); ht8574->regs.output = value; return ESP_OK; } @@ -144,6 +144,7 @@ static esp_err_t del(esp_io_expander_t *handle) { esp_io_expander_ht8574_t *ht8574 = (esp_io_expander_ht8574_t *)__containerof(handle, esp_io_expander_ht8574_t, base); + ESP_RETURN_ON_ERROR(i2c_master_bus_rm_device(ht8574->i2c_handle), TAG, "Remove I2C device failed"); free(ht8574); return ESP_OK; } diff --git a/src/port/esp_io_expander_ht8574.h b/src/port/esp_io_expander_ht8574.h index 316994c..ef6369d 100644 --- a/src/port/esp_io_expander_ht8574.h +++ b/src/port/esp_io_expander_ht8574.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,33 +7,31 @@ #pragma once #include - -#include "driver/i2c.h" #include "esp_err.h" - +#include "driver/i2c_master.h" #include "esp_io_expander.h" #ifdef __cplusplus extern "C" { #endif -#define ESP_IO_EXPANDER_HT8574_VER_MAJOR (0) -#define ESP_IO_EXPANDER_HT8574_VER_MINOR (1) +#define ESP_IO_EXPANDER_HT8574_VER_MAJOR (2) +#define ESP_IO_EXPANDER_HT8574_VER_MINOR (0) #define ESP_IO_EXPANDER_HT8574_VER_PATCH (0) /** - * @brief Create a new ht8574 IO expander driver - * - * @note The I2C communication should be initialized before use this function + * @brief Create a HT8574 IO expander object * - * @param i2c_num: I2C port num - * @param i2c_address: I2C address of chip - * @param handle: IO expander handle + * @param[in] i2c_bus I2C bus handle. Obtained from `i2c_new_master_bus()` + * @param[in] i2c_dev_cfg I2C device configuration. + * @param[out] handle_ret Handle to created IO expander object * * @return * - ESP_OK: Success, otherwise returns ESP_ERR_xxx */ -esp_err_t esp_io_expander_new_i2c_ht8574(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle); +esp_err_t esp_io_expander_new_i2c_ht8574( + i2c_master_bus_handle_t i2c_bus, const i2c_device_config_t *i2c_dev_cfg, esp_io_expander_handle_t *handle_ret +); /** * @brief I2C address of the ht8574 @@ -58,6 +56,19 @@ esp_err_t esp_io_expander_new_i2c_ht8574(i2c_port_t i2c_num, uint32_t i2c_addres #define ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_011 (0x2B) #define ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_100 (0x2C) +/** + * @brief Macro to create a I2C device configuration for HT8574 + * + * @param[in] address I2C device address + * @param[in] freq_hz I2C bus frequency + * @return I2C device configuration + */ +#define ESP_IO_EXPANDER_I2C_HT8574_DEVICE_CFG(address, freq_hz) \ + { \ + .dev_addr_length = I2C_ADDR_BIT_LEN_7, \ + .device_address = address, \ + .scl_speed_hz = freq_hz, \ + } #ifdef __cplusplus } diff --git a/src/port/esp_io_expander_tca9554.c b/src/port/esp_io_expander_tca9554.c index ef17872..1da544f 100644 --- a/src/port/esp_io_expander_tca9554.c +++ b/src/port/esp_io_expander_tca9554.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,19 +7,16 @@ #include #include #include - -#include "driver/i2c.h" #include "esp_bit_defs.h" #include "esp_check.h" #include "esp_log.h" - #include "esp_io_expander.h" #include "esp_io_expander_tca9554.h" +#include "private/esp_expander_utils.h" -#include "esp_expander_utils.h" - -/* Timeout of each I2C communication */ -#define I2C_TIMEOUT_MS (10) +/* I2C communication related */ +#define I2C_TIMEOUT_MS (1000) +#define I2C_CLK_SPEED (400000) #define IO_COUNT (8) @@ -34,11 +31,11 @@ /** * @brief Device Structure Type + * */ typedef struct { esp_io_expander_t base; - i2c_port_t i2c_num; - uint32_t i2c_address; + i2c_master_dev_handle_t i2c_handle; struct { uint8_t direction; uint8_t output; @@ -55,20 +52,24 @@ static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *v static esp_err_t reset(esp_io_expander_t *handle); static esp_err_t del(esp_io_expander_t *handle); -esp_err_t esp_io_expander_new_i2c_tca9554(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle) +esp_err_t esp_io_expander_new_i2c_tca9554( + i2c_master_bus_handle_t i2c_bus, const i2c_device_config_t *i2c_dev_cfg, esp_io_expander_handle_t *handle_ret +) { ESP_LOGI(TAG, "version: %d.%d.%d", ESP_IO_EXPANDER_TCA9554_VER_MAJOR, ESP_IO_EXPANDER_TCA9554_VER_MINOR, ESP_IO_EXPANDER_TCA9554_VER_PATCH); - ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "Invalid i2c num"); - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); + ESP_RETURN_ON_FALSE(handle_ret != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid handle_ret"); + // Allocate memory for driver object esp_io_expander_tca9554_t *tca9554 = (esp_io_expander_tca9554_t *)calloc(1, sizeof(esp_io_expander_tca9554_t)); - ESP_RETURN_ON_FALSE(tca9554, ESP_ERR_NO_MEM, TAG, "Malloc failed"); + ESP_RETURN_ON_FALSE(tca9554 != NULL, ESP_ERR_NO_MEM, TAG, "Malloc failed"); + + // Add new I2C device + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_ERROR(i2c_master_bus_add_device(i2c_bus, i2c_dev_cfg, &tca9554->i2c_handle), err, TAG, "Add new I2C device failed"); tca9554->base.config.io_count = IO_COUNT; tca9554->base.config.flags.dir_out_bit_zero = 1; - tca9554->i2c_num = i2c_num; - tca9554->i2c_address = i2c_address; tca9554->base.read_input_reg = read_input_reg; tca9554->base.write_output_reg = write_output_reg; tca9554->base.read_output_reg = read_output_reg; @@ -77,14 +78,19 @@ esp_err_t esp_io_expander_new_i2c_tca9554(i2c_port_t i2c_num, uint32_t i2c_addre tca9554->base.del = del; tca9554->base.reset = reset; - esp_err_t ret = ESP_OK; /* Reset configuration and register status */ ESP_GOTO_ON_ERROR(reset(&tca9554->base), err, TAG, "Reset failed"); - *handle = &tca9554->base; + *handle_ret = &tca9554->base; return ESP_OK; + err: - free(tca9554); + if (tca9554 != NULL) { + if (tca9554->i2c_handle != NULL) { + i2c_master_bus_rm_device(tca9554->i2c_handle); + } + free(tca9554); + } return ret; } @@ -93,11 +99,9 @@ static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value esp_io_expander_tca9554_t *tca9554 = (esp_io_expander_tca9554_t *)__containerof(handle, esp_io_expander_tca9554_t, base); uint8_t temp = 0; - // *INDENT-OFF* - ESP_RETURN_ON_ERROR( - i2c_master_write_read_device(tca9554->i2c_num, tca9554->i2c_address, (uint8_t[]){INPUT_REG_ADDR}, 1, &temp, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Read input reg failed"); - // *INDENT-ON* + ESP_RETURN_ON_ERROR(i2c_master_transmit_receive(tca9554->i2c_handle, (uint8_t[]) { + INPUT_REG_ADDR + }, 1, &temp, sizeof(temp), I2C_TIMEOUT_MS), TAG, "Read input reg failed"); *value = temp; return ESP_OK; } @@ -108,9 +112,7 @@ static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t valu value &= 0xff; uint8_t data[] = {OUTPUT_REG_ADDR, value}; - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(tca9554->i2c_num, tca9554->i2c_address, data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write output reg failed"); + ESP_RETURN_ON_ERROR(i2c_master_transmit(tca9554->i2c_handle, data, sizeof(data), I2C_TIMEOUT_MS), TAG, "Write output reg failed"); tca9554->regs.output = value; return ESP_OK; } @@ -129,9 +131,7 @@ static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t v value &= 0xff; uint8_t data[] = {DIRECTION_REG_ADDR, value}; - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(tca9554->i2c_num, tca9554->i2c_address, data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write direction reg failed"); + ESP_RETURN_ON_ERROR(i2c_master_transmit(tca9554->i2c_handle, data, sizeof(data), I2C_TIMEOUT_MS), TAG, "Write direction reg failed"); tca9554->regs.direction = value; return ESP_OK; } @@ -155,6 +155,7 @@ static esp_err_t del(esp_io_expander_t *handle) { esp_io_expander_tca9554_t *tca9554 = (esp_io_expander_tca9554_t *)__containerof(handle, esp_io_expander_tca9554_t, base); + ESP_RETURN_ON_ERROR(i2c_master_bus_rm_device(tca9554->i2c_handle), TAG, "Remove I2C device failed"); free(tca9554); return ESP_OK; } diff --git a/src/port/esp_io_expander_tca9554.h b/src/port/esp_io_expander_tca9554.h index b3f39ed..b7642e8 100644 --- a/src/port/esp_io_expander_tca9554.h +++ b/src/port/esp_io_expander_tca9554.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,33 +12,31 @@ #pragma once #include - -#include "driver/i2c.h" #include "esp_err.h" - +#include "driver/i2c_master.h" #include "esp_io_expander.h" #ifdef __cplusplus extern "C" { #endif -#define ESP_IO_EXPANDER_TCA9554_VER_MAJOR (1) +#define ESP_IO_EXPANDER_TCA9554_VER_MAJOR (2) #define ESP_IO_EXPANDER_TCA9554_VER_MINOR (0) #define ESP_IO_EXPANDER_TCA9554_VER_PATCH (1) /** - * @brief Create a new TCA9554 IO expander driver + * @brief Create a TCA9554(A) IO expander object * - * @note The I2C communication should be initialized before use this function - * - * @param i2c_num: I2C port num - * @param i2c_address: I2C address of chip - * @param handle: IO expander handle + * @param[in] i2c_bus I2C bus handle. Obtained from `i2c_new_master_bus()` + * @param[in] i2c_dev_cfg I2C device configuration. + * @param[out] handle_ret Handle to created IO expander object * * @return * - ESP_OK: Success, otherwise returns ESP_ERR_xxx */ -esp_err_t esp_io_expander_new_i2c_tca9554(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle); +esp_err_t esp_io_expander_new_i2c_tca9554( + i2c_master_bus_handle_t i2c_bus, const i2c_device_config_t *i2c_dev_cfg, esp_io_expander_handle_t *handle_ret +); /** * @brief I2C address of the TCA9554 @@ -93,6 +91,20 @@ esp_err_t esp_io_expander_new_i2c_tca9554(i2c_port_t i2c_num, uint32_t i2c_addre #define ESP_IO_EXPANDER_I2C_TCA9554A_ADDRESS_110 (0x3E) #define ESP_IO_EXPANDER_I2C_TCA9554A_ADDRESS_111 (0x3F) +/** + * @brief Macro to create a I2C device configuration for TCA9554(A) + * + * @param[in] address I2C device address + * @param[in] freq_hz I2C bus frequency + * @return I2C device configuration + */ +#define ESP_IO_EXPANDER_I2C_TCA9554_DEVICE_CFG(address, freq_hz) \ + { \ + .dev_addr_length = I2C_ADDR_BIT_LEN_7, \ + .device_address = address, \ + .scl_speed_hz = freq_hz, \ + } + #ifdef __cplusplus } #endif diff --git a/src/port/esp_io_expander_tca95xx_16bit.c b/src/port/esp_io_expander_tca95xx_16bit.c index 4062d31..d1a6f73 100644 --- a/src/port/esp_io_expander_tca95xx_16bit.c +++ b/src/port/esp_io_expander_tca95xx_16bit.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,19 +7,15 @@ #include #include #include - -#include "driver/i2c.h" #include "esp_bit_defs.h" #include "esp_check.h" #include "esp_log.h" - #include "esp_io_expander.h" #include "esp_io_expander_tca95xx_16bit.h" +#include "private/esp_expander_utils.h" -#include "esp_expander_utils.h" - -/* Timeout of each I2C communication */ -#define I2C_TIMEOUT_MS (10) +/* I2C communication related */ +#define I2C_TIMEOUT_MS (1000) #define IO_COUNT (16) @@ -34,11 +30,11 @@ /** * @brief Device Structure Type + * */ typedef struct { esp_io_expander_t base; - i2c_port_t i2c_num; - uint32_t i2c_address; + i2c_master_dev_handle_t i2c_handle; struct { uint16_t direction; uint16_t output; @@ -55,20 +51,24 @@ static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *v static esp_err_t reset(esp_io_expander_t *handle); static esp_err_t del(esp_io_expander_t *handle); -esp_err_t esp_io_expander_new_i2c_tca95xx_16bit(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle) +esp_err_t esp_io_expander_new_i2c_tca95xx_16bit( + i2c_master_bus_handle_t i2c_bus, const i2c_device_config_t *i2c_dev_cfg, esp_io_expander_handle_t *handle_ret +) { ESP_LOGI(TAG, "version: %d.%d.%d", ESP_IO_EXPANDER_TCA95XX_16BIT_VER_MAJOR, ESP_IO_EXPANDER_TCA95XX_16BIT_VER_MINOR, ESP_IO_EXPANDER_TCA95XX_16BIT_VER_PATCH); - ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "Invalid i2c num"); - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); + ESP_RETURN_ON_FALSE(handle_ret != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid handle_ret"); + // Allocate memory for driver object esp_io_expander_tca95xx_16bit_t *tca = (esp_io_expander_tca95xx_16bit_t *)calloc(1, sizeof(esp_io_expander_tca95xx_16bit_t)); ESP_RETURN_ON_FALSE(tca, ESP_ERR_NO_MEM, TAG, "Malloc failed"); + // Add new I2C device + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_ERROR(i2c_master_bus_add_device(i2c_bus, i2c_dev_cfg, &tca->i2c_handle), err, TAG, "Add new I2C device failed"); + tca->base.config.io_count = IO_COUNT; tca->base.config.flags.dir_out_bit_zero = 1; - tca->i2c_num = i2c_num; - tca->i2c_address = i2c_address; tca->base.read_input_reg = read_input_reg; tca->base.write_output_reg = write_output_reg; tca->base.read_output_reg = read_output_reg; @@ -77,14 +77,18 @@ esp_err_t esp_io_expander_new_i2c_tca95xx_16bit(i2c_port_t i2c_num, uint32_t i2c tca->base.del = del; tca->base.reset = reset; - esp_err_t ret = ESP_OK; /* Reset configuration and register status */ ESP_GOTO_ON_ERROR(reset(&tca->base), err, TAG, "Reset failed"); - *handle = &tca->base; + *handle_ret = &tca->base; return ESP_OK; err: - free(tca); + if (tca != NULL) { + if (tca->i2c_handle != NULL) { + i2c_master_bus_rm_device(tca->i2c_handle); + } + free(tca); + } return ret; } @@ -93,11 +97,9 @@ static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value esp_io_expander_tca95xx_16bit_t *tca = (esp_io_expander_tca95xx_16bit_t *)__containerof(handle, esp_io_expander_tca95xx_16bit_t, base); uint8_t temp[2] = {0, 0}; - // *INDENT-OFF* - ESP_RETURN_ON_ERROR( - i2c_master_write_read_device(tca->i2c_num, tca->i2c_address, (uint8_t[]){INPUT_REG_ADDR}, 1, (uint8_t*)&temp, 2, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Read input reg failed"); - // *INDENT-ON* + ESP_RETURN_ON_ERROR(i2c_master_transmit_receive(tca->i2c_handle, (uint8_t[]) { + INPUT_REG_ADDR + }, 1, temp, sizeof(temp), I2C_TIMEOUT_MS), TAG, "Read input reg failed"); *value = (((uint32_t)temp[1]) << 8) | (temp[0]); return ESP_OK; } @@ -108,9 +110,7 @@ static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t valu value &= 0xffff; uint8_t data[] = {OUTPUT_REG_ADDR, value & 0xff, value >> 8}; - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(tca->i2c_num, tca->i2c_address, data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write output reg failed"); + ESP_RETURN_ON_ERROR(i2c_master_transmit(tca->i2c_handle, data, sizeof(data), I2C_TIMEOUT_MS), TAG, "Write output reg failed"); tca->regs.output = value; return ESP_OK; } @@ -129,9 +129,7 @@ static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t v value &= 0xffff; uint8_t data[] = {DIRECTION_REG_ADDR, value & 0xff, value >> 8}; - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(tca->i2c_num, tca->i2c_address, data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write direction reg failed"); + ESP_RETURN_ON_ERROR(i2c_master_transmit(tca->i2c_handle, data, sizeof(data), I2C_TIMEOUT_MS), TAG, "Write direction reg failed"); tca->regs.direction = value; return ESP_OK; } @@ -155,6 +153,7 @@ static esp_err_t del(esp_io_expander_t *handle) { esp_io_expander_tca95xx_16bit_t *tca = (esp_io_expander_tca95xx_16bit_t *)__containerof(handle, esp_io_expander_tca95xx_16bit_t, base); + ESP_RETURN_ON_ERROR(i2c_master_bus_rm_device(tca->i2c_handle), TAG, "Remove I2C device failed"); free(tca); return ESP_OK; } diff --git a/src/port/esp_io_expander_tca95xx_16bit.h b/src/port/esp_io_expander_tca95xx_16bit.h index 80cb54f..816557f 100644 --- a/src/port/esp_io_expander_tca95xx_16bit.h +++ b/src/port/esp_io_expander_tca95xx_16bit.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,33 +7,31 @@ #pragma once #include - -#include "driver/i2c.h" #include "esp_err.h" - +#include "driver/i2c_master.h" #include "esp_io_expander.h" #ifdef __cplusplus extern "C" { #endif -#define ESP_IO_EXPANDER_TCA95XX_16BIT_VER_MAJOR (1) +#define ESP_IO_EXPANDER_TCA95XX_16BIT_VER_MAJOR (2) #define ESP_IO_EXPANDER_TCA95XX_16BIT_VER_MINOR (0) #define ESP_IO_EXPANDER_TCA95XX_16BIT_VER_PATCH (0) /** - * @brief Create a new TCA95XX_16BIT IO expander driver + * @brief Create a TCA95539 or TCA9555 IO expander object * - * @note The I2C communication should be initialized before use this function - * - * @param i2c_num: I2C port num - * @param i2c_address: I2C address of chip (\see esp_io_expander_tca_95xx_16bit_address) - * @param handle: IO expander handle + * @param[in] i2c_bus I2C bus handle. Obtained from `i2c_new_master_bus()` + * @param[in] i2c_dev_cfg I2C device configuration. + * @param[out] handle_ret Handle to created IO expander object * * @return * - ESP_OK: Success, otherwise returns ESP_ERR_xxx */ -esp_err_t esp_io_expander_new_i2c_tca95xx_16bit(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle); +esp_err_t esp_io_expander_new_i2c_tca95xx_16bit( + i2c_master_bus_handle_t i2c_bus, const i2c_device_config_t *i2c_dev_cfg, esp_io_expander_handle_t *handle_ret +); /** * @brief I2C address of the TCA9539 or TCA9555 @@ -77,6 +75,20 @@ enum esp_io_expander_tca_95xx_16bit_address { ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_111 = 0b0100111, }; +/** + * @brief Macro to create a I2C device configuration for TCA9539 or TCA9555 + * + * @param[in] address I2C device address + * @param[in] freq_hz I2C bus frequency + * @return I2C device configuration + */ +#define ESP_IO_EXPANDER_I2C_TCA95XX_16BIT_DEVICE_CFG(address, freq_hz) \ + { \ + .dev_addr_length = I2C_ADDR_BIT_LEN_7, \ + .device_address = address, \ + .scl_speed_hz = freq_hz, \ + } + #ifdef __cplusplus } #endif diff --git a/src/esp_expander_utils.h b/src/private/esp_expander_utils.h similarity index 100% rename from src/esp_expander_utils.h rename to src/private/esp_expander_utils.h diff --git a/test_apps/main/test_chip_general.cpp b/test_apps/main/test_chip_general.cpp index 007d627..6cfb8cd 100644 --- a/test_apps/main/test_chip_general.cpp +++ b/test_apps/main/test_chip_general.cpp @@ -6,7 +6,6 @@ #include #include -#include "driver/i2c.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_heap_caps.h" @@ -25,17 +24,12 @@ static const char *TAG = "general_test"; #define TEST_HOST_I2C_SDA_PIN (47) #define TEST_DEVICE_ADDRESS (ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000) -#define HOST_CONFIG_DEFAULT(scl, sda) \ +#define HOST_CONFIG_DEFAULT() \ { \ - .mode = I2C_MODE_MASTER, \ - .sda_io_num = (sda), \ - .scl_io_num = (scl), \ - .sda_pullup_en = GPIO_PULLUP_ENABLE, \ - .scl_pullup_en = GPIO_PULLUP_ENABLE, \ - .master = { \ - .clk_speed = 400000, \ - }, \ - .clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL, \ + .i2c_port = TEST_HOST_ID, \ + .sda_io_num = static_cast(TEST_HOST_I2C_SDA_PIN), \ + .scl_io_num = static_cast(TEST_HOST_I2C_SCL_PIN), \ + .clk_source = I2C_CLK_SRC_DEFAULT, \ } #define _CREATE_DEVICE(name, ...) \ @@ -47,16 +41,19 @@ static const char *TAG = "general_test"; }) #define CREATE_DEVICE(name, ...) _CREATE_DEVICE(name, ##__VA_ARGS__) -static void init_host(void) +static i2c_master_bus_handle_t init_host(void) { - const i2c_config_t i2c_config = HOST_CONFIG_DEFAULT(TEST_HOST_I2C_SCL_PIN, TEST_HOST_I2C_SDA_PIN); - TEST_ASSERT_EQUAL(i2c_param_config(TEST_HOST_ID, &i2c_config), ESP_OK); - TEST_ASSERT_EQUAL(i2c_driver_install(TEST_HOST_ID, i2c_config.mode, 0, 0, 0), ESP_OK); + const i2c_master_bus_config_t i2c_config = HOST_CONFIG_DEFAULT(); + i2c_master_bus_handle_t i2c_handle = nullptr; + esp_err_t ret = i2c_new_master_bus(&i2c_config, &i2c_handle); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C install returned error"); + return i2c_handle; } -static void deinit_host(void) +static void deinit_host(i2c_master_bus_handle_t i2c_handle) { - TEST_ASSERT_EQUAL(i2c_driver_delete(TEST_HOST_ID), ESP_OK); + esp_err_t ret = i2c_del_master_bus(i2c_handle); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C uninstall returned error"); } static void test_device(std::shared_ptr device) @@ -73,7 +70,7 @@ static void test_device(std::shared_ptr device) std::shared_ptr expander = nullptr; \ \ ESP_LOGI(TAG, "Initialize I2C host"); \ - init_host(); \ + i2c_master_bus_handle_t i2c_handle = init_host(); \ \ ESP_LOGI(TAG, "Test constructor with (int host_id, uint8_t address) (external I2C)"); \ expander = CREATE_DEVICE(device_name, TEST_HOST_ID, TEST_DEVICE_ADDRESS); \ @@ -83,7 +80,7 @@ static void test_device(std::shared_ptr device) ESP_LOGI(TAG, "Test constructor with (const Config &config) (external I2C)"); \ Base::Config external_i2c_config = { \ .host_id = TEST_HOST_ID, \ - .device = Base::DeviceConfig{ \ + .device = Base::DevicePartialConfig{ \ .address = TEST_DEVICE_ADDRESS, \ }, \ }; \ @@ -92,7 +89,7 @@ static void test_device(std::shared_ptr device) expander = nullptr; \ \ ESP_LOGI(TAG, "Deinitialize I2C host"); \ - deinit_host(); \ + deinit_host(i2c_handle); \ \ ESP_LOGI(TAG, "Test constructor with (int scl_io, int sda_io, uint8_t address) (internal I2C)"); \ expander = CREATE_DEVICE(device_name, TEST_HOST_I2C_SCL_PIN, TEST_HOST_I2C_SDA_PIN, TEST_DEVICE_ADDRESS); \ @@ -103,10 +100,10 @@ static void test_device(std::shared_ptr device) Base::Config internal_i2c_config = { \ .host_id = TEST_HOST_ID, \ .host = Base::HostPartialConfig{ \ - .sda_io_num = TEST_HOST_I2C_SDA_PIN, \ - .scl_io_num = TEST_HOST_I2C_SCL_PIN, \ + .sda_io_num = static_cast(TEST_HOST_I2C_SDA_PIN), \ + .scl_io_num = static_cast(TEST_HOST_I2C_SCL_PIN), \ }, \ - .device = Base::DeviceConfig{ \ + .device = Base::DevicePartialConfig{ \ .address = TEST_DEVICE_ADDRESS, \ }, \ }; \