mirror of
https://github.com/synthetos/g2.git
synced 2026-02-06 11:11:57 +08:00
696 lines
25 KiB
C++
696 lines
25 KiB
C++
/*
|
|
* gpio.cpp - digital IO handling functions
|
|
* This file is part of the g2core project
|
|
*
|
|
* Copyright (c) 2015 - 2107 Alden S. Hart, Jr.
|
|
* Copyright (c) 2015 - 2017 Robert Giseburt
|
|
*
|
|
* This file ("the software") is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License, version 2 as published by the
|
|
* Free Software Foundation. You should have received a copy of the GNU General Public
|
|
* License, version 2 along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* As a special exception, you may use this file as part of a software library without
|
|
* restriction. Specifically, if other files instantiate templates or use macros or
|
|
* inline functions from this file, or you compile this file and link it with other
|
|
* files to produce an executable, this file does not by itself cause the resulting
|
|
* executable to be covered by the GNU General Public License. This exception does not
|
|
* however invalidate any other reasons why the executable file might be covered by the
|
|
* GNU General Public License.
|
|
*
|
|
* THE SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY
|
|
* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
* SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
/* Switch Modes
|
|
*
|
|
* The switches are considered to be homing switches when cycle_state is
|
|
* CYCLE_HOMING. At all other times they are treated as limit switches:
|
|
* - Hitting a homing switch puts the current move into feedhold
|
|
* - Hitting a limit switch causes the machine to shut down and go into lockdown until reset
|
|
*
|
|
* The normally open switch modes (NO) trigger an interrupt on the falling edge
|
|
* and lockout subsequent interrupts for the defined lockout period. This approach
|
|
* beats doing debouncing as an integration as switches fire immediately.
|
|
*
|
|
* The normally closed switch modes (NC) trigger an interrupt on the rising edge
|
|
* and lockout subsequent interrupts for the defined lockout period. Ditto on the method.
|
|
*/
|
|
|
|
#include "g2core.h" // #1
|
|
#include "config.h" // #2
|
|
#include "gpio.h"
|
|
#include "stepper.h"
|
|
#include "encoder.h"
|
|
#include "hardware.h"
|
|
#include "canonical_machine.h"
|
|
|
|
#include "text_parser.h"
|
|
#include "controller.h"
|
|
#include "util.h"
|
|
#include "report.h"
|
|
#include "xio.h"
|
|
|
|
#include "MotateTimers.h"
|
|
using namespace Motate;
|
|
|
|
/**** Allocate structures ****/
|
|
|
|
d_in_t d_in[D_IN_CHANNELS];
|
|
d_out_t d_out[D_OUT_CHANNELS];
|
|
a_in_t a_in[A_IN_CHANNELS];
|
|
a_out_t a_out[A_OUT_CHANNELS];
|
|
|
|
/**** Extended DI structure ****/
|
|
|
|
// To be merged with ioDigitalInput later.
|
|
// For now, we use a pointer to the correct d_in, since the old code did.
|
|
// input_pin_num is the Motate pin number.
|
|
// ext_pin_number is the JSON ("external") pin number, as in "di1".
|
|
template <pin_number input_pin_num, uint8_t ext_pin_number>
|
|
struct ioDigitalInputExt {
|
|
IRQPin<input_pin_num> input_pin;
|
|
|
|
/* Priority only needs set once in the system during startup.
|
|
* However, if we wish to switch the interrupt trigger, here are other options:
|
|
* kPinInterruptOnRisingEdge
|
|
* kPinInterruptOnFallingEdge
|
|
*
|
|
* To change the trigger or priority provide a third paramater intValue that will
|
|
* be called as pin.setInterrupts(intValue), or call pin.setInterrupts at any point.
|
|
* Note that it may cause an interrupt to fire *immediately*!
|
|
* intValue defaults to kPinInterruptOnChange|kPinInterruptPriorityMedium if not specified.
|
|
*/
|
|
ioDigitalInputExt() : input_pin {kPullUp|kDebounce, [&]{this->pin_changed();}} {
|
|
};
|
|
|
|
ioDigitalInputExt(const ioDigitalInputExt&) = delete; // delete copy
|
|
ioDigitalInputExt(ioDigitalInputExt&&) = delete; // delete move
|
|
|
|
void reset() {
|
|
if (D_IN_CHANNELS < ext_pin_number) { return; }
|
|
|
|
d_in_t *in = &d_in[ext_pin_number-1];
|
|
|
|
if (in->mode == IO_MODE_DISABLED) {
|
|
in->state = INPUT_DISABLED;
|
|
return;
|
|
}
|
|
|
|
bool pin_value = (bool)input_pin;
|
|
int8_t pin_value_corrected = (pin_value ^ ((int)in->mode ^ 1)); // correct for NO or NC mode
|
|
in->state = (ioState)pin_value_corrected;
|
|
}
|
|
|
|
void pin_changed() {
|
|
if (D_IN_CHANNELS < ext_pin_number) { return; }
|
|
|
|
d_in_t *in = &d_in[ext_pin_number-1];
|
|
|
|
// return if input is disabled (not supposed to happen)
|
|
if (in->mode == IO_MODE_DISABLED) {
|
|
in->state = INPUT_DISABLED;
|
|
return;
|
|
}
|
|
|
|
// return if the input is in lockout period (take no action)
|
|
if (in->lockout_timer.isSet() && !in->lockout_timer.isPast()) {
|
|
return;
|
|
}
|
|
|
|
// return if no change in state
|
|
bool pin_value = (bool)input_pin;
|
|
int8_t pin_value_corrected = (pin_value ^ ((int)in->mode ^ 1)); // correct for NO or NC mode
|
|
if (in->state == (ioState)pin_value_corrected) {
|
|
return;
|
|
}
|
|
|
|
// lockout the pin for lockout_ms
|
|
in->lockout_timer.set(in->lockout_ms);
|
|
|
|
// record the changed state
|
|
in->state = (ioState)pin_value_corrected;
|
|
if (pin_value_corrected == INPUT_ACTIVE) {
|
|
in->edge = INPUT_EDGE_LEADING;
|
|
} else {
|
|
in->edge = INPUT_EDGE_TRAILING;
|
|
}
|
|
|
|
// perform homing operations if in homing mode
|
|
if (in->homing_mode) {
|
|
if (in->edge == INPUT_EDGE_LEADING) { // we only want the leading edge to fire
|
|
en_take_encoder_snapshot();
|
|
cm_start_hold();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// perform probing operations if in probing mode
|
|
if (in->probing_mode) {
|
|
// We want to capture either way.
|
|
// Probing tests the start condition for the correct direction ahead of time.
|
|
// If we see any edge, it's the right one.
|
|
en_take_encoder_snapshot();
|
|
cm_start_hold();
|
|
return;
|
|
}
|
|
|
|
// *** NOTE: From this point on all conditionals assume we are NOT in homing or probe mode ***
|
|
|
|
// trigger the action on leading edges
|
|
if (in->edge == INPUT_EDGE_LEADING) {
|
|
if (in->action == INPUT_ACTION_STOP) {
|
|
cm_start_hold();
|
|
}
|
|
if (in->action == INPUT_ACTION_FAST_STOP) {
|
|
cm_start_hold(); // for now is same as STOP
|
|
}
|
|
if (in->action == INPUT_ACTION_HALT) {
|
|
cm_halt_all(); // hard stop, including spindle and coolant
|
|
}
|
|
if (in->action == INPUT_ACTION_ALARM) {
|
|
char msg[10];
|
|
sprintf(msg, "input %d", ext_pin_number);
|
|
cm_alarm(STAT_ALARM, msg);
|
|
}
|
|
if (in->action == INPUT_ACTION_SHUTDOWN) {
|
|
char msg[10];
|
|
sprintf(msg, "input %d", ext_pin_number);
|
|
cm_shutdown(STAT_SHUTDOWN, msg);
|
|
}
|
|
if (in->action == INPUT_ACTION_PANIC) {
|
|
char msg[10];
|
|
sprintf(msg, "input %d", ext_pin_number);
|
|
cm_panic(STAT_PANIC, msg);
|
|
}
|
|
if (in->action == INPUT_ACTION_RESET) {
|
|
hw_hard_reset();
|
|
}
|
|
}
|
|
|
|
// these functions trigger on the leading edge
|
|
if (in->edge == INPUT_EDGE_LEADING) {
|
|
if (in->function == INPUT_FUNCTION_LIMIT) {
|
|
cm->limit_requested = ext_pin_number;
|
|
|
|
} else if (in->function == INPUT_FUNCTION_SHUTDOWN) {
|
|
cm->shutdown_requested = ext_pin_number;
|
|
|
|
} else if (in->function == INPUT_FUNCTION_INTERLOCK) {
|
|
cm->safety_interlock_disengaged = ext_pin_number;
|
|
}
|
|
}
|
|
|
|
// trigger interlock release on trailing edge
|
|
if (in->edge == INPUT_EDGE_TRAILING) {
|
|
if (in->function == INPUT_FUNCTION_INTERLOCK) {
|
|
cm->safety_interlock_reengaged = ext_pin_number;
|
|
}
|
|
}
|
|
|
|
sr_request_status_report(SR_REQUEST_TIMED); //+++++ Put this one back in.
|
|
};
|
|
};
|
|
|
|
/**** Setup Low Level Stuff ****/
|
|
|
|
ioDigitalInputExt<kInput1_PinNumber , 1> _din1;
|
|
ioDigitalInputExt<kInput2_PinNumber , 2> _din2;
|
|
ioDigitalInputExt<kInput3_PinNumber , 3> _din3;
|
|
ioDigitalInputExt<kInput4_PinNumber , 4> _din4;
|
|
ioDigitalInputExt<kInput5_PinNumber , 5> _din5;
|
|
ioDigitalInputExt<kInput6_PinNumber , 6> _din6;
|
|
ioDigitalInputExt<kInput7_PinNumber , 7> _din7;
|
|
ioDigitalInputExt<kInput8_PinNumber , 8> _din8;
|
|
ioDigitalInputExt<kInput9_PinNumber , 9> _din9;
|
|
ioDigitalInputExt<kInput10_PinNumber , 10> _din10;
|
|
ioDigitalInputExt<kInput11_PinNumber , 11> _din11;
|
|
ioDigitalInputExt<kInput12_PinNumber , 12> _din12;
|
|
|
|
// Generated with:
|
|
// perl -e 'for($i=1;$i<14;$i++) { print "#if OUTPUT${i}_PWM == 1\nstatic PWMOutputPin<kOutput${i}_PinNumber> output_${i}_pin;\n#else\nstatic PWMLikeOutputPin<kOutput${i}_PinNumber> output_${i}_pin;\n#endif\n";}'
|
|
// BEGIN generated
|
|
#if OUTPUT1_PWM == 1
|
|
static PWMOutputPin<kOutput1_PinNumber> output_1_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput1_PinNumber> output_1_pin;
|
|
#endif
|
|
#if OUTPUT2_PWM == 1
|
|
static PWMOutputPin<kOutput2_PinNumber> output_2_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput2_PinNumber> output_2_pin;
|
|
#endif
|
|
#if OUTPUT3_PWM == 1
|
|
static PWMOutputPin<kOutput3_PinNumber> output_3_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput3_PinNumber> output_3_pin;
|
|
#endif
|
|
#if OUTPUT4_PWM == 1
|
|
static PWMOutputPin<kOutput4_PinNumber> output_4_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput4_PinNumber> output_4_pin;
|
|
#endif
|
|
#if OUTPUT5_PWM == 1
|
|
static PWMOutputPin<kOutput5_PinNumber> output_5_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput5_PinNumber> output_5_pin;
|
|
#endif
|
|
#if OUTPUT6_PWM == 1
|
|
static PWMOutputPin<kOutput6_PinNumber> output_6_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput6_PinNumber> output_6_pin;
|
|
#endif
|
|
#if OUTPUT7_PWM == 1
|
|
static PWMOutputPin<kOutput7_PinNumber> output_7_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput7_PinNumber> output_7_pin;
|
|
#endif
|
|
#if OUTPUT8_PWM == 1
|
|
static PWMOutputPin<kOutput8_PinNumber> output_8_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput8_PinNumber> output_8_pin;
|
|
#endif
|
|
#if OUTPUT9_PWM == 1
|
|
static PWMOutputPin<kOutput9_PinNumber> output_9_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput9_PinNumber> output_9_pin;
|
|
#endif
|
|
#if OUTPUT10_PWM == 1
|
|
static PWMOutputPin<kOutput10_PinNumber> output_10_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput10_PinNumber> output_10_pin;
|
|
#endif
|
|
#if OUTPUT11_PWM == 1
|
|
static PWMOutputPin<kOutput11_PinNumber> output_11_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput11_PinNumber> output_11_pin;
|
|
#endif
|
|
#if OUTPUT12_PWM == 1
|
|
static PWMOutputPin<kOutput12_PinNumber> output_12_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput12_PinNumber> output_12_pin;
|
|
#endif
|
|
#if OUTPUT13_PWM == 1
|
|
static PWMOutputPin<kOutput13_PinNumber> output_13_pin {kNormal, 200000};
|
|
#else
|
|
static PWMLikeOutputPin<kOutput13_PinNumber> output_13_pin;
|
|
#endif
|
|
// END generated
|
|
|
|
/************************************************************************************
|
|
**** CODE **************************************************************************
|
|
************************************************************************************/
|
|
/*
|
|
* gpio_init() - initialize inputs and outputs
|
|
* gpio_reset() - reset inputs and outputs (no initialization)
|
|
*/
|
|
|
|
void gpio_init(void)
|
|
{
|
|
// These are here due to an unfound glitch where for Timer-based pins the frequency isn't getting set.
|
|
|
|
// Generated with:
|
|
// perl -e 'for($i=1;$i<14;$i++) { print "output_${i}_pin.setFrequency(200000);\n";}'
|
|
// BEGIN generated
|
|
output_1_pin.setFrequency(200000);
|
|
output_2_pin.setFrequency(200000);
|
|
output_3_pin.setFrequency(200000);
|
|
output_4_pin.setFrequency(200000);
|
|
output_5_pin.setFrequency(200000);
|
|
output_6_pin.setFrequency(200000);
|
|
output_7_pin.setFrequency(200000);
|
|
output_8_pin.setFrequency(200000);
|
|
output_9_pin.setFrequency(200000);
|
|
output_10_pin.setFrequency(200000);
|
|
output_11_pin.setFrequency(200000);
|
|
output_12_pin.setFrequency(200000);
|
|
output_13_pin.setFrequency(200000);
|
|
// END generated
|
|
|
|
return(gpio_reset());
|
|
}
|
|
|
|
void outputs_reset(void) {
|
|
// If the output is ACTIVE_LOW set it to 1. ACTIVE_HIGH gets set to 0.
|
|
if (d_out[1-1].mode != IO_MODE_DISABLED) { (output_1_pin = (d_out[1-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
if (d_out[2-1].mode != IO_MODE_DISABLED) { (output_2_pin = (d_out[2-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
if (d_out[3-1].mode != IO_MODE_DISABLED) { (output_3_pin = (d_out[3-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
if (d_out[4-1].mode != IO_MODE_DISABLED) { (output_4_pin = (d_out[4-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
if (d_out[5-1].mode != IO_MODE_DISABLED) { (output_5_pin = (d_out[5-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
#if D_OUT_CHANNELS >= 6
|
|
if (d_out[6-1].mode != IO_MODE_DISABLED) { (output_6_pin = (d_out[6-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
#endif
|
|
#if D_OUT_CHANNELS >= 7
|
|
if (d_out[7-1].mode != IO_MODE_DISABLED) { (output_7_pin = (d_out[7-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
#endif
|
|
#if D_OUT_CHANNELS >= 8
|
|
if (d_out[8-1].mode != IO_MODE_DISABLED) { (output_8_pin = (d_out[8-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
#endif
|
|
#if D_OUT_CHANNELS >= 9
|
|
if (d_out[9-1].mode != IO_MODE_DISABLED) { (output_9_pin = (d_out[9-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
#endif
|
|
#if D_OUT_CHANNELS >= 10
|
|
if (d_out[10-1].mode != IO_MODE_DISABLED) { (output_10_pin = (d_out[10-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
#endif
|
|
#if D_OUT_CHANNELS >= 11
|
|
if (d_out[11-1].mode != IO_MODE_DISABLED) { (output_11_pin = (d_out[11-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
#endif
|
|
#if D_OUT_CHANNELS >= 12
|
|
if (d_out[12-1].mode != IO_MODE_DISABLED) { (output_12_pin = (d_out[12-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
#endif
|
|
#if D_OUT_CHANNELS >= 13
|
|
if (d_out[13-1].mode != IO_MODE_DISABLED) { (output_13_pin = (d_out[13-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); }
|
|
#endif
|
|
}
|
|
|
|
void inputs_reset(void) {
|
|
d_in_t *in;
|
|
|
|
for (uint8_t i=0; i<D_IN_CHANNELS; i++) {
|
|
in = &d_in[i];
|
|
if (in->mode == IO_MODE_DISABLED) {
|
|
in->state = INPUT_DISABLED;
|
|
continue;
|
|
}
|
|
in->lockout_ms = INPUT_LOCKOUT_MS;
|
|
in->lockout_timer.clear();
|
|
}
|
|
|
|
_din1.reset();
|
|
_din2.reset();
|
|
_din3.reset();
|
|
_din4.reset();
|
|
_din5.reset();
|
|
_din6.reset();
|
|
_din7.reset();
|
|
_din8.reset();
|
|
_din9.reset();
|
|
_din10.reset();
|
|
_din11.reset();
|
|
_din12.reset();
|
|
|
|
}
|
|
|
|
void gpio_reset(void)
|
|
{
|
|
inputs_reset();
|
|
outputs_reset();
|
|
}
|
|
|
|
/******************************
|
|
* Interrupt Service Routines *
|
|
******************************/
|
|
/*
|
|
* ARM pin change interrupts are setup above when defining the IRQPins (inside the ioDigitalInputExt).
|
|
*/
|
|
|
|
/********************************************
|
|
**** Digital Input Supporting Functions ****
|
|
********************************************/
|
|
/*
|
|
* switch_rtc_callback() - called from RTC for each RTC tick.
|
|
*
|
|
* Each switch has a counter which is initially set to negative SW_DEGLITCH_TICKS.
|
|
* When a switch closure is DETECTED the count increments for each RTC tick.
|
|
* When the count reaches zero the switch is tripped and action occurs.
|
|
* The counter continues to increment positive until the lockout is exceeded.
|
|
*/
|
|
|
|
/*
|
|
* gpio_set_homing_mode() - set/clear input to homing mode
|
|
* gpio_set_probing_mode() - set/clear input to probing mode
|
|
* gpio_read_input() - read conditioned input
|
|
*
|
|
(* Note: input_num_ext means EXTERNAL input number -- 1-based
|
|
*/
|
|
void gpio_set_homing_mode(const uint8_t input_num_ext, const bool is_homing)
|
|
{
|
|
if (input_num_ext == 0) {
|
|
return;
|
|
}
|
|
d_in[input_num_ext-1].homing_mode = is_homing;
|
|
}
|
|
|
|
void gpio_set_probing_mode(const uint8_t input_num_ext, const bool is_probing)
|
|
{
|
|
if (input_num_ext == 0) {
|
|
return;
|
|
}
|
|
d_in[input_num_ext-1].probing_mode = is_probing;
|
|
}
|
|
|
|
bool gpio_read_input(const uint8_t input_num_ext)
|
|
{
|
|
if (input_num_ext == 0) {
|
|
return false;
|
|
}
|
|
return (d_in[input_num_ext-1].state);
|
|
}
|
|
|
|
|
|
/***********************************************************************************
|
|
* CONFIGURATION AND INTERFACE FUNCTIONS
|
|
* Functions to get and set variables from the cfgArray table
|
|
* These functions are not part of the NIST defined functions
|
|
***********************************************************************************/
|
|
|
|
static stat_t _input_set_helper(nvObj_t *nv, const int8_t lower_bound, const int8_t upper_bound)
|
|
{
|
|
if ((nv->value < lower_bound) || (nv->value >= upper_bound)) {
|
|
return (STAT_INPUT_VALUE_RANGE_ERROR);
|
|
}
|
|
set_ui8(nv); // will this work if -1 is a valid value?
|
|
if (cm_get_machine_state() != MACHINE_INITIALIZING) {
|
|
inputs_reset();
|
|
}
|
|
return (STAT_OK);
|
|
}
|
|
|
|
static stat_t _output_set_helper(nvObj_t *nv, const int8_t lower_bound, const int8_t upper_bound)
|
|
{
|
|
if ((nv->value < lower_bound) || (nv->value >= upper_bound)) {
|
|
return (STAT_INPUT_VALUE_RANGE_ERROR);
|
|
}
|
|
set_ui8(nv); // will this work in -1 is a valid value?
|
|
if (cm_get_machine_state() != MACHINE_INITIALIZING) {
|
|
outputs_reset();
|
|
}
|
|
return (STAT_OK);
|
|
}
|
|
|
|
stat_t io_set_mo(nvObj_t *nv) // input type or disabled
|
|
{
|
|
return (_input_set_helper(nv, IO_ACTIVE_LOW, IO_MODE_MAX));
|
|
}
|
|
|
|
stat_t io_set_ac(nvObj_t *nv) // input action
|
|
{
|
|
return (_input_set_helper(nv, INPUT_ACTION_NONE, INPUT_ACTION_MAX));
|
|
}
|
|
|
|
stat_t io_set_fn(nvObj_t *nv) // input function
|
|
{
|
|
return (_input_set_helper(nv, INPUT_FUNCTION_NONE, INPUT_FUNCTION_MAX));
|
|
}
|
|
|
|
/*
|
|
* io_get_input() - return input state given an nv object
|
|
*/
|
|
stat_t io_get_input(nvObj_t *nv)
|
|
{
|
|
char *num_start = nv->token;
|
|
if (*(nv->group) == 0) {
|
|
// if we don't have a group, then the group name is in the token
|
|
// skip over "in"
|
|
num_start+=2;
|
|
}
|
|
nv->value = d_in[strtol(num_start, NULL, 10)-1].state;
|
|
|
|
if (nv->value > 1.1) {
|
|
nv->valuetype = TYPE_NULL;
|
|
} else {
|
|
nv->valuetype = TYPE_BOOL;
|
|
}
|
|
return (STAT_OK);
|
|
}
|
|
|
|
stat_t io_set_domode(nvObj_t *nv) // output function
|
|
{
|
|
char *num_start = nv->token;
|
|
if (*(nv->group) == 0) {
|
|
// if we don't have a group, then the group name is in the token
|
|
// skip over "out"
|
|
num_start+=3;
|
|
}
|
|
// the token has been stripped down to an ASCII digit string - use it as an index
|
|
uint8_t output_num = strtol(num_start, NULL, 10);
|
|
|
|
if (output_num > D_OUT_CHANNELS) {
|
|
nv->valuetype = TYPE_NULL;
|
|
return(STAT_NO_GPIO);
|
|
}
|
|
|
|
// Force pins that aren't available to be "disabled"
|
|
switch (output_num) {
|
|
case 1: if (output_1_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
case 2: if (output_2_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
case 3: if (output_3_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
case 4: if (output_4_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
case 5: if (output_5_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
case 6: if (output_6_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
case 7: if (output_7_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
case 8: if (output_8_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
case 9: if (output_9_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
case 10: if (output_10_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
case 11: if (output_11_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
case 12: if (output_12_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
case 13: if (output_13_pin.isNull()) { nv->value = IO_MODE_DISABLED; } break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (_output_set_helper(nv, IO_ACTIVE_LOW, IO_MODE_MAX));
|
|
}
|
|
|
|
/*
|
|
* io_get_output() - return output state given an nv object
|
|
*/
|
|
stat_t io_get_output(nvObj_t *nv)
|
|
{
|
|
char *num_start = nv->token;
|
|
if (*(nv->group) == 0) {
|
|
// if we don't have a group, then the group name is in the token
|
|
// skip over "out"
|
|
num_start+=3;
|
|
}
|
|
// the token has been stripped down to an ASCII digit string - use it as an index
|
|
uint8_t output_num = strtol(num_start, NULL, 10);
|
|
|
|
if (output_num > D_OUT_CHANNELS) {
|
|
nv->valuetype = TYPE_NULL;
|
|
return(STAT_NO_GPIO);
|
|
}
|
|
|
|
ioMode outMode = d_out[output_num-1].mode;
|
|
if (outMode == IO_MODE_DISABLED) {
|
|
// nv->value = 0;
|
|
nv->valuetype = TYPE_NULL; // reports back as NULL
|
|
} else {
|
|
nv->valuetype = TYPE_FLOAT;
|
|
nv->precision = 2;
|
|
bool invert = (outMode == 0);
|
|
// Note: !! forces a value to boolean 0 or 1
|
|
switch (output_num) {
|
|
case 1: { nv->value = (float)output_1_pin; } break;
|
|
case 2: { nv->value = (float)output_2_pin; } break;
|
|
case 3: { nv->value = (float)output_3_pin; } break;
|
|
case 4: { nv->value = (float)output_4_pin; } break;
|
|
case 5: { nv->value = (float)output_5_pin; } break;
|
|
case 6: { nv->value = (float)output_6_pin; } break;
|
|
case 7: { nv->value = (float)output_7_pin; } break;
|
|
case 8: { nv->value = (float)output_8_pin; } break;
|
|
case 9: { nv->value = (float)output_9_pin; } break;
|
|
case 10: { nv->value = (float)output_10_pin; } break;
|
|
case 11: { nv->value = (float)output_11_pin; } break;
|
|
case 12: { nv->value = (float)output_12_pin; } break;
|
|
case 13: { nv->value = (float)output_13_pin; } break;
|
|
|
|
default:
|
|
{
|
|
// nv->value = 0; // inactive
|
|
nv->valuetype = TYPE_NULL; // reports back as NULL
|
|
}
|
|
}
|
|
if (invert) {
|
|
nv->value = 1.0 - nv->value;
|
|
}
|
|
}
|
|
return (STAT_OK);
|
|
}
|
|
|
|
/*
|
|
* io_set_output() - return input state given an nv object
|
|
*/
|
|
stat_t io_set_output(nvObj_t *nv)
|
|
{
|
|
char *num_start = nv->token;
|
|
if (*(nv->group) == 0) {
|
|
// if we don't have a group, then the group name is in the token
|
|
// skip over "out"
|
|
num_start+=3;
|
|
}
|
|
// the token has been stripped down to an ASCII digit string - use it as an index
|
|
uint8_t output_num = strtol(num_start, NULL, 10);
|
|
|
|
ioMode outMode = d_out[output_num-1].mode;
|
|
if (outMode == IO_MODE_DISABLED) {
|
|
nv->value = 0; // Inactive?
|
|
} else {
|
|
bool invert = (outMode == 0);
|
|
float value = nv->value;
|
|
if (invert) {
|
|
value = 1.0 - value;
|
|
}
|
|
switch (output_num) {
|
|
// Generated with:
|
|
// perl -e 'for($i=1;$i<14;$i++) { print "case ${i}: { output_${i}_pin = value; } break;\n";}'
|
|
// BEGIN generated
|
|
case 1: { output_1_pin = value; } break;
|
|
case 2: { output_2_pin = value; } break;
|
|
case 3: { output_3_pin = value; } break;
|
|
case 4: { output_4_pin = value; } break;
|
|
case 5: { output_5_pin = value; } break;
|
|
case 6: { output_6_pin = value; } break;
|
|
case 7: { output_7_pin = value; } break;
|
|
case 8: { output_8_pin = value; } break;
|
|
case 9: { output_9_pin = value; } break;
|
|
case 10: { output_10_pin = value; } break;
|
|
case 11: { output_11_pin = value; } break;
|
|
case 12: { output_12_pin = value; } break;
|
|
case 13: { output_13_pin = value; } break;
|
|
// END generated
|
|
default: { nv->value = 0; } // inactive
|
|
}
|
|
}
|
|
return (STAT_OK);
|
|
}
|
|
|
|
/***********************************************************************************
|
|
* TEXT MODE SUPPORT
|
|
* Functions to print variables from the cfgArray table
|
|
***********************************************************************************/
|
|
|
|
#ifdef __TEXT_MODE
|
|
|
|
static const char fmt_gpio_mo[] = "[%smo] input mode%17d [0=NO,1=NC,2=disabled]\n";
|
|
static const char fmt_gpio_ac[] = "[%sac] input action%15d [0=none,1=stop,2=fast_stop,3=halt,4=alarm,5=shutdown,6=panic,7=reset]\n";
|
|
static const char fmt_gpio_fn[] = "[%sfn] input function%13d [0=none,1=limit,2=interlock,3=shutdown]\n";
|
|
static const char fmt_gpio_in[] = "Input %s state: %5d\n";
|
|
|
|
static const char fmt_gpio_domode[] = "[%smo] output mode%16d [0=active low,1=active high,2=disabled]\n";
|
|
static const char fmt_gpio_out[] = "Output %s state: %5d\n";
|
|
|
|
static void _print_di(nvObj_t *nv, const char *format)
|
|
{
|
|
sprintf(cs.out_buf, format, nv->group, (int)nv->value);
|
|
xio_writeline(cs.out_buf);
|
|
}
|
|
void io_print_mo(nvObj_t *nv) {_print_di(nv, fmt_gpio_mo);}
|
|
void io_print_ac(nvObj_t *nv) {_print_di(nv, fmt_gpio_ac);}
|
|
void io_print_fn(nvObj_t *nv) {_print_di(nv, fmt_gpio_fn);}
|
|
void io_print_in(nvObj_t *nv) {
|
|
sprintf(cs.out_buf, fmt_gpio_in, nv->token, (int)nv->value);
|
|
xio_writeline(cs.out_buf);
|
|
}
|
|
|
|
void io_print_domode(nvObj_t *nv) {_print_di(nv, fmt_gpio_domode);}
|
|
void io_print_out(nvObj_t *nv) {
|
|
sprintf(cs.out_buf, fmt_gpio_out, nv->token, (int)nv->value);
|
|
xio_writeline(cs.out_buf);
|
|
}
|
|
#endif
|