Skip to content

Commit

Permalink
TCA95xx_16bit (BSP-326) (espressif#174)
Browse files Browse the repository at this point in the history
* TCA95xx_16bit

espressif#137
  • Loading branch information
hephaisto authored May 31, 2023
1 parent a402bd3 commit 6f02d62
Show file tree
Hide file tree
Showing 7 changed files with 488 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/upload_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ jobs:
components/bh1750;components/ds18b20;components/es8311;components/es7210;components/fbm320;components/hts221;components/mag3110;components/mpu6050;components/ssd1306;components/esp_lvgl_port;
components/lcd_touch/esp_lcd_touch;components/lcd_touch/esp_lcd_touch_ft5x06;components/lcd_touch/esp_lcd_touch_gt911;components/lcd_touch/esp_lcd_touch_tt21100;components/lcd_touch/esp_lcd_touch_gt1151;components/lcd_touch/esp_lcd_touch_cst816s;
components/lcd/esp_lcd_gc9a01;components/lcd/esp_lcd_ili9341;components/lcd/esp_lcd_ra8875;components/lcd_touch/esp_lcd_touch_stmpe610;components/lcd/esp_lcd_sh1107;components/lcd/esp_lcd_st7796;
components/io_expander/esp_io_expander;components/io_expander/esp_io_expander_tca9554;
components/io_expander/esp_io_expander;components/io_expander/esp_io_expander_tca9554;components/io_expander/esp_io_expander_tca95xx_16bit;
namespace: "espressif"
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
idf_component_register(SRCS "esp_io_expander_tca95xx_16bit.c" INCLUDE_DIRS "include" REQUIRES "driver")
42 changes: 42 additions & 0 deletions components/io_expander/esp_io_expander_tca95xx_16bit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# ESP IO Expander Chip TCA9539 and TCA9555

[![Component Registry](https://components.espressif.com/components/espressif/esp_io_expander_tca95xx_16bit/badge.svg)](https://components.espressif.com/components/espressif/esp_io_expander_tca95xx_16bit)

Implementation of the TCA9539 and TCA9555 io expander chip with esp_io_expander component.

| Chip | Communication interface | Component name | Link to datasheet |
| :--------------: | :---------------------: | :---------------------------: | :---------------: |
| TCA9539 | I2C | esp_io_expander_tca95xx_16bit | [datasheet](https://www.ti.com/lit/gpn/tca9539) |
| TCA9555 | I2C | esp_io_expander_tca95xx_16bit | [datasheet](https://www.ti.com/lit/gpn/tca9555) |

## Add to project

Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/).
You can add them to your project via `idf.py add-dependency`, e.g.
```
idf.py add-dependency esp_io_expander_tca95xx_16bit==1.0.0
```

Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).

## Example use

Creation of the component.

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

Set pin 0 and pin 1 with output dircetion and low level:

```
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:

```
esp_io_expander_print_state(io_expander);
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* SPDX-FileCopyrightText: 2015-2022 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_tca95xx_16bit.h"

/* Timeout of each I2C communication */
#define I2C_TIMEOUT_MS (10)

#define IO_COUNT (16)

/* Register address */
#define INPUT_REG_ADDR (0x00)
#define OUTPUT_REG_ADDR (0x02)
#define DIRECTION_REG_ADDR (0x06)

/* Default register value on power-up */
#define DIR_REG_DEFAULT_VAL (0xffff)
#define OUT_REG_DEFAULT_VAL (0xffff)

/**
* @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_16bit_t;

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);
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_tca95xx_16bit(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_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");

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_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*
*value = (((uint32_t)temp[0]) << 8) | (temp[1]);
return ESP_OK;
}

static esp_err_t write_output_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);
value &= 0xffff;

uint8_t data[] = {OUTPUT_REG_ADDR, value >> 8, value & 0xff};
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_16bit_t *tca = (esp_io_expander_tca95xx_16bit_t *)__containerof(handle, esp_io_expander_tca95xx_16bit_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_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, value >> 8, value & 0xff};
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_16bit_t *tca = (esp_io_expander_tca95xx_16bit_t *)__containerof(handle, esp_io_expander_tca95xx_16bit_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_16bit_t *tca = (esp_io_expander_tca95xx_16bit_t *)__containerof(handle, esp_io_expander_tca95xx_16bit_t, base);

free(tca);
return ESP_OK;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dependencies:
esp_io_expander:
version: ^1.0.1
idf: '>=4.4.2'
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.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <stdint.h>

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

#include "esp_io_expander.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @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
*
* The 8-bit address format for the TCA9539 is as follows:
*
* (Slave Address)
* ┌─────────────────┷─────────────────┐
* ┌─────┐─────┐─────┐─────┐─────┐─────┐─────┐─────┐
* | 1 | 1 | 1 | 0 | 1 | A1 | A0 | R/W |
* └─────┘─────┘─────┘─────┘─────┘─────┘─────┘─────┘
* └────────┯──────────────┘ └──┯──┘
* (Fixed) (Hareware Selectable)
*
* The 8-bit address format for the TCA9555 is as follows:
*
* (Slave Address)
* ┌─────────────────┷─────────────────┐
* ┌─────┐─────┐─────┐─────┐─────┐─────┐─────┐─────┐
* | 0 | 1 | 0 | 0 | A2 | A1 | A0 | R/W |
* └─────┘─────┘─────┘─────┘─────┘─────┘─────┘─────┘
* └────────┯────────┘ └─────┯──────┘
* (Fixed) (Hareware 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.
* Then users can use `ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_000` to init it.
*/
enum esp_io_expander_tca_95xx_16bit_address {
ESP_IO_EXPANDER_I2C_TCA9539_ADDRESS_00 = 0b1110100,
ESP_IO_EXPANDER_I2C_TCA9539_ADDRESS_01 = 0b1110101,
ESP_IO_EXPANDER_I2C_TCA9539_ADDRESS_10 = 0b1110110,
ESP_IO_EXPANDER_I2C_TCA9539_ADDRESS_11 = 0b1110111,
ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_000 = 0b0100000,
ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_001 = 0b0100001,
ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_010 = 0b0100010,
ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_011 = 0b0100011,
ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_100 = 0b0100000,
ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_101 = 0b0100101,
ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_110 = 0b0100110,
ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_111 = 0b0100111,
};

#ifdef __cplusplus
}
#endif
Loading

0 comments on commit 6f02d62

Please sign in to comment.