Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements to Pico(W) gpio driver #874

Merged
merged 1 commit into from
Oct 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
14 changes: 10 additions & 4 deletions libs/eavmlib/src/gpio.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
89 changes: 76 additions & 13 deletions src/platforms/rp2040/src/lib/gpiodriver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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);
pguyot marked this conversation as resolved.
Show resolved Hide resolved
}
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"));
}
Expand Down
Loading