Skip to content

Commit 643b880

Browse files
espzavPetrESP
authored andcommitted
feat(io_expander): New IO Expander AW9523
1 parent 6aec94b commit 643b880

File tree

13 files changed

+708
-0
lines changed

13 files changed

+708
-0
lines changed

.github/workflows/upload_component.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ jobs:
103103
components/io_expander/esp_io_expander_tca95xx_16bit
104104
components/io_expander/esp_io_expander_ht8574
105105
components/io_expander/esp_io_expander_pi4ioe5v6408
106+
components/io_expander/esp_io_expander_aw9523
106107
components/sensors/icm42670
107108
components/sensors/aht30
108109
components/sensors/bmi270

.ignore_build_warnings.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ warning: ignoring attribute 'section .+' because it conflicts with previous 'sec
44
warning: #warning "This set of Touch APIs has been deprecated*
55
Warning: The smallest app partition is nearly full*
66
warning: unknown kconfig symbol*
7+
warning: 'ISP_AWB_SAMPLE_POINT_BEFORE_CCM' is deprecated
8+
warning: 'ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC' is deprecated
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
set(req)
2+
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.2.6")
3+
list(APPEND req "esp_driver_i2c")
4+
else()
5+
list(APPEND req "driver")
6+
endif()
7+
8+
idf_component_register(SRCS "esp_io_expander_aw9523.c" INCLUDE_DIRS "include" REQUIRES ${req})
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# ESP IO Expander Chip AW9523
2+
3+
[![Component Registry](https://components.espressif.com/components/espressif/esp_io_expander_aw9523/badge.svg)](https://components.espressif.com/components/espressif/esp_io_expander_aw9523)
4+
5+
Implementation of the AW9523 io expander chip with esp_io_expander component.
6+
7+
| Chip | Communication interface | Component name | Link to datasheet |
8+
| :--------------: | :---------------------: | :------------: | :---------------: |
9+
| AW9523 | I2C | esp_io_expander_aw9523 | [datasheet](https://doc.awinic.com/doc/202403/deffbf3b-7e7b-4ff6-8e91-fd85e2d845d5.pdf) |
10+
11+
> [!NOTE]
12+
> This component does not support the LED control mode of the AW9523.
13+
14+
## Add to project
15+
16+
Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/).
17+
You can add them to your project via `idf.py add-dependency`, e.g.
18+
```
19+
idf.py add-dependency esp_io_expander_aw9523==1.0.0
20+
```
21+
22+
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).
23+
24+
## Example use
25+
26+
Creation of the i2c bus.
27+
28+
```c
29+
i2c_master_bus_handle_t i2c_handle = NULL;
30+
const i2c_master_bus_config_t bus_config = {
31+
.i2c_port = I2C_NUM_0,
32+
.sda_io_num = 47,
33+
.scl_io_num = 48,
34+
.clk_source = I2C_CLK_SRC_DEFAULT,
35+
};
36+
i2c_new_master_bus(&bus_config, &i2c_handle);
37+
```
38+
39+
Creation of the component.
40+
41+
```c
42+
esp_io_expander_handle_t io_expander = NULL;
43+
esp_io_expander_new_i2c_aw9523(i2c_handle, ESP_IO_EXPANDER_I2C_AW9523_ADDRESS_00, &io_expander);
44+
```
45+
46+
Print all pins's status to the log:
47+
48+
```c
49+
esp_io_expander_print_state(io_expander);
50+
```
51+
52+
Set pin 0 and pin 1 with output direction and low level:
53+
54+
```c
55+
esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_OUTPUT);
56+
esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 0);
57+
```
58+
59+
Set pin 2 and pin 3 with input direction:
60+
61+
```c
62+
uint32_t pin_levels = 0;
63+
esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, IO_EXPANDER_INPUT);
64+
esp_io_expander_get_level(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, &pin_levels);
65+
```
66+
67+
## Example use with GPIO API
68+
69+
1. Enable `CONFIG_IO_EXPANDER_ENABLE_GPIO_API_WRAPPER=y` in `menuconfig`
70+
2. Include `#include "esp_io_expander_gpio_wrapper.h"` to your project.
71+
72+
3. Creation of the i2c bus.
73+
74+
```c
75+
i2c_master_bus_handle_t i2c_handle = NULL;
76+
const i2c_master_bus_config_t bus_config = {
77+
.i2c_port = I2C_NUM_0,
78+
.sda_io_num = 47,
79+
.scl_io_num = 48,
80+
.clk_source = I2C_CLK_SRC_DEFAULT,
81+
};
82+
i2c_new_master_bus(&bus_config, &i2c_handle);
83+
```
84+
85+
4. Creation of the component.
86+
87+
```c
88+
esp_io_expander_handle_t io_expander = NULL;
89+
esp_io_expander_new_i2c_aw9523(i2c_handle, ESP_IO_EXPANDER_I2C_AW9523_ADDRESS_00, &io_expander);
90+
```
91+
92+
5. Append IO Expander to GPIO wrapper.
93+
94+
```c
95+
esp_io_expander_gpio_wrapper_append_handler(io_expander, GPIO_NUM_MAX);
96+
```
97+
98+
6. Use it as standard GPIO.
99+
100+
```c
101+
const uint8_t io_led1 = GPIO_NUM_MAX + 6; // IO Expander pin 6
102+
gpio_set_direction(io_led1, GPIO_MODE_OUTPUT);
103+
104+
ESP_LOGI(TAG, "LED1 set ON");
105+
gpio_set_level(io_led1, 1);
106+
107+
ESP_LOGI(TAG, "LED1 set OFF");
108+
gpio_set_level(io_led1, 0);
109+
```
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <inttypes.h>
8+
#include <string.h>
9+
#include <stdlib.h>
10+
#include "esp_bit_defs.h"
11+
#include "esp_check.h"
12+
#include "esp_log.h"
13+
#include "esp_io_expander.h"
14+
#include "esp_io_expander_aw9523.h"
15+
16+
/* I2C communication related */
17+
#define I2C_TIMEOUT_MS (1000)
18+
#define I2C_CLK_SPEED (400000)
19+
20+
#define IO_COUNT (16)
21+
22+
/* Register address */
23+
#define INPUT_REG_ADDR (0x00)
24+
#define OUTPUT_REG_ADDR (0x02)
25+
#define DIRECTION_REG_ADDR (0x04)
26+
#define RESET_REG_ADDR (0x7F)
27+
28+
/* Default register value on power-up */
29+
#define DIR_REG_DEFAULT_VAL (0xffff)
30+
#define OUT_REG_DEFAULT_VAL (0xffff)
31+
32+
/**
33+
* @brief Device Structure Type
34+
*
35+
*/
36+
typedef struct {
37+
esp_io_expander_t base;
38+
i2c_master_dev_handle_t i2c_handle;
39+
struct {
40+
uint16_t direction;
41+
uint16_t output;
42+
} regs;
43+
} esp_io_expander_aw9523_t;
44+
45+
static char *TAG = "aw9523";
46+
47+
static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value);
48+
static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t value);
49+
static esp_err_t read_output_reg(esp_io_expander_handle_t handle, uint32_t *value);
50+
static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t value);
51+
static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *value);
52+
static esp_err_t reset(esp_io_expander_t *handle);
53+
static esp_err_t del(esp_io_expander_t *handle);
54+
55+
esp_err_t esp_io_expander_new_i2c_aw9523(i2c_master_bus_handle_t i2c_bus, uint32_t dev_addr,
56+
esp_io_expander_handle_t *handle_ret)
57+
{
58+
ESP_RETURN_ON_FALSE(handle_ret != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid handle_ret");
59+
60+
// Allocate memory for driver object
61+
esp_io_expander_aw9523_t *aw9523 = (esp_io_expander_aw9523_t *)calloc(1, sizeof(esp_io_expander_aw9523_t));
62+
ESP_RETURN_ON_FALSE(aw9523 != NULL, ESP_ERR_NO_MEM, TAG, "Malloc failed");
63+
64+
// Add new I2C device
65+
esp_err_t ret = ESP_OK;
66+
const i2c_device_config_t i2c_dev_cfg = {
67+
.device_address = dev_addr,
68+
.scl_speed_hz = I2C_CLK_SPEED,
69+
};
70+
ESP_GOTO_ON_ERROR(i2c_master_bus_add_device(i2c_bus, &i2c_dev_cfg, &aw9523->i2c_handle), err, TAG,
71+
"Add new I2C device failed");
72+
73+
aw9523->base.config.io_count = IO_COUNT;
74+
aw9523->base.config.flags.dir_out_bit_zero = 1;
75+
aw9523->base.read_input_reg = read_input_reg;
76+
aw9523->base.write_output_reg = write_output_reg;
77+
aw9523->base.read_output_reg = read_output_reg;
78+
aw9523->base.write_direction_reg = write_direction_reg;
79+
aw9523->base.read_direction_reg = read_direction_reg;
80+
aw9523->base.del = del;
81+
aw9523->base.reset = reset;
82+
83+
/* Reset configuration and register status */
84+
ESP_GOTO_ON_ERROR(reset(&aw9523->base), err1, TAG, "Reset failed");
85+
86+
*handle_ret = &aw9523->base;
87+
return ESP_OK;
88+
err1:
89+
i2c_master_bus_rm_device(aw9523->i2c_handle);
90+
err:
91+
free(aw9523);
92+
return ret;
93+
}
94+
95+
static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value)
96+
{
97+
esp_io_expander_aw9523_t *aw9523 = (esp_io_expander_aw9523_t *)__containerof(handle, esp_io_expander_aw9523_t,
98+
base);
99+
100+
uint8_t temp[2] = {0, 0};
101+
ESP_RETURN_ON_ERROR(i2c_master_transmit_receive(aw9523->i2c_handle, (uint8_t[]) {
102+
INPUT_REG_ADDR
103+
}, 1, temp, sizeof(temp), I2C_TIMEOUT_MS), TAG, "Read input reg failed");
104+
*value = (((uint32_t)temp[1]) << 8) | (temp[0]);
105+
return ESP_OK;
106+
}
107+
108+
static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t value)
109+
{
110+
esp_io_expander_aw9523_t *aw9523 = (esp_io_expander_aw9523_t *)__containerof(handle, esp_io_expander_aw9523_t,
111+
base);
112+
value &= 0xffff;
113+
114+
uint8_t data[] = {OUTPUT_REG_ADDR, value & 0xff, value >> 8};
115+
ESP_RETURN_ON_ERROR(i2c_master_transmit(aw9523->i2c_handle, data, sizeof(data), I2C_TIMEOUT_MS), TAG,
116+
"Write output reg failed");
117+
aw9523->regs.output = value;
118+
return ESP_OK;
119+
}
120+
121+
static esp_err_t read_output_reg(esp_io_expander_handle_t handle, uint32_t *value)
122+
{
123+
esp_io_expander_aw9523_t *aw9523 = (esp_io_expander_aw9523_t *)__containerof(handle, esp_io_expander_aw9523_t,
124+
base);
125+
126+
*value = aw9523->regs.output;
127+
return ESP_OK;
128+
}
129+
130+
static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t value)
131+
{
132+
esp_io_expander_aw9523_t *aw9523 = (esp_io_expander_aw9523_t *)__containerof(handle, esp_io_expander_aw9523_t,
133+
base);
134+
value &= 0xffff;
135+
136+
uint8_t data[] = {DIRECTION_REG_ADDR, value & 0xff, value >> 8};
137+
ESP_RETURN_ON_ERROR(i2c_master_transmit(aw9523->i2c_handle, data, sizeof(data), I2C_TIMEOUT_MS), TAG,
138+
"Write direction reg failed");
139+
aw9523->regs.direction = value;
140+
return ESP_OK;
141+
}
142+
143+
static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *value)
144+
{
145+
esp_io_expander_aw9523_t *aw9523 = (esp_io_expander_aw9523_t *)__containerof(handle, esp_io_expander_aw9523_t,
146+
base);
147+
148+
*value = aw9523->regs.direction;
149+
return ESP_OK;
150+
}
151+
152+
static esp_err_t reset(esp_io_expander_t *handle)
153+
{
154+
esp_io_expander_aw9523_t *aw9523 = (esp_io_expander_aw9523_t *)__containerof(handle, esp_io_expander_aw9523_t,
155+
base);
156+
157+
uint8_t data[] = {RESET_REG_ADDR, 0x00};
158+
ESP_RETURN_ON_ERROR(i2c_master_transmit(aw9523->i2c_handle, data, sizeof(data), I2C_TIMEOUT_MS), TAG,
159+
"Write direction reg failed");
160+
return ESP_OK;
161+
}
162+
163+
static esp_err_t del(esp_io_expander_t *handle)
164+
{
165+
esp_io_expander_aw9523_t *aw9523 = (esp_io_expander_aw9523_t *)__containerof(handle, esp_io_expander_aw9523_t,
166+
base);
167+
168+
ESP_RETURN_ON_ERROR(i2c_master_bus_rm_device(aw9523->i2c_handle), TAG, "Remove I2C device failed");
169+
free(aw9523);
170+
return ESP_OK;
171+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
version: "1.0.0"
2+
description: ESP IO Expander - AW9523
3+
url: https://github.com/espressif/esp-bsp/tree/master/components/io_expander/esp_io_expander_aw9523
4+
dependencies:
5+
idf: ">=5.2"
6+
esp_io_expander:
7+
version: "^1.2.0"
8+
public: true
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* @file
9+
* @brief ESP IO expander: AW9523
10+
*/
11+
12+
#pragma once
13+
14+
#include <stdint.h>
15+
#include "esp_err.h"
16+
#include "driver/i2c_master.h"
17+
#include "esp_io_expander.h"
18+
19+
#ifdef __cplusplus
20+
extern "C" {
21+
#endif
22+
23+
/**
24+
* @brief Create a AW9523 IO expander object
25+
*
26+
* @param[in] i2c_bus I2C bus handle. Obtained from `i2c_new_master_bus()`
27+
* @param[in] dev_addr I2C device address of chip. Can be `ESP_IO_EXPANDER_I2C_AW9523_ADDRESS_XX`.
28+
* @param[out] handle_ret Handle to created IO expander object
29+
*
30+
* @return
31+
* - ESP_OK: Success, otherwise returns ESP_ERR_xxx
32+
*/
33+
esp_err_t esp_io_expander_new_i2c_aw9523(i2c_master_bus_handle_t i2c_bus, uint32_t dev_addr,
34+
esp_io_expander_handle_t *handle_ret);
35+
36+
/**
37+
* @brief I2C address of the AW9523
38+
*
39+
* The 8-bit address format is as follows:
40+
*
41+
* (Slave Address)
42+
* ┌─────────────────┷─────────────────┐
43+
* ┌─────┐─────┐─────┐─────┐─────┐─────┐─────┐─────┐
44+
* | 1 | 0 | 1 | 1 | 0 | AD1 | AD0 | R/W |
45+
* └─────┘─────┘─────┘─────┘─────┘─────┘─────┘─────┘
46+
* └────────┯────────┘ └──┯────┘
47+
* (Fixed) (Hareware Selectable)
48+
*
49+
* And the 7-bit slave address is the most important data for users.
50+
* For example, if a chip's AD0,AD1 are connected to GND, it's 7-bit slave address is 1011000b(0x58).
51+
* Then users can use `ESP_IO_EXPANDER_I2C_AW9523_ADDRESS_000` to init it.
52+
*/
53+
#define ESP_IO_EXPANDER_I2C_AW9523_ADDRESS_00 (0x58)
54+
#define ESP_IO_EXPANDER_I2C_AW9523_ADDRESS_01 (0x59)
55+
#define ESP_IO_EXPANDER_I2C_AW9523_ADDRESS_10 (0x5A)
56+
#define ESP_IO_EXPANDER_I2C_AW9523_ADDRESS_11 (0x5B)
57+
58+
#ifdef __cplusplus
59+
}
60+
#endif

0 commit comments

Comments
 (0)