Skip to content

Commit

Permalink
feat(ch422g): support OCx output
Browse files Browse the repository at this point in the history
Closes #5
  • Loading branch information
Lzw655 committed Oct 9, 2024
1 parent e5378da commit ec03f61
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 43 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# ChangeLog

## v0.0.4 - 2024-10-09

### Enhancements:

* feat(ch422g): support OCx output

## v0.0.3 - 2024-05-07

### Enhancements:
Expand Down
11 changes: 11 additions & 0 deletions examples/TestFunctions/TestFunctions.ino
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ void setup()
expander->init();
expander->begin();

/* For CH422G */
/**
* 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 |
*/
// static_cast<ESP_IOExpander_CH422G *>(expander)->enableOC_PushPull();
// static_cast<ESP_IOExpander_CH422G *>(expander)->enableOC_OpenDrain();

Serial.println("Original status:");
expander->printStatus();

Expand Down
4 changes: 2 additions & 2 deletions library.properties
Original file line number Diff line number Diff line change
@@ -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
Expand Down
137 changes: 107 additions & 30 deletions src/chip/CH422G.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,28 @@
/* 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)
#define CH422G_REG_WR_SET (0x48)
#define CH422G_REG_WR_OC (0x46)
#define CH422G_REG_WR_IO (0x70)
#define CH422G_REG_RD_IO (0x4D)

/* Default register value on power-up */
#define DIR_REG_DEFAULT_VAL (0xff)
#define OUT_REG_DEFAULT_VAL (0xdf)
// *INDENT-OFF*
#define REG_WR_SET_DEFAULT_VAL (0x00UL) // Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
// |---------|---|---|---------|---|----------|---|---------|
// Value: | [SLEEP] | 0 | 0 | [OD_EN] | 0 | [A_SCAN] | 0 | [IO_OE] |
// |---------|---|---|---------|---|----------|---|---------|
// Default: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
// *INDENT-OFF*
#define REG_WR_OC_DEFAULT_VAL (0x00UL)
#define REG_WR_IO_DEFAULT_VAL (0x00UL)
#define REG_OUT_DEFAULT_VAL (REG_WR_OC_DEFAULT_VAL << 8 | REG_WR_IO_DEFAULT_VAL)

#define REG_WR_SET_BIT_IO_OE (1 << 0)
#define REG_WR_SET_BIT_OD_EN (1 << 4)

/**
* @brief Device Structure Type
Expand All @@ -38,8 +51,9 @@ 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;

Expand All @@ -62,6 +76,32 @@ void ESP_IOExpander_CH422G::begin(void)
CHECK_ERROR_RETURN(esp_io_expander_new_i2c_ch422g(i2c_id, i2c_address, &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[2] = {CH422G_REG_WR_SET, (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->i2c_address, data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS))
);
ch422g->regs.wr_set = data[1];
}

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[2] = {CH422G_REG_WR_SET, (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->i2c_address, data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS))
);
ch422g->regs.wr_set = data[1];
}

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);
Expand All @@ -82,7 +122,9 @@ static esp_err_t esp_io_expander_new_i2c_ch422g(i2c_port_t i2c_num, uint32_t i2c
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;
Expand All @@ -107,65 +149,99 @@ 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;
uint8_t reg = CH422G_REG_RD_IO;

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_write_read_device(ch422g->i2c_num, ch422g->i2c_address, &reg, 1, &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;
uint8_t data[2] = {};

// WR-OC
if (wr_oc_data) {
data[0] = CH422G_REG_WR_OC;
data[1] = wr_oc_data;
ESP_RETURN_ON_ERROR(
i2c_master_write_to_device(ch422g->i2c_num, ch422g->i2c_address, data, sizeof(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) {
data[0] = CH422G_REG_WR_IO;
data[1] = wr_io_data;
ESP_RETURN_ON_ERROR(
i2c_master_write_to_device(ch422g->i2c_num, ch422g->i2c_address, data, sizeof(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;
}

static esp_err_t read_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 = 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[2] = {CH422G_REG_WR_SET, ch422g->regs.wr_set};

if (value > 0) {
data[1] |= REG_WR_SET_BIT_IO_OE;
} else {
data[1] &= ~REG_WR_SET_BIT_IO_OE;
}

// WR-SET
ESP_RETURN_ON_ERROR(
i2c_master_write_to_device(ch422g->i2c_num, ch422g->i2c_address, data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)),
TAG, "Write WR_SET reg failed"
);
ch422g->regs.wr_set = data[1];

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_WR_SET_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;
}

Expand All @@ -174,5 +250,6 @@ static esp_err_t del(esp_io_expander_t *handle)
esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base);

free(ch422g);

return ESP_OK;
}
12 changes: 12 additions & 0 deletions src/chip/CH422G.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ class ESP_IOExpander_CH422G: public ESP_IOExpander {
*
*/
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);
};

/**
Expand Down
20 changes: 9 additions & 11 deletions test_apps/main/test_ESP_IOExpander.cpp
Original file line number Diff line number Diff line change
@@ -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
*/
Expand All @@ -16,27 +16,25 @@

#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);

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();
Expand All @@ -45,15 +43,15 @@ 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();
expander->del();
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();
Expand Down

0 comments on commit ec03f61

Please sign in to comment.