Skip to content

Commit

Permalink
Merge pull request #874 from UncleGrumpy/pico-w_gpio
Browse files Browse the repository at this point in the history
Enhancements to Pico(W) gpio driver

Modifies the Pico gpio driver to accept atoms for the special wl_gpio pins
present in the Pico-W.
- Adds support for `{wl, 0..1}` as output pins in `gpio:digital_write/2`.
- Adds support for `{wl, 2}` as an input pin in `gpio:digital_read/1`.
- Raises error `badarg` instead of returning `error`

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
  • Loading branch information
bettio committed Oct 29, 2023
2 parents bf8ae4f + 1f310fb commit 7ee1adf
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 17 deletions.
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);
}
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

0 comments on commit 7ee1adf

Please sign in to comment.