From 2ca1b78b04fb93aa1caa170b340c6cbf7b38b628 Mon Sep 17 00:00:00 2001 From: H-sw123 <1150857014@qq.com> Date: Fri, 18 Oct 2024 10:27:08 +0800 Subject: [PATCH 01/10] Update CH422G and add a new example Closes https://github.com/esp-arduino-libs/ESP32_IO_Expander/pull/7 Closes https://github.com/esp-arduino-libs/ESP32_IO_Expander/issues/5 --- CHANGELOG.md | 6 + examples/TestCH422G/TestCH422G.ino | 102 +++++++++++++ examples/TestFunctions/TestFunctions.ino | 8 ++ library.properties | 4 +- src/chip/CH422G.cpp | 176 +++++++++++++++++------ src/chip/CH422G.h | 59 ++++++-- test_apps/main/test_ESP_IOExpander.cpp | 20 ++- 7 files changed, 306 insertions(+), 69 deletions(-) create mode 100644 examples/TestCH422G/TestCH422G.ino diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b88de5..6c4e4d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # ChangeLog +## v0.0.4 - 2024-10-18 + +### Enhancements: + +* Update CH422G and add a new example to show how to use it (@H-sw123) + ## v0.0.3 - 2024-05-07 ### Enhancements: diff --git a/examples/TestCH422G/TestCH422G.ino b/examples/TestCH422G/TestCH422G.ino new file mode 100644 index 0000000..016acdf --- /dev/null +++ b/examples/TestCH422G/TestCH422G.ino @@ -0,0 +1,102 @@ +/** + * | Supported IO Expanders | CH422G | + * | ------------------------- | ------ | + * + * # CH422G Test Example + * + * The hardware device used in this example is waveshare ESP32-S3-Touch-LCD-4.3B-BOX. To test the simultaneous use of I/O input and OC output, connect DO0 to DI0, and connect DO1 to DI1. + * + * ## How to use + * + * 1. Enable USB CDC. + * 2. Verify and upload the example to your board. + * + * ## Serial Output + * + * ``` + * ... + * Test begin + * Set the OC pin to push-pull output mode. + * Set the IO0-7 pin to input mode. + * Set pint 8 and 9 to:0, 1 + * + * Read pin 0 and 5 level: 0, 1 + * + * Set pint 8 and 9 to:1, 0 + * + * Read pin 0 and 5 level: 1, 0 + * ... + * ``` + * + * ## Troubleshooting + * + * The driver initialization by default sets CH422G's IO0-7 to output high-level mode. + Since the input/output mode of CH422G's IO0-7 must remain consistent, the driver will only set IO0-7 to + input mode when it determines that all pins are configured as input. + Using pinMode and multiPinMode will be invalid. You can only set the pin working mode through enableAllIO_Input, enableAllIO_Output, enableOC_PushPull and enableOC_OpenDrain + * + */ + +#include +#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; + +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->init(); + expander->begin(); + + /* For CH422G */ + Serial.println("Set the OC pin to push-pull output mode."); + expander->enableOC_PushPull(); + + // Serial.println("Set the OC pin to open_drain output mode."); + // expander->enableOC_OpenDrain(); + + Serial.println("Set the IO0-7 pin to input mode."); + expander->enableAllIO_Input(); + + // Serial.println("Set the IO0-7 pin to output mode."); + // expander->enableAllIO_Output(); +} + +int level[2] = { 0, 0 }; + +void loop() { + for (int i = 0; i < 100; i++) { + bool toggle = i % 2; + + Serial.print("Set pint 8 and 9 to:"); + Serial.print(toggle); + Serial.print(", "); + Serial.println(!toggle); + Serial.println(); + + // Set pin 8 and 9 level + expander->digitalWrite(8, toggle); + expander->digitalWrite(9, !toggle); + delay(1); + + // Read pin 0 and 5 level + level[0] = expander->digitalRead(0); + level[1] = expander->digitalRead(5); + + Serial.print("Read pin 0 and 5 level: "); + Serial.print(level[0]); + Serial.print(", "); + Serial.println(level[1]); + Serial.println(); + + delay(1000); + } +} diff --git a/examples/TestFunctions/TestFunctions.ino b/examples/TestFunctions/TestFunctions.ino index 39f33a5..a30791d 100644 --- a/examples/TestFunctions/TestFunctions.ino +++ b/examples/TestFunctions/TestFunctions.ino @@ -12,6 +12,8 @@ #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__) @@ -29,6 +31,12 @@ void setup() 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(); diff --git a/library.properties b/library.properties index 5c080fb..cc76fb7 100644 --- a/library.properties +++ b/library.properties @@ -1,6 +1,6 @@ name=ESP32_IO_Expander -version=0.0.3 -author=lzw655 +version=0.0.4 +author=espressif maintainer=espressif sentence=ESP32_IO_Expander is a library designed for driving IO expander chips using ESP32 SoCs paragraph=Currently support TCA95xx(8bit), TCA95xx(16bit), HT8574, CH422G diff --git a/src/chip/CH422G.cpp b/src/chip/CH422G.cpp index 56abfba..5a9019f 100644 --- a/src/chip/CH422G.cpp +++ b/src/chip/CH422G.cpp @@ -19,15 +19,30 @@ /* Timeout of each I2C communication */ #define I2C_TIMEOUT_MS (10) -#define IO_COUNT (8) +#define IO_COUNT (12) /* Register address */ -#define CH422G_REG_IN (0x26) -#define CH422G_REG_OUT (0x38) - -/* Default register value on power-up */ -#define DIR_REG_DEFAULT_VAL (0xff) -#define OUT_REG_DEFAULT_VAL (0xdf) +#define CH422G_REG_WR_SET (0x48 >> 1) +#define CH422G_REG_WR_OC (0x46 >> 1) +#define CH422G_REG_WR_IO (0x70 >> 1) +#define CH422G_REG_RD_IO (0x4D >> 1) + +/* Default register value when reset */ +// *INDENT-OFF* +#define REG_WR_SET_DEFAULT_VAL (0x01UL) // Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + // | --- | --- | --- | --- | ------- | ------- | -------- | ------- | + // Value: | / | / | / | / | [SLEEP] | [OD_EN] | [A_SCAN] | [IO_OE] | + // | --- | --- | --- | --- | ------- | ------- | -------- | ------- | + // Default: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | + +// *INDENT-OFF* +#define REG_WR_OC_DEFAULT_VAL (0x0FUL) +#define REG_WR_IO_DEFAULT_VAL (0xFFUL) +#define REG_OUT_DEFAULT_VAL ((REG_WR_OC_DEFAULT_VAL << 8) | REG_WR_IO_DEFAULT_VAL) +#define REG_DIR_DEFAULT_VAL (0xFFFUL) + +#define REG_WR_SET_BIT_IO_OE (1 << 0) +#define REG_WR_SET_BIT_OD_EN (1 << 2) /** * @brief Device Structure Type @@ -38,14 +53,22 @@ typedef struct { i2c_port_t i2c_num; uint32_t i2c_address; struct { - uint8_t direction; - uint8_t output; + uint8_t wr_set; + uint8_t wr_oc; + uint8_t wr_io; } regs; } esp_io_expander_ch422g_t; 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); +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_IOExpander_CH422G::~ESP_IOExpander_CH422G() { @@ -62,13 +85,55 @@ void ESP_IOExpander_CH422G::begin(void) CHECK_ERROR_RETURN(esp_io_expander_new_i2c_ch422g(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); +void ESP_IOExpander_CH422G::enableOC_OpenDrain(void) +{ + 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)) + ); + ch422g->regs.wr_set = data; +} + +void ESP_IOExpander_CH422G::enableOC_PushPull(void) +{ + 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)) + ); + ch422g->regs.wr_set = data; +} + +void ESP_IOExpander_CH422G::enableAllIO_Input(void) +{ + 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)) + ); + ch422g->regs.wr_set = data; + // Delay 1ms to wait for the IO expander to switch to input mode + vTaskDelay(pdMS_TO_TICKS(2)); +} + +void ESP_IOExpander_CH422G::enableAllIO_Output(void) +{ + 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)) + ); + 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) { @@ -79,10 +144,11 @@ static esp_err_t esp_io_expander_new_i2c_ch422g(i2c_port_t i2c_num, uint32_t i2c ESP_RETURN_ON_FALSE(ch422g, ESP_ERR_NO_MEM, TAG, "Malloc failed"); ch422g->base.config.io_count = IO_COUNT; - ch422g->base.config.flags.dir_out_bit_zero = 1; ch422g->i2c_num = i2c_num; ch422g->i2c_address = i2c_address; - ch422g->regs.output = OUT_REG_DEFAULT_VAL; + 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; @@ -105,37 +171,42 @@ static esp_err_t esp_io_expander_new_i2c_ch422g(i2c_port_t i2c_num, uint32_t i2c static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value) { esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - uint8_t temp = 0; ESP_RETURN_ON_ERROR( - i2c_master_read_from_device(ch422g->i2c_num, ch422g->i2c_address, &temp, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Read input reg failed"); - - // *INDENT-OFF* - ESP_RETURN_ON_ERROR( - i2c_master_read_from_device(ch422g->i2c_num, CH422G_REG_IN, &temp, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Read input reg failed"); - // *INDENT-ON* + i2c_master_read_from_device(ch422g->i2c_num, CH422G_REG_RD_IO, &temp, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), + TAG, "Read RD-IO reg failed" + ); *value = temp; + return ESP_OK; } static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t value) { esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - value &= 0xff; - uint8_t out_temp = 0x01; - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(ch422g->i2c_num, ch422g->i2c_address, &out_temp, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write output reg failed"); + uint8_t wr_oc_data = (value & 0xF00) >> 8; + uint8_t wr_io_data = value & 0xFF; + + // WR-OC + if (wr_oc_data) { + ESP_RETURN_ON_ERROR( + i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_OC, &wr_oc_data, sizeof(wr_oc_data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), + TAG, "Write WR-OC reg failed" + ); + ch422g->regs.wr_oc = wr_oc_data; + } + + // WR-IO + if (wr_io_data) { + ESP_RETURN_ON_ERROR( + i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_IO, &wr_io_data, sizeof(wr_io_data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), + TAG, "Write WR-IO reg failed" + ); + ch422g->regs.wr_io = wr_io_data; + } - uint8_t data = (uint8_t)value; - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_OUT, &data, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write output reg failed"); - ch422g->regs.output = value; return ESP_OK; } @@ -143,29 +214,50 @@ static esp_err_t read_output_reg(esp_io_expander_handle_t handle, uint32_t *valu { esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - *value = ch422g->regs.output; + *value = ch422g->regs.wr_io | (((uint32_t)ch422g->regs.wr_oc) << 8); + return ESP_OK; } static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t value) { esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - value &= 0xff; - ch422g->regs.direction = value; + uint8_t data = ch422g->regs.wr_set; + + value &= 0xFF; + if (value != 0) { + data |= REG_WR_SET_BIT_IO_OE; + } else { + data &= ~REG_WR_SET_BIT_IO_OE; + } + + // WR-SET + ESP_RETURN_ON_ERROR( + i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), + TAG, "Write WR_SET reg failed" + ); + ch422g->regs.wr_set = data; + return ESP_OK; } +#define DIR_OUT_VALUE (0xFFF) +#define DIR_IN_VALUE (0xF00) + static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *value) { esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - *value = ch422g->regs.direction; + *value = (ch422g->regs.wr_set & REG_WR_SET_BIT_IO_OE) ? DIR_OUT_VALUE : DIR_IN_VALUE; + return ESP_OK; } static esp_err_t reset(esp_io_expander_t *handle) { - ESP_RETURN_ON_ERROR(write_output_reg(handle, OUT_REG_DEFAULT_VAL), TAG, "Write output reg failed"); + ESP_RETURN_ON_ERROR(write_direction_reg(handle, REG_DIR_DEFAULT_VAL), TAG, "Write direction reg (WR_SET) failed"); + ESP_RETURN_ON_ERROR(write_output_reg(handle, REG_OUT_DEFAULT_VAL), TAG, "Write output reg (WR_OC & WR_IO) failed"); + return ESP_OK; } diff --git a/src/chip/CH422G.h b/src/chip/CH422G.h index 1153009..114d909 100644 --- a/src/chip/CH422G.h +++ b/src/chip/CH422G.h @@ -13,6 +13,13 @@ #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: /** @@ -21,11 +28,10 @@ class ESP_IOExpander_CH422G: public ESP_IOExpander { * @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 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, address, config) { }; + 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 @@ -33,12 +39,11 @@ class ESP_IOExpander_CH422G: public ESP_IOExpander { * @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 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, address, scl, sda) { }; + 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 @@ -46,10 +51,9 @@ class ESP_IOExpander_CH422G: public ESP_IOExpander { * @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. + * @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, address) { }; + ESP_IOExpander_CH422G(i2c_port_t id, uint8_t address): ESP_IOExpander(id, 0xFF) { }; /** * @brief Destructor @@ -61,15 +65,42 @@ class ESP_IOExpander_CH422G: public ESP_IOExpander { /** * @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 + * @brief I2C address of the ch422g. Just to keep the same with other IO expanders, but it is ignored. * - * 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 1001000b(0x48). - * Then users can use `ESP_IO_EXPANDER_I2C_CH422G_ADDRESS_000` to init it. */ -#define ESP_IO_EXPANDER_I2C_CH422G_ADDRESS_000 (0x24) +#define ESP_IO_EXPANDER_I2C_CH422G_ADDRESS (0x24) diff --git a/test_apps/main/test_ESP_IOExpander.cpp b/test_apps/main/test_ESP_IOExpander.cpp index 5d9d87a..9a1b0fa 100644 --- a/test_apps/main/test_ESP_IOExpander.cpp +++ b/test_apps/main/test_ESP_IOExpander.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,19 +16,17 @@ #include "ESP_IOExpander_Library.h" -// Refer to `esp32-hal-gpio.h` -#define INPUT 0x01 -#define OUTPUT 0x03 -#define LOW 0x0 -#define HIGH 0x1 - static const char *TAG = "ESP_IOxpander_test"; +#define CHIP_NAME TCA95xx_8bit #define I2C_HOST (I2C_NUM_0) #define I2C_SDA_PIN (8) #define I2C_SCL_PIN (18) -TEST_CASE("test ESP IO expander for TCA9554", "[tca9554]") +#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]") { ESP_IOExpander *expander = NULL; const i2c_config_t i2c_config = EXPANDER_I2C_CONFIG_DEFAULT(I2C_SCL_PIN, I2C_SDA_PIN); @@ -36,7 +34,7 @@ TEST_CASE("test ESP IO expander for TCA9554", "[tca9554]") 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 ESP_IOExpander_TCA95xx_8bit(I2C_HOST, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000); + expander = new EXAMPLE_CHIP_CLASS(CHIP_NAME, I2C_HOST, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000); expander->init(); expander->begin(); expander->reset(); @@ -45,7 +43,7 @@ TEST_CASE("test ESP IO expander for TCA9554", "[tca9554]") i2c_driver_delete(I2C_HOST); ESP_LOGI(TAG, "Test initialization with internal I2C (with config)"); - expander = new ESP_IOExpander_TCA95xx_8bit(I2C_HOST, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000, &i2c_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(); @@ -53,7 +51,7 @@ TEST_CASE("test ESP IO expander for TCA9554", "[tca9554]") delete expander; ESP_LOGI(TAG, "Test initialization with internal I2C (without config)"); - expander = new ESP_IOExpander_TCA95xx_8bit(I2C_HOST, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000, I2C_SCL_PIN, I2C_SDA_PIN); + 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(); From bccbedde2ecc6b7c616c63e4c3295e528f9330d2 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Tue, 5 Nov 2024 09:36:06 +0800 Subject: [PATCH 02/10] Upload to the ESP Registry and support to build on the esp-idf --- .codespellrc | 2 + .flake8 | 141 ++++++++++++++++++ ...heck_versions.sh => check_lib_versions.sh} | 46 +++--- .github/workflows/build_test.yml | 4 +- ...ck_versions.yml => check_lib_versions.yml} | 7 +- .github/workflows/issue_comment.yml | 24 +++ .github/workflows/new_issues.yml | 24 +++ .github/workflows/new_prs.yml | 29 ++++ .github/workflows/pre-commit.yml | 2 +- .github/workflows/upload_component.yml | 20 +++ .pre-commit-config.yaml | 95 +++++++++--- CHANGELOG.md | 7 + README.md | 24 ++- check_copyright_config.yaml | 11 +- idf_component.yml | 7 + library.properties | 2 +- src/base/esp_io_expander.h | 6 +- src/chip/HT8574.h | 2 +- src/chip/TCA95xx_16bit.h | 4 +- src/chip/TCA95xx_8bit.h | 4 +- test_apps/CMakeLists.txt | 3 +- test_apps/main/CMakeLists.txt | 2 +- test_apps/main/idf_component.yml | 8 +- ...t_ESP_IOExpander.cpp => test_app_main.cpp} | 0 test_apps/pytest_esp_io_expander.py | 11 -- test_apps/sdkconfig.defaults | 2 +- 26 files changed, 407 insertions(+), 80 deletions(-) create mode 100644 .codespellrc create mode 100644 .flake8 rename .github/scripts/{check_versions.sh => check_lib_versions.sh} (89%) mode change 100644 => 100755 rename .github/workflows/{check_versions.yml => check_lib_versions.yml} (84%) create mode 100644 .github/workflows/issue_comment.yml create mode 100644 .github/workflows/new_issues.yml create mode 100644 .github/workflows/new_prs.yml create mode 100644 .github/workflows/upload_component.yml create mode 100644 idf_component.yml rename test_apps/main/{test_ESP_IOExpander.cpp => test_app_main.cpp} (100%) delete mode 100644 test_apps/pytest_esp_io_expander.py diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 0000000..0a3c74d --- /dev/null +++ b/.codespellrc @@ -0,0 +1,2 @@ +[codespell] +skip = ./src/touch/base/esp_lcd_touch_xpt2046.c diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..648e169 --- /dev/null +++ b/.flake8 @@ -0,0 +1,141 @@ +[flake8] + +select = + # Full lists are given in order to suppress all errors from other plugins + # Full list of pyflakes error codes: + F401, # module imported but unused + F402, # import module from line N shadowed by loop variable + F403, # 'from module import *' used; unable to detect undefined names + F404, # future import(s) name after other statements + F405, # name may be undefined, or defined from star imports: module + F406, # 'from module import *' only allowed at module level + F407, # an undefined __future__ feature name was imported + F601, # dictionary key name repeated with different values + F602, # dictionary key variable name repeated with different values + F621, # too many expressions in an assignment with star-unpacking + F622, # two or more starred expressions in an assignment (a, *b, *c = d) + F631, # assertion test is a tuple, which are always True + F701, # a break statement outside of a while or for loop + F702, # a continue statement outside of a while or for loop + F703, # a continue statement in a finally block in a loop + F704, # a yield or yield from statement outside of a function + F705, # a return statement with arguments inside a generator + F706, # a return statement outside of a function/method + F707, # an except: block as not the last exception handler + F721, F722, # doctest syntax error syntax error in forward type annotation + F811, # redefinition of unused name from line N + F812, # list comprehension redefines name from line N + F821, # undefined name name + F822, # undefined name name in __all__ + F823, # local variable name referenced before assignment + F831, # duplicate argument name in function definition + F841, # local variable name is assigned to but never used + F901, # raise NotImplemented should be raise NotImplementedError + + # Full list of pycodestyle violations: + E101, # indentation contains mixed spaces and tabs + E111, # indentation is not a multiple of four + E112, # expected an indented block + E113, # unexpected indentation + E114, # indentation is not a multiple of four (comment) + E115, # expected an indented block (comment) + E116, # unexpected indentation (comment) + E121, # continuation line under-indented for hanging indent + E122, # continuation line missing indentation or outdented + E123, # closing bracket does not match indentation of opening bracket's line + E124, # closing bracket does not match visual indentation + E125, # continuation line with same indent as next logical line + E126, # continuation line over-indented for hanging indent + E127, # continuation line over-indented for visual indent + E128, # continuation line under-indented for visual indent + E129, # visually indented line with same indent as next logical line + E131, # continuation line unaligned for hanging indent + E133, # closing bracket is missing indentation + E201, # whitespace after '(' + E202, # whitespace before ')' + E203, # whitespace before ':' + E211, # whitespace before '(' + E221, # multiple spaces before operator + E222, # multiple spaces after operator + E223, # tab before operator + E224, # tab after operator + E225, # missing whitespace around operator + E226, # missing whitespace around arithmetic operator + E227, # missing whitespace around bitwise or shift operator + E228, # missing whitespace around modulo operator + E231, # missing whitespace after ',', ';', or ':' + E241, # multiple spaces after ',' + E242, # tab after ',' + E251, # unexpected spaces around keyword / parameter equals + E261, # at least two spaces before inline comment + E262, # inline comment should start with '# ' + E265, # block comment should start with '# ' + E266, # too many leading '#' for block comment + E271, # multiple spaces after keyword + E272, # multiple spaces before keyword + E273, # tab after keyword + E274, # tab before keyword + E275, # missing whitespace after keyword + E301, # expected 1 blank line, found 0 + E302, # expected 2 blank lines, found 0 + E303, # too many blank lines + E304, # blank lines found after function decorator + E305, # expected 2 blank lines after end of function or class + E306, # expected 1 blank line before a nested definition + E401, # multiple imports on one line + E402, # module level import not at top of file + E501, # line too long (82 > 79 characters) + E502, # the backslash is redundant between brackets + E701, # multiple statements on one line (colon) + E702, # multiple statements on one line (semicolon) + E703, # statement ends with a semicolon + E704, # multiple statements on one line (def) + E711, # comparison to None should be 'if cond is None:' + E712, # comparison to True should be 'if cond is True:' or 'if cond:' + E713, # test for membership should be 'not in' + E714, # test for object identity should be 'is not' + E721, # do not compare types, use 'isinstance()' + E722, # do not use bare except, specify exception instead + E731, # do not assign a lambda expression, use a def + E741, # do not use variables named 'l', 'O', or 'I' + E742, # do not define classes named 'l', 'O', or 'I' + E743, # do not define functions named 'l', 'O', or 'I' + E901, # SyntaxError or IndentationError + E902, # IOError + W191, # indentation contains tabs + W291, # trailing whitespace + W292, # no newline at end of file + W293, # blank line contains whitespace + W391, # blank line at end of file + W503, # line break before binary operator + W504, # line break after binary operator + W505, # doc line too long (82 > 79 characters) + W601, # .has_key() is deprecated, use 'in' + W602, # deprecated form of raising exception + W603, # '<>' is deprecated, use '!=' + W604, # backticks are deprecated, use 'repr()' + W605, # invalid escape sequence 'x' + W606, # 'async' and 'await' are reserved keywords starting with Python 3.7 + + # Full list of flake8 violations + E999, # failed to compile a file into an Abstract Syntax Tree for the plugins that require it + + # Full list of mccabe violations + C901 # complexity value provided by the user + +ignore = + E221, # multiple spaces before operator + E231, # missing whitespace after ',', ';', or ':' + E241, # multiple spaces after ',' + W503, # line break before binary operator + W504 # line break after binary operator + +max-line-length = 160 + +show_source = True + +statistics = True + +exclude = + .git, + __pycache__, diff --git a/.github/scripts/check_versions.sh b/.github/scripts/check_lib_versions.sh old mode 100644 new mode 100755 similarity index 89% rename from .github/scripts/check_versions.sh rename to .github/scripts/check_lib_versions.sh index d458178..cdfe6d8 --- a/.github/scripts/check_versions.sh +++ b/.github/scripts/check_lib_versions.sh @@ -13,26 +13,6 @@ check_version_format() { return 0 } -if [ $# -lt 1 ]; then - latest_version="0.0.0" - echo "Don't get the lastest version, use \"0.0.0\" as default" -else - # Get the first input parameter as the version to be compared - latest_version="$1" - # Check the version format - check_version_format "${latest_version}" - result=$? - if [ ${result} -ne 0 ]; then - echo "The latest release version (${latest_version}) format is incorrect." - exit 1 - fi -fi - -# Specify the directory path -target_directory="./" - -echo "Checking directory: ${target_directory}" - # Function: Check if a file exists # Input parameters: $1 The file to check # Return value: 0 if the file exists, 1 if the file does not exist @@ -68,6 +48,32 @@ compare_versions() { return 0 } +# Get the latest release version +latest_version="v0.0.0" +for i in "$@"; do + case $i in + --latest_version=*) + latest_version="${i#*=}" + shift + ;; + *) + ;; + esac +done +# Check the version format +check_version_format "${latest_version}" +result=$? +if [ ${result} -ne 0 ]; then + echo "The latest release version (${latest_version}) format is incorrect." + exit 1 +fi +echo "Get the latest release version: ${latest_version}" + +# Specify the directory path +target_directory="./" + +echo "Checking directory: ${target_directory}" + echo "Checking file: library.properties" # Check if "library.properties" file exists check_file_exists "${target_directory}/library.properties" diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 9f1f2e9..61831e0 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -9,7 +9,7 @@ jobs: build: strategy: matrix: - idf_ver: ["v4.4.5", "v5.0", "v5.1"] + idf_ver: ["release-v5.1", "release-v5.2", "release-v5.3"] idf_target: ["esp32", "esp32s2", "esp32c3", "esp32s3"] runs-on: ubuntu-20.04 container: espressif/idf:${{ matrix.idf_ver }} @@ -25,4 +25,4 @@ jobs: export PEDANTIC_FLAGS="-DIDF_CI_BUILD -Werror -Werror=deprecated-declarations -Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function" export EXTRA_CFLAGS="${PEDANTIC_FLAGS} -Wstrict-prototypes" export EXTRA_CXXFLAGS="${PEDANTIC_FLAGS}" - idf.py build \ No newline at end of file + idf.py build diff --git a/.github/workflows/check_versions.yml b/.github/workflows/check_lib_versions.yml similarity index 84% rename from .github/workflows/check_versions.yml rename to .github/workflows/check_lib_versions.yml index eb2d116..48a4899 100644 --- a/.github/workflows/check_versions.yml +++ b/.github/workflows/check_lib_versions.yml @@ -1,11 +1,11 @@ -name: Check Versions +name: Check Library Versions on: pull_request: types: [opened, reopened, synchronize] jobs: - check_versions: + check_lib_versions: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -26,5 +26,4 @@ jobs: echo "prerelease: ${{ steps.last_release.outputs.prerelease }}" echo "url: ${{ steps.last_release.outputs.url }}" - name: Check & Compare versions - run: bash ./.github/scripts/check_versions.sh ${{ steps.last_release.outputs.tag_name }} - + run: bash ./.github/scripts/check_lib_versions.sh --latest_version=${{ steps.last_release.outputs.tag_name }} diff --git a/.github/workflows/issue_comment.yml b/.github/workflows/issue_comment.yml new file mode 100644 index 0000000..3b6fcc1 --- /dev/null +++ b/.github/workflows/issue_comment.yml @@ -0,0 +1,24 @@ +name: Sync issue comments to JIRA + +# This workflow will be triggered when new issue comment is created (including PR comments) +on: issue_comment + +# Limit to single concurrent run for workflows which can create Jira issues. +# Same concurrency group is used in new_issues.yml +concurrency: jira_issues + +jobs: + sync_issue_comments_to_jira: + name: Sync Issue Comments to Jira + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Sync issue comments to JIRA + uses: espressif/github-actions/sync_issues_to_jira@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JIRA_PASS: ${{ secrets.JIRA_PASS }} + JIRA_PROJECT: ${{ secrets.JIRA_PROJECT }} + JIRA_COMPONENT: ${{ secrets.JIRA_COMPONENT }} + JIRA_URL: ${{ secrets.JIRA_URL }} + JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/.github/workflows/new_issues.yml b/.github/workflows/new_issues.yml new file mode 100644 index 0000000..f0fa402 --- /dev/null +++ b/.github/workflows/new_issues.yml @@ -0,0 +1,24 @@ +name: Sync issues to Jira + +# This workflow will be triggered when a new issue is opened +on: issues + +# Limit to single concurrent run for workflows which can create Jira issues. +# Same concurrency group is used in issue_comment.yml +concurrency: jira_issues + +jobs: + sync_issues_to_jira: + name: Sync issues to Jira + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Sync GitHub issues to Jira project + uses: espressif/github-actions/sync_issues_to_jira@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JIRA_PASS: ${{ secrets.JIRA_PASS }} + JIRA_PROJECT: ${{ secrets.JIRA_PROJECT }} + JIRA_COMPONENT: ${{ secrets.JIRA_COMPONENT }} + JIRA_URL: ${{ secrets.JIRA_URL }} + JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/.github/workflows/new_prs.yml b/.github/workflows/new_prs.yml new file mode 100644 index 0000000..01d7fe2 --- /dev/null +++ b/.github/workflows/new_prs.yml @@ -0,0 +1,29 @@ +name: Sync remain PRs to Jira + +# This workflow will be triggered every hour, to sync remaining PRs (i.e. PRs with zero comment) to Jira project +# Note that, PRs can also get synced when new PR comment is created +on: + schedule: + - cron: "0 * * * *" + +# Limit to single concurrent run for workflows which can create Jira issues. +# Same concurrency group is used in issue_comment.yml +concurrency: jira_issues + +jobs: + sync_prs_to_jira: + name: Sync PRs to Jira + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Sync PRs to Jira project + uses: espressif/github-actions/sync_issues_to_jira@master + with: + cron_job: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JIRA_PASS: ${{ secrets.JIRA_PASS }} + JIRA_PROJECT: ${{ secrets.JIRA_PROJECT }} + JIRA_COMPONENT: ${{ secrets.JIRA_COMPONENT }} + JIRA_URL: ${{ secrets.JIRA_URL }} + JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 51f77cd..7111ea9 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -11,4 +11,4 @@ jobs: steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - - uses: pre-commit/action@v2.0.3 \ No newline at end of file + - uses: pre-commit/action@v2.0.3 diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml new file mode 100644 index 0000000..45e9b78 --- /dev/null +++ b/.github/workflows/upload_component.yml @@ -0,0 +1,20 @@ +name: Push components to Espressif Component Service + +on: + workflow_dispatch: + release: + types: [published] + +jobs: + upload_components: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + with: + submodules: 'recursive' + - name: Upload components to component service + uses: espressif/upload-components-ci-action@v1 + with: + name: "ESP32_IO_Expander" + namespace: "espressif" + api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8bb4083..9100936 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,25 +1,82 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks + +exclude: 'libraries/ui/' repos: -- repo: https://github.com/igrr/astyle_py.git - rev: master + - repo: https://github.com/igrr/astyle_py.git + rev: v1.0.5 hooks: - - id: astyle_py + - id: astyle_py args: ['--style=otbs', '--attach-namespaces', '--attach-classes', '--indent=spaces=4', '--convert-tabs', '--align-pointer=name', '--align-reference=name', '--keep-one-line-statements', '--pad-header', '--pad-oper'] -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + - repo: https://github.com/espressif/check-copyright/ + rev: v1.0.3 + hooks: + - id: check-copyright + args: ['--config', 'check_copyright_config.yaml'] + + - repo: https://github.com/PyCQA/flake8 + rev: 5.0.4 + hooks: + - id: flake8 + types: [python] + args: ['--config=.flake8', '--tee', '--benchmark'] + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 hooks: - - id: trailing-whitespace - types_or: [c, c++] - - id: end-of-file-fixer - types_or: [c, c++] - - id: check-merge-conflict - - id: mixed-line-ending - types_or: [c, c++] - args: ['--fix=lf'] - description: Forces to replace line ending by the UNIX 'lf' character + - id: trailing-whitespace + # note: whitespace exclusions use multiline regex, see https://pre-commit.com/#regular-expressions + # items are: + # 1 - some file extensions + # 2 - any file matching *test*/*expected* (for host tests, if possible use this naming pattern always) + # 3 - any file with known-warnings in the name + # 4 - any directory named 'testdata' + # 5 - protobuf auto-generated files + exclude: &whitespace_excludes | + (?x)^( + .+\.(md|rst|map|bin)| + .+test.*\/.*expected.*| + .+known-warnings.*| + .+\/testdata\/.+| + .*_pb2.py| + .*.pb-c.h| + .*.pb-c.c| + .*.yuv + )$ + - id: end-of-file-fixer + exclude: *whitespace_excludes + - id: check-executables-have-shebangs + - id: check-shebang-scripts-are-executable + - id: mixed-line-ending + args: ['-f=lf'] + - id: double-quote-string-fixer + - id: no-commit-to-branch + name: Do not use more than one slash in the branch name + args: ['--pattern', '^[^/]*/[^/]*/'] + - id: no-commit-to-branch + name: Do not use uppercase letters in the branch name + args: ['--pattern', '^[^A-Z]*[A-Z]'] -- repo: https://github.com/espressif/check-copyright/ - rev: v1.0.3 - hooks: - - id: check-copyright - args: ['--config', 'check_copyright_config.yaml'] \ No newline at end of file + - repo: https://github.com/espressif/conventional-precommit-linter + rev: v1.8.0 + hooks: + - id: conventional-precommit-linter + stages: [commit-msg] + args: + - --subject-min-length=15 + - --body-max-line-length=200 + + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + args: ['-w' , '--config', '.codespellrc'] + + - repo: local + hooks: + - id: check-library-versions + name: Check library versions + entry: ./.github/scripts/check_lib_versions.sh + language: system + files: '(idf_component.yml|library.properties)' diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c4e4d1..a7ec7f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # ChangeLog +## v0.1.0 - 2024-11-05 + +### Enhancements: + +* Upload to the ESP Registry and support to build on the esp-idf +* Update pre-commit configuration + ## v0.0.4 - 2024-10-18 ### Enhancements: diff --git a/README.md b/README.md index fe8ae4c..81ccad3 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,21 @@ [![Arduino Lint](https://github.com/esp-arduino-libs/ESP32_IO_Expander/actions/workflows/arduino_lint.yml/badge.svg)](https://github.com/esp-arduino-libs/ESP32_IO_Expander/actions/workflows/arduino_lint.yml) [![pre-commit](https://github.com/esp-arduino-libs/ESP32_IO_Expander/actions/workflows/pre-commit.yml/badge.svg)](https://github.com/esp-arduino-libs/ESP32_IO_Expander/actions/workflows/pre-commit.yml) [![Build Test Apps](https://github.com/esp-arduino-libs/ESP32_IO_Expander/actions/workflows/build_test.yml/badge.svg)](https://github.com/esp-arduino-libs/ESP32_IO_Expander/actions/workflows/build_test.yml) +**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) + # ESP32_IO_Expander -ESP32_IO_Expander is an Arduino library designed for driving [IO expander chips](#supported-drivers) using ESP32 SoCs. +ESP32_IO_Expander is a library designed for driving [IO expander chips](#supported-drivers) using ESP32 SoCs. -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) and can be easily downloaded and integrated into the 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. ## Features * 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. ## Supported Drivers @@ -24,9 +29,17 @@ ESP32_IO_Expander encapsulates various components from the [Espressif Components ## Dependencies Version +### Arduino + | **Name** | **Version** | | ----------------------------------------------------------- | ----------- | -| [arduino-esp32](https://github.com/espressif/arduino-esp32) | >= v2.0.9 | +| [arduino-esp32](https://github.com/espressif/arduino-esp32) | >= v3.0.0 | + +### ESP-IDF + +| **Name** | **Version** | +| ----------------------------------------------- | ----------- | +| [esp-idf](https://github.com/espressif/esp-idf) | >= v5.1 | ## How to Use @@ -35,15 +48,18 @@ For information on how to use the library in the Arduino IDE, please refer to th ### 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. ### Detailed Usage ```cpp #include -// Create an ESP_IOExpander object according to the chip type +// 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); +expander->init(); +expander->begin(); // Control a single pin (0-31) expander->pinMode(0, OUTPUT); diff --git a/check_copyright_config.yaml b/check_copyright_config.yaml index d0c1e09..78761a6 100644 --- a/check_copyright_config.yaml +++ b/check_copyright_config.yaml @@ -7,6 +7,7 @@ DEFAULT: # when setting this option in a section, you need to list all the allowed licenses allowed_licenses: - Apache-2.0 + - MIT license_for_new_files: Apache-2.0 # license to be used when inserting a new copyright notice new_notice_c: | # notice for new C, CPP, H, HPP and LD files /* @@ -28,14 +29,14 @@ DEFAULT: # You can create your own rules for files or group of files examples_and_unit_tests: include: - - 'test_apps/' + - 'test_apps/' + - 'examples/' allowed_licenses: - Apache-2.0 - Unlicense - CC0-1.0 license_for_new_files: CC0-1.0 -ignore: # You can also select ignoring files here - perform_check: no # Don't check files from that block - include: - - 'examples/' \ No newline at end of file +# ignore: # You can also select ignoring files here +# perform_check: no # Don't check files from that block +# include: diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 0000000..c503891 --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,7 @@ +version: "0.1.0" +description: ESP32_IO_Expander is a library designed for driving IO expander chips using ESP32 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" diff --git a/library.properties b/library.properties index cc76fb7..3ff246e 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ESP32_IO_Expander -version=0.0.4 +version=0.1.0 author=espressif maintainer=espressif sentence=ESP32_IO_Expander is a library designed for driving IO expander chips using ESP32 SoCs diff --git a/src/base/esp_io_expander.h b/src/base/esp_io_expander.h index bd6d71c..da6e121 100644 --- a/src/base/esp_io_expander.h +++ b/src/base/esp_io_expander.h @@ -69,7 +69,7 @@ typedef enum { */ typedef enum { IO_EXPANDER_INPUT, /*!< Input direction */ - IO_EXPANDER_OUTPUT, /*!< Output dircetion */ + IO_EXPANDER_OUTPUT, /*!< Output direction */ } esp_io_expander_dir_t; /** @@ -134,7 +134,7 @@ struct esp_io_expander_s { /** * @brief Write value to direction register (mandatory) * - * @note The value represents the diection of IO + * @note The value represents the direction of IO * @note If there are multiple input registers in the device, their values should be spliced together in order to form the `value`. * * @param handle: IO Expander handle @@ -215,7 +215,7 @@ esp_err_t esp_io_expander_set_dir(esp_io_expander_handle_t handle, uint32_t pin_ esp_err_t esp_io_expander_set_level(esp_io_expander_handle_t handle, uint32_t pin_num_mask, uint8_t level); /** - * @brief Get the intput level of a set of target IOs + * @brief Get the input level of a set of target IOs * * @note This function can be called whenever target IOs are in input mode or output mode * diff --git a/src/chip/HT8574.h b/src/chip/HT8574.h index 54f8840..00ef52e 100644 --- a/src/chip/HT8574.h +++ b/src/chip/HT8574.h @@ -76,7 +76,7 @@ class ESP_IOExpander_HT8574: public ESP_IOExpander { * | 0 | 1 | 1 | 1 | A2 | A1 | A0 | R/W | * └─────┘─────┘─────┘─────┘─────┘─────┘─────┘─────┘ * └────────┯────────┘ └─────┯──────┘ - * (Fixed) (Hareware Selectable) + * (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). diff --git a/src/chip/TCA95xx_16bit.h b/src/chip/TCA95xx_16bit.h index 795f2b4..e3e110d 100644 --- a/src/chip/TCA95xx_16bit.h +++ b/src/chip/TCA95xx_16bit.h @@ -76,7 +76,7 @@ class ESP_IOExpander_TCA95xx_16bit: public ESP_IOExpander { * | 1 | 1 | 1 | 0 | 1 | A1 | A0 | R/W | * └─────┘─────┘─────┘─────┘─────┘─────┘─────┘─────┘ * └────────┯──────────────┘ └──┯──┘ - * (Fixed) (Hareware Selectable) + * (Fixed) (Hardware Selectable) * * The 8-bit address format for the TCA9555 is as follows: * @@ -86,7 +86,7 @@ class ESP_IOExpander_TCA95xx_16bit: public ESP_IOExpander { * | 0 | 1 | 0 | 0 | A2 | A1 | A0 | R/W | * └─────┘─────┘─────┘─────┘─────┘─────┘─────┘─────┘ * └────────┯────────┘ └─────┯──────┘ - * (Fixed) (Hareware Selectable) + * (Fixed) (Hardware Selectable) * * And the 7-bit slave address is the most important data for users. * For example, if a TCA9555 chip's A0,A1,A2 are connected to GND, it's 7-bit slave address is 0b0100000. diff --git a/src/chip/TCA95xx_8bit.h b/src/chip/TCA95xx_8bit.h index db061b5..bab368a 100644 --- a/src/chip/TCA95xx_8bit.h +++ b/src/chip/TCA95xx_8bit.h @@ -76,7 +76,7 @@ class ESP_IOExpander_TCA95xx_8bit: public ESP_IOExpander { * | 0 | 1 | 0 | 0 | A2 | A1 | A0 | R/W | * └─────┘─────┘─────┘─────┘─────┘─────┘─────┘─────┘ * └────────┯────────┘ └─────┯──────┘ - * (Fixed) (Hareware Selectable) + * (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 0100000b(0x20). @@ -103,7 +103,7 @@ class ESP_IOExpander_TCA95xx_8bit: public ESP_IOExpander { * | 0 | 1 | 1 | 1 | A2 | A1 | A0 | R/W | * └─────┘─────┘─────┘─────┘─────┘─────┘─────┘─────┘ * └────────┯────────┘ └─────┯──────┘ - * (Fixed) (Hareware Selectable) + * (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). diff --git a/test_apps/CMakeLists.txt b/test_apps/CMakeLists.txt index 19ad7fd..6850cc5 100644 --- a/test_apps/CMakeLists.txt +++ b/test_apps/CMakeLists.txt @@ -1,6 +1,5 @@ # 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(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components" "../") include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(panel_io_3wire_spi_test) \ No newline at end of file +project(io_expander_test) diff --git a/test_apps/main/CMakeLists.txt b/test_apps/main/CMakeLists.txt index e06a302..d833001 100644 --- a/test_apps/main/CMakeLists.txt +++ b/test_apps/main/CMakeLists.txt @@ -1,3 +1,3 @@ -idf_component_register(SRCS "test_ESP_IOExpander.cpp") +idf_component_register(SRCS "test_app_main.cpp") target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-missing-field-initializers) diff --git a/test_apps/main/idf_component.yml b/test_apps/main/idf_component.yml index 29cf227..c6e36a8 100644 --- a/test_apps/main/idf_component.yml +++ b/test_apps/main/idf_component.yml @@ -1,3 +1,9 @@ ## IDF Component Manager Manifest File dependencies: - idf: ">=4.4" \ No newline at end of file + 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_ESP_IOExpander.cpp b/test_apps/main/test_app_main.cpp similarity index 100% rename from test_apps/main/test_ESP_IOExpander.cpp rename to test_apps/main/test_app_main.cpp diff --git a/test_apps/pytest_esp_io_expander.py b/test_apps/pytest_esp_io_expander.py deleted file mode 100644 index 7c39966..0000000 --- a/test_apps/pytest_esp_io_expander.py +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD -# SPDX-License-Identifier: CC0-1.0 - -import pytest -from pytest_embedded import Dut - -@pytest.mark.target('esp32s3') -@pytest.mark.env('esp32_s3_lcd_ev_board') -def test_usb_stream(dut: Dut)-> None: - dut.run_all_single_board_cases() - diff --git a/test_apps/sdkconfig.defaults b/test_apps/sdkconfig.defaults index 1e8cdb8..f61533c 100644 --- a/test_apps/sdkconfig.defaults +++ b/test_apps/sdkconfig.defaults @@ -7,4 +7,4 @@ 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 \ No newline at end of file +CONFIG_ESP_TASK_WDT=n From a227c6036c0f1486853e0aaaf597cd07e1d1021e Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Thu, 5 Dec 2024 21:14:26 +0800 Subject: [PATCH 03/10] feat(repo): refactor with esp-lib-utils --- .codespellrc | 1 - .github/workflows/build_test.yml | 2 +- README.md | 124 +++++-- examples/TestFunctions/TestFunctions.ino | 88 ----- .../TestCH422G.ino => ch422g/ch422g.ino} | 10 +- examples/general/general.ino | 83 +++++ idf_component.yml | 7 +- library.properties | 7 +- src/ESP_IOExpander.cpp | 118 ------ src/ESP_IOExpander.h | 196 ---------- src/ESP_IOExpander_Library.h | 15 +- src/chip/CH422G.h | 106 ------ src/chip/HT8574.h | 89 ----- src/chip/TCA95xx_8bit.cpp | 175 --------- src/chip/esp_expander_base.cpp | 188 ++++++++++ src/chip/esp_expander_base.hpp | 351 ++++++++++++++++++ src/chip/esp_expander_ch422g.cpp | 100 +++++ src/chip/esp_expander_ch422g.hpp | 121 ++++++ src/chip/esp_expander_ht8574.cpp | 40 ++ src/chip/esp_expander_ht8574.hpp | 81 ++++ src/chip/esp_expander_tca95xx_16bit.cpp | 40 ++ src/chip/esp_expander_tca95xx_16bit.hpp | 81 ++++ src/chip/esp_expander_tca95xx_8bit.cpp | 40 ++ src/chip/esp_expander_tca95xx_8bit.hpp | 81 ++++ src/esp_expander_utils.h | 10 + src/esp_io_expander.hpp | 21 ++ src/{base => port}/esp_io_expander.c | 12 +- src/{base => port}/esp_io_expander.h | 7 +- .../esp_io_expander_ch422g.c} | 118 +++--- src/port/esp_io_expander_ch422g.h | 54 +++ .../esp_io_expander_ht8574.c} | 30 +- src/port/esp_io_expander_ht8574.h | 64 ++++ src/port/esp_io_expander_tca9554.c | 161 ++++++++ .../esp_io_expander_tca9554.h} | 81 ++-- .../esp_io_expander_tca95xx_16bit.c} | 44 +-- .../esp_io_expander_tca95xx_16bit.h} | 76 ++-- src/private/CheckResult.cpp | 26 -- src/private/CheckResult.h | 68 ---- test_apps/CMakeLists.txt | 1 + test_apps/main/CMakeLists.txt | 7 +- test_apps/main/idf_component.yml | 2 - test_apps/main/test_app_main.cpp | 99 +---- test_apps/main/test_chip_general.cpp | 172 +++++++++ test_apps/sdkconfig.defaults | 11 +- 44 files changed, 1969 insertions(+), 1239 deletions(-) delete mode 100644 examples/TestFunctions/TestFunctions.ino rename examples/{TestCH422G/TestCH422G.ino => ch422g/ch422g.ino} (89%) create mode 100644 examples/general/general.ino delete mode 100644 src/ESP_IOExpander.cpp delete mode 100644 src/ESP_IOExpander.h delete mode 100644 src/chip/CH422G.h delete mode 100644 src/chip/HT8574.h delete mode 100644 src/chip/TCA95xx_8bit.cpp create mode 100644 src/chip/esp_expander_base.cpp create mode 100644 src/chip/esp_expander_base.hpp create mode 100644 src/chip/esp_expander_ch422g.cpp create mode 100644 src/chip/esp_expander_ch422g.hpp create mode 100644 src/chip/esp_expander_ht8574.cpp create mode 100644 src/chip/esp_expander_ht8574.hpp create mode 100644 src/chip/esp_expander_tca95xx_16bit.cpp create mode 100644 src/chip/esp_expander_tca95xx_16bit.hpp create mode 100644 src/chip/esp_expander_tca95xx_8bit.cpp create mode 100644 src/chip/esp_expander_tca95xx_8bit.hpp create mode 100644 src/esp_expander_utils.h create mode 100644 src/esp_io_expander.hpp rename src/{base => port}/esp_io_expander.c (97%) rename src/{base => port}/esp_io_expander.h (98%) rename src/{chip/CH422G.cpp => port/esp_io_expander_ch422g.c} (83%) create mode 100644 src/port/esp_io_expander_ch422g.h rename src/{chip/HT8574.cpp => port/esp_io_expander_ht8574.c} (87%) create mode 100644 src/port/esp_io_expander_ht8574.h create mode 100644 src/port/esp_io_expander_tca9554.c rename src/{chip/TCA95xx_8bit.h => port/esp_io_expander_tca9554.h} (60%) rename src/{chip/TCA95xx_16bit.cpp => port/esp_io_expander_tca95xx_16bit.c} (81%) rename src/{chip/TCA95xx_16bit.h => port/esp_io_expander_tca95xx_16bit.h} (56%) delete mode 100644 src/private/CheckResult.cpp delete mode 100644 src/private/CheckResult.h create mode 100644 test_apps/main/test_chip_general.cpp 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/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..d50f860 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/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 From 7ce4518e496f5ce3fa5fb134317c35fe994bdbed Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Fri, 6 Dec 2024 21:02:58 +0800 Subject: [PATCH 04/10] feat(repo): support micropython --- CHANGELOG.md | 7 +++++++ library.properties | 2 +- micropython.cmake | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 micropython.cmake 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/library.properties b/library.properties index d50f860..b15e1ee 100644 --- a/library.properties +++ b/library.properties @@ -8,4 +8,4 @@ category=Other architectures=esp32 url=https://github.com/esp-arduino-libs/ESP32_IO_Expander includes=esp_io_expander.hpp -depends=esp-lib-utils (>=0.1.0 && < 0.2.0) +depends=esp-lib-utils (>=0.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) From 7433f984c33f1370c36bd4ed12553d83b5025026 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Wed, 11 Dec 2024 18:48:49 +0800 Subject: [PATCH 05/10] fix(repo): compile *.cpp files on MicroPython --- CHANGELOG.md | 6 ++++++ idf_component.yml | 2 +- library.properties | 2 +- micropython.cmake | 5 +++-- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f893249..cef8c7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # ChangeLog +## v1.0.1 - 2024-12-11 + +### Bug Fixes: + +* fix(repo): compile *.cpp files on MicroPython + ## v1.0.0 - 2024-12-06 ### Enhancements: diff --git a/idf_component.yml b/idf_component.yml index 5d75262..f4b137c 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.0.0" +version: "1.0.1" description: ESP32_IO_Expander is a library designed for driving IO expander chips using ESP SoCs url: https://github.com/esp-arduino-libs/ESP32_IO_Expander repository: https://github.com/esp-arduino-libs/ESP32_IO_Expander.git diff --git a/library.properties b/library.properties index b15e1ee..df3c950 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ESP32_IO_Expander -version=1.0.0 +version=1.0.1 author=espressif maintainer=espressif sentence=ESP32_IO_Expander is a library designed for driving IO expander chips using ESP SoCs diff --git a/micropython.cmake b/micropython.cmake index 207fc55..ed63811 100644 --- a/micropython.cmake +++ b/micropython.cmake @@ -4,10 +4,11 @@ add_library(usermod_esp_io_expander INTERFACE) # Set the source directorya and find all source files. set(SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/src) -file(GLOB_RECURSE SRCS ${SRC_DIR}/*.c) +file(GLOB_RECURSE SRCS_C ${SRC_DIR}/*.c) +file(GLOB_RECURSE SRCS_CXX ${SRC_DIR}/*.cpp) # Add our source files to the library. -target_sources(usermod_esp_io_expander INTERFACE ${SRCS}) +target_sources(usermod_esp_io_expander INTERFACE ${SRCS_C} ${SRCS_CXX}) # Add the current directory as an include directory. target_include_directories(usermod_esp_io_expander INTERFACE ${SRC_DIR}) From cbd89f6e917f3009ab464976ad169ad34ef40341 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Fri, 3 Jan 2025 15:40:00 +0800 Subject: [PATCH 06/10] feat(base): update base class --- CHANGELOG.md | 6 +- CMakeLists.txt | 2 +- examples/ch422g/ch422g.ino | 1 - idf_component.yml | 2 +- micropython.cmake | 6 + src/ESP_IOExpander_Library.h | 1 - src/chip/esp_expander_base.cpp | 175 +++++++++++++--- src/chip/esp_expander_base.hpp | 251 ++++++++++------------- src/chip/esp_expander_ch422g.cpp | 27 ++- src/chip/esp_expander_ch422g.hpp | 14 +- src/chip/esp_expander_ht8574.cpp | 19 +- src/chip/esp_expander_ht8574.hpp | 10 +- src/chip/esp_expander_tca95xx_16bit.cpp | 19 +- src/chip/esp_expander_tca95xx_16bit.hpp | 10 +- src/chip/esp_expander_tca95xx_8bit.cpp | 19 +- src/chip/esp_expander_tca95xx_8bit.hpp | 10 +- src/esp_expander_utils.h | 1 + src/port/esp_io_expander.c | 1 - src/port/esp_io_expander.h | 4 - src/port/esp_io_expander_ch422g.c | 1 - src/port/esp_io_expander_ch422g.h | 1 - src/port/esp_io_expander_ht8574.c | 1 - src/port/esp_io_expander_tca9554.c | 1 - src/port/esp_io_expander_tca95xx_16bit.c | 1 - test_apps/main/test_chip_general.cpp | 19 +- test_apps/sdkconfig.ci.debug_log | 2 + 26 files changed, 350 insertions(+), 254 deletions(-) create mode 100644 test_apps/sdkconfig.ci.debug_log diff --git a/CHANGELOG.md b/CHANGELOG.md index cef8c7e..eebfcfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # ChangeLog -## v1.0.1 - 2024-12-11 +## v1.0.1 - 2025-01-23 + +### Enhancements: + +* feat(base): update base class ### Bug Fixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index 6eefe2d..c1af501 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,4 +13,4 @@ idf_component_register( driver ) -target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-missing-field-initializers) +target_compile_options(${COMPONENT_LIB} PUBLIC -Wno-missing-field-initializers) diff --git a/examples/ch422g/ch422g.ino b/examples/ch422g/ch422g.ino index 51f067c..89709e1 100644 --- a/examples/ch422g/ch422g.ino +++ b/examples/ch422g/ch422g.ino @@ -34,7 +34,6 @@ Since the input/output mode of CH422G's IO0-7 must remain consistent, the driver will only set IO0-7 to input mode when it determines that all pins are configured as input. Using pinMode and multiPinMode will be invalid. You can only set the pin working mode through enableAllIO_Input, enableAllIO_Output, enableOC_PushPull and enableOC_OpenDrain - * */ #include diff --git a/idf_component.yml b/idf_component.yml index f4b137c..846d206 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -6,5 +6,5 @@ issues: https://github.com/esp-arduino-libs/ESP32_IO_Expander/issues dependencies: idf: ">=5.1" espressif/esp-lib-utils: - version: ">=0.1.0,<=0.2.0" + version: "0.1.*" public: true diff --git a/micropython.cmake b/micropython.cmake index ed63811..62d0d5a 100644 --- a/micropython.cmake +++ b/micropython.cmake @@ -13,5 +13,11 @@ target_sources(usermod_esp_io_expander INTERFACE ${SRCS_C} ${SRCS_CXX}) # Add the current directory as an include directory. target_include_directories(usermod_esp_io_expander INTERFACE ${SRC_DIR}) +# Add compile options. Since the target is not created by `idf_component_register()`, we need to add the `ESP_PLATFORM` define manually. +target_compile_options(usermod_esp_io_expander + INTERFACE + -Wno-missing-field-initializers -DESP_PLATFORM $<$:-std=gnu++2b> +) + # Link our INTERFACE library to the usermod target. target_link_libraries(usermod INTERFACE usermod_esp_io_expander) diff --git a/src/ESP_IOExpander_Library.h b/src/ESP_IOExpander_Library.h index 5280839..fb9fa32 100644 --- a/src/ESP_IOExpander_Library.h +++ b/src/ESP_IOExpander_Library.h @@ -6,7 +6,6 @@ /** * This file is just to keep the compatibility with the old version of the library. Please use the file `esp_io_expander.hpp` instead. - * */ #pragma once diff --git a/src/chip/esp_expander_base.cpp b/src/chip/esp_expander_base.cpp index 3ee7e66..d535efe 100644 --- a/src/chip/esp_expander_base.cpp +++ b/src/chip/esp_expander_base.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,20 +13,138 @@ namespace esp_expander { +void Base::Config::convertPartialToFull(void) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + if (isHostConfigValid() && std::holds_alternative(host.value())) { +#if ESP_UTILS_CONF_LOG_LEVEL == ESP_UTILS_LOG_LEVEL_DEBUG + printHostConfig(); +#endif // ESP_UTILS_LOG_LEVEL_DEBUG + auto &config = std::get(host.value()); + host = HostFullConfig{ + .mode = I2C_MODE_MASTER, + .sda_io_num = config.sda_io_num, + .scl_io_num = config.scl_io_num, + .sda_pullup_en = config.sda_pullup_en, + .scl_pullup_en = config.scl_pullup_en, + .master = { + .clk_speed = static_cast(config.clk_speed), + }, + .clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL, + }; + } + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); +} + +void Base::Config::printHostConfig(void) const +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + if (!isHostConfigValid()) { + ESP_UTILS_LOGI("\n\t{Host config}[skipped]"); + goto end; + } + + if (std::holds_alternative(host.value())) { + auto &config = std::get(host.value()); + ESP_UTILS_LOGI( + "\n\t{Host config}[full]\n" + "\t\t-> [host_id]: %d\n" + "\t\t-> [mode]: %d\n" + "\t\t-> [sda_io_num]: %d\n" + "\t\t-> [scl_io_num]: %d\n" + "\t\t-> [sda_pullup_en]: %d\n" + "\t\t-> [scl_pullup_en]: %d\n" + "\t\t-> [master.clk_speed]: %d\n" + "\t\t-> [clk_flags]: %d" + , host_id + , config.mode + , config.sda_io_num + , config.scl_io_num + , config.sda_pullup_en + , config.scl_pullup_en + , config.master.clk_speed + , config.clk_flags + ); + } else { + auto &config = std::get(host.value()); + ESP_UTILS_LOGI( + "\n\t{Host config}[partial]\n" + "\t\t-> [host_id]: %d\n" + "\t\t-> [sda_io_num]: %d\n" + "\t\t-> [scl_io_num]: %d\n" + "\t\t-> [sda_pullup_en]: %d\n" + "\t\t-> [scl_pullup_en]: %d\n" + "\t\t-> [clk_speed]: %d" + , host_id + , config.sda_io_num + , config.scl_io_num + , config.sda_pullup_en + , config.scl_pullup_en + , config.clk_speed + ); + } + +end: + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); +} + +void Base::Config::printDeviceConfig(void) const +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_LOGI( + "\n\t{Device config}[partial]\n" + "\t\t-> [host_id]: %d\n" + "\t\t-> [address]: 0x%02X" + , host_id + , device.address + ); + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); +} + +bool Base::configHostSkipInit(bool skip_init) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::INIT), false, "Should be called before `init()`"); + + _is_host_skip_init = skip_init; + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); + + return true; +} + bool Base::init(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(!checkIsInit(), false, "Already initialized"); + ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::INIT), false, "Already initialized"); + + // Convert the partial configuration to full configuration + _config.convertPartialToFull(); +#if ESP_UTILS_CONF_LOG_LEVEL == ESP_UTILS_LOG_LEVEL_DEBUG + _config.printHostConfig(); + _config.printDeviceConfig(); +#endif // ESP_UTILS_LOG_LEVEL_DEBUG - if (!checkIsSkipInitHost()) { - ESP_UTILS_CHECK_ERROR_RETURN(i2c_param_config(getHostID(), &_host_config), false, "I2C param config failed"); + // Initialize the I2C host if not skipped + if (!isHostSkipInit()) { + i2c_port_t host_id = static_cast(getConfig().host_id); ESP_UTILS_CHECK_ERROR_RETURN( - i2c_driver_install(getHostID(), _host_config.mode, 0, 0, 0), false, "I2C driver install failed" + i2c_param_config(host_id, getHostFullConfig()), false, "I2C param config failed" ); - ESP_UTILS_LOGI("Init I2C host(%d)", _host_id); + ESP_UTILS_CHECK_ERROR_RETURN( + i2c_driver_install(host_id, getHostFullConfig()->mode, 0, 0, 0), false, "I2C driver install failed" + ); + ESP_UTILS_LOGD("Init I2C host(%d)", static_cast(host_id)); } - _flags.is_init = true; + + setState(State::INIT); ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); @@ -37,7 +155,7 @@ bool Base::reset(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_reset(device_handle), false, "Reset failed"); @@ -50,20 +168,20 @@ bool Base::del(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - if (checkIsBegun()) { + if (device_handle != nullptr) { ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_del(device_handle), false, "Delete failed"); device_handle = nullptr; - ESP_UTILS_LOGD("Delete IO expander(@%p)", device_handle); + ESP_UTILS_LOGD("Delete @%p", device_handle); } - if (checkIsInit()) { - if (!checkIsSkipInitHost()) { - ESP_UTILS_CHECK_ERROR_RETURN(i2c_driver_delete(getHostID()), false, "I2C driver delete failed"); - ESP_UTILS_LOGI("Delete I2C host(%d)", _host_id); - } - _flags.is_init = false; + if (isOverState(State::INIT) && !isHostSkipInit()) { + i2c_port_t host_id = static_cast(getConfig().host_id); + ESP_UTILS_CHECK_ERROR_RETURN(i2c_driver_delete(host_id), false, "I2C driver delete failed"); + ESP_UTILS_LOGD("Delete I2C host(%d)", static_cast(host_id)); } + setState(State::DEINIT); + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); return true; @@ -73,7 +191,7 @@ bool Base::pinMode(uint8_t pin, uint8_t mode) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_LOGD("Param: pin(%d), mode(%d)", pin, mode); ESP_UTILS_CHECK_FALSE_RETURN(IS_VALID_PIN(pin), false, "Invalid pin"); @@ -91,7 +209,7 @@ bool Base::digitalWrite(uint8_t pin, uint8_t value) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_LOGD("Param: pin(%d), value(%d)", pin, value); ESP_UTILS_CHECK_FALSE_RETURN(IS_VALID_PIN(pin), false, "Invalid pin"); @@ -109,7 +227,7 @@ int Base::digitalRead(uint8_t pin) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_LOGD("Param: pin(%d)", pin); ESP_UTILS_CHECK_FALSE_RETURN(IS_VALID_PIN(pin), -1, "Invalid pin"); @@ -128,7 +246,7 @@ bool Base::multiPinMode(uint32_t pin_mask, uint8_t mode) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_LOGD("Param: pin_mask(%0x), mode(%d)", pin_mask, mode); ESP_UTILS_CHECK_FALSE_RETURN((mode == INPUT) || (mode == OUTPUT), false, "Invalid mode"); @@ -145,7 +263,7 @@ bool Base::multiDigitalWrite(uint32_t pin_mask, uint8_t value) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_LOGD("Param: pin_mask(%0x), value(%d)", pin_mask, value); @@ -160,7 +278,7 @@ int64_t Base::multiDigitalRead(uint32_t pin_mask) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_LOGD("Param: pin_mask(%0x)", pin_mask); @@ -172,11 +290,11 @@ int64_t Base::multiDigitalRead(uint32_t pin_mask) return level; } -bool Base::printStatus(void) +bool Base::printStatus(void) const { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_print_state(device_handle), false, "Print state failed"); @@ -185,4 +303,13 @@ bool Base::printStatus(void) return true; } +Base::HostFullConfig *Base::getHostFullConfig() +{ + if (std::holds_alternative(_config.host.value())) { + _config.convertPartialToFull(); + } + + return &std::get(_config.host.value()); +} + } // namespace esp_expander diff --git a/src/chip/esp_expander_base.hpp b/src/chip/esp_expander_base.hpp index 402ad7f..957a620 100644 --- a/src/chip/esp_expander_base.hpp +++ b/src/chip/esp_expander_base.hpp @@ -1,12 +1,13 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include +#include +#include #include "driver/i2c.h" #include "port/esp_io_expander.h" @@ -29,92 +30,81 @@ namespace esp_expander { /** * @brief The IO expander device class * - * @note This is a base class for all chips. Due to it is a virtual class, users cannot use it directly - * + * @note This class is a base class for all types of IO expander chips. Due to it is a virtual class, users cannot use + * it directly */ class Base { public: - /* Default I2C host ID */ - constexpr static int HOST_ID_DEFAULT = static_cast(I2C_NUM_0); + /** + * Here are some default values for I2C bus + */ + constexpr static int I2C_HOST_ID_DEFAULT = static_cast(I2C_NUM_0); + constexpr static int I2C_CLK_SPEED_DEFAULT = 400 * 1000; + + using DeviceHandle = esp_io_expander_handle_t; + + struct HostPartialConfig { + int sda_io_num = -1; + int scl_io_num = -1; + bool sda_pullup_en = GPIO_PULLUP_ENABLE; + bool scl_pullup_en = GPIO_PULLUP_ENABLE; + int clk_speed = I2C_CLK_SPEED_DEFAULT; + }; + using HostFullConfig = i2c_config_t; + using HostConfig = std::variant; + + struct DeviceConfig { + uint8_t address = 0; + }; /** * @brief Configuration for Base object - * */ struct Config { - i2c_port_t getHostID(void) const - { - return static_cast(host_id); - } - - i2c_config_t getHostConfig(void) const - { - return { - .mode = I2C_MODE_MASTER, - .sda_io_num = host_sda_io_num, - .scl_io_num = host_scl_io_num, - .sda_pullup_en = host_sda_pullup_en, - .scl_pullup_en = host_scl_pullup_en, - .master = { - .clk_speed = host_clk_speed, - }, - .clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL, - }; - } + void convertPartialToFull(void); + void printHostConfig(void) const; + void printDeviceConfig(void) const; - uint8_t getDeviceAddress(void) const + bool isHostConfigValid(void) const { - return device_address; + return host.has_value(); } - static Config create(int scl_io, int sda_io, uint8_t address) - { - return Config{ - .host_sda_io_num = sda_io, - .host_scl_io_num = scl_io, - .device_address = address, - .skip_init_host = false, - }; - } - - static Config create(int host_id, uint8_t address) - { - return Config{ - .host_id = host_id, - .device_address = address, - .skip_init_host = true, - }; - } + int host_id = I2C_HOST_ID_DEFAULT; /*!< I2C host ID */ + std::optional host; /*!< I2C host configuration */ + DeviceConfig device = {}; /*!< I2C device configuration */ + }; - // Host - int host_id = HOST_ID_DEFAULT; /*!< I2C host ID */ - int host_sda_io_num = -1; /*!< I2C SDA pin number */ - int host_scl_io_num = -1; /*!< I2C SCL pin number */ - bool host_sda_pullup_en = GPIO_PULLUP_ENABLE; /*!< I2C SDA pullup enable */ - bool host_scl_pullup_en = GPIO_PULLUP_ENABLE; /*!< I2C SCL pullup enable */ - uint32_t host_clk_speed = 400000; /*!< I2C clock speed */ - // Device - uint8_t device_address = 0; /*!< I2C device 7-bit address */ - // Extra - bool skip_init_host = false; /*!< Skip I2C initialization when call `init()` */ + /** + * @brief The driver state enumeration + */ + enum class State : uint8_t { + DEINIT = 0, + INIT, + BEGIN, }; +// *INDENT-OFF* /** * @brief Construct a base device. With this function, call `init()` will initialize I2C by using the host * configuration. * * @param[in] scl_io I2C SCL pin number * @param[in] sda_io I2C SDA pin number - * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - * + * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. */ - Base(int scl_io, int sda_io, uint8_t address) + Base(int scl_io, int sda_io, uint8_t address): + _config{ + .host_id = I2C_HOST_ID_DEFAULT, + .host = HostPartialConfig{ + .sda_io_num = sda_io, + .scl_io_num = scl_io, + }, + .device = DeviceConfig{ + .address = address + } + } { - auto config = Config::create(scl_io, sda_io, address); - _host_id = config.getHostID(); - _host_config = config.getHostConfig(); - _device_address = config.getDeviceAddress(); - _flags.skip_init_host = config.skip_init_host; } /** @@ -122,49 +112,43 @@ class Base { * initialize it manually. * * @param[in] host_id I2C host ID. - * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - * + * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. */ - Base(int host_id, uint8_t address) + Base(int host_id, uint8_t address): + _config{ + .host_id = host_id, + .device = DeviceConfig{ + .address = address + } + } { - auto config = Config::create(host_id, address); - _host_id = config.getHostID(); - _host_config = config.getHostConfig(); - _device_address = config.getDeviceAddress(); - _flags.skip_init_host = config.skip_init_host; } +// *INDENT-ON* /** * @brief Construct a base device. * * @param[in] config Configuration for the object - * */ - Base(const Config &config) - { - _host_id = config.getHostID(); - _host_config = config.getHostConfig(); - _device_address = config.getDeviceAddress(); - _flags.skip_init_host = config.skip_init_host; - } + Base(const Config &config): _config(config) {} /** - * @deprecated Deprecated and will be removed in the next major version. Please use other constructors instead. + * @brief Virtual desutruct object. * + * @note Here make it virtual so that we can delete the derived object by using the base pointer. */ - Base(i2c_port_t id, uint8_t address, int scl_io, int sda_io): - Base(scl_io, sda_io, address) - { - _host_id = id; - } + virtual ~Base() = default; /** - * @brief Virtual desutruct object. + * @brief Configure whether to skip I2C initialization * - * @note Here make it virtual so that we can delete the derived object by using the base pointer. + * @note This function should be called before `init()`. + * + * @param[in] skip_init Whether to skip I2C initialization * + * @return true if success, otherwise false */ - virtual ~Base() = default; + bool configHostSkipInit(bool skip_init); /** * @brief Initialize object @@ -172,7 +156,6 @@ class Base { * @note This function will initialize I2C if needed. * * @return true if success, otherwise false - * */ bool init(void); @@ -180,7 +163,6 @@ class Base { * @brief Begin object * * @note This function typically calls `esp_io_expander_new_i2c_*()` to create the IO expander handle. - * */ virtual bool begin(void) = 0; @@ -188,13 +170,11 @@ class Base { * @brief Reset object * * @return true if success, otherwise false - * */ bool reset(void); /** * @brief Delete object - * */ bool del(void); @@ -207,7 +187,6 @@ class Base { * @param[in] mode Pin mode (INPUT / OUTPUT) * * @return true if success, otherwise false - * */ bool pinMode(uint8_t pin, uint8_t mode); @@ -220,7 +199,6 @@ class Base { * @param[in] value Pin level (HIGH / LOW) * * @return true if success, otherwise false - * */ bool digitalWrite(uint8_t pin, uint8_t value); @@ -232,7 +210,6 @@ class Base { * @param[in] pin Pin number (0-31) * * @return Pin level. HIGH or LOW if success, otherwise -1 - * */ int digitalRead(uint8_t pin); @@ -243,7 +220,6 @@ class Base { * @param mode Mode to set (INPUT / OUTPUT) * * @return true if success, otherwise false - * */ bool multiPinMode(uint32_t pin_mask, uint8_t mode); @@ -254,7 +230,6 @@ class Base { * @param value Value to write (HIGH / LOW) * * @return true if success, otherwise false - * */ bool multiDigitalWrite(uint32_t pin_mask, uint8_t value); @@ -264,7 +239,6 @@ class Base { * @param pin_mask Pin mask (Bitwise OR of `IO_EXPANDER_PIN_NUM_*`) * * @return Pin levels, every bit represents a pin (HIGH / LOW) - * */ int64_t multiDigitalRead(uint32_t pin_mask); @@ -272,80 +246,79 @@ class Base { * @brief Print IO expander status, include pin index, direction, input level and output level * * @return Pin levels, every bit represents a pin (HIGH / LOW) - * */ - bool printStatus(void); + bool printStatus(void) const; /** - * @brief Get low-level handle. Users can use this handle to call low-level functions (esp_io_expander_*()). + * @brief Check if the driver has reached or passed the specified state * - * @return Handle if success, otherwise nullptr + * @param[in] state The state to check against current state * + * @return true if current state >= given state, otherwise false */ - esp_io_expander_handle_t getDeviceHandle(void) + bool isOverState(State state) const { - return device_handle; + return (_state >= state); } /** - * @deprecated Deprecated and will be removed in the next major version. Please use `getDeviceHandle()` instead. + * @brief Get the IO expander configuration * + * @return IO expander Configuration */ - [[deprecated("Deprecated and will be removed in the next major version. Please use `getDeviceHandle()` instead.")]] - esp_io_expander_handle_t getHandle(void) + const Config &getConfig(void) const { - return getDeviceHandle(); + return _config; } -protected: - bool checkIsInit(void) - { - return _flags.is_init; - } - - bool checkIsBegun(void) + /** + * @brief Get low-level handle. Users can use this handle to call low-level functions (esp_io_expander_*()). + * + * @return Device handle if success, otherwise nullptr + */ + DeviceHandle getDeviceHandle(void) const { - return (device_handle != nullptr); + return device_handle; } - bool checkIsSkipInitHost(void) + // TODO: Remove in the next major version + Base(i2c_port_t id, uint8_t address, int scl_io, int sda_io): + Base(scl_io, sda_io, address) { - return _flags.skip_init_host; + _config.host_id = id; } - - i2c_port_t getHostID(void) + [[deprecated("Deprecated and will be removed in the next major version. Please use `getDeviceHandle()` instead.")]] + esp_io_expander_handle_t getHandle(void) const { - return static_cast(_host_id); + return getDeviceHandle(); } - const i2c_config_t &getHostConfig(void) +protected: + bool isHostSkipInit(void) const { - return _host_config; + return !_config.isHostConfigValid() || _is_host_skip_init; } - uint8_t getDeviceAddress(void) + void setState(State state) { - return _device_address; + _state = state; } - esp_io_expander_handle_t device_handle = nullptr; + DeviceHandle device_handle = nullptr; private: - struct { - uint8_t is_init: 1; - uint8_t skip_init_host: 1; - } _flags = {}; - // Host - int _host_id = HOST_ID_DEFAULT; - i2c_config_t _host_config = {}; - // Device - uint8_t _device_address = 0; + HostFullConfig *getHostFullConfig(); + + State _state = State::DEINIT; + bool _is_host_skip_init = false; + Config _config = {}; }; } // namespace esp_expander /** - * @deprecated Deprecated and will be removed in the next major version. Please use `esp_expander::Base` instead. + * @brief Alias for backward compatibility * + * @deprecated Use `esp_expander::Base` instead */ -typedef esp_expander::Base ESP_IOExpander __attribute__((deprecated("Deprecated and will be removed in the next major version. Please use `esp_expander::Base` instead."))); +using ESP_IOExpander [[deprecated("Use `esp_expander::Base` instead.")]] = esp_expander::Base; diff --git a/src/chip/esp_expander_ch422g.cpp b/src/chip/esp_expander_ch422g.cpp index f2cd30a..1a0dcbd 100644 --- a/src/chip/esp_expander_ch422g.cpp +++ b/src/chip/esp_expander_ch422g.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,14 +23,21 @@ bool CH422G::begin(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsInit(), false, "Not initialized"); - ESP_UTILS_CHECK_FALSE_RETURN(!checkIsBegun(), false, "Already begun"); + ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::BEGIN), false, "Already begun"); + + // Initialize the device if not initialized + if (!isOverState(State::INIT)) { + ESP_UTILS_CHECK_FALSE_RETURN(init(), false, "Init failed"); + } ESP_UTILS_CHECK_ERROR_RETURN( - esp_io_expander_new_i2c_ch422g(getHostID(), getDeviceAddress(), &device_handle), false, - "Create CH422G IO expander failed" + esp_io_expander_new_i2c_ch422g( + static_cast(getConfig().host_id), getConfig().device.address, &device_handle + ), false, "Create CH422G failed" ); - ESP_UTILS_LOGD("Create CH422G IO expander(@%p)", device_handle); + ESP_UTILS_LOGD("Create CH422G @%p", device_handle); + + setState(State::BEGIN); ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); @@ -41,7 +48,7 @@ bool CH422G::enableOC_OpenDrain(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_CHECK_ERROR_RETURN( esp_io_expander_ch422g_set_oc_open_drain(device_handle), false, "Set OC open-drain failed" @@ -56,7 +63,7 @@ bool CH422G::enableOC_PushPull(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_CHECK_ERROR_RETURN( esp_io_expander_ch422g_set_oc_push_pull(device_handle), false, "Set OC push-pull failed" @@ -71,7 +78,7 @@ bool CH422G::enableAllIO_Input(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_CHECK_ERROR_RETURN( esp_io_expander_ch422g_set_all_input(device_handle), false, "Set all input failed" @@ -86,7 +93,7 @@ bool CH422G::enableAllIO_Output(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsBegun(), false, "Not begun"); + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); ESP_UTILS_CHECK_ERROR_RETURN( esp_io_expander_ch422g_set_all_output(device_handle), false, "Set all output failed" diff --git a/src/chip/esp_expander_ch422g.hpp b/src/chip/esp_expander_ch422g.hpp index ac7281f..ab54b04 100644 --- a/src/chip/esp_expander_ch422g.hpp +++ b/src/chip/esp_expander_ch422g.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,7 +18,6 @@ namespace esp_expander { * | Pin Number | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | * | ------------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | * | Function | IO0 | IO1 | IO2 | IO3 | IO4 | IO5 | IO6 | IO7 | OC0 | OC1 | OC2 | OC3 | - * */ class CH422G: public Base { public: @@ -29,7 +28,6 @@ class CH422G: public Base { * @param[in] scl_io I2C SCL pin number * @param[in] sda_io I2C SDA pin number * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - * */ CH422G(int scl_io, int sda_io, uint8_t address): Base(scl_io, sda_io, address) {} @@ -39,7 +37,6 @@ class CH422G: public Base { * * @param[in] host_id I2C host ID. * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - * */ CH422G(int host_id, uint8_t address): Base(host_id, address) {} @@ -47,20 +44,17 @@ class CH422G: public Base { * @brief Construct a CH422G device. * * @param[in] config Configuration for the object - * */ CH422G(const Config &config): Base(config) {} /** * @deprecated Deprecated and will be removed in the next major version. Please use other constructors instead. - * */ [[deprecated("Deprecated and will be removed in the next major version. Please use other constructors instead.")]] CH422G(i2c_port_t id, uint8_t address, int scl_io, int sda_io): Base(id, address, scl_io, sda_io) {} /** * @brief Desutruct object. This function will call `del()` to delete the object. - * */ ~CH422G() override; @@ -71,7 +65,6 @@ class CH422G: public Base { * @note This function sets all IO0-7 pins to output high-level mode by default. * * @return true if success, otherwise false - * */ bool begin(void) override; @@ -79,7 +72,6 @@ class CH422G: public Base { * @brief Enable OC0-OC3 output open-drain * * @return true if success, otherwise false - * */ bool enableOC_OpenDrain(void); @@ -87,7 +79,6 @@ class CH422G: public Base { * @brief Enable OC0-OC3 output push-pull (default mode when power-on) * * @return true if success, otherwise false - * */ bool enableOC_PushPull(void); @@ -99,7 +90,6 @@ class CH422G: public Base { * input mode when it determines that all pins are configured as input. * * @return true if success, otherwise false - * */ bool enableAllIO_Input(void); @@ -107,7 +97,6 @@ class CH422G: public Base { * @brief Enable IO0-7 output mode * * @return true if success, otherwise false - * */ bool enableAllIO_Output(void); }; @@ -116,6 +105,5 @@ class CH422G: public Base { /** * @deprecated Deprecated and will be removed in the next major version. Please use `esp_expander::CH422G` instead. - * */ typedef esp_expander::CH422G ESP_IOExpander_CH422G __attribute__((deprecated("Deprecated and will be removed in the next major version. Please use `esp_expander::CH422G` instead."))); diff --git a/src/chip/esp_expander_ht8574.cpp b/src/chip/esp_expander_ht8574.cpp index e69f566..837f299 100644 --- a/src/chip/esp_expander_ht8574.cpp +++ b/src/chip/esp_expander_ht8574.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,14 +23,21 @@ bool HT8574::begin(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsInit(), false, "Not initialized"); - ESP_UTILS_CHECK_FALSE_RETURN(!checkIsBegun(), false, "Already begun"); + ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::BEGIN), false, "Already begun"); + + // Initialize the device if not initialized + if (!isOverState(State::INIT)) { + ESP_UTILS_CHECK_FALSE_RETURN(init(), false, "Init failed"); + } ESP_UTILS_CHECK_ERROR_RETURN( - esp_io_expander_new_i2c_ht8574(getHostID(), getDeviceAddress(), &device_handle), false, - "Create HT8574 IO expander failed" + esp_io_expander_new_i2c_ht8574( + static_cast(getConfig().host_id), getConfig().device.address, &device_handle + ), false, "Create HT8574 failed" ); - ESP_UTILS_LOGD("Create HT8574 IO expander(@%p)", device_handle); + ESP_UTILS_LOGD("Create HT8574 @%p", device_handle); + + setState(State::BEGIN); ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); diff --git a/src/chip/esp_expander_ht8574.hpp b/src/chip/esp_expander_ht8574.hpp index 87cd23b..e7b68ce 100644 --- a/src/chip/esp_expander_ht8574.hpp +++ b/src/chip/esp_expander_ht8574.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,7 +14,6 @@ namespace esp_expander { * @brief The HT8574 IO expander device class * * @note This class is a derived class of `esp_expander::Base`, user can use it directly - * */ class HT8574: public Base { public: @@ -25,7 +24,6 @@ class HT8574: public Base { * @param[in] scl_io I2C SCL pin number * @param[in] sda_io I2C SDA pin number * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - * */ HT8574(int scl_io, int sda_io, uint8_t address): Base(scl_io, sda_io, address) {} @@ -35,7 +33,6 @@ class HT8574: public Base { * * @param[in] host_id I2C host ID. * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - * */ HT8574(int host_id, uint8_t address): Base(host_id, address) {} @@ -43,20 +40,17 @@ class HT8574: public Base { * @brief Construct a HT8574 device. * * @param[in] config Configuration for the object - * */ HT8574(const Config &config): Base(config) {} /** * @deprecated Deprecated and will be removed in the next major version. Please use other constructors instead. - * */ [[deprecated("Deprecated and will be removed in the next major version. Please use other constructors instead.")]] HT8574(i2c_port_t id, uint8_t address, int scl_io, int sda_io): Base(id, address, scl_io, sda_io) {} /** * @brief Desutruct object. This function will call `del()` to delete the object. - * */ ~HT8574() override; @@ -67,7 +61,6 @@ class HT8574: public Base { * @note The driver initialization by default sets CH422G's IO0-7 to output high-level mode. * * @return true if success, otherwise false - * */ bool begin(void) override; }; @@ -76,6 +69,5 @@ class HT8574: public Base { /** * @deprecated Deprecated and will be removed in the next major version. Please use `esp_expander::HT8574` instead. - * */ typedef esp_expander::HT8574 ESP_IOExpander_HT8574 __attribute__((deprecated("Deprecated and will be removed in the next major version. Please use `esp_expander::HT8574` instead."))); diff --git a/src/chip/esp_expander_tca95xx_16bit.cpp b/src/chip/esp_expander_tca95xx_16bit.cpp index 2f5c8f7..68f4702 100644 --- a/src/chip/esp_expander_tca95xx_16bit.cpp +++ b/src/chip/esp_expander_tca95xx_16bit.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,14 +23,21 @@ bool TCA95XX_16BIT::begin(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsInit(), false, "Not initialized"); - ESP_UTILS_CHECK_FALSE_RETURN(!checkIsBegun(), false, "Already begun"); + ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::BEGIN), false, "Already begun"); + + // Initialize the bus if not initialized + if (!isOverState(State::INIT)) { + ESP_UTILS_CHECK_FALSE_RETURN(init(), false, "Init failed"); + } ESP_UTILS_CHECK_ERROR_RETURN( - esp_io_expander_new_i2c_tca95xx_16bit(getHostID(), getDeviceAddress(), &device_handle), false, - "Create TCA95XX_16BIT IO expander failed" + esp_io_expander_new_i2c_tca95xx_16bit( + static_cast(getConfig().host_id), getConfig().device.address, &device_handle + ), false, "Create TCA95XX_16BIT failed" ); - ESP_UTILS_LOGD("Create TCA95XX_16BIT IO expander(@%p)", device_handle); + ESP_UTILS_LOGD("Create TCA95XX_16BIT @%p", device_handle); + + setState(State::BEGIN); ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); diff --git a/src/chip/esp_expander_tca95xx_16bit.hpp b/src/chip/esp_expander_tca95xx_16bit.hpp index b27ea6d..94f4c35 100644 --- a/src/chip/esp_expander_tca95xx_16bit.hpp +++ b/src/chip/esp_expander_tca95xx_16bit.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,7 +14,6 @@ namespace esp_expander { * @brief The TCA95XX_16BIT IO expander device class * * @note This class is a derived class of `esp_expander::Base`, user can use it directly - * */ class TCA95XX_16BIT: public Base { public: @@ -25,7 +24,6 @@ class TCA95XX_16BIT: public Base { * @param[in] scl_io I2C SCL pin number * @param[in] sda_io I2C SDA pin number * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - * */ TCA95XX_16BIT(int scl_io, int sda_io, uint8_t address): Base(scl_io, sda_io, address) {} @@ -35,7 +33,6 @@ class TCA95XX_16BIT: public Base { * * @param[in] host_id I2C host ID. * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - * */ TCA95XX_16BIT(int host_id, uint8_t address): Base(host_id, address) {} @@ -43,20 +40,17 @@ class TCA95XX_16BIT: public Base { * @brief Construct a TCA95XX_16BIT device. * * @param[in] config Configuration for the object - * */ TCA95XX_16BIT(const Config &config): Base(config) {} /** * @deprecated Deprecated and will be removed in the next major version. Please use other constructors instead. - * */ [[deprecated("Deprecated and will be removed in the next major version. Please use other constructors instead.")]] TCA95XX_16BIT(i2c_port_t id, uint8_t address, int scl_io, int sda_io): Base(id, address, scl_io, sda_io) {} /** * @brief Desutruct object. This function will call `del()` to delete the object. - * */ ~TCA95XX_16BIT() override; @@ -67,7 +61,6 @@ class TCA95XX_16BIT: public Base { * @note This function sets all pins to inpurt mode by default. * * @return true if success, otherwise false - * */ bool begin(void) override; }; @@ -76,6 +69,5 @@ class TCA95XX_16BIT: public Base { /** * @deprecated Deprecated and will be removed in the next major version. Please use `esp_expander::TCA95XX_16BIT` instead. - * */ typedef esp_expander::TCA95XX_16BIT ESP_IOExpander_TCA95xx_16bit __attribute__((deprecated("Deprecated and will be removed in the next major version. Please use `esp_expander::TCA95XX_16BIT` instead."))); diff --git a/src/chip/esp_expander_tca95xx_8bit.cpp b/src/chip/esp_expander_tca95xx_8bit.cpp index 7ea43c6..60ebdca 100644 --- a/src/chip/esp_expander_tca95xx_8bit.cpp +++ b/src/chip/esp_expander_tca95xx_8bit.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,14 +23,21 @@ bool TCA95XX_8BIT::begin(void) { ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); - ESP_UTILS_CHECK_FALSE_RETURN(checkIsInit(), false, "Not initialized"); - ESP_UTILS_CHECK_FALSE_RETURN(!checkIsBegun(), false, "Already begun"); + ESP_UTILS_CHECK_FALSE_RETURN(!isOverState(State::BEGIN), false, "Already begun"); + + // Initialize the device if not initialized + if (!isOverState(State::INIT)) { + ESP_UTILS_CHECK_FALSE_RETURN(init(), false, "Init failed"); + } ESP_UTILS_CHECK_ERROR_RETURN( - esp_io_expander_new_i2c_tca9554(getHostID(), getDeviceAddress(), &device_handle), false, - "Create TCA95XX_8BIT IO expander failed" + esp_io_expander_new_i2c_tca9554( + static_cast(getConfig().host_id), getConfig().device.address, &device_handle + ), false, "Create TCA95XX_8BIT failed" ); - ESP_UTILS_LOGD("Create TCA95XX_8BIT IO expander(@%p)", device_handle); + ESP_UTILS_LOGD("Create TCA95XX_8BIT @%p", device_handle); + + setState(State::BEGIN); ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); diff --git a/src/chip/esp_expander_tca95xx_8bit.hpp b/src/chip/esp_expander_tca95xx_8bit.hpp index 75da75e..0946a3f 100644 --- a/src/chip/esp_expander_tca95xx_8bit.hpp +++ b/src/chip/esp_expander_tca95xx_8bit.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,7 +14,6 @@ namespace esp_expander { * @brief The TCA95XX_8BIT IO expander device class * * @note This class is a derived class of `esp_expander::Base`, user can use it directly - * */ class TCA95XX_8BIT: public Base { public: @@ -25,7 +24,6 @@ class TCA95XX_8BIT: public Base { * @param[in] scl_io I2C SCL pin number * @param[in] sda_io I2C SDA pin number * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - * */ TCA95XX_8BIT(int scl_io, int sda_io, uint8_t address): Base(scl_io, sda_io, address) {} @@ -35,7 +33,6 @@ class TCA95XX_8BIT: public Base { * * @param[in] host_id I2C host ID. * @param[in] address I2C device 7-bit address. Should be like `ESP_IO_EXPANDER_I2C__ADDRESS`. - * */ TCA95XX_8BIT(int host_id, uint8_t address): Base(host_id, address) {} @@ -43,20 +40,17 @@ class TCA95XX_8BIT: public Base { * @brief Construct a TCA95XX_8BIT device. * * @param[in] config Configuration for the object - * */ TCA95XX_8BIT(const Config &config): Base(config) {} /** * @deprecated Deprecated and will be removed in the next major version. Please use other constructors instead. - * */ [[deprecated("Deprecated and will be removed in the next major version. Please use other constructors instead.")]] TCA95XX_8BIT(i2c_port_t id, uint8_t address, int scl_io, int sda_io): Base(id, address, scl_io, sda_io) {} /** * @brief Desutruct object. This function will call `del()` to delete the object. - * */ ~TCA95XX_8BIT() override; @@ -67,7 +61,6 @@ class TCA95XX_8BIT: public Base { * @note This function sets all pins to inpurt mode by default. * * @return true if success, otherwise false - * */ bool begin(void) override; }; @@ -76,6 +69,5 @@ class TCA95XX_8BIT: public Base { /** * @deprecated Deprecated and will be removed in the next major version. Please use `esp_expander::TCA95XX_8BIT` instead. - * */ typedef esp_expander::TCA95XX_8BIT ESP_IOExpander_TCA95xx_8bit __attribute__((deprecated("Deprecated and will be removed in the next major version. Please use `esp_expander::TCA95XX_8BIT` instead."))); diff --git a/src/esp_expander_utils.h b/src/esp_expander_utils.h index a107319..922dc85 100644 --- a/src/esp_expander_utils.h +++ b/src/esp_expander_utils.h @@ -8,3 +8,4 @@ // Define the log tag for the current library, should be declared before `esp_lib_utils.hpp` #define ESP_UTILS_LOG_TAG "Expander" #include "esp_lib_utils.h" +#include "esp_utils_helpers.h" diff --git a/src/port/esp_io_expander.c b/src/port/esp_io_expander.c index b27c743..d93f50b 100644 --- a/src/port/esp_io_expander.c +++ b/src/port/esp_io_expander.c @@ -19,7 +19,6 @@ /** * @brief Register type - * */ typedef enum { REG_INPUT = 0, diff --git a/src/port/esp_io_expander.h b/src/port/esp_io_expander.h index e36adf4..a01f066 100644 --- a/src/port/esp_io_expander.h +++ b/src/port/esp_io_expander.h @@ -24,14 +24,12 @@ extern "C" { /** * @brief IO Expander Device Type - * */ typedef struct esp_io_expander_s esp_io_expander_t; typedef esp_io_expander_t *esp_io_expander_handle_t; /** * @brief IO Expander Pin Num - * */ typedef enum { IO_EXPANDER_PIN_NUM_0 = (1ULL << 0), @@ -70,7 +68,6 @@ typedef enum { /** * @brief IO Expander Pin direction - * */ typedef enum { IO_EXPANDER_INPUT, /*!< Input direction */ @@ -79,7 +76,6 @@ typedef enum { /** * @brief IO Expander Configuration Type - * */ typedef struct { uint8_t io_count; /*!< Count of device's IO, must be less or equal than `IO_COUNT_MAX` */ diff --git a/src/port/esp_io_expander_ch422g.c b/src/port/esp_io_expander_ch422g.c index 9b6ce6a..59c8d17 100644 --- a/src/port/esp_io_expander_ch422g.c +++ b/src/port/esp_io_expander_ch422g.c @@ -48,7 +48,6 @@ /** * @brief Device Structure Type - * */ typedef struct { esp_io_expander_t base; diff --git a/src/port/esp_io_expander_ch422g.h b/src/port/esp_io_expander_ch422g.h index 46f4f2a..1aecf43 100644 --- a/src/port/esp_io_expander_ch422g.h +++ b/src/port/esp_io_expander_ch422g.h @@ -37,7 +37,6 @@ esp_err_t esp_io_expander_new_i2c_ch422g(i2c_port_t i2c_num, uint32_t i2c_addres /** * @brief I2C address of the ch422g. Just to keep the same with other IO expanders, but it is ignored. - * */ #define ESP_IO_EXPANDER_I2C_CH422G_ADDRESS (0x24) diff --git a/src/port/esp_io_expander_ht8574.c b/src/port/esp_io_expander_ht8574.c index 5236bf3..bbfd79a 100644 --- a/src/port/esp_io_expander_ht8574.c +++ b/src/port/esp_io_expander_ht8574.c @@ -29,7 +29,6 @@ /** * @brief Device Structure Type - * */ typedef struct { esp_io_expander_t base; diff --git a/src/port/esp_io_expander_tca9554.c b/src/port/esp_io_expander_tca9554.c index f3ebaf1..3732bd7 100644 --- a/src/port/esp_io_expander_tca9554.c +++ b/src/port/esp_io_expander_tca9554.c @@ -34,7 +34,6 @@ /** * @brief Device Structure Type - * */ typedef struct { esp_io_expander_t base; diff --git a/src/port/esp_io_expander_tca95xx_16bit.c b/src/port/esp_io_expander_tca95xx_16bit.c index 18edd5a..b9f2f17 100644 --- a/src/port/esp_io_expander_tca95xx_16bit.c +++ b/src/port/esp_io_expander_tca95xx_16bit.c @@ -34,7 +34,6 @@ /** * @brief Device Structure Type - * */ typedef struct { esp_io_expander_t base; diff --git a/test_apps/main/test_chip_general.cpp b/test_apps/main/test_chip_general.cpp index 5542dd8..007d627 100644 --- a/test_apps/main/test_chip_general.cpp +++ b/test_apps/main/test_chip_general.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -83,8 +83,9 @@ static void test_device(std::shared_ptr device) ESP_LOGI(TAG, "Test constructor with (const Config &config) (external I2C)"); \ Base::Config external_i2c_config = { \ .host_id = TEST_HOST_ID, \ - .device_address = TEST_DEVICE_ADDRESS, \ - .skip_init_host = true, \ + .device = Base::DeviceConfig{ \ + .address = TEST_DEVICE_ADDRESS, \ + }, \ }; \ expander = CREATE_DEVICE(device_name, external_i2c_config); \ test_device(expander); \ @@ -101,10 +102,13 @@ static void test_device(std::shared_ptr device) ESP_LOGI(TAG, "Test constructor with (const Config &config) (internal I2C)"); \ Base::Config internal_i2c_config = { \ .host_id = TEST_HOST_ID, \ - .host_sda_io_num = TEST_HOST_I2C_SDA_PIN, \ - .host_scl_io_num = TEST_HOST_I2C_SCL_PIN, \ - .device_address = TEST_DEVICE_ADDRESS, \ - .skip_init_host = false, \ + .host = Base::HostPartialConfig{ \ + .sda_io_num = TEST_HOST_I2C_SDA_PIN, \ + .scl_io_num = TEST_HOST_I2C_SCL_PIN, \ + }, \ + .device = Base::DeviceConfig{ \ + .address = TEST_DEVICE_ADDRESS, \ + }, \ }; \ expander = CREATE_DEVICE(device_name, internal_i2c_config); \ test_device(expander); \ @@ -164,7 +168,6 @@ static void test_device(std::shared_ptr device) /** * Here to create test cases for different devices - * */ CREATE_TEST_CASE(TCA95XX_8BIT) CREATE_TEST_CASE(TCA95XX_16BIT) diff --git a/test_apps/sdkconfig.ci.debug_log b/test_apps/sdkconfig.ci.debug_log new file mode 100644 index 0000000..191f9c8 --- /dev/null +++ b/test_apps/sdkconfig.ci.debug_log @@ -0,0 +1,2 @@ +CONFIG_ESP_UTILS_CONF_LOG_LEVEL_DEBUG=y +CONFIG_ESP_UTILS_CONF_ENABLE_LOG_TRACE=y From a8bee03ef861c815e0f68386a66edb423cc15c4e Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Fri, 7 Feb 2025 16:26:27 +0800 Subject: [PATCH 07/10] feat(repo): add legacy header files to maintain compatibility --- src/ESP_IOExpander.h | 16 ++++++++++++++++ src/base/esp_io_expander.h | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/ESP_IOExpander.h create mode 100644 src/base/esp_io_expander.h diff --git a/src/ESP_IOExpander.h b/src/ESP_IOExpander.h new file mode 100644 index 0000000..7212f7d --- /dev/null +++ b/src/ESP_IOExpander.h @@ -0,0 +1,16 @@ + +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * This file is just to keep the compatibility with the old version of the library. Please use the file `chip/esp_expander_base.hpp` instead. + */ + +#pragma once + +#warning "This file is deprecated. Please use the file `chip/esp_expander_base.hpp` instead." + +#include "chip/esp_expander_base.hpp" diff --git a/src/base/esp_io_expander.h b/src/base/esp_io_expander.h new file mode 100644 index 0000000..0f26e86 --- /dev/null +++ b/src/base/esp_io_expander.h @@ -0,0 +1,16 @@ + +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * This file is just to keep the compatibility with the old version of the library. Please use the file `port/esp_io_expander.h` instead. + */ + +#pragma once + +#warning "This file is deprecated. Please use the file `port/esp_io_expander.h` instead." + +#include "port/esp_io_expander.h" From 25cb9404b431336b0b01ca1909eb136ace532c60 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Fri, 7 Feb 2025 16:26:45 +0800 Subject: [PATCH 08/10] feat(repo): update with esp-lib-utils v0.2.* --- CHANGELOG.md | 7 ++++++ CMakeLists.txt | 7 +++++- idf_component.yml | 4 ++-- library.properties | 4 ++-- micropython.cmake | 2 +- src/chip/esp_expander_base.cpp | 39 +++++++++++++++++----------------- 6 files changed, 38 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eebfcfa..3b1a2ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # ChangeLog +## v1.1.0 - 2025-02-07 + +### Enhancements: + +* feat(repo): add legacy header files to maintain compatibility +* feat(repo): update with esp-lib-utils v0.2.* + ## v1.0.1 - 2025-01-23 ### Enhancements: diff --git a/CMakeLists.txt b/CMakeLists.txt index c1af501..350d52b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,4 +13,9 @@ idf_component_register( driver ) -target_compile_options(${COMPONENT_LIB} PUBLIC -Wno-missing-field-initializers) +target_compile_options(${COMPONENT_LIB} + PUBLIC + -Wno-missing-field-initializers + PRIVATE + $<$:-std=gnu++17> +) diff --git a/idf_component.yml b/idf_component.yml index 846d206..3cd47cf 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.0.1" +version: "1.1.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 @@ -6,5 +6,5 @@ issues: https://github.com/esp-arduino-libs/ESP32_IO_Expander/issues dependencies: idf: ">=5.1" espressif/esp-lib-utils: - version: "0.1.*" + version: "0.2.*" public: true diff --git a/library.properties b/library.properties index df3c950..46fc8ad 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ESP32_IO_Expander -version=1.0.1 +version=1.1.0 author=espressif maintainer=espressif sentence=ESP32_IO_Expander is a library designed for driving IO expander chips using ESP SoCs @@ -8,4 +8,4 @@ category=Other architectures=esp32 url=https://github.com/esp-arduino-libs/ESP32_IO_Expander includes=esp_io_expander.hpp -depends=esp-lib-utils (>=0.1.0 && <0.2.0) +depends=esp-lib-utils (>=0.2.0 && <0.3.0) diff --git a/micropython.cmake b/micropython.cmake index 62d0d5a..f0caa53 100644 --- a/micropython.cmake +++ b/micropython.cmake @@ -16,7 +16,7 @@ target_include_directories(usermod_esp_io_expander INTERFACE ${SRC_DIR}) # Add compile options. Since the target is not created by `idf_component_register()`, we need to add the `ESP_PLATFORM` define manually. target_compile_options(usermod_esp_io_expander INTERFACE - -Wno-missing-field-initializers -DESP_PLATFORM $<$:-std=gnu++2b> + -Wno-missing-field-initializers -DESP_PLATFORM $<$:-std=gnu++17> ) # Link our INTERFACE library to the usermod target. diff --git a/src/chip/esp_expander_base.cpp b/src/chip/esp_expander_base.cpp index d535efe..1ad2262 100644 --- a/src/chip/esp_expander_base.cpp +++ b/src/chip/esp_expander_base.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "inttypes.h" #include "driver/i2c.h" #include "esp_expander_utils.h" #include "esp_expander_base.hpp" @@ -59,14 +60,14 @@ void Base::Config::printHostConfig(void) const "\t\t-> [scl_pullup_en]: %d\n" "\t\t-> [master.clk_speed]: %d\n" "\t\t-> [clk_flags]: %d" - , host_id - , config.mode - , config.sda_io_num - , config.scl_io_num - , config.sda_pullup_en - , config.scl_pullup_en - , config.master.clk_speed - , config.clk_flags + , static_cast(host_id) + , static_cast(config.mode) + , static_cast(config.sda_io_num) + , static_cast(config.scl_io_num) + , static_cast(config.sda_pullup_en) + , static_cast(config.scl_pullup_en) + , static_cast(config.master.clk_speed) + , static_cast(config.clk_flags) ); } else { auto &config = std::get(host.value()); @@ -78,12 +79,12 @@ void Base::Config::printHostConfig(void) const "\t\t-> [sda_pullup_en]: %d\n" "\t\t-> [scl_pullup_en]: %d\n" "\t\t-> [clk_speed]: %d" - , host_id - , config.sda_io_num - , config.scl_io_num - , config.sda_pullup_en - , config.scl_pullup_en - , config.clk_speed + , static_cast(host_id) + , static_cast(config.sda_io_num) + , static_cast(config.scl_io_num) + , static_cast(config.sda_pullup_en) + , static_cast(config.scl_pullup_en) + , static_cast(config.clk_speed) ); } @@ -99,8 +100,8 @@ void Base::Config::printDeviceConfig(void) const "\n\t{Device config}[partial]\n" "\t\t-> [host_id]: %d\n" "\t\t-> [address]: 0x%02X" - , host_id - , device.address + , static_cast(host_id) + , static_cast(device.address) ); ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); @@ -248,7 +249,7 @@ bool Base::multiPinMode(uint32_t pin_mask, uint8_t mode) ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); - ESP_UTILS_LOGD("Param: pin_mask(%0x), mode(%d)", pin_mask, mode); + ESP_UTILS_LOGD("Param: pin_mask(0x%" PRIx32 "), mode(%d)", pin_mask, mode); ESP_UTILS_CHECK_FALSE_RETURN((mode == INPUT) || (mode == OUTPUT), false, "Invalid mode"); esp_io_expander_dir_t dir = (mode == INPUT) ? IO_EXPANDER_INPUT : IO_EXPANDER_OUTPUT; @@ -265,7 +266,7 @@ bool Base::multiDigitalWrite(uint32_t pin_mask, uint8_t value) ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); - ESP_UTILS_LOGD("Param: pin_mask(%0x), value(%d)", pin_mask, value); + ESP_UTILS_LOGD("Param: pin_mask(0x%" PRIx32 "), value(%d)", pin_mask, value); ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_set_level(device_handle, pin_mask, value), false, "Set level failed"); @@ -280,7 +281,7 @@ int64_t Base::multiDigitalRead(uint32_t pin_mask) ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); - ESP_UTILS_LOGD("Param: pin_mask(%0x)", pin_mask); + ESP_UTILS_LOGD("Param: pin_mask(0x%" PRIx32 ")", pin_mask); uint32_t level = 0; ESP_UTILS_CHECK_ERROR_RETURN(esp_io_expander_get_level(device_handle, pin_mask, &level), false, "Get level failed"); From 86c81f6101d1dbc6cb04ceeb351f710527cf4db5 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Mon, 7 Jul 2025 15:44:30 +0800 Subject: [PATCH 09/10] fix(port): fix discarded qualifiers warning @isthaison (#22) Closes https://github.com/esp-arduino-libs/ESP32_IO_Expander/issues/22 --- idf_component.yml | 2 +- library.properties | 2 +- src/port/esp_io_expander.c | 2 +- src/port/esp_io_expander_ht8574.c | 2 +- src/port/esp_io_expander_tca9554.c | 2 +- src/port/esp_io_expander_tca95xx_16bit.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/idf_component.yml b/idf_component.yml index 3cd47cf..4ad0e50 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.1.0" +version: "1.1.1" description: ESP32_IO_Expander is a library designed for driving IO expander chips using ESP SoCs url: https://github.com/esp-arduino-libs/ESP32_IO_Expander repository: https://github.com/esp-arduino-libs/ESP32_IO_Expander.git diff --git a/library.properties b/library.properties index 46fc8ad..4065fba 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ESP32_IO_Expander -version=1.1.0 +version=1.1.1 author=espressif maintainer=espressif sentence=ESP32_IO_Expander is a library designed for driving IO expander chips using ESP SoCs diff --git a/src/port/esp_io_expander.c b/src/port/esp_io_expander.c index d93f50b..f442168 100644 --- a/src/port/esp_io_expander.c +++ b/src/port/esp_io_expander.c @@ -26,7 +26,7 @@ typedef enum { REG_DIRECTION, } reg_type_t; -static char *TAG = "io_expander"; +static const char *TAG = "io_expander"; static esp_err_t write_reg(esp_io_expander_handle_t handle, reg_type_t reg, uint32_t value); static esp_err_t read_reg(esp_io_expander_handle_t handle, reg_type_t reg, uint32_t *value); diff --git a/src/port/esp_io_expander_ht8574.c b/src/port/esp_io_expander_ht8574.c index bbfd79a..840992f 100644 --- a/src/port/esp_io_expander_ht8574.c +++ b/src/port/esp_io_expander_ht8574.c @@ -40,7 +40,7 @@ typedef struct { } regs; } esp_io_expander_ht8574_t; -static char *TAG = "ht8574"; +static const char *TAG = "ht8574"; static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value); static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t value); diff --git a/src/port/esp_io_expander_tca9554.c b/src/port/esp_io_expander_tca9554.c index 3732bd7..ef17872 100644 --- a/src/port/esp_io_expander_tca9554.c +++ b/src/port/esp_io_expander_tca9554.c @@ -45,7 +45,7 @@ typedef struct { } regs; } esp_io_expander_tca9554_t; -static char *TAG = "tca9554"; +static const char *TAG = "tca9554"; static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value); static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t value); diff --git a/src/port/esp_io_expander_tca95xx_16bit.c b/src/port/esp_io_expander_tca95xx_16bit.c index b9f2f17..4062d31 100644 --- a/src/port/esp_io_expander_tca95xx_16bit.c +++ b/src/port/esp_io_expander_tca95xx_16bit.c @@ -45,7 +45,7 @@ typedef struct { } regs; } esp_io_expander_tca95xx_16bit_t; -static char *TAG = "tca95xx_16"; +static const char *TAG = "tca95xx_16"; static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value); static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t value); From 8cb62dbc69965ab98fd77eb924ecf9f9fcb87edb Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Mon, 7 Jul 2025 15:44:45 +0800 Subject: [PATCH 10/10] feat(ch422g): support enter/exit sleep --- .github/workflows/arduino_lint.yml | 3 ++ .github/workflows/build_test.yml | 3 ++ .github/workflows/issue_comment.yml | 24 -------------- .github/workflows/new_issues.yml | 24 -------------- .github/workflows/new_prs.yml | 29 ----------------- .github/workflows/pre-commit.yml | 2 +- .github/workflows/upload_component.yml | 2 +- CHANGELOG.md | 10 ++++++ src/chip/esp_expander_ch422g.cpp | 30 ++++++++++++++++++ src/chip/esp_expander_ch422g.hpp | 14 +++++++++ src/port/esp_io_expander_ch422g.c | 43 +++++++++++++++++++++++--- src/port/esp_io_expander_ch422g.h | 4 +++ 12 files changed, 104 insertions(+), 84 deletions(-) delete mode 100644 .github/workflows/issue_comment.yml delete mode 100644 .github/workflows/new_issues.yml delete mode 100644 .github/workflows/new_prs.yml diff --git a/.github/workflows/arduino_lint.yml b/.github/workflows/arduino_lint.yml index 0a6b9a8..a0f4707 100644 --- a/.github/workflows/arduino_lint.yml +++ b/.github/workflows/arduino_lint.yml @@ -4,6 +4,9 @@ on: workflow_dispatch: pull_request: types: [opened, reopened, synchronize] + push: + branches: + - master jobs: lint: diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 9d3c04c..e87ca79 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -4,6 +4,9 @@ on: workflow_dispatch: pull_request: types: [opened, reopened, synchronize] + push: + branches: + - master jobs: build: diff --git a/.github/workflows/issue_comment.yml b/.github/workflows/issue_comment.yml deleted file mode 100644 index 3b6fcc1..0000000 --- a/.github/workflows/issue_comment.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Sync issue comments to JIRA - -# This workflow will be triggered when new issue comment is created (including PR comments) -on: issue_comment - -# Limit to single concurrent run for workflows which can create Jira issues. -# Same concurrency group is used in new_issues.yml -concurrency: jira_issues - -jobs: - sync_issue_comments_to_jira: - name: Sync Issue Comments to Jira - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Sync issue comments to JIRA - uses: espressif/github-actions/sync_issues_to_jira@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - JIRA_PASS: ${{ secrets.JIRA_PASS }} - JIRA_PROJECT: ${{ secrets.JIRA_PROJECT }} - JIRA_COMPONENT: ${{ secrets.JIRA_COMPONENT }} - JIRA_URL: ${{ secrets.JIRA_URL }} - JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/.github/workflows/new_issues.yml b/.github/workflows/new_issues.yml deleted file mode 100644 index f0fa402..0000000 --- a/.github/workflows/new_issues.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Sync issues to Jira - -# This workflow will be triggered when a new issue is opened -on: issues - -# Limit to single concurrent run for workflows which can create Jira issues. -# Same concurrency group is used in issue_comment.yml -concurrency: jira_issues - -jobs: - sync_issues_to_jira: - name: Sync issues to Jira - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Sync GitHub issues to Jira project - uses: espressif/github-actions/sync_issues_to_jira@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - JIRA_PASS: ${{ secrets.JIRA_PASS }} - JIRA_PROJECT: ${{ secrets.JIRA_PROJECT }} - JIRA_COMPONENT: ${{ secrets.JIRA_COMPONENT }} - JIRA_URL: ${{ secrets.JIRA_URL }} - JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/.github/workflows/new_prs.yml b/.github/workflows/new_prs.yml deleted file mode 100644 index 01d7fe2..0000000 --- a/.github/workflows/new_prs.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Sync remain PRs to Jira - -# This workflow will be triggered every hour, to sync remaining PRs (i.e. PRs with zero comment) to Jira project -# Note that, PRs can also get synced when new PR comment is created -on: - schedule: - - cron: "0 * * * *" - -# Limit to single concurrent run for workflows which can create Jira issues. -# Same concurrency group is used in issue_comment.yml -concurrency: jira_issues - -jobs: - sync_prs_to_jira: - name: Sync PRs to Jira - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Sync PRs to Jira project - uses: espressif/github-actions/sync_issues_to_jira@master - with: - cron_job: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - JIRA_PASS: ${{ secrets.JIRA_PASS }} - JIRA_PROJECT: ${{ secrets.JIRA_PROJECT }} - JIRA_COMPONENT: ${{ secrets.JIRA_COMPONENT }} - JIRA_URL: ${{ secrets.JIRA_URL }} - JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 7111ea9..cc477db 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -11,4 +11,4 @@ jobs: steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - - uses: pre-commit/action@v2.0.3 + - uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml index 45e9b78..333c718 100644 --- a/.github/workflows/upload_component.yml +++ b/.github/workflows/upload_component.yml @@ -15,6 +15,6 @@ jobs: - name: Upload components to component service uses: espressif/upload-components-ci-action@v1 with: - name: "ESP32_IO_Expander" + name: "esp32_io_expander" namespace: "espressif" api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b1a2ff..4e14044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # ChangeLog +## v1.1.1 - 2025-07-07 + +### Enhancements: + +* feat(ch422g): support enter/exit sleep + +### Bug Fixes: + +* fix(port): fix discarded qualifiers warning + ## v1.1.0 - 2025-02-07 ### Enhancements: diff --git a/src/chip/esp_expander_ch422g.cpp b/src/chip/esp_expander_ch422g.cpp index 1a0dcbd..dcf3223 100644 --- a/src/chip/esp_expander_ch422g.cpp +++ b/src/chip/esp_expander_ch422g.cpp @@ -104,4 +104,34 @@ bool CH422G::enableAllIO_Output(void) return true; } +bool CH422G::enterSleep(void) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); + + ESP_UTILS_CHECK_ERROR_RETURN( + esp_io_expander_ch422g_enter_sleep(device_handle), false, "Enter sleep failed" + ); + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); + + return true; +} + +bool CH422G::exitSleep(void) +{ + ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS(); + + ESP_UTILS_CHECK_FALSE_RETURN(isOverState(State::BEGIN), false, "Not begun"); + + ESP_UTILS_CHECK_ERROR_RETURN( + esp_io_expander_ch422g_exit_sleep(device_handle), false, "Exit sleep failed" + ); + + ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS(); + + return true; +} + } // namespace esp_expander diff --git a/src/chip/esp_expander_ch422g.hpp b/src/chip/esp_expander_ch422g.hpp index ab54b04..c438bde 100644 --- a/src/chip/esp_expander_ch422g.hpp +++ b/src/chip/esp_expander_ch422g.hpp @@ -99,6 +99,20 @@ class CH422G: public Base { * @return true if success, otherwise false */ bool enableAllIO_Output(void); + + /** + * @brief Enter sleep mode + * + * @return true if success, otherwise false + */ + bool enterSleep(void); + + /** + * @brief Exit sleep mode + * + * @return true if success, otherwise false + */ + bool exitSleep(void); }; } // namespace esp_expander diff --git a/src/port/esp_io_expander_ch422g.c b/src/port/esp_io_expander_ch422g.c index 59c8d17..f16fd02 100644 --- a/src/port/esp_io_expander_ch422g.c +++ b/src/port/esp_io_expander_ch422g.c @@ -38,13 +38,14 @@ // Default: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | // *INDENT-OFF* -#define REG_WR_OC_DEFAULT_VAL (0x0FUL) -#define REG_WR_IO_DEFAULT_VAL (0xFFUL) +#define REG_WR_OC_DEFAULT_VAL (0x0FU) +#define REG_WR_IO_DEFAULT_VAL (0xFFU) #define REG_OUT_DEFAULT_VAL ((REG_WR_OC_DEFAULT_VAL << 8) | REG_WR_IO_DEFAULT_VAL) -#define REG_DIR_DEFAULT_VAL (0xFFFUL) +#define REG_DIR_DEFAULT_VAL (0xFFFU) -#define REG_WR_SET_BIT_IO_OE (1 << 0) -#define REG_WR_SET_BIT_OD_EN (1 << 2) +#define REG_WR_SET_BIT_IO_OE (1U << 0) +#define REG_WR_SET_BIT_OD_EN (1U << 2) +#define REG_WR_SET_BIT_SLEEP (1U << 3) /** * @brief Device Structure Type @@ -171,6 +172,38 @@ esp_err_t esp_io_expander_ch422g_set_all_output(esp_io_expander_handle_t handle) return ESP_OK; } +esp_err_t esp_io_expander_ch422g_enter_sleep(esp_io_expander_handle_t handle) +{ + esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); + uint8_t data = (uint8_t)(ch422g->regs.wr_set | REG_WR_SET_BIT_SLEEP); + + // WR-SET + ESP_RETURN_ON_ERROR( + i2c_master_write_to_device( + ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) + ), TAG, "Write WR_SET reg failed" + ); + ch422g->regs.wr_set = data; + + return ESP_OK; +} + +esp_err_t esp_io_expander_ch422g_exit_sleep(esp_io_expander_handle_t handle) +{ + esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); + uint8_t data = (uint8_t)(ch422g->regs.wr_set & ~REG_WR_SET_BIT_SLEEP); + + // WR-SET + ESP_RETURN_ON_ERROR( + i2c_master_write_to_device( + ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) + ), TAG, "Write WR_SET reg failed" + ); + ch422g->regs.wr_set = data; + + return ESP_OK; +} + static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value) { esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); diff --git a/src/port/esp_io_expander_ch422g.h b/src/port/esp_io_expander_ch422g.h index 1aecf43..a312edf 100644 --- a/src/port/esp_io_expander_ch422g.h +++ b/src/port/esp_io_expander_ch422g.h @@ -48,6 +48,10 @@ esp_err_t esp_io_expander_ch422g_set_all_input(esp_io_expander_handle_t handle); esp_err_t esp_io_expander_ch422g_set_all_output(esp_io_expander_handle_t handle); +esp_err_t esp_io_expander_ch422g_enter_sleep(esp_io_expander_handle_t handle); + +esp_err_t esp_io_expander_ch422g_exit_sleep(esp_io_expander_handle_t handle); + #ifdef __cplusplus } #endif pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy