Skip to content

Commit eb76dc6

Browse files
authored
Merge pull request #737 from espressif/feat/io_expander_gpio_config
feat(io_expander): Added support for gpio_config and gpio_reset_pin into GPIO wrappers
2 parents 307c9fd + ff81830 commit eb76dc6

File tree

7 files changed

+290
-54
lines changed

7 files changed

+290
-54
lines changed

components/io_expander/esp_io_expander/CMakeLists.txt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ idf_component_register(
1111
)
1212

1313
if(CONFIG_IO_EXPANDER_ENABLE_GPIO_API_WRAPPER)
14-
target_sources(${COMPONENT_LIB} PRIVATE "esp_io_expander_gpio_wrapper.c")
14+
target_sources(${COMPONENT_LIB} PRIVATE "esp_io_expander_gpio_wrapper.c"
15+
"esp_io_expander_gpio_wrapper_weak.c")
16+
17+
target_include_directories(${COMPONENT_LIB} PRIVATE "priv_include")
1518

1619
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=gpio_set_level"
1720
"-Wl,--wrap=gpio_get_level"
1821
"-Wl,--wrap=gpio_set_direction"
1922
"-Wl,--wrap=gpio_set_pull_mode"
20-
)
21-
endif()
23+
"-Wl,--wrap=gpio_config"
24+
"-Wl,--wrap=gpio_reset_pin"
25+
"-u __wrap_gpio_set_level")
26+
27+
endif()

components/io_expander/esp_io_expander/Kconfig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ menu "ESP IO Expander"
22

33
config IO_EXPANDER_ENABLE_GPIO_API_WRAPPER
44
bool "Enable GPIO API wrapper"
5-
default n
5+
default y
66
help
7-
If enabled, ESP-IDF GPIO APIs can be used to control the IOs of IO expander.
7+
If enabled, ESP-IDF GPIO APIs can be used to control the IOs of IO expander. Full GPIO expander support links when you call esp_io_expander_gpio_wrapper_append_handler()
88

99
endmenu

components/io_expander/esp_io_expander/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Supported wrapped APIs:
3636
- `gpio_get_level`
3737
- `gpio_set_direction` (supports `GPIO_MODE_INPUT`, `GPIO_MODE_OUTPUT`, `GPIO_MODE_OUTPUT_OD` if the chip supports high-Z write)
3838
- `gpio_set_pull_mode` (supports `GPIO_PULLUP_ONLY`, `GPIO_PULLDOWN_ONLY` if the chip supports pulldown select, and `GPIO_FLOATING`)
39+
- `gpio_config`
40+
- `gpio_reset_pin` (supported only as wrapper for compatibility - no function)
3941

4042
Example:
4143

components/io_expander/esp_io_expander/esp_io_expander_gpio_wrapper.c

Lines changed: 121 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@
22
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Linked only when the app references esp_io_expander_gpio_wrapper_append_handler
7+
* (or remove_handler). Provides strong esp_io_expander_gpio_wrapper_*.
58
*/
69

710
#include <stdlib.h>
811
#include <stdbool.h>
912
#include "esp_io_expander_gpio_wrapper.h"
13+
#include "esp_io_expander_gpio_wrapper_priv.h"
1014
#include "freertos/FreeRTOS.h"
1115
#include "freertos/portmacro.h"
1216
#include "esp_heap_caps.h"
1317
#include "esp_err.h"
18+
#include "esp_check.h"
1419
#include "esp_log.h"
1520
#include "driver/gpio.h"
1621
#include "soc/gpio_num.h"
@@ -33,11 +38,6 @@ static portMUX_TYPE s_ioexp_lock = portMUX_INITIALIZER_UNLOCKED;
3338

3439
static char *TAG = "io_expander_wrapper";
3540

36-
esp_err_t __real_gpio_set_level(gpio_num_t gpio_num, uint32_t level);
37-
int __real_gpio_get_level(gpio_num_t gpio_num);
38-
esp_err_t __real_gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode);
39-
esp_err_t __real_gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull);
40-
4141
static bool find_ioexp_for_num(uint32_t gpio_num, esp_io_expander_handle_t *out_handler, uint32_t *out_pin_mask)
4242
{
4343
bool found = false;
@@ -145,53 +145,38 @@ esp_err_t esp_io_expander_gpio_wrapper_remove_handler(esp_io_expander_handle_t h
145145
return ESP_OK;
146146
}
147147

148-
esp_err_t __wrap_gpio_set_level(gpio_num_t gpio_num, uint32_t level)
148+
esp_err_t esp_io_expander_gpio_wrapper_set_level(gpio_num_t gpio_num, uint32_t level)
149149
{
150-
if (gpio_num < GPIO_NUM_MAX) {
151-
// Call the ESP-IDF implementation for regular GPIOs
152-
return __real_gpio_set_level(gpio_num, level);
153-
}
154-
// Redirect GPIO set level calls to ESP IO Expander here
155-
esp_io_expander_handle_t handler = NULL;
150+
esp_io_expander_handle_t handle = NULL;
156151
uint32_t pin_mask = 0;
157-
if (!find_ioexp_for_num((uint32_t)gpio_num, &handler, &pin_mask)) {
152+
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {
158153
ESP_LOGE(TAG, "GPIO %d is not assigned to any IO Expander", gpio_num);
159154
return ESP_ERR_INVALID_ARG;
160155
}
161-
return esp_io_expander_set_level(handler, pin_mask, (uint8_t)(level ? 1 : 0));
156+
return esp_io_expander_set_level(handle, pin_mask, (uint8_t)(level ? 1 : 0));
162157
}
163158

164-
int __wrap_gpio_get_level(gpio_num_t gpio_num)
159+
int esp_io_expander_gpio_wrapper_get_level(gpio_num_t gpio_num)
165160
{
166-
if (gpio_num < GPIO_NUM_MAX) {
167-
// Call the ESP-IDF implementation for regular GPIOs
168-
return __real_gpio_get_level(gpio_num);
169-
}
170-
// Redirect GPIO get level calls to ESP IO Expander here
171-
esp_io_expander_handle_t handler = NULL;
161+
esp_io_expander_handle_t handle = NULL;
172162
uint32_t pin_mask = 0;
173-
if (!find_ioexp_for_num((uint32_t)gpio_num, &handler, &pin_mask)) {
163+
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {
174164
ESP_LOGE(TAG, "GPIO %d is not assigned to any IO Expander", gpio_num);
175-
return -1; // Indicate error
165+
return -1;
176166
}
177167
uint32_t level_mask = 0;
178-
esp_err_t err = esp_io_expander_get_level(handler, pin_mask, &level_mask);
168+
esp_err_t err = esp_io_expander_get_level(handle, pin_mask, &level_mask);
179169
if (err != ESP_OK) {
180-
return -1; // Indicate error
170+
return -1;
181171
}
182172
return (level_mask & pin_mask) ? 1 : 0;
183173
}
184174

185-
esp_err_t __wrap_gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
175+
esp_err_t esp_io_expander_gpio_wrapper_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
186176
{
187-
if (gpio_num < GPIO_NUM_MAX) {
188-
// Call the ESP-IDF implementation for regular GPIOs
189-
return __real_gpio_set_direction(gpio_num, mode);
190-
}
191-
// Redirect GPIO set direction calls to ESP IO Expander here
192-
esp_io_expander_handle_t handler = NULL;
177+
esp_io_expander_handle_t handle = NULL;
193178
uint32_t pin_mask = 0;
194-
if (!find_ioexp_for_num((uint32_t)gpio_num, &handler, &pin_mask)) {
179+
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {
195180
ESP_LOGE(TAG, "GPIO %d is not assigned to any IO Expander", gpio_num);
196181
return ESP_ERR_INVALID_ARG;
197182
}
@@ -209,7 +194,7 @@ esp_err_t __wrap_gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
209194
case GPIO_MODE_OUTPUT_OD:
210195
dir = IO_EXPANDER_OUTPUT;
211196
out_mode = IO_EXPANDER_OUTPUT_MODE_OPEN_DRAIN;
212-
if (!handler->write_highz_reg) {
197+
if (!handle->write_highz_reg) {
213198
mode_valid = false;
214199
}
215200
break;
@@ -220,38 +205,33 @@ esp_err_t __wrap_gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
220205
ESP_LOGE(TAG, "Unsupported GPIO mode %d for IO Expander GPIO %d", mode, gpio_num);
221206
return ESP_ERR_INVALID_ARG;
222207
}
223-
esp_err_t err = esp_io_expander_set_dir(handler, pin_mask, dir);
224-
if (err == ESP_OK && dir == IO_EXPANDER_OUTPUT && handler->write_highz_reg) {
225-
err = esp_io_expander_set_output_mode(handler, pin_mask, out_mode);
208+
esp_err_t err = esp_io_expander_set_dir(handle, pin_mask, dir);
209+
if (err == ESP_OK && dir == IO_EXPANDER_OUTPUT && handle->write_highz_reg) {
210+
err = esp_io_expander_set_output_mode(handle, pin_mask, out_mode);
226211
}
227212
return err;
228213
}
229214

230-
esp_err_t __wrap_gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
215+
esp_err_t esp_io_expander_gpio_wrapper_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
231216
{
232-
if (gpio_num < GPIO_NUM_MAX) {
233-
// Call the ESP-IDF implementation for regular GPIOs
234-
return __real_gpio_set_pull_mode(gpio_num, pull);
235-
}
236-
// Redirect GPIO set pull mode calls to ESP IO Expander here
237-
esp_io_expander_handle_t handler = NULL;
217+
esp_io_expander_handle_t handle = NULL;
238218
uint32_t pin_mask = 0;
239-
if (!find_ioexp_for_num((uint32_t)gpio_num, &handler, &pin_mask)) {
219+
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {
240220
ESP_LOGE(TAG, "GPIO %d is not assigned to any IO Expander", gpio_num);
241221
return ESP_ERR_INVALID_ARG;
242222
}
243223
esp_io_expander_pullupdown_t pud = IO_EXPANDER_PULL_NONE;
244224
bool pud_valid = true;
245225
switch (pull) {
246226
case GPIO_PULLUP_ONLY:
247-
if (!handler->write_pullup_en_reg) {
227+
if (!handle->write_pullup_en_reg) {
248228
pud_valid = false;
249229
} else {
250230
pud = IO_EXPANDER_PULL_UP;
251231
}
252232
break;
253233
case GPIO_PULLDOWN_ONLY:
254-
if (!handler->write_pullup_en_reg || !handler->write_pullup_sel_reg) {
234+
if (!handle->write_pullup_en_reg || !handle->write_pullup_sel_reg) {
255235
pud_valid = false;
256236
} else {
257237
pud = IO_EXPANDER_PULL_DOWN;
@@ -268,5 +248,98 @@ esp_err_t __wrap_gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
268248
ESP_LOGE(TAG, "Unsupported GPIO pull mode %d for IO Expander GPIO %d", pull, gpio_num);
269249
return ESP_ERR_INVALID_ARG;
270250
}
271-
return esp_io_expander_set_pullupdown(handler, pin_mask, pud);
251+
return esp_io_expander_set_pullupdown(handle, pin_mask, pud);
252+
}
253+
254+
esp_err_t esp_io_expander_gpio_wrapper_configure_pin(gpio_num_t gpio_num, const gpio_config_t *config)
255+
{
256+
if (config == NULL) {
257+
return ESP_ERR_INVALID_ARG;
258+
}
259+
esp_err_t err = ESP_OK;
260+
esp_io_expander_handle_t handle = NULL;
261+
uint32_t pin_mask = 0;
262+
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {
263+
ESP_LOGE(TAG, "GPIO %d is not assigned to any IO Expander", gpio_num);
264+
return ESP_ERR_INVALID_ARG;
265+
}
266+
267+
if ((config->mode) & GPIO_MODE_DEF_INPUT) {
268+
err = esp_io_expander_set_dir(handle, pin_mask, IO_EXPANDER_INPUT);
269+
} else if ((config->mode) & GPIO_MODE_DEF_OUTPUT) {
270+
err = esp_io_expander_set_dir(handle, pin_mask, IO_EXPANDER_OUTPUT);
271+
if (handle->write_highz_reg) {
272+
if ((config->mode) & GPIO_MODE_DEF_OD) {
273+
err = esp_io_expander_set_output_mode(handle, pin_mask, IO_EXPANDER_OUTPUT_MODE_OPEN_DRAIN);
274+
} else {
275+
err = esp_io_expander_set_output_mode(handle, pin_mask, IO_EXPANDER_OUTPUT_MODE_PUSH_PULL);
276+
}
277+
} else if ((config->mode) & GPIO_MODE_DEF_OD) {
278+
ESP_LOGW(TAG, "IO Expander GPIO does not support open drain mode.");
279+
}
280+
} else {
281+
ESP_LOGE(TAG, "Unsupported GPIO mode %d for IO Expander GPIO %d", config->mode, gpio_num);
282+
return ESP_ERR_INVALID_ARG;
283+
}
284+
285+
esp_io_expander_pullupdown_t pull_mode = IO_EXPANDER_PULL_NONE;
286+
bool pull_mode_valid = true;
287+
if (config->pull_up_en && config->pull_down_en) {
288+
pull_mode_valid = false;
289+
} else if (config->pull_up_en) {
290+
if (!handle->write_pullup_en_reg) {
291+
pull_mode_valid = false;
292+
} else {
293+
pull_mode = IO_EXPANDER_PULL_UP;
294+
}
295+
} else if (config->pull_down_en) {
296+
if (!handle->write_pullup_en_reg || !handle->write_pullup_sel_reg) {
297+
pull_mode_valid = false;
298+
} else {
299+
pull_mode = IO_EXPANDER_PULL_DOWN;
300+
}
301+
} else {
302+
pull_mode = IO_EXPANDER_PULL_NONE;
303+
}
304+
305+
if (pull_mode_valid) {
306+
if (handle->write_pullup_en_reg) {
307+
err = esp_io_expander_set_pullupdown(handle, pin_mask, pull_mode);
308+
}
309+
} else {
310+
ESP_LOGE(TAG, "Unsupported GPIO pull mode for IO Expander GPIO %d", gpio_num);
311+
}
312+
313+
if (config->intr_type) {
314+
ESP_LOGW(TAG, "IO Expander does not support interrupts.");
315+
}
316+
317+
return err;
318+
}
319+
320+
esp_err_t esp_io_expander_gpio_wrapper_reset_pin(gpio_num_t gpio_num)
321+
{
322+
esp_err_t ret = ESP_OK;
323+
esp_io_expander_handle_t handle = NULL;
324+
uint32_t pin_mask = 0;
325+
if (!find_ioexp_for_num((uint32_t)gpio_num, &handle, &pin_mask)) {
326+
ESP_LOGE(TAG, "GPIO %d is not assigned to any IO Expander", gpio_num);
327+
return ESP_ERR_INVALID_ARG;
328+
}
329+
330+
// Set level to LOW
331+
ESP_RETURN_ON_ERROR(esp_io_expander_set_level(handle, pin_mask, (uint8_t)(0)), TAG,
332+
"Set level of IO expander pin %d failed.", gpio_num);
333+
334+
// Set as input
335+
ESP_RETURN_ON_ERROR(esp_io_expander_set_dir(handle, pin_mask, IO_EXPANDER_INPUT), TAG,
336+
"Set direction of IO expander pin %d failed.", gpio_num);
337+
338+
// Remove pull resistors
339+
if (handle->write_pullup_en_reg) {
340+
ESP_RETURN_ON_ERROR(esp_io_expander_set_pullupdown(handle, pin_mask, IO_EXPANDER_PULL_NONE), TAG,
341+
"Set pull mode of IO expander pin %d failed.", gpio_num);
342+
}
343+
344+
return ret;
272345
}

0 commit comments

Comments
 (0)