Skip to content

Commit

Permalink
Update GPIO documentation for STM32 port
Browse files Browse the repository at this point in the history
Updates the types and edoc entries for gpio.erl, and the "Programmers Guide"
with details about the updated STM32 gpio driver.

Signed-off-by: Winford <winford@object.stream>
  • Loading branch information
UncleGrumpy committed Oct 14, 2023
1 parent 857c983 commit b1c60ba
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 7 deletions.
27 changes: 26 additions & 1 deletion doc/src/programmers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1043,13 +1043,16 @@ The `esp:get_mac/1` function can be used to retrieve the network Media Access Co

## Peripherals

The AtomVM virtual machine and libraries support APIs for interfacing with peripheral devices connected to the ESP32. This section provides information about these APIs.
The AtomVM virtual machine and libraries support APIs for interfacing with peripheral devices connected to the ESP32 and other supported microcontrollers. This section provides information about these APIs. Unless otherwise stated the documentation for these peripherals is specific to the ESP32, most peripherals are not yet supported on rp2040 or stm32 devices - but work is on-going to expand support on these platforms.

### GPIO

The GPIO peripheral has nif support on all platforms. One notable difference on the STM32 platform is that `Pin`s are defined as a tuple consisting of the bank (a.k.a. port) and pin number. For example a pin labeled PB7 on your board would be `{b,7}`.

You can read and write digital values on GPIO pins using the `gpio` module, using the `digital_read/1` and `digital_write/2` functions. You must first set the direction of the pin using the `gpio:set_direction/2` function, using `input` or `output` as the direction parameter.

To read the value of a GPIO pin (`high` or `low`), use `gpio:digital_read/1`:
For ESP32 or RP2040:

%% erlang
Pin = 2,
Expand All @@ -1061,15 +1064,37 @@ To read the value of a GPIO pin (`high` or `low`), use `gpio:digital_read/1`:
io:format("Pin ~p is low ~n", [Pin])
end.

For STM32 only the line with the Pin definition needs to be changed:

%% erlang
Pin = {c, 13},
gpio:set_direction(Pin, input),
case gpio:digital_read(Pin) of
high ->
io:format("Pin ~p is high ~n", [Pin]);
low ->
io:format("Pin ~p is low ~n", [Pin])
end.

To set the value of a GPIO pin (`high` or `low`), use `gpio:digital_write/2`:
For ESP32 or RP2040:

%% erlang
Pin = 2,
gpio:set_direction(Pin, output),
gpio:digital_write(Pin, low).

And Similarly for the STM32:

%% erlang
Pin = {b, 7},
gpio:set_direction(Pin, output),
gpio:digital_write(Pin, low).

#### Interrupt Handling

Interrupts are supported on both the ESP32 and STM32 platforms.

You can get notified of changes in the state of a GPIO pin by using the `gpio:set_int/2` function. This function takes a reference to a GPIO Pin and a trigger. Allowable triggers are `rising`, `falling`, `both`, `low`, `high`, and `none` (to disable an interrupt).

