Skip to content

Commit

Permalink
Add gpio:set_int/4 for ESP32 and STM32
Browse files Browse the repository at this point in the history
Allows setting the pid() or registered name of the process to receive GPIO
interrupt messages as the fourth parameter of `gpio:set_int/4`, for both
ESP32 and STM32 GPIO port drivers.

Signed-off-by: Winford <[email protected]>
  • Loading branch information
UncleGrumpy committed Dec 20, 2023
1 parent d2f4a8d commit 520c616
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `esp:get_default_mac/0` for retrieving the default MAC address on ESP32.
- Added support for `pico` and `poci` as an alternative to `mosi` and `miso` for SPI
- ESP32: Added support to SPI peripherals other than hspi and vspi
- Added `gpio:set_int/4`, with the 4th parameter being the pid() or registered name of the process to receive interrupt messages

### Changed

Expand Down
27 changes: 26 additions & 1 deletion libs/eavmlib/src/gpio.erl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
read/2,
set_direction/3,
set_level/3,
set_int/3,
set_int/3, set_int/4,
remove_int/2,
stop/0,
close/1
Expand Down Expand Up @@ -251,6 +251,31 @@ set_level(GPIO, Pin, Level) ->
set_int(GPIO, Pin, Trigger) ->
port:call(GPIO, {set_int, Pin, Trigger}).

%%-----------------------------------------------------------------------------
%% @param GPIO pid that was returned from `gpio:start/0'
%% @param Pin number of the pin to set the interrupt on
%% @param Trigger is the state that will trigger an interrupt
%% @param Pid is the process that will receive the interrupt message
%% @returns ok | error | {error, Reason}
%% @doc Set a GPIO interrupt
%%
%% Available triggers are `none' (which is the same as disabling an
%% interrupt), `rising', `falling', `both' (rising or falling), `low', and
%% `high'. When the interrupt is triggered it will send a tuple:
%% `{gpio_interrupt, Pin}'
%% to the process that set the interrupt. Pin will be the number
%% of the pin that triggered the interrupt.
%%
%% The STM32 port only supports `rising', `falling', or `both'.
%%
%% The rp2040 (Pico) port does not support gpio interrupts at this time.
%% @end
%%-----------------------------------------------------------------------------
-spec set_int(GPIO :: gpio(), Pin :: pin(), Trigger :: trigger(), Pid :: pid()) ->
ok | {error, Reason :: atom()} | error.
set_int(GPIO, Pin, Trigger, Pid) ->
port:call(GPIO, {set_int, Pin, Trigger, Pid}).

%%-----------------------------------------------------------------------------
%% @param GPIO pid that was returned from `gpio:start/0'
%% @param Pin number of the pin to remove the interrupt
Expand Down
30 changes: 28 additions & 2 deletions src/platforms/esp32/components/avm_builtins/gpio_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
#include "atom.h"
#include "bif.h"
#include "context.h"
#include "scheduler.h"
#include "debug.h"
#include "defaultatoms.h"
#include "globalcontext.h"
Expand All @@ -41,6 +40,7 @@
#include "module.h"
#include "nifs.h"
#include "platform_defaultatoms.h"
#include "scheduler.h"
#include "term.h"
#include "utils.h"

Expand Down Expand Up @@ -133,6 +133,8 @@ struct GPIOData
struct ListHead gpio_listeners;
};

/* TODO: Change error returns to {error, Reason} (See: https://github.com/atomvm/AtomVM/issues/894) */

