diff --git a/.codespellrc b/.codespellrc index 0a3c74d..60bbe06 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,2 +1 @@ [codespell] -skip = ./src/touch/base/esp_lcd_touch_xpt2046.c diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 61831e0..9d3c04c 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -15,7 +15,7 @@ jobs: container: espressif/idf:${{ matrix.idf_ver }} steps: - uses: actions/checkout@v3 - - name: Build ESP_IOExpander Test Application + - name: Build Test Application env: IDF_TARGET: ${{ matrix.idf_target }} working-directory: test_apps diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ec7f1..f893249 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # ChangeLog +## v1.0.0 - 2024-12-06 + +### Enhancements: + +* feat(repo): refactor with esp-lib-utils +* feat(repo): support micropython + ## v0.1.0 - 2024-11-05 ### Enhancements: diff --git a/README.md b/README.md index 81ccad3..6ebc43c 100644 --- a/README.md +++ b/README.md @@ -2,62 +2,106 @@ **Latest Arduino Library Version**: [![GitHub Release](https://img.shields.io/github/v/release/esp-arduino-libs/ESP32_IO_Expander)](https://github.com/esp-arduino-libs/ESP32_IO_Expander/releases) -**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) +**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 -ESP32_IO_Expander is a library designed for driving [IO expander chips](#supported-drivers) using ESP32 SoCs. +## Overview + +`ESP32_IO_Expander` is a library designed for driving [IO expander chips](#supported-drivers) using ESP SoCs. It encapsulates various components from the [Espressif Components Registry](https://components.espressif.com/) and includes the following features: + +* Supports various IO expander chips, such as TCA95xx, HT8574, and CH422G. +* Supports controlling individual IO pin with functions like `pinMode()`, `digitalWrite()`, and `digitalRead()`. +* Supports controlling multiple IO pins simultaneously with functions like `multiPinMode()`, `multiDigitalWrite()`, and `multiDigitalRead()`. +* Compatible with the `Arduino`, `ESP-IDF` and `MicroPython` for compilation. + +## Table of Contents + +- [ESP32\_IO\_Expander](#esp32_io_expander) + - [Overview](#overview) + - [Table of Contents](#table-of-contents) + - [Supported Drivers](#supported-drivers) + - [How to Use](#how-to-use) + - [ESP-IDF Framework](#esp-idf-framework) + - [Dependencies and Versions](#dependencies-and-versions) + - [Adding to Project](#adding-to-project) + - [Configuration Instructions](#configuration-instructions) + - [Arduino IDE](#arduino-ide) + - [Dependencies and Versions](#dependencies-and-versions-1) + - [Installing the Library](#installing-the-library) + - [Configuration Instructions](#configuration-instructions-1) + - [Examples](#examples) + - [Detailed Usage](#detailed-usage) + - [FAQ](#faq) + - [Where is the directory for Arduino libraries?](#where-is-the-directory-for-arduino-libraries) + - [How to Install ESP32\_IO\_Expander in Arduino IDE?](#how-to-install-esp32_io_expander-in-arduino-ide) -ESP32_IO_Expander encapsulates various components from the [Espressif Components Registry](https://components.espressif.com/). It is developed based on [arduino-esp32](https://github.com/espressif/arduino-esp32) or [esp-idf](https://github.com/espressif/esp-idf), and can be easily downloaded and integrated into the Arduino IDE. +## Supported Drivers -## Features +| **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 | +| CH422G | x | -* Supports various IO expander chips. -* Supports controlling individual IO pin (using pinMode(), digitalRead(), and digitalWrite() functions). -* Supports controlling multiple IO pins simultaneously. -* Supports building on the Arduino IDE and the ESP-IDF framework. +## How to Use -## Supported Drivers +### ESP-IDF Framework -| **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 | -| CH422G | x | +#### Dependencies and Versions -## Dependencies Version +| **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 | -### Arduino +#### Adding to Project -| **Name** | **Version** | -| ----------------------------------------------------------- | ----------- | -| [arduino-esp32](https://github.com/espressif/arduino-esp32) | >= v3.0.0 | +`ESP32_IO_Expander` has been uploaded to the [Espressif Component Registry](https://components.espressif.com/), and users can add it to their project using the `idf.py add-dependency` command, for example: -### ESP-IDF +```bash +idf.py add-dependency "espressif/ESP32_IO_Expander" +``` -| **Name** | **Version** | -| ----------------------------------------------- | ----------- | -| [esp-idf](https://github.com/espressif/esp-idf) | >= v5.1 | +Alternatively, users can create or modify the *idf_component.yml* file in the project directory. For more details, please refer to the [Espressif Documentation - IDF Component Manager](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). -## How to Use +#### Configuration Instructions + +Since `ESP32_IO_Expander` depends on the `esp-lib-utils` library which implements the `logging`, `checking`, and `memory` functions, to configure it when using ESP-IDF, please refer to the [instructions](https://github.com/esp-arduino-libs/esp-lib-utils#configuration-instructions). + +### Arduino IDE + +#### 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 | -For information on how to use the library in the Arduino IDE, please refer to the documentation for [Arduino IDE v1.x.x](https://docs.arduino.cc/software/ide-v1/tutorials/installing-libraries) or [Arduino IDE v2.x.x](https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-installing-a-library). +#### Installing the Library + +For installation of the `ESP32_IO_Expander` library, refer to [How to Install ESP32_IO_Expander in Arduino IDE](#how-to-install-ESP32_IO_Expander-in-arduino-ide). + +#### Configuration Instructions + +Since `ESP32_IO_Expander` depends on the `esp-lib-utils` library which implements the `logging`, `checking`, and `memory` functions, to configure it when using Arduino, please refer to the [instructions](https://github.com/esp-arduino-libs/esp-lib-utils#configuration-instructions-1). ### Examples -* [Test Functions](examples/TestFunctions): Demonstrates how to use ESP32_IO_Expander and test all functions. -* [Test CH422G](examples/TestCH422G): Demonstrates how to use ESP32_IO_Expander with the CH422G chip. +* [General](examples/general): Demonstrates how to use `ESP32_IO_Expander` and test general functions. +* [CH422G](examples/ch422g): Demonstrates how to use `ESP32_IO_Expander` specifically with the CH422G chip. ### Detailed Usage ```cpp -#include +#include -// Create and initialize an ESP_IOExpander object according to the chip type -ESP_IOExpander *expander = new ESP_IOExpander_TCA95xx_8bit(EXAMPLE_I2C_NUM_0, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000, - EXAMPLE_I2C_SCL_PIN, EXAMPLE_I2C_SDA_PIN); +// Create and initialize the IO expander chip, such as TCA95XX_8BIT +esp_expander::Base *expander = new esp_expander::TCA95XX_8BIT( + EXAMPLE_I2C_SCL_PIN, EXAMPLE_I2C_SDA_PIN, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000 +); expander->init(); expander->begin(); @@ -75,6 +119,18 @@ expander->multiDigitalWrite(IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, LOW); expander->multiPinMode(IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, INPUT); uint32_t level = expander->multiDigitalRead(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3); -// Release the ESP_IOExpander object +// Release the Base object delete expander; ``` + +## FAQ + +### Where is the directory for Arduino libraries? + +Users can find and modify the directory path for Arduino libraries by selecting `File` > `Preferences` > `Settings` > `Sketchbook location` from the menu bar in the Arduino IDE. + +### How to Install ESP32_IO_Expander in Arduino IDE? + +- **If users want to install online**, navigate to `Sketch` > `Include Library` > `Manage Libraries...` in the Arduino IDE, then search for `ESP32_IO_Expander` and click the `Install` button to install it. +- **If users want to install manually**, download the required version of the `.zip` file from [ESP32_IO_Expander](https://github.com/esp-arduino-libs/ESP32_IO_Expander), then navigate to `Sketch` > `Include Library` > `Add .ZIP Library...` in the Arduino IDE, select the downloaded `.zip` file, and click `Open` to install it. +- Users can also refer to the guides on library installation in the [Arduino IDE v1.x.x](https://docs.arduino.cc/software/ide-v1/tutorials/installing-libraries) or [Arduino IDE v2.x.x](https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-installing-a-library) documentation. diff --git a/examples/TestFunctions/TestFunctions.ino b/examples/TestFunctions/TestFunctions.ino deleted file mode 100644 index a30791d..0000000 --- a/examples/TestFunctions/TestFunctions.ino +++ /dev/null @@ -1,88 +0,0 @@ -#include -#include - -/** - * Create an ESP_IOExpander object, Currently supports: - * - TCA95xx_8bit - * - TCA95xx_16bit - * - HT8574 - * - CH422G - */ -#define EXAMPLE_CHIP_NAME TCA95xx_8bit -#define EXAMPLE_I2C_NUM (0) -#define EXAMPLE_I2C_SDA_PIN (8) -#define EXAMPLE_I2C_SCL_PIN (18) -#define EXAMPLE_I2C_ADDR (ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000) // Modify this value according to the - // hardware address - -#define _EXAMPLE_CHIP_CLASS(name, ...) ESP_IOExpander_##name(__VA_ARGS__) -#define EXAMPLE_CHIP_CLASS(name, ...) _EXAMPLE_CHIP_CLASS(name, ##__VA_ARGS__) - -ESP_IOExpander *expander = NULL; - -void setup() -{ - Serial.begin(115200); - Serial.println("Test begin"); - - expander = new EXAMPLE_CHIP_CLASS(EXAMPLE_CHIP_NAME, - (i2c_port_t)EXAMPLE_I2C_NUM, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000, - EXAMPLE_I2C_SCL_PIN, EXAMPLE_I2C_SDA_PIN); - expander->init(); - expander->begin(); - - /* For CH422G */ - // static_cast(expander)->enableOC_PushPull(); - // static_cast(expander)->enableOC_OpenDrain(); - // static_cast(expander)->enableAllIO_Input(); - // static_cast(expander)->enableAllIO_Output(); - - Serial.println("Original status:"); - expander->printStatus(); - - expander->pinMode(0, OUTPUT); - expander->pinMode(1, OUTPUT); - expander->multiPinMode(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, OUTPUT); - - Serial.println("Set pint 0-3 to output mode:"); - expander->printStatus(); - - expander->digitalWrite(0, LOW); - expander->digitalWrite(1, LOW); - expander->multiDigitalWrite(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, LOW); - - Serial.println("Set pint 0-3 to low level:"); - expander->printStatus(); - - expander->pinMode(0, INPUT); - expander->pinMode(1, INPUT); - expander->multiPinMode(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, INPUT); - - Serial.println("Set pint 0-3 to input mode:"); - expander->printStatus(); -} - -int level[4] = {0, 0, 0, 0}; -uint32_t level_temp; -String level_str; - -void loop() -{ - // Read pin 0-3 level - level[0] = expander->digitalRead(0); - level[1] = expander->digitalRead(1); - level_temp = expander->multiDigitalRead(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3); - level[2] = level_temp & IO_EXPANDER_PIN_NUM_2 ? HIGH : LOW; - level[3] = level_temp & IO_EXPANDER_PIN_NUM_3 ? HIGH : LOW; - - Serial.print("Pin level: "); - Serial.print(level[0]); - Serial.print(", "); - Serial.print(level[1]); - Serial.print(", "); - Serial.print(level[2]); - Serial.print(", "); - Serial.println(level[3]); - - delay(1000); -} diff --git a/examples/TestCH422G/TestCH422G.ino b/examples/ch422g/ch422g.ino similarity index 89% rename from examples/TestCH422G/TestCH422G.ino rename to examples/ch422g/ch422g.ino index 016acdf..51f067c 100644 --- a/examples/TestCH422G/TestCH422G.ino +++ b/examples/ch422g/ch422g.ino @@ -1,5 +1,5 @@ /** - * | Supported IO Expanders | CH422G | + * | Supported IO Expanders | CH422G | * | ------------------------- | ------ | * * # CH422G Test Example @@ -38,25 +38,23 @@ */ #include -#include +#include -#define EXAMPLE_I2C_NUM (0) #define EXAMPLE_I2C_SDA_PIN (8) #define EXAMPLE_I2C_SCL_PIN (9) #define EXAMPLE_I2C_ADDR (ESP_IO_EXPANDER_I2C_CH422G_ADDRESS) -ESP_IOExpander_CH422G *expander = NULL; +esp_expander::CH422G *expander = NULL; void setup() { Serial.begin(115200); delay(1000); Serial.println("Test begin"); - expander = new ESP_IOExpander_CH422G((i2c_port_t)EXAMPLE_I2C_NUM, EXAMPLE_I2C_ADDR, EXAMPLE_I2C_SCL_PIN, EXAMPLE_I2C_SDA_PIN); + expander = new esp_expander::CH422G(EXAMPLE_I2C_SCL_PIN, EXAMPLE_I2C_SDA_PIN, EXAMPLE_I2C_ADDR); expander->init(); expander->begin(); - /* For CH422G */ Serial.println("Set the OC pin to push-pull output mode."); expander->enableOC_PushPull(); diff --git a/examples/general/general.ino b/examples/general/general.ino new file mode 100644 index 0000000..ccfd757 --- /dev/null +++ b/examples/general/general.ino @@ -0,0 +1,83 @@ +#include +#include + +/* The following default configurations are for the board 'Espressif: ESP32_S3_LCD_EV_BOARD_V1_5, TCA9554' */ +/** + * Choose one of the following chip names: + * - TCA95XX_8BIT + * - TCA95XX_16BIT + * - HT8574 + * - CH422G + */ +#define EXAMPLE_CHIP_NAME TCA95XX_8BIT +#define EXAMPLE_I2C_SDA_PIN (47) +#define EXAMPLE_I2C_SCL_PIN (48) +#define EXAMPLE_I2C_ADDR (ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000) // Change this value according to the + // hardware address + +#define _EXAMPLE_CHIP_CLASS(name, ...) esp_expander::name(__VA_ARGS__) +#define EXAMPLE_CHIP_CLASS(name, ...) _EXAMPLE_CHIP_CLASS(name, ##__VA_ARGS__) + +esp_expander::Base *expander = nullptr; + +void setup() +{ + Serial.begin(115200); + Serial.println("Test begin"); + + /** + * Taking `TCA95XX_8BIT` as an example, the following is the code after macro expansion: + * expander = new esp_expander::TCA95XX_8BIT((48), (47), (0x20)) + */ + expander = new EXAMPLE_CHIP_CLASS( + EXAMPLE_CHIP_NAME, EXAMPLE_I2C_SCL_PIN, EXAMPLE_I2C_SDA_PIN, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000 + ); + expander->init(); + expander->begin(); + + /* For CH422G */ + // static_cast(expander)->enableOC_OpenDrain(); + // static_cast(expander)->enableOC_PushPull(); + // static_cast(expander)->enableAllIO_Input(); + // static_cast(expander)->enableAllIO_Output(); + + Serial.println("Original status:"); + expander->printStatus(); + + expander->pinMode(0, OUTPUT); + expander->pinMode(1, OUTPUT); + expander->multiPinMode(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, OUTPUT); + + Serial.println("Set pint 0-3 to output mode:"); + expander->printStatus(); + + expander->digitalWrite(0, LOW); + expander->digitalWrite(1, LOW); + expander->multiDigitalWrite(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, LOW); + + Serial.println("Set pint 0-3 to low level:"); + expander->printStatus(); + + expander->pinMode(0, INPUT); + expander->pinMode(1, INPUT); + expander->multiPinMode(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, INPUT); + + Serial.println("Set pint 0-3 to input mode:"); + expander->printStatus(); +} + +int level[4] = {0, 0, 0, 0}; +uint32_t level_temp; + +void loop() +{ + // Read pin 0-3 level + level[0] = expander->digitalRead(0); + level[1] = expander->digitalRead(1); + level_temp = expander->multiDigitalRead(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3); + level[2] = level_temp & IO_EXPANDER_PIN_NUM_2 ? HIGH : LOW; + level[3] = level_temp & IO_EXPANDER_PIN_NUM_3 ? HIGH : LOW; + Serial.printf("Pin level: %d, %d, %d, %d\n", level[0], level[1], level[2], level[3]); + + delay(1000); +} diff --git a/idf_component.yml b/idf_component.yml index c503891..5d75262 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,7 +1,10 @@ -version: "0.1.0" -description: ESP32_IO_Expander is a library designed for driving IO expander chips using ESP32 SoCs +version: "1.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" + espressif/esp-lib-utils: + version: ">=0.1.0,<=0.2.0" + public: true diff --git a/library.properties b/library.properties index 3ff246e..b15e1ee 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,11 @@ name=ESP32_IO_Expander -version=0.1.0 +version=1.0.0 author=espressif maintainer=espressif -sentence=ESP32_IO_Expander is a library designed for driving IO expander chips using ESP32 SoCs +sentence=ESP32_IO_Expander is a library designed for driving IO expander chips using ESP SoCs paragraph=Currently support TCA95xx(8bit), TCA95xx(16bit), HT8574, CH422G category=Other architectures=esp32 url=https://github.com/esp-arduino-libs/ESP32_IO_Expander -includes=ESP_IOExpander_Library.h +includes=esp_io_expander.hpp +depends=esp-lib-utils (>=0.1.0 && <0.2.0) diff --git a/micropython.cmake b/micropython.cmake new file mode 100644 index 0000000..207fc55 --- /dev/null +++ b/micropython.cmake @@ -0,0 +1,16 @@ +# This file is to be given as "make USER_C_MODULES=..." when building Micropython port + +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) + +# Add our source files to the library. +target_sources(usermod_esp_io_expander INTERFACE ${SRCS}) + +# Add the current directory as an include directory. +target_include_directories(usermod_esp_io_expander INTERFACE ${SRC_DIR}) + +# Link our INTERFACE library to the usermod target. +target_link_libraries(usermod INTERFACE usermod_esp_io_expander) diff --git a/src/ESP_IOExpander.cpp b/src/ESP_IOExpander.cpp deleted file mode 100644 index f2e9a12..0000000 --- a/src/ESP_IOExpander.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "driver/i2c.h" - -#include "private/CheckResult.h" -#include "ESP_IOExpander.h" - -// Check whether it is a valid pin number -#define IS_VALID_PIN(pin_num) (pin_num < IO_COUNT_MAX) - -static const char *TAG = "ESP_IOExpander"; - -ESP_IOExpander::ESP_IOExpander(i2c_port_t id, uint8_t address, const i2c_config_t *config): - handle(NULL), - i2c_id(id), - i2c_config(*config), - i2c_address(address), - i2c_need_init(true) -{ -} - -ESP_IOExpander::ESP_IOExpander(i2c_port_t id, uint8_t address, int scl, int sda): - handle(NULL), - i2c_id(id), - i2c_config((i2c_config_t)EXPANDER_I2C_CONFIG_DEFAULT(scl, sda)), - i2c_address(address), - i2c_need_init(true) -{ -} - -ESP_IOExpander::ESP_IOExpander(i2c_port_t id, uint8_t address): - handle(NULL), - i2c_id(id), - i2c_address(address), - i2c_need_init(false) -{ -} - -void ESP_IOExpander::init(void) -{ - if (i2c_need_init) { - CHECK_ERROR_RETURN(i2c_param_config(i2c_id, &i2c_config)); - CHECK_ERROR_RETURN(i2c_driver_install(i2c_id, i2c_config.mode, 0, 0, 0)); - } -} - -void ESP_IOExpander::reset(void) -{ - CHECK_ERROR_RETURN(esp_io_expander_reset(handle)); -} - -void ESP_IOExpander::del(void) -{ - CHECK_ERROR_RETURN(esp_io_expander_del(handle)); - handle = NULL; -} - -esp_io_expander_handle_t ESP_IOExpander::getHandle(void) -{ - CHECK_NULL_GOTO(handle, err); -err: - return handle; -} - -void ESP_IOExpander::pinMode(uint8_t pin, uint8_t mode) -{ - CHECK_FALSE_RETURN(IS_VALID_PIN(pin)); - CHECK_FALSE_RETURN(mode == INPUT || mode == OUTPUT); - - esp_io_expander_dir_t dir = (mode == INPUT) ? IO_EXPANDER_INPUT : IO_EXPANDER_OUTPUT; - CHECK_ERROR_RETURN(esp_io_expander_set_dir(handle, BIT64(pin), dir)); -} - -void ESP_IOExpander::digitalWrite(uint8_t pin, uint8_t val) -{ - CHECK_FALSE_RETURN(IS_VALID_PIN(pin)); - CHECK_ERROR_RETURN(esp_io_expander_set_level(handle, BIT64(pin), val)); -} - -int ESP_IOExpander::digitalRead(uint8_t pin) -{ - uint32_t level = 0; - CHECK_FALSE_GOTO(IS_VALID_PIN(pin), err); - - CHECK_ERROR_GOTO(esp_io_expander_get_level(handle, BIT64(pin), &level), err); -err: - return (level & BIT64(pin)) ? HIGH : LOW; -} - -void ESP_IOExpander::multiPinMode(uint32_t pin_mask, uint8_t mode) -{ - CHECK_FALSE_RETURN(mode == INPUT || mode == OUTPUT); - - esp_io_expander_dir_t dir = (mode == INPUT) ? IO_EXPANDER_INPUT : IO_EXPANDER_OUTPUT; - CHECK_ERROR_RETURN(esp_io_expander_set_dir(handle, pin_mask, dir)); -} - -void ESP_IOExpander::multiDigitalWrite(uint32_t pin_mask, uint8_t value) -{ - CHECK_ERROR_RETURN(esp_io_expander_set_level(handle, pin_mask, value)); -} - -uint32_t ESP_IOExpander::multiDigitalRead(uint32_t pin_mask) -{ - uint32_t level = 0; - CHECK_ERROR_GOTO(esp_io_expander_get_level(handle, pin_mask, &level), err); -err: - return level; -} - -void ESP_IOExpander::printStatus(void) -{ - CHECK_ERROR_RETURN(esp_io_expander_print_state(handle)); -} diff --git a/src/ESP_IOExpander.h b/src/ESP_IOExpander.h deleted file mode 100644 index 8ca025c..0000000 --- a/src/ESP_IOExpander.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef ESP_IOEXPANDER_H -#define ESP_IOEXPANDER_H - -#include - -#include "driver/i2c.h" - -#include "base/esp_io_expander.h" - -// Refer to `esp32-hal-gpio.h` -#ifndef INPUT -#define INPUT 0x01 -#endif -#ifndef OUTPUT -#define OUTPUT 0x03 -#endif -#ifndef LOW -#define LOW 0x0 -#endif -#ifndef HIGH -#define HIGH 0x1 -#endif - -#define EXPANDER_I2C_CONFIG_DEFAULT(scl, sda) \ - { \ - .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, \ - } - -/** - * @brief ESP_IOExpander class. - * - */ -class ESP_IOExpander { -public: - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note After using this function, call `init()` will initialize I2C bus. - * - * @param id I2C port number - * @param address I2C device address. Should be like `ESP_IO_EXPANDER_I2C_*`. - * Can be found in the header file of each IO expander.h. - * @param config Pointer to I2C bus configuration - */ - ESP_IOExpander(i2c_port_t id, uint8_t address, const i2c_config_t *config); - - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note After using this function, call `init()` will initialize I2C bus. - * - * @param id I2C port number - * @param address I2C device address. Should be like `ESP_IO_EXPANDER_I2C_*`. - * Can be found in the header file of each IO expander.h. - * @param scl SCL pin number - * @param sda SDA pin number - */ - ESP_IOExpander(i2c_port_t id, uint8_t address, int scl, int sda); - - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note If use this function, should initialize I2C bus before call `init()`. - * - * @param id I2C port number - * @param address I2C device address. Should be like `ESP_IO_EXPANDER_I2C_*`. - * Can be found in the header file of each IO expander.h. - */ - ESP_IOExpander(i2c_port_t id, uint8_t address); - - /** - * @brief Initialize IO expander - * - * @note This function will initialize I2C bus if it is not initialized. - * - */ - void init(void); - - /** - * @brief Reset IO expander - * - */ - void reset(void); - - /** - * @brief Delete IO expander object - * - */ - void del(void); - - /** - * @brief Get IO expander handle - * - * @return IO expander handle, which can be use to call `esp_io_expander_*` functions - * - */ - esp_io_expander_handle_t getHandle(void); - - /** - * @brief Set pin mode - * - * @note This function is same as Arduino's `pinMode()`. - * - * @param pin Pin number (0-31) - * @param mode Pin mode (INPUT/OUTPUT) - */ - void pinMode(uint8_t pin, uint8_t mode); - - /** - * @brief Set pin level - * - * @note This function is same as Arduino's `digitalWrite()`. - * - * @param pin Pin number (0-31) - * @param val Pin level (HIGH/LOW) - */ - void digitalWrite(uint8_t pin, uint8_t val); - - /** - * @brief Read pin level - * - * @note This function is same as Arduino's `digitalRead()`. - * - * @param pin Pin number (0-31) - * @return Pin level (HIGH/LOW) - */ - int digitalRead(uint8_t pin); - - /** - * @brief Set multiple pin modes - * - * @param pin_mask Pin mask (Bitwise OR of `IO_EXPANDER_PIN_NUM_*`) - * @param mode Mode to set (INPUT/OUTPUT) - */ - void multiPinMode(uint32_t pin_mask, uint8_t mode); - - /** - * @brief Write to multiple pins - * - * @param pin_mask Pin mask (Bitwise OR of `IO_EXPANDER_PIN_NUM_*`) - * @param value Value to write (HIGH/LOW) - */ - void multiDigitalWrite(uint32_t pin_mask, uint8_t value); - - /** - * @brief Read multiple pin levels - * - * @param pin_mask Pin mask (Bitwise OR of `IO_EXPANDER_PIN_NUM_*`) - * @return Pin levels, every bit represents a pin (HIGH/LOW) - */ - uint32_t multiDigitalRead(uint32_t pin_mask); - - /** - * @brief Print IO expander status, include pin index, direction, input level and output level - * - */ - void printStatus(void); - - /** - * @brief Virtual destructor - */ - virtual ~ESP_IOExpander() = default; - - /** - * @brief Begin IO expander - * - */ - virtual void begin(void) = 0; - - -protected: - esp_io_expander_handle_t handle; /*!< IO expander handle */ - - // I2C - i2c_port_t i2c_id; /*!< I2C port number */ - i2c_config_t i2c_config; /*!< I2C bus configuration */ - uint8_t i2c_address; /*!< I2C device address */ - bool i2c_need_init; /*!< Whether need to initialize I2C bus */ - -}; - -#endif diff --git a/src/ESP_IOExpander_Library.h b/src/ESP_IOExpander_Library.h index ccbeaf4..5280839 100644 --- a/src/ESP_IOExpander_Library.h +++ b/src/ESP_IOExpander_Library.h @@ -4,14 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -#ifndef ESP_IOEXPANDER_LIBRARY_H -#define ESP_IOEXPANDER_LIBRARY_H +/** + * This file is just to keep the compatibility with the old version of the library. Please use the file `esp_io_expander.hpp` instead. + * + */ -#include "ESP_IOExpander.h" +#pragma once -#include "chip/TCA95xx_8bit.h" -#include "chip/TCA95xx_16bit.h" -#include "chip/HT8574.h" -#include "chip/CH422G.h" +#warning "This file is deprecated. Please use the file `esp_io_expander.hpp` instead." -#endif +#include "esp_io_expander.hpp" diff --git a/src/chip/CH422G.h b/src/chip/CH422G.h deleted file mode 100644 index 114d909..0000000 --- a/src/chip/CH422G.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include - -#include "driver/i2c.h" -#include "esp_err.h" - -#include "../ESP_IOExpander.h" - -/** - * Pin mapping: - * - * | 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 ESP_IOExpander_CH422G: public ESP_IOExpander { -public: - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note After using this function, call `init()` will initialize I2C bus. - * - * @param id I2C port number - * @param address I2C device address. Just to keep the same with other IO expanders, but it is ignored. - * @param config Pointer to I2C bus configuration - */ - ESP_IOExpander_CH422G(i2c_port_t id, uint8_t address, const i2c_config_t *config): ESP_IOExpander(id, 0xFF, config) { }; - - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note After using this function, call `init()` will initialize I2C bus. - * - * @param id I2C port number - * @param address I2C device address. Just to keep the same with other IO expanders, but it is ignored. - * @param scl SCL pin number - * @param sda SDA pin number - */ - ESP_IOExpander_CH422G(i2c_port_t id, uint8_t address, int scl, int sda): ESP_IOExpander(id, 0xFF, scl, sda) { }; - - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note If use this function, should initialize I2C bus before call `init()`. - * - * @param id I2C port number - * @param address I2C device address. Just to keep the same with other IO expanders, but it is ignored. - */ - ESP_IOExpander_CH422G(i2c_port_t id, uint8_t address): ESP_IOExpander(id, 0xFF) { }; - - /** - * @brief Destructor - * - * @note This function will delete I2C driver if it is initialized by ESP_IOExpander and delete ESP_IOExpander object. - */ - ~ESP_IOExpander_CH422G() override; - - /** - * @brief Begin IO expander - * - * @note The driver initialization by default sets CH422G's IO0-7 to output high-level mode. - * - */ - void begin(void) override; - - /** - * @brief Enable OC0-OC3 output open-drain - * - */ - void enableOC_OpenDrain(void); - - /** - * @brief Enable OC0-OC3 output push-pull (default mode when power-on) - * - */ - void enableOC_PushPull(void); - - /** - * @brief Enable IO0-7 input mode - * - * @note The driver initialization by default sets CH422G's IO0-7 to output high-level mode. - * @note 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. - * - */ - void enableAllIO_Input(void); - - /** - * @brief Enable IO0-7 output mode - * - */ - void enableAllIO_Output(void); -}; - -/** - * @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) diff --git a/src/chip/HT8574.h b/src/chip/HT8574.h deleted file mode 100644 index 00ef52e..0000000 --- a/src/chip/HT8574.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include - -#include "driver/i2c.h" -#include "esp_err.h" - -#include "../ESP_IOExpander.h" - -class ESP_IOExpander_HT8574: public ESP_IOExpander { -public: - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note After using this function, call `init()` will initialize I2C bus. - * - * @param id I2C port number - * @param address I2C device address. Should be like `ESP_IO_EXPANDER_I2C_*`. - * Can be found in the header file of each IO expander.h. - * @param config Pointer to I2C bus configuration - */ - ESP_IOExpander_HT8574(i2c_port_t id, uint8_t address, const i2c_config_t *config): ESP_IOExpander(id, address, config) { }; - - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note After using this function, call `init()` will initialize I2C bus. - * - * @param id I2C port number - * @param address I2C device address. Should be like `ESP_IO_EXPANDER_I2C_*`. - * Can be found in the header file of each IO expander.h. - * @param scl SCL pin number - * @param sda SDA pin number - */ - ESP_IOExpander_HT8574(i2c_port_t id, uint8_t address, int scl, int sda): ESP_IOExpander(id, address, scl, sda) { }; - - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note If use this function, should initialize I2C bus before call `init()`. - * - * @param id I2C port number - * @param address I2C device address. Should be like `ESP_IO_EXPANDER_I2C_*`. - * Can be found in the header file of each IO expander.h. - */ - ESP_IOExpander_HT8574(i2c_port_t id, uint8_t address): ESP_IOExpander(id, address) { }; - - /** - * @brief Destructor - * - * @note This function will delete I2C driver if it is initialized by ESP_IOExpander and delete ESP_IOExpander object. - */ - ~ESP_IOExpander_HT8574() override; - - /** - * @brief Begin IO expander - * - */ - void begin(void) override; -}; - -/** - * @brief I2C address of the ht8574 - * - * The 8-bit address format is as follows: - * - * (Slave Address) - * ┌─────────────────┷─────────────────┐ - * ┌─────┐─────┐─────┐─────┐─────┐─────┐─────┐─────┐ - * | 0 | 1 | 1 | 1 | A2 | A1 | A0 | R/W | - * └─────┘─────┘─────┘─────┘─────┘─────┘─────┘─────┘ - * └────────┯────────┘ └─────┯──────┘ - * (Fixed) (Hardware Selectable) - * - * And the 7-bit slave address is the most important data for users. - * For example, if a chip's A0,A1,A2 are connected to GND, it's 7-bit slave address is 0111000b(0x38). - * Then users can use `ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_000` to init it. - */ -#define ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_000 (0x38) -#define ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_001 (0x29) -#define ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_010 (0x2A) -#define ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_011 (0x2B) -#define ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_100 (0x2C) diff --git a/src/chip/TCA95xx_8bit.cpp b/src/chip/TCA95xx_8bit.cpp deleted file mode 100644 index 3312d31..0000000 --- a/src/chip/TCA95xx_8bit.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include - -#include "driver/i2c.h" -#include "esp_bit_defs.h" -#include "esp_check.h" -#include "esp_log.h" - -#include "../private/CheckResult.h" -#include "TCA95xx_8bit.h" - -/* Timeout of each I2C communication */ -#define I2C_TIMEOUT_MS (10) - -#define IO_COUNT (8) - -/* Register address */ -#define INPUT_REG_ADDR (0x00) -#define OUTPUT_REG_ADDR (0x01) -#define DIRECTION_REG_ADDR (0x03) - -/* Default register value on power-up */ -#define DIR_REG_DEFAULT_VAL (0xff) -#define OUT_REG_DEFAULT_VAL (0xff) - -/** - * @brief Device Structure Type - * - */ -typedef struct { - esp_io_expander_t base; - i2c_port_t i2c_num; - uint32_t i2c_address; - struct { - uint8_t direction; - uint8_t output; - } regs; -} esp_io_expander_tca95xx_8bit_t; - -static const char *TAG = "tca95xx_8bit"; - -static esp_err_t esp_io_expander_new_i2c_tca95xx_8bit(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle); - -ESP_IOExpander_TCA95xx_8bit::~ESP_IOExpander_TCA95xx_8bit() -{ - if (i2c_need_init) { - i2c_driver_delete(i2c_id); - } - if (handle) { - del(); - } -} - -void ESP_IOExpander_TCA95xx_8bit::begin(void) -{ - CHECK_ERROR_RETURN(esp_io_expander_new_i2c_tca95xx_8bit(i2c_id, i2c_address, &handle)); -} - -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); -static esp_err_t read_output_reg(esp_io_expander_handle_t handle, uint32_t *value); -static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t value); -static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *value); -static esp_err_t reset(esp_io_expander_t *handle); -static esp_err_t del(esp_io_expander_t *handle); - -static esp_err_t esp_io_expander_new_i2c_tca95xx_8bit(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle) -{ - 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_io_expander_tca95xx_8bit_t *tca = (esp_io_expander_tca95xx_8bit_t *)calloc(1, sizeof(esp_io_expander_tca95xx_8bit_t)); - ESP_RETURN_ON_FALSE(tca, ESP_ERR_NO_MEM, TAG, "Malloc 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; - tca->base.write_direction_reg = write_direction_reg; - tca->base.read_direction_reg = read_direction_reg; - 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; - return ESP_OK; -err: - free(tca); - return ret; -} - -static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value) -{ - esp_io_expander_tca95xx_8bit_t *tca = (esp_io_expander_tca95xx_8bit_t *)__containerof(handle, esp_io_expander_tca95xx_8bit_t, base); - - uint8_t temp = 0; - uint8_t reg = INPUT_REG_ADDR; - // *INDENT-OFF* - ESP_RETURN_ON_ERROR( - i2c_master_write_read_device(tca->i2c_num, tca->i2c_address, ®, 1, &temp, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Read input reg failed"); - // *INDENT-ON* - *value = temp; - return ESP_OK; -} - -static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t value) -{ - esp_io_expander_tca95xx_8bit_t *tca = (esp_io_expander_tca95xx_8bit_t *)__containerof(handle, esp_io_expander_tca95xx_8bit_t, base); - value &= 0xff; - - uint8_t data[] = {OUTPUT_REG_ADDR, (uint8_t)value}; - 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"); - tca->regs.output = value; - return ESP_OK; -} - -static esp_err_t read_output_reg(esp_io_expander_handle_t handle, uint32_t *value) -{ - esp_io_expander_tca95xx_8bit_t *tca = (esp_io_expander_tca95xx_8bit_t *)__containerof(handle, esp_io_expander_tca95xx_8bit_t, base); - - *value = tca->regs.output; - return ESP_OK; -} - -static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t value) -{ - esp_io_expander_tca95xx_8bit_t *tca = (esp_io_expander_tca95xx_8bit_t *)__containerof(handle, esp_io_expander_tca95xx_8bit_t, base); - value &= 0xff; - - uint8_t data[] = {DIRECTION_REG_ADDR, (uint8_t)value}; - 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"); - tca->regs.direction = value; - return ESP_OK; -} - -static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *value) -{ - esp_io_expander_tca95xx_8bit_t *tca = (esp_io_expander_tca95xx_8bit_t *)__containerof(handle, esp_io_expander_tca95xx_8bit_t, base); - - *value = tca->regs.direction; - return ESP_OK; -} - -static esp_err_t reset(esp_io_expander_t *handle) -{ - ESP_RETURN_ON_ERROR(write_direction_reg(handle, DIR_REG_DEFAULT_VAL), TAG, "Write dir reg failed"); - ESP_RETURN_ON_ERROR(write_output_reg(handle, OUT_REG_DEFAULT_VAL), TAG, "Write output reg failed"); - return ESP_OK; -} - -static esp_err_t del(esp_io_expander_t *handle) -{ - esp_io_expander_tca95xx_8bit_t *tca = (esp_io_expander_tca95xx_8bit_t *)__containerof(handle, esp_io_expander_tca95xx_8bit_t, base); - - free(tca); - return ESP_OK; -} diff --git a/src/chip/esp_expander_base.cpp b/src/chip/esp_expander_base.cpp new file mode 100644 index 0000000..3ee7e66 --- /dev/null +++ b/src/chip/esp_expander_base.cpp @@ -0,0 +1,188 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "driver/i2c.h" +#include "esp_expander_utils.h" +#include "esp_expander_base.hpp" + +// Check whether it is a valid pin number +#define IS_VALID_PIN(pin_num) (pin_num < IO_COUNT_MAX) + +namespace esp_expander { + +bool Base::init(void) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_RETURN(!checkIsInit(), false, "Already initialized"); + + if (!checkIsSkipInitHost()) { + ESP_UTILS_CHECK_ERROR_RETURN(i2c_param_config(getHostID(), &_host_config), false, "I2C param config failed"); + ESP_UTILS_CHECK_ERROR_RETURN( + i2c_driver_install(getHostID(), _host_config.mode, 0, 0, 0), false, "I2C driver install failed" + ); + ESP_UTILS_LOGI("Init I2C host(%d)", _host_id); + } + _flags.is_init = true; + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); + + return true; +} + +bool Base::reset(void) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), 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) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + if (checkIsBegun()) { + 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); + } + + 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; + } + + 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_CHECK_FALSE_RETURN(checkIsBegun(), 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"); + 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; + 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_CHECK_FALSE_RETURN(checkIsBegun(), 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"); + + ESP_UTILS_CHECK_ERROR_RETURN( + 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_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + + ESP_UTILS_LOGD("Param: pin(%d)", pin); + ESP_UTILS_CHECK_FALSE_RETURN(IS_VALID_PIN(pin), -1, "Invalid pin"); + + uint32_t level = 0; + ESP_UTILS_CHECK_ERROR_RETURN( + 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_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + + ESP_UTILS_LOGD("Param: pin_mask(%0x), 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; + 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_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + + ESP_UTILS_LOGD("Param: pin_mask(%0x), value(%d)", pin_mask, 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_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + + ESP_UTILS_LOGD("Param: pin_mask(%0x)", 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) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), 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; +} + +} // namespace esp_expander diff --git a/src/chip/esp_expander_base.hpp b/src/chip/esp_expander_base.hpp new file mode 100644 index 0000000..402ad7f --- /dev/null +++ b/src/chip/esp_expander_base.hpp @@ -0,0 +1,351 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "driver/i2c.h" +#include "port/esp_io_expander.h" + +// Refer to `esp32-hal-gpio.h` in Arduino +#ifndef INPUT +#define INPUT 0x01 +#endif +#ifndef OUTPUT +#define OUTPUT 0x03 +#endif +#ifndef LOW +#define LOW 0x0 +#endif +#ifndef HIGH +#define HIGH 0x1 +#endif + +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 + * + */ +class Base { +public: + /* Default I2C host ID */ + constexpr static int HOST_ID_DEFAULT = static_cast(I2C_NUM_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, + }; + } + + uint8_t getDeviceAddress(void) const + { + return device_address; + } + + 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, + }; + } + + // 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 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`. + * + */ + Base(int scl_io, int sda_io, uint8_t 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; + } + + /** + * @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`. + * + */ + Base(int host_id, uint8_t 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; + } + + /** + * @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; + } + + /** + * @deprecated Deprecated and will be removed in the next major version. Please use other constructors instead. + * + */ + Base(i2c_port_t id, uint8_t address, int scl_io, int sda_io): + Base(scl_io, sda_io, address) + { + _host_id = id; + } + + /** + * @brief Virtual desutruct object. + * + * @note Here make it virtual so that we can delete the derived object by using the base pointer. + * + */ + virtual ~Base() = default; + + /** + * @brief Initialize object + * + * @note This function will initialize I2C if needed. + * + * @return true if success, otherwise false + * + */ + bool init(void); + + /** + * @brief Begin object + * + * @note This function typically calls `esp_io_expander_new_i2c_*()` to create the IO expander handle. + * + */ + virtual bool begin(void) = 0; + + /** + * @brief Reset object + * + * @return true if success, otherwise false + * + */ + bool reset(void); + + /** + * @brief Delete object + * + */ + bool del(void); + + /** + * @brief Set pin mode + * + * @note This function is same as Arduino's `pinMode()`. + * + * @param[in] pin Pin number (0-31) + * @param[in] mode Pin mode (INPUT / OUTPUT) + * + * @return true if success, otherwise false + * + */ + bool pinMode(uint8_t pin, uint8_t mode); + + /** + * @brief Set pin level + * + * @note This function is same as Arduino's `digitalWrite()`. + * + * @param[in] pin Pin number (0-31) + * @param[in] value Pin level (HIGH / LOW) + * + * @return true if success, otherwise false + * + */ + bool digitalWrite(uint8_t pin, uint8_t value); + + /** + * @brief Read pin level + * + * @note This function is same as Arduino's `digitalRead()`. + * + * @param[in] pin Pin number (0-31) + * + * @return Pin level. HIGH or LOW if success, otherwise -1 + * + */ + int digitalRead(uint8_t pin); + + /** + * @brief Set multiple pin modes + * + * @param pin_mask Pin mask (Bitwise OR of `IO_EXPANDER_PIN_NUM_*`) + * @param mode Mode to set (INPUT / OUTPUT) + * + * @return true if success, otherwise false + * + */ + bool multiPinMode(uint32_t pin_mask, uint8_t mode); + + /** + * @brief Set multiple pins level + * + * @param pin_mask Pin mask (Bitwise OR of `IO_EXPANDER_PIN_NUM_*`) + * @param value Value to write (HIGH / LOW) + * + * @return true if success, otherwise false + * + */ + bool multiDigitalWrite(uint32_t pin_mask, uint8_t value); + + /** + * @brief Read multiple pin levels + * + * @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); + + /** + * @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); + + /** + * @brief Get low-level handle. Users can use this handle to call low-level functions (esp_io_expander_*()). + * + * @return Handle if success, otherwise nullptr + * + */ + esp_io_expander_handle_t getDeviceHandle(void) + { + return device_handle; + } + + /** + * @deprecated Deprecated and will be removed in the next major version. Please use `getDeviceHandle()` instead. + * + */ + [[deprecated("Deprecated and will be removed in the next major version. Please use `getDeviceHandle()` instead.")]] + esp_io_expander_handle_t getHandle(void) + { + return getDeviceHandle(); + } + +protected: + bool checkIsInit(void) + { + return _flags.is_init; + } + + bool checkIsBegun(void) + { + return (device_handle != nullptr); + } + + bool checkIsSkipInitHost(void) + { + return _flags.skip_init_host; + } + + i2c_port_t getHostID(void) + { + return static_cast(_host_id); + } + + const i2c_config_t &getHostConfig(void) + { + return _host_config; + } + + uint8_t getDeviceAddress(void) + { + return _device_address; + } + + esp_io_expander_handle_t 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; +}; + +} // namespace esp_expander + +/** + * @deprecated Deprecated and will be removed in the next major version. Please 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."))); diff --git a/src/chip/esp_expander_ch422g.cpp b/src/chip/esp_expander_ch422g.cpp new file mode 100644 index 0000000..f2cd30a --- /dev/null +++ b/src/chip/esp_expander_ch422g.cpp @@ -0,0 +1,100 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_expander_utils.h" +#include "port/esp_io_expander_ch422g.h" +#include "esp_expander_ch422g.hpp" + +namespace esp_expander { + +CH422G::~CH422G() +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_EXIT(del(), "Delete failed"); + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); +} + +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_ERROR_RETURN( + esp_io_expander_new_i2c_ch422g(getHostID(), getDeviceAddress(), &device_handle), false, + "Create CH422G IO expander failed" + ); + ESP_UTILS_LOGD("Create CH422G IO expander(@%p)", device_handle); + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); + + return true; +} + +bool CH422G::enableOC_OpenDrain(void) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + + ESP_UTILS_CHECK_ERROR_RETURN( + 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) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + + ESP_UTILS_CHECK_ERROR_RETURN( + 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) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + + ESP_UTILS_CHECK_ERROR_RETURN( + 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) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + + ESP_UTILS_CHECK_ERROR_RETURN( + esp_io_expander_ch422g_set_all_output(device_handle), false, "Set all output 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 new file mode 100644 index 0000000..ac7281f --- /dev/null +++ b/src/chip/esp_expander_ch422g.hpp @@ -0,0 +1,121 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_expander_base.hpp" + +namespace esp_expander { + +/** + * @brief The CH422G IO expander device class + * + * @note This class is a derived class of `esp_expander::Base`, user can use it directly + * @note Pin map: + * | 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: + /** + * @brief Construct a CH422G 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`. + * + */ + CH422G(int scl_io, int sda_io, uint8_t address): 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`. + * + */ + CH422G(int host_id, uint8_t address): 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) {} + + /** + * @brief Desutruct object. This function will call `del()` to delete the object. + * + */ + ~CH422G() 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 IO0-7 pins to output high-level mode by default. + * + * @return true if success, otherwise false + * + */ + bool begin(void) override; + + /** + * @brief Enable OC0-OC3 output open-drain + * + * @return true if success, otherwise false + * + */ + bool enableOC_OpenDrain(void); + + /** + * @brief Enable OC0-OC3 output push-pull (default mode when power-on) + * + * @return true if success, otherwise false + * + */ + bool enableOC_PushPull(void); + + /** + * @brief Enable IO0-7 input mode + * + * @note The driver initialization by default sets CH422G's IO0-7 to output high-level mode. + * @note 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. + * + * @return true if success, otherwise false + * + */ + bool enableAllIO_Input(void); + + /** + * @brief Enable IO0-7 output mode + * + * @return true if success, otherwise false + * + */ + bool enableAllIO_Output(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 new file mode 100644 index 0000000..e69f566 --- /dev/null +++ b/src/chip/esp_expander_ht8574.cpp @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_expander_utils.h" +#include "port/esp_io_expander_ht8574.h" +#include "esp_expander_ht8574.hpp" + +namespace esp_expander { + +HT8574::~HT8574() +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_EXIT(del(), "Delete failed"); + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); +} + +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_ERROR_RETURN( + esp_io_expander_new_i2c_ht8574(getHostID(), getDeviceAddress(), &device_handle), false, + "Create HT8574 IO expander failed" + ); + ESP_UTILS_LOGD("Create HT8574 IO expander(@%p)", device_handle); + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); + + return true; +} + +} // namespace esp_expander diff --git a/src/chip/esp_expander_ht8574.hpp b/src/chip/esp_expander_ht8574.hpp new file mode 100644 index 0000000..87cd23b --- /dev/null +++ b/src/chip/esp_expander_ht8574.hpp @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 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_16bit.cpp b/src/chip/esp_expander_tca95xx_16bit.cpp new file mode 100644 index 0000000..2f5c8f7 --- /dev/null +++ b/src/chip/esp_expander_tca95xx_16bit.cpp @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_expander_utils.h" +#include "port/esp_io_expander_tca95xx_16bit.h" +#include "esp_expander_tca95xx_16bit.hpp" + +namespace esp_expander { + +TCA95XX_16BIT::~TCA95XX_16BIT() +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_EXIT(del(), "Delete failed"); + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); +} + +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_ERROR_RETURN( + esp_io_expander_new_i2c_tca95xx_16bit(getHostID(), getDeviceAddress(), &device_handle), false, + "Create TCA95XX_16BIT IO expander failed" + ); + ESP_UTILS_LOGD("Create TCA95XX_16BIT IO expander(@%p)", device_handle); + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); + + return true; +} + +} // namespace esp_expander diff --git a/src/chip/esp_expander_tca95xx_16bit.hpp b/src/chip/esp_expander_tca95xx_16bit.hpp new file mode 100644 index 0000000..b27ea6d --- /dev/null +++ b/src/chip/esp_expander_tca95xx_16bit.hpp @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_expander_base.hpp" + +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: + /** + * @brief Construct a TCA95XX_16BIT 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_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`. + * + */ + TCA95XX_16BIT(int host_id, uint8_t address): Base(host_id, address) {} + + /** + * @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; + + /** + * @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_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 new file mode 100644 index 0000000..7ea43c6 --- /dev/null +++ b/src/chip/esp_expander_tca95xx_8bit.cpp @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_expander_utils.h" +#include "port/esp_io_expander_tca9554.h" +#include "esp_expander_tca95xx_8bit.hpp" + +namespace esp_expander { + +TCA95XX_8BIT::~TCA95XX_8BIT() +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_EXIT(del(), "Delete failed"); + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); +} + +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_ERROR_RETURN( + esp_io_expander_new_i2c_tca9554(getHostID(), getDeviceAddress(), &device_handle), false, + "Create TCA95XX_8BIT IO expander failed" + ); + ESP_UTILS_LOGD("Create TCA95XX_8BIT IO expander(@%p)", device_handle); + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); + + return true; +} + +} // namespace esp_expander diff --git a/src/chip/esp_expander_tca95xx_8bit.hpp b/src/chip/esp_expander_tca95xx_8bit.hpp new file mode 100644 index 0000000..75da75e --- /dev/null +++ b/src/chip/esp_expander_tca95xx_8bit.hpp @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 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/esp_expander_utils.h b/src/esp_expander_utils.h new file mode 100644 index 0000000..a107319 --- /dev/null +++ b/src/esp_expander_utils.h @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +// 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" diff --git a/src/esp_io_expander.hpp b/src/esp_io_expander.hpp new file mode 100644 index 0000000..e9a2062 --- /dev/null +++ b/src/esp_io_expander.hpp @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +/* Porting drivers */ +#include "port/esp_io_expander.h" +#include "port/esp_io_expander_ch422g.h" +#include "port/esp_io_expander_ht8574.h" +#include "port/esp_io_expander_tca9554.h" +#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" diff --git a/src/base/esp_io_expander.c b/src/port/esp_io_expander.c similarity index 97% rename from src/base/esp_io_expander.c rename to src/port/esp_io_expander.c index 3cf86a8..b27c743 100644 --- a/src/base/esp_io_expander.c +++ b/src/port/esp_io_expander.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,14 +9,12 @@ #include "esp_bit_defs.h" #include "esp_check.h" -#ifdef LOG_LOCAL_LEVEL -#undef LOG_LOCAL_LEVEL -#endif -#define LOG_LOCAL_LEVEL ESP_LOG_INFO #include "esp_log.h" #include "esp_io_expander.h" +#include "esp_expander_utils.h" + #define VALID_IO_COUNT(handle) ((handle)->config.io_count <= IO_COUNT_MAX ? (handle)->config.io_count : IO_COUNT_MAX) /** @@ -29,7 +27,7 @@ typedef enum { REG_DIRECTION, } reg_type_t; -static const char *TAG = "io_expander"; +static 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); @@ -134,8 +132,6 @@ esp_err_t esp_io_expander_print_state(esp_io_expander_handle_t handle) { ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); - esp_log_level_set(TAG, ESP_LOG_INFO); - uint8_t io_count = VALID_IO_COUNT(handle); uint32_t input_reg, output_reg, dir_reg; ESP_RETURN_ON_ERROR(read_reg(handle, REG_INPUT, &input_reg), TAG, "Read input reg failed"); diff --git a/src/base/esp_io_expander.h b/src/port/esp_io_expander.h similarity index 98% rename from src/base/esp_io_expander.h rename to src/port/esp_io_expander.h index da6e121..e36adf4 100644 --- a/src/base/esp_io_expander.h +++ b/src/port/esp_io_expander.h @@ -1,9 +1,14 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +/** + * @file + * @brief ESP IO expander + */ + #pragma once #include diff --git a/src/chip/CH422G.cpp b/src/port/esp_io_expander_ch422g.c similarity index 83% rename from src/chip/CH422G.cpp rename to src/port/esp_io_expander_ch422g.c index 5a9019f..9b6ce6a 100644 --- a/src/chip/CH422G.cpp +++ b/src/port/esp_io_expander_ch422g.c @@ -13,8 +13,10 @@ #include "esp_check.h" #include "esp_log.h" -#include "../private/CheckResult.h" -#include "CH422G.h" +#include "esp_io_expander.h" +#include "esp_io_expander_ch422g.h" + +#include "esp_expander_utils.h" /* Timeout of each I2C communication */ #define I2C_TIMEOUT_MS (10) @@ -61,7 +63,6 @@ typedef struct { static const char *TAG = "ch422g"; -static esp_err_t esp_io_expander_new_i2c_ch422g(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle); 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); static esp_err_t read_output_reg(esp_io_expander_handle_t handle, uint32_t *value); @@ -70,102 +71,105 @@ 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_IOExpander_CH422G::~ESP_IOExpander_CH422G() +esp_err_t esp_io_expander_new_i2c_ch422g(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle) { - if (i2c_need_init) { - i2c_driver_delete(i2c_id); - } - if (handle) { - del(); - } -} + 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"); -void ESP_IOExpander_CH422G::begin(void) -{ - CHECK_ERROR_RETURN(esp_io_expander_new_i2c_ch422g(i2c_id, i2c_address, &handle)); + 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"); + + 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; + ch422g->base.read_input_reg = read_input_reg; + ch422g->base.write_output_reg = write_output_reg; + ch422g->base.read_output_reg = read_output_reg; + ch422g->base.write_direction_reg = write_direction_reg; + ch422g->base.read_direction_reg = read_direction_reg; + 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; + return ESP_OK; +err: + free(ch422g); + return ret; } -void ESP_IOExpander_CH422G::enableOC_OpenDrain(void) +esp_err_t esp_io_expander_ch422g_set_oc_open_drain(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_OD_EN); // WR-SET - CHECK_ERROR_RETURN( - i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)) + 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; } -void ESP_IOExpander_CH422G::enableOC_PushPull(void) +esp_err_t esp_io_expander_ch422g_set_oc_push_pull(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_OD_EN); // WR-SET - CHECK_ERROR_RETURN( - i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)) + 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; } -void ESP_IOExpander_CH422G::enableAllIO_Input(void) +esp_err_t esp_io_expander_ch422g_set_all_input(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_IO_OE); // WR-SET - CHECK_ERROR_RETURN( - i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)) + 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; // Delay 1ms to wait for the IO expander to switch to input mode vTaskDelay(pdMS_TO_TICKS(2)); + + return ESP_OK; } -void ESP_IOExpander_CH422G::enableAllIO_Output(void) +esp_err_t esp_io_expander_ch422g_set_all_output(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_IO_OE); // WR-SET - CHECK_ERROR_RETURN( - i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)) + 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; -} - -static 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_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_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"); - - 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; - ch422g->base.read_input_reg = read_input_reg; - ch422g->base.write_output_reg = write_output_reg; - ch422g->base.read_output_reg = read_output_reg; - ch422g->base.write_direction_reg = write_direction_reg; - ch422g->base.read_direction_reg = read_direction_reg; - 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; return ESP_OK; -err: - free(ch422g); - return ret; } static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value) diff --git a/src/port/esp_io_expander_ch422g.h b/src/port/esp_io_expander_ch422g.h new file mode 100644 index 0000000..46f4f2a --- /dev/null +++ b/src/port/esp_io_expander_ch422g.h @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include "driver/i2c.h" +#include "esp_err.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_PATCH (0) + +/** + * @brief Create a new ch422g IO expander driver + * + * @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 + * + * @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); + +/** + * @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) + +esp_err_t esp_io_expander_ch422g_set_oc_open_drain(esp_io_expander_handle_t handle); + +esp_err_t esp_io_expander_ch422g_set_oc_push_pull(esp_io_expander_handle_t handle); + +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); + +#ifdef __cplusplus +} +#endif diff --git a/src/chip/HT8574.cpp b/src/port/esp_io_expander_ht8574.c similarity index 87% rename from src/chip/HT8574.cpp rename to src/port/esp_io_expander_ht8574.c index 9a82d88..5236bf3 100644 --- a/src/chip/HT8574.cpp +++ b/src/port/esp_io_expander_ht8574.c @@ -13,8 +13,10 @@ #include "esp_check.h" #include "esp_log.h" -#include "../private/CheckResult.h" -#include "HT8574.h" +#include "esp_io_expander.h" +#include "esp_io_expander_ht8574.h" + +#include "esp_expander_utils.h" /* Timeout of each I2C communication */ #define I2C_TIMEOUT_MS (10) @@ -39,24 +41,7 @@ typedef struct { } regs; } esp_io_expander_ht8574_t; -static const char *TAG = "ht8574"; - -static 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_IOExpander_HT8574::~ESP_IOExpander_HT8574() -{ - if (i2c_need_init) { - i2c_driver_delete(i2c_id); - } - if (handle) { - del(); - } -} - -void ESP_IOExpander_HT8574::begin(void) -{ - CHECK_ERROR_RETURN(esp_io_expander_new_i2c_ht8574(i2c_id, i2c_address, &handle)); -} +static 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); @@ -66,8 +51,10 @@ 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); -static 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_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle) { + 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"); @@ -78,7 +65,6 @@ static esp_err_t esp_io_expander_new_i2c_ht8574(i2c_port_t i2c_num, uint32_t i2c ht8574->base.config.flags.dir_out_bit_zero = 1; ht8574->i2c_num = i2c_num; ht8574->i2c_address = i2c_address; - ht8574->regs.output = OUT_REG_DEFAULT_VAL; ht8574->base.read_input_reg = read_input_reg; ht8574->base.write_output_reg = write_output_reg; ht8574->base.read_output_reg = read_output_reg; diff --git a/src/port/esp_io_expander_ht8574.h b/src/port/esp_io_expander_ht8574.h new file mode 100644 index 0000000..316994c --- /dev/null +++ b/src/port/esp_io_expander_ht8574.h @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include "driver/i2c.h" +#include "esp_err.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_PATCH (0) + +/** + * @brief Create a new ht8574 IO expander driver + * + * @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 + * + * @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); + +/** + * @brief I2C address of the ht8574 + * + * The 8-bit address format is as follows: + * + * (Slave Address) + * ┌─────────────────┷─────────────────┐ + * ┌─────┐─────┐─────┐─────┐─────┐─────┐─────┐─────┐ + * | 0 | 1 | 1 | 1 | A2 | A1 | A0 | R/W | + * └─────┘─────┘─────┘─────┘─────┘─────┘─────┘─────┘ + * └────────┯────────┘ └─────┯──────┘ + * (Fixed) (Hardware Selectable) + * + * And the 7-bit slave address is the most important data for users. + * For example, if a chip's A0,A1,A2 are connected to GND, it's 7-bit slave address is 0111000b(0x38). + * Then users can use `ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_000` to init it. + */ +#define ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_000 (0x38) +#define ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_001 (0x29) +#define ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_010 (0x2A) +#define ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_011 (0x2B) +#define ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_100 (0x2C) + + +#ifdef __cplusplus +} +#endif diff --git a/src/port/esp_io_expander_tca9554.c b/src/port/esp_io_expander_tca9554.c new file mode 100644 index 0000000..f3ebaf1 --- /dev/null +++ b/src/port/esp_io_expander_tca9554.c @@ -0,0 +1,161 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#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 "esp_expander_utils.h" + +/* Timeout of each I2C communication */ +#define I2C_TIMEOUT_MS (10) + +#define IO_COUNT (8) + +/* Register address */ +#define INPUT_REG_ADDR (0x00) +#define OUTPUT_REG_ADDR (0x01) +#define DIRECTION_REG_ADDR (0x03) + +/* Default register value on power-up */ +#define DIR_REG_DEFAULT_VAL (0xff) +#define OUT_REG_DEFAULT_VAL (0xff) + +/** + * @brief Device Structure Type + * + */ +typedef struct { + esp_io_expander_t base; + i2c_port_t i2c_num; + uint32_t i2c_address; + struct { + uint8_t direction; + uint8_t output; + } regs; +} esp_io_expander_tca9554_t; + +static 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); +static esp_err_t read_output_reg(esp_io_expander_handle_t handle, uint32_t *value); +static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t value); +static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *value); +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_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_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"); + + 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; + tca9554->base.write_direction_reg = write_direction_reg; + tca9554->base.read_direction_reg = read_direction_reg; + 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; + return ESP_OK; +err: + free(tca9554); + return ret; +} + +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* + *value = temp; + return ESP_OK; +} + +static esp_err_t write_output_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); + 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"); + tca9554->regs.output = value; + return ESP_OK; +} + +static esp_err_t read_output_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); + + *value = tca9554->regs.output; + return ESP_OK; +} + +static esp_err_t write_direction_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); + 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"); + tca9554->regs.direction = value; + return ESP_OK; +} + +static esp_err_t read_direction_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); + + *value = tca9554->regs.direction; + return ESP_OK; +} + +static esp_err_t reset(esp_io_expander_t *handle) +{ + ESP_RETURN_ON_ERROR(write_direction_reg(handle, DIR_REG_DEFAULT_VAL), TAG, "Write dir reg failed"); + ESP_RETURN_ON_ERROR(write_output_reg(handle, OUT_REG_DEFAULT_VAL), TAG, "Write output reg failed"); + return ESP_OK; +} + +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); + + free(tca9554); + return ESP_OK; +} diff --git a/src/chip/TCA95xx_8bit.h b/src/port/esp_io_expander_tca9554.h similarity index 60% rename from src/chip/TCA95xx_8bit.h rename to src/port/esp_io_expander_tca9554.h index bab368a..b3f39ed 100644 --- a/src/chip/TCA95xx_8bit.h +++ b/src/port/esp_io_expander_tca9554.h @@ -1,9 +1,14 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +/** + * @file + * @brief ESP IO expander: TCA9554 + */ + #pragma once #include @@ -11,59 +16,29 @@ #include "driver/i2c.h" #include "esp_err.h" -#include "../ESP_IOExpander.h" +#include "esp_io_expander.h" -class ESP_IOExpander_TCA95xx_8bit: public ESP_IOExpander { -public: - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note After using this function, call `init()` will initialize I2C bus. - * - * @param id I2C port number - * @param address I2C device address. Should be like `ESP_IO_EXPANDER_I2C_*`. - * Can be found in the header file of each IO expander.h. - * @param config Pointer to I2C bus configuration - */ - ESP_IOExpander_TCA95xx_8bit(i2c_port_t id, uint8_t address, const i2c_config_t *config): ESP_IOExpander(id, address, config) { }; +#ifdef __cplusplus +extern "C" { +#endif - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note After using this function, call `init()` will initialize I2C bus. - * - * @param id I2C port number - * @param address I2C device address. Should be like `ESP_IO_EXPANDER_I2C_*`. - * Can be found in the header file of each IO expander.h. - * @param scl SCL pin number - * @param sda SDA pin number - */ - ESP_IOExpander_TCA95xx_8bit(i2c_port_t id, uint8_t address, int scl, int sda): ESP_IOExpander(id, address, scl, sda) { }; +#define ESP_IO_EXPANDER_TCA9554_VER_MAJOR (1) +#define ESP_IO_EXPANDER_TCA9554_VER_MINOR (0) +#define ESP_IO_EXPANDER_TCA9554_VER_PATCH (1) - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note If use this function, should initialize I2C bus before call `init()`. - * - * @param id I2C port number - * @param address I2C device address. Should be like `ESP_IO_EXPANDER_I2C_*`. - * Can be found in the header file of each IO expander.h. - */ - ESP_IOExpander_TCA95xx_8bit(i2c_port_t id, uint8_t address): ESP_IOExpander(id, address) { }; - - /** - * @brief Destructor - * - * @note This function will delete I2C driver if it is initialized by ESP_IOExpander and delete ESP_IOExpander object. - */ - ~ESP_IOExpander_TCA95xx_8bit() override; - - /** - * @brief Begin IO expander - * - */ - void begin(void) override; -}; +/** + * @brief Create a new TCA9554 IO expander driver + * + * @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 + * + * @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); /** * @brief I2C address of the TCA9554 @@ -117,3 +92,7 @@ class ESP_IOExpander_TCA95xx_8bit: public ESP_IOExpander { #define ESP_IO_EXPANDER_I2C_TCA9554A_ADDRESS_101 (0x3D) #define ESP_IO_EXPANDER_I2C_TCA9554A_ADDRESS_110 (0x3E) #define ESP_IO_EXPANDER_I2C_TCA9554A_ADDRESS_111 (0x3F) + +#ifdef __cplusplus +} +#endif diff --git a/src/chip/TCA95xx_16bit.cpp b/src/port/esp_io_expander_tca95xx_16bit.c similarity index 81% rename from src/chip/TCA95xx_16bit.cpp rename to src/port/esp_io_expander_tca95xx_16bit.c index 7c73d61..18edd5a 100644 --- a/src/chip/TCA95xx_16bit.cpp +++ b/src/port/esp_io_expander_tca95xx_16bit.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,8 +13,10 @@ #include "esp_check.h" #include "esp_log.h" -#include "../private/CheckResult.h" -#include "TCA95xx_16bit.h" +#include "esp_io_expander.h" +#include "esp_io_expander_tca95xx_16bit.h" + +#include "esp_expander_utils.h" /* Timeout of each I2C communication */ #define I2C_TIMEOUT_MS (10) @@ -39,29 +41,12 @@ typedef struct { i2c_port_t i2c_num; uint32_t i2c_address; struct { - uint8_t direction; - uint8_t output; + uint16_t direction; + uint16_t output; } regs; } esp_io_expander_tca95xx_16bit_t; -static const char *TAG = "tca95xx_16bit"; - -ESP_IOExpander_TCA95xx_16bit::~ESP_IOExpander_TCA95xx_16bit() -{ - if (i2c_need_init) { - i2c_driver_delete(i2c_id); - } - if (handle) { - del(); - } -} - -static 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); - -void ESP_IOExpander_TCA95xx_16bit::begin(void) -{ - CHECK_ERROR_RETURN(esp_io_expander_new_i2c_tca95xx_16bit(i2c_id, i2c_address, &handle)); -} +static 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); @@ -71,8 +56,10 @@ 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); -static 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_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle) { + 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"); @@ -107,13 +94,12 @@ 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}; - uint8_t reg = INPUT_REG_ADDR; // *INDENT-OFF* ESP_RETURN_ON_ERROR( - i2c_master_write_read_device(tca->i2c_num, tca->i2c_address, ®, 1, (uint8_t*)&temp, 2, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), + 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* - *value = (((uint32_t)temp[0]) << 8) | (temp[1]); + *value = (((uint32_t)temp[1]) << 8) | (temp[0]); return ESP_OK; } @@ -122,7 +108,7 @@ static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t valu esp_io_expander_tca95xx_16bit_t *tca = (esp_io_expander_tca95xx_16bit_t *)__containerof(handle, esp_io_expander_tca95xx_16bit_t, base); value &= 0xffff; - uint8_t data[] = {OUTPUT_REG_ADDR, (uint8_t)(value >> 8), (uint8_t)(value & 0xff)}; + 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"); @@ -143,7 +129,7 @@ static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t v esp_io_expander_tca95xx_16bit_t *tca = (esp_io_expander_tca95xx_16bit_t *)__containerof(handle, esp_io_expander_tca95xx_16bit_t, base); value &= 0xffff; - uint8_t data[] = {DIRECTION_REG_ADDR, (uint8_t)(value >> 8), (uint8_t)(value & 0xff)}; + 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"); diff --git a/src/chip/TCA95xx_16bit.h b/src/port/esp_io_expander_tca95xx_16bit.h similarity index 56% rename from src/chip/TCA95xx_16bit.h rename to src/port/esp_io_expander_tca95xx_16bit.h index e3e110d..80cb54f 100644 --- a/src/chip/TCA95xx_16bit.h +++ b/src/port/esp_io_expander_tca95xx_16bit.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,59 +11,29 @@ #include "driver/i2c.h" #include "esp_err.h" -#include "../ESP_IOExpander.h" +#include "esp_io_expander.h" -class ESP_IOExpander_TCA95xx_16bit: public ESP_IOExpander { -public: - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note After using this function, call `init()` will initialize I2C bus. - * - * @param id I2C port number - * @param address I2C device address. Should be like `ESP_IO_EXPANDER_I2C_*`. - * Can be found in the header file of each IO expander.h. - * @param config Pointer to I2C bus configuration - */ - ESP_IOExpander_TCA95xx_16bit(i2c_port_t id, uint8_t address, const i2c_config_t *config): ESP_IOExpander(id, address, config) { }; +#ifdef __cplusplus +extern "C" { +#endif - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note After using this function, call `init()` will initialize I2C bus. - * - * @param id I2C port number - * @param address I2C device address. Should be like `ESP_IO_EXPANDER_I2C_*`. - * Can be found in the header file of each IO expander.h. - * @param scl SCL pin number - * @param sda SDA pin number - */ - ESP_IOExpander_TCA95xx_16bit(i2c_port_t id, uint8_t address, int scl, int sda): ESP_IOExpander(id, address, scl, sda) { }; +#define ESP_IO_EXPANDER_TCA95XX_16BIT_VER_MAJOR (1) +#define ESP_IO_EXPANDER_TCA95XX_16BIT_VER_MINOR (0) +#define ESP_IO_EXPANDER_TCA95XX_16BIT_VER_PATCH (0) - /** - * @brief Constructor to create ESP_IOExpander object - * - * @note If use this function, should initialize I2C bus before call `init()`. - * - * @param id I2C port number - * @param address I2C device address. Should be like `ESP_IO_EXPANDER_I2C_*`. - * Can be found in the header file of each IO expander.h. - */ - ESP_IOExpander_TCA95xx_16bit(i2c_port_t id, uint8_t address): ESP_IOExpander(id, address) { }; - - /** - * @brief Destructor - * - * @note This function will delete I2C driver if it is initialized by ESP_IOExpander and delete ESP_IOExpander object. - */ - ~ESP_IOExpander_TCA95xx_16bit() override; - - /** - * @brief Begin IO expander - * - */ - void begin(void) override; -}; +/** + * @brief Create a new TCA95XX_16BIT IO expander driver + * + * @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 + * + * @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); /** * @brief I2C address of the TCA9539 or TCA9555 @@ -106,3 +76,7 @@ enum esp_io_expander_tca_95xx_16bit_address { ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_110 = 0b0100110, ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_111 = 0b0100111, }; + +#ifdef __cplusplus +} +#endif diff --git a/src/private/CheckResult.cpp b/src/private/CheckResult.cpp deleted file mode 100644 index da17b7f..0000000 --- a/src/private/CheckResult.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -namespace esp_io_expander { - -const char *path_to_file_name(const char *path) -{ - size_t i = 0; - size_t pos = 0; - char *p = (char *)path; - while (*p) { - i++; - if (*p == '/' || *p == '\\') { - pos = i; - } - p++; - } - return path + pos; -} - -} diff --git a/src/private/CheckResult.h b/src/private/CheckResult.h deleted file mode 100644 index 18183c6..0000000 --- a/src/private/CheckResult.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef CHECKRESULT_H -#define CHECKRESULT_H - -#include -#include -#include - -#include "esp_check.h" -#include "esp_log.h" - -#define ERROR_CHECK_LOG_FORMAT(format) "[%s:%u] %s(): " format, esp_io_expander::path_to_file_name(__FILE__), __LINE__, __FUNCTION__ -#define ERROR_CHECK_LOGE(tag, format, ...) ESP_LOGE(tag, ERROR_CHECK_LOG_FORMAT(format), ##__VA_ARGS__) - -#define CHECK_ERROR_RETURN(x) do { \ - esp_err_t err_rc_ = (x); \ - if (unlikely(err_rc_ != ESP_OK)) { \ - ERROR_CHECK_LOGE(TAG, "Check error %d (%s)", err_rc_, esp_err_to_name(err_rc_)); \ - return; \ - } \ - } while(0) - -#define CHECK_ERROR_GOTO(x, goto_tag) do { \ - esp_err_t err_rc_ = (x); \ - if (unlikely(err_rc_ != ESP_OK)) { \ - ERROR_CHECK_LOGE(TAG, "Check error %d (%s)", err_rc_, esp_err_to_name(err_rc_)); \ - goto goto_tag; \ - } \ - } while(0) - -#define CHECK_NULL_RETURN(x) do { \ - if ((x) == NULL) { \ - ERROR_CHECK_LOGE(TAG, "Check NULL"); \ - return; \ - } \ - } while(0) - -#define CHECK_NULL_GOTO(x, goto_tag) do { \ - if ((x) == NULL) { \ - ERROR_CHECK_LOGE(TAG, "Check NULL"); \ - goto goto_tag; \ - } \ - } while(0) - -#define CHECK_FALSE_RETURN(x) do { \ - if (unlikely((x) == false)) { \ - ERROR_CHECK_LOGE(TAG, "Check false"); \ - return; \ - } \ - } while(0) - -#define CHECK_FALSE_GOTO(x, goto_tag) do { \ - if (unlikely((x) == false)) { \ - ERROR_CHECK_LOGE(TAG, "Check false"); \ - goto goto_tag; \ - } \ - } while(0) - -namespace esp_io_expander { -const char *path_to_file_name(const char *path); -} - -#endif diff --git a/test_apps/CMakeLists.txt b/test_apps/CMakeLists.txt index 6850cc5..55de4fe 100644 --- a/test_apps/CMakeLists.txt +++ b/test_apps/CMakeLists.txt @@ -1,5 +1,6 @@ # The following lines of boilerplate have to be in your project's CMakeLists # in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) +set(component main) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(io_expander_test) diff --git a/test_apps/main/CMakeLists.txt b/test_apps/main/CMakeLists.txt index d833001..4bf02cc 100644 --- a/test_apps/main/CMakeLists.txt +++ b/test_apps/main/CMakeLists.txt @@ -1,3 +1,4 @@ -idf_component_register(SRCS "test_app_main.cpp") - -target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-missing-field-initializers) +idf_component_register( + SRCS "test_app_main.cpp" "test_chip_general.cpp" + WHOLE_ARCHIVE +) diff --git a/test_apps/main/idf_component.yml b/test_apps/main/idf_component.yml index c6e36a8..6af3a2e 100644 --- a/test_apps/main/idf_component.yml +++ b/test_apps/main/idf_component.yml @@ -2,8 +2,6 @@ dependencies: test_utils: path: ${IDF_PATH}/tools/unit-test-app/components/test_utils - test_driver_utils: - path: ${IDF_PATH}/components/driver/test_apps/components/test_driver_utils ESP32_IO_Expander: version: "*" override_path: "../../../ESP32_IO_Expander" diff --git a/test_apps/main/test_app_main.cpp b/test_apps/main/test_app_main.cpp index 9a1b0fa..ff6d33f 100644 --- a/test_apps/main/test_app_main.cpp +++ b/test_apps/main/test_app_main.cpp @@ -4,108 +4,36 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include - #include "driver/i2c.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_heap_caps.h" #include "esp_log.h" #include "unity.h" -#include "unity_test_runner.h" - -#include "ESP_IOExpander_Library.h" - -static const char *TAG = "ESP_IOxpander_test"; +#include "unity_test_utils.h" -#define CHIP_NAME TCA95xx_8bit -#define I2C_HOST (I2C_NUM_0) -#define I2C_SDA_PIN (8) -#define I2C_SCL_PIN (18) +#define TEST_MEMORY_LEAK_THRESHOLD (400) -#define _EXAMPLE_CHIP_CLASS(name, ...) ESP_IOExpander_##name(__VA_ARGS__) -#define EXAMPLE_CHIP_CLASS(name, ...) _EXAMPLE_CHIP_CLASS(name, ##__VA_ARGS__) - -TEST_CASE("test ESP IO expander functions", "[io_expander]") +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) +void setUp(void) { - ESP_IOExpander *expander = NULL; - const i2c_config_t i2c_config = EXPANDER_I2C_CONFIG_DEFAULT(I2C_SCL_PIN, I2C_SDA_PIN); - - ESP_LOGI(TAG, "Test initialization with external I2C"); - TEST_ASSERT_EQUAL(i2c_param_config(I2C_HOST, &i2c_config), ESP_OK); - TEST_ASSERT_EQUAL(i2c_driver_install(I2C_HOST, i2c_config.mode, 0, 0, 0), ESP_OK); - expander = new EXAMPLE_CHIP_CLASS(CHIP_NAME, I2C_HOST, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000); - expander->init(); - expander->begin(); - expander->reset(); - expander->del(); - delete expander; - i2c_driver_delete(I2C_HOST); - - ESP_LOGI(TAG, "Test initialization with internal I2C (with config)"); - expander = new EXAMPLE_CHIP_CLASS(CHIP_NAME, I2C_HOST, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000, &i2c_config); - expander->init(); - expander->begin(); - expander->reset(); - expander->del(); - delete expander; - - ESP_LOGI(TAG, "Test initialization with internal I2C (without config)"); - expander = new EXAMPLE_CHIP_CLASS(CHIP_NAME, I2C_HOST, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000, I2C_SCL_PIN, I2C_SDA_PIN); - expander->init(); - expander->begin(); - expander->reset(); - - ESP_LOGI(TAG, "Test input/output functions"); - ESP_LOGI(TAG, "Original status:"); - expander->printStatus(); - - expander->pinMode(0, OUTPUT); - expander->pinMode(1, OUTPUT); - expander->multiPinMode(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, OUTPUT); - - ESP_LOGI(TAG, "Set pint 0-3 to output mode:"); - expander->printStatus(); - - expander->digitalWrite(0, LOW); - expander->digitalWrite(1, LOW); - expander->multiDigitalWrite(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, LOW); - - ESP_LOGI(TAG, "Set pint 0-3 to low level:"); - expander->printStatus(); - - expander->pinMode(0, INPUT); - expander->pinMode(1, INPUT); - expander->multiPinMode(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, INPUT); - - ESP_LOGI(TAG, "Set pint 0-3 to input mode:"); - expander->printStatus(); - - int level[4] = {0, 0, 0, 0}; - uint32_t level_temp; - - // Read pin 0-3 level - level[0] = expander->digitalRead(0); - level[1] = expander->digitalRead(1); - level_temp = expander->multiDigitalRead(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3); - level[2] = level_temp & IO_EXPANDER_PIN_NUM_2 ? HIGH : LOW; - level[3] = level_temp & IO_EXPANDER_PIN_NUM_3 ? HIGH : LOW; - ESP_LOGI(TAG, "Pin 0-3 level: %d %d %d %d", level[0], level[1], level[2], level[3]); - - delete expander; + unity_utils_record_free_mem(); } -// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case -#define TEST_MEMORY_LEAK_THRESHOLD (-300) - +void tearDown(void) +{ + esp_reent_cleanup(); //clean up some of the newlib's lazy allocations + unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD); +} +#else static size_t before_free_8bit; static size_t before_free_32bit; static void check_leak(size_t before_free, size_t after_free, const char *type) { - ssize_t delta = after_free - before_free; + ssize_t delta = before_free - after_free; printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); - TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); + TEST_ASSERT_MESSAGE(delta < TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); } void setUp(void) @@ -121,6 +49,7 @@ void tearDown(void) check_leak(before_free_8bit, after_free_8bit, "8BIT"); check_leak(before_free_32bit, after_free_32bit, "32BIT"); } +#endif extern "C" void app_main(void) { diff --git a/test_apps/main/test_chip_general.cpp b/test_apps/main/test_chip_general.cpp new file mode 100644 index 0000000..5542dd8 --- /dev/null +++ b/test_apps/main/test_chip_general.cpp @@ -0,0 +1,172 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "driver/i2c.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "esp_io_expander.hpp" + +using namespace esp_expander; + +static const char *TAG = "general_test"; + +/* The following default configurations are for the board 'Espressif: ESP32_S3_LCD_EV_BOARD_V1_5, TCA9554' */ +#define TEST_HOST_ID (I2C_NUM_0) +#define TEST_HOST_I2C_SCL_PIN (48) +#define TEST_HOST_I2C_SDA_PIN (47) +#define TEST_DEVICE_ADDRESS (ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000) + +#define HOST_CONFIG_DEFAULT(scl, sda) \ + { \ + .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, \ + } + +#define _CREATE_DEVICE(name, ...) \ + ({ \ + ESP_LOGI(TAG, "Create device: " #name); \ + std::shared_ptr device = std::make_shared(__VA_ARGS__); \ + TEST_ASSERT_NOT_NULL_MESSAGE(device, "Create device failed"); \ + device; \ + }) +#define CREATE_DEVICE(name, ...) _CREATE_DEVICE(name, ##__VA_ARGS__) + +static void 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); +} + +static void deinit_host(void) +{ + TEST_ASSERT_EQUAL(i2c_driver_delete(TEST_HOST_ID), ESP_OK); +} + +static void test_device(std::shared_ptr device) +{ + TEST_ASSERT_MESSAGE(device->init(), "Device initialization failed"); + TEST_ASSERT_MESSAGE(device->begin(), "Device begin failed"); + TEST_ASSERT_MESSAGE(device->reset(), "Device reset failed"); + TEST_ASSERT_MESSAGE(device->del(), "Device del failed"); +} + +#define CREATE_TEST_CASE(device_name) \ + TEST_CASE("test " #device_name " general functions", "[io_expander][general][" #device_name "]") \ + { \ + std::shared_ptr expander = nullptr; \ + \ + ESP_LOGI(TAG, "Initialize I2C host"); \ + 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); \ + test_device(expander); \ + expander = nullptr; \ + \ + 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, \ + }; \ + expander = CREATE_DEVICE(device_name, external_i2c_config); \ + test_device(expander); \ + expander = nullptr; \ + \ + ESP_LOGI(TAG, "Deinitialize I2C host"); \ + deinit_host(); \ + \ + 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); \ + test_device(expander); \ + expander = nullptr; \ + \ + 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, \ + }; \ + expander = CREATE_DEVICE(device_name, internal_i2c_config); \ + test_device(expander); \ + expander = nullptr; \ + \ + expander = CREATE_DEVICE(device_name, TEST_HOST_I2C_SCL_PIN, TEST_HOST_I2C_SDA_PIN, TEST_DEVICE_ADDRESS); \ + TEST_ASSERT_MESSAGE(expander->init(), "Device initialization failed"); \ + TEST_ASSERT_MESSAGE(expander->begin(), "Device begin failed"); \ + \ + ESP_LOGI(TAG, "Test input/output functions"); \ + ESP_LOGI(TAG, "Original status:"); \ + TEST_ASSERT_MESSAGE(expander->printStatus(), "Print status failed"); \ + \ + TEST_ASSERT_MESSAGE(expander->pinMode(0, OUTPUT), "Set pin 0 to output mode failed"); \ + TEST_ASSERT_MESSAGE(expander->pinMode(1, OUTPUT), "Set pin 1 to output mode failed"); \ + TEST_ASSERT_MESSAGE( \ + expander->multiPinMode(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, OUTPUT), \ + "Set pin 2-3 to output mode failed" \ + ); \ + \ + ESP_LOGI(TAG, "Set pint 0-3 to output mode:"); \ + TEST_ASSERT_MESSAGE(expander->printStatus(), "Print status failed"); \ + \ + TEST_ASSERT_MESSAGE(expander->digitalWrite(0, LOW), "Set pin 0 to low level failed"); \ + TEST_ASSERT_MESSAGE(expander->digitalWrite(1, LOW), "Set pin 1 to low level failed"); \ + TEST_ASSERT_MESSAGE( \ + expander->multiDigitalWrite(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, LOW), \ + "Set pin 2-3 to low level failed" \ + ); \ + \ + ESP_LOGI(TAG, "Set pint 0-3 to low level:"); \ + TEST_ASSERT_MESSAGE(expander->printStatus(), "Print status failed"); \ + \ + TEST_ASSERT_MESSAGE(expander->pinMode(0, INPUT), "Set pin 0 to input mode failed"); \ + TEST_ASSERT_MESSAGE(expander->pinMode(1, INPUT), "Set pin 1 to input mode failed"); \ + TEST_ASSERT_MESSAGE( \ + expander->multiPinMode(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, INPUT), \ + "Set pin 2-3 to input mode failed" \ + ); \ + \ + ESP_LOGI(TAG, "Set pint 0-3 to input mode:"); \ + TEST_ASSERT_MESSAGE(expander->printStatus(), "Print status failed"); \ + \ + int level[4] = {0, 0, 0, 0}; \ + int64_t level_temp; \ + \ + level[0] = expander->digitalRead(0); \ + TEST_ASSERT_MESSAGE(level[0] >= 0, "Read pin 0 level failed"); \ + level[1] = expander->digitalRead(1); \ + TEST_ASSERT_MESSAGE(level[1] >= 0, "Read pin 0 level failed"); \ + level_temp = expander->multiDigitalRead(IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3); \ + TEST_ASSERT_MESSAGE(level_temp >= 0, "Read pin 2-3 level failed"); \ + level[2] = level_temp & IO_EXPANDER_PIN_NUM_2 ? HIGH : LOW; \ + level[3] = level_temp & IO_EXPANDER_PIN_NUM_3 ? HIGH : LOW; \ + ESP_LOGI(TAG, "Pin 0-3 level: %d %d %d %d", level[0], level[1], level[2], level[3]); \ + } + +/** + * Here to create test cases for different devices + * + */ +CREATE_TEST_CASE(TCA95XX_8BIT) +CREATE_TEST_CASE(TCA95XX_16BIT) +CREATE_TEST_CASE(CH422G) +CREATE_TEST_CASE(HT8574) diff --git a/test_apps/sdkconfig.defaults b/test_apps/sdkconfig.defaults index f61533c..0495c8f 100644 --- a/test_apps/sdkconfig.defaults +++ b/test_apps/sdkconfig.defaults @@ -1,10 +1,3 @@ -# For IDF 5.0 -CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y -CONFIG_FREERTOS_HZ=1000 CONFIG_ESP_TASK_WDT_EN=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 - -# For IDF4.4 -CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y -CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y -CONFIG_ESP_TASK_WDT=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_COMPILER_CXX_EXCEPTIONS=y