diff --git a/.build-test-rules.yml b/.build-test-rules.yml index 29637015..2ec04526 100644 --- a/.build-test-rules.yml +++ b/.build-test-rules.yml @@ -84,3 +84,24 @@ components/icm42670: components/qma6100p: depends_filepatterns: - "components/qma6100p/**" + +components/io_expander/esp_io_expander_ht8574: + depends_filepatterns: + - "components/io_expander/esp_io_expander_ht8574/**" + disable: + - if: (IDF_VERSION_MAJOR == 5 and IDF_VERSION_MINOR < 2) or IDF_VERSION_MAJOR < 5 + reason: Requires I2C Driver-NG which was introduced in v5.2 + +components/io_expander/esp_io_expander_tca95xx_16bit: + depends_filepatterns: + - "components/io_expander/esp_io_expander_tca95xx_16bit/**" + disable: + - if: (IDF_VERSION_MAJOR == 5 and IDF_VERSION_MINOR < 2) or IDF_VERSION_MAJOR < 5 + reason: Requires I2C Driver-NG which was introduced in v5.2 + +components/io_expander/esp_io_expander_tca9554: + depends_filepatterns: + - "components/io_expander/esp_io_expander_tca9554/**" + disable: + - if: (IDF_VERSION_MAJOR == 5 and IDF_VERSION_MINOR < 2) or IDF_VERSION_MAJOR < 5 + reason: Requires I2C Driver-NG which was introduced in v5.2 diff --git a/components/io_expander/esp_io_expander_ht8574/README.md b/components/io_expander/esp_io_expander_ht8574/README.md index 48669e3f..13624213 100644 --- a/components/io_expander/esp_io_expander_ht8574/README.md +++ b/components/io_expander/esp_io_expander_ht8574/README.md @@ -18,22 +18,43 @@ Alternatively, you can create `idf_component.yml`. More is in [Espressif's docum ## Example use -Creation of the component. +Creation of the i2c bus. +```c + i2c_master_bus_handle_t i2c_handle = NULL; + const i2c_master_bus_config_t bus_config = { + .i2c_port = I2C_NUM_0, + .sda_io_num = 47, + .scl_io_num = 48, + .clk_source = I2C_CLK_SRC_DEFAULT, + }; + i2c_new_master_bus(&bus_config, &i2c_handle); ``` + +Creation of the component. + +```c esp_io_expander_handle_t io_expander = NULL; - esp_io_expander_new_i2c_ht8574(1, ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_000, &io_expander); + esp_io_expander_new_i2c_ht8574(i2c_handle, ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_000, &io_expander); ``` -Set pin 0 and pin 1 with output dircetion and low level: +Print all pins's status to the log: +```c + esp_io_expander_print_state(io_expander); ``` + +Set pin 0 and pin 1 with output dircetion and low level: + +```c esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_OUTPUT); esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 0); ``` -Print all pins's status to the log: +Set pin 2 and pin 3 with input dircetion: -``` - esp_io_expander_print_state(io_expander); +```c + uint32_t pin_levels = 0; + esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, IO_EXPANDER_INPUT); + esp_io_expander_get_level(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, &pin_levels); ``` diff --git a/components/io_expander/esp_io_expander_ht8574/esp_io_expander_ht8574.c b/components/io_expander/esp_io_expander_ht8574/esp_io_expander_ht8574.c index 0be9384a..de96fdc2 100644 --- a/components/io_expander/esp_io_expander_ht8574/esp_io_expander_ht8574.c +++ b/components/io_expander/esp_io_expander_ht8574/esp_io_expander_ht8574.c @@ -25,14 +25,16 @@ #define DIR_REG_DEFAULT_VAL (0xff) #define OUT_REG_DEFAULT_VAL (0xff) +/* Default I2C clock speed */ +#define I2C_CLK_SPEED 400000 + /** * @brief Device Structure Type * */ typedef struct { esp_io_expander_t base; - i2c_port_t i2c_num; - uint32_t i2c_address; + i2c_master_dev_handle_t i2c_handle; struct { uint8_t direction; uint8_t output; @@ -49,18 +51,25 @@ static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *v static esp_err_t reset(esp_io_expander_t *handle); static esp_err_t del(esp_io_expander_t *handle); -esp_err_t esp_io_expander_new_i2c_ht8574(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle) +esp_err_t esp_io_expander_new_i2c_ht8574(i2c_master_bus_handle_t i2c_bus, uint32_t dev_addr, esp_io_expander_handle_t *handle_ret) { - ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "Invalid i2c num"); - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); + ESP_RETURN_ON_FALSE(handle_ret != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid handle_ret"); + // Allocate memory for driver object esp_io_expander_ht8574_t *ht8574 = (esp_io_expander_ht8574_t *)calloc(1, sizeof(esp_io_expander_ht8574_t)); - ESP_RETURN_ON_FALSE(ht8574, ESP_ERR_NO_MEM, TAG, "Malloc failed"); + ESP_RETURN_ON_FALSE(ht8574 != NULL, ESP_ERR_NO_MEM, TAG, "Malloc failed"); + + // Add new I2C device + esp_err_t ret = ESP_OK; + const i2c_device_config_t i2c_dev_cfg = { + .device_address = dev_addr, + .scl_speed_hz = I2C_CLK_SPEED, + }; + ESP_GOTO_ON_ERROR(i2c_master_bus_add_device(i2c_bus, &i2c_dev_cfg, &ht8574->i2c_handle), err, TAG, "Add new I2C device failed"); + // Initialize device structure ht8574->base.config.io_count = IO_COUNT; ht8574->base.config.flags.dir_out_bit_zero = 1; - ht8574->i2c_num = i2c_num; - ht8574->i2c_address = i2c_address; ht8574->base.read_input_reg = read_input_reg; ht8574->base.write_output_reg = write_output_reg; ht8574->base.read_output_reg = read_output_reg; @@ -69,12 +78,12 @@ esp_err_t esp_io_expander_new_i2c_ht8574(i2c_port_t i2c_num, uint32_t i2c_addres ht8574->base.del = del; ht8574->base.reset = reset; - esp_err_t ret = ESP_OK; - /* Reset configuration and register status */ + // Reset configuration and register status ESP_GOTO_ON_ERROR(reset(&ht8574->base), err, TAG, "Reset failed"); - *handle = &ht8574->base; + *handle_ret = &ht8574->base; return ESP_OK; + err: free(ht8574); return ret; @@ -83,13 +92,9 @@ esp_err_t esp_io_expander_new_i2c_ht8574(i2c_port_t i2c_num, uint32_t i2c_addres static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value) { esp_io_expander_ht8574_t *ht8574 = (esp_io_expander_ht8574_t *)__containerof(handle, esp_io_expander_ht8574_t, base); - uint8_t temp = 0; - // *INDENT-OFF* - ESP_RETURN_ON_ERROR( - i2c_master_read_from_device(ht8574->i2c_num, ht8574->i2c_address, &temp, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Read input reg failed"); - // *INDENT-ON* + + ESP_RETURN_ON_ERROR(i2c_master_receive(ht8574->i2c_handle, &temp, sizeof(temp), -1), TAG, "Read input reg failed"); *value = temp; return ESP_OK; } @@ -98,11 +103,9 @@ static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t valu { esp_io_expander_ht8574_t *ht8574 = (esp_io_expander_ht8574_t *)__containerof(handle, esp_io_expander_ht8574_t, base); value &= 0xff; - uint8_t data = (uint8_t)value; - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(ht8574->i2c_num, ht8574->i2c_address, &data, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write output reg failed"); + + ESP_RETURN_ON_ERROR(i2c_master_transmit(ht8574->i2c_handle, &data, sizeof(data), -1), TAG, "Write output reg failed"); ht8574->regs.output = value; return ESP_OK; } @@ -141,6 +144,7 @@ static esp_err_t del(esp_io_expander_t *handle) { esp_io_expander_ht8574_t *ht8574 = (esp_io_expander_ht8574_t *)__containerof(handle, esp_io_expander_ht8574_t, base); + ESP_RETURN_ON_ERROR(i2c_master_bus_rm_device(ht8574->i2c_handle), TAG, "Remove I2C device failed"); free(ht8574); return ESP_OK; } diff --git a/components/io_expander/esp_io_expander_ht8574/idf_component.yml b/components/io_expander/esp_io_expander_ht8574/idf_component.yml index abe0caa8..3c1d435e 100644 --- a/components/io_expander/esp_io_expander_ht8574/idf_component.yml +++ b/components/io_expander/esp_io_expander_ht8574/idf_component.yml @@ -1,7 +1,8 @@ -dependencies: - esp_io_expander: - version: ^1.0.1 - idf: '>=4.4.2' +version: 2.0.0 description: ESP IO Expander - HT8574 url: https://github.com/espressif/esp-bsp/tree/master/components/io_expander/esp_io_expander_ht8574 -version: 1.0.0 +dependencies: + idf: ">=5.2" + esp_io_expander: + version: "^1.0.1" + public: true diff --git a/components/io_expander/esp_io_expander_ht8574/include/esp_io_expander_ht8574.h b/components/io_expander/esp_io_expander_ht8574/include/esp_io_expander_ht8574.h index 4e41b25e..7387c231 100644 --- a/components/io_expander/esp_io_expander_ht8574/include/esp_io_expander_ht8574.h +++ b/components/io_expander/esp_io_expander_ht8574/include/esp_io_expander_ht8574.h @@ -7,10 +7,8 @@ #pragma once #include - -#include "driver/i2c.h" #include "esp_err.h" - +#include "driver/i2c_master.h" #include "esp_io_expander.h" #ifdef __cplusplus @@ -18,18 +16,16 @@ extern "C" { #endif /** - * @brief Create a new ht8574 IO expander driver - * - * @note The I2C communication should be initialized before use this function + * @brief Create a HT8574 IO expander object * - * @param i2c_num: I2C port num - * @param i2c_address: I2C address of chip - * @param handle: IO expander handle + * @param[in] i2c_bus I2C bus handle. Obtained from `i2c_new_master_bus()` + * @param[in] dev_addr I2C device address of chip. Can be `ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_XXX`. + * @param[out] handle_ret Handle to created IO expander object * * @return * - ESP_OK: Success, otherwise returns ESP_ERR_xxx */ -esp_err_t esp_io_expander_new_i2c_ht8574(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle); +esp_err_t esp_io_expander_new_i2c_ht8574(i2c_master_bus_handle_t i2c_bus, uint32_t dev_addr, esp_io_expander_handle_t *handle_ret); /** * @brief I2C address of the ht8574 diff --git a/components/io_expander/esp_io_expander_ht8574/test_apps/CMakeLists.txt b/components/io_expander/esp_io_expander_ht8574/test_apps/CMakeLists.txt new file mode 100644 index 00000000..b3b589d3 --- /dev/null +++ b/components/io_expander/esp_io_expander_ht8574/test_apps/CMakeLists.txt @@ -0,0 +1,7 @@ +# 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") +set(COMPONENTS main) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_app_ht8574) diff --git a/components/io_expander/esp_io_expander_ht8574/test_apps/main/CMakeLists.txt b/components/io_expander/esp_io_expander_ht8574/test_apps/main/CMakeLists.txt new file mode 100644 index 00000000..e190b042 --- /dev/null +++ b/components/io_expander/esp_io_expander_ht8574/test_apps/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "test_app_ht8574.c" + REQUIRES unity + ) diff --git a/components/io_expander/esp_io_expander_ht8574/test_apps/main/idf_component.yml b/components/io_expander/esp_io_expander_ht8574/test_apps/main/idf_component.yml new file mode 100644 index 00000000..cd82ed1d --- /dev/null +++ b/components/io_expander/esp_io_expander_ht8574/test_apps/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=5.2" + esp_io_expander_ht8574: + version: "*" + override_path: "../../../esp_io_expander_ht8574" diff --git a/components/io_expander/esp_io_expander_ht8574/test_apps/main/test_app_ht8574.c b/components/io_expander/esp_io_expander_ht8574/test_apps/main/test_app_ht8574.c new file mode 100644 index 00000000..473b76de --- /dev/null +++ b/components/io_expander/esp_io_expander_ht8574/test_apps/main/test_app_ht8574.c @@ -0,0 +1,126 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_system.h" +#include "esp_log.h" +#include "driver/i2c_master.h" +#include "esp_io_expander_ht8574.h" + +// Pinout for ESP32-S3-LCD-Ev-Board +#define I2C_MASTER_SCL_IO 48 /*!< gpio number for I2C master clock */ +#define I2C_MASTER_SDA_IO 47 /*!< gpio number for I2C master data */ +#define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master dev */ +#define I2C_ADDRESS ESP_IO_EXPANDER_I2C_HT8574_ADDRESS_000 +/*!< I2C address of slave dev */ + +#define TEST_LOOP_CNT 100 +#define TEST_LOOP_DELAY_MS 50 +#define TEST_OUTPUT_PINS (IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1) +#define TEST_INPUT_PINS (IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3) + +static const char *TAG = "ht8574 test"; +static esp_io_expander_handle_t io_expander = NULL; +static i2c_master_bus_handle_t i2c_handle = NULL; + +static void i2c_bus_init(void) +{ + const i2c_master_bus_config_t bus_config = { + .i2c_port = I2C_MASTER_NUM, + .sda_io_num = I2C_MASTER_SDA_IO, + .scl_io_num = I2C_MASTER_SCL_IO, + .clk_source = I2C_CLK_SRC_DEFAULT, + }; + + esp_err_t ret = i2c_new_master_bus(&bus_config, &i2c_handle); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C install returned error"); +} + +static void i2c_bus_deinit(void) +{ + esp_err_t ret = i2c_del_master_bus(i2c_handle); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C uninstall returned error"); +} + +static void i2c_dev_ht8574_init(void) +{ + esp_err_t ret = esp_io_expander_new_i2c_ht8574(i2c_handle, I2C_ADDRESS, &io_expander); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "HT8574 create returned error"); +} + +static void i2c_dev_ht8574_deinit(void) +{ + esp_err_t ret = esp_io_expander_del(io_expander); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "HT8574 delete returned error"); +} + +TEST_CASE("IO expander ht8574 test", "[ht8574]") +{ + i2c_bus_init(); + i2c_dev_ht8574_init(); + + esp_err_t ret; + /* Test output level function */ + ret = esp_io_expander_set_dir(io_expander, TEST_OUTPUT_PINS, IO_EXPANDER_OUTPUT); + TEST_ASSERT_EQUAL(ESP_OK, ret); + // Print state + ret = esp_io_expander_print_state(io_expander); + TEST_ASSERT_EQUAL(ESP_OK, ret); + for (int i = 0; i < TEST_LOOP_CNT; i++) { + // Set level to 0 + ESP_LOGI(TAG, "Set level to 0"); + ret = esp_io_expander_set_level(io_expander, TEST_OUTPUT_PINS, 0); + TEST_ASSERT_EQUAL(ESP_OK, ret); + vTaskDelay(pdMS_TO_TICKS(TEST_LOOP_DELAY_MS / 2)); + // Set level to 1 + ESP_LOGI(TAG, "Set level to 1"); + ret = esp_io_expander_set_level(io_expander, TEST_OUTPUT_PINS, 1); + TEST_ASSERT_EQUAL(ESP_OK, ret); + vTaskDelay(pdMS_TO_TICKS(TEST_LOOP_DELAY_MS / 2)); + } + + /* Test output level function */ + uint32_t input_level_mask = 0; + ret = esp_io_expander_set_dir(io_expander, TEST_INPUT_PINS, IO_EXPANDER_INPUT); + TEST_ASSERT_EQUAL(ESP_OK, ret); + // Print state + ret = esp_io_expander_print_state(io_expander); + TEST_ASSERT_EQUAL(ESP_OK, ret); + for (int i = 0; i < TEST_LOOP_CNT; i++) { + // Get input level + ret = esp_io_expander_get_level(io_expander, TEST_INPUT_PINS, &input_level_mask); + TEST_ASSERT_EQUAL(ESP_OK, ret); + ESP_LOGI(TAG, "Input level mask: 0x%02" PRIX32, input_level_mask); + vTaskDelay(pdMS_TO_TICKS(TEST_LOOP_DELAY_MS)); + } + + i2c_dev_ht8574_deinit(); + i2c_bus_deinit(); + vTaskDelay(10); // Give FreeRTOS some time to free its resources +} + +#define TEST_MEMORY_LEAK_THRESHOLD (500) + +void setUp(void) +{ + unity_utils_set_leak_level(TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + unity_utils_evaluate_leaks(); +} + +void app_main(void) +{ + unity_run_menu(); +} diff --git a/components/io_expander/esp_io_expander_ht8574/test_apps/sdkconfig.defaults b/components/io_expander/esp_io_expander_ht8574/test_apps/sdkconfig.defaults new file mode 100644 index 00000000..6070c236 --- /dev/null +++ b/components/io_expander/esp_io_expander_ht8574/test_apps/sdkconfig.defaults @@ -0,0 +1,4 @@ +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/components/io_expander/esp_io_expander_tca9554/CMakeLists.txt b/components/io_expander/esp_io_expander_tca9554/CMakeLists.txt index b6bee603..3e9a6c17 100644 --- a/components/io_expander/esp_io_expander_tca9554/CMakeLists.txt +++ b/components/io_expander/esp_io_expander_tca9554/CMakeLists.txt @@ -1 +1 @@ -idf_component_register(SRCS "esp_io_expander_tca9554.c" INCLUDE_DIRS "include" REQUIRES "driver" "esp_io_expander") +idf_component_register(SRCS "esp_io_expander_tca9554.c" INCLUDE_DIRS "include" REQUIRES "driver") diff --git a/components/io_expander/esp_io_expander_tca9554/README.md b/components/io_expander/esp_io_expander_tca9554/README.md index a543df29..43132019 100644 --- a/components/io_expander/esp_io_expander_tca9554/README.md +++ b/components/io_expander/esp_io_expander_tca9554/README.md @@ -20,22 +20,43 @@ Alternatively, you can create `idf_component.yml`. More is in [Espressif's docum ## Example use -Creation of the component. +Creation of the i2c bus. +```c + i2c_master_bus_handle_t i2c_handle = NULL; + const i2c_master_bus_config_t bus_config = { + .i2c_port = I2C_NUM_0, + .sda_io_num = 47, + .scl_io_num = 48, + .clk_source = I2C_CLK_SRC_DEFAULT, + }; + i2c_new_master_bus(&bus_config, &i2c_handle); ``` + +Creation of the component. + +```c esp_io_expander_handle_t io_expander = NULL; - esp_io_expander_new_i2c_tca9554(1, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000, &io_expander); + esp_io_expander_new_i2c_tca9554(i2c_handle, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000, &io_expander); ``` -Set pin 0 and pin 1 with output dircetion and low level: +Print all pins's status to the log: +```c + esp_io_expander_print_state(io_expander); ``` + +Set pin 0 and pin 1 with output dircetion and low level: + +```c esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_OUTPUT); esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 0); ``` -Print all pins's status to the log: +Set pin 2 and pin 3 with input dircetion: -``` - esp_io_expander_print_state(io_expander); +```c + uint32_t pin_levels = 0; + esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, IO_EXPANDER_INPUT); + esp_io_expander_get_level(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, &pin_levels); ``` diff --git a/components/io_expander/esp_io_expander_tca9554/esp_io_expander_tca9554.c b/components/io_expander/esp_io_expander_tca9554/esp_io_expander_tca9554.c index fd649de9..d0540345 100644 --- a/components/io_expander/esp_io_expander_tca9554/esp_io_expander_tca9554.c +++ b/components/io_expander/esp_io_expander_tca9554/esp_io_expander_tca9554.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,12 +7,9 @@ #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" @@ -30,14 +27,16 @@ #define DIR_REG_DEFAULT_VAL (0xff) #define OUT_REG_DEFAULT_VAL (0xff) +/* Default I2C clock speed */ +#define I2C_CLK_SPEED 400000 + /** * @brief Device Structure Type * */ typedef struct { esp_io_expander_t base; - i2c_port_t i2c_num; - uint32_t i2c_address; + i2c_master_dev_handle_t i2c_handle; struct { uint8_t direction; uint8_t output; @@ -54,18 +53,24 @@ static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *v static esp_err_t reset(esp_io_expander_t *handle); static esp_err_t del(esp_io_expander_t *handle); -esp_err_t esp_io_expander_new_i2c_tca9554(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle) +esp_err_t esp_io_expander_new_i2c_tca9554(i2c_master_bus_handle_t i2c_bus, uint32_t dev_addr, esp_io_expander_handle_t *handle_ret) { - ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "Invalid i2c num"); - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); + ESP_RETURN_ON_FALSE(handle_ret != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid handle_ret"); + // Allocate memory for driver object esp_io_expander_tca9554_t *tca9554 = (esp_io_expander_tca9554_t *)calloc(1, sizeof(esp_io_expander_tca9554_t)); - ESP_RETURN_ON_FALSE(tca9554, ESP_ERR_NO_MEM, TAG, "Malloc failed"); + ESP_RETURN_ON_FALSE(tca9554 != NULL, ESP_ERR_NO_MEM, TAG, "Malloc failed"); + + // Add new I2C device + esp_err_t ret = ESP_OK; + const i2c_device_config_t i2c_dev_cfg = { + .device_address = dev_addr, + .scl_speed_hz = I2C_CLK_SPEED, + }; + ESP_GOTO_ON_ERROR(i2c_master_bus_add_device(i2c_bus, &i2c_dev_cfg, &tca9554->i2c_handle), err, TAG, "Add new I2C device failed"); tca9554->base.config.io_count = IO_COUNT; tca9554->base.config.flags.dir_out_bit_zero = 1; - tca9554->i2c_num = i2c_num; - tca9554->i2c_address = i2c_address; tca9554->base.read_input_reg = read_input_reg; tca9554->base.write_output_reg = write_output_reg; tca9554->base.read_output_reg = read_output_reg; @@ -74,11 +79,10 @@ esp_err_t esp_io_expander_new_i2c_tca9554(i2c_port_t i2c_num, uint32_t i2c_addre tca9554->base.del = del; tca9554->base.reset = reset; - esp_err_t ret = ESP_OK; /* Reset configuration and register status */ ESP_GOTO_ON_ERROR(reset(&tca9554->base), err, TAG, "Reset failed"); - *handle = &tca9554->base; + *handle_ret = &tca9554->base; return ESP_OK; err: free(tca9554); @@ -90,11 +94,12 @@ 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* + i2c_master_transmit_receive(tca9554->i2c_handle, (uint8_t[]) { + INPUT_REG_ADDR + }, 1, &temp, sizeof(temp), -1), TAG, + "Read input reg failed" + ); *value = temp; return ESP_OK; } @@ -105,9 +110,7 @@ static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t valu value &= 0xff; uint8_t data[] = {OUTPUT_REG_ADDR, value}; - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(tca9554->i2c_num, tca9554->i2c_address, data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write output reg failed"); + ESP_RETURN_ON_ERROR(i2c_master_transmit(tca9554->i2c_handle, data, sizeof(data), -1), TAG, "Write output reg failed"); tca9554->regs.output = value; return ESP_OK; } @@ -126,9 +129,7 @@ static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t v value &= 0xff; uint8_t data[] = {DIRECTION_REG_ADDR, value}; - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(tca9554->i2c_num, tca9554->i2c_address, data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write direction reg failed"); + ESP_RETURN_ON_ERROR(i2c_master_transmit(tca9554->i2c_handle, data, sizeof(data), -1), TAG, "Write direction reg failed"); tca9554->regs.direction = value; return ESP_OK; } @@ -152,6 +153,7 @@ static esp_err_t del(esp_io_expander_t *handle) { esp_io_expander_tca9554_t *tca9554 = (esp_io_expander_tca9554_t *)__containerof(handle, esp_io_expander_tca9554_t, base); + ESP_RETURN_ON_ERROR(i2c_master_bus_rm_device(tca9554->i2c_handle), TAG, "Remove I2C device failed"); free(tca9554); return ESP_OK; } diff --git a/components/io_expander/esp_io_expander_tca9554/idf_component.yml b/components/io_expander/esp_io_expander_tca9554/idf_component.yml index 4724e567..abdef367 100644 --- a/components/io_expander/esp_io_expander_tca9554/idf_component.yml +++ b/components/io_expander/esp_io_expander_tca9554/idf_component.yml @@ -1,7 +1,8 @@ -version: "1.0.1" +version: "2.0.0" description: ESP IO Expander - TCA9554(A) url: https://github.com/espressif/esp-bsp/tree/master/components/io_expander/esp_io_expander_tca9554 dependencies: - idf: ">=4.4.2" + idf: ">=5.2" esp_io_expander: version: "^1.0.1" + public: true diff --git a/components/io_expander/esp_io_expander_tca9554/include/esp_io_expander_tca9554.h b/components/io_expander/esp_io_expander_tca9554/include/esp_io_expander_tca9554.h index 3ace93c1..192107ce 100644 --- a/components/io_expander/esp_io_expander_tca9554/include/esp_io_expander_tca9554.h +++ b/components/io_expander/esp_io_expander_tca9554/include/esp_io_expander_tca9554.h @@ -12,10 +12,8 @@ #pragma once #include - -#include "driver/i2c.h" #include "esp_err.h" - +#include "driver/i2c_master.h" #include "esp_io_expander.h" #ifdef __cplusplus @@ -23,18 +21,16 @@ extern "C" { #endif /** - * @brief Create a new TCA9554 IO expander driver - * - * @note The I2C communication should be initialized before use this function + * @brief Create a TCA9554(A) IO expander object * - * @param i2c_num: I2C port num - * @param i2c_address: I2C address of chip - * @param handle: IO expander handle + * @param[in] i2c_bus I2C bus handle. Obtained from `i2c_new_master_bus()` + * @param[in] dev_addr I2C device address of chip. Can be `ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_XXX` or `ESP_IO_EXPANDER_I2C_TCA9554A_ADDRESS_XXX`. + * @param[out] handle_ret Handle to created IO expander object * * @return * - ESP_OK: Success, otherwise returns ESP_ERR_xxx */ -esp_err_t esp_io_expander_new_i2c_tca9554(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle); +esp_err_t esp_io_expander_new_i2c_tca9554(i2c_master_bus_handle_t i2c_bus, uint32_t dev_addr, esp_io_expander_handle_t *handle_ret); /** * @brief I2C address of the TCA9554 diff --git a/components/io_expander/esp_io_expander_tca9554/test_apps/CMakeLists.txt b/components/io_expander/esp_io_expander_tca9554/test_apps/CMakeLists.txt new file mode 100644 index 00000000..b3b589d3 --- /dev/null +++ b/components/io_expander/esp_io_expander_tca9554/test_apps/CMakeLists.txt @@ -0,0 +1,7 @@ +# 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") +set(COMPONENTS main) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_app_ht8574) diff --git a/components/io_expander/esp_io_expander_tca9554/test_apps/main/CMakeLists.txt b/components/io_expander/esp_io_expander_tca9554/test_apps/main/CMakeLists.txt new file mode 100644 index 00000000..80cb7414 --- /dev/null +++ b/components/io_expander/esp_io_expander_tca9554/test_apps/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "test_app_tca9554.c" + REQUIRES unity + ) diff --git a/components/io_expander/esp_io_expander_tca9554/test_apps/main/idf_component.yml b/components/io_expander/esp_io_expander_tca9554/test_apps/main/idf_component.yml new file mode 100644 index 00000000..2d761345 --- /dev/null +++ b/components/io_expander/esp_io_expander_tca9554/test_apps/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=5.2" + esp_io_expander_tca9554: + version: "*" + override_path: "../../../esp_io_expander_tca9554" diff --git a/components/io_expander/esp_io_expander_tca9554/test_apps/main/test_app_tca9554.c b/components/io_expander/esp_io_expander_tca9554/test_apps/main/test_app_tca9554.c new file mode 100644 index 00000000..37b13937 --- /dev/null +++ b/components/io_expander/esp_io_expander_tca9554/test_apps/main/test_app_tca9554.c @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_system.h" +#include "esp_log.h" +#include "driver/i2c_master.h" +#include "esp_io_expander_tca9554.h" + +// Pinout for ESP32-S3-LCD-Ev-Board +#define I2C_MASTER_SCL_IO 48 /*!< gpio number for I2C master clock */ +#define I2C_MASTER_SDA_IO 47 /*!< gpio number for I2C master data */ +#define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master dev */ +#define I2C_ADDRESS ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000 +/*!< I2C address of slave dev */ + +#define TEST_LOOP_CNT 10 +#define TEST_LOOP_DELAY_MS 500 +#define TEST_OUTPUT_PINS (IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1) +#define TEST_INPUT_PINS (IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3) + +static const char *TAG = "tca9554 test"; +static esp_io_expander_handle_t io_expander = NULL; +static i2c_master_bus_handle_t i2c_handle = NULL; + +static void i2c_bus_init(void) +{ + const i2c_master_bus_config_t bus_config = { + .i2c_port = I2C_MASTER_NUM, + .sda_io_num = I2C_MASTER_SDA_IO, + .scl_io_num = I2C_MASTER_SCL_IO, + .clk_source = I2C_CLK_SRC_DEFAULT, + }; + + esp_err_t ret = i2c_new_master_bus(&bus_config, &i2c_handle); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C install returned error"); +} + +static void i2c_bus_deinit(void) +{ + esp_err_t ret = i2c_del_master_bus(i2c_handle); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C uninstall returned error"); +} + +static void i2c_dev_tca9554_init(void) +{ + esp_err_t ret = esp_io_expander_new_i2c_tca9554(i2c_handle, I2C_ADDRESS, &io_expander); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "TCA9554 create returned error"); +} + +static void i2c_dev_tca9554_deinit(void) +{ + esp_err_t ret = esp_io_expander_del(io_expander); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "TCA9554 delete returned error"); +} + +TEST_CASE("IO expander tca9554 test", "[tca9554]") +{ + i2c_bus_init(); + i2c_dev_tca9554_init(); + + esp_err_t ret; + /* Test output level function */ + ret = esp_io_expander_set_dir(io_expander, TEST_OUTPUT_PINS, IO_EXPANDER_OUTPUT); + TEST_ASSERT_EQUAL(ESP_OK, ret); + // Print state + ret = esp_io_expander_print_state(io_expander); + TEST_ASSERT_EQUAL(ESP_OK, ret); + for (int i = 0; i < TEST_LOOP_CNT; i++) { + // Set level to 0 + ESP_LOGI(TAG, "Set level to 0"); + ret = esp_io_expander_set_level(io_expander, TEST_OUTPUT_PINS, 0); + TEST_ASSERT_EQUAL(ESP_OK, ret); + vTaskDelay(pdMS_TO_TICKS(TEST_LOOP_DELAY_MS / 2)); + // Set level to 1 + ESP_LOGI(TAG, "Set level to 1"); + ret = esp_io_expander_set_level(io_expander, TEST_OUTPUT_PINS, 1); + TEST_ASSERT_EQUAL(ESP_OK, ret); + vTaskDelay(pdMS_TO_TICKS(TEST_LOOP_DELAY_MS / 2)); + } + + /* Test output level function */ + uint32_t input_level_mask = 0; + ret = esp_io_expander_set_dir(io_expander, TEST_INPUT_PINS, IO_EXPANDER_INPUT); + TEST_ASSERT_EQUAL(ESP_OK, ret); + // Print state + ret = esp_io_expander_print_state(io_expander); + TEST_ASSERT_EQUAL(ESP_OK, ret); + for (int i = 0; i < TEST_LOOP_CNT; i++) { + // Get input level + ret = esp_io_expander_get_level(io_expander, TEST_INPUT_PINS, &input_level_mask); + TEST_ASSERT_EQUAL(ESP_OK, ret); + ESP_LOGI(TAG, "Input level mask: 0x%02" PRIX32, input_level_mask); + vTaskDelay(pdMS_TO_TICKS(TEST_LOOP_DELAY_MS)); + } + + i2c_dev_tca9554_deinit(); + i2c_bus_deinit(); + vTaskDelay(10); // Give FreeRTOS some time to free its resources +} + +#define TEST_MEMORY_LEAK_THRESHOLD (500) + +void setUp(void) +{ + unity_utils_set_leak_level(TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + unity_utils_evaluate_leaks(); +} + +void app_main(void) +{ + /** + * ________ ______ ______ ______ _______ _______ __ __ ___ ______ ___ + * | \ / \ / \ / \ | \ | \| \ | \ / \ / \ | \ + * \$$$$$$$$| $$$$$$\| $$$$$$\| $$$$$$\| $$$$$$$ | $$$$$$$| $$ | $$ / $$$| $$$$$$\ \$$$\ + * | $$ | $$ \$$| $$__| $$| $$__/ $$| $$____ | $$____ | $$__| $$| $$ | $$__| $$ \$$\ + * | $$ | $$ | $$ $$ \$$ $$| $$ \ | $$ \| $$ $$| $$ | $$ $$ | $$ + * | $$ | $$ __ | $$$$$$$$ _\$$$$$$$ \$$$$$$$\ \$$$$$$$\\$$$$$$$$| $$ | $$$$$$$$ | $$ + * | $$ | $$__/ \| $$ | $$| \__/ $$| \__| $$| \__| $$ | $$ \$$\_ | $$ | $$ _/ $$ + * | $$ \$$ $$| $$ | $$ \$$ $$ \$$ $$ \$$ $$ | $$ \$$ \| $$ | $$| $$ + * \$$ \$$$$$$ \$$ \$$ \$$$$$$ \$$$$$$ \$$$$$$ \$$ \$$$ \$$ \$$ \$$$ + * + */ + printf(" ________ ______ ______ ______ _______ _______ __ __ ___ ______ ___\r\n"); + printf("| \\ / \\ / \\ / \\ | \\ | \\| \\ | \\ / \\ / \\ | \\\r\n"); + printf(" \\$$$$$$$$| $$$$$$\\| $$$$$$\\| $$$$$$\\| $$$$$$$ | $$$$$$$| $$ | $$ / $$$| $$$$$$\\ \\$$$\\\r\n"); + printf(" | $$ | $$ \\$$| $$__| $$| $$__/ $$| $$____ | $$____ | $$__| $$| $$ | $$__| $$ \\$$\\\r\n"); + printf(" | $$ | $$ | $$ $$ \\$$ $$| $$ \\ | $$ \\| $$ $$| $$ | $$ $$ | $$\r\n"); + printf(" | $$ | $$ __ | $$$$$$$$ _\\$$$$$$$ \\$$$$$$$\\ \\$$$$$$$\\\\$$$$$$$$| $$ | $$$$$$$$ | $$\r\n"); + printf(" | $$ | $$__/ \\| $$ | $$| \\__/ $$| \\__| $$| \\__| $$ | $$ \\$$\\_ | $$ | $$ _/ $$\r\n"); + printf(" | $$ \\$$ $$| $$ | $$ \\$$ $$ \\$$ $$ \\$$ $$ | $$ \\$$ \\| $$ | $$| $$\r\n"); + printf(" \\$$ \\$$$$$$ \\$$ \\$$ \\$$$$$$ \\$$$$$$ \\$$$$$$ \\$$ \\$$$ \\$$ \\$$ \\$$$\r\n"); + unity_run_menu(); +} diff --git a/components/io_expander/esp_io_expander_tca9554/test_apps/sdkconfig.defaults b/components/io_expander/esp_io_expander_tca9554/test_apps/sdkconfig.defaults new file mode 100644 index 00000000..6070c236 --- /dev/null +++ b/components/io_expander/esp_io_expander_tca9554/test_apps/sdkconfig.defaults @@ -0,0 +1,4 @@ +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 diff --git a/components/io_expander/esp_io_expander_tca95xx_16bit/README.md b/components/io_expander/esp_io_expander_tca95xx_16bit/README.md index 4f554dc1..488fbd9d 100644 --- a/components/io_expander/esp_io_expander_tca95xx_16bit/README.md +++ b/components/io_expander/esp_io_expander_tca95xx_16bit/README.md @@ -21,22 +21,43 @@ Alternatively, you can create `idf_component.yml`. More is in [Espressif's docum ## Example use -Creation of the component. +Creation of the i2c bus. +```c + i2c_master_bus_handle_t i2c_handle = NULL; + const i2c_master_bus_config_t bus_config = { + .i2c_port = I2C_NUM_0, + .sda_io_num = 47, + .scl_io_num = 48, + .clk_source = I2C_CLK_SRC_DEFAULT, + }; + i2c_new_master_bus(&bus_config, &i2c_handle); ``` + +Creation of the component. + +```c esp_io_expander_handle_t io_expander = NULL; - esp_io_expander_new_i2c_tca95xx_16bit(1, ESP_IO_EXPANDER_I2C_TCA9539_ADDRESS_00, &io_expander); + esp_io_expander_new_i2c_tca95xx_16bit(i2c_handle, ESP_IO_EXPANDER_I2C_TCA9539_ADDRESS_00, &io_expander); ``` -Set pin 0 and pin 1 with output dircetion and low level: +Print all pins's status to the log: +```c + esp_io_expander_print_state(io_expander); ``` + +Set pin 0 and pin 1 with output dircetion and low level: + +```c esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_OUTPUT); esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 0); ``` -Print all pins's status to the log: +Set pin 2 and pin 3 with input dircetion: -``` - esp_io_expander_print_state(io_expander); +```c + uint32_t pin_levels = 0; + esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, IO_EXPANDER_INPUT); + esp_io_expander_get_level(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, &pin_levels); ``` diff --git a/components/io_expander/esp_io_expander_tca95xx_16bit/esp_io_expander_tca95xx_16bit.c b/components/io_expander/esp_io_expander_tca95xx_16bit/esp_io_expander_tca95xx_16bit.c index 63322bbd..1ed60e9e 100644 --- a/components/io_expander/esp_io_expander_tca95xx_16bit/esp_io_expander_tca95xx_16bit.c +++ b/components/io_expander/esp_io_expander_tca95xx_16bit/esp_io_expander_tca95xx_16bit.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,12 +7,9 @@ #include #include #include - -#include "driver/i2c.h" #include "esp_bit_defs.h" #include "esp_check.h" #include "esp_log.h" - #include "esp_io_expander.h" #include "esp_io_expander_tca95xx_16bit.h" @@ -30,14 +27,16 @@ #define DIR_REG_DEFAULT_VAL (0xffff) #define OUT_REG_DEFAULT_VAL (0xffff) +/* Default I2C clock speed */ +#define I2C_CLK_SPEED 400000 + /** * @brief Device Structure Type * */ typedef struct { esp_io_expander_t base; - i2c_port_t i2c_num; - uint32_t i2c_address; + i2c_master_dev_handle_t i2c_handle; struct { uint16_t direction; uint16_t output; @@ -54,18 +53,24 @@ static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *v static esp_err_t reset(esp_io_expander_t *handle); static esp_err_t del(esp_io_expander_t *handle); -esp_err_t esp_io_expander_new_i2c_tca95xx_16bit(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle) +esp_err_t esp_io_expander_new_i2c_tca95xx_16bit(i2c_master_bus_handle_t i2c_bus, uint32_t dev_addr, esp_io_expander_handle_t *handle_ret) { - ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "Invalid i2c num"); - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); + ESP_RETURN_ON_FALSE(handle_ret != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid handle_ret"); + // Allocate memory for driver object esp_io_expander_tca95xx_16bit_t *tca = (esp_io_expander_tca95xx_16bit_t *)calloc(1, sizeof(esp_io_expander_tca95xx_16bit_t)); ESP_RETURN_ON_FALSE(tca, ESP_ERR_NO_MEM, TAG, "Malloc failed"); + // Add new I2C device + esp_err_t ret = ESP_OK; + const i2c_device_config_t i2c_dev_cfg = { + .device_address = dev_addr, + .scl_speed_hz = I2C_CLK_SPEED, + }; + ESP_GOTO_ON_ERROR(i2c_master_bus_add_device(i2c_bus, &i2c_dev_cfg, &tca->i2c_handle), err, TAG, "Add new I2C device failed"); + tca->base.config.io_count = IO_COUNT; tca->base.config.flags.dir_out_bit_zero = 1; - tca->i2c_num = i2c_num; - tca->i2c_address = i2c_address; tca->base.read_input_reg = read_input_reg; tca->base.write_output_reg = write_output_reg; tca->base.read_output_reg = read_output_reg; @@ -74,11 +79,10 @@ esp_err_t esp_io_expander_new_i2c_tca95xx_16bit(i2c_port_t i2c_num, uint32_t i2c tca->base.del = del; tca->base.reset = reset; - esp_err_t ret = ESP_OK; /* Reset configuration and register status */ ESP_GOTO_ON_ERROR(reset(&tca->base), err, TAG, "Reset failed"); - *handle = &tca->base; + *handle_ret = &tca->base; return ESP_OK; err: free(tca); @@ -90,11 +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}; - // *INDENT-OFF* ESP_RETURN_ON_ERROR( - i2c_master_write_read_device(tca->i2c_num, tca->i2c_address, (uint8_t[]){INPUT_REG_ADDR}, 1, (uint8_t*)&temp, 2, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Read input reg failed"); - // *INDENT-ON* + i2c_master_transmit_receive(tca->i2c_handle, (uint8_t[]) { + INPUT_REG_ADDR + }, 1, temp, sizeof(temp), -1), TAG, + "Read input reg failed" + ); *value = (((uint32_t)temp[1]) << 8) | (temp[0]); return ESP_OK; } @@ -105,9 +110,7 @@ static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t valu value &= 0xffff; uint8_t data[] = {OUTPUT_REG_ADDR, value & 0xff, value >> 8}; - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(tca->i2c_num, tca->i2c_address, data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write output reg failed"); + ESP_RETURN_ON_ERROR(i2c_master_transmit(tca->i2c_handle, data, sizeof(data), -1), TAG, "Write output reg failed"); tca->regs.output = value; return ESP_OK; } @@ -126,9 +129,7 @@ static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t v value &= 0xffff; uint8_t data[] = {DIRECTION_REG_ADDR, value & 0xff, value >> 8}; - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(tca->i2c_num, tca->i2c_address, data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write direction reg failed"); + ESP_RETURN_ON_ERROR(i2c_master_transmit(tca->i2c_handle, data, sizeof(data), -1), TAG, "Write direction reg failed"); tca->regs.direction = value; return ESP_OK; } @@ -152,6 +153,7 @@ static esp_err_t del(esp_io_expander_t *handle) { esp_io_expander_tca95xx_16bit_t *tca = (esp_io_expander_tca95xx_16bit_t *)__containerof(handle, esp_io_expander_tca95xx_16bit_t, base); + ESP_RETURN_ON_ERROR(i2c_master_bus_rm_device(tca->i2c_handle), TAG, "Remove I2C device failed"); free(tca); return ESP_OK; } diff --git a/components/io_expander/esp_io_expander_tca95xx_16bit/idf_component.yml b/components/io_expander/esp_io_expander_tca95xx_16bit/idf_component.yml index ab73adf7..6933d84f 100644 --- a/components/io_expander/esp_io_expander_tca95xx_16bit/idf_component.yml +++ b/components/io_expander/esp_io_expander_tca95xx_16bit/idf_component.yml @@ -1,7 +1,8 @@ -dependencies: - esp_io_expander: - version: ^1.0.1 - idf: '>=4.4.2' +version: 2.0.0 description: ESP IO Expander - tca9539 and tca9555 url: https://github.com/espressif/esp-bsp/tree/master/components/io_expander/esp_io_expander_tca95xx_16bit -version: 1.0.1 +dependencies: + idf: ">=5.2" + esp_io_expander: + version: "^1.0.1" + public: true diff --git a/components/io_expander/esp_io_expander_tca95xx_16bit/include/esp_io_expander_tca95xx_16bit.h b/components/io_expander/esp_io_expander_tca95xx_16bit/include/esp_io_expander_tca95xx_16bit.h index 55c4eae7..ee003716 100644 --- a/components/io_expander/esp_io_expander_tca95xx_16bit/include/esp_io_expander_tca95xx_16bit.h +++ b/components/io_expander/esp_io_expander_tca95xx_16bit/include/esp_io_expander_tca95xx_16bit.h @@ -7,10 +7,8 @@ #pragma once #include - -#include "driver/i2c.h" #include "esp_err.h" - +#include "driver/i2c_master.h" #include "esp_io_expander.h" #ifdef __cplusplus @@ -18,18 +16,16 @@ extern "C" { #endif /** - * @brief Create a new TCA95xx_16bit IO expander driver - * - * @note The I2C communication should be initialized before use this function + * @brief Create a TCA95539 or TCA9555 IO expander object * - * @param i2c_num: I2C port num - * @param i2c_address: I2C address of chip (\see esp_io_expander_tca_95xx_16bit_address) - * @param handle: IO expander handle + * @param[in] i2c_bus I2C bus handle. Obtained from `i2c_new_master_bus()` + * @param[in] dev_addr I2C device address of chip. Can be `ESP_IO_EXPANDER_I2C_TCA9539_ADDRESS_XX` or `ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_XXX`. + * @param[out] handle_ret Handle to created IO expander object * * @return * - ESP_OK: Success, otherwise returns ESP_ERR_xxx */ -esp_err_t esp_io_expander_new_i2c_tca95xx_16bit(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle); +esp_err_t esp_io_expander_new_i2c_tca95xx_16bit(i2c_master_bus_handle_t i2c_bus, uint32_t dev_addr, esp_io_expander_handle_t *handle_ret); /** * @brief I2C address of the TCA9539 or TCA9555 diff --git a/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/CMakeLists.txt b/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/CMakeLists.txt new file mode 100644 index 00000000..b3b589d3 --- /dev/null +++ b/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/CMakeLists.txt @@ -0,0 +1,7 @@ +# 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") +set(COMPONENTS main) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_app_ht8574) diff --git a/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/main/CMakeLists.txt b/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/main/CMakeLists.txt new file mode 100644 index 00000000..b98ac929 --- /dev/null +++ b/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "test_app_tca95xx_16bit.c" + REQUIRES unity + ) diff --git a/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/main/idf_component.yml b/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/main/idf_component.yml new file mode 100644 index 00000000..46d5a738 --- /dev/null +++ b/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=5.2" + esp_io_expander_tca95xx_16bit: + version: "*" + override_path: "../../../esp_io_expander_tca95xx_16bit" diff --git a/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/main/test_app_tca95xx_16bit.c b/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/main/test_app_tca95xx_16bit.c new file mode 100644 index 00000000..ef3cf829 --- /dev/null +++ b/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/main/test_app_tca95xx_16bit.c @@ -0,0 +1,148 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_system.h" +#include "esp_log.h" +#include "driver/i2c_master.h" +#include "esp_io_expander_tca95xx_16bit.h" + +// Pinout for ESP32-S3-LCD-Ev-Board +#define I2C_MASTER_SCL_IO 48 /*!< gpio number for I2C master clock */ +#define I2C_MASTER_SDA_IO 47 /*!< gpio number for I2C master data */ +#define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master dev */ +#define I2C_ADDRESS ESP_IO_EXPANDER_I2C_TCA9539_ADDRESS_00 +/*!< I2C address of slave dev */ + +#define TEST_LOOP_CNT 10 +#define TEST_LOOP_DELAY_MS 500 +#define TEST_OUTPUT_PINS (IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1) +#define TEST_INPUT_PINS (IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3) + +static const char *TAG = "tca95xx_16bit test"; +static esp_io_expander_handle_t io_expander = NULL; +static i2c_master_bus_handle_t i2c_handle = NULL; + +static void i2c_bus_init(void) +{ + const i2c_master_bus_config_t bus_config = { + .i2c_port = I2C_MASTER_NUM, + .sda_io_num = I2C_MASTER_SDA_IO, + .scl_io_num = I2C_MASTER_SCL_IO, + .clk_source = I2C_CLK_SRC_DEFAULT, + }; + + esp_err_t ret = i2c_new_master_bus(&bus_config, &i2c_handle); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C install returned error"); +} + +static void i2c_bus_deinit(void) +{ + esp_err_t ret = i2c_del_master_bus(i2c_handle); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C uninstall returned error"); +} + +static void i2c_dev_tca95xx_16bit_init(void) +{ + esp_err_t ret = esp_io_expander_new_i2c_tca95xx_16bit(i2c_handle, I2C_ADDRESS, &io_expander); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "TCA9554 create returned error"); +} + +static void i2c_dev_tca95xx_16bit_deinit(void) +{ + esp_err_t ret = esp_io_expander_del(io_expander); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "TCA9554 delete returned error"); +} + +TEST_CASE("IO expander tca95xx_16bit test", "[tca95xx_16bit]") +{ + i2c_bus_init(); + i2c_dev_tca95xx_16bit_init(); + + esp_err_t ret; + /* Test output level function */ + ret = esp_io_expander_set_dir(io_expander, TEST_OUTPUT_PINS, IO_EXPANDER_OUTPUT); + TEST_ASSERT_EQUAL(ESP_OK, ret); + // Print state + ret = esp_io_expander_print_state(io_expander); + TEST_ASSERT_EQUAL(ESP_OK, ret); + for (int i = 0; i < TEST_LOOP_CNT; i++) { + // Set level to 0 + ESP_LOGI(TAG, "Set level to 0"); + ret = esp_io_expander_set_level(io_expander, TEST_OUTPUT_PINS, 0); + TEST_ASSERT_EQUAL(ESP_OK, ret); + vTaskDelay(pdMS_TO_TICKS(TEST_LOOP_DELAY_MS / 2)); + // Set level to 1 + ESP_LOGI(TAG, "Set level to 1"); + ret = esp_io_expander_set_level(io_expander, TEST_OUTPUT_PINS, 1); + TEST_ASSERT_EQUAL(ESP_OK, ret); + vTaskDelay(pdMS_TO_TICKS(TEST_LOOP_DELAY_MS / 2)); + } + + /* Test output level function */ + uint32_t input_level_mask = 0; + ret = esp_io_expander_set_dir(io_expander, TEST_INPUT_PINS, IO_EXPANDER_INPUT); + TEST_ASSERT_EQUAL(ESP_OK, ret); + // Print state + ret = esp_io_expander_print_state(io_expander); + TEST_ASSERT_EQUAL(ESP_OK, ret); + for (int i = 0; i < TEST_LOOP_CNT; i++) { + // Get input level + ret = esp_io_expander_get_level(io_expander, TEST_INPUT_PINS, &input_level_mask); + TEST_ASSERT_EQUAL(ESP_OK, ret); + ESP_LOGI(TAG, "Input level mask: 0x%02" PRIX32, input_level_mask); + vTaskDelay(pdMS_TO_TICKS(TEST_LOOP_DELAY_MS)); + } + + i2c_dev_tca95xx_16bit_deinit(); + i2c_bus_deinit(); + vTaskDelay(10); // Give FreeRTOS some time to free its resources +} + +#define TEST_MEMORY_LEAK_THRESHOLD (500) + +void setUp(void) +{ + unity_utils_set_leak_level(TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + unity_utils_evaluate_leaks(); +} + +void app_main(void) +{ + /** + * ________ ______ ______ ______ _______ __ __ __ __ __ ______ _______ ______ ________ + * | \ / \ / \ / \ | \ | \ | \| \ | \ _/ \ / \ | \ | \| \ + * \$$$$$$$$| $$$$$$\| $$$$$$\| $$$$$$\| $$$$$$$ | $$ | $$| $$ | $$ | $$ | $$$$$$\| $$$$$$$\ \$$$$$$ \$$$$$$$$ + * | $$ | $$ \$$| $$__| $$| $$__/ $$| $$____ \$$\/ $$ \$$\/ $$ \$$$$ | $$___\$$| $$__/ $$ | $$ | $$ + * | $$ | $$ | $$ $$ \$$ $$| $$ \ >$$ $$ >$$ $$ | $$ | $$ \ | $$ $$ | $$ | $$ + * | $$ | $$ __ | $$$$$$$$ _\$$$$$$$ \$$$$$$$\ / $$$$\ / $$$$\ | $$ | $$$$$$$\| $$$$$$$\ | $$ | $$ + * | $$ | $$__/ \| $$ | $$| \__/ $$| \__| $$| $$ \$$\| $$ \$$\ _| $$_| $$__/ $$| $$__/ $$ _| $$_ | $$ + * | $$ \$$ $$| $$ | $$ \$$ $$ \$$ $$| $$ | $$| $$ | $$ ______| $$ \\$$ $$| $$ $$| $$ \ | $$ + * \$$ \$$$$$$ \$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$ \$$| \\$$$$$$ \$$$$$$ \$$$$$$$ \$$$$$$ \$$ + * \$$$$$$ + */ + printf(" ________ ______ ______ ______ _______ __ __ __ __ __ ______ _______ ______ ________\r\n"); + printf("| \\ / \\ / \\ / \\ | \\ | \\ | \\| \\ | \\ _/ \\ / \\ | \\ | \\| \\\r\n"); + printf(" \\$$$$$$$$| $$$$$$\\| $$$$$$\\| $$$$$$\\| $$$$$$$ | $$ | $$| $$ | $$ | $$ | $$$$$$\\| $$$$$$$\\ \\$$$$$$ \\$$$$$$$$\r\n"); + printf(" | $$ | $$ \\$$| $$__| $$| $$__/ $$| $$____ \\$$\\/ $$ \\$$\\/ $$ \\$$$$ | $$___\\$$| $$__/ $$ | $$ | $$\r\n"); + printf(" | $$ | $$ | $$ $$ \\$$ $$| $$ \\ >$$ $$ >$$ $$ | $$ | $$ \\ | $$ $$ | $$ | $$\r\n"); + printf(" | $$ | $$ __ | $$$$$$$$ _\\$$$$$$$ \\$$$$$$$\\ / $$$$\\ / $$$$\\ | $$ | $$$$$$$\\| $$$$$$$\\ | $$ | $$\r\n"); + printf(" | $$ | $$__/ \\| $$ | $$| \\__/ $$| \\__| $$| $$ \\$$\\| $$ \\$$\\ _| $$_| $$__/ $$| $$__/ $$ _| $$_ | $$\r\n"); + printf(" | $$ \\$$ $$| $$ | $$ \\$$ $$ \\$$ $$| $$ | $$| $$ | $$ ______| $$ \\\\$$ $$| $$ $$| $$ \\ | $$\r\n"); + printf(" \\$$ \\$$$$$$ \\$$ \\$$ \\$$$$$$ \\$$$$$$ \\$$ \\$$ \\$$ \\$$| \\\\$$$$$$ \\$$$$$$ \\$$$$$$$ \\$$$$$$ \\$$\r\n"); + printf(" \\$$$$$$\r\n"); + unity_run_menu(); +} diff --git a/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/sdkconfig.defaults b/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/sdkconfig.defaults new file mode 100644 index 00000000..6070c236 --- /dev/null +++ b/components/io_expander/esp_io_expander_tca95xx_16bit/test_apps/sdkconfig.defaults @@ -0,0 +1,4 @@ +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP_TASK_WDT_EN=n +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096