When a trigger event occurs, such as a pin rising in voltage, a tuple will be delivered to the process containing the atom `gpio_interrupt` and the pin.
Expand Down
118 changes: 112 additions & 6 deletions libs/eavmlib/src/gpio.erl
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,21 @@
%% (General Purpose Input and Output) pins.
%%
%% Note: `-type pin()' used in this driver refers to a pin number on Espressif
%% chips, or a tuple {GPIO_GROUP, PIN} for stm32 chips.
%% chips, or a tuple {GPIO_BANK, PIN} for stm32 chips.
%% @end
%%-----------------------------------------------------------------------------
-module(gpio).

-export([
start/0, open/0, read/2, set_direction/3, set_level/3, set_int/3, remove_int/2, stop/0, close/1
start/0,
open/0,
read/2,
set_direction/3,
set_level/3,
set_int/3,
remove_int/2,
stop/0,
close/1
]).
-export([
init/1,
Expand All @@ -49,13 +57,27 @@
]).

-type gpio() :: pid().
-type pin() :: non_neg_integer() | {atom(), non_neg_integer()}.
-type direction() :: input | output | output_od.
%% This is the pid returned by `gpio:start/0'.
-type pin() :: non_neg_integer() | stm32_pin().
%% The pin definition on the STM32 platform varies from ESP32 and PR2040.
-type stm32_pin() :: {gpio_bank(), 0..15}.
%% A pin argument 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'.
-type direction() :: input | output | output_od | stm32_direction().
%% 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.
-type stm32_direction() :: {direction(), pull()} | {output, pull(), output_speed()}.
%% Extended mode configuration options on STM32. Default pull() is `floating', default output_speed() is `mhz_2' if options are omitted.
-type pull() :: up | down | up_down | floating.
%% Internal resistor pull mode. STM32 does not support `up_down'.
-type output_speed() :: mhz_2 | mhz_25 | mhz_50 | mhz_100.
%% Output clock speed. Only available on STM32, default is `mhz_2'.
-type low_level() :: low | 0.
-type high_level() :: high | 1.
-type level() :: low_level() | high_level().
%% Valid pin levels can be atom or binary representation.
-type trigger() :: none | rising | falling | both | low | high.
%% Event type that will trigger a `gpio_interrupt'. STM32 only supports `rising', `falling', or `both'.

%%-----------------------------------------------------------------------------
%% @returns Pid
Expand All @@ -65,6 +87,8 @@
%% port driver will be stared and registered as `gpio'. The use of
%% `gpio:open/0' or `gpio:start/0' is required before using any functions
%% that require a GPIO pid as a parameter.
%%
%% Not currently available on rp2040 (Pico) port, use nif functions.
%% @end
%%-----------------------------------------------------------------------------
-spec start() -> gpio().
Expand All @@ -85,6 +109,8 @@ start() ->
%% `gpio:start/0' the command will fail. The use of `gpio:open/0' or
%% `gpio:start/0' is required before using any functions that require a
%% GPIO pid as a parameter.
%%
%% Not currently available on rp2040 (Pico) port, use nif functions.
%% @end
%%-----------------------------------------------------------------------------
-spec open() -> gpio().
Expand All @@ -98,6 +124,8 @@ open() ->
%%
%% This function disables any interrupts that are set, stops
%% the listening port, and frees all of its resources.
%%
%% Not currently available on rp2040 (Pico) port, use nif functions.
%% @end
%%-----------------------------------------------------------------------------
-spec close(GPIO :: gpio()) -> ok | error.
Expand All @@ -110,6 +138,8 @@ close(GPIO) ->
%%
%% This function disables any interrupts that are set, stops
%% the listening port, and frees all of its resources.
%%
%% Not currently available on rp2040 (Pico) port, use nif functions.
%% @end
%%-----------------------------------------------------------------------------
-spec stop() -> ok | error.
Expand All @@ -123,8 +153,10 @@ stop() ->
%% @doc Read the digital state of a GPIO pin
%%
%% Read if an input pin state is `high' or `low'.
%% Warning: if a is not previously configured as an input using
%% Warning: if the pin was not previously configured as an input using
%% `gpio:set_direction/3' it will always read as low.
%%
%% Not supported on rp2040 (Pico), use `gpio:digital_read/1' instead.
%% @end
%%-----------------------------------------------------------------------------
-spec read(GPIO :: gpio(), Pin :: pin()) -> high | low.
Expand All @@ -139,6 +171,17 @@ read(GPIO, Pin) ->
%% @doc Set the operational mode of a pin
%%
%% Pins can be used for input, output, or output with open drain.
%%
%% The STM32 platform has extended direction mode configuration options.
%% See @type stm32_direction() for details. All configuration must be set using
%% `set_direction/3', including pull() mode, unlike the ESP32 which has a
%% separate function (`set_pin_pull/2'). If you are configuring multiple pins
%% on the same GPIO `bank' with the same options the pins may be configured all
%% at the same time by giving a list of pin numbers in the pin tuple.
%% Example to configure all of the leds on a Nucleo board:
%% `gpio:set_direction({b, [0,7,14], output)'
%%
%% Not supported on rp2040 (Pico), use `gpio:set_pin_mode/2' instead.
%% @end
%%-----------------------------------------------------------------------------
-spec set_direction(GPIO :: gpio(), Pin :: pin(), Direction :: direction()) -> ok | error.
Expand All @@ -153,6 +196,24 @@ set_direction(GPIO, Pin, Direction) ->
%% @doc Set GPIO digital output level
%%
%% Set a pin to `high' (1) or `low' (0).
%%
%% The STM32 is capable of setting the state for any, or all of the output pins
%% on a single bank at the same time, this is done by passing a list of pins numbers
%% in the pin tuple.
%%
%% For example, setting all of the even numbered pins to a `high' state,
%% and all of the odd numbered pins to a `low' state can be accomplished in two lines:
%% <pre>
%% gpio:digital_write({c, [0,2,4,6,8,10,12,14]}, high}),
%% gpio:digital_write({c, [1,3,5,7,9,11,13,15]}, low}).
%% </pre>
%%
%% To set the same state for all of the pins that have been previously configured as outputs
%% 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.
%%
%% Not supported on rp2040 (Pico), use `gpio:digital_write/2' instead.
%% @end
%%-----------------------------------------------------------------------------
-spec set_level(GPIO :: gpio(), Pin :: pin(), Level :: level()) -> ok | error.
Expand All @@ -172,6 +233,10 @@ set_level(GPIO, Pin, Level) ->
%% `{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()) -> ok | error.
Expand All @@ -185,6 +250,8 @@ set_int(GPIO, Pin, Trigger) ->
%% @doc Remove a GPIO interrupt
%%
%% Removes an interrupt from the specified pin.
%%
%% The rp2040 (Pico) port does not support gpio interrupts at this time.
%% @end
%%-----------------------------------------------------------------------------
-spec remove_int(GPIO :: gpio(), Pin :: pin()) -> ok | error.
Expand Down Expand Up @@ -220,6 +287,15 @@ deinit(_Pin) ->
%% @doc Set the operational mode of a pin
%%
%% Pins can be used for input, output, or output with open drain.
%%
%% The STM32 platform has extended direction mode configuration options.
%% See @type stm32_direction() for details. All configuration must be set using
%% `set_direction/3', including pull() mode, unlike the ESP32 which has a
%% separate function (`set_pin_pull/2'). If you are configuring multiple pins
%% on the same GPIO `bank' with the same options the pins may be configured all
%% at the same time by giving a list of pin numbers in the pin tuple.
%% Example to configure all of the leds on a Nucleo board:
%% `gpio:set_direction({b, [0,7,14], output)'
%% @end
%%-----------------------------------------------------------------------------
-spec set_pin_mode(Pin :: pin(), Direction :: direction()) -> ok | error.
Expand All @@ -234,6 +310,10 @@ set_pin_mode(_Pin, _Mode) ->
%%
%% Pins can be internally pulled `up', `down', `up_down' (pulled in
%% both directions), or left `floating'.
%%
%% This function is not supported on STM32, the internal resistor must
%% be configured when setting the direction mode, see `set_direction/3'
%% or `set_pin_mode/2'.
%% @end
%%-----------------------------------------------------------------------------
-spec set_pin_pull(Pin :: pin(), Pull :: pull()) -> ok | error.
Expand All @@ -258,6 +338,8 @@ set_pin_pull(_Pin, _Pull) ->
%% will resume the hold function when the chip wakes up from
%% Deep-sleep. If the digital gpio also needs to be held during
%% Deep-sleep `gpio:deep_sleep_hold_en' should also be called.
%%
%% This function is only supported on ESP32.
%% @end
%%-----------------------------------------------------------------------------
-spec hold_en(Pin :: pin()) -> ok | error.
Expand All @@ -278,6 +360,8 @@ hold_en(_Pin) ->
%% low level(because gpio18 is input mode by default). If you don’t
%% want this behavior, you should configure gpio18 as output mode and
%% set it to hight level before calling `gpio:hold_dis'.
%%
%% This function is only supported on ESP32.
%% @end
%%-----------------------------------------------------------------------------
-spec hold_dis(Pin :: pin()) -> ok | error.
Expand All @@ -301,6 +385,8 @@ hold_dis(_Pin) ->
%% Power down or call `gpio_hold_dis' will disable this function,
%% otherwise, the digital gpio hold feature works as long as the chip
%% enters Deep-sleep.
%%
%% This function is only supported on ESP32.
%% @end
%%-----------------------------------------------------------------------------
-spec deep_sleep_hold_en() -> ok.
Expand All @@ -310,6 +396,8 @@ deep_sleep_hold_en() ->
%%-----------------------------------------------------------------------------
%% @returns ok
%% @doc Disable all gpio pad functions during Deep-sleep.
%%
%% This function is only supported on ESP32.
%% @end
%%-----------------------------------------------------------------------------
-spec deep_sleep_hold_dis() -> ok.
Expand All @@ -323,6 +411,20 @@ deep_sleep_hold_dis() ->
%% @doc Set GPIO digital output level
%%
%% Set a pin to `high' (1) or `low' (0).
%%
%% The STM32 is capable of setting the state for any, or all of the output pins
%% on a single bank at the same time, this is done by passing a list of pins numbers
%% in the pin tuple. For example, setting all of the even numbered pins to a `high' state,
%% and all of the odd numbered pins to a `low' state can be accomplished in two lines:
%% <pre>
%% gpio:digital_write({c, [0,2,4,6,8,10,12,14]}, high}),
%% gpio:digital_write({c, [1,3,5,7,9,11,13,15]}, low}).
%% </pre>
%%
%% To set the same state for all of the pins that have been previously configured as outputs
%% 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.
%% @end
%%-----------------------------------------------------------------------------
-spec digital_write(Pin :: pin(), Level :: level()) -> ok | error.
Expand All @@ -335,7 +437,7 @@ digital_write(_Pin, _Level) ->
%% @doc Read the digital state of a GPIO pin
%%
%% Read if an input pin state is high or low.
%% Warning: if a is not previously configured as an input using
%% Warning: if the pin was not previously configured as an input using
%% `gpio:set_pin_mode/2' it will always read as low.
%% @end
%%-----------------------------------------------------------------------------
Expand All @@ -357,6 +459,8 @@ digital_read(_Pin) ->
%% used in an application. If multiple pins are being configured with
%% interrupt triggers gpio:set_int/3 should be used otherwise there is
%% a race condition when start() is called internally by this function.
%%
%% The rp2040 (Pico) port does not support gpio interrupts at this time.
%% @end
%%-----------------------------------------------------------------------------
-spec attach_interrupt(Pin :: pin(), Trigger :: trigger()) -> ok | error.
Expand All @@ -373,6 +477,8 @@ attach_interrupt(Pin, Trigger) ->
%%
%% Unlike `gpio:attach_interrupt/2' this function can be safely used
%% regardless of the number of interrupt pins used in the application.
%%
%% The rp2040 (Pico) port does not support gpio interrupts at this time.
%% @end
%%-----------------------------------------------------------------------------
-spec detach_interrupt(Pin :: pin()) -> ok | error.
Expand Down

0 comments on commit b1c60ba

Please sign in to comment.