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

Add gpio:set_int/4 for ESP32 and STM32 #837

Merged
merged 1 commit into from
Dec 23, 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 @@ -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;
bettio marked this conversation as resolved.
Show resolved Hide resolved
}
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);
fadushin marked this conversation as resolved.
Show resolved Hide resolved
}
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);
bettio marked this conversation as resolved.
Show resolved Hide resolved
}
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
Loading