diff --git a/.github/workflows/arduino_lint.yml b/.github/workflows/arduino_lint.yml index 0a6b9a8..a0f4707 100644 --- a/.github/workflows/arduino_lint.yml +++ b/.github/workflows/arduino_lint.yml @@ -4,6 +4,9 @@ on: workflow_dispatch: pull_request: types: [opened, reopened, synchronize] + push: + branches: + - master jobs: lint: diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 9d3c04c..e87ca79 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -4,6 +4,9 @@ on: workflow_dispatch: pull_request: types: [opened, reopened, synchronize] + push: + branches: + - master jobs: build: diff --git a/.github/workflows/issue_comment.yml b/.github/workflows/issue_comment.yml deleted file mode 100644 index 3b6fcc1..0000000 --- a/.github/workflows/issue_comment.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Sync issue comments to JIRA - -# This workflow will be triggered when new issue comment is created (including PR comments) -on: issue_comment - -# Limit to single concurrent run for workflows which can create Jira issues. -# Same concurrency group is used in new_issues.yml -concurrency: jira_issues - -jobs: - sync_issue_comments_to_jira: - name: Sync Issue Comments to Jira - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Sync issue comments to JIRA - uses: espressif/github-actions/sync_issues_to_jira@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - JIRA_PASS: ${{ secrets.JIRA_PASS }} - JIRA_PROJECT: ${{ secrets.JIRA_PROJECT }} - JIRA_COMPONENT: ${{ secrets.JIRA_COMPONENT }} - JIRA_URL: ${{ secrets.JIRA_URL }} - JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/.github/workflows/new_issues.yml b/.github/workflows/new_issues.yml deleted file mode 100644 index f0fa402..0000000 --- a/.github/workflows/new_issues.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Sync issues to Jira - -# This workflow will be triggered when a new issue is opened -on: issues - -# Limit to single concurrent run for workflows which can create Jira issues. -# Same concurrency group is used in issue_comment.yml -concurrency: jira_issues - -jobs: - sync_issues_to_jira: - name: Sync issues to Jira - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Sync GitHub issues to Jira project - uses: espressif/github-actions/sync_issues_to_jira@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - JIRA_PASS: ${{ secrets.JIRA_PASS }} - JIRA_PROJECT: ${{ secrets.JIRA_PROJECT }} - JIRA_COMPONENT: ${{ secrets.JIRA_COMPONENT }} - JIRA_URL: ${{ secrets.JIRA_URL }} - JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/.github/workflows/new_prs.yml b/.github/workflows/new_prs.yml deleted file mode 100644 index 01d7fe2..0000000 --- a/.github/workflows/new_prs.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Sync remain PRs to Jira - -# This workflow will be triggered every hour, to sync remaining PRs (i.e. PRs with zero comment) to Jira project -# Note that, PRs can also get synced when new PR comment is created -on: - schedule: - - cron: "0 * * * *" - -# Limit to single concurrent run for workflows which can create Jira issues. -# Same concurrency group is used in issue_comment.yml -concurrency: jira_issues - -jobs: - sync_prs_to_jira: - name: Sync PRs to Jira - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Sync PRs to Jira project - uses: espressif/github-actions/sync_issues_to_jira@master - with: - cron_job: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - JIRA_PASS: ${{ secrets.JIRA_PASS }} - JIRA_PROJECT: ${{ secrets.JIRA_PROJECT }} - JIRA_COMPONENT: ${{ secrets.JIRA_COMPONENT }} - JIRA_URL: ${{ secrets.JIRA_URL }} - JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 7111ea9..cc477db 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -11,4 +11,4 @@ jobs: steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - - uses: pre-commit/action@v2.0.3 + - uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml index 45e9b78..333c718 100644 --- a/.github/workflows/upload_component.yml +++ b/.github/workflows/upload_component.yml @@ -15,6 +15,6 @@ jobs: - name: Upload components to component service uses: espressif/upload-components-ci-action@v1 with: - name: "ESP32_IO_Expander" + name: "esp32_io_expander" namespace: "espressif" api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index f893249..4e14044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # ChangeLog +## v1.1.1 - 2025-07-07 + +### Enhancements: + +* feat(ch422g): support enter/exit sleep + +### Bug Fixes: + +* fix(port): fix discarded qualifiers warning + +## v1.1.0 - 2025-02-07 + +### Enhancements: + +* feat(repo): add legacy header files to maintain compatibility +* feat(repo): update with esp-lib-utils v0.2.* + +## v1.0.1 - 2025-01-23 + +### Enhancements: + +* feat(base): update base class + +### Bug Fixes: + +* fix(repo): compile *.cpp files on MicroPython + ## v1.0.0 - 2024-12-06 ### Enhancements: diff --git a/CMakeLists.txt b/CMakeLists.txt index 6eefe2d..350d52b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,4 +13,9 @@ idf_component_register( driver ) -target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-missing-field-initializers) +target_compile_options(${COMPONENT_LIB} + PUBLIC + -Wno-missing-field-initializers + PRIVATE + $<$:-std=gnu++17> +) diff --git a/examples/ch422g/ch422g.ino b/examples/ch422g/ch422g.ino index 51f067c..89709e1 100644 --- a/examples/ch422g/ch422g.ino +++ b/examples/ch422g/ch422g.ino @@ -34,7 +34,6 @@ Since the input/output mode of CH422G's IO0-7 must remain consistent, the driver will only set IO0-7 to input mode when it determines that all pins are configured as input. Using pinMode and multiPinMode will be invalid. You can only set the pin working mode through enableAllIO_Input, enableAllIO_Output, enableOC_PushPull and enableOC_OpenDrain - * */ #include diff --git a/idf_component.yml b/idf_component.yml index 5d75262..4ad0e50 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.0.0" +version: "1.1.1" 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 @@ -6,5 +6,5 @@ issues: https://github.com/esp-arduino-libs/ESP32_IO_Expander/issues dependencies: idf: ">=5.1" espressif/esp-lib-utils: - version: ">=0.1.0,<=0.2.0" + version: "0.2.*" public: true diff --git a/library.properties b/library.properties index b15e1ee..4065fba 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ESP32_IO_Expander -version=1.0.0 +version=1.1.1 author=espressif maintainer=espressif sentence=ESP32_IO_Expander is a library designed for driving IO expander chips using ESP SoCs @@ -8,4 +8,4 @@ category=Other architectures=esp32 url=https://github.com/esp-arduino-libs/ESP32_IO_Expander includes=esp_io_expander.hpp -depends=esp-lib-utils (>=0.1.0 && <0.2.0) +depends=esp-lib-utils (>=0.2.0 && <0.3.0) diff --git a/micropython.cmake b/micropython.cmake index 207fc55..f0caa53 100644 --- a/micropython.cmake +++ b/micropython.cmake @@ -4,13 +4,20 @@ add_library(usermod_esp_io_expander INTERFACE) # Set the source directorya and find all source files. set(SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/src) -file(GLOB_RECURSE SRCS ${SRC_DIR}/*.c) +file(GLOB_RECURSE SRCS_C ${SRC_DIR}/*.c) +file(GLOB_RECURSE SRCS_CXX ${SRC_DIR}/*.cpp) # Add our source files to the library. -target_sources(usermod_esp_io_expander INTERFACE ${SRCS}) +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 new file mode 100644 index 0000000..7212f7d --- /dev/null +++ b/src/ESP_IOExpander.h @@ -0,0 +1,16 @@ + +/* + * 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 index 5280839..fb9fa32 100644 --- a/src/ESP_IOExpander_Library.h +++ b/src/ESP_IOExpander_Library.h @@ -6,7 +6,6 @@ /** * 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 diff --git a/src/base/esp_io_expander.h b/src/base/esp_io_expander.h new file mode 100644 index 0000000..0f26e86 --- /dev/null +++ b/src/base/esp_io_expander.h @@ -0,0 +1,16 @@ + +/* + * 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_base.cpp b/src/chip/esp_expander_base.cpp index 3ee7e66..1ad2262 100644 --- a/src/chip/esp_expander_base.cpp +++ b/src/chip/esp_expander_base.cpp @@ -1,9 +1,10 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +#include "inttypes.h" #include "driver/i2c.h" #include "esp_expander_utils.h" #include "esp_expander_base.hpp" @@ -13,20 +14,138 @@ namespace esp_expander { +void Base::Config::convertPartialToFull(void) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + if (isHostConfigValid() && std::holds_alternative(host.value())) { +#if ESP_UTILS_CONF_LOG_LEVEL == ESP_UTILS_LOG_LEVEL_DEBUG + printHostConfig(); +#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), + }, + .clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL, + }; + } + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); +} + +void Base::Config::printHostConfig(void) const +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + if (!isHostConfigValid()) { + ESP_UTILS_LOGI("\n\t{Host config}[skipped]"); + goto end; + } + + 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-> [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) + , 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) + ); + } else { + auto &config = std::get(host.value()); + ESP_UTILS_LOGI( + "\n\t{Host config}[partial]\n" + "\t\t-> [host_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" + , 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) + ); + } + +end: + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); +} + +void Base::Config::printDeviceConfig(void) 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_EXIT_WITH_THIS(); +} + +bool Base::configHostSkipInit(bool skip_init) +{ + ESP_UTILS_LOG_TRACE_ENTER_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(); + + return true; +} + bool Base::init(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(!checkIsInit(), false, "Already initialized"); + ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::INIT), false, "Already initialized"); + + // Convert the partial configuration to full configuration + _config.convertPartialToFull(); +#if ESP_UTILS_CONF_LOG_LEVEL == ESP_UTILS_LOG_LEVEL_DEBUG + _config.printHostConfig(); + _config.printDeviceConfig(); +#endif // ESP_UTILS_LOG_LEVEL_DEBUG - if (!checkIsSkipInitHost()) { - ESP_UTILS_CHECK_ERROR_RETURN(i2c_param_config(getHostID(), &_host_config), false, "I2C param config failed"); + // 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_driver_install(getHostID(), _host_config.mode, 0, 0, 0), false, "I2C driver install failed" + i2c_param_config(host_id, getHostFullConfig()), false, "I2C param config failed" ); - ESP_UTILS_LOGI("Init I2C host(%d)", _host_id); + ESP_UTILS_CHECK_ERROR_RETURN( + i2c_driver_install(host_id, getHostFullConfig()->mode, 0, 0, 0), false, "I2C driver install failed" + ); + ESP_UTILS_LOGD("Init I2C host(%d)", static_cast(host_id)); } - _flags.is_init = true; + + setState(State::INIT); ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); @@ -37,7 +156,7 @@ bool Base::reset(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + 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"); @@ -50,20 +169,20 @@ bool Base::del(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - if (checkIsBegun()) { + if (device_handle != nullptr) { ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_del(device_handle), false, "Delete failed"); device_handle = nullptr; - ESP_UTILS_LOGD("Delete IO expander(@%p)", device_handle); + ESP_UTILS_LOGD("Delete @%p", device_handle); } - if (checkIsInit()) { - if (!checkIsSkipInitHost()) { - ESP_UTILS_CHECK_ERROR_RETURN(i2c_driver_delete(getHostID()), false, "I2C driver delete failed"); - ESP_UTILS_LOGI("Delete I2C host(%d)", _host_id); - } - _flags.is_init = false; + 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)); } + setState(State::DEINIT); + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); return true; @@ -73,7 +192,7 @@ bool Base::pinMode(uint8_t pin, uint8_t mode) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_LOGD("Param: pin(%d), mode(%d)", pin, mode); ESP_UTILS_CHECK_FALSE_RETURN(IS_VALID_PIN(pin), false, "Invalid pin"); @@ -91,7 +210,7 @@ bool Base::digitalWrite(uint8_t pin, uint8_t value) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_LOGD("Param: pin(%d), value(%d)", pin, value); ESP_UTILS_CHECK_FALSE_RETURN(IS_VALID_PIN(pin), false, "Invalid pin"); @@ -109,7 +228,7 @@ int Base::digitalRead(uint8_t pin) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_LOGD("Param: pin(%d)", pin); ESP_UTILS_CHECK_FALSE_RETURN(IS_VALID_PIN(pin), -1, "Invalid pin"); @@ -128,9 +247,9 @@ bool Base::multiPinMode(uint32_t pin_mask, uint8_t mode) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); - ESP_UTILS_LOGD("Param: pin_mask(%0x), mode(%d)", pin_mask, mode); + ESP_UTILS_LOGD("Param: pin_mask(0x%" PRIx32 "), mode(%d)", pin_mask, mode); ESP_UTILS_CHECK_FALSE_RETURN((mode == INPUT) || (mode == OUTPUT), false, "Invalid mode"); esp_io_expander_dir_t dir = (mode == INPUT) ? IO_EXPANDER_INPUT : IO_EXPANDER_OUTPUT; @@ -145,9 +264,9 @@ bool Base::multiDigitalWrite(uint32_t pin_mask, uint8_t value) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); - ESP_UTILS_LOGD("Param: pin_mask(%0x), value(%d)", pin_mask, value); + ESP_UTILS_LOGD("Param: pin_mask(0x%" PRIx32 "), value(%d)", pin_mask, value); ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_set_level(device_handle, pin_mask, value), false, "Set level failed"); @@ -160,9 +279,9 @@ int64_t Base::multiDigitalRead(uint32_t pin_mask) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); - ESP_UTILS_LOGD("Param: pin_mask(%0x)", pin_mask); + ESP_UTILS_LOGD("Param: pin_mask(0x%" PRIx32 ")", 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"); @@ -172,11 +291,11 @@ int64_t Base::multiDigitalRead(uint32_t pin_mask) return level; } -bool Base::printStatus(void) +bool Base::printStatus(void) const { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + 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"); @@ -185,4 +304,13 @@ bool Base::printStatus(void) return true; } +Base::HostFullConfig *Base::getHostFullConfig() +{ + if (std::holds_alternative(_config.host.value())) { + _config.convertPartialToFull(); + } + + return &std::get(_config.host.value()); +} + } // namespace esp_expander diff --git a/src/chip/esp_expander_base.hpp b/src/chip/esp_expander_base.hpp index 402ad7f..957a620 100644 --- a/src/chip/esp_expander_base.hpp +++ b/src/chip/esp_expander_base.hpp @@ -1,12 +1,13 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include +#include +#include #include "driver/i2c.h" #include "port/esp_io_expander.h" @@ -29,92 +30,81 @@ namespace esp_expander { /** * @brief The IO expander device class * - * @note This is a base class for all chips. Due to it is a virtual class, users cannot use it directly - * + * @note This class is a base class for all types of IO expander chips. Due to it is a virtual class, users cannot use + * it directly */ class Base { public: - /* Default I2C host ID */ - constexpr static int HOST_ID_DEFAULT = static_cast(I2C_NUM_0); + /** + * 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; + + 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; + }; + using HostFullConfig = i2c_config_t; + using HostConfig = std::variant; + + struct DeviceConfig { + uint8_t address = 0; + }; /** * @brief Configuration for Base object - * */ struct Config { - i2c_port_t getHostID(void) const - { - return static_cast(host_id); - } - - i2c_config_t getHostConfig(void) const - { - return { - .mode = I2C_MODE_MASTER, - .sda_io_num = host_sda_io_num, - .scl_io_num = host_scl_io_num, - .sda_pullup_en = host_sda_pullup_en, - .scl_pullup_en = host_scl_pullup_en, - .master = { - .clk_speed = host_clk_speed, - }, - .clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL, - }; - } + void convertPartialToFull(void); + void printHostConfig(void) const; + void printDeviceConfig(void) const; - uint8_t getDeviceAddress(void) const + bool isHostConfigValid(void) const { - return device_address; + return host.has_value(); } - static Config create(int scl_io, int sda_io, uint8_t address) - { - return Config{ - .host_sda_io_num = sda_io, - .host_scl_io_num = scl_io, - .device_address = address, - .skip_init_host = false, - }; - } - - static Config create(int host_id, uint8_t address) - { - return Config{ - .host_id = host_id, - .device_address = address, - .skip_init_host = true, - }; - } + int host_id = I2C_HOST_ID_DEFAULT; /*!< I2C host ID */ + std::optional host; /*!< I2C host configuration */ + DeviceConfig device = {}; /*!< I2C device configuration */ + }; - // Host - int host_id = HOST_ID_DEFAULT; /*!< I2C host ID */ - int host_sda_io_num = -1; /*!< I2C SDA pin number */ - int host_scl_io_num = -1; /*!< I2C SCL pin number */ - bool host_sda_pullup_en = GPIO_PULLUP_ENABLE; /*!< I2C SDA pullup enable */ - bool host_scl_pullup_en = GPIO_PULLUP_ENABLE; /*!< I2C SCL pullup enable */ - uint32_t host_clk_speed = 400000; /*!< I2C clock speed */ - // Device - uint8_t device_address = 0; /*!< I2C device 7-bit address */ - // Extra - bool skip_init_host = false; /*!< Skip I2C initialization when call `init()` */ + /** + * @brief The driver state enumeration + */ + enum class State : uint8_t { + DEINIT = 0, + INIT, + BEGIN, }; +// *INDENT-OFF* /** * @brief Construct a base 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`. - * + * @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) + 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 + } + } { - auto config = Config::create(scl_io, sda_io, address); - _host_id = config.getHostID(); - _host_config = config.getHostConfig(); - _device_address = config.getDeviceAddress(); - _flags.skip_init_host = config.skip_init_host; } /** @@ -122,49 +112,43 @@ class Base { * 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) + Base(int host_id, uint8_t address): + _config{ + .host_id = host_id, + .device = DeviceConfig{ + .address = address + } + } { - auto config = Config::create(host_id, address); - _host_id = config.getHostID(); - _host_config = config.getHostConfig(); - _device_address = config.getDeviceAddress(); - _flags.skip_init_host = config.skip_init_host; } +// *INDENT-ON* /** * @brief Construct a base device. * * @param[in] config Configuration for the object - * */ - Base(const Config &config) - { - _host_id = config.getHostID(); - _host_config = config.getHostConfig(); - _device_address = config.getDeviceAddress(); - _flags.skip_init_host = config.skip_init_host; - } + Base(const Config &config): _config(config) {} /** - * @deprecated Deprecated and will be removed in the next major version. Please use other constructors instead. + * @brief Virtual desutruct object. * + * @note Here make it virtual so that we can delete the derived object by using the base pointer. */ - Base(i2c_port_t id, uint8_t address, int scl_io, int sda_io): - Base(scl_io, sda_io, address) - { - _host_id = id; - } + virtual ~Base() = default; /** - * @brief Virtual desutruct object. + * @brief Configure whether to skip I2C initialization * - * @note Here make it virtual so that we can delete the derived object by using the base pointer. + * @note This function should be called before `init()`. + * + * @param[in] skip_init Whether to skip I2C initialization * + * @return true if success, otherwise false */ - virtual ~Base() = default; + bool configHostSkipInit(bool skip_init); /** * @brief Initialize object @@ -172,7 +156,6 @@ class Base { * @note This function will initialize I2C if needed. * * @return true if success, otherwise false - * */ bool init(void); @@ -180,7 +163,6 @@ class Base { * @brief Begin object * * @note This function typically calls `esp_io_expander_new_i2c_*()` to create the IO expander handle. - * */ virtual bool begin(void) = 0; @@ -188,13 +170,11 @@ class Base { * @brief Reset object * * @return true if success, otherwise false - * */ bool reset(void); /** * @brief Delete object - * */ bool del(void); @@ -207,7 +187,6 @@ class Base { * @param[in] mode Pin mode (INPUT / OUTPUT) * * @return true if success, otherwise false - * */ bool pinMode(uint8_t pin, uint8_t mode); @@ -220,7 +199,6 @@ class Base { * @param[in] value Pin level (HIGH / LOW) * * @return true if success, otherwise false - * */ bool digitalWrite(uint8_t pin, uint8_t value); @@ -232,7 +210,6 @@ class Base { * @param[in] pin Pin number (0-31) * * @return Pin level. HIGH or LOW if success, otherwise -1 - * */ int digitalRead(uint8_t pin); @@ -243,7 +220,6 @@ class Base { * @param mode Mode to set (INPUT / OUTPUT) * * @return true if success, otherwise false - * */ bool multiPinMode(uint32_t pin_mask, uint8_t mode); @@ -254,7 +230,6 @@ class Base { * @param value Value to write (HIGH / LOW) * * @return true if success, otherwise false - * */ bool multiDigitalWrite(uint32_t pin_mask, uint8_t value); @@ -264,7 +239,6 @@ class Base { * @param pin_mask Pin mask (Bitwise OR of `IO_EXPANDER_PIN_NUM_*`) * * @return Pin levels, every bit represents a pin (HIGH / LOW) - * */ int64_t multiDigitalRead(uint32_t pin_mask); @@ -272,80 +246,79 @@ class Base { * @brief Print IO expander status, include pin index, direction, input level and output level * * @return Pin levels, every bit represents a pin (HIGH / LOW) - * */ - bool printStatus(void); + bool printStatus(void) const; /** - * @brief Get low-level handle. Users can use this handle to call low-level functions (esp_io_expander_*()). + * @brief Check if the driver has reached or passed the specified state * - * @return Handle if success, otherwise nullptr + * @param[in] state The state to check against current state * + * @return true if current state >= given state, otherwise false */ - esp_io_expander_handle_t getDeviceHandle(void) + bool isOverState(State state) const { - return device_handle; + return (_state >= state); } /** - * @deprecated Deprecated and will be removed in the next major version. Please use `getDeviceHandle()` instead. + * @brief Get the IO expander configuration * + * @return IO expander Configuration */ - [[deprecated("Deprecated and will be removed in the next major version. Please use `getDeviceHandle()` instead.")]] - esp_io_expander_handle_t getHandle(void) + const Config &getConfig(void) const { - return getDeviceHandle(); + return _config; } -protected: - bool checkIsInit(void) - { - return _flags.is_init; - } - - bool checkIsBegun(void) + /** + * @brief Get low-level handle. Users can use this handle to call low-level functions (esp_io_expander_*()). + * + * @return Device handle if success, otherwise nullptr + */ + DeviceHandle getDeviceHandle(void) const { - return (device_handle != nullptr); + return device_handle; } - bool checkIsSkipInitHost(void) + // 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) { - return _flags.skip_init_host; + _config.host_id = id; } - - i2c_port_t getHostID(void) + [[deprecated("Deprecated and will be removed in the next major version. Please use `getDeviceHandle()` instead.")]] + esp_io_expander_handle_t getHandle(void) const { - return static_cast(_host_id); + return getDeviceHandle(); } - const i2c_config_t &getHostConfig(void) +protected: + bool isHostSkipInit(void) const { - return _host_config; + return !_config.isHostConfigValid() || _is_host_skip_init; } - uint8_t getDeviceAddress(void) + void setState(State state) { - return _device_address; + _state = state; } - esp_io_expander_handle_t device_handle = nullptr; + DeviceHandle device_handle = nullptr; private: - struct { - uint8_t is_init: 1; - uint8_t skip_init_host: 1; - } _flags = {}; - // Host - int _host_id = HOST_ID_DEFAULT; - i2c_config_t _host_config = {}; - // Device - uint8_t _device_address = 0; + HostFullConfig *getHostFullConfig(); + + State _state = State::DEINIT; + bool _is_host_skip_init = false; + Config _config = {}; }; } // namespace esp_expander /** - * @deprecated Deprecated and will be removed in the next major version. Please use `esp_expander::Base` instead. + * @brief Alias for backward compatibility * + * @deprecated Use `esp_expander::Base` instead */ -typedef esp_expander::Base ESP_IOExpander __attribute__((deprecated("Deprecated and will be removed in the next major version. Please 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/chip/esp_expander_ch422g.cpp index f2cd30a..dcf3223 100644 --- a/src/chip/esp_expander_ch422g.cpp +++ b/src/chip/esp_expander_ch422g.cpp @@ -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 */ @@ -23,14 +23,21 @@ bool CH422G::begin(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsInit(), false, "Not initialized"); - ESP_UTILS_CHECK_FALSE_RETURN(!checkIsBegun(), false, "Already begun"); + ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::BEGIN), false, "Already begun"); + + // Initialize the device if not initialized + if (!isOverState(State::INIT)) { + ESP_UTILS_CHECK_FALSE_RETURN(init(), false, "Init failed"); + } ESP_UTILS_CHECK_ERROR_RETURN( - esp_io_expander_new_i2c_ch422g(getHostID(), getDeviceAddress(), &device_handle), false, - "Create CH422G IO expander failed" + esp_io_expander_new_i2c_ch422g( + static_cast(getConfig().host_id), getConfig().device.address, &device_handle + ), false, "Create CH422G failed" ); - ESP_UTILS_LOGD("Create CH422G IO expander(@%p)", device_handle); + ESP_UTILS_LOGD("Create CH422G @%p", device_handle); + + setState(State::BEGIN); ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); @@ -41,7 +48,7 @@ bool CH422G::enableOC_OpenDrain(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_CHECK_ERROR_RETURN( esp_io_expander_ch422g_set_oc_open_drain(device_handle), false, "Set OC open-drain failed" @@ -56,7 +63,7 @@ bool CH422G::enableOC_PushPull(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_CHECK_ERROR_RETURN( esp_io_expander_ch422g_set_oc_push_pull(device_handle), false, "Set OC push-pull failed" @@ -71,7 +78,7 @@ bool CH422G::enableAllIO_Input(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_CHECK_ERROR_RETURN( esp_io_expander_ch422g_set_all_input(device_handle), false, "Set all input failed" @@ -86,7 +93,7 @@ bool CH422G::enableAllIO_Output(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_CHECK_ERROR_RETURN( esp_io_expander_ch422g_set_all_output(device_handle), false, "Set all output failed" @@ -97,4 +104,34 @@ bool CH422G::enableAllIO_Output(void) return true; } +bool CH422G::enterSleep(void) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); + + ESP_UTILS_CHECK_ERROR_RETURN( + 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) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); + + ESP_UTILS_CHECK_ERROR_RETURN( + esp_io_expander_ch422g_exit_sleep(device_handle), false, "Exit sleep failed" + ); + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); + + return true; +} + } // namespace esp_expander diff --git a/src/chip/esp_expander_ch422g.hpp b/src/chip/esp_expander_ch422g.hpp index ac7281f..c438bde 100644 --- a/src/chip/esp_expander_ch422g.hpp +++ b/src/chip/esp_expander_ch422g.hpp @@ -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 */ @@ -18,7 +18,6 @@ namespace esp_expander { * | Pin Number | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | * | ------------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | * | Function | IO0 | IO1 | IO2 | IO3 | IO4 | IO5 | IO6 | IO7 | OC0 | OC1 | OC2 | OC3 | - * */ class CH422G: public Base { public: @@ -29,7 +28,6 @@ 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`. - * */ CH422G(int scl_io, int sda_io, uint8_t address): Base(scl_io, sda_io, address) {} @@ -39,7 +37,6 @@ class CH422G: public Base { * * @param[in] host_id I2C host ID. * @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) {} @@ -47,20 +44,17 @@ class CH422G: public Base { * @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) {} /** * @brief Desutruct object. This function will call `del()` to delete the object. - * */ ~CH422G() override; @@ -71,7 +65,6 @@ class CH422G: public Base { * @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; @@ -79,7 +72,6 @@ class CH422G: public Base { * @brief Enable OC0-OC3 output open-drain * * @return true if success, otherwise false - * */ bool enableOC_OpenDrain(void); @@ -87,7 +79,6 @@ class CH422G: public Base { * @brief Enable OC0-OC3 output push-pull (default mode when power-on) * * @return true if success, otherwise false - * */ bool enableOC_PushPull(void); @@ -99,7 +90,6 @@ class CH422G: public Base { * input mode when it determines that all pins are configured as input. * * @return true if success, otherwise false - * */ bool enableAllIO_Input(void); @@ -107,15 +97,27 @@ class CH422G: public Base { * @brief Enable IO0-7 output mode * * @return true if success, otherwise false - * */ bool enableAllIO_Output(void); + + /** + * @brief Enter sleep mode + * + * @return true if success, otherwise false + */ + bool enterSleep(void); + + /** + * @brief Exit sleep mode + * + * @return true if success, otherwise false + */ + bool exitSleep(void); }; } // 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/chip/esp_expander_ht8574.cpp index e69f566..837f299 100644 --- a/src/chip/esp_expander_ht8574.cpp +++ b/src/chip/esp_expander_ht8574.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,14 +23,21 @@ bool HT8574::begin(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsInit(), false, "Not initialized"); - ESP_UTILS_CHECK_FALSE_RETURN(!checkIsBegun(), false, "Already begun"); + ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::BEGIN), false, "Already begun"); + + // Initialize the device if not initialized + if (!isOverState(State::INIT)) { + ESP_UTILS_CHECK_FALSE_RETURN(init(), false, "Init failed"); + } ESP_UTILS_CHECK_ERROR_RETURN( - esp_io_expander_new_i2c_ht8574(getHostID(), getDeviceAddress(), &device_handle), false, - "Create HT8574 IO expander failed" + esp_io_expander_new_i2c_ht8574( + static_cast(getConfig().host_id), getConfig().device.address, &device_handle + ), false, "Create HT8574 failed" ); - ESP_UTILS_LOGD("Create HT8574 IO expander(@%p)", device_handle); + ESP_UTILS_LOGD("Create HT8574 @%p", device_handle); + + setState(State::BEGIN); ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); diff --git a/src/chip/esp_expander_ht8574.hpp b/src/chip/esp_expander_ht8574.hpp index 87cd23b..e7b68ce 100644 --- a/src/chip/esp_expander_ht8574.hpp +++ b/src/chip/esp_expander_ht8574.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,7 +14,6 @@ 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: @@ -25,7 +24,6 @@ class HT8574: 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`. - * */ HT8574(int scl_io, int sda_io, uint8_t address): Base(scl_io, sda_io, address) {} @@ -35,7 +33,6 @@ class HT8574: public Base { * * @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) {} @@ -43,20 +40,17 @@ class HT8574: public Base { * @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; @@ -67,7 +61,6 @@ class HT8574: public Base { * @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; }; @@ -76,6 +69,5 @@ class HT8574: public Base { /** * @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_16bit.cpp b/src/chip/esp_expander_tca95xx_16bit.cpp index 2f5c8f7..68f4702 100644 --- a/src/chip/esp_expander_tca95xx_16bit.cpp +++ b/src/chip/esp_expander_tca95xx_16bit.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,14 +23,21 @@ bool TCA95XX_16BIT::begin(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsInit(), false, "Not initialized"); - ESP_UTILS_CHECK_FALSE_RETURN(!checkIsBegun(), false, "Already begun"); + ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::BEGIN), false, "Already begun"); + + // Initialize the bus if not initialized + if (!isOverState(State::INIT)) { + ESP_UTILS_CHECK_FALSE_RETURN(init(), false, "Init failed"); + } ESP_UTILS_CHECK_ERROR_RETURN( - esp_io_expander_new_i2c_tca95xx_16bit(getHostID(), getDeviceAddress(), &device_handle), false, - "Create TCA95XX_16BIT IO expander failed" + esp_io_expander_new_i2c_tca95xx_16bit( + static_cast(getConfig().host_id), getConfig().device.address, &device_handle + ), false, "Create TCA95XX_16BIT failed" ); - ESP_UTILS_LOGD("Create TCA95XX_16BIT IO expander(@%p)", device_handle); + ESP_UTILS_LOGD("Create TCA95XX_16BIT @%p", device_handle); + + setState(State::BEGIN); ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); diff --git a/src/chip/esp_expander_tca95xx_16bit.hpp b/src/chip/esp_expander_tca95xx_16bit.hpp index b27ea6d..94f4c35 100644 --- a/src/chip/esp_expander_tca95xx_16bit.hpp +++ b/src/chip/esp_expander_tca95xx_16bit.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,7 +14,6 @@ namespace esp_expander { * @brief The TCA95XX_16BIT IO expander device class * * @note This class is a derived class of `esp_expander::Base`, user can use it directly - * */ class TCA95XX_16BIT: public Base { public: @@ -25,7 +24,6 @@ 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`. - * */ TCA95XX_16BIT(int scl_io, int sda_io, uint8_t address): Base(scl_io, sda_io, address) {} @@ -35,7 +33,6 @@ class TCA95XX_16BIT: public Base { * * @param[in] host_id I2C host ID. * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - * */ TCA95XX_16BIT(int host_id, uint8_t address): Base(host_id, address) {} @@ -43,20 +40,17 @@ class TCA95XX_16BIT: public Base { * @brief Construct a TCA95XX_16BIT device. * * @param[in] config Configuration for the object - * */ 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) {} /** * @brief Desutruct object. This function will call `del()` to delete the object. - * */ ~TCA95XX_16BIT() override; @@ -67,7 +61,6 @@ class TCA95XX_16BIT: public Base { * @note This function sets all pins to inpurt mode by default. * * @return true if success, otherwise false - * */ bool begin(void) override; }; @@ -76,6 +69,5 @@ class TCA95XX_16BIT: public Base { /** * @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/chip/esp_expander_tca95xx_8bit.cpp index 7ea43c6..60ebdca 100644 --- a/src/chip/esp_expander_tca95xx_8bit.cpp +++ b/src/chip/esp_expander_tca95xx_8bit.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,14 +23,21 @@ bool TCA95XX_8BIT::begin(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsInit(), false, "Not initialized"); - ESP_UTILS_CHECK_FALSE_RETURN(!checkIsBegun(), false, "Already begun"); + ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::BEGIN), false, "Already begun"); + + // Initialize the device if not initialized + if (!isOverState(State::INIT)) { + ESP_UTILS_CHECK_FALSE_RETURN(init(), false, "Init failed"); + } ESP_UTILS_CHECK_ERROR_RETURN( - esp_io_expander_new_i2c_tca9554(getHostID(), getDeviceAddress(), &device_handle), false, - "Create TCA95XX_8BIT IO expander failed" + esp_io_expander_new_i2c_tca9554( + static_cast(getConfig().host_id), getConfig().device.address, &device_handle + ), false, "Create TCA95XX_8BIT failed" ); - ESP_UTILS_LOGD("Create TCA95XX_8BIT IO expander(@%p)", device_handle); + ESP_UTILS_LOGD("Create TCA95XX_8BIT @%p", device_handle); + + setState(State::BEGIN); ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); diff --git a/src/chip/esp_expander_tca95xx_8bit.hpp b/src/chip/esp_expander_tca95xx_8bit.hpp index 75da75e..0946a3f 100644 --- a/src/chip/esp_expander_tca95xx_8bit.hpp +++ b/src/chip/esp_expander_tca95xx_8bit.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,7 +14,6 @@ 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: @@ -25,7 +24,6 @@ class TCA95XX_8BIT: 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`. - * */ TCA95XX_8BIT(int scl_io, int sda_io, uint8_t address): Base(scl_io, sda_io, address) {} @@ -35,7 +33,6 @@ class TCA95XX_8BIT: public Base { * * @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) {} @@ -43,20 +40,17 @@ class TCA95XX_8BIT: public Base { * @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; @@ -67,7 +61,6 @@ class TCA95XX_8BIT: public Base { * @note This function sets all pins to inpurt mode by default. * * @return true if success, otherwise false - * */ bool begin(void) override; }; @@ -76,6 +69,5 @@ class TCA95XX_8BIT: public Base { /** * @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/esp_expander_utils.h b/src/esp_expander_utils.h index a107319..922dc85 100644 --- a/src/esp_expander_utils.h +++ b/src/esp_expander_utils.h @@ -8,3 +8,4 @@ // Define the log tag for the current library, should be declared before `esp_lib_utils.hpp` #define ESP_UTILS_LOG_TAG "Expander" #include "esp_lib_utils.h" +#include "esp_utils_helpers.h" diff --git a/src/port/esp_io_expander.c b/src/port/esp_io_expander.c index b27c743..f442168 100644 --- a/src/port/esp_io_expander.c +++ b/src/port/esp_io_expander.c @@ -19,7 +19,6 @@ /** * @brief Register type - * */ typedef enum { REG_INPUT = 0, @@ -27,7 +26,7 @@ typedef enum { REG_DIRECTION, } reg_type_t; -static char *TAG = "io_expander"; +static const char *TAG = "io_expander"; static esp_err_t write_reg(esp_io_expander_handle_t handle, reg_type_t reg, uint32_t value); static esp_err_t read_reg(esp_io_expander_handle_t handle, reg_type_t reg, uint32_t *value); diff --git a/src/port/esp_io_expander.h b/src/port/esp_io_expander.h index e36adf4..a01f066 100644 --- a/src/port/esp_io_expander.h +++ b/src/port/esp_io_expander.h @@ -24,14 +24,12 @@ extern "C" { /** * @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), @@ -70,7 +68,6 @@ typedef enum { /** * @brief IO Expander Pin direction - * */ typedef enum { IO_EXPANDER_INPUT, /*!< Input direction */ @@ -79,7 +76,6 @@ 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 9b6ce6a..f16fd02 100644 --- a/src/port/esp_io_expander_ch422g.c +++ b/src/port/esp_io_expander_ch422g.c @@ -38,17 +38,17 @@ // Default: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | // *INDENT-OFF* -#define REG_WR_OC_DEFAULT_VAL (0x0FUL) -#define REG_WR_IO_DEFAULT_VAL (0xFFUL) +#define REG_WR_OC_DEFAULT_VAL (0x0FU) +#define REG_WR_IO_DEFAULT_VAL (0xFFU) #define REG_OUT_DEFAULT_VAL ((REG_WR_OC_DEFAULT_VAL << 8) | REG_WR_IO_DEFAULT_VAL) -#define REG_DIR_DEFAULT_VAL (0xFFFUL) +#define REG_DIR_DEFAULT_VAL (0xFFFU) -#define REG_WR_SET_BIT_IO_OE (1 << 0) -#define REG_WR_SET_BIT_OD_EN (1 << 2) +#define REG_WR_SET_BIT_IO_OE (1U << 0) +#define REG_WR_SET_BIT_OD_EN (1U << 2) +#define REG_WR_SET_BIT_SLEEP (1U << 3) /** * @brief Device Structure Type - * */ typedef struct { esp_io_expander_t base; @@ -172,6 +172,38 @@ esp_err_t esp_io_expander_ch422g_set_all_output(esp_io_expander_handle_t handle) return ESP_OK; } +esp_err_t esp_io_expander_ch422g_enter_sleep(esp_io_expander_handle_t handle) +{ + esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); + uint8_t data = (uint8_t)(ch422g->regs.wr_set | REG_WR_SET_BIT_SLEEP); + + // 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" + ); + ch422g->regs.wr_set = data; + + return ESP_OK; +} + +esp_err_t esp_io_expander_ch422g_exit_sleep(esp_io_expander_handle_t handle) +{ + esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); + uint8_t data = (uint8_t)(ch422g->regs.wr_set & ~REG_WR_SET_BIT_SLEEP); + + // 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" + ); + ch422g->regs.wr_set = data; + + return ESP_OK; +} + static esp_err_t read_input_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); diff --git a/src/port/esp_io_expander_ch422g.h b/src/port/esp_io_expander_ch422g.h index 46f4f2a..a312edf 100644 --- a/src/port/esp_io_expander_ch422g.h +++ b/src/port/esp_io_expander_ch422g.h @@ -37,7 +37,6 @@ esp_err_t esp_io_expander_new_i2c_ch422g(i2c_port_t i2c_num, uint32_t i2c_addres /** * @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) @@ -49,6 +48,10 @@ esp_err_t esp_io_expander_ch422g_set_all_input(esp_io_expander_handle_t handle); esp_err_t esp_io_expander_ch422g_set_all_output(esp_io_expander_handle_t handle); +esp_err_t esp_io_expander_ch422g_enter_sleep(esp_io_expander_handle_t handle); + +esp_err_t esp_io_expander_ch422g_exit_sleep(esp_io_expander_handle_t handle); + #ifdef __cplusplus } #endif diff --git a/src/port/esp_io_expander_ht8574.c b/src/port/esp_io_expander_ht8574.c index 5236bf3..840992f 100644 --- a/src/port/esp_io_expander_ht8574.c +++ b/src/port/esp_io_expander_ht8574.c @@ -29,7 +29,6 @@ /** * @brief Device Structure Type - * */ typedef struct { esp_io_expander_t base; @@ -41,7 +40,7 @@ typedef struct { } regs; } esp_io_expander_ht8574_t; -static char *TAG = "ht8574"; +static const char *TAG = "ht8574"; 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); diff --git a/src/port/esp_io_expander_tca9554.c b/src/port/esp_io_expander_tca9554.c index f3ebaf1..ef17872 100644 --- a/src/port/esp_io_expander_tca9554.c +++ b/src/port/esp_io_expander_tca9554.c @@ -34,7 +34,6 @@ /** * @brief Device Structure Type - * */ typedef struct { esp_io_expander_t base; @@ -46,7 +45,7 @@ typedef struct { } regs; } esp_io_expander_tca9554_t; -static char *TAG = "tca9554"; +static const char *TAG = "tca9554"; 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); diff --git a/src/port/esp_io_expander_tca95xx_16bit.c b/src/port/esp_io_expander_tca95xx_16bit.c index 18edd5a..4062d31 100644 --- a/src/port/esp_io_expander_tca95xx_16bit.c +++ b/src/port/esp_io_expander_tca95xx_16bit.c @@ -34,7 +34,6 @@ /** * @brief Device Structure Type - * */ typedef struct { esp_io_expander_t base; @@ -46,7 +45,7 @@ typedef struct { } regs; } esp_io_expander_tca95xx_16bit_t; -static char *TAG = "tca95xx_16"; +static const char *TAG = "tca95xx_16"; 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); diff --git a/test_apps/main/test_chip_general.cpp b/test_apps/main/test_chip_general.cpp index 5542dd8..007d627 100644 --- a/test_apps/main/test_chip_general.cpp +++ b/test_apps/main/test_chip_general.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -83,8 +83,9 @@ 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_address = TEST_DEVICE_ADDRESS, \ - .skip_init_host = true, \ + .device = Base::DeviceConfig{ \ + .address = TEST_DEVICE_ADDRESS, \ + }, \ }; \ expander = CREATE_DEVICE(device_name, external_i2c_config); \ test_device(expander); \ @@ -101,10 +102,13 @@ static void test_device(std::shared_ptr device) ESP_LOGI(TAG, "Test constructor with (const Config &config) (internal I2C)"); \ Base::Config internal_i2c_config = { \ .host_id = TEST_HOST_ID, \ - .host_sda_io_num = TEST_HOST_I2C_SDA_PIN, \ - .host_scl_io_num = TEST_HOST_I2C_SCL_PIN, \ - .device_address = TEST_DEVICE_ADDRESS, \ - .skip_init_host = false, \ + .host = Base::HostPartialConfig{ \ + .sda_io_num = TEST_HOST_I2C_SDA_PIN, \ + .scl_io_num = TEST_HOST_I2C_SCL_PIN, \ + }, \ + .device = Base::DeviceConfig{ \ + .address = TEST_DEVICE_ADDRESS, \ + }, \ }; \ expander = CREATE_DEVICE(device_name, internal_i2c_config); \ test_device(expander); \ @@ -164,7 +168,6 @@ static void test_device(std::shared_ptr device) /** * Here to create test cases for different devices - * */ CREATE_TEST_CASE(TCA95XX_8BIT) CREATE_TEST_CASE(TCA95XX_16BIT) diff --git a/test_apps/sdkconfig.ci.debug_log b/test_apps/sdkconfig.ci.debug_log new file mode 100644 index 0000000..191f9c8 --- /dev/null +++ b/test_apps/sdkconfig.ci.debug_log @@ -0,0 +1,2 @@ +CONFIG_ESP_UTILS_CONF_LOG_LEVEL_DEBUG=y +CONFIG_ESP_UTILS_CONF_ENABLE_LOG_TRACE=y pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy