diff --git a/CHANGELOG.md b/CHANGELOG.md index 750101e7c..f94e5eec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added cmake configuration option `AVM_CONFIG_REBOOT_ON_NOT_OK` for STM32 - New gpio driver for STM32 with nif and port support for read and write functions. - Added support for interrupts to STM32 GPIO port driver. +- Added suppoprt for PicoW extra gpio pins (led) to the gpio driver. ## [0.6.0-alpha.1] - 2023-10-09 diff --git a/libs/eavmlib/src/gpio.erl b/libs/eavmlib/src/gpio.erl index f91ac90d1..d2fd246ac 100644 --- a/libs/eavmlib/src/gpio.erl +++ b/libs/eavmlib/src/gpio.erl @@ -59,11 +59,11 @@ -type gpio() :: pid(). %% This is the pid returned by `gpio:start/0'. -type pin() :: non_neg_integer() | pin_tuple(). -%% The pin definition for ESP32 and PR2040 is a non-negative integer, on the STM32 platform it is a tuple. +%% The pin definition for ESP32 and PR2040 is a non-negative integer. A tuple is used on the STM32 platform and for the extra "WL" pins on the Pico-W. -type pin_tuple() :: {gpio_bank(), 0..15}. -%% A pin parameter on STM32 is a tuple consisting of a GPIO bank and pin number. --type gpio_bank() :: a | b | c | d | e | f | g | h | i | j | k. -%% STM32 gpio banks vary by board, some only break out `a' thru `h'. +%% A pin parameter on STM32 is a tuple consisting of a GPIO bank and pin number, also used on the Pico-W for the extra "WL" pins `0..2'. +-type gpio_bank() :: a | b | c | d | e | f | g | h | i | j | k | wl. +%% STM32 gpio banks vary by board, some only break out `a' thru `h'. The extra "WL" pins on Pico-W use bank `wl'. -type direction() :: input | output | output_od | mode_config(). %% The direction is used to set the mode of operation for a GPIO pin, either as an input, an output, or output with open drain. %% On the STM32 platform pull mode and output_speed must be set at the same time as direction. See @type mode_config() @@ -438,6 +438,9 @@ deep_sleep_hold_dis() -> %% on a specific bank the atom `all' may be used, this will have no effect on any pins on the %% same bank that have been configured as inputs, so it is safe to use with mixed direction %% modes on a bank. +%% +%% The LED pin on the Pico-W can be controlled on the extended pin `{wl, 0}', and does not +%% require or accept `set_pin_mode' or `set_pin_pull' before use. %% @end %%----------------------------------------------------------------------------- -spec digital_write(Pin :: pin(), Level :: level()) -> ok | {error, Reason :: atom()} | error. @@ -452,6 +455,9 @@ digital_write(_Pin, _Level) -> %% Read if an input pin state is high or low. %% Warning: if the pin was not previously configured as an input using %% `gpio:set_pin_mode/2' it will always read as low. +%% +%% The VBUS detect pin on the Pico-W can be read on the extended pin `{wl, 2}', +%% and does not require or accept `set_pin_mode' or `set_pin_pull' before use. %% @end %%----------------------------------------------------------------------------- -spec digital_read(Pin :: pin()) -> high | low | {error, Reason :: atom()} | error. diff --git a/src/platforms/rp2040/src/lib/gpiodriver.c b/src/platforms/rp2040/src/lib/gpiodriver.c index 30b56f7a0..9539e9e1b 100644 --- a/src/platforms/rp2040/src/lib/gpiodriver.c +++ b/src/platforms/rp2040/src/lib/gpiodriver.c @@ -30,6 +30,8 @@ #include "rp2040_sys.h" #include "trace.h" +#define WL_ATOM globalcontext_make_atom(ctx->global, ATOM_STR("\x2", "wl")) + static const struct Nif *gpio_nif_get_nif(const char *nifname); static const AtomStringIntPair pin_mode_table[] = { @@ -72,7 +74,10 @@ static term nif_gpio_init(Context *ctx, int argc, term argv[]) UNUSED(argc); VALIDATE_VALUE(argv[0], term_is_integer); - int pin = term_to_int(argv[0]); + uint pin = term_to_int(argv[0]); + if (UNLIKELY(pin >= NUM_BANK0_GPIOS)) { + RAISE_ERROR(BADARG_ATOM); + } gpio_init(pin); return OK_ATOM; } @@ -83,7 +88,10 @@ static term nif_gpio_deinit(Context *ctx, int argc, term argv[]) UNUSED(argc); VALIDATE_VALUE(argv[0], term_is_integer); - int gpio_num = term_to_int(argv[0]); + uint gpio_num = term_to_int(argv[0]); + if (UNLIKELY(gpio_num >= NUM_BANK0_GPIOS)) { + RAISE_ERROR(BADARG_ATOM); + } gpio_deinit(gpio_num); return OK_ATOM; } @@ -93,10 +101,13 @@ static term nif_gpio_set_pin_mode(Context *ctx, int argc, term argv[]) UNUSED(argc); VALIDATE_VALUE(argv[0], term_is_integer); - int gpio_num = term_to_int(argv[0]); + uint gpio_num = term_to_int(argv[0]); + if (UNLIKELY(gpio_num >= NUM_BANK0_GPIOS)) { + RAISE_ERROR(BADARG_ATOM); + } int mode = interop_atom_term_select_int(pin_mode_table, argv[1], ctx->global); if (UNLIKELY(mode < 0)) { - return ERROR_ATOM; + RAISE_ERROR(BADARG_ATOM); } gpio_set_dir(gpio_num, mode); return OK_ATOM; @@ -107,7 +118,10 @@ static term nif_gpio_set_pin_pull(Context *ctx, int argc, term argv[]) UNUSED(argc); VALIDATE_VALUE(argv[0], term_is_integer); - int gpio_num = term_to_int(argv[0]); + uint gpio_num = term_to_int(argv[0]); + if (UNLIKELY(gpio_num >= NUM_BANK0_GPIOS)) { + RAISE_ERROR(BADARG_ATOM); + } int pull_mode = interop_atom_term_select_int(pull_mode_table, argv[1], ctx->global); gpio_set_pulls(gpio_num, pull_mode & AtomVMRP2040GPIOPullUp, pull_mode & AtomVMRP2040GPIOPullDown); return OK_ATOM; @@ -117,23 +131,47 @@ static term nif_gpio_digital_write(Context *ctx, int argc, term argv[]) { UNUSED(argc); - VALIDATE_VALUE(argv[0], term_is_integer); - int gpio_num = term_to_int(argv[0]); + term gpio_pin = argv[0]; term level_term = argv[1]; int level; if (term_is_integer(level_term)) { level = term_to_int32(level_term); if (UNLIKELY((level != 0) && (level != 1))) { - return ERROR_ATOM; + RAISE_ERROR(BADARG_ATOM); } } else { level = interop_atom_term_select_int(pin_level_table, level_term, ctx->global); if (UNLIKELY(level < 0)) { - return ERROR_ATOM; + RAISE_ERROR(BADARG_ATOM); } } - gpio_put(gpio_num, level); + + uint gpio_num; + if (term_is_integer(gpio_pin)) { + gpio_num = (uint) term_to_int32(gpio_pin); + if (UNLIKELY(gpio_num >= NUM_BANK0_GPIOS)) { + RAISE_ERROR(BADARG_ATOM); + } + gpio_put(gpio_num, level); +#ifdef LIB_PICO_CYW43_ARCH + } else if (term_is_tuple(gpio_pin)) { + term gpio_bank_atom = term_get_tuple_element(gpio_pin, 0); + VALIDATE_VALUE(gpio_bank_atom, term_is_atom); + if (UNLIKELY(gpio_bank_atom != WL_ATOM)) { + RAISE_ERROR(BADARG_ATOM); + } + term pin_term = term_get_tuple_element(gpio_pin, 1); + VALIDATE_VALUE(pin_term, term_is_integer); + gpio_num = (uint) term_to_int32(pin_term); + if (UNLIKELY((gpio_num == -1) || (gpio_num > 1))) { + RAISE_ERROR(BADARG_ATOM); + } + cyw43_arch_gpio_put(gpio_num, level); +#endif + } else { + RAISE_ERROR(BADARG_ATOM); + } return OK_ATOM; } @@ -142,9 +180,34 @@ static term nif_gpio_digital_read(Context *ctx, int argc, term argv[]) { UNUSED(argc); - VALIDATE_VALUE(argv[0], term_is_integer); - int gpio_num = term_to_int(argv[0]); - bool level = gpio_get(gpio_num); + term gpio_pin = argv[0]; + uint gpio_num; + bool level; + + if (term_is_integer(gpio_pin)) { + gpio_num = (uint) term_to_int32(gpio_pin); + if (UNLIKELY(gpio_num >= NUM_BANK0_GPIOS)) { + RAISE_ERROR(BADARG_ATOM); + } + level = gpio_get(gpio_num); +#ifdef LIB_PICO_CYW43_ARCH + } else if (term_is_tuple(gpio_pin)) { + term gpio_bank_atom = term_get_tuple_element(gpio_pin, 0); + VALIDATE_VALUE(gpio_bank_atom, term_is_atom); + if (UNLIKELY(gpio_bank_atom != WL_ATOM)) { + RAISE_ERROR(BADARG_ATOM); + } + term pin_term = term_get_tuple_element(gpio_pin, 1); + VALIDATE_VALUE(pin_term, term_is_integer); + gpio_num = (uint) term_to_int32(pin_term); + if (UNLIKELY(gpio_num != 2)) { + RAISE_ERROR(BADARG_ATOM); + } + level = cyw43_arch_gpio_get(gpio_num); +#endif + } else { + RAISE_ERROR(BADARG_ATOM); + } return level ? globalcontext_make_atom(ctx->global, ATOM_STR("\x4", "high")) : globalcontext_make_atom(ctx->global, ATOM_STR("\x3", "low")); }