-
Notifications
You must be signed in to change notification settings - Fork 295
GPIO Implementation
This page discusses upcoming functionality being added to the GPIO system.
- GPIO Overview
- GPIO Design Goals
- GPIO Primitives
- GPIO Objects and Bindings
- GPIO Implementation
See also:
GPIO implementation is discussed in the following sections:
- Consuming an input or driving an output
- Base Classes: How inputs/outputs are implemented
- How to implement a new input/output subclass
All GPIO primitives have two programmatic interfaces:
- JSON/text mode
- This interface consists of a series of functions that take
nvObj_t *
inputs and have a return ofstat_t
- This interface consists of a series of functions that take
- Direct
- These are functions that will be used by the rest of the system and may have different inputs and outputs
- The direct functions are used by the JSON/text mode functions
Here we will focus on the direct interface, which is what will be used from other parts of the code (the objects that are binding to the primitives).
In gpio.{h,cpp}
the digital inputs are defined (roughly) as:
gpioDigitalInput* const d_in[D_IN_CHANNELS];
The d_in
pointers will be subclasses of gpioDigitalInput
which has the following interface:
-
bool getState()
- Returns input state:
true
means input isactive
state, accounting for polarity. - Example:
if (d_in[input_num]->getState()) { // do something only if that input is active }
- Returns input state:
-
inputAction getAction()
- Returns the
inputAction
enum value for the currently set action
- Returns the
-
bool setAction(const inputAction)
- Sets the action for the input to the given value
- Returns
false
if the action could not be set,true
if it was
-
ioEnabled getEnabled()
- Returns the
ioEnabled
enum value of this input - Keep in mind that both
IO_UNAVAILABLE
andIO_DISABLED
mean "disabled," but onlyIO_DISABLED
means thatsetEnabled(...)
may be used - Example:
if (d_in[input_num]->getEnabled() != IO_ENABLED) { // this pin is not available }
- Returns the
-
bool setEnabled(const ioEnabled)
- Sets the enabled state for the input to the given value - only
IO_ENABLED
andIO_DISABLED
are valid - Returns
false
if the enabled state could not be set,true
if it was
- Sets the enabled state for the input to the given value - only
-
ioPolarity getPolarity()
- Returns the current
ioPolarity
of this pin -
Note: It is advised that code only use the enum values
IO_ACTIVE_HIGH
(non-inverted polarity) andIO_ACTIVE_LOW
(inverted polarity) and never their numeric values internally
- Returns the current
-
bool setPolarity(const ioPolarity)
- Sets the polarity for the input to the given value - either
IO_ACTIVE_HIGH
(non-inverted polarity) orIO_ACTIVE_LOW
(inverted polarity) - Returns
false
if the polarity could not be set,true
if it was
- Sets the polarity for the input to the given value - either
-
const uint8_t getExternalNumber()
- Returns the current external number of this pin
- See next function for more info
-
bool setExternalNumber(const uint8_t)
- Sets the external number for the input to the given value
-
0
means no external number is assigned - If the value is anything but
0
then the JSON objectin
N is exposed, where N is the provided value
-
- Returns
false
if the external number could not be set,true
if it was
- Sets the external number for the input to the given value
The gpioDigitalInputHandler
class is used to create handlers for input events. It can be used stand-alone, as a parameter of a class, or as a superclass. Here we will only demonstrate using it as a stand-alone object.
There are only three values in a gpioDigitalInputHandler
, and they are (in this order, which is vital):
-
callback
- thisconst
is the function with the signaturebool(const bool state, const inputEdgeFlag edge, const uint8_t triggering_pin_number)
-
state
is the current state of the input wheretrue
isactive
, accounting for polarity. -
edge
is eitherINPUT_EDGE_LEADING
(transition from inactive to active) orINPUT_EDGE_TRAILING
(transition from active to inactive) -
triggering_pin_number
is the number of the pin, so fordin3
that would be3
- The callback must return
true
if no other handlers are to see the event, orfalse
to allow this event to propagate - IMPORTANT the callback is called from an interrupt context, and must act accordingly so as to not interrupt the typical flow of the system! The callback should be relatively light weight and must be non-blocking (run-to-completion). In general, requesting processing (e.g. feedholds), setting flags or altering state machines for further processing from less critical contexts should be what's done here.
-
-
priority
- thisconst
is the priority of the event handler, with100
being the highest and1
being the lowest. By convention, use 5 for "normal" handlers, and 100 for special handlers like homing and probing that need to consume events w/o firing lower-priority handlers.-
This has not been used yet to decide how we should group these
-
-
next
- this is the only mutable value, and should always be initialized asnullptr
You set the parameters during the initialization of the object, and the callback is generally a lambda. An example:
gpioDigitalInputHandler limitHandler {
[&](const bool state, const inputEdgeFlag edge, const uint8_t triggering_pin_number) {
if (edge != INPUT_EDGE_LEADING) { return; }
limit_requested = true; // record that a limit was requested for later processing
return false; // allow lower-priority handlers to see this event
// return true to consume the event and stop further processing
},
5, // priority (for example)
nullptr // next - set to nullptr to start with
};
Now that the object is created, it must be registered. Registration is done via the global gpioDigitalInputHandlerList din_handlers[]
array. Each gpioDigitalInputHandlerList
is a linked list for a different inputAction
value, with the special value INPUT_ACTION_INTERNAL
being used for all pin changes.
An example of how to register the limitHandler
we instantiated above:
din_handlers[INPUT_ACTION_LIMIT].registerHandler(limitHandler);
And to remove it later:
din_handlers[INPUT_ACTION_LIMIT].deregisterHandler(limitHandler);
In gpio.{h,cpp}
the digital outputs are defined (roughly) as:
gpioDigitalOutput* const d_out[D_OUT_CHANNELS];
The d_out
pointers will be subclasses of gpioDigitalOutput
which has the following interface:
-
float getValue()
- Returns the current (last set) value of this output
- Value is a
float
from0.0
(fully inactive) to1.0
(fully active), where polarity is accounted for
-
bool setValue(const float)
- Sets the PWM duty cycle of the output base on the provided
float
with a value from0.0
to1.0
- Returns
false
if the value could not be set,true
if it was
- Sets the PWM duty cycle of the output base on the provided
-
float getFrequency()
- Returns the last set frequency value of this output, or
0
if it was never set
- Returns the last set frequency value of this output, or
-
bool setFrequency(const float)
- Set the frequency (in Hz) of the PWM of this pin, if it is capable of PWM
- Returns
false
if the frequency could not be set,true
if it was
-
ioEnabled getEnabled()
- Returns the
ioEnabled
enum value of this input - Keep in mind that both
IO_UNAVAILABLE
andIO_DISABLED
mean "disabled," but onlyIO_DISABLED
means thatsetEnabled(...)
may be used - Example:
if (d_out[output_num]->getEnabled() < IO_ENABLED) { // this pin is not available }
- Returns the
-
bool setEnabled(const ioEnabled)
- Sets the enabled state for the input to the given value - only
IO_ENABLED
andIO_DISABLED
are valid - Returns
false
if the enabled state could not be set,true
if it was
- Sets the enabled state for the input to the given value - only
-
ioPolarity getPolarity()
- Returns the current
ioPolarity
of this output -
Note: It is advised that code only use the enum values
IO_ACTIVE_HIGH
andIO_ACTIVE_LOW
and never their numeric values internally
- Returns the current
-
bool setPolarity(const ioPolarity)
- Sets the polarity for the output to the given value - either
IO_ACTIVE_HIGH
orIO_ACTIVE_LOW
- Returns
false
if the polarity could not be set,true
if it was
- Sets the polarity for the output to the given value - either
-
const uint8_t getExternalNumber()
- Returns the current external number of this pin
- See next function for more info
-
bool setExternalNumber(const uint8_t)
- Sets the external number for the input to the given value
-
0
means no external number is assigned - If the value is anything but
0
then the JSON objectin
N is exposed, where N is the provided value
-
- Returns
false
if the external number could not be set,true
if it was
- Sets the external number for the input to the given value
In board_gpio.{h,cpp}
the digital outputs are defined (roughly) as:
gpioAnalogInput* const a_in[A_IN_CHANNELS];
The a_in
pointers will be subclasses of gpioAnalogInput
which has the following interface:
-
float getValue()
- Returns the last computed voltage value of this input
- Value is generally is from
0.0
to the max voltage the ADC can read, which is3.3
for all currently supported boards - For non-ADC (external) analog inputs this value may be negative and potentially have values outside of this range
-
float getResistance()
- Returns the last computed resistance value of this input, based on the computed circuit
- If the circuit is not configured, it will return -1
- The range and value is determined by the circuit
-
AnalogInputType_t getType()
- Gets the
AnalogInputType_t
enum value of this input - See above documentation for more info
- Gets the
-
bool setType(const AnalogInputType_t)
- Returns
false
if the frequency could not be set,true
if it was
- Returns
-
AnalogCircuit_t getCircuit()
- Gets the
AnalogCircuit_t
enum value of this input - See above documentation for more info
- Gets the
-
bool setCircuit(const AnalogCircuit_t)
- Returns
false
if the frequency could not be set,true
if it was
- Returns
-
float getParameter(const uint8_t p)
- Returns the float value of tha parameter number specified by
p
- See above documentation for more info
- Returns the float value of tha parameter number specified by
-
bool setParameter(const uint8_t p, const float v)
- Sets the value of the parameter number specified by
p
to the float valuev
- Returns
false
if the frequency could not be set,true
if it was
- Sets the value of the parameter number specified by
-
void startSampling()
- Starts one sample
- Should be able to be called during an in-progress sample and be ignored
- May be ignored (but must be implemented) by some analog input types if sampling does not require polling
-
Add
getEnabled()
andsetEnabled()
?
** Not yet implemented: **
-
const uint8_t getExternalNumber()
- Returns the current external number of this pin
- See next function for more info
-
bool setExternalNumber(const uint8_t)
- Sets the external number for the input to the given value
-
0
means no external number is assigned - If the value is anything but
0
then the JSON objectin
N is exposed, where N is the provided value
-
- Returns
false
if the external number could not be set,true
if it was
- Sets the external number for the input to the given value
For digital inputs the parent class is gpioDigitalInput
and for outputs the parent class is gpioDigitalOutput
. These each provide both the JSON interface that calls the direct interface as well as pure virtual (virtual
functions with no provided definition) functions for the direct interface. Subclasses only need to implement and override those virtual functions of the direct interface.
For input, there are provided concrete subclasses for Motate::IRQPin
-compliant objects gpioDigitalInputPin<typename Pin_t>
. These also handle the pin change interrupts (using the IRQPin
callback mechanism) and gpioDigitalInputHandler
calls. Note: The typename Pin_t
does not need to be a subclass of Motate::IRQPin
but only must adhere to the minimal interface used (see below).
For analog inputs there are provided concrete subclasses for Motate::ADCPin
-compliant objects gpioAnalogInputPin<typename ADCPin_t>
. These also handle the analog update interrupts (using the ADCPin
callback mechanism). There is not currently a mechanism to subscribe to these updates.
Similarly, for output pins there are provided concrete subclasses for Motate::PWMOutputPin
-compliant objects gpioDigitalOutputPin<typename Pin_t>
.
All these are used (and thus linked to actual pins or fake pins) in the board_gpio.{h,cpp}
files.
It's possible to create new digital input or output types by either subclassing gpioDigitalInput
, gpioAnalogInput
, or gpioDigitaOutput
or by implementing the appropriate Motate pin interface methods. In either case, you create those objects in board_gpio.{h,cpp}
and place them in the appropriate array of pointers (d_in[]
, a_in[]
, or d_out[]
).
The interface used by gpioDigitalInputPin<typename Pin_t>
of a Pin_t
(typically Motate::IRQPin
) are:
Function | Notes |
---|---|
IRQPin(const PinOptions_t options, const std::function<void(void)> &&interrupt, ...) |
This is the constructor, the name would change accordingly of course interrupt must be called when a pin changes, and that is expected to be called from an interrupt context (but might not be) ... indicates that additional parameters passed to the contructor of gpioDigitalInputPin<> will be forwarded |
bool isNull() |
This should always return the same value, and unless the pin is physically unavailable, it should return false
|
void setOptions(const PinOptions_t options, const bool fromConstructor=false) |
PinOptions_t is defined as ((polarity == IO_ACTIVE_LOW) ? kPullUp|kDebounce : kDebounce) and fromConstructor will only be true the first time it's called |
operator bool() |
this returns the actual boolean value of the "pin" and does not account for polarity |
The interface used by gpioDigitalOutputPin<typename Pin_t>
of a Pin_t
(typically Motate::PWMOutputPin
or Motate::PWMLikeOutputPin
) are:
Function | Notes |
---|---|
PWMOutputPin(const PinOptions_t options, ...) |
This is the constructor, the name would change accordingly of course options is set as ((polarity == IO_ACTIVE_LOW) ? kStartHigh|kPWMPinInverted : kStartLow) ... indicates that additional parameters passed to the contructor of gpioDigitalOutputPin<> will be forwarded |
bool isNull() |
This should always return the same value, and unless the pin is physically unavailable, it should return false
|
void setOptions(const PinOptions_t options, const bool fromConstructor=false) |
PinOptions_t is defined as (polarity == IO_ACTIVE_LOW) ? kStartHigh|kPWMPinInverted : kStartLow) - note that checking against Motate::kPWMPinInverted will indicate you need to invert the output fromConstructor will only be true the first time it's called |
void setFrequency(const uint32_t freq) |
requests the pwm frequency provided |
void operator=(const float value) |
used to set the output value, passed as a float from 0.0 to 1.0 and is not adjusted for polarity (use setOptions() to determine if output needs inverted) |
The interface used by gpioAnalogInputPin<typename ADCPin_t>
of a ADCPin_t
(typically Motate::ADCPin
) are:
Function | Notes |
---|---|
ADCPin(const PinOptions_t options, std::function<void(void)> &&interrupt, ...) |
This is the constructor, the name would change accordingly of course interrupt must be called when a pin changes, and that is expected to be called from an interrupt context (but might not be) ... indicates that additional parameters passed to the contructor of gpioAnalogInputPin<> will be forwarded |
bool isNull() |
This should always return the same value, and unless the pin is physically unavailable, it should return false
|
void setVoltageRange(const float vref, const float min_expected = 0, const float max_expected = -1, const float ideal_steps = 1) |
Sets what is expected as an valid input range and requested resolution so offsetting and scaling may be used if available (can be safely ignored, but must be implemented) |
void setInterrupts(const uint32_t interrupts) |
Always called as `pin.setInterrupts(Motate::kPinInterruptOnChange |
float getTopVoltage() |
returns the maximum possible value |
void startSampling() |
start the sampling (if necessary) - when the new value is in interrupt (given in the constructor) must be called |
int32_t getRaw() |
return the raw ADC value (used only for debugging, and may not be called at all) |
float getVoltage() |
returns the ADC value converted to a voltage level but otherwise unprocessed |
static constexpr bool is_differential |
a static constant that must be in the class - true means the value is treated as a differential and the valid range is -getTopVoltage() to getTopVoltage() , and false means the valid range is 0.0 to getTopVoltage()
|
Getting Started Pages
- Home
- What is g2core?
- Who uses g2core?
- Jerk-Controlled Motion
- Getting Started with g2core
- Connecting to g2core
- Configuring g2core
- Flashing g2core
- Troubleshooting
Reference Pages
- Gcodes
- Mcodes
- Text Mode
- JSON Communications
- GPIO Digital IO
- Alarms & Exceptions
- Power Management
- Coordinate Systems
- Status Reports
- Status Codes
- G2 Communications
- Tool Offsets and Selection
- Probing
- Feedhold, Resume, Job Kill
- Marlin Compatibility
- 9 Axis UVW Operation
- gQuintic Specs
Discussion Topics
- Roadmap
- GPIO for 1.X Releases
- Toolheads
- Raster Streaming Prototol
- g2core REST Interface
- Gcode Parsing
- G2 3DP Dialect
- Consensus Gcode
- Digital DRO
- Overview of Motion Processing
Developer Pages
- Development & Contribution
- Branching and Release - DRAFT
- Getting Started with g2core Development
- Project Structure & Motate
- Compiling G2
- OSX w/Xcode
- OSX/Linux Command Line
- Windows10 w/AtmelStudio7
- Debugging G2 on OSX
- Board and Machine Profiles
- Arduino Due Pinout
- Arduino DUE External Interfaces
- Diagnostics
- Debugging w/Motate Pins
- Development Troubleshooting
- g2core Communications
- Git Procedures
- Windows 10 / VMware 8 Issues
- Dual Endpoint USB Internals
- G2core License
- VSCode Setup
- Compatibility Axioms
- Wiki History