static inline term gpio_set_pin_mode(Context *ctx, term gpio_num_term, term mode_term)
{
int gpio_num = term_to_int(gpio_num_term);
Expand Down Expand Up @@ -343,16 +345,38 @@ static bool gpiodriver_is_gpio_attached(struct GPIOData *gpio_data, int gpio_num
static term gpiodriver_set_int(Context *ctx, int32_t target_pid, term cmd)
{
GlobalContext *glb = ctx->global;
int32_t target_local_pid;

struct GPIOData *gpio_data = ctx->platform_data;

int32_t gpio_num = term_to_int32(term_get_tuple_element(cmd, 1));
term trigger = term_get_tuple_element(cmd, 2);
if (term_get_tuple_arity(cmd) == 4) {
term pid = term_get_tuple_element(cmd, 3);
if (UNLIKELY(!term_is_pid(pid) && !term_is_atom(pid))) {
ESP_LOGE(TAG, "Invalid listener parameter, must be a pid() or registered process!");
return ERROR_ATOM;
}
if (term_is_pid(pid)) {
target_local_pid = term_to_local_process_id(pid);
} else {
int pid_atom_index = term_to_atom_index(pid);
int32_t registered_process = (int32_t) globalcontext_get_registered_process(ctx->global, pid_atom_index);
if (UNLIKELY(registered_process == 0)) {
ESP_LOGE(TAG, "Invalid listener parameter, atom() is not a registered process name!");
return ERROR_ATOM;
}
target_local_pid = registered_process;
}
} else {
target_local_pid = target_pid;
}

if (gpiodriver_is_gpio_attached(gpio_data, gpio_num)) {
return ERROR_ATOM;
}

/* TODO: GPIO specific atoms should be removed from platform_defaultatoms and constructed within this driver */
gpio_int_type_t interrupt_type;
switch (trigger) {
case NONE_ATOM:
Expand Down Expand Up @@ -397,7 +421,7 @@ static term gpiodriver_set_int(Context *ctx, int32_t target_pid, term cmd)
}
list_append(&gpio_data->gpio_listeners, &data->gpio_listener_list_head);
data->gpio = gpio_num;
data->target_local_pid = target_pid;
data->target_local_pid = target_local_pid;
sys_register_listener(glb, &data->listener);
data->listener.sender = data;
data->listener.handler = gpio_interrupt_callback;
Expand Down Expand Up @@ -523,6 +547,8 @@ REGISTER_PORT_DRIVER(gpio, gpio_driver_init, NULL, gpio_driver_create_port)

#ifdef CONFIG_AVM_ENABLE_GPIO_NIFS

/* TODO: in the case of {error, Return} we should RAISE_ERROR(Reason) */

static term nif_gpio_set_pin_mode(Context *ctx, int argc, term argv[])
{
return gpio_set_pin_mode(ctx, argv[0], argv[1]);
Expand Down
26 changes: 25 additions & 1 deletion src/platforms/stm32/src/lib/gpio_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ static NativeHandlerResult consume_gpio_mailbox(Context *ctx);
static const char *const gpio_atom = ATOM_STR("\x4", "gpio");
static const char *const gpio_interrupt_atom = ATOM_STR("\xE", "gpio_interrupt");
static const char *const invalid_trigger_atom = ATOM_STR("\xF", "invalid_trigger");
static const char *const invalid_listener_atom = ATOM_STR("\x10", "invalid_listener");

#define INVALID_EXTI_TRIGGER 0xEE

Expand Down Expand Up @@ -748,6 +749,8 @@ static bool gpiodriver_is_gpio_attached(struct GPIOData *gpio_data, term gpio_ba

static term gpiodriver_set_int(Context *ctx, int32_t target_pid, term cmd)
{
int32_t target_local_pid;

term gpio_tuple = term_get_tuple_element(cmd, 1);
if (UNLIKELY(!term_is_tuple(gpio_tuple))) {
AVM_LOGE(TAG, "Invalid GPIO Pin tuple, expect {Bank, Pin}.");
Expand Down Expand Up @@ -789,6 +792,27 @@ static term gpiodriver_set_int(Context *ctx, int32_t target_pid, term cmd)
return create_pair(ctx, ERROR_ATOM, globalcontext_make_atom(ctx->global, invalid_trigger_atom));
}

if (term_get_tuple_arity(cmd) == 4) {
term pid = term_get_tuple_element(cmd, 3);
if (UNLIKELY(!term_is_pid(pid) && !term_is_atom(pid))) {
AVM_LOGE(TAG, "Invalid listener parameter, must be a pid() or registered process!");
return create_pair(ctx, ERROR_ATOM, invalid_listener_atom);
}
if (term_is_pid(pid)) {
target_local_pid = term_to_local_process_id(pid);
} else {
int pid_atom_index = term_to_atom_index(pid);
int32_t registered_process = (int32_t) globalcontext_get_registered_process(ctx->global, pid_atom_index);
if (UNLIKELY(registered_process == 0)) {
AVM_LOGE(TAG, "Invalid listener parameter, atom() is not a registered process name!");
return create_pair(ctx, ERROR_ATOM, NOPROC_ATOM);
}
target_local_pid = registered_process;
}
} else {
target_local_pid = target_pid;
}

uint32_t exti = 1U << gpio_pin;

if (!list_is_empty(&gpio_data->gpio_listeners)) {
Expand Down Expand Up @@ -816,7 +840,7 @@ static term gpiodriver_set_int(Context *ctx, int32_t target_pid, term cmd)
AVM_ABORT();
}
list_append(&gpio_data->gpio_listeners, &data->gpio_listener_list_head);
data->target_local_pid = target_pid;
data->target_local_pid = target_local_pid;
data->bank_atom = gpio_bank_atom;
data->gpio_pin = gpio_pin;
data->exti = exti;
Expand Down

0 comments on commit 520c616

Please sign in to comment.