Skip to content

Commit

Permalink
refactor(io_expander): support i2c ng
Browse files Browse the repository at this point in the history
  • Loading branch information
Lzw655 authored and tore-espressif committed Nov 2, 2024
1 parent 0064ab9 commit 93d0694
Show file tree
Hide file tree
Showing 29 changed files with 693 additions and 141 deletions.
24 changes: 24 additions & 0 deletions .build-test-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,27 @@ components/icm42670:
components/qma6100p:
depends_filepatterns:
- "components/qma6100p/**"

components/io_expander/esp_io_expander_ht8574:
depends_filepatterns:
- "components/io_expander/esp_io_expander/**"
- "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/**"
- "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/**"
- "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
33 changes: 27 additions & 6 deletions components/io_expander/esp_io_expander_ht8574/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
```
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <inttypes.h>
#include <string.h>
#include <stdlib.h>

#include "driver/i2c.h"
#include "esp_bit_defs.h"
#include "esp_check.h"
#include "esp_log.h"

#include "esp_io_expander.h"
#include "esp_io_expander_ht8574.h"

/* Timeout of each I2C communication */
#define I2C_TIMEOUT_MS (10)
/* I2C communication related */
#define I2C_TIMEOUT_MS (1000)
#define I2C_CLK_SPEED (400000)

#define IO_COUNT (8)

Expand All @@ -31,8 +29,7 @@
*/
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;
Expand All @@ -49,18 +46,25 @@ static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *v
static esp_err_t reset(esp_io_expander_t *handle);
static esp_err_t del(esp_io_expander_t *handle);

esp_err_t esp_io_expander_new_i2c_ht8574(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle)
esp_err_t esp_io_expander_new_i2c_ht8574(i2c_master_bus_handle_t i2c_bus, 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;
Expand All @@ -69,12 +73,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;
Expand All @@ -83,13 +87,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), I2C_TIMEOUT_MS), TAG, "Read input reg failed");
*value = temp;
return ESP_OK;
}
Expand All @@ -98,11 +98,9 @@ static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t valu
{
esp_io_expander_ht8574_t *ht8574 = (esp_io_expander_ht8574_t *)__containerof(handle, esp_io_expander_ht8574_t, base);
value &= 0xff;

uint8_t data = (uint8_t)value;
ESP_RETURN_ON_ERROR(
i2c_master_write_to_device(ht8574->i2c_num, ht8574->i2c_address, &data, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)),
TAG, "Write output reg failed");

ESP_RETURN_ON_ERROR(i2c_master_transmit(ht8574->i2c_handle, &data, sizeof(data), I2C_TIMEOUT_MS), TAG, "Write output reg failed");
ht8574->regs.output = value;
return ESP_OK;
}
Expand Down Expand Up @@ -141,6 +139,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;
}
11 changes: 6 additions & 5 deletions components/io_expander/esp_io_expander_ht8574/idf_component.yml
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,25 @@
#pragma once

#include <stdint.h>

#include "driver/i2c.h"
#include "esp_err.h"

#include "driver/i2c_master.h"
#include "esp_io_expander.h"

#ifdef __cplusplus
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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +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(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_app_ht8574)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
idf_component_register(
SRCS "test_app_ht8574.c"
REQUIRES unity
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## IDF Component Manager Manifest File
dependencies:
idf: ">=5.2"
esp_io_expander_ht8574:
version: "*"
override_path: "../../../esp_io_expander_ht8574"
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdio.h>
#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();
}
Loading

0 comments on commit 93d0694

Please sign in to comment.