- 
        Couldn't load subscription status. 
- Fork 303
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: truemeans input isactivestate, 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 inputActionenum value for the currently set action
 
- Returns the 
- 
bool setAction(const inputAction)- Sets the action for the input to the given value
- Returns falseif the action could not be set,trueif it was
 
- 
ioEnabled getEnabled()- Returns the ioEnabledenum value of this input
- Keep in mind that both IO_UNAVAILABLEandIO_DISABLEDmean "disabled," but onlyIO_DISABLEDmeans 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_ENABLEDandIO_DISABLEDare valid
- Returns falseif the enabled state could not be set,trueif it was
 
- Sets the enabled state for the input to the given value - only 
- 
ioPolarity getPolarity()- Returns the current ioPolarityof 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 falseif the polarity could not be set,trueif 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
- 
0means no external number is assigned
- If the value is anything but 0then the JSON objectinN is exposed, where N is the provided value
 
- 
- Returns falseif the external number could not be set,trueif 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- thisconstis the function with the signaturebool(const bool state, const inputEdgeFlag edge, const uint8_t triggering_pin_number)- 
stateis the current state of the input wheretrueisactive, accounting for polarity.
- 
edgeis eitherINPUT_EDGE_LEADING(transition from inactive to active) orINPUT_EDGE_TRAILING(transition from active to inactive)
- 
triggering_pin_numberis the number of the pin, so fordin3that would be3
- The callback must return trueif no other handlers are to see the event, orfalseto 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- thisconstis the priority of the event handler, with100being the highest and1being 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 floatfrom0.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 floatwith a value from0.0to1.0
- Returns falseif the value could not be set,trueif 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 0if 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 falseif the frequency could not be set,trueif it was
 
- 
ioEnabled getEnabled()- Returns the ioEnabledenum value of this input
- Keep in mind that both IO_UNAVAILABLEandIO_DISABLEDmean "disabled," but onlyIO_DISABLEDmeans 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_ENABLEDandIO_DISABLEDare valid
- Returns falseif the enabled state could not be set,trueif it was
 
- Sets the enabled state for the input to the given value - only 
- 
ioPolarity getPolarity()- Returns the current ioPolarityof this output
- 
Note: It is advised that code only use the enum values IO_ACTIVE_HIGHandIO_ACTIVE_LOWand 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_HIGHorIO_ACTIVE_LOW
- Returns falseif the polarity could not be set,trueif 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
- 
0means no external number is assigned
- If the value is anything but 0then the JSON objectinN is exposed, where N is the provided value
 
- 
- Returns falseif the external number could not be set,trueif 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.0to the max voltage the ADC can read, which is3.3for 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_tenum value of this input
- See above documentation for more info
 
- Gets the 
- 
bool setType(const AnalogInputType_t)- Returns falseif the frequency could not be set,trueif it was
 
- Returns 
- 
AnalogCircuit_t getCircuit()- Gets the AnalogCircuit_tenum value of this input
- See above documentation for more info
 
- Gets the 
- 
bool setCircuit(const AnalogCircuit_t)- Returns falseif the frequency could not be set,trueif 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 pto the float valuev
- Returns falseif the frequency could not be set,trueif 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
- 
0means no external number is assigned
- If the value is anything but 0then the JSON objectinN is exposed, where N is the provided value
 
- 
- Returns falseif the external number could not be set,trueif 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 interruptmust 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 ofgpioDigitalInputPin<>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_tis defined as((polarity == IO_ACTIVE_LOW) ? kPullUp|kDebounce : kDebounce)andfromConstructorwill 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 optionsis set as((polarity == IO_ACTIVE_LOW) ? kStartHigh|kPWMPinInverted : kStartLow)...indicates that additional parameters passed to the contructor ofgpioDigitalOutputPin<>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_tis defined as(polarity == IO_ACTIVE_LOW) ? kStartHigh|kPWMPinInverted : kStartLow)- note that checking againstMotate::kPWMPinInvertedwill indicate you need to invert the outputfromConstructorwill 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.0to1.0and is not adjusted for polarity (usesetOptions()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 interruptmust 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 ofgpioAnalogInputPin<>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 - truemeans the value is treated as a differential and the valid range is-getTopVoltage()togetTopVoltage(), andfalsemeans the valid range is0.0togetTopVoltage() | 
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