diff --git a/.gitignore b/.gitignore
index b54877c5..0a6475fe 100755
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,5 @@ Tools/gcc-arm-none-eabi-*.tar.bz2
*/TinyG2.componentinfo.xml
g2core/g2core.componentinfo.xml
.tags
+
+g2core/compile_commands.json
diff --git a/Motate b/Motate
index 1540f1af..18c93112 160000
--- a/Motate
+++ b/Motate
@@ -1 +1 @@
-Subproject commit 1540f1af7fd2213a704fc8585513effae041f9f1
+Subproject commit 18c93112f1e1e13eb0fa740e185c4b973e4419fc
diff --git a/README.md b/README.md
index 9d70f08d..6592e57f 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+
+
[](https://travis-ci.org/synthetos/g2) [](http://waffle.io/synthetos/g2) [](http://waffle.io/synthetos/g2)
# g2core - Edge Branch
diff --git a/Resources/debug/mb_analyze.py b/Resources/debug/mb_analyze.py
index 13ab4deb..3e3368cb 100644
--- a/Resources/debug/mb_analyze.py
+++ b/Resources/debug/mb_analyze.py
@@ -25,13 +25,13 @@ def load_pool(filename):
if buf:
current_buffer = int(buf.groups()[0], 16)
pool[TUMBLER[tumbler_pos]] = current_buffer
- # print "TUMBLER[tumbler_pos]: %s=%i" % (TUMBLER[tumbler_pos], current_buffer)
+ print "TUMBLER[tumbler_pos]: %s=%i" % (TUMBLER[tumbler_pos], current_buffer)
tumbler_pos += 1
else:
buf = re.search('\(mpBuf_t\s\*\)\s(0x[0-9a-fA-F]+)', line)
if buf:
current_buffer = int(buf.groups()[0], 16)
- # print "setup current_buffer: %s" % current_buffer
+ print "setup current_buffer: %s" % current_buffer
pool[current_buffer] = {'move_time': 0}
if current_buffer == pool['r']:
pool[current_buffer]['is_r'] = True
@@ -49,9 +49,11 @@ def load_pool(filename):
if key == 'pv':
pv = int(val, 16)
pool[current_buffer]['pv'] = pv
+ print "pv: %s" % pv
elif key == 'nx':
- nv = int(val, 16)
- pool[current_buffer]['nx'] = nv
+ nx = int(val, 16)
+ pool[current_buffer]['nx'] = nx
+ print "nx: %s" % nx
elif key == 'buffer_state':
pool[current_buffer]['buffer_state'] = val
elif key == 'jerk':
@@ -60,6 +62,7 @@ def load_pool(filename):
pool[current_buffer]['linenum'] = val
elif key == 'buffer_number':
pool[current_buffer]['buffer_number'] = val
+ print "#: %s" % val
elif key == 'junction_vmax':
pool[current_buffer]['junction_vmax'] = float(val)
elif key == 'cruise_velocity':
diff --git a/g2core.atsln b/g2core.atsln
old mode 100755
new mode 100644
index ca5ee57b..93cd6ae1
--- a/g2core.atsln
+++ b/g2core.atsln
@@ -3,9 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "g2core", "g2core\g2core.cppproj", "{44EA8FEC-55D7-4149-8A78-A574FC26BF51}"
- ProjectSection(ProjectDependencies) = postProject
- {D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0} = {D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0}
- EndProjectSection
EndProject
Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "Motate", "Motate\Motate.cppproj", "{D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0}"
EndProject
@@ -22,6 +19,7 @@ Global
Shapeoko2|ARM = Shapeoko2|ARM
shopbotShield|ARM = shopbotShield|ARM
Stub|ARM = Stub|ARM
+ TestQuadratic|ARM = TestQuadratic|ARM
TestQuintic|ARM = TestQuintic|ARM
TestV9|ARM = TestV9|ARM
Ultimakerv9k|ARM = Ultimakerv9k|ARM
@@ -49,6 +47,8 @@ Global
{44EA8FEC-55D7-4149-8A78-A574FC26BF51}.shopbotShield|ARM.ActiveCfg = shopbotShield|ARM
{44EA8FEC-55D7-4149-8A78-A574FC26BF51}.shopbotShield|ARM.Build.0 = shopbotShield|ARM
{44EA8FEC-55D7-4149-8A78-A574FC26BF51}.Stub|ARM.ActiveCfg = Shapeoko2|ARM
+ {44EA8FEC-55D7-4149-8A78-A574FC26BF51}.TestQuadratic|ARM.ActiveCfg = TestQuadratic|ARM
+ {44EA8FEC-55D7-4149-8A78-A574FC26BF51}.TestQuadratic|ARM.Build.0 = TestQuadratic|ARM
{44EA8FEC-55D7-4149-8A78-A574FC26BF51}.TestQuintic|ARM.ActiveCfg = TestQuintic|ARM
{44EA8FEC-55D7-4149-8A78-A574FC26BF51}.TestQuintic|ARM.Build.0 = TestQuintic|ARM
{44EA8FEC-55D7-4149-8A78-A574FC26BF51}.TestV9|ARM.ActiveCfg = TestV9|ARM
@@ -68,6 +68,7 @@ Global
{D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0}.Shapeoko2|ARM.ActiveCfg = Stub|ARM
{D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0}.shopbotShield|ARM.ActiveCfg = Stub|ARM
{D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0}.Stub|ARM.ActiveCfg = Stub|ARM
+ {D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0}.TestQuadratic|ARM.ActiveCfg = Stub|ARM
{D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0}.TestQuintic|ARM.ActiveCfg = Stub|ARM
{D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0}.TestV9|ARM.ActiveCfg = Stub|ARM
{D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0}.Ultimakerv9k|ARM.ActiveCfg = Stub|ARM
diff --git a/g2core/board/Archim/board_xio.cpp b/g2core/board/Archim/board_xio.cpp
old mode 100755
new mode 100644
index 11599fb7..87ab4dd3
--- a/g2core/board/Archim/board_xio.cpp
+++ b/g2core/board/Archim/board_xio.cpp
@@ -42,20 +42,23 @@ const Motate::USBSettings_t Motate::USBSettings = {
};
/*gProductVersion = */ //0.1,
-//Motate::USBDevice< Motate::USBCDC > usb;
-Motate::USBDevice< Motate::USBCDC, Motate::USBCDC > usb;
+XIOUSBDevice_t usb;
+
+extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
+extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
+
decltype(usb.mixin<0>::Serial) &SerialUSB = usb.mixin<0>::Serial;
-decltype(usb.mixin<1>::Serial) &SerialUSB1 = usb.mixin<1>::Serial;
+//decltype(usb.mixin<1>::Serial) &SerialUSB1 = usb.mixin<1>::Serial;
-// 115200 is the default, as well.
-//UART Serial {115200, UARTMode::RTSCTSFlowControl};
-
-MOTATE_SET_USB_VENDOR_STRING({'S', 'y', 'n', 't', 'h', 'e', 't', 'o', 's'})
-MOTATE_SET_USB_PRODUCT_STRING({'T', 'i', 'n', 'y', 'G', ' ', 'v', '2'})
+MOTATE_SET_USB_VENDOR_STRING( u"Synthetos" )
+MOTATE_SET_USB_PRODUCT_STRING( u"TinyG v2" )
MOTATE_SET_USB_SERIAL_NUMBER_STRING_FROM_CHIPID()
#endif // XIO_HAS_USB
+
//******** SPI ********
#if XIO_HAS_SPI
Motate::SPI spi;
diff --git a/g2core/board/Archim/board_xio.h b/g2core/board/Archim/board_xio.h
index 102698d3..09f20fef 100755
--- a/g2core/board/Archim/board_xio.h
+++ b/g2core/board/Archim/board_xio.h
@@ -29,15 +29,25 @@
#ifndef board_xio_h
#define board_xio_h
+#include "settings.h"
+
//******** USB ********
#if XIO_HAS_USB
#include "MotateUSB.h"
#include "MotateUSBCDC.h"
-// extern Motate::USBDevice< Motate::USBCDC > usb;
-extern Motate::USBDevice usb;
+#if USB_SERIAL_PORTS_EXPOSED == 1
+typedef Motate::USBDevice< Motate::USBCDC > XIOUSBDevice_t;
+#endif
+#if USB_SERIAL_PORTS_EXPOSED == 2
+typedef Motate::USBDevice XIOUSBDevice_t;
+#endif
+
+extern XIOUSBDevice_t usb;
extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
#endif // XIO_HAS_USB
diff --git a/g2core/board/Archim/hardware.h b/g2core/board/Archim/hardware.h
old mode 100755
new mode 100644
index d66cae7d..6da17870
--- a/g2core/board/Archim/hardware.h
+++ b/g2core/board/Archim/hardware.h
@@ -126,9 +126,8 @@ using Motate::OutputPin;
// Timer definitions. See stepper.h and other headers for setup
typedef TimerChannel<3,0> dda_timer_type; // stepper pulse generation in stepper.cpp
-typedef TimerChannel<4,0> dwell_timer_type; // dwell timing in stepper.cpp
-typedef ServiceCall<0> load_timer_type; // request load timer in stepper.cpp
-typedef ServiceCall<1> exec_timer_type; // request exec timer in stepper.cpp
+typedef TimerChannel<4,0> load_timer_type; // request load timer in stepper.cpp
+typedef ServiceCall<1> exec_timer_type; // request exec timer in stepper.cpp
typedef ServiceCall<2> fwd_plan_timer_type; // request exec timer in stepper.cpp
// Pin assignments
diff --git a/g2core/board/ArduinoDue/board_xio.cpp b/g2core/board/ArduinoDue/board_xio.cpp
index 04d9a326..7f2a0774 100755
--- a/g2core/board/ArduinoDue/board_xio.cpp
+++ b/g2core/board/ArduinoDue/board_xio.cpp
@@ -32,27 +32,31 @@
#include "board_xio.h"
//******** USB ********
+#if XIO_HAS_USB
const Motate::USBSettings_t Motate::USBSettings = {
/*gVendorID = */ 0x1d50,
/*gProductID = */ 0x606d,
/*gProductVersion = */ G2CORE_FIRMWARE_VERSION,
/*gAttributes = */ kUSBConfigAttributeSelfPowered,
- /*gPowerConsumption = */ 500};
-/*gProductVersion = */ // 0.1,
+ /*gPowerConsumption = */ 500
+};
+/*gProductVersion = */ //0.1,
-// Motate::USBDevice< Motate::USBCDC > usb;
-Motate::USBDevice usb;
+XIOUSBDevice_t usb;
-decltype(usb.mixin<0>::Serial)& SerialUSB = usb.mixin<0>::Serial;
-decltype(usb.mixin<1>::Serial)& SerialUSB1 = usb.mixin<1>::Serial;
-
-// 115200 is the default, as well.
-// UART Serial {115200, UARTMode::RTSCTSFlowControl};
+extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
+extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
-MOTATE_SET_USB_VENDOR_STRING({'S', 'y', 'n', 't', 'h', 'e', 't', 'o', 's'})
-MOTATE_SET_USB_PRODUCT_STRING({'T', 'i', 'n', 'y', 'G', ' ', 'v', '2'})
+decltype(usb.mixin<0>::Serial) &SerialUSB = usb.mixin<0>::Serial;
+//decltype(usb.mixin<1>::Serial) &SerialUSB1 = usb.mixin<1>::Serial;
+
+MOTATE_SET_USB_VENDOR_STRING( u"Synthetos" )
+MOTATE_SET_USB_PRODUCT_STRING( u"TinyG v2" )
MOTATE_SET_USB_SERIAL_NUMBER_STRING_FROM_CHIPID()
+#endif // XIO_HAS_USB
diff --git a/g2core/board/ArduinoDue/board_xio.h b/g2core/board/ArduinoDue/board_xio.h
index a7bf7959..31afd8c8 100755
--- a/g2core/board/ArduinoDue/board_xio.h
+++ b/g2core/board/ArduinoDue/board_xio.h
@@ -29,15 +29,26 @@
#ifndef board_xio_h
#define board_xio_h
+#include "settings.h"
+
//******** USB ********
+#if XIO_HAS_USB
#include "MotateUSB.h"
#include "MotateUSBCDC.h"
-// extern Motate::USBDevice< Motate::USBCDC > usb;
-extern Motate::USBDevice usb;
-extern decltype(usb.mixin<0>::Serial)& SerialUSB;
-extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#if USB_SERIAL_PORTS_EXPOSED == 1
+typedef Motate::USBDevice< Motate::USBCDC > XIOUSBDevice_t;
+#endif
+#if USB_SERIAL_PORTS_EXPOSED == 2
+typedef Motate::USBDevice XIOUSBDevice_t;
+#endif
+extern XIOUSBDevice_t usb;
+extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
+extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
+#endif // XIO_HAS_USB
//******** SPI ********
diff --git a/g2core/board/ArduinoDue/hardware.h b/g2core/board/ArduinoDue/hardware.h
old mode 100755
new mode 100644
index 1b5bbe6b..e01e70d4
--- a/g2core/board/ArduinoDue/hardware.h
+++ b/g2core/board/ArduinoDue/hardware.h
@@ -128,9 +128,8 @@ using Motate::OutputPin;
// Timer definitions. See stepper.h and other headers for setup
typedef TimerChannel<3,0> dda_timer_type; // stepper pulse generation in stepper.cpp
-typedef TimerChannel<4,0> dwell_timer_type; // dwell timing in stepper.cpp
-typedef ServiceCall<0> load_timer_type; // request load timer in stepper.cpp
-typedef ServiceCall<1> exec_timer_type; // request exec timer in stepper.cpp
+typedef TimerChannel<4,0> load_timer_type; // request load timer in stepper.cpp
+typedef ServiceCall<1> exec_timer_type; // request exec timer in stepper.cpp
typedef ServiceCall<2> fwd_plan_timer_type; // request exec timer in stepper.cpp
// Pin assignments
diff --git a/g2core/board/G2v9/board_xio.cpp b/g2core/board/G2v9/board_xio.cpp
index 04d9a326..09ccaf80 100755
--- a/g2core/board/G2v9/board_xio.cpp
+++ b/g2core/board/G2v9/board_xio.cpp
@@ -32,28 +32,31 @@
#include "board_xio.h"
//******** USB ********
+#if XIO_HAS_USB
const Motate::USBSettings_t Motate::USBSettings = {
/*gVendorID = */ 0x1d50,
/*gProductID = */ 0x606d,
/*gProductVersion = */ G2CORE_FIRMWARE_VERSION,
/*gAttributes = */ kUSBConfigAttributeSelfPowered,
- /*gPowerConsumption = */ 500};
-/*gProductVersion = */ // 0.1,
+ /*gPowerConsumption = */ 500
+};
+/*gProductVersion = */ //0.1,
-// Motate::USBDevice< Motate::USBCDC > usb;
-Motate::USBDevice usb;
+XIOUSBDevice_t usb;
-decltype(usb.mixin<0>::Serial)& SerialUSB = usb.mixin<0>::Serial;
-decltype(usb.mixin<1>::Serial)& SerialUSB1 = usb.mixin<1>::Serial;
-
-// 115200 is the default, as well.
-// UART Serial {115200, UARTMode::RTSCTSFlowControl};
+extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
+extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
-MOTATE_SET_USB_VENDOR_STRING({'S', 'y', 'n', 't', 'h', 'e', 't', 'o', 's'})
-MOTATE_SET_USB_PRODUCT_STRING({'T', 'i', 'n', 'y', 'G', ' ', 'v', '2'})
+decltype(usb.mixin<0>::Serial) &SerialUSB = usb.mixin<0>::Serial;
+//decltype(usb.mixin<1>::Serial) &SerialUSB1 = usb.mixin<1>::Serial;
+
+MOTATE_SET_USB_VENDOR_STRING( u"Synthetos" )
+MOTATE_SET_USB_PRODUCT_STRING( u"TinyG v2" )
MOTATE_SET_USB_SERIAL_NUMBER_STRING_FROM_CHIPID()
-
+#endif // XIO_HAS_USB
//******** SPI ********
diff --git a/g2core/board/G2v9/board_xio.h b/g2core/board/G2v9/board_xio.h
index a7bf7959..09f20fef 100755
--- a/g2core/board/G2v9/board_xio.h
+++ b/g2core/board/G2v9/board_xio.h
@@ -29,15 +29,26 @@
#ifndef board_xio_h
#define board_xio_h
+#include "settings.h"
+
//******** USB ********
+#if XIO_HAS_USB
#include "MotateUSB.h"
#include "MotateUSBCDC.h"
-// extern Motate::USBDevice< Motate::USBCDC > usb;
-extern Motate::USBDevice usb;
-extern decltype(usb.mixin<0>::Serial)& SerialUSB;
-extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#if USB_SERIAL_PORTS_EXPOSED == 1
+typedef Motate::USBDevice< Motate::USBCDC > XIOUSBDevice_t;
+#endif
+#if USB_SERIAL_PORTS_EXPOSED == 2
+typedef Motate::USBDevice XIOUSBDevice_t;
+#endif
+extern XIOUSBDevice_t usb;
+extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
+extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
+#endif // XIO_HAS_USB
//******** SPI ********
@@ -52,9 +63,8 @@ extern Motate::SPI spi;
extern Motate::UART Serial;
#endif
-
-
//******* Generic Functions *******
-void board_xio_init(void);
+void board_hardware_init(void); // called 1st
+void board_xio_init(void); // called later
#endif // board_xio_h
diff --git a/g2core/board/G2v9/hardware.cpp b/g2core/board/G2v9/hardware.cpp
index 580ed646..45d63c37 100755
--- a/g2core/board/G2v9/hardware.cpp
+++ b/g2core/board/G2v9/hardware.cpp
@@ -43,6 +43,7 @@
void hardware_init()
{
+ board_hardware_init();
return;
}
diff --git a/g2core/board/G2v9/hardware.h b/g2core/board/G2v9/hardware.h
index 17ac7bf3..b8f19f4a 100644
--- a/g2core/board/G2v9/hardware.h
+++ b/g2core/board/G2v9/hardware.h
@@ -129,9 +129,8 @@ using Motate::OutputPin;
// Timer definitions. See stepper.h and other headers for setup
typedef TimerChannel<3,0> dda_timer_type; // stepper pulse generation in stepper.cpp
-typedef TimerChannel<4,0> dwell_timer_type; // dwell timing in stepper.cpp
-typedef ServiceCall<0> load_timer_type; // request load timer in stepper.cpp
-typedef ServiceCall<1> exec_timer_type; // request exec timer in stepper.cpp
+typedef TimerChannel<4,0> load_timer_type; // request load timer in stepper.cpp
+typedef ServiceCall<1> exec_timer_type; // request exec timer in stepper.cpp
typedef ServiceCall<2> fwd_plan_timer_type; // request exec timer in stepper.cpp
// Pin assignments
diff --git a/g2core/board/gquadratic.mk b/g2core/board/gquadratic.mk
index 14bae333..9527cdc1 100755
--- a/g2core/board/gquadratic.mk
+++ b/g2core/board/gquadratic.mk
@@ -12,15 +12,15 @@
##########
# BOARDs for use directly from the make command line (with default settings) or by CONFIGs.
-ifeq ("$(BOARD)","gquadratic-a")
+ifeq ("$(BOARD)","gquadratic-b")
BASE_BOARD=gquadratic
- DEVICE_DEFINES += MOTATE_BOARD="gquadratic-a"
+ DEVICE_DEFINES += MOTATE_BOARD="gquadratic-b"
DEVICE_DEFINES += SETTINGS_FILE=${SETTINGS_FILE}
endif
##########
-# The general pboard-a BASE_BOARD.
+# The general gquadratic BASE_BOARD.
ifeq ("$(BASE_BOARD)","gquadratic")
_BOARD_FOUND = 1
@@ -34,7 +34,7 @@ ifeq ("$(BASE_BOARD)","gquadratic")
CHIP_LOWERCASE = sams70n19
BOARD_PATH = ./board/gquadratic
- SOURCE_DIRS += ${BOARD_PATH} device/step_dir_driver
+ SOURCE_DIRS += ${BOARD_PATH} device/step_dir_driver device/step_dir_hobbyservo device/neopixel
PLATFORM_BASE = ${MOTATE_PATH}/platform/atmel_sam
include $(PLATFORM_BASE).mk
diff --git a/g2core/board/gquadratic/board_stepper.cpp b/g2core/board/gquadratic/board_stepper.cpp
index 183d946c..bc927558 100755
--- a/g2core/board/gquadratic/board_stepper.cpp
+++ b/g2core/board/gquadratic/board_stepper.cpp
@@ -47,15 +47,8 @@ StepDirStepper
motor_2{};
-// StepDirStepper<
-// Motate::kSocket3_StepPinNumber,
-// Motate::kSocket3_DirPinNumber,
-// Motate::kSocket3_EnablePinNumber,
-// Motate::kSocket3_Microstep_0PinNumber,
-// Motate::kSocket3_Microstep_1PinNumber,
-// Motate::kSocket3_Microstep_2PinNumber,
-// Motate::kSocket3_VrefPinNumber> motor_3 {};
-//
+StepDirHobbyServo motor_3;
+
// StepDirStepper<
// Motate::kSocket4_StepPinNumber,
// Motate::kSocket4_DirPinNumber,
@@ -83,7 +76,7 @@ StepDirStepper motor_6 {};
-Stepper* Motors[MOTORS] = {&motor_1, &motor_2};
+Stepper* Motors[MOTORS] = {&motor_1, &motor_2, &motor_3};
void board_stepper_init() {
for (uint8_t motor = 0; motor < MOTORS; motor++) { Motors[motor]->init(); }
diff --git a/g2core/board/gquadratic/board_stepper.h b/g2core/board/gquadratic/board_stepper.h
index 1282ff0e..6e044efa 100755
--- a/g2core/board/gquadratic/board_stepper.h
+++ b/g2core/board/gquadratic/board_stepper.h
@@ -30,6 +30,7 @@
#include "hardware.h" // for MOTORS
#include "step_dir_driver.h"
+#include "step_dir_hobbyservo.h"
extern StepDirStepper
motor_2;
-// extern StepDirStepper<
-// Motate::kSocket3_StepPinNumber,
-// Motate::kSocket3_DirPinNumber,
-// Motate::kSocket3_EnablePinNumber,
-// Motate::kSocket3_Microstep_0PinNumber,
-// Motate::kSocket3_Microstep_1PinNumber,
-// Motate::kSocket3_Microstep_2PinNumber,
-// Motate::kSocket3_VrefPinNumber> motor_3;
-//
-// extern StepDirStepper<
-// Motate::kSocket4_StepPinNumber,
-// Motate::kSocket4_DirPinNumber,
-// Motate::kSocket4_EnablePinNumber,
-// Motate::kSocket4_Microstep_0PinNumber,
-// Motate::kSocket4_Microstep_1PinNumber,
-// Motate::kSocket4_Microstep_2PinNumber,
-// Motate::kSocket4_VrefPinNumber> motor_4;
-//
+ extern StepDirHobbyServo motor_3;
+
// extern StepDirStepper<
// Motate::kSocket5_StepPinNumber,
// Motate::kSocket5_DirPinNumber,
@@ -89,4 +74,4 @@ extern Stepper* Motors[MOTORS];
void board_stepper_init();
-#endif // BOARD_STEPPER_H_ONCE
\ No newline at end of file
+#endif // BOARD_STEPPER_H_ONCE
diff --git a/g2core/board/gquadratic/board_xio.cpp b/g2core/board/gquadratic/board_xio.cpp
index 028c60a5..8b4835b2 100755
--- a/g2core/board/gquadratic/board_xio.cpp
+++ b/g2core/board/gquadratic/board_xio.cpp
@@ -42,20 +42,23 @@ const Motate::USBSettings_t Motate::USBSettings = {
};
/*gProductVersion = */ //0.1,
-//Motate::USBDevice< Motate::USBCDC > usb;
-Motate::USBDevice< Motate::USBCDC, Motate::USBCDC > usb;
+XIOUSBDevice_t usb;
+
+extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
+extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
+
decltype(usb.mixin<0>::Serial) &SerialUSB = usb.mixin<0>::Serial;
-decltype(usb.mixin<1>::Serial) &SerialUSB1 = usb.mixin<1>::Serial;
+//decltype(usb.mixin<1>::Serial) &SerialUSB1 = usb.mixin<1>::Serial;
-// 115200 is the default, as well.
-//UART Serial {115200, UARTMode::RTSCTSFlowControl};
-
-MOTATE_SET_USB_VENDOR_STRING( u"Synthetos (http://synthetos.com)" )
-MOTATE_SET_USB_PRODUCT_STRING( u"g2core gQuadratic" )
+MOTATE_SET_USB_VENDOR_STRING( u"Synthetos" )
+MOTATE_SET_USB_PRODUCT_STRING( u"TinyG v2" )
MOTATE_SET_USB_SERIAL_NUMBER_STRING_FROM_CHIPID()
#endif // XIO_HAS_USB
+
//******** SPI ********
#if XIO_HAS_SPI
Motate::SPI spi;
@@ -64,14 +67,14 @@ Motate::SPI spi;
//******** UART ********
#if XIO_HAS_UART
-Motate::UART Serial {115200, Motate::UARTMode::RTSCTSFlowControl};
+Motate::UART Serial {115200, Motate::UARTMode::RTSCTSFlowControl};
#endif
void board_hardware_init(void) // called 1st
{
#if XIO_HAS_USB
// Init USB
- usb.attach(); // USB setup. Runs in "background" as the rest of this executes
+ usb.attach();
#endif // XIO_HAS_USB
}
diff --git a/g2core/board/gquadratic/board_xio.h b/g2core/board/gquadratic/board_xio.h
index dc57cd3c..09f20fef 100755
--- a/g2core/board/gquadratic/board_xio.h
+++ b/g2core/board/gquadratic/board_xio.h
@@ -29,15 +29,25 @@
#ifndef board_xio_h
#define board_xio_h
+#include "settings.h"
+
//******** USB ********
#if XIO_HAS_USB
#include "MotateUSB.h"
#include "MotateUSBCDC.h"
-// extern Motate::USBDevice< Motate::USBCDC > usb;
-extern Motate::USBDevice usb;
+#if USB_SERIAL_PORTS_EXPOSED == 1
+typedef Motate::USBDevice< Motate::USBCDC > XIOUSBDevice_t;
+#endif
+#if USB_SERIAL_PORTS_EXPOSED == 2
+typedef Motate::USBDevice XIOUSBDevice_t;
+#endif
+
+extern XIOUSBDevice_t usb;
extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
#endif // XIO_HAS_USB
@@ -50,7 +60,7 @@ extern Motate::SPI spi;
//******** UART ********
#if XIO_HAS_UART
#include "MotateUART.h"
-extern Motate::UART Serial;
+extern Motate::UART Serial;
#endif
//******* Generic Functions *******
diff --git a/g2core/board/gquadratic/gquadratic-a-pinout.h b/g2core/board/gquadratic/gquadratic-a-pinout.h
deleted file mode 100755
index 600accfe..00000000
--- a/g2core/board/gquadratic/gquadratic-a-pinout.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * gquadratic-a-pinout.h - board pinout specification
- * This file is part of the g2core project
- *
- * Copyright (c) 2016 Robert Giseburt
- * Copyright (c) 2016 Alden S. Hart Jr.
- *
- * This file is part of the Motate Library.
- *
- * 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 .
- *
- * 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.
- *
- */
-
-#ifndef GQUADRATIC_A_PINOUT_H
-#define GQUADRATIC_A_PINOUT_H
-
-/*
- * USAGE NOTES
- *
- * Read this first:
- * https://github.com/synthetos/g2/wiki/Adding-a-new-G2-board-(or-revision)-to-G2#making-a-new-pin-assignment
- *
- * USAGE:
- *
- * This file is lays out all the pin capabilities of the SAM3X8C organized by pin number.
- * Each pin has its associated functions listed at the bottom of the file, and is essentially
- * immutable for each processor.
- *
- * To use, assign Motate pin numbers to the first value in the _MAKE_MOTATE_PIN() macro.
- * ALL PINS MUST BE ASSIGNED A NUMBER, even if they are not used. There will NOT be a
- * code-size or speed penalty for unused pins, but the WILL be a compiler-failure for
- * unassigned pins. This new restriction allows for simplification of linkages deep in
- * Motate.
- */
-/* See motate_pin_assignments.h for pin names to be used int he rest of the G2 code.
- * EXAMPLES:
- *
- * *** Vanilla pin example ***
- *
- * _MAKE_MOTATE_PIN(4, A, 'A', 27); // SPI0_SCKPinNumber
- *
- * This assigns Motate pin 4 to Port A, pin 27 (A27)
- * Look in motate_pin_assignments.h to see that this is kSPI_SCKPinNumber
- *
- * ** Other pin functions ***
- *
- * Please look in /platform/atmel_sam/motate_chip_pin_functions.h
- */
-
-
-#include
-
-// We don't have all of the inputs, so we have to indicate as much:
-#define INPUT1_AVAILABLE 1
-#define INPUT2_AVAILABLE 1
-#define INPUT3_AVAILABLE 1
-#define INPUT4_AVAILABLE 1
-#define INPUT5_AVAILABLE 0
-#define INPUT6_AVAILABLE 0
-#define INPUT7_AVAILABLE 0
-#define INPUT8_AVAILABLE 0
-#define INPUT9_AVAILABLE 0
-#define INPUT10_AVAILABLE 0
-#define INPUT11_AVAILABLE 0
-#define INPUT12_AVAILABLE 0
-#define INPUT13_AVAILABLE 0
-
-
-#define ADC0_AVAILABLE 0
-#define ADC1_AVAILABLE 0
-#define ADC2_AVAILABLE 0
-#define ADC3_AVAILABLE 0
-
-#define XIO_HAS_USB 1
-#define XIO_HAS_UART 1
-#define XIO_HAS_SPI 0
-#define XIO_HAS_I2C 0
-
-#define TEMPERATURE_OUTPUT_ON 0 // NO ADC yet
-
-// Some pins, if the PWM capability is turned on, it will cause timer conflicts.
-// So we have to explicitly enable them as PWM pins.
-// Generated with:
-// perl -e 'for($i=1;$i<14;$i++) { print "#define OUTPUT${i}_PWM 0\n";}'
-#define OUTPUT1_PWM 1 // Timer0ch0
-#define OUTPUT2_PWM 1 // PWM0.0
-#define OUTPUT3_PWM 1 // Timer0ch1
-#define OUTPUT4_PWM 1 // PWM1.3
-#define OUTPUT5_PWM 0 // unused
-#define OUTPUT6_PWM 0 // unused
-#define OUTPUT7_PWM 0 // unused
-#define OUTPUT8_PWM 0 // unused
-#define OUTPUT9_PWM 0 // unused
-#define OUTPUT10_PWM 0 // unused
-#define OUTPUT11_PWM 0 // unused
-#define OUTPUT12_PWM 0 // Unused
-#define OUTPUT13_PWM 0 // Unused
-
-namespace Motate {
-
-// Arduino pin name & function
-_MAKE_MOTATE_PIN(kOutput1_PinNumber, 'A', 0); //
-_MAKE_MOTATE_PIN(kOutput3_PinNumber, 'A', 1); //
-_MAKE_MOTATE_PIN(kSocket2_VrefPinNumber, 'A', 2); //
-_MAKE_MOTATE_PIN(kI2C1_SDAPinNumber, 'A', 3); //
-_MAKE_MOTATE_PIN(kI2C1_SCLPinNumber, 'A', 4); //
-_MAKE_MOTATE_PIN(kOutput4_PinNumber, 'A', 5); //
-_MAKE_MOTATE_PIN(kUnassigned1, 'A', 6); // Missing in 100-pin package
-_MAKE_MOTATE_PIN(kUnassigned2, 'A', 7); //
-_MAKE_MOTATE_PIN(kUnassigned3, 'A', 8); //
-_MAKE_MOTATE_PIN(kSerial_RX, 'A', 9); //
-_MAKE_MOTATE_PIN(kSerial_TX, 'A', 10); //
-_MAKE_MOTATE_PIN(kSocket1_Microstep_1PinNumber, 'A', 11); //
-_MAKE_MOTATE_PIN(kSocket1_EnablePinNumber, 'A', 12); //
-_MAKE_MOTATE_PIN(kUnassigned4, 'A', 13); //
-_MAKE_MOTATE_PIN(kUnassigned6, 'A', 14); //
-_MAKE_MOTATE_PIN(kUnassigned5, 'A', 15); //
-_MAKE_MOTATE_PIN(kUnassigned7, 'A', 16); //
-_MAKE_MOTATE_PIN(kUnassigned8, 'A', 17); //
-_MAKE_MOTATE_PIN(kUnassigned9, 'A', 18); //
-_MAKE_MOTATE_PIN(kUnassigned10, 'A', 19); //
-_MAKE_MOTATE_PIN(kUnassigned11, 'A', 20); //
-_MAKE_MOTATE_PIN(kUnassigned13, 'A', 21); //
-_MAKE_MOTATE_PIN(kUnassigned12, 'A', 22); //
-_MAKE_MOTATE_PIN(kUnassigned14, 'A', 23); //
-_MAKE_MOTATE_PIN(kUnassigned15, 'A', 24); //
-_MAKE_MOTATE_PIN(kUnassigned16, 'A', 25); //
-_MAKE_MOTATE_PIN(kUnassigned17, 'A', 26); //
-_MAKE_MOTATE_PIN(kSocket1_VrefPinNumber, 'A', 27); //
-_MAKE_MOTATE_PIN(kSerial_CTS, 'A', 28); //
-_MAKE_MOTATE_PIN(kUnassigned18, 'A', 29); // Missing in 100-pin package
-_MAKE_MOTATE_PIN(kSerial_RTS, 'A', 30); //
-_MAKE_MOTATE_PIN(kOutputSAFE_PinNumber, 'A', 31); //
-
-_MAKE_MOTATE_PIN(kUnassigned20, 'B', 0); //
-_MAKE_MOTATE_PIN(kUnassigned22, 'B', 1); //
-_MAKE_MOTATE_PIN(kUnassigned21, 'B', 2); //
-_MAKE_MOTATE_PIN(kUnassigned23, 'B', 3); //
-//_MAKE_MOTATE_PIN( , 'B', 4); // TDI
-//_MAKE_MOTATE_PIN( , 'B', 5); // TRACESDO
-//_MAKE_MOTATE_PIN( , 'B', 6); // SWDIO
-//_MAKE_MOTATE_PIN( , 'B', 7); // SWDCLK
-//_MAKE_MOTATE_PIN( , 'B', 8); // XOUT
-//_MAKE_MOTATE_PIN( , 'B', 9); // XIN
-//_MAKE_MOTATE_PIN( , 'B', 10); // USB_D-
-//_MAKE_MOTATE_PIN( , 'B', 11); // USB_D+
-//_MAKE_MOTATE_PIN( , 'B', 12); // ERASE
-_MAKE_MOTATE_PIN(kUnassigned38, 'B', 13); // LED_1 (Heartbeat) - PWM2
-
-//_MAKE_MOTATE_PIN( , 'D', 0); // USB_VBUS
-_MAKE_MOTATE_PIN(kInput1_PinNumber, 'D', 1); //
-_MAKE_MOTATE_PIN(kInput2_PinNumber, 'D', 2); //
-_MAKE_MOTATE_PIN(kUnassigned24, 'D', 3); //
-_MAKE_MOTATE_PIN(kUnassigned25, 'D', 4); //
-_MAKE_MOTATE_PIN(kUnassigned26, 'D', 5); //
-_MAKE_MOTATE_PIN(kInput3_PinNumber, 'D', 6); //
-_MAKE_MOTATE_PIN(kInput4_PinNumber, 'D', 7); //
-_MAKE_MOTATE_PIN(kUnassigned19, 'D', 8); // INTERRUPT_OUT
-_MAKE_MOTATE_PIN(kUnassigned27, 'D', 9); //
-_MAKE_MOTATE_PIN(kOutput2_PinNumber, 'D', 10); //
-_MAKE_MOTATE_PIN(kUnassigned28, 'D', 11); //
-_MAKE_MOTATE_PIN(kSocket2_DirPinNumber, 'D', 12); //
-_MAKE_MOTATE_PIN(kSocket2_EnablePinNumber, 'D', 13); //
-_MAKE_MOTATE_PIN(kSocket2_StepPinNumber, 'D', 14); //
-_MAKE_MOTATE_PIN(kUnassigned29, 'D', 15); //
-_MAKE_MOTATE_PIN(kSocket2_Microstep_0PinNumber, 'D', 16); //
-_MAKE_MOTATE_PIN(kSocket2_Microstep_1PinNumber, 'D', 17); //
-_MAKE_MOTATE_PIN(kSocket1_DirPinNumber, 'D', 18); //
-_MAKE_MOTATE_PIN(kSocket1_StepPinNumber, 'D', 19); //
-_MAKE_MOTATE_PIN(kSocket1_Microstep_0PinNumber, 'D', 20); //
-_MAKE_MOTATE_PIN(kSocket1_Microstep_2PinNumber, 'D', 21); //
-_MAKE_MOTATE_PIN(kUnassigned30, 'D', 22); //
-_MAKE_MOTATE_PIN(kUnassigned31, 'D', 23); // Missing in 100-pin package
-_MAKE_MOTATE_PIN(kUnassigned32, 'D', 24); //
-_MAKE_MOTATE_PIN(kUnassigned33, 'D', 25); //
-_MAKE_MOTATE_PIN(kUnassigned34, 'D', 26); //
-_MAKE_MOTATE_PIN(kUnassigned35, 'D', 27); //
-_MAKE_MOTATE_PIN(kSocket2_Microstep_2PinNumber, 'D', 28); //
-_MAKE_MOTATE_PIN(kUnassigned36, 'D', 29); // Missing in 100-pin package
-_MAKE_MOTATE_PIN(kLEDPWM_PinNumber, 'D', 30); //
-_MAKE_MOTATE_PIN(kUnassigned37, 'D', 31); //
-
-} // namespace Motate
-
-// We then allow each chip-type to have it's onw function definitions
-// that will refer to these pin assignments.
-#include "motate_chip_pin_functions.h"
-
-#endif
-
-// GQUADRATIC_A_PINOUT_H
diff --git a/g2core/board/gquadratic/gquadratic-b-pinout.h b/g2core/board/gquadratic/gquadratic-b-pinout.h
new file mode 100755
index 00000000..0eaa3c5d
--- /dev/null
+++ b/g2core/board/gquadratic/gquadratic-b-pinout.h
@@ -0,0 +1,209 @@
+/*
+ * gquadratic-b-pinout.h - board pinout specification
+ * This file is part of the g2core project
+ *
+ * Copyright (c) 2016 Robert Giseburt
+ * Copyright (c) 2016 Alden S. Hart Jr.
+ *
+ * This file is part of the Motate Library.
+ *
+ * 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 .
+ *
+ * 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.
+ *
+ */
+
+#ifndef GQUADRATIC_B_PINOUT_H
+#define GQUADRATIC_B_PINOUT_H
+
+/*
+ * USAGE NOTES
+ *
+ * Read this first:
+ * https://github.com/synthetos/g2/wiki/Adding-a-new-G2-board-(or-revision)-to-G2#making-a-new-pin-assignment
+ *
+ * USAGE:
+ *
+ * This file is lays out all the pin capabilities of the SAM3X8C organized by pin number.
+ * Each pin has its associated functions listed at the bottom of the file, and is essentially
+ * immutable for each processor.
+ *
+ * To use, assign Motate pin numbers to the first value in the _MAKE_MOTATE_PIN() macro.
+ * ALL PINS MUST BE ASSIGNED A NUMBER, even if they are not used. There will NOT be a
+ * code-size or speed penalty for unused pins, but the WILL be a compiler-failure for
+ * unassigned pins. This new restriction allows for simplification of linkages deep in
+ * Motate.
+ */
+/* See motate_pin_assignments.h for pin names to be used int he rest of the G2 code.
+ * EXAMPLES:
+ *
+ * *** Vanilla pin example ***
+ *
+ * _MAKE_MOTATE_PIN(4, A, 'A', 27); // SPI0_SCKPinNumber
+ *
+ * This assigns Motate pin 4 to Port A, pin 27 (A27)
+ * Look in motate_pin_assignments.h to see that this is kSPI_SCKPinNumber
+ *
+ * ** Other pin functions ***
+ *
+ * Please look in /platform/atmel_sam/motate_chip_pin_functions.h
+ */
+
+
+#include
+
+// We don't have all of the inputs, so we have to indicate as much:
+#define INPUT1_AVAILABLE 1
+#define INPUT2_AVAILABLE 1
+#define INPUT3_AVAILABLE 1
+#define INPUT4_AVAILABLE 1
+#define INPUT5_AVAILABLE 0
+#define INPUT6_AVAILABLE 0
+#define INPUT7_AVAILABLE 0
+#define INPUT8_AVAILABLE 0
+#define INPUT9_AVAILABLE 0
+#define INPUT10_AVAILABLE 0
+#define INPUT11_AVAILABLE 0
+#define INPUT12_AVAILABLE 0
+#define INPUT13_AVAILABLE 0
+
+
+#define ADC0_AVAILABLE 0
+#define ADC1_AVAILABLE 0
+#define ADC2_AVAILABLE 0
+#define ADC3_AVAILABLE 0
+
+#define XIO_HAS_USB 1
+#define XIO_HAS_UART 1
+#define XIO_HAS_SPI 0
+#define XIO_HAS_I2C 0
+
+#define TEMPERATURE_OUTPUT_ON 0 // NO ADC yet
+
+// Some pins, if the PWM capability is turned on, it will cause timer conflicts.
+// So we have to explicitly enable them as PWM pins.
+// Generated with:
+// perl -e 'for($i=1;$i<14;$i++) { print "#define OUTPUT${i}_PWM 0\n";}'
+#define OUTPUT1_PWM 1 // TC0.0
+#define OUTPUT2_PWM 1 // PWM0.0
+#define OUTPUT3_PWM 1 // TC0.1
+#define OUTPUT4_PWM 1 // PWM1.3
+#define OUTPUT5_PWM 0 // unused
+#define OUTPUT6_PWM 0 // unused
+#define OUTPUT7_PWM 0 // unused
+#define OUTPUT8_PWM 0 // unused
+#define OUTPUT9_PWM 0 // unused
+#define OUTPUT10_PWM 0 // unused
+#define OUTPUT11_PWM 0 // unused
+#define OUTPUT12_PWM 0 // Unused
+#define OUTPUT13_PWM 0 // Unused
+
+namespace Motate {
+
+// Arduino pin name & function
+_MAKE_MOTATE_PIN(kOutput2_PinNumber, 'A', 0); // TC0.0 or PWM0
+_MAKE_MOTATE_PIN(kOutput3_PinNumber, 'A', 1); // TC0.1 or PWM1
+_MAKE_MOTATE_PIN(kSocket2_VrefPinNumber, 'A', 2); // PWM1
+_MAKE_MOTATE_PIN(kI2C1_SDAPinNumber, 'A', 3); //
+_MAKE_MOTATE_PIN(kI2C1_SCLPinNumber, 'A', 4); //
+_MAKE_MOTATE_PIN(kUnassigned1, 'A', 5); //
+_MAKE_MOTATE_PIN(kUnassigned2, 'A', 6); // Missing in 100-pin package
+_MAKE_MOTATE_PIN(kUnassigned3, 'A', 7); //
+_MAKE_MOTATE_PIN(kUnassigned4, 'A', 8); //
+_MAKE_MOTATE_PIN(kSerial_RXPinNumber, 'A', 9); //
+_MAKE_MOTATE_PIN(kSerial_TXPinNumber, 'A', 10); //
+_MAKE_MOTATE_PIN(kSocket1_Microstep_1PinNumber, 'A', 11); //
+_MAKE_MOTATE_PIN(kSocket1_EnablePinNumber, 'A', 12); //
+_MAKE_MOTATE_PIN(kUnassigned5, 'A', 13); //
+_MAKE_MOTATE_PIN(kUnassigned6, 'A', 14); //
+_MAKE_MOTATE_PIN(kUnassigned7, 'A', 15); //
+_MAKE_MOTATE_PIN(kUnassigned8, 'A', 16); //
+_MAKE_MOTATE_PIN(kUnassigned9, 'A', 17); //
+_MAKE_MOTATE_PIN(kUnassigned10, 'A', 18); //
+_MAKE_MOTATE_PIN(kUnassigned11, 'A', 19); //
+_MAKE_MOTATE_PIN(kUnassigned12, 'A', 20); //
+_MAKE_MOTATE_PIN(kUnassigned13, 'A', 21); //
+_MAKE_MOTATE_PIN(kUnassigned14, 'A', 22); //
+_MAKE_MOTATE_PIN(kUnassigned15, 'A', 23); //
+_MAKE_MOTATE_PIN(kUnassigned16, 'A', 24); //
+_MAKE_MOTATE_PIN(kUnassigned17, 'A', 25); //
+_MAKE_MOTATE_PIN(kUnassigned18, 'A', 26); //
+_MAKE_MOTATE_PIN(kSocket1_VrefPinNumber, 'A', 27); // TC2.1
+_MAKE_MOTATE_PIN(kSerial_CTSPinNumber, 'A', 28); //
+_MAKE_MOTATE_PIN(kUnassigned19, 'A', 29); // Missing in 100-pin package
+_MAKE_MOTATE_PIN(kServo1_PinNumber, 'A', 30); // PWM2
+_MAKE_MOTATE_PIN(kInput4_PinNumber, 'A', 31); //
+
+_MAKE_MOTATE_PIN(kUnassigned20, 'B', 0); //
+_MAKE_MOTATE_PIN(kUnassigned21, 'B', 1); //
+_MAKE_MOTATE_PIN(kUnassigned22, 'B', 2); //
+_MAKE_MOTATE_PIN(kUnassigned23, 'B', 3); //
+//_MAKE_MOTATE_PIN( , 'B', 4); // TDI
+//_MAKE_MOTATE_PIN( , 'B', 5); // TRACESDO
+//_MAKE_MOTATE_PIN( , 'B', 6); // SWDIO
+//_MAKE_MOTATE_PIN( , 'B', 7); // SWDCLK
+//_MAKE_MOTATE_PIN( , 'B', 8); // XOUT
+//_MAKE_MOTATE_PIN( , 'B', 9); // XIN
+//_MAKE_MOTATE_PIN( , 'B', 10); // USB_D-
+//_MAKE_MOTATE_PIN( , 'B', 11); // USB_D+
+//_MAKE_MOTATE_PIN( , 'B', 12); // ERASE
+_MAKE_MOTATE_PIN(kUnassigned24, 'B', 13); // LED_1 (Heartbeat) - PWM2
+
+//_MAKE_MOTATE_PIN( , 'D', 0); // USB_VBUS
+_MAKE_MOTATE_PIN(kInput1_PinNumber, 'D', 1); //
+_MAKE_MOTATE_PIN(kInput2_PinNumber, 'D', 2); //
+_MAKE_MOTATE_PIN(kUnassigned25, 'D', 3); //
+_MAKE_MOTATE_PIN(kUnassigned26, 'D', 4); //
+_MAKE_MOTATE_PIN(kUnassigned27, 'D', 5); //
+_MAKE_MOTATE_PIN(kInput3_PinNumber, 'D', 6); //
+_MAKE_MOTATE_PIN(kSerial_RTSPinNumber, 'D', 7); //
+_MAKE_MOTATE_PIN(kUnassigned28, 'D', 8); // INTERRUPT_OUT
+_MAKE_MOTATE_PIN(kUnassigned29, 'D', 9); //
+_MAKE_MOTATE_PIN(kUnassigned30, 'D', 10); //
+_MAKE_MOTATE_PIN(kLED_RGBWPixelPinNumber, 'D', 11); // PWM0
+_MAKE_MOTATE_PIN(kSocket2_DirPinNumber, 'D', 12); //
+_MAKE_MOTATE_PIN(kSocket2_EnablePinNumber, 'D', 13); //
+_MAKE_MOTATE_PIN(kSocket2_StepPinNumber, 'D', 14); //
+_MAKE_MOTATE_PIN(kOutputSAFE_PinNumber, 'D', 15); //
+_MAKE_MOTATE_PIN(kSocket2_Microstep_0PinNumber, 'D', 16); //
+_MAKE_MOTATE_PIN(kSocket2_Microstep_1PinNumber, 'D', 17); //
+_MAKE_MOTATE_PIN(kSocket1_DirPinNumber, 'D', 18); //
+_MAKE_MOTATE_PIN(kSocket1_StepPinNumber, 'D', 19); //
+_MAKE_MOTATE_PIN(kSocket1_Microstep_0PinNumber, 'D', 20); //
+_MAKE_MOTATE_PIN(kSocket1_Microstep_2PinNumber, 'D', 21); //
+_MAKE_MOTATE_PIN(kUnassigned31, 'D', 22); //
+_MAKE_MOTATE_PIN(kUnassigned32, 'D', 23); // Missing in 100-pin package
+_MAKE_MOTATE_PIN(kUnassigned33, 'D', 24); //
+_MAKE_MOTATE_PIN(kUnassigned34, 'D', 25); //
+_MAKE_MOTATE_PIN(kUnassigned35, 'D', 26); //
+_MAKE_MOTATE_PIN(kUnassigned36, 'D', 27); //
+_MAKE_MOTATE_PIN(kSocket2_Microstep_2PinNumber, 'D', 28); //
+_MAKE_MOTATE_PIN(kUnassigned37, 'D', 29); // Missing in 100-pin package
+_MAKE_MOTATE_PIN(kLEDPWM_PinNumber, 'D', 30); //
+_MAKE_MOTATE_PIN(kUnassigned38, 'D', 31); //
+
+} // namespace Motate
+
+// We then allow each chip-type to have it's onw function definitions
+// that will refer to these pin assignments.
+#include "motate_chip_pin_functions.h"
+
+#endif
+
+// GQUADRATIC_B_PINOUT_H
diff --git a/g2core/board/gquadratic/hardware.cpp b/g2core/board/gquadratic/hardware.cpp
index e11d595c..f40a7928 100755
--- a/g2core/board/gquadratic/hardware.cpp
+++ b/g2core/board/gquadratic/hardware.cpp
@@ -37,7 +37,37 @@
#include "MotatePower.h"
//Motate::ClockOutputPin external_clk_pin {16000000}; // 16MHz optimally
-Motate::OutputPin external_clk_pin {Motate::kStartLow};
+//Motate::OutputPin external_clk_pin {Motate::kStartLow};
+
+#include "neopixel.h"
+#include "canonical_machine.h"
+
+namespace LEDs {
+ NeoPixel rgbw_leds {NeoPixelOrder::GRB};
+
+ RGB_Color_t display_color[15] {
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ {0, 0, 0, 5},
+ };
+
+ bool alarm_red = false; // if we are in alarm, the tells us if we're going to red (pulsing)
+ bool shutdown_white = false; // if we are in shutdown, the tells us if we're going to red (pulsing)
+ cmMachineState last_see_machine_state;
+ float old_x_pos = 0.0;
+}
/*
* hardware_init() - lowest level hardware init
@@ -46,7 +76,13 @@ Motate::OutputPin external_clk_pin {Motate::k
void hardware_init()
{
board_hardware_init();
- external_clk_pin = 0; // Force external clock to 0 for now.
+// external_clk_pin = 0; // Force external clock to 0 for now.
+
+ for (uint8_t pixel = 0; pixel < LEDs::rgbw_leds.count; pixel++) {
+ LEDs::display_color[pixel].startTransition(100, 0, 0, 0);
+ LEDs::rgbw_leds.setPixel(pixel, LEDs::display_color[pixel]);
+ }
+ LEDs::rgbw_leds.update();
}
/*
@@ -55,6 +91,36 @@ void hardware_init()
stat_t hardware_periodic()
{
+ float x_pos = cm_get_work_position(ACTIVE_MODEL, AXIS_X);
+ if (fabs(LEDs::old_x_pos - x_pos) > 0.01) {
+ LEDs::old_x_pos = x_pos;
+
+ float led_pos = x_pos * ((float)(LEDs::rgbw_leds.count-1) / 40);
+
+ for (uint8_t pixel = 0; pixel < LEDs::rgbw_leds.count; pixel++) {
+ float value = fabs(led_pos - (float)pixel);
+ if (value < 1.001) {
+ value = 1.0 - value;
+ if (LEDs::display_color[pixel].red < value) {
+ LEDs::display_color[pixel].startTransition(10, value, value, value);
+ } else {
+ LEDs::display_color[pixel].startTransition(500, 0, 0, 0);
+ }
+ } else {
+// LEDs::display_color[pixel].startTransition(500, 0, 0, 0);
+ }
+// LEDs::display_color[pixel].startTransition(100, std::max(0.0f, std::min(40.0f, (float)(x_pos/40.0) )), 0, 0);
+ }
+ }
+
+ for (uint8_t pixel = 0; pixel < LEDs::rgbw_leds.count; pixel++) {
+ if (LEDs::display_color[pixel].update()) {
+ LEDs::rgbw_leds.setPixel(pixel, LEDs::display_color[pixel]);
+ }
+ }
+
+ LEDs::rgbw_leds.update();
+
return STAT_OK;
}
diff --git a/g2core/board/gquadratic/hardware.h b/g2core/board/gquadratic/hardware.h
old mode 100755
new mode 100644
index 9784b4d6..a28bf858
--- a/g2core/board/gquadratic/hardware.h
+++ b/g2core/board/gquadratic/hardware.h
@@ -58,7 +58,7 @@ enum hwPlatform {
#define AXES 6 // number of axes supported in this version
#define HOMING_AXES 4 // number of axes that can be homed (assumes Zxyabc sequence)
-#define MOTORS 2 // number of motors on the board
+#define MOTORS 3 // number of motors on the board
#define COORDS 6 // number of supported coordinate systems (1-6)
#define PWMS 2 // number of supported PWM channels
@@ -110,8 +110,8 @@ using Motate::OutputPin;
*
* The following interrupts are defined w/indicated priorities
*
- * 0 DDA_TIMER (3) for step pulse generation
- * 1 DWELL_TIMER (4) for dwell timing
+ * 0 DDA_TIMER (9) for step pulse generation
+ * 1 DWELL_TIMER (10) for dwell timing
* 2 LOADER software generated interrupt (STIR / SGI)
* 3 Serial read character interrupt
* 4 EXEC software generated interrupt (STIR / SGI)
@@ -128,11 +128,10 @@ using Motate::OutputPin;
/**** Motate Definitions ****/
// Timer definitions. See stepper.h and other headers for setup
-typedef TimerChannel<0, 0> dda_timer_type; // stepper pulse generation in stepper.cpp
-typedef TimerChannel<1, 0> dwell_timer_type; // dwell timing in stepper.cpp
-typedef ServiceCall<0> load_timer_type; // request load timer in stepper.cpp
-typedef ServiceCall<1> exec_timer_type; // request exec timer in stepper.cpp
-typedef ServiceCall<2> fwd_plan_timer_type; // request exec timer in stepper.cpp
+typedef TimerChannel<9, 0> dda_timer_type; // stepper pulse generation in stepper.cpp
+typedef TimerChannel<10, 0> load_timer_type; // request load timer in stepper.cpp
+typedef ServiceCall<1> exec_timer_type; // request exec timer in stepper.cpp
+typedef ServiceCall<2> fwd_plan_timer_type; // request exec timer in stepper.cpp
// Pin assignments
diff --git a/g2core/board/gquadratic/motate_pin_assignments.h b/g2core/board/gquadratic/motate_pin_assignments.h
index 77609574..9cc268e7 100755
--- a/g2core/board/gquadratic/motate_pin_assignments.h
+++ b/g2core/board/gquadratic/motate_pin_assignments.h
@@ -30,8 +30,6 @@
#ifndef motate_pin_assignments_h
#define motate_pin_assignments_h
-#include
-
// Board pinout is pulled in after naming, so we can use the naming there.
namespace Motate {
@@ -40,15 +38,15 @@ namespace Motate {
// Undefined pins will be equivalent to Motate::NullPin, and return 1 for Pin<>::isNull();
-pin_number kSerial_RX = 0;
-pin_number kSerial_TX = 1;
-pin_number kSerial_RTS = 2; // added later
-pin_number kSerial_CTS = 3; // added later
+pin_number kSerial_RXPinNumber = 0;
+pin_number kSerial_TXPinNumber = 1;
+pin_number kSerial_RTSPinNumber = 2; // added later
+pin_number kSerial_CTSPinNumber = 3; // added later
-pin_number kSerial0_RX = 0;
-pin_number kSerial0_TX = 1;
-pin_number kSerial0_RTS = 2; // added later
-pin_number kSerial0_CTS = 3; // added later
+pin_number kSerial0_RXPinNumber = 0;
+pin_number kSerial0_TXPinNumber = 1;
+pin_number kSerial0_RTSPinNumber = 2; // added later
+pin_number kSerial0_CTSPinNumber = 3; // added later
pin_number kI2C_SDAPinNumber = 5;
pin_number kI2C_SCLPinNumber = 6;
@@ -159,13 +157,15 @@ pin_number kDebug3_PinNumber = -1; // 116; //e Not the out-of-order numbering &
pin_number kDebug4_PinNumber = -1; // 114;
// END DEBUG PINS
-pin_number kLED_USBRXPinNumber = 117;
-pin_number kLED_USBTXPinNumber = 118;
-pin_number kSD_CardDetectPinNumber = 119;
-pin_number kSD_ChipSelectPinNumber = 120;
-pin_number kInterlock_InPinNumber = 121;
-pin_number kOutputSAFE_PinNumber = 122; // SAFE signal
-pin_number kLEDPWM_PinNumber = 123;
+pin_number kLED_USBRXPinNumber = 117;
+pin_number kLED_USBTXPinNumber = 118;
+pin_number kSD_CardDetectPinNumber = 119;
+pin_number kSD_ChipSelectPinNumber = 120;
+pin_number kInterlock_InPinNumber = 121;
+pin_number kOutputSAFE_PinNumber = 122; // SAFE signal
+pin_number kLEDPWM_PinNumber = 123;
+pin_number kOutputInterrupt_PinNumber = 124; // to-host interrupt signal
+pin_number kLED_RGBWPixelPinNumber = 125; // 117;
// GRBL / gShield compatibility pins -- Due board ONLY
@@ -213,8 +213,12 @@ pin_number kADC14_PinNumber = 164; // Not physically pinned out
pin_number kExternalClock1_PinNumber = 170; // External pins for exporting a clock signal (for Trinamics)
+pin_number kServo1_PinNumber = 171; //
+pin_number kServo2_PinNumber = 172; //
+pin_number kServo3_PinNumber = 173; //
-// start next sequence at 171
+
+// start next sequence at 174
// blank spots for unassigned pins - all unassigned pins need a unique number (do not re-use numbers)
diff --git a/g2core/board/gquintic.gdb b/g2core/board/gquintic.gdb
index 3a78d77d..864a9f89 100644
--- a/g2core/board/gquintic.gdb
+++ b/g2core/board/gquintic.gdb
@@ -4,7 +4,7 @@ target remote | /usr/local/bin/openocd -c "set CHIPNAME ${CHIP}" -f ${MOTATE_PAT
source ./board/g2_default.gdb
define boot_from_flash
- monitor at91sam4 gpnvm set 1
+ monitor atsamv gpnvm set 1
end
source -s arm.gdb
diff --git a/g2core/board/gquintic.mk b/g2core/board/gquintic.mk
index 4f512476..ab60f6cb 100755
--- a/g2core/board/gquintic.mk
+++ b/g2core/board/gquintic.mk
@@ -6,32 +6,33 @@
# make BOARD=gquintic-a
# You can also choose a CONFIG from boards.mk:
-# make CONFIG=PrintrbotPlus BOARD=gquintic-a
+# make CONFIG=PrintrbotPlus BOARD=gquintic-b
##########
# BOARDs for use directly from the make command line (with default settings) or by CONFIGs.
-ifeq ("$(BOARD)","gquintic-a")
+ifeq ("$(BOARD)","gquintic-b")
BASE_BOARD=gquintic
- DEVICE_DEFINES += MOTATE_BOARD="gquintic-a"
+ DEVICE_DEFINES += MOTATE_BOARD="gquintic-b"
DEVICE_DEFINES += SETTINGS_FILE=${SETTINGS_FILE}
endif
##########
-# The general pboard-a BASE_BOARD.
+# The general gquintic BASE_BOARD.
+# NOTE: rev a is no longer supported!
ifeq ("$(BASE_BOARD)","gquintic")
_BOARD_FOUND = 1
DEVICE_DEFINES += MOTATE_CONFIG_HAS_USBSERIAL=1
- FIRST_LINK_SOURCES += $(sort $(wildcard ${MOTATE_PATH}/Atmel_sam_common/*.cpp)) $(sort $(wildcard ${MOTATE_PATH}/Atmel_sam4e/*.cpp))
+ FIRST_LINK_SOURCES += $(sort $(wildcard ${MOTATE_PATH}/Atmel_sam_common/*.cpp)) $(sort $(wildcard ${MOTATE_PATH}/Atmel_sams70/*.cpp))
- CHIP = SAM4E8C
+ CHIP = SAMS70N19
export CHIP
- CHIP_LOWERCASE = sam4e8c
+ CHIP_LOWERCASE = sams70n19
BOARD_PATH = ./board/gquintic
SOURCE_DIRS += ${BOARD_PATH} device/trinamic
diff --git a/g2core/board/gquintic/board_stepper.h b/g2core/board/gquintic/board_stepper.h
index f1b90dd3..23b8c4db 100755
--- a/g2core/board/gquintic/board_stepper.h
+++ b/g2core/board/gquintic/board_stepper.h
@@ -31,7 +31,7 @@
#include "hardware.h" // for MOTORS
#include "tmc2130.h"
-typedef Motate::SPIBus SPIBus_used_t;
+typedef Motate::SPIBus SPIBus_used_t;
// These are identical to board_stepper.h, except for the word "extern" and the initialization
extern Trinamic2130 usb;
-Motate::USBDevice< Motate::USBCDC, Motate::USBCDC > usb;
+XIOUSBDevice_t usb;
+
+extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
+extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
+
decltype(usb.mixin<0>::Serial) &SerialUSB = usb.mixin<0>::Serial;
-decltype(usb.mixin<1>::Serial) &SerialUSB1 = usb.mixin<1>::Serial;
+//decltype(usb.mixin<1>::Serial) &SerialUSB1 = usb.mixin<1>::Serial;
-// 115200 is the default, as well.
-//UART Serial {115200, UARTMode::RTSCTSFlowControl};
-
-MOTATE_SET_USB_VENDOR_STRING( {'S' ,'y', 'n', 't', 'h', 'e', 't', 'o', 's'} )
-MOTATE_SET_USB_PRODUCT_STRING( {'T', 'i', 'n', 'y', 'G', ' ', 'v', '2'} )
+MOTATE_SET_USB_VENDOR_STRING( u"Synthetos" )
+MOTATE_SET_USB_PRODUCT_STRING( u"TinyG v2" )
MOTATE_SET_USB_SERIAL_NUMBER_STRING_FROM_CHIPID()
#endif // XIO_HAS_USB
+
//******** SPI ********
#if XIO_HAS_SPI
Motate::SPI spi;
@@ -64,14 +67,14 @@ Motate::SPI spi;
//******** UART ********
#if XIO_HAS_UART
-Motate::UART Serial {115200, Motate::UARTMode::RTSCTSFlowControl};
+Motate::UART Serial {115200, Motate::UARTMode::RTSCTSFlowControl};
#endif
void board_hardware_init(void) // called 1st
{
#if XIO_HAS_USB
// Init USB
- usb.attach(); // USB setup. Runs in "background" as the rest of this executes
+ usb.attach();
#endif // XIO_HAS_USB
}
diff --git a/g2core/board/gquintic/board_xio.h b/g2core/board/gquintic/board_xio.h
index dc57cd3c..09f20fef 100755
--- a/g2core/board/gquintic/board_xio.h
+++ b/g2core/board/gquintic/board_xio.h
@@ -29,15 +29,25 @@
#ifndef board_xio_h
#define board_xio_h
+#include "settings.h"
+
//******** USB ********
#if XIO_HAS_USB
#include "MotateUSB.h"
#include "MotateUSBCDC.h"
-// extern Motate::USBDevice< Motate::USBCDC > usb;
-extern Motate::USBDevice usb;
+#if USB_SERIAL_PORTS_EXPOSED == 1
+typedef Motate::USBDevice< Motate::USBCDC > XIOUSBDevice_t;
+#endif
+#if USB_SERIAL_PORTS_EXPOSED == 2
+typedef Motate::USBDevice XIOUSBDevice_t;
+#endif
+
+extern XIOUSBDevice_t usb;
extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
#endif // XIO_HAS_USB
@@ -50,7 +60,7 @@ extern Motate::SPI spi;
//******** UART ********
#if XIO_HAS_UART
#include "MotateUART.h"
-extern Motate::UART Serial;
+extern Motate::UART Serial;
#endif
//******* Generic Functions *******
diff --git a/g2core/board/gquintic/gquintic-a-pinout.h b/g2core/board/gquintic/gquintic-b-pinout.h
similarity index 62%
rename from g2core/board/gquintic/gquintic-a-pinout.h
rename to g2core/board/gquintic/gquintic-b-pinout.h
index 67e9ecd3..75d7c52e 100755
--- a/g2core/board/gquintic/gquintic-a-pinout.h
+++ b/g2core/board/gquintic/gquintic-b-pinout.h
@@ -89,7 +89,7 @@
#define ADC2_AVAILABLE 0
#define ADC3_AVAILABLE 0
-#define XIO_HAS_USB 0
+#define XIO_HAS_USB 1
#define XIO_HAS_UART 1
#define XIO_HAS_SPI 0
#define XIO_HAS_I2C 0
@@ -100,60 +100,66 @@
// So we have to explicitly enable them as PWM pins.
// Generated with:
// perl -e 'for($i=1;$i<14;$i++) { print "#define OUTPUT${i}_PWM 0\n";}'
-#define OUTPUT1_PWM 1 // PWM1 - Fet 1
-#define OUTPUT2_PWM 1 // PWM2 - Fet 2
-#define OUTPUT3_PWM 1 // PWM3 - Fan 1
-#define OUTPUT4_PWM 0 // PWM2 - Fan 2
-#define OUTPUT5_PWM 1 // Timer2ch0 - Fan 3
-#define OUTPUT6_PWM 1 // Timer2ch1
-#define OUTPUT7_PWM 0 // PWM2
-#define OUTPUT8_PWM 1 // PWM0
-#define OUTPUT9_PWM 0 // PWM3 or Timer1ch0
-#define OUTPUT10_PWM 0 // PWM1
-#define OUTPUT11_PWM 0 // Can't PWM - Fet 3
+#define OUTPUT1_PWM 1 // TC0,1 - Fet 1
+#define OUTPUT2_PWM 1 // PWM1 - Fet 2
+#define OUTPUT3_PWM 1 // TC1,0 - Fan 1
+#define OUTPUT4_PWM 1 // TC1,1 - Fan 2
+#define OUTPUT5_PWM 1 // TC2,0 - Fan 3
+#define OUTPUT6_PWM 1 // PWM8+0
+#define OUTPUT7_PWM 1 // PWM3
+#define OUTPUT8_PWM 1 // PWM2
+#define OUTPUT9_PWM 0 // PWM2
+#define OUTPUT10_PWM 1 // PWM8+2
+#define OUTPUT11_PWM 1 // PWM8+3 - Fet 3
#define OUTPUT12_PWM 0 // Unused
#define OUTPUT13_PWM 0 // Unused
namespace Motate {
-// Arduino pin name & function
-_MAKE_MOTATE_PIN(kOutput2_PinNumber, 'A', 0); //
+// Unused:
+//
+//
+//
+//
+//
+
+_MAKE_MOTATE_PIN(kLED_RGBWPixelPinNumber, 'A', 0); //
_MAKE_MOTATE_PIN(kOutput1_PinNumber, 'A', 1); //
-_MAKE_MOTATE_PIN(kSocket5_EnablePinNumber, 'A', 2); //
+_MAKE_MOTATE_PIN(kOutput2_PinNumber, 'A', 2); //
_MAKE_MOTATE_PIN(kI2C1_SDAPinNumber, 'A', 3); //
_MAKE_MOTATE_PIN(kI2C1_SCLPinNumber, 'A', 4); //
-_MAKE_MOTATE_PIN(kUnassigned1, 'A', 5); //
+_MAKE_MOTATE_PIN(kOutput11_PinNumber, 'A', 5); //
_MAKE_MOTATE_PIN(kExternalClock1_PinNumber, 'A', 6); // CPU_CLK
-_MAKE_MOTATE_PIN(kSerial_CTS, 'A', 7); //
-_MAKE_MOTATE_PIN(kUnassigned2, 'A', 8); //
-_MAKE_MOTATE_PIN(kSerial_RX, 'A', 9); //
-_MAKE_MOTATE_PIN(kSerial_TX, 'A', 10); //
-_MAKE_MOTATE_PIN(kSocket1_SPISlaveSelectPinNumber, 'A', 11); //
-_MAKE_MOTATE_PIN(kSPI0_MISOPinNumber, 'A', 12); //
-_MAKE_MOTATE_PIN(kSPI0_MOSIPinNumber, 'A', 13); //
-_MAKE_MOTATE_PIN(kSPI0_SCKPinNumber, 'A', 14); //
-_MAKE_MOTATE_PIN(kOutput9_PinNumber, 'A', 15); //
-_MAKE_MOTATE_PIN(kOutput7_PinNumber, 'A', 16); //
+_MAKE_MOTATE_PIN(kOutput7_PinNumber, 'A', 7); //
+_MAKE_MOTATE_PIN(kSerial_RTSPinNumber, 'A', 8); //
+_MAKE_MOTATE_PIN(kSerial_RXPinNumber, 'A', 9); //
+_MAKE_MOTATE_PIN(kSerial_TXPinNumber, 'A', 10); //
+_MAKE_MOTATE_PIN(kSocket2_EnablePinNumber, 'A', 11); //
+_MAKE_MOTATE_PIN(kOutput6_PinNumber, 'A', 12); //
+_MAKE_MOTATE_PIN(kOutput8_PinNumber, 'A', 13); //
+_MAKE_MOTATE_PIN(kSocket1_StepPinNumber, 'A', 14); //
+_MAKE_MOTATE_PIN(kOutput3_PinNumber, 'A', 15); //
+_MAKE_MOTATE_PIN(kOutput4_PinNumber, 'A', 16); //
_MAKE_MOTATE_PIN(kADC3_PinNumber, 'A', 17); //
_MAKE_MOTATE_PIN(kADC2_PinNumber, 'A', 18); //
_MAKE_MOTATE_PIN(kADC1_PinNumber, 'A', 19); //
_MAKE_MOTATE_PIN(kADC0_PinNumber, 'A', 20); //
-_MAKE_MOTATE_PIN(kUnassigned3, 'A', 21); //
-_MAKE_MOTATE_PIN(kSocket2_SPISlaveSelectPinNumber, 'A', 22); //
-_MAKE_MOTATE_PIN(kOutput8_PinNumber, 'A', 23); //
-_MAKE_MOTATE_PIN(kOutput10_PinNumber, 'A', 24); //
-_MAKE_MOTATE_PIN(kOutput4_PinNumber, 'A', 25); //
+_MAKE_MOTATE_PIN(kSerial_CTSPinNumber, 'A', 21); //
+_MAKE_MOTATE_PIN(kSocket1_EnablePinNumber, 'A', 22); //
+_MAKE_MOTATE_PIN(kOutput10_PinNumber, 'A', 23); //
+_MAKE_MOTATE_PIN(kSocket3_EnablePinNumber, 'A', 24); //
+_MAKE_MOTATE_PIN(kSocket2_DirPinNumber, 'A', 25); //
_MAKE_MOTATE_PIN(kOutput5_PinNumber, 'A', 26); // On Timer 2!
-_MAKE_MOTATE_PIN(kOutput6_PinNumber, 'A', 27); // On Timer 2!
-_MAKE_MOTATE_PIN(kUnassigned13, 'A', 28); // DIAG0
-_MAKE_MOTATE_PIN(kInput8_PinNumber, 'A', 29); //
+_MAKE_MOTATE_PIN(kSocket3_StepPinNumber, 'A', 27); // On Timer 2!
+_MAKE_MOTATE_PIN(kUnassigned1, 'A', 28); // DIAG1
+_MAKE_MOTATE_PIN(kUnassigned2, 'A', 29); //
_MAKE_MOTATE_PIN(kInput1_PinNumber, 'A', 30); //
-_MAKE_MOTATE_PIN(kInput2_PinNumber, 'A', 31); //
+_MAKE_MOTATE_PIN(kInput4_PinNumber, 'A', 31); //
_MAKE_MOTATE_PIN(kInput12_PinNumber, 'B', 0); //
_MAKE_MOTATE_PIN(kInput11_PinNumber, 'B', 1); //
-_MAKE_MOTATE_PIN(kSocket3_SPISlaveSelectPinNumber, 'B', 2); //
-_MAKE_MOTATE_PIN(kSerial_RTS, 'B', 3); //
+_MAKE_MOTATE_PIN(kSocket1_SPISlaveSelectPinNumber, 'B', 2); //
+_MAKE_MOTATE_PIN(kOutputSAFE_PinNumber, 'B', 3); //
//_MAKE_MOTATE_PIN( , 'B', 4); // TDI
//_MAKE_MOTATE_PIN( , 'B', 5); // TRACESDO
//_MAKE_MOTATE_PIN( , 'B', 6); // SWDIO
@@ -168,35 +174,35 @@ _MAKE_MOTATE_PIN(kSocket4_SPISlaveSelectPinNumber, 'B', 14); // NOT CONNECTED
//_MAKE_MOTATE_PIN( , 'D', 0); // USB_VBUS
-_MAKE_MOTATE_PIN(kInput10_PinNumber, 'D', 1); //
-_MAKE_MOTATE_PIN(kInput9_PinNumber, 'D', 2); //
-_MAKE_MOTATE_PIN(kInput7_PinNumber, 'D', 3); //
-_MAKE_MOTATE_PIN(kInput6_PinNumber, 'D', 4); //
-_MAKE_MOTATE_PIN(kInput5_PinNumber, 'D', 5); //
-_MAKE_MOTATE_PIN(kInput4_PinNumber, 'D', 6); //
+_MAKE_MOTATE_PIN(kInput9_PinNumber, 'D', 1); //
+_MAKE_MOTATE_PIN(kInput10_PinNumber, 'D', 2); //
+_MAKE_MOTATE_PIN(kInput8_PinNumber, 'D', 3); //
+_MAKE_MOTATE_PIN(kInput7_PinNumber, 'D', 4); //
+_MAKE_MOTATE_PIN(kInput6_PinNumber, 'D', 5); //
+_MAKE_MOTATE_PIN(kInput5_PinNumber, 'D', 6); //
_MAKE_MOTATE_PIN(kInput3_PinNumber, 'D', 7); //
-_MAKE_MOTATE_PIN(kUnassigned11, 'D', 8); // DIAG1
-_MAKE_MOTATE_PIN(kOutputSAFE_PinNumber, 'D', 9); //
-_MAKE_MOTATE_PIN(kSocket5_StepPinNumber, 'D', 10); //
-_MAKE_MOTATE_PIN(kSocket5_DirPinNumber, 'D', 11); //
-_MAKE_MOTATE_PIN(kSocket4_StepPinNumber, 'D', 12); //
-_MAKE_MOTATE_PIN(kUnassigned5, 'D', 13); //
-_MAKE_MOTATE_PIN(kSocket4_DirPinNumber, 'D', 14); //
-_MAKE_MOTATE_PIN(kUnassigned6, 'D', 15); //
-_MAKE_MOTATE_PIN(kSocket4_EnablePinNumber, 'D', 16); //
-_MAKE_MOTATE_PIN(kSocket3_DirPinNumber, 'D', 17); //
-_MAKE_MOTATE_PIN(kUnassigned7, 'D', 18); //
-_MAKE_MOTATE_PIN(kSocket3_EnablePinNumber, 'D', 19); //
-_MAKE_MOTATE_PIN(kSocket2_StepPinNumber, 'D', 20); //
-_MAKE_MOTATE_PIN(kSocket2_DirPinNumber, 'D', 21); //
-_MAKE_MOTATE_PIN(kUnassigned8, 'D', 22); //
-_MAKE_MOTATE_PIN(kOutput3_PinNumber, 'D', 23); //
-_MAKE_MOTATE_PIN(kSocket2_EnablePinNumber, 'D', 24); //
-_MAKE_MOTATE_PIN(kSocket1_DirPinNumber, 'D', 25); //
-_MAKE_MOTATE_PIN(kSocket1_StepPinNumber, 'D', 26); //
-_MAKE_MOTATE_PIN(kSocket1_EnablePinNumber, 'D', 27); //
-_MAKE_MOTATE_PIN(kSocket3_StepPinNumber, 'D', 28); //
-_MAKE_MOTATE_PIN(kOutput11_PinNumber, 'D', 29); //
+_MAKE_MOTATE_PIN(kInput2_PinNumber, 'D', 8); // ]
+_MAKE_MOTATE_PIN(kUnassigned3, 'D', 9); // DIAG0
+_MAKE_MOTATE_PIN(kUnassigned4, 'D', 10); //
+_MAKE_MOTATE_PIN(kSocket5_StepPinNumber, 'D', 11); //
+_MAKE_MOTATE_PIN(kSocket3_SPISlaveSelectPinNumber, 'D', 12); //
+_MAKE_MOTATE_PIN(kSocket5_DirPinNumber, 'D', 13); //
+_MAKE_MOTATE_PIN(kSocket5_EnablePinNumber, 'D', 14); //
+_MAKE_MOTATE_PIN(kUnassigned5, 'D', 15); //
+_MAKE_MOTATE_PIN(kSocket4_StepPinNumber, 'D', 16); //
+_MAKE_MOTATE_PIN(kSocket4_DirPinNumber, 'D', 17); //
+_MAKE_MOTATE_PIN(kSocket3_DirPinNumber, 'D', 18); //
+_MAKE_MOTATE_PIN(kUnassigned6, 'D', 19); //
+_MAKE_MOTATE_PIN(kSPI0_MISOPinNumber, 'D', 20); //
+_MAKE_MOTATE_PIN(kSPI0_MOSIPinNumber, 'D', 21); //
+_MAKE_MOTATE_PIN(kSPI0_SCKPinNumber, 'D', 22); //
+_MAKE_MOTATE_PIN(kUnassigned7, 'D', 23); //
+_MAKE_MOTATE_PIN(kSocket2_StepPinNumber, 'D', 24); //
+_MAKE_MOTATE_PIN(kSocket2_SPISlaveSelectPinNumber, 'D', 25); //
+_MAKE_MOTATE_PIN(kOutput9_PinNumber, 'D', 26); //
+_MAKE_MOTATE_PIN(kSocket1_DirPinNumber, 'D', 27); //
+_MAKE_MOTATE_PIN(kSocket4_EnablePinNumber, 'D', 28); //
+_MAKE_MOTATE_PIN(kUnassigned8, 'D', 29); //
_MAKE_MOTATE_PIN(kUnassigned9, 'D', 30); // INTERRUPT_OUT
_MAKE_MOTATE_PIN(kUnassigned10, 'D', 31); //
diff --git a/g2core/board/gquintic/hardware.h b/g2core/board/gquintic/hardware.h
index 2f3fc969..e7fb56ba 100755
--- a/g2core/board/gquintic/hardware.h
+++ b/g2core/board/gquintic/hardware.h
@@ -119,18 +119,19 @@ using Motate::OutputPin;
/**** Stepper DDA and dwell timer settings ****/
//#define FREQUENCY_DDA 200000UL // Hz step frequency. Interrupts actually fire at 2x (400 KHz)
-#define FREQUENCY_DDA 150000UL // Hz step frequency. Interrupts actually fire at 2x (300 KHz)
+#define FREQUENCY_DDA 300000UL // Hz step frequency. Interrupts actually fire at 2x (300 KHz)
#define FREQUENCY_DWELL 1000UL
#define FREQUENCY_SGI 200000UL // 200,000 Hz means software interrupts will fire 5 uSec after being called
/**** Motate Definitions ****/
// Timer definitions. See stepper.h and other headers for setup
-typedef TimerChannel<0,0> dda_timer_type; // stepper pulse generation in stepper.cpp
-typedef TimerChannel<1,0> dwell_timer_type; // dwell timing in stepper.cpp
-typedef ServiceCall<0> load_timer_type; // request load timer in stepper.cpp
-typedef ServiceCall<1> exec_timer_type; // request exec timer in stepper.cpp
-typedef ServiceCall<2> fwd_plan_timer_type; // request exec timer in stepper.cpp
+typedef TimerChannel<9, 0> dda_timer_type; // stepper pulse generation in stepper.cpp
+typedef TimerChannel<10, 0> load_timer_type; // request load timer in stepper.cpp
+typedef ServiceCall<1> exec_timer_type; // request exec timer in stepper.cpp
+typedef ServiceCall<2> fwd_plan_timer_type; // request exec timer in stepper.cpp
+
+Motate::service_call_number kSPI_ServiceCallNumber = 3;
// Pin assignments
diff --git a/g2core/board/gquintic/motate_pin_assignments.h b/g2core/board/gquintic/motate_pin_assignments.h
index 12ec29bf..af029d31 100755
--- a/g2core/board/gquintic/motate_pin_assignments.h
+++ b/g2core/board/gquintic/motate_pin_assignments.h
@@ -40,15 +40,15 @@ namespace Motate {
// Undefined pins will be equivalent to Motate::NullPin, and return 1 for Pin<>::isNull();
-pin_number kSerial_RX = 0;
-pin_number kSerial_TX = 1;
-pin_number kSerial_RTS = 2; // added later
-pin_number kSerial_CTS = 3; // added later
+pin_number kSerial_RXPinNumber = 0;
+pin_number kSerial_TXPinNumber = 1;
+pin_number kSerial_RTSPinNumber = 2; // added later
+pin_number kSerial_CTSPinNumber = 3; // added later
-pin_number kSerial0_RX = 0;
-pin_number kSerial0_TX = 1;
-pin_number kSerial0_RTS = 2; // added later
-pin_number kSerial0_CTS = 3; // added later
+pin_number kSerial0_RXPinNumber = 0;
+pin_number kSerial0_TXPinNumber = 1;
+pin_number kSerial0_RTSPinNumber = 2; // added later
+pin_number kSerial0_CTSPinNumber = 3; // added later
pin_number kI2C_SDAPinNumber = 5;
pin_number kI2C_SCLPinNumber = 6;
@@ -166,6 +166,8 @@ pin_number kSD_ChipSelectPinNumber = 120;
pin_number kInterlock_InPinNumber = 121;
pin_number kOutputSAFE_PinNumber = 122; // SAFE signal
pin_number kLEDPWM_PinNumber = 123;
+pin_number kOutputInterrupt_PinNumber = 124; // to-host interrupt signal
+pin_number kLED_RGBWPixelPinNumber = 125; // 117;
// GRBL / gShield compatibility pins -- Due board ONLY
@@ -238,12 +240,6 @@ pin_number kUnassigned4 = 251;
pin_number kUnassigned3 = 252;
pin_number kUnassigned2 = 253;
pin_number kUnassigned1 = 254; // 254 is the max.. Do not exceed this number
-
-/** NOTE: When adding pin definitions here, they must be
- * added to ALL board pin assignment files, even if
- * they are defined as -1.
- **/
-
} // namespace Motate
// For the SAM3X8C boards, we actually use the same NUMBERING, but have different number to pin linkages
diff --git a/g2core/board/printrboardg2/board_xio.cpp b/g2core/board/printrboardg2/board_xio.cpp
index 709b0e57..8b4835b2 100755
--- a/g2core/board/printrboardg2/board_xio.cpp
+++ b/g2core/board/printrboardg2/board_xio.cpp
@@ -32,6 +32,7 @@
#include "board_xio.h"
//******** USB ********
+#if XIO_HAS_USB
const Motate::USBSettings_t Motate::USBSettings = {
/*gVendorID = */ 0x1d50,
/*gProductID = */ 0x606d,
@@ -41,18 +42,21 @@ const Motate::USBSettings_t Motate::USBSettings = {
};
/*gProductVersion = */ //0.1,
-//Motate::USBDevice< Motate::USBCDC > usb;
-Motate::USBDevice< Motate::USBCDC, Motate::USBCDC > usb;
+XIOUSBDevice_t usb;
+
+extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
+extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
+
decltype(usb.mixin<0>::Serial) &SerialUSB = usb.mixin<0>::Serial;
-decltype(usb.mixin<1>::Serial) &SerialUSB1 = usb.mixin<1>::Serial;
+//decltype(usb.mixin<1>::Serial) &SerialUSB1 = usb.mixin<1>::Serial;
-// 115200 is the default, as well.
-//UART Serial {115200, UARTMode::RTSCTSFlowControl};
-
-MOTATE_SET_USB_VENDOR_STRING( {'S' ,'y', 'n', 't', 'h', 'e', 't', 'o', 's'} )
-MOTATE_SET_USB_PRODUCT_STRING( {'T', 'i', 'n', 'y', 'G', ' ', 'v', '2'} )
+MOTATE_SET_USB_VENDOR_STRING( u"Synthetos" )
+MOTATE_SET_USB_PRODUCT_STRING( u"TinyG v2" )
MOTATE_SET_USB_SERIAL_NUMBER_STRING_FROM_CHIPID()
+#endif // XIO_HAS_USB
//******** SPI ********
@@ -68,8 +72,10 @@ Motate::UART usb;
-extern Motate::USBDevice usb;
-extern decltype(usb.mixin<0>::Serial)& SerialUSB;
-extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#if USB_SERIAL_PORTS_EXPOSED == 1
+typedef Motate::USBDevice< Motate::USBCDC > XIOUSBDevice_t;
+#endif
+#if USB_SERIAL_PORTS_EXPOSED == 2
+typedef Motate::USBDevice XIOUSBDevice_t;
+#endif
+extern XIOUSBDevice_t usb;
+extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
+extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
+#endif // XIO_HAS_USB
//******** SPI ********
#if XIO_HAS_SPI
diff --git a/g2core/board/printrboardg2/hardware.h b/g2core/board/printrboardg2/hardware.h
index 6046c789..244a2681 100755
--- a/g2core/board/printrboardg2/hardware.h
+++ b/g2core/board/printrboardg2/hardware.h
@@ -126,8 +126,7 @@ using Motate::OutputPin;
// Timer definitions. See stepper.h and other headers for setup
typedef TimerChannel<3,0> dda_timer_type; // stepper pulse generation in stepper.cpp
-typedef TimerChannel<4,0> dwell_timer_type; // dwell timing in stepper.cpp
-typedef ServiceCall<0> load_timer_type; // request load timer in stepper.cpp
+typedef TimerChannel<4,0> load_timer_type; // request load timer in stepper.cpp
typedef ServiceCall<1> exec_timer_type; // request exec timer in stepper.cpp
typedef ServiceCall<2> fwd_plan_timer_type; // request exec timer in stepper.cpp
diff --git a/g2core/board/sbv300/board_xio.cpp b/g2core/board/sbv300/board_xio.cpp
index 712a89e9..8b4835b2 100755
--- a/g2core/board/sbv300/board_xio.cpp
+++ b/g2core/board/sbv300/board_xio.cpp
@@ -32,6 +32,7 @@
#include "board_xio.h"
//******** USB ********
+#if XIO_HAS_USB
const Motate::USBSettings_t Motate::USBSettings = {
/*gVendorID = */ 0x1d50,
/*gProductID = */ 0x606d,
@@ -41,20 +42,21 @@ const Motate::USBSettings_t Motate::USBSettings = {
};
/*gProductVersion = */ //0.1,
-//Motate::USBDevice< Motate::USBCDC > usb;
-Motate::USBDevice< Motate::USBCDC, Motate::USBCDC > usb;
+XIOUSBDevice_t usb;
+
+extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
+extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
+#endif
+
decltype(usb.mixin<0>::Serial) &SerialUSB = usb.mixin<0>::Serial;
-decltype(usb.mixin<1>::Serial) &SerialUSB1 = usb.mixin<1>::Serial;
+//decltype(usb.mixin<1>::Serial) &SerialUSB1 = usb.mixin<1>::Serial;
-// 115200 is the default, as well.
-//UART Serial {115200, UARTMode::RTSCTSFlowControl};
-
-
-MOTATE_SET_USB_VENDOR_STRING( {'S' ,'y', 'n', 't', 'h', 'e', 't', 'o', 's'} )
-MOTATE_SET_USB_PRODUCT_STRING( {'T', 'i', 'n', 'y', 'G', ' ', 'v', '2'} )
+MOTATE_SET_USB_VENDOR_STRING( u"Synthetos" )
+MOTATE_SET_USB_PRODUCT_STRING( u"TinyG v2" )
MOTATE_SET_USB_SERIAL_NUMBER_STRING_FROM_CHIPID()
-
+#endif // XIO_HAS_USB
//******** SPI ********
@@ -65,25 +67,26 @@ Motate::SPI spi;
//******** UART ********
#if XIO_HAS_UART
-Motate::UART Serial{
- 115200, Motate::UARTMode::RTSCTSFlowControl};
+Motate::UART Serial {115200, Motate::UARTMode::RTSCTSFlowControl};
#endif
-void board_hardware_init(void) // called 1st
+void board_hardware_init(void) // called 1st
{
+#if XIO_HAS_USB
// Init USB
- usb.attach(); // USB setup. Runs in "background" as the rest of this executes
+ usb.attach();
+#endif // XIO_HAS_USB
}
-void board_xio_init(void) // called later than board_hardware_init (there are thing in between)
+void board_xio_init(void) // called later than board_hardware_init (there are thing in between)
{
-// Init SPI
+ // Init SPI
#if XIO_HAS_SPI
-// handled internally for now
+ // handled internally for now
#endif
-// Init UART
+ // Init UART
#if XIO_HAS_UART
Serial.init();
#endif
diff --git a/g2core/board/sbv300/board_xio.h b/g2core/board/sbv300/board_xio.h
index 40b42a09..6bc4421f 100755
--- a/g2core/board/sbv300/board_xio.h
+++ b/g2core/board/sbv300/board_xio.h
@@ -29,28 +29,38 @@
#ifndef board_xio_h
#define board_xio_h
+#include "settings.h"
+
//******** USB ********
+#if XIO_HAS_USB
#include "MotateUSB.h"
#include "MotateUSBCDC.h"
-// extern Motate::USBDevice< Motate::USBCDC > usb;
-extern Motate::USBDevice usb;
+#if USB_SERIAL_PORTS_EXPOSED == 1
+typedef Motate::USBDevice< Motate::USBCDC > XIOUSBDevice_t;
+#endif
+#if USB_SERIAL_PORTS_EXPOSED == 2
+typedef Motate::USBDevice XIOUSBDevice_t;
+#endif
+
+extern XIOUSBDevice_t usb;
extern decltype(usb.mixin<0>::Serial)& SerialUSB;
+#if USB_SERIAL_PORTS_EXPOSED == 2
extern decltype(usb.mixin<1>::Serial)& SerialUSB1;
-
-
+#endif
+#endif // XIO_HAS_USB
//******** SPI ********
-//#include "MotateSPI.h"
-// extern Motate::SPI spi;
-
-
+#if XIO_HAS_SPI
+#include "MotateSPI.h"
+extern Motate::SPI spi;
+#endif
//******** UART ********
-//#include "MotateUART.h"
-// extern Motate::UART Serial;
-
-
+#if XIO_HAS_UART
+#include "MotateUART.h"
+extern Motate::UART Serial;
+#endif
//******* Generic Functions *******
void board_hardware_init(void); // called 1st
diff --git a/g2core/board/sbv300/hardware.h b/g2core/board/sbv300/hardware.h
index 0e576ab4..5ded07eb 100755
--- a/g2core/board/sbv300/hardware.h
+++ b/g2core/board/sbv300/hardware.h
@@ -126,8 +126,7 @@ using Motate::OutputPin;
// Timer definitions. See stepper.h and other headers for setup
typedef TimerChannel<3,0> dda_timer_type; // stepper pulse generation in stepper.cpp
-typedef TimerChannel<4,0> dwell_timer_type; // dwell timing in stepper.cpp
-typedef ServiceCall<0> load_timer_type; // request load timer in stepper.cpp
+typedef TimerChannel<4,0> load_timer_type; // request load timer in stepper.cpp
typedef ServiceCall<1> exec_timer_type; // request exec timer in stepper.cpp
typedef ServiceCall<2> fwd_plan_timer_type; // request exec timer in stepper.cpp
diff --git a/g2core/boards.mk b/g2core/boards.mk
index 81443f81..2643578d 100644
--- a/g2core/boards.mk
+++ b/g2core/boards.mk
@@ -72,7 +72,7 @@ endif
ifeq ("$(CONFIG)","TestQuintic")
ifeq ("$(BOARD)","NONE")
- BOARD=gquintic-a
+ BOARD=gquintic-b
endif
SETTINGS_FILE="settings_test.h"
endif
@@ -80,12 +80,11 @@ endif
ifeq ("$(CONFIG)","TestQuadratic")
ifeq ("$(BOARD)","NONE")
- BOARD=gquadratic-a
+ BOARD=gquadratic-b
endif
SETTINGS_FILE="settings_test.h"
endif
-
##########
# Shopbot configs:
@@ -138,5 +137,23 @@ ifeq ("$(CONFIG)","Ultimakerv9k")
SETTINGS_FILE="settings_ultimaker.h"
endif
+##########
+# EMSL configs:
+
+ifeq ("$(CONFIG)","WaterColorBotv2")
+ ifeq ("$(BOARD)","NONE")
+ BOARD=gquadratic-b
+ endif
+ SETTINGS_FILE="settings_watercolorbot_v2.h"
+endif
+
+ifeq ("$(CONFIG)","EggBot")
+ ifeq ("$(BOARD)","NONE")
+ BOARD=gquadratic-b
+ endif
+ SETTINGS_FILE="settings_eggbot.h"
+endif
+
+
include $(wildcard ./board/$(STAR).mk)
diff --git a/g2core/canonical_machine.cpp b/g2core/canonical_machine.cpp
index 01152b35..3899ed89 100644
--- a/g2core/canonical_machine.cpp
+++ b/g2core/canonical_machine.cpp
@@ -225,7 +225,7 @@ cmCombinedState cm_get_combined_state()
* ACTIVE_MODEL cm.am // active model pointer is maintained by state management
*/
uint32_t cm_get_linenum(const GCodeState_t *gcode_state) { return gcode_state->linenum;}
-uint8_t cm_get_motion_mode(const GCodeState_t *gcode_state) { return gcode_state->motion_mode;}
+cmMotionMode cm_get_motion_mode(const GCodeState_t *gcode_state) { return gcode_state->motion_mode;}
uint8_t cm_get_coord_system(const GCodeState_t *gcode_state) { return gcode_state->coord_system;}
uint8_t cm_get_units_mode(const GCodeState_t *gcode_state) { return gcode_state->units_mode;}
uint8_t cm_get_select_plane(const GCodeState_t *gcode_state) { return gcode_state->select_plane;}
@@ -1683,8 +1683,9 @@ void cm_request_queue_flush()
{
if ((cm.hold_state != FEEDHOLD_OFF) && // don't honor request unless you are in a feedhold
(cm.queue_flush_state == FLUSH_OFF)) { // ...and only once
- xio_flush_read(); // flush the input buffers - you can do that now
cm.queue_flush_state = FLUSH_REQUESTED; // request planner flush once motion has stopped
+
+ // NOTE: we used to flush the input buffers, but this is handled in xio *prior* to queue flush now
}
}
@@ -2311,7 +2312,7 @@ void _cm_recalc_max_junction_accel(const uint8_t axis) {
void cm_set_axis_jerk(const uint8_t axis, const float jerk)
{
cm.a[axis].jerk_max = jerk;
- cm.a[axis].recip_jerk = 1/(jerk * JERK_MULTIPLIER);
+ //cm.a[axis].recip_jerk = 1/(jerk * JERK_MULTIPLIER);
// Must recalculate the max_junction_accel now that the jerk has changed.
_cm_recalc_max_junction_accel(axis);
diff --git a/g2core/canonical_machine.h b/g2core/canonical_machine.h
index 85d3db45..509df9d4 100755
--- a/g2core/canonical_machine.h
+++ b/g2core/canonical_machine.h
@@ -320,7 +320,7 @@ typedef enum { // axis modes (ordered: see _cm_get_feed_time())
*/
typedef struct GCodeState { // Gcode model state - used by model, planning and runtime
uint32_t linenum; // Gcode block line number
- uint8_t motion_mode; // Group1: G0, G1, G2, G3, G38.2, G80, G81,
+ cmMotionMode motion_mode; // Group1: G0, G1, G2, G3, G38.2, G80, G81,
// G82, G83 G84, G85, G86, G87, G88, G89
float target[AXES]; // XYZABC where the move should go
@@ -340,6 +340,31 @@ typedef struct GCodeState { // Gcode model state - used by model, pl
cmCoordSystem coord_system; // G54-G59 - select coordinate system 1-9
uint8_t tool; // G // M6 tool change - moves "tool_select" to "tool"
uint8_t tool_select; // G // T value - T sets this value
+
+ void reset() {
+ linenum = 0;
+ motion_mode = MOTION_MODE_STRAIGHT_TRAVERSE;
+
+ for (uint8_t i = 0; i< AXES; i++) {
+ target[i] = 0.0;
+ work_offset[i] = 0.0;
+ }
+
+ feed_rate = 0.0;
+ parameter = 0.0;
+
+ feed_rate_mode = INVERSE_TIME_MODE;
+ select_plane = CANON_PLANE_XY;
+ units_mode = INCHES;
+ path_control = PATH_EXACT_PATH;
+ distance_mode = ABSOLUTE_MODE;
+ arc_distance_mode = ABSOLUTE_MODE;
+ absolute_override = ABSOLUTE_OVERRIDE_OFF;
+ coord_system = ABSOLUTE_COORDS;
+ tool = 0;
+ tool_select = 0;
+
+ };
} GCodeState_t;
typedef struct GCodeStateExtended { // Gcode dynamic state extensions - used by model and arcs
@@ -372,7 +397,7 @@ typedef struct GCodeStateExtended { // Gcode dynamic state extensions - used
typedef struct GCodeInput { // Gcode model inputs - meaning depends on context
uint8_t next_action; // handles G modal group 1 moves & non-modals
- uint8_t motion_mode; // Group1: G0, G1, G2, G3, G38.2, G80, G81, G82
+ cmMotionMode motion_mode; // Group1: G0, G1, G2, G3, G38.2, G80, G81, G82
// G83, G84, G85, G86, G87, G88, G89
uint8_t program_flow; // used only by the gcode_parser
@@ -472,7 +497,7 @@ typedef struct cmAxis {
float travel_min; // min work envelope for soft limits
float jerk_max; // max jerk (Jm) in mm/min^3 divided by 1 million
float jerk_high; // high speed deceleration jerk (Jh) in mm/min^3 divided by 1 million
- float recip_jerk; // stored reciprocal of current jerk value - has the million in it
+ //float recip_jerk; // stored reciprocal of current jerk value - has the million in it
float max_junction_accel; // high speed deceleration jerk (Jh) in mm/min^3 divided by 1 million
float junction_dev; // aka cornering delta -- DEPRICATED!
float radius; // radius in mm for rotary axis modes
@@ -581,7 +606,7 @@ float cm_get_axis_jerk(const uint8_t axis);
void cm_set_axis_jerk(const uint8_t axis, const float jerk);
uint32_t cm_get_linenum(const GCodeState_t *gcode_state);
-uint8_t cm_get_motion_mode(const GCodeState_t *gcode_state);
+cmMotionMode cm_get_motion_mode(const GCodeState_t *gcode_state);
uint8_t cm_get_coord_system(const GCodeState_t *gcode_state);
uint8_t cm_get_units_mode(const GCodeState_t *gcode_state);
uint8_t cm_get_select_plane(const GCodeState_t *gcode_state);
@@ -676,7 +701,7 @@ stat_t cm_arc_feed(const float target[], const bool target_f[], // G
const float radius, const bool radius_f, // radius if radius mode
const float P_word, const bool P_word_f, // parameter
const bool modal_g1_f, // modal group flag for motion group
- const uint8_t motion_mode); // defined motion mode
+ const cmMotionMode motion_mode); // defined motion mode
// Spindle Functions (4.3.7)
// see spindle.h for spindle functions - which would go right here
diff --git a/g2core/config.cpp b/g2core/config.cpp
old mode 100755
new mode 100644
index 279eff86..5b0b048c
--- a/g2core/config.cpp
+++ b/g2core/config.cpp
@@ -584,7 +584,9 @@ nvObj_t *nv_add_object(const char *token) // add an object to the body usi
nvObj_t *nv = nv_body;
for (uint8_t i=0; ivaluetype != TYPE_EMPTY) {
- if ((nv = nv->nx) == NULL) return(NULL); // not supposed to find a NULL; here for safety
+ if ((nv = nv->nx) == NULL) { // not supposed to find a NULL; here for safety
+ return(NULL);
+ }
continue;
}
// load the index from the token or die trying
@@ -600,7 +602,9 @@ nvObj_t *nv_add_integer(const char *token, const uint32_t value)// add an intege
nvObj_t *nv = nv_body;
for (uint8_t i=0; ivaluetype != TYPE_EMPTY) {
- if ((nv = nv->nx) == NULL) return(NULL); // not supposed to find a NULL; here for safety
+ if ((nv = nv->nx) == NULL) { // not supposed to find a NULL; here for safety
+ return(NULL);
+ }
continue;
}
strncpy(nv->token, token, TOKEN_LEN);
@@ -616,7 +620,9 @@ nvObj_t *nv_add_data(const char *token, const uint32_t value)// add an integer o
nvObj_t *nv = nv_body;
for (uint8_t i=0; ivaluetype != TYPE_EMPTY) {
- if ((nv = nv->nx) == NULL) return(NULL); // not supposed to find a NULL; here for safety
+ if ((nv = nv->nx) == NULL) { // not supposed to find a NULL; here for safety
+ return(NULL);
+ }
continue;
}
strcpy(nv->token, token);
@@ -633,7 +639,9 @@ nvObj_t *nv_add_float(const char *token, const float value) // add a float ob
nvObj_t *nv = nv_body;
for (uint8_t i=0; ivaluetype != TYPE_EMPTY) {
- if ((nv = nv->nx) == NULL) return(NULL); // not supposed to find a NULL; here for safety
+ if ((nv = nv->nx) == NULL) { // not supposed to find a NULL; here for safety
+ return(NULL);
+ }
continue;
}
strncpy(nv->token, token, TOKEN_LEN);
@@ -649,7 +657,9 @@ nvObj_t *nv_add_string(const char *token, const char *string) // add a string ob
nvObj_t *nv = nv_body;
for (uint8_t i=0; ivaluetype != TYPE_EMPTY) {
- if ((nv = nv->nx) == NULL) return(NULL); // not supposed to find a NULL; here for safety
+ if ((nv = nv->nx) == NULL) { // not supposed to find a NULL; here for safety
+ return(NULL);
+ }
continue;
}
strncpy(nv->token, token, TOKEN_LEN);
diff --git a/g2core/device/neopixel/neopixel.h b/g2core/device/neopixel/neopixel.h
index b57f5135..4c69f8dc 100755
--- a/g2core/device/neopixel/neopixel.h
+++ b/g2core/device/neopixel/neopixel.h
@@ -215,6 +215,12 @@ struct RGB_Color_t : NeopixelColorTag {
float green;
float blue;
+ enum ColorFilter {
+ Set = 0,
+ Lighten,
+ Darken
+ };
+
const uint32_t _update_timeout_ms;
Motate::Timeout _update_timeout;
@@ -233,7 +239,18 @@ struct RGB_Color_t : NeopixelColorTag {
_update_timeout.set(0);
};
- void startTransition(uint32_t milliseconds, float to_red, float to_green, float to_blue) {
+ void startTransition(uint32_t milliseconds, float to_red, float to_green, float to_blue, ColorFilter cf = Set) {
+ if (cf == Lighten) {
+ to_red = std::max(to_red, red);
+ to_green = std::max(to_green, green);
+ to_blue = std::max(to_blue, blue);
+ }
+ else if (cf == Darken) {
+ to_red = std::min(to_red, red);
+ to_green = std::min(to_green, green);
+ to_blue = std::min(to_blue, blue);
+ }
+
_transition_steps_left = 0.5 + (milliseconds / _update_timeout_ms);
float h = 1.0 / _transition_steps_left;
float h_2 = h * h;
@@ -418,6 +435,10 @@ struct NeoPixel {
};
void setPixel(uint8_t pixel, uint8_t red, uint8_t green, uint8_t blue, int16_t white = -1) {
+ uint8_t data_width = 32;
+ if (!_has_white) {
+ data_width = 24;
+ }
if (_has_white && (white == -1)) {
// Adjust all of the RGB to accomodate white
white = std::min(red, std::min(green, blue));
@@ -425,32 +446,32 @@ struct NeoPixel {
// green -= white;
// blue -= white;
}
- _period_buffer[0 + 1 + _red_offset + (pixel * 32)] = (red & 0b10000000) ? led_ON : led_OFF;
- _period_buffer[1 + 1 + _red_offset + (pixel * 32)] = (red & 0b01000000) ? led_ON : led_OFF;
- _period_buffer[2 + 1 + _red_offset + (pixel * 32)] = (red & 0b00100000) ? led_ON : led_OFF;
- _period_buffer[3 + 1 + _red_offset + (pixel * 32)] = (red & 0b00010000) ? led_ON : led_OFF;
- _period_buffer[4 + 1 + _red_offset + (pixel * 32)] = (red & 0b00001000) ? led_ON : led_OFF;
- _period_buffer[5 + 1 + _red_offset + (pixel * 32)] = (red & 0b00000100) ? led_ON : led_OFF;
- _period_buffer[6 + 1 + _red_offset + (pixel * 32)] = (red & 0b00000010) ? led_ON : led_OFF;
- _period_buffer[7 + 1 + _red_offset + (pixel * 32)] = (red & 0b00000001) ? led_ON : led_OFF;
+ _period_buffer[0 + 1 + _red_offset + (pixel * data_width)] = (red & 0b10000000) ? led_ON : led_OFF;
+ _period_buffer[1 + 1 + _red_offset + (pixel * data_width)] = (red & 0b01000000) ? led_ON : led_OFF;
+ _period_buffer[2 + 1 + _red_offset + (pixel * data_width)] = (red & 0b00100000) ? led_ON : led_OFF;
+ _period_buffer[3 + 1 + _red_offset + (pixel * data_width)] = (red & 0b00010000) ? led_ON : led_OFF;
+ _period_buffer[4 + 1 + _red_offset + (pixel * data_width)] = (red & 0b00001000) ? led_ON : led_OFF;
+ _period_buffer[5 + 1 + _red_offset + (pixel * data_width)] = (red & 0b00000100) ? led_ON : led_OFF;
+ _period_buffer[6 + 1 + _red_offset + (pixel * data_width)] = (red & 0b00000010) ? led_ON : led_OFF;
+ _period_buffer[7 + 1 + _red_offset + (pixel * data_width)] = (red & 0b00000001) ? led_ON : led_OFF;
- _period_buffer[0 + 1 + _green_offset + (pixel * 32)] = (green & 0b10000000) ? led_ON : led_OFF;
- _period_buffer[1 + 1 + _green_offset + (pixel * 32)] = (green & 0b01000000) ? led_ON : led_OFF;
- _period_buffer[2 + 1 + _green_offset + (pixel * 32)] = (green & 0b00100000) ? led_ON : led_OFF;
- _period_buffer[3 + 1 + _green_offset + (pixel * 32)] = (green & 0b00010000) ? led_ON : led_OFF;
- _period_buffer[4 + 1 + _green_offset + (pixel * 32)] = (green & 0b00001000) ? led_ON : led_OFF;
- _period_buffer[5 + 1 + _green_offset + (pixel * 32)] = (green & 0b00000100) ? led_ON : led_OFF;
- _period_buffer[6 + 1 + _green_offset + (pixel * 32)] = (green & 0b00000010) ? led_ON : led_OFF;
- _period_buffer[7 + 1 + _green_offset + (pixel * 32)] = (green & 0b00000001) ? led_ON : led_OFF;
+ _period_buffer[0 + 1 + _green_offset + (pixel * data_width)] = (green & 0b10000000) ? led_ON : led_OFF;
+ _period_buffer[1 + 1 + _green_offset + (pixel * data_width)] = (green & 0b01000000) ? led_ON : led_OFF;
+ _period_buffer[2 + 1 + _green_offset + (pixel * data_width)] = (green & 0b00100000) ? led_ON : led_OFF;
+ _period_buffer[3 + 1 + _green_offset + (pixel * data_width)] = (green & 0b00010000) ? led_ON : led_OFF;
+ _period_buffer[4 + 1 + _green_offset + (pixel * data_width)] = (green & 0b00001000) ? led_ON : led_OFF;
+ _period_buffer[5 + 1 + _green_offset + (pixel * data_width)] = (green & 0b00000100) ? led_ON : led_OFF;
+ _period_buffer[6 + 1 + _green_offset + (pixel * data_width)] = (green & 0b00000010) ? led_ON : led_OFF;
+ _period_buffer[7 + 1 + _green_offset + (pixel * data_width)] = (green & 0b00000001) ? led_ON : led_OFF;
- _period_buffer[0 + 1 + _blue_offset + (pixel * 32)] = (blue & 0b10000000) ? led_ON : led_OFF;
- _period_buffer[1 + 1 + _blue_offset + (pixel * 32)] = (blue & 0b01000000) ? led_ON : led_OFF;
- _period_buffer[2 + 1 + _blue_offset + (pixel * 32)] = (blue & 0b00100000) ? led_ON : led_OFF;
- _period_buffer[3 + 1 + _blue_offset + (pixel * 32)] = (blue & 0b00010000) ? led_ON : led_OFF;
- _period_buffer[4 + 1 + _blue_offset + (pixel * 32)] = (blue & 0b00001000) ? led_ON : led_OFF;
- _period_buffer[5 + 1 + _blue_offset + (pixel * 32)] = (blue & 0b00000100) ? led_ON : led_OFF;
- _period_buffer[6 + 1 + _blue_offset + (pixel * 32)] = (blue & 0b00000010) ? led_ON : led_OFF;
- _period_buffer[7 + 1 + _blue_offset + (pixel * 32)] = (blue & 0b00000001) ? led_ON : led_OFF;
+ _period_buffer[0 + 1 + _blue_offset + (pixel * data_width)] = (blue & 0b10000000) ? led_ON : led_OFF;
+ _period_buffer[1 + 1 + _blue_offset + (pixel * data_width)] = (blue & 0b01000000) ? led_ON : led_OFF;
+ _period_buffer[2 + 1 + _blue_offset + (pixel * data_width)] = (blue & 0b00100000) ? led_ON : led_OFF;
+ _period_buffer[3 + 1 + _blue_offset + (pixel * data_width)] = (blue & 0b00010000) ? led_ON : led_OFF;
+ _period_buffer[4 + 1 + _blue_offset + (pixel * data_width)] = (blue & 0b00001000) ? led_ON : led_OFF;
+ _period_buffer[5 + 1 + _blue_offset + (pixel * data_width)] = (blue & 0b00000100) ? led_ON : led_OFF;
+ _period_buffer[6 + 1 + _blue_offset + (pixel * data_width)] = (blue & 0b00000010) ? led_ON : led_OFF;
+ _period_buffer[7 + 1 + _blue_offset + (pixel * data_width)] = (blue & 0b00000001) ? led_ON : led_OFF;
if (_has_white) {
_period_buffer[0 + 1 + _white_offset + (pixel * 32)] = (white & 0b10000000) ? led_ON : led_OFF;
diff --git a/g2core/device/step_dir_driver/step_dir_driver.h b/g2core/device/step_dir_driver/step_dir_driver.h
index 390cdb65..da3cfc1e 100644
--- a/g2core/device/step_dir_driver/step_dir_driver.h
+++ b/g2core/device/step_dir_driver/step_dir_driver.h
@@ -49,7 +49,7 @@ template
-struct StepDirStepper : Stepper {
+struct StepDirStepper final : Stepper {
/* stepper pin assignments */
OutputPin _step;
diff --git a/g2core/device/step_dir_hobbyservo/step_dir_hobbyservo.h b/g2core/device/step_dir_hobbyservo/step_dir_hobbyservo.h
new file mode 100644
index 00000000..d631b558
--- /dev/null
+++ b/g2core/device/step_dir_hobbyservo/step_dir_hobbyservo.h
@@ -0,0 +1,153 @@
+/*
+ * step_dir_hobbyservo.cpp - control over a hobby servo (PWM-driven) using steper Step/Direction/Enable from software
+ * This file is part of G2 project
+ *
+ * Copyright (c) 2016 Alden S. Hart, Jr.
+ * Copyright (c) 2016 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 .
+ *
+ * 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.
+ */
+#ifndef STEPP_DIR_HOBBYSERVO_H_ONCE
+#define STEPP_DIR_HOBBYSERVO_H_ONCE
+
+#include "MotatePins.h"
+#include "MotateTimers.h"
+
+#include "stepper.h"
+
+using Motate::pin_number;
+using Motate::OutputPin;
+using Motate::PWMOutputPin;
+using Motate::kStartHigh;
+using Motate::kNormal;
+using Motate::Timeout;
+
+
+// Motor structures
+template // Setup a stepper template to hold our pins
+struct StepDirHobbyServo final : Stepper {
+ /* stepper pin assignments */
+
+ int16_t _microsteps_per_step = 1;
+ bool _step_is_forward = false;
+ int32_t _position = 0; // in steps from 0 - 6400 for a full "rotation"
+ int32_t _position_computed = 0; // in steps from 0 - 6400 for a full "rotation"
+ float _min_value;
+ float _max_value;
+ float _value_range;
+ bool _enabled = false;
+ PWMOutputPin _pwm_pin;
+ Motate::Timeout check_timer;
+
+ // sets default pwm freq for all motor vrefs (commented line below also sets HiZ)
+ StepDirHobbyServo(const uint32_t frequency = 50) : Stepper{}, _pwm_pin{kNormal, frequency} {
+ _pwm_pin.setFrequency(frequency); // redundant due to a bug
+ uint16_t _top_value = _pwm_pin.getTopValue();
+ float frequency_inv = 1.0/(float)frequency;
+ _min_value = (float)_top_value / ((frequency_inv)/(750.0/1000000.0));
+ _max_value = (float)_top_value / ((frequency_inv)/(2000.0/1000000.0));
+ _value_range = _max_value - _min_value;
+ _position_computed = _min_value;
+ check_timer.set(1);
+ };
+
+ /* Optional override of init */
+
+ /* Functions that must be implemented in subclasses */
+
+ bool canStep() override { return true; };
+
+ void setMicrosteps(const uint8_t microsteps) override {
+ switch (microsteps) {
+ case (1): {
+ _microsteps_per_step = 32;
+ break;
+ }
+ case (2): {
+ _microsteps_per_step = 16;
+ break;
+ }
+ case (4): {
+ _microsteps_per_step = 8;
+ break;
+ }
+ case (8): {
+ _microsteps_per_step = 4;
+ break;
+ }
+ case (16): {
+ _microsteps_per_step = 2;
+ break;
+ }
+ case (32): {
+ _microsteps_per_step = 1;
+ break;
+ }
+ }
+ };
+
+ void _enableImpl() override {
+ _enabled = true;
+ _pwm_pin.setExactDutyCycle(_position_computed, true);
+ };
+
+ void _disableImpl() override {
+ _enabled = false;
+ _pwm_pin.setExactDutyCycle(0, true);
+ };
+
+ void stepStart() override {
+ if (!_enabled) return;
+
+ if (_step_is_forward) {
+ _position += _microsteps_per_step;
+ } else {
+ _position -= _microsteps_per_step;
+ }
+
+ if (!check_timer.isPast()) { return; }
+ check_timer.set(10);
+
+ float used_position = _position;
+ if (used_position > 6400.0) {
+ used_position = 6400.0;
+ }
+ if (used_position < 0.0) {
+ used_position = 0.0;
+ }
+
+ _position_computed = _min_value + ((used_position/6400.0) * _value_range);
+ _pwm_pin.setExactDutyCycle(_position_computed, true); // apply the change
+ };
+
+ void stepEnd() override {
+ };
+
+ void setDirection(uint8_t new_direction) override {
+ _step_is_forward = new_direction;
+ };
+
+ void setPowerLevel(float new_pl) override {
+ ; // ignore this
+ };
+};
+
+#endif // STEPP_DIR_HOBBYSERVO_H_ONCE
diff --git a/g2core/device/trinamic/tmc2130.h b/g2core/device/trinamic/tmc2130.h
index aa6580bd..9c541851 100644
--- a/g2core/device/trinamic/tmc2130.h
+++ b/g2core/device/trinamic/tmc2130.h
@@ -46,7 +46,7 @@ template
-struct Trinamic2130 : Stepper {
+struct Trinamic2130 final : Stepper {
// Pins that are directly managed
OutputPin _step;
OutputPin _dir;
@@ -501,7 +501,8 @@ struct Trinamic2130 : Stepper {
}
out_buffer.addr = (uint8_t) next_reg;
- _device.queueMessage(_message.setup((uint8_t *)&out_buffer, (uint8_t *)&in_buffer, 5, SPIMessage::DeassertAfter, SPIMessage::KeepTransaction));
+ _message.setup((uint8_t *)&out_buffer, (uint8_t *)&in_buffer, 5, SPIMessage::DeassertAfter, SPIMessage::KeepTransaction);
+ _device.queueMessage(&_message);
};
void _doneReadingCallback()
@@ -538,7 +539,7 @@ struct Trinamic2130 : Stepper {
_reading_only = false;
_transmitting = false;
- _startNextReadWrite();
+ //_startNextReadWrite();
};
void init() override
diff --git a/g2core/error.h b/g2core/error.h
old mode 100755
new mode 100644
diff --git a/g2core/g2core.cppproj b/g2core/g2core.cppproj
index 2b1f1c3d..4669f074 100644
--- a/g2core/g2core.cppproj
+++ b/g2core/g2core.cppproj
@@ -1454,6 +1454,72 @@
Makefile
bin\PrintrbotSimple1608-printrboardG2v3\
+
+
+
+ True
+ True
+ True
+ True
+ True
+
+
+ C:\Program Files\Atmel\Atmel Studio 6.0\extensions\Atmel\ARMGCC\3.3.1.128\ARMSupportFiles
+ C:\Program Files\Atmel\Atmel Studio 6.0\extensions\Atmel\ARMGCC\3.3.1.128\ARMSupportFiles\CMSIS\Include
+ C:\Program Files\Atmel\Atmel Studio 6.0\extensions\Atmel\ARMGCC\3.3.1.128\ARMSupportFiles\Device\ATMEL
+ C:\Program Files\Atmel\Atmel Studio 6.0\extensions\Atmel\ARMGCC\3.3.1.128\ARMSupportFiles\Device\ATMEL\sam3xa\include
+ C:\Program Files\Atmel\Atmel Toolchain\ARM GCC\Native\4.7.2.87\arm-gnu-toolchain\bin\..\..\CMSIS_Atmel
+ C:\Program Files\Atmel\Atmel Toolchain\ARM GCC\Native\4.7.2.87\arm-gnu-toolchain\bin\..\..\CMSIS_Atmel\CMSIS\Include
+ C:\Program Files\Atmel\Atmel Toolchain\ARM GCC\Native\4.7.2.87\arm-gnu-toolchain\bin\..\..\CMSIS_Atmel\Device\ATMEL
+ C:\Program Files\Atmel\Atmel Toolchain\ARM GCC\Native\4.7.2.87\arm-gnu-toolchain\bin\..\..\CMSIS_Atmel\Device\ATMEL\sam3xa\include
+
+
+ Optimize (-O1)
+ True
+ Maximum (-g3)
+ True
+
+
+ C:\Program Files\Atmel\Atmel Studio 6.0\extensions\Atmel\ARMGCC\3.3.1.128\ARMSupportFiles
+ C:\Program Files\Atmel\Atmel Studio 6.0\extensions\Atmel\ARMGCC\3.3.1.128\ARMSupportFiles\CMSIS\Include
+ C:\Program Files\Atmel\Atmel Studio 6.0\extensions\Atmel\ARMGCC\3.3.1.128\ARMSupportFiles\Device\ATMEL
+ C:\Program Files\Atmel\Atmel Studio 6.0\extensions\Atmel\ARMGCC\3.3.1.128\ARMSupportFiles\Device\ATMEL\sam3xa\include
+ C:\Program Files\Atmel\Atmel Toolchain\ARM GCC\Native\4.7.2.87\arm-gnu-toolchain\bin\..\..\CMSIS_Atmel
+ C:\Program Files\Atmel\Atmel Toolchain\ARM GCC\Native\4.7.2.87\arm-gnu-toolchain\bin\..\..\CMSIS_Atmel\CMSIS\Include
+ C:\Program Files\Atmel\Atmel Toolchain\ARM GCC\Native\4.7.2.87\arm-gnu-toolchain\bin\..\..\CMSIS_Atmel\Device\ATMEL
+ C:\Program Files\Atmel\Atmel Toolchain\ARM GCC\Native\4.7.2.87\arm-gnu-toolchain\bin\..\..\CMSIS_Atmel\Device\ATMEL\sam3xa\include
+
+
+ Optimize (-O1)
+ True
+ Maximum (-g3)
+ True
+
+
+ libm
+
+
+ True
+ -Tsam3x8c_flash.ld
+ Default (-g)
+
+
+ C:\Program Files\Atmel\Atmel Toolchain\ARM GCC\Native\4.7.2.87\arm-gnu-toolchain\bin\..\..\CMSIS_Atmel
+ C:\Program Files\Atmel\Atmel Toolchain\ARM GCC\Native\4.7.2.87\arm-gnu-toolchain\bin\..\..\CMSIS_Atmel\CMSIS\Include
+ C:\Program Files\Atmel\Atmel Toolchain\ARM GCC\Native\4.7.2.87\arm-gnu-toolchain\bin\..\..\CMSIS_Atmel\Device\ATMEL
+ C:\Program Files\Atmel\Atmel Toolchain\ARM GCC\Native\4.7.2.87\arm-gnu-toolchain\bin\..\..\CMSIS_Atmel\Device\ATMEL\sam3xa\include
+
+
+ Default (-Wa,-g)
+
+
+ True
+
+ CONFIG=TestQuadratic COLOR=0 VERBOSE=1 DEBUG=1
+ clean CONFIG=TestQuintic
+ Makefile
+ bin\TestQuadratic\
+
compile
@@ -1542,7 +1608,7 @@
compile
-
+
compile
@@ -1566,7 +1632,7 @@
compile
-
+
compile
diff --git a/g2core/g2core.xcodeproj/project.pbxproj b/g2core/g2core.xcodeproj/project.pbxproj
index d6ac4fe3..e4d1864c 100644
--- a/g2core/g2core.xcodeproj/project.pbxproj
+++ b/g2core/g2core.xcodeproj/project.pbxproj
@@ -58,6 +58,8 @@
D44E72C21D663B0F00ECD5DD /* coolant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coolant.h; sourceTree = ""; };
D4522DC91C45F41D0086AAE6 /* g2core_info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = g2core_info.h; sourceTree = ""; };
D457117B17053EFA00EA19A8 /* Makefile */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; path = Makefile; sourceTree = ""; usesTabs = 1; xcLanguageSpecificationIdentifier = xcode.lang.sh; };
+ D4782AB21D9B5F9400B32136 /* settings_watercolorbot_v2.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = settings_watercolorbot_v2.h; sourceTree = ""; };
+ D4782AB41D9E2FF300B32136 /* settings_eggbot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = settings_eggbot.h; sourceTree = ""; };
D47B744C1D4925B4004FAB53 /* device */ = {isa = PBXFileReference; lastKnownFileType = folder; path = device; sourceTree = ""; };
D48F5A40172CB1F900D0E055 /* canonical_machine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = canonical_machine.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
D48F5A41172CB1F900D0E055 /* config_app.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = config_app.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
@@ -146,6 +148,8 @@
isa = PBXGroup;
children = (
D4AC39081C77C1F20098D685 /* settings_default.h */,
+ D4782AB41D9E2FF300B32136 /* settings_eggbot.h */,
+ D4782AB21D9B5F9400B32136 /* settings_watercolorbot_v2.h */,
D4AC39091C77C1F20098D685 /* settings_makeblock.h */,
D4AC390A1C77C1F20098D685 /* settings_othermill_test.h */,
D4AC390B1C77C1F20098D685 /* settings_othermill.h */,
@@ -291,6 +295,34 @@
passBuildSettingsInEnvironment = 1;
productName = g3_due;
};
+ D4782AAE1D9B16BE00B32136 /* G2 Probotix */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "$(ACTION) VERBOSE=1 COLOR=0 CONFIG=ProbotixV90 DEBUG=1";
+ buildConfigurationList = D4782AAF1D9B16BE00B32136 /* Build configuration list for PBXLegacyTarget "G2 Probotix" */;
+ buildPhases = (
+ );
+ buildToolPath = /usr/bin/make;
+ buildWorkingDirectory = .;
+ dependencies = (
+ );
+ name = "G2 Probotix";
+ passBuildSettingsInEnvironment = 1;
+ productName = g3_due;
+ };
+ D4782AB61D9E39EC00B32136 /* G2 EggBot */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "$(ACTION) VERBOSE=1 COLOR=0 CONFIG=EggBot DEBUG=1";
+ buildConfigurationList = D4782AB71D9E39EC00B32136 /* Build configuration list for PBXLegacyTarget "G2 EggBot" */;
+ buildPhases = (
+ );
+ buildToolPath = /usr/bin/make;
+ buildWorkingDirectory = .;
+ dependencies = (
+ );
+ name = "G2 EggBot";
+ passBuildSettingsInEnvironment = 1;
+ productName = g3_due;
+ };
D4AC391A1C7BAB8E0098D685 /* G2 sbv300 */ = {
isa = PBXLegacyTarget;
buildArgumentsString = "$(ACTION) VERBOSE=1 COLOR=0 CONFIG=sbv300 DEBUG=2";
@@ -349,7 +381,7 @@
};
D4F226F31D088A48008ED57C /* G2 gQuintic PrintrbotPlus */ = {
isa = PBXLegacyTarget;
- buildArgumentsString = "$(ACTION) VERBOSE=1 COLOR=0 CONFIG=PrintrbotPlus BOARD=gquintic-a DEBUG=2";
+ buildArgumentsString = "$(ACTION) VERBOSE=1 COLOR=0 CONFIG=PrintrbotPlus BOARD=gquintic-b DEBUG=2";
buildConfigurationList = D4F226F41D088A48008ED57C /* Build configuration list for PBXLegacyTarget "G2 gQuintic PrintrbotPlus" */;
buildPhases = (
);
@@ -411,7 +443,9 @@
D4AC391A1C7BAB8E0098D685 /* G2 sbv300 */,
D4F226F31D088A48008ED57C /* G2 gQuintic PrintrbotPlus */,
D43FF7141D537A5C00FB5B53 /* G2 gQuadratic Test */,
+ D4782AAE1D9B16BE00B32136 /* G2 Probotix */,
D457112317053BFB00EA19A8 /* index */,
+ D4782AB61D9E39EC00B32136 /* G2 EggBot */,
);
};
/* End PBXProject section */
@@ -653,6 +687,38 @@
};
name = Release;
};
+ D4782AB01D9B16BE00B32136 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PATH = "$PATH:/usr/local/gcc-arm-none-eabi/bin";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ D4782AB11D9B16BE00B32136 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PATH = "$PATH:/usr/local/gcc-arm-none-eabi/bin";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ D4782AB81D9E39EC00B32136 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PATH = "$PATH:/usr/local/gcc-arm-none-eabi/bin";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ D4782AB91D9E39EC00B32136 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PATH = "$PATH:/usr/local/gcc-arm-none-eabi/bin";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
D4AC391C1C7BAB8E0098D685 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -790,6 +856,24 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ D4782AAF1D9B16BE00B32136 /* Build configuration list for PBXLegacyTarget "G2 Probotix" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D4782AB01D9B16BE00B32136 /* Debug */,
+ D4782AB11D9B16BE00B32136 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ D4782AB71D9E39EC00B32136 /* Build configuration list for PBXLegacyTarget "G2 EggBot" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D4782AB81D9E39EC00B32136 /* Debug */,
+ D4782AB91D9E39EC00B32136 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
D4AC391B1C7BAB8E0098D685 /* Build configuration list for PBXLegacyTarget "G2 sbv300" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/g2core/g2core_info.h b/g2core/g2core_info.h
index d81a3420..f9a5344a 100644
--- a/g2core/g2core_info.h
+++ b/g2core/g2core_info.h
@@ -21,7 +21,7 @@
#ifndef G2CORE_INFO_H_ONCE
#define G2CORE_INFO_H_ONCE
-#define G2CORE_FIRMWARE_BUILD 100.12 // Fix UART flow control
+#define G2CORE_FIRMWARE_BUILD 100.12 // Changes to USB serial for SAMS70 and SAM8X targets
#ifdef GIT_VERSION
#define G2CORE_FIRMWARE_BUILD_STRING GIT_VERSION
#else
diff --git a/g2core/gcode_parser.cpp b/g2core/gcode_parser.cpp
old mode 100755
new mode 100644
index c8095d18..cd591cda
--- a/g2core/gcode_parser.cpp
+++ b/g2core/gcode_parser.cpp
@@ -697,6 +697,7 @@ static stat_t _execute_gcode_block(char *active_comment)
cm.gn.motion_mode);
break;
}
+ default: break;
}
cm_set_absolute_override(MODEL, ABSOLUTE_OVERRIDE_OFF); // un-set absolute override once the move is planned
}
diff --git a/g2core/plan_arc.cpp b/g2core/plan_arc.cpp
index 8698c231..d9dc6bc8 100644
--- a/g2core/plan_arc.cpp
+++ b/g2core/plan_arc.cpp
@@ -108,7 +108,7 @@ stat_t cm_arc_feed(const float target[], const bool target_f[], // target en
const float radius, const bool radius_f, // radius if radius mode
const float P_word, const bool P_word_f, // parameter
const bool modal_g1_f, // modal group flag for motion group
- const uint8_t motion_mode) // defined motion mode
+ const cmMotionMode motion_mode) // defined motion mode
{
// Start setting up the arc and trapping arc specification errors
diff --git a/g2core/plan_exec.cpp b/g2core/plan_exec.cpp
index 8ea3fef7..8727e4e4 100644
--- a/g2core/plan_exec.cpp
+++ b/g2core/plan_exec.cpp
@@ -46,96 +46,197 @@ static stat_t _exec_aline_segment(void);
static void _init_forward_diffs(float v_0, float v_1);
-/*************************************************************************
- * mp_plan_move() - call ramping function to plan moves ahead of the exec
+/*******************************************************************************
+ * mp_forward_plan() - plan commands and moves ahead of exec; call ramping for moves
*
- * This should NOT normally be called directly! Instead call st_request_plan_move().
+ **** WARNING ****
+ * This function should NOT be called directly!
+ * Instead call st_request_forward_plan(), which mediates access.
*
+ * mp_forward_plan() performs just-in-time forward planning immediately before
+ * lines and commands are queued to the move execution runtime (exec).
+ * Unlike backward planning, buffers are only forward planned once.
+ *
+ * mp_forward_plan() is called aggressively via st_request_forward_plan().
+ * It has a relatively low interrupt level to call its own.
+ * See also: Planner Overview notes in planner.h
+ *
+ * It examines the currently running buffer and its adjacent buffers to:
+ *
+ * - Stop the system from re-planning or planning something that's not prepped
+ * - Plan the next available ALINE (movement) block past the COMMAND blocks
+ * - Skip past/ or pre-plan COMMAND blocks while labeling them as PLANNED
+ *
+ * Returns:
+ * - STAT_OK if exec should be called to kickstart (or continue) movement
+ * - STAT_NOOP to exit with no action taken (do not call exec)
+ */
+/*
+ * --- Forward Planning Processing and Cases ---
+ *
+ * These cases describe all possible sequences of buffers in the planner queue starting
+ * with the currently executing (or about to execute) Run buffer, looking forward
+ * to more recently arrived buffers. In most cases only one or two buffers need to
+ * be examined, but contiguous groups of commands may need to be processed.
+ *
+ * 'Running' cases are where the run buffer state is RUNNING. Bootstrap handles all other cases.
+ * 'Bootstrap' occurs during the startup phase where moves are collected before starting movement.
+ * Conditions that are impossible based on this definition are not listed in the tables below.
+ *
+ * See planner.h / bufferState enum for shorthand used in the descriptions.
+ * All cases assume a mix of moves and commands, as noted in the shorthand.
+ * All cases assume 2 'blocks' - Run block & Plan block. Cases will need to be
+ * revisited and generalized if more blocks are used in the future (deeper
+ * forward planning).
+ *
+ * 'NOT_PREPPED' refers to any preliminary state below PREPPED, i.e. < PREPPED.
+ * ' NOT_PREPPED' can be either a move or command, we don't care so it's not specified.
+ *
+ * 'COMMAND' or 'COMMAND(s)' refers to one command or a contiguous group of command buffers
+ * that may be in PREPPED or PLANNED states. Processing is always the same. Plan all
+ * PREPPED commands and skip past all PLANNED commands.
+ *
+ * Note 1: For MOVEs use the exit velocity of the Run block (mr.r->exit_velocity) as the
+ * entry velocity of the next adjacent move.
+ *
+ * Note 1a: In this special COMMAND case we trust mr.r->exit_velocity because the
+ * backplanner has already handled this case for us.
+ *
+ * Note 2: For COMMANDs use the entry velocity of the current runtime (mr.entry_velocity)
+ * as the entry velocity for the next adjacent move. mr.entry_velocity is almost always 0,
+ * but could be non-0 in a race condition.
+ * FYI: mr.entry_velocity is set at the end of the last running block in mp_exec_aline().
+ *
+ *
+ * CASE:
+ * 0. Nothing to do
+ *
+ * run_buffer
+ * ----------
+ * a. Run buffer has not yet been initialized (prep null buffer and return NOOP)
+ * b. NOT_PREPPED No moves or commands in run buffer. Exit with no action
+ *
+ * 1. Bootstrap cases (buffer state < RUNNING)
+ *
+ * run_buffer next N bufs terminal buf Actions
+ * ---------- ----------- ------------ ----------------------------------
+ * a. PREPPED-MOVE plan move, exit OK
+ * b. PLANNED-MOVE NOT_PREPPED exit NOOP
+ * c. PLANNED-MOVE PREPPED-MOVE exit NOOP (don't plan past a PLANNED buffer)
+ * d. PLANNED-MOVE PLANNED-MOVE trap illegal condition, exit NOOP
+ * e. PLANNED-MOVE COMMAND(s) exit NOOP
+ * f. PREPPED-COMMAND NOT_PREPPED plan command, exit OK
+ * g. PREPPED-COMMAND PREPPED-MOVE plan command, plan move (Note 2), exit OK
+ * h. PREPPED-COMMAND PLANNED-MOVE trap illegal condition, exit NOOP
+ * i. PLANNED-COMMAND NOT_PREPPED skip command, exit OK
+ * j. PLANNED-COMMAND PREPPED-MOVE skip command, plan move (Note 2), exit OK
+ * k. PLANNED-COMMAND PLANNED-MOVE exit NOOP
+ *
+ * 2. Running cases (buffer state == RUNNING)
+ *
+ * run_buffer next N bufs terminal buf Actions
+ * ---------- ----------- ------------ ----------------------------------
+ * a. RUNNING-MOVE PREPPED-MOVE plan move, exit OK
+ * b. RUNNING-MOVE PLANNED-MOVE exit NOOP
+ * c. RUNNING-MOVE COMMAND(s) NOT_PREPPED skip/plan command(s), exit OK
+ * d. RUNNING-MOVE COMMAND(s) PREPPED-MOVE skip/plan command(s), plan move, exit OK
+ * e. RUNNING-MOVE COMMAND(s) PLANNED-MOVE exit NOOP
+ * f. RUNNING-COMMAND PREPPED-MOVE plan move, exit OK
+ * g. RUNNING-COMMAND PLANNED-MOVE exit NOOP
+ * h. RUNNING-COMMAND COMMAND(s) NOT_PREPPED skip/plan command(s), exit OK
+ * i. RUNNING-COMMAND COMMAND(s) PREPPED-MOVE skip/plan command(s), plan move (Note 1a), exit OK
+ * j. RUNNING-COMMAND COMMAND(s) PLANNED-MOVE skip command(s), exit NOOP
+ * (Note: all COMMAND(s) in j. should be in PLANNED state)
*/
-stat_t mp_plan_move()
+static mpBuf_t *_plan_commands(mpBuf_t *bf) // plan or skip commands; return bf past last command
{
- mpBuf_t *bf;
+ // must test for buffer state first as the buffer is only "safe" once it's >= PREPPED
+ while ((bf->buffer_state >= MP_BUFFER_PREPPED) && (bf->block_type >= BLOCK_TYPE_COMMAND)) {
+ if (bf->buffer_state != MP_BUFFER_PLANNED) { // skip already planned buffers
+ bf->buffer_state = MP_BUFFER_PLANNED; // "planning" is just setting the state (for now)
+ }
+ bf = bf->nx;
+ }
+ return (bf);
+}
- // NULL means nothing's running - this is OK
- if ((bf = mp_get_run_buffer()) == NULL) {
+static stat_t _plan_move(mpBuf_t *bf, float entry_velocity)
+{
+ // Calculate ramps for the current planning block and the next PREPPED buffer
+ // The PREPPED buffer will be set to PLANNED later...
+ //
+ // Pass in the bf buffer that will "link" with the planned block
+ // The block and the buffer are implicitly linked for exec_aline()
+ //
+ // Note that that can only be one PLANNED move at a time.
+ // This is to help sync mr.p to point to the next planned mr.bf
+ // mr.p is only advanced in mp_exec_aline(), after mp.r = mr.p.
+ // This code aligns the buffers and the blocks for exec_aline().
+
+ mpBlockRuntimeBuf_t* block = mr.p; // set a local planning block so it doesn't change on you
+ mp_calculate_ramps(block, bf, entry_velocity); // (which it will if you don't do this)
+
+ // diagnostic traps
+ if (block->exit_velocity > block->cruise_velocity) {
+ __asm__("BKPT"); // exit > cruise after calculate_block
+ }
+ if (block->head_length < 0.00001 && block->body_length < 0.00001 && block->tail_length < 0.00001) {
+ __asm__("BKPT"); // zero or negative length block
+ }
+ bf->buffer_state = MP_BUFFER_PLANNED; //...here
+ bf->plannable = false;
+ return (STAT_OK); // report that we planned something...
+}
+
+stat_t mp_forward_plan()
+{
+ mpBuf_t *bf = mp_get_run_buffer();
+ float entry_velocity;
+
+ // Case 0: Examine current running buffer for early exit conditions
+ if (bf == NULL) { // case 0a: NULL means nothing is running - this is OK
st_prep_null();
return (STAT_NOOP);
}
-
- if (bf->buffer_state < MP_BUFFER_PREPPED) {
- // Get outta here.
- // We did nothing
+ if (bf->buffer_state < MP_BUFFER_PREPPED) { // case 0b: nothing to do. get outta here.
return (STAT_NOOP);
}
- if (bf->block_type != BLOCK_TYPE_ALINE) {
- // Nothing to see here...
-
- bf->buffer_state = MP_BUFFER_PLANNED;
-
- // report that we "planned" something...
- return (STAT_OK);
- }
-
- // We default to the planning block
- mpBlockRuntimeBuf_t* block = mr.p;
-
- // Default to the planning buffer
- float entry_velocity = mr.entry_velocity;
-
- // At this point, bf == bf.r
+ // Case 2: Running cases - move bf past run buffer so it acts like case 1
if (bf->buffer_state == MP_BUFFER_RUNNING) {
- // Update bf to bf->nx, set entry_* to mr.r->exit_*.
bf = bf->nx;
- entry_velocity = mr.r->exit_velocity;
+ entry_velocity = mr.r->exit_velocity; // set Note 1 entry_velocity (move cases)
+ } else {
+ entry_velocity = mr.entry_velocity; // set Note 2 entry velocity (command cases)
+ }
- if (bf->buffer_state < MP_BUFFER_PREPPED) {
- // Get outta here.
- // We did nothing
- return (STAT_NOOP);
- }
+ // bf points to command; start cases 1f, 1g, 1h, 1i, 1j, 1k, 2c, 2d, 2e, 2h, 2i, 2j
+ if (bf->block_type != BLOCK_TYPE_ALINE) { // meaning it's a COMMAND
+ bf = _plan_commands(bf); // plan commands or skip past already planned commands
+ // bf now points to the first non-command buffer past the command(s)
+ if ((bf->block_type == BLOCK_TYPE_ALINE) && (bf->buffer_state > MP_BUFFER_PREPPED )) { // case 1i
+ entry_velocity = mr.r->exit_velocity; // set entry_velocity for Note 1a
+ }
+ }
+ // bf will always be on a non-command at this point - either a move or empty buffer
- if (bf->block_type != BLOCK_TYPE_ALINE) {
- // Nothing to see here...
-
- bf->buffer_state = MP_BUFFER_PLANNED;
- // report that we "planned" something...
- return (STAT_OK);
+ // process move
+ if (bf->block_type == BLOCK_TYPE_ALINE) { // do cases 1a - 1e; finish cases 1f - 1k
+ if (bf->buffer_state == MP_BUFFER_PREPPED) {// do 1a; finish 1f, 1j, 2d, 2i
+ return (_plan_move(bf, entry_velocity));
+ } else {
+ return (STAT_NOOP); // do 1b, 1c, 1d, 1e; finish 1g, 1h, 1j, 1k, 2e, 2j
}
}
-
- if (bf->buffer_state == MP_BUFFER_PLANNED) {
- // Get outta here.
- // We did nothing
- return (STAT_NOOP);
- }
-
- // Note that that can only be one PLANNED move at a time.
- // This is to help sync mr.p to point to the next planned mr.bf
- // mr.p is only advanced in mp_exec_aline, after mp.r = mr.p.
-
- mp_calculate_ramps(block, bf, entry_velocity);
-
- if (block->exit_velocity > block->cruise_velocity) {
- __asm__("BKPT"); // exit > cruise after calculate_block
- }
- if (block->head_length < 0.00001 && block->body_length < 0.00001 && block->tail_length < 0.00001) {
- __asm__("BKPT"); // zero or negative length block
- }
-
- bf->buffer_state = MP_BUFFER_PLANNED;
- bf->plannable = false;
-
- // report that we planned something...
- return (STAT_OK);
+ return (STAT_OK); // report that we planned something...
}
/*************************************************************************
* mp_exec_move() - execute runtime functions to prep move for steppers
*
- * Dequeues the buffer queue and executes the move continuations.
- * Manages run buffers and other details
+ * Dequeues the buffer queue and executes the move continuations.
+ * Manages run buffers and other details
*/
stat_t mp_exec_move()
@@ -149,12 +250,12 @@ stat_t mp_exec_move()
}
if (bf->block_type == BLOCK_TYPE_ALINE) { // cycle auto-start for lines only
+
// first-time operations
if (bf->buffer_state != MP_BUFFER_RUNNING) {
if ((bf->buffer_state < MP_BUFFER_PREPPED) && (cm.motion_state == MOTION_RUN)) {
- __asm__("BKPT");
- // rpt_exception(42, "mp_exec_move() buffer is not prepped");
- // ^^^ CAUSES A CRASH. We can't rpt_exception from here!
+ __asm__("BKPT"); // mp_exec_move() buffer is not prepped
+ // IMPORTANT: We can't rpt_exception from here!
st_prep_null();
return (STAT_NOOP);
}
@@ -166,11 +267,11 @@ stat_t mp_exec_move()
if (bf->buffer_state == MP_BUFFER_PREPPED) {
if (cm.motion_state == MOTION_RUN) {
-// __asm__("BKPT"); // we are running but don't have a block planned
+ __asm__("BKPT"); // we are running but don't have a block planned
}
// We need to have it planned. We don't want to do this here, as it
// might already be happening in a lower interrupt.
- st_request_plan_move();
+ st_request_forward_plan();
return (STAT_NOOP);
}
@@ -182,12 +283,12 @@ stat_t mp_exec_move()
mp_planner_time_accounting();
}
- if (bf->nx->buffer_state == MP_BUFFER_PREPPED) {
+ if (bf->nx->buffer_state >= MP_BUFFER_PREPPED) {
// We go ahead and *ask* for a forward planning of the next move.
// This won't call mp_plan_move until we leave this function
// (and have called mp_exec_aline via bf->bf_func).
// This also allows mp_exec_aline to advance mr.p first.
- st_request_plan_move();
+ st_request_forward_plan();
}
// Manage motion state transitions
@@ -215,24 +316,23 @@ stat_t mp_exec_move()
* Returns:
* STAT_OK move is done
* STAT_EAGAIN move is not finished - has more segments to run
- * STAT_NOOP cause no operation from the steppers - do not load the move
- * STAT_xxxxx fatal error. Ends the move and frees the bf buffer
+ * STAT_NOOP cause no operation from the steppers - do not load the move
+ * STAT_xxxxx fatal error. Ends the move and frees the bf buffer
*
- * This routine is called from the (LO) interrupt level. The interrupt
- * sequencing relies on the behaviors of the routines being exactly correct.
- * Each call to _exec_aline() must execute and prep *one and only one*
- * segment. If the segment is the not the last segment in the bf buffer the
- * _aline() must return STAT_EAGAIN. If it's the last segment it must return
- * STAT_OK. If it encounters a fatal error that would terminate the move it
- * should return a valid error code. Failure to obey this will introduce
- * subtle and very difficult to diagnose bugs (trust me on this).
+ * This routine is called from the (LO) interrupt level. The interrupt sequencing
+ * relies on the behaviors of the routines being exactly correct. Each call to
+ * _exec_aline() must execute and prep *one and only one* segment. If the segment
+ * is the not the last segment in the bf buffer the _aline() must return STAT_EAGAIN.
+ * If it's the last segment it must return STAT_OK. If it encounters a fatal error
+ * that would terminate the move it should return a valid error code. Failure to
+ * obey this will introduce subtle and very difficult to diagnose bugs (trust us on this).
*
- * Note 1 Returning STAT_OK ends the move and frees the bf buffer.
+ * Note 1: Returning STAT_OK ends the move and frees the bf buffer.
* Returning STAT_OK at this point does NOT advance position meaning any
* position error will be compensated by the next move.
*
- * Note 2 Solves a potential race condition where the current move ends but the
- * new move has not started because the previous move is still being run
+ * Note 2: Solves a potential race condition where the current move ends but the
+ * new move has not started because the previous move is still being run
* by the steppers. Planning can overwrite the new move.
*/
/* --- State transitions - hierarchical state machine ---
@@ -247,12 +347,35 @@ stat_t mp_exec_move()
* _NEW - trigger initialization
* _RUN1 - run the first part
* _RUN2 - run the second part
+ *
+ * Important distinction to note:
+ * - mp_plan move() is called for every type of move
+ * - mp_exec_move() is called for every type of move
+ * - mp_exec_aline() is only called for alines
*/
-/* Note:
- * For a version of these routines that execute using the original equation-of-motion
- * math (as opposed to the forward difference math) please refer to build 357.xx or earlier.
- * Builds 358 onward have only forward difference code. ALso, the Kahan corrections for the
- * forward differencing were also been removed shortly after as they were not needed.
+/* Synchronization of run BUFFER and run BLOCK
+ *
+ * Note first: mp_exec_aline() makes a huge assumption: When it comes time to get a
+ * new run block (mr.r) it assumes the planner block (mr.p) has been fully planned
+ * via the JIT forward planning and is ready for use as the new run block.
+ *
+ * The runtime uses 2 structures for the current move or commend, the run BUFFER
+ * from the planner queue (mb.r, aka bf), and the run BLOCK from the runtime
+ * singleton (mr.r). These structures are synchronized implicitly, but not
+ * explicitly referenced, as pointers can lead to race conditions.
+ * See plan_zoid.cpp / mp_calculate_ramps() for more details
+ *
+ * When mp_exec_aline() needs to grab a new planner buffer for a new move or command
+ * (i.e. block state is inactive) it swaps (rolls) the run and planner BLOCKS so that
+ * mr.p (planner block) is now the mr.r (run block), and the old mr.r block becomes
+ * available for planning; it becomes mr.p block.
+ *
+ * At the same time, it's when finished with its current run buffer (mb.r), it has already
+ * advanced to the next buffer. mp_exec_move() does this at the end of previous move.
+ * Or in the bootstrap case, there never was a previous mb.r, so the current one is OK.
+ *
+ * As if by magic, the new mb.r aligns with the run block that was just moved in from
+ * the planning block.
*/
/**** NOTICE ** NOTICE ** NOTICE ****
@@ -285,13 +408,15 @@ stat_t mp_exec_aline(mpBuf_t *bf)
// Start a new move by setting up the runtime singleton (mr)
memcpy(&mr.gm, &(bf->gm), sizeof(GCodeState_t)); // copy in the gcode model state
- bf->block_state = BLOCK_ACTIVE; // note that this buffer is running -- note the planner doesn't look at block_state
+ bf->block_state = BLOCK_ACTIVE; // note that this buffer is running
+ // note the planner doesn't look at block_state
mr.block_state = BLOCK_INITIAL_ACTION;
mr.section = SECTION_HEAD;
mr.section_state = SECTION_NEW;
- mr.r = mr.p;
- mr.p = mr.p->nx;
+ // This is the only place in the system where mr.r and mr.p are allowed to be changed
+ mr.r = mr.p; // we are now going to run the planning block
+ mr.p = mr.p->nx; // re-use the old running block as the new planning block
// Assumptions that are required for this to work:
// entry velocity <= cruise velocity && cruise velocity >= exit velocity
@@ -508,11 +633,11 @@ stat_t mp_exec_aline(mpBuf_t *bf)
}
// There are 4 things that can happen here depending on return conditions:
- // status bf->block_state Description
- // ----------- -------------- ----------------------------------------
- // STAT_EAGAIN mr buffer has more segments to run
- // STAT_OK BLOCK_ACTIVE mr and bf buffers are done
- // STAT_OK BLOCK_INITIAL_ACTION mr done; bf must be run again (it's been reused)
+ // status bf->block_state Description
+ // ----------- -------------- ----------------------------------------
+ // STAT_EAGAIN mr buffer has more segments to run
+ // STAT_OK BLOCK_ACTIVE mr and bf buffers are done
+ // STAT_OK BLOCK_INITIAL_ACTION mr done; bf must be run again (it's been reused)
// There is no fourth thing. Nobody expects the Spanish Inquisition
if (status == STAT_EAGAIN) {
@@ -531,7 +656,7 @@ stat_t mp_exec_aline(mpBuf_t *bf)
cm_cycle_end(); // free buffer & end cycle if planner is empty
}
} else {
- st_request_plan_move();
+ st_request_forward_plan();
}
}
}
@@ -821,6 +946,9 @@ static stat_t _exec_aline_body(mpBuf_t *bf)
mr.section_state = SECTION_RUNNING; // uses PERIOD_2 so last segment detection works
}
if (_exec_aline_segment() == STAT_OK) { // OK means this section is done
+ if (fp_ZERO(mr.r->tail_length)) {
+ return(STAT_OK); // ends the move
+ }
mr.section = SECTION_TAIL;
mr.section_state = SECTION_NEW;
}
diff --git a/g2core/plan_line.cpp b/g2core/plan_line.cpp
index 27637acd..1132345b 100644
--- a/g2core/plan_line.cpp
+++ b/g2core/plan_line.cpp
@@ -35,6 +35,7 @@
#include "report.h"
#include "util.h"
#include "spindle.h"
+#include "settings.h"
#include "xio.h"
@@ -102,7 +103,7 @@ float mp_get_runtime_work_position(uint8_t axis) {
/*
* mp_get_runtime_busy() - returns TRUE if motion control busy (i.e. robot is moving)
- * mp_runtime_is_idle() - returns TRUE is steppers are not actively moving
+ * mp_runtime_is_idle() - returns TRUE is steppers are not actively moving
*
* Use mp_get_runtime_busy() to sync to the queue. If you wait until it returns
* FALSE you know the queue is empty and the motors have stopped.
@@ -148,31 +149,36 @@ stat_t mp_aline(GCodeState_t* gm_in)
float length;
// A few notes about the rotated coordinate space:
- // These are position PRE-rotation:
+ // These are positions PRE-rotation:
// gm_in.* (anything in gm_in)
//
// These are positions POST-rotation:
- // target_rotated (after the rotiton here, of course)
+ // target_rotated (after the rotation here, of course)
// mp.* (anything in mp, including mp.gm.*)
-
- // a being target[0],
- // b being target[1],
- // c being target[2],
- // x_1 being cm.rotation_matrix[1][0]
-
+ //
// Shorthand:
- // target_rotated[0] = a x_0 + b y_0 + c z_0
- // target_rotated[1] = a x_1 + b y_1 + c z_1
- // target_rotated[2] = a x_2 + b y_2 + c z_2 + z_offset
+ // target_rotated[0] = a x_0 + b y_0 + c z_0
+ // target_rotated[1] = a x_1 + b y_1 + c z_1
+ // target_rotated[2] = a x_2 + b y_2 + c z_2 + z_offset
+ //
+ // With:
+ // a being target[0],
+ // b being target[1],
+ // c being target[2],
+ // x_1 being cm.rotation_matrix[1][0]
- target_rotated[0] = gm_in->target[0] * cm.rotation_matrix[0][0] + gm_in->target[1] * cm.rotation_matrix[0][1] +
+ target_rotated[0] = gm_in->target[0] * cm.rotation_matrix[0][0] +
+ gm_in->target[1] * cm.rotation_matrix[0][1] +
gm_in->target[2] * cm.rotation_matrix[0][2];
- target_rotated[1] = gm_in->target[0] * cm.rotation_matrix[1][0] + gm_in->target[1] * cm.rotation_matrix[1][1] +
+ target_rotated[1] = gm_in->target[0] * cm.rotation_matrix[1][0] +
+ gm_in->target[1] * cm.rotation_matrix[1][1] +
gm_in->target[2] * cm.rotation_matrix[1][2];
- target_rotated[2] = gm_in->target[0] * cm.rotation_matrix[2][0] + gm_in->target[1] * cm.rotation_matrix[2][1] +
- gm_in->target[2] * cm.rotation_matrix[2][2] + cm.rotation_z_offset;
+ target_rotated[2] = gm_in->target[0] * cm.rotation_matrix[2][0] +
+ gm_in->target[1] * cm.rotation_matrix[2][1] +
+ gm_in->target[2] * cm.rotation_matrix[2][2] +
+ cm.rotation_z_offset;
// copy rotation axes ABC
target_rotated[3] = gm_in->target[3];
@@ -194,6 +200,7 @@ stat_t mp_aline(GCodeState_t* gm_in)
if (fp_ZERO(length)) {
sr_request_status_report(SR_REQUEST_TIMED_FULL); // Was SR_REQUEST_IMMEDIATE_FULL
return (STAT_MINIMUM_LENGTH_MOVE);
+// return (STAT_OK); //+++++ test this
}
// get a cleared buffer and copy in the Gcode model state
@@ -261,7 +268,7 @@ void mp_plan_block_list()
}
if (mp.planner_state > PLANNER_STARTUP) {
if (planned_something && (cm.hold_state != FEEDHOLD_HOLD)) {
- st_request_plan_move(); // start motion if runtime is not already busy
+ st_request_forward_plan(); // start motion if runtime is not already busy
}
}
mp.p = bf; // update planner pointer
@@ -287,13 +294,13 @@ static mpBuf_t* _plan_block(mpBuf_t* bf)
}
}
_calculate_override(bf); // adjust cruise_vmax for feed/traverse override
- // bf->plannable_time = bf->pv->plannable_time; // set plannable time - excluding current move
+ // bf->plannable_time = bf->pv->plannable_time; // set plannable time - excluding current move
bf->buffer_state = MP_BUFFER_IN_PROCESS;
// +++++ Why do we have to do this here?
// bf->pv_group = bf->pv;
- bf->hint = NO_HINT; // ensure we've cleared the hints
+ bf->hint = NO_HINT; // ensure we've cleared the hints
// Time: 12us-41us
if (bf->nx->plannable) { // read in new buffers until EMPTY
return (bf->nx);
@@ -321,15 +328,15 @@ static mpBuf_t* _plan_block(mpBuf_t* bf)
bf->iterations++;
bf->plannable = bf->plannable && !optimal; // Don't accidentally enable plannable!
- // Let's be mindful that for ward planning may change exit_vmax, and our exit velocity may be lowered
+ // Let's be mindful that forward planning may change exit_vmax, and our exit velocity may be lowered
braking_velocity = min(braking_velocity, bf->exit_vmax);
// We *must* set cruise before exit, and keep it at least as high as exit.
bf->cruise_velocity = max(braking_velocity, bf->cruise_velocity);
bf->exit_velocity = braking_velocity;
- // We have two places where it could be a mixed decel or an asymetric bump,
- // dpending on if the pv->exit_vmax is the same as bf.cruise_vmax
+ // We have two places where it could be a mixed decel or an asymmetric bump,
+ // depending on if the pv->exit_vmax is the same as bf.cruise_vmax
bool test_decel_or_bump = false;
// command blocks
@@ -505,7 +512,22 @@ static void _calculate_jerk(mpBuf_t* bf)
for (uint8_t axis = 0; axis < AXES; axis++) {
if (fabs(bf->unit[axis]) > 0) { // if this axis is participating in the move
- jerk = cm.a[axis].jerk_max / fabs(bf->unit[axis]);
+ float axis_jerk = 0;
+#ifdef TRAVERSE_AT_HIGH_JERK
+#warning using experimental feature TRAVERSE_AT_HIGH_JERK!
+ switch (bf->gm.motion_mode) {
+ case MOTION_MODE_STRAIGHT_TRAVERSE:
+ //case MOTION_MODE_STRAIGHT_PROBE: // <-- not sure on this one
+ axis_jerk = cm.a[axis].jerk_high;
+ break;
+ default:
+ axis_jerk = cm.a[axis].jerk_max;
+ }
+#else
+ axis_jerk = cm.a[axis].jerk_max;
+#endif
+
+ jerk = axis_jerk / fabs(bf->unit[axis]);
if (jerk < bf->jerk) {
bf->jerk = jerk;
// bf->jerk_axis = axis; // +++ diagnostic
diff --git a/g2core/plan_zoid.cpp b/g2core/plan_zoid.cpp
old mode 100755
new mode 100644
index 8e9c3025..28a54ee4
--- a/g2core/plan_zoid.cpp
+++ b/g2core/plan_zoid.cpp
@@ -80,7 +80,7 @@ static float _get_meet_velocity(const float v_0,
*
* Note we use three data structures: mr, bf, and block.
*
- * bf holds the data from aline and back-planning. For the most part it is immuatable.
+ * bf holds the data from aline and back-planning. For the most part it is immutable.
*
* block is the data for forward-planning. There are only two block structures, and they
* are for the current block and the next block.
@@ -94,8 +94,8 @@ static float _get_meet_velocity(const float v_0,
*
* All values of block are expected to be setup by mp_calculate_ramps.
*
- * Quick cheat-sheet on which is in bf and whcih is in block:
- * bf:
+ * Quick cheat-sheet on which is in buffer (bf) and which is in block:
+ * buffer (bf):
* block_type
* hint
* {cruise,exit}_vmax
@@ -290,9 +290,9 @@ void mp_calculate_ramps(mpBlockRuntimeBuf_t* block, mpBuf_t* bf, const float ent
// PERFECT_DECELERATION
// MIXED_DECELERATION
- // All that remians is ASYMMETRIC_BUMP and SYMMETRIC_BUMP.
+ // All that remains is ASYMMETRIC_BUMP and SYMMETRIC_BUMP.
// We don't really care if it's symmetric, since the first test that _get_meet_velocity
- // does is for a symmetic move. It's cheaper to just let it do that then to try and prevent it.
+ // does is for a symmetric move. It's cheaper to just let it do that then to try and prevent it.
// *** Requested-Fit cases (2) ***
diff --git a/g2core/planner.cpp b/g2core/planner.cpp
index 04714315..0f17ef13 100644
--- a/g2core/planner.cpp
+++ b/g2core/planner.cpp
@@ -44,7 +44,7 @@
* functions. Data from the Gcode model is transferred to the motion planner by the mp_xxxx()
* functions called by the canonical machine.
*
- * The planner should only use data in the planner model. When a move (block) is ready for
+ * The planner should only use data in the planner model. When a move (buffer) is ready for
* execution the relevant data from the planner is transferred to the runtime model,
* which should also be isolated.
*
@@ -177,6 +177,14 @@ stat_t planner_test_assertions()
) {
return(cm_panic(STAT_PLANNER_ASSERTION_FAILURE, "planner_test_assertions()"));
}
+// for (uint8_t i=0; i < PLANNER_BUFFER_POOL_SIZE; i++) {
+// if (mb.bf[i].nx == nullptr) {
+// _debug_trap("buffer has nullptr for nx");
+// }
+// if (mb.bf[i].pv == nullptr) {
+// _debug_trap("buffer has nullptr for pv");
+// }
+// }
return (STAT_OK);
}
@@ -470,8 +478,8 @@ bool mp_is_phat_city_time()
* mp_planner_callback()'s job is to invoke backward planning intelligently.
* The flow of control and division of responsibilities for planning is:
*
- * - mp_aline() receives new Gcode blocks and initializes the local variables
- * for the new block.
+ * - mp_aline() receives new Gcode moves and initializes the local variables
+ * for the new buffer.
*
* - mp_planner_callback() is called regularly from the main loop.
* It's job is to determine whether or not to call mp_plan_block_list(),
@@ -480,9 +488,9 @@ bool mp_is_phat_city_time()
* mp_planner_callback() also manages planner state - whether the planner
* is IDLE, in STARTUP or in one of the running states.
*
- * - _plan_block_list() / _planblock() is the backward planning function.
+ * - _plan_block() is the backward planning function for a single buffer.
*
- * - Forward planning is just-in-time by the execution runtime
+ * - Just-in-time forward planning is performed by mp_plan_move() in the plan_exec.cpp runtime executive
*
* Some Items to note:
*
@@ -702,7 +710,7 @@ static inline void _clear_buffer(mpBuf_t *bf)
// the pointers and buffer number during interrupts
// We'll have to figure something else out for C, sorry.
- bf->clear();
+ bf->reset();
}
void _init_planner_queue(uint8_t q, mpBuf_t *pool, uint8_t size)
@@ -730,6 +738,29 @@ void _init_planner_queue(uint8_t q, mpBuf_t *pool, uint8_t size)
b->bf[i].nx = nx; // setup circular list pointers
b->bf[i].pv = pv;
pv = &b->bf[i];
+
+/*=======
+
+ //memset(&mb, 0, sizeof(mb)); // clear all values, pointers and status
+ mb.magic_start = MAGICNUM;
+ mb.magic_end = MAGICNUM;
+
+ mb.w = &mb.bf[0]; // init all buffer pointers
+ mb.r = &mb.bf[0];
+ pv = &mb.bf[PLANNER_BUFFER_POOL_SIZE-1];
+ for (uint8_t i=0; i < PLANNER_BUFFER_POOL_SIZE; i++) {
+ _clear_buffer(&mb.bf[i]);
+ uint8_t nx_i = ((i<(PLANNER_BUFFER_POOL_SIZE-1))?(i+1):0); // buffer incr & wrap
+
+ mb.bf[i].buffer_number = i; //+++++ number it for diagnostics only (otherwise not used)
+
+ nx = &mb.bf[nx_i];
+ mb.bf[i].nx = nx; // setup ring pointers
+ mb.bf[i].pv = pv;
+
+ pv = &mb.bf[i];
+>>refs/heads/dev-168-gquadratic
+*/
}
}
@@ -799,7 +830,8 @@ void mp_commit_write_buffer(const blockType block_type)
if ((mp.planner_state > PLANNER_STARTUP) && (cm.hold_state == FEEDHOLD_OFF)) {
// NB: BEWARE! the exec may result in the planner buffer being
// processed IMMEDIATELY and then freed - invalidating the contents
- st_request_plan_move(); // request an exec if the runtime is not busy
+// st_request_plan_move(); // ++++ request an exec if the runtime is not busy
+ st_request_forward_plan(); // request an exec if the runtime is not busy
}
}
q->w->plannable = true; // enable block for planning
diff --git a/g2core/planner.h b/g2core/planner.h
index 471e4940..2766af71 100644
--- a/g2core/planner.h
+++ b/g2core/planner.h
@@ -25,6 +25,126 @@
* 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.
*/
+/*x
+ * --- Planner Background ---
+ *
+ * The planner is a complicated beast that takes a lot of things into account.
+ * Planner documentation is scattered about and co-located with the functions
+ * that perform the actions. Key files are:
+ *
+ * - planner.h - This file has defines, structures and prototypes. What you would expect
+ * - planner.cpp - Core and common functions, queue handling, JSON and command handlers
+ * - plan_line.cpp - Move planning and queuing, backward planning functions
+ * - plan_zoid.cpp - Move forward planning, velocity contour calculations and crazy math
+ * - plan_exec.cpp - Runtime execution functions, calls zoid's forward planning functions
+ * - stepper.cpp/h - Real-time step generation, segment loading, pulls from plan_exec
+ * - plan_arc.cpp/h- Arc calculation and runtime functions - layer above the rest of this
+ *
+ * --- Planner Overview ---
+ *
+ * At high level the planner's job is to reconstruct smooth motion from a set of linear
+ * approximations while observing and operating within the physical constraints of the
+ * machine and the physics of motion. Gcode - which consists of a series of into linear
+ * motion segments - is interpreted, queued to the planner, and joined together to produce
+ * continuous, synchronized motion. Non-motion commands such as pauses (dwells) and
+ * peripheral controls such as spindles can also be synchronized in the queue. Arcs are
+ * just a special case consisting of many linear moves. Arcs are not interpreted directly.
+ *
+ * The planner sits in the middle of three system layers:
+ * - The Gcode interpreter and canonical machine (the 'model'), which feeds...
+ * - The planner - taking generic commands from the model and queuing them for...
+ * - The runtime layer - pulling from the planner and driving stepper motors or other devices
+ *
+ * The planner queue is the heart of the planner. It's a circular list of ~48 complex structures
+ * that carry the state of the system needs to execute a linear motion, run a pre-planned command,
+ * like turning on a spindle, or executing an arbitrary JSON command such as an active comment.
+ *
+ * The queue can be viewed as a list of instructions that will execute in exact sequence.
+ * Some instructions control motion and need to be joined to their forward and backwards
+ * neighbors so that position, velocity, acceleration, and jerk constraints are not
+ * violated when moving from one motion to the next.
+ *
+ * Others are "commands" that are actually just function callbacks that happen to execute
+ * at a particular point in time (synchronized with motion commands). Commands can control
+ * anything you can reasonably program, such as digital IO, serial communications, or
+ * interpreted commands encoded in JSON.
+ *
+ * The buffers in the planner queue are treated as a 'closure' - with all state needed for
+ * proper execution carried in the planner structure. This is important as it keeps
+ * model state coherent in a heavily pipelined system. The local copy of the Gcode
+ * model is carried in the gm structure that is part of each planner buffer.
+ * See header notes in planner.cpp for more details.
+ *
+ * The planner is entered by calling one of:
+ * - mp_aline() - plan and queue a move with acceleration management
+ * - mp_dwell() - plan and queue a pause (dwell) to the planner queue
+ * - mp_queue_command() - queue a canned command
+ * - mp_json_command() - queue a JSON command for run-time interpretation and execution (M100)
+ * - mp_json_wait() - queue a JSON wait for run-time interpretation and execution (M101)
+ * -
+ * In addition, cm_arc_feed() valaidates and sets up a arc paramewters and calls mp_aline()
+ * repeatedly to spool out the arc segments into the planner queue.
+ *
+ * All the above queueing commands other than mp_aline() are relatively trivial; they just
+ * post callbacks into the next available planner buffer. Command functions are in 2 parts:
+ * the part that posts to the queue, and the callback that is executed when the command is
+ * finally reached in the queue - the _exec().
+ *
+ * All mp_aline() does is some preliminary math and then posts an initialized buffer to
+ * the planner queue. The rest of the move planning operations takes place in background;
+ * via mp_planner_callback() called from the main loop, and as 'pulls' from the runtime
+ * stepper operations.
+ *
+ * Motion planning is separated into backward planning and forward planning stages.
+ * Backward planning is initiated by mp_planner_callback() which is called repeatedly
+ * from the main loop. Backwards planning is performed by mp_plan_block_list() and
+ * _plan_block(). It starts at the most recently arrived Gcode block. Backward
+ * planning can occur multiple times for a given buffer, as new moves arriving
+ * can make the motion profile more optimal.
+ *
+ * Backward planning uses velocity and jerk constraints to set maximum entry,
+ * travel (cruise) and exit velocities for the moves in the queue. In addition,
+ * it observes the maximum cornering velocities that adjoining moves can sustain
+ * in a corner or a 'kink' to ensure that the jerk limit of any axis participating
+ * in the move is not violated. See mp_planner_callback() header comments for more detail.
+ *
+ * Forward planning is performed just-in-time and only once, right before the
+ * planner runtime needs the next buffer. Forward planning provides the final
+ * contouring of the move. It is invoked by mp_forward_plan() and executed by
+ * mp_calculate_ramps() in plan_zoid.cpp.
+ *
+ * Planner timing operates at a few different levels:
+ *
+ * - New lines of ASCII containing commands and moves arriving from the USB are
+ * parsed and executed as the lowest priority background task from the main loop.
+ *
+ * - Backward planning is invoked by a main loop callback, so it also executes as
+ * a background task, albeit a higher priority one.
+ *
+ * - Forward planning and the ultimate preparation of the move for the runtime runs
+ * as an interrupt as a 'pull' from the planner queue that uses a series of
+ * interrupts at progressively lower priorities to ensure that the next planner
+ * buffer is ready before the runtime runs out of forward-planned moves and starves.
+ *
+ * Some other functions performed by the planner include:
+ *
+ * - Velocity throttling to ensure that very short moves do not execute faster
+ * than the serial interface can deliver them
+ *
+ * - Feed hold and cycle start (resume) operations
+ *
+ * - Feed rate override functions and replanning
+ *
+ * Some terms that are useful that we try to use consistently:
+ *
+ * - buffer - in this context a planner buffer holding a move or a command: mb._ or bf
+ * - block - a data structure for planning or runtime control. See mp_calculate_ramps() comments
+ * - move - a linear Gcode move, typically from a G0 or G1 code
+ * - command - a non-move executable in the planner
+ * - group - a collection of moves or commands that are treated as a unit
+ * - line - a line of ASCII gcode or arbitrary text
+ * - bootstrap - the startup period where the planner collects moves but does not yet execute them
+ */
#ifndef PLANNER_H_ONCE
#define PLANNER_H_ONCE
@@ -60,12 +180,12 @@ typedef enum { // bf->buffer_state values in incresing orde
} bufferState;
typedef enum { // bf->block_type values
- BLOCK_TYPE_NULL = 0, // null move - does a no-op
- BLOCK_TYPE_ALINE, // acceleration planned line
- BLOCK_TYPE_DWELL, // delay with no movement
- BLOCK_TYPE_COMMAND, // general command
- BLOCK_TYPE_JSON_WAIT, // general command
- BLOCK_TYPE_TOOL, // T command
+ BLOCK_TYPE_NULL = 0, // MUST=0 null move - does a no-op
+ BLOCK_TYPE_ALINE = 1, // MUST=1 acceleration planned line
+ BLOCK_TYPE_COMMAND = 2, // MUST=2 general command
+ BLOCK_TYPE_DWELL, // Gcode dwell
+ BLOCK_TYPE_JSON_WAIT, // JSON wait command
+ BLOCK_TYPE_TOOL, // T command (T, not M6 tool change)
BLOCK_TYPE_SPINDLE_SPEED, // S command
BLOCK_TYPE_STOP, // program stop
BLOCK_TYPE_END // program end
@@ -179,7 +299,12 @@ typedef enum {
#define UPDATE_MP_DIAGNOSTICS { mp.plannable_time_ms = mp.plannable_time*60000; }
/*
- * Planner structures
+ * Planner structures
+ *
+ * You should be aware of the distinction between 'buffers' and 'blocks'
+ * Please refer to header comments in for important details on buffers and blocks
+ * - plan_zoid.cpp / mp_calculate_ramps()
+ * - plan_exec.cpp / mp_exec_aline()
*/
struct mpBuffer_to_clear {
@@ -236,8 +361,46 @@ struct mpBuffer_to_clear {
GCodeState_t gm; // Gcode model state - passed from model, used by planner and runtime
- void clear() {
- memset((void *)(this), 0, sizeof(mpBuffer_to_clear));
+ void reset() {
+ //memset((void *)(this), 0, sizeof(mpBuffer_to_clear));
+
+ bf_func = nullptr;
+ cm_func = nullptr;
+
+ linenum = 0;
+ iterations = 0;
+ block_time_ms = 0;
+ plannable_time_ms = 0;
+ plannable_length = 0;
+ meet_iterations = 0;
+
+ buffer_state = MP_BUFFER_EMPTY;
+ block_type = BLOCK_TYPE_NULL;
+ block_state = BLOCK_INACTIVE;
+ hint = NO_HINT;
+
+ for (uint8_t i = 0; i< AXES; i++) {
+ unit[i] = 0;
+ axis_flags[i] = 0;
+ }
+
+ plannable = false;
+ length = 0.0;
+ block_time = 0.0;
+ override_factor = 0.0;
+ cruise_velocity = 0.0;
+ exit_velocity = 0.0;
+ cruise_vset = 0.0;
+ cruise_vmax = 0.0;
+ exit_vmax = 0.0;
+ absolute_vmax = 0.0;
+ junction_vmax = 0.0;
+ jerk = 0.0;
+ jerk_sq = 0.0;
+ recip_jerk = 0.0;
+ sqrt_j = 0.0;
+ q_recip_2_sqrt_j = 0.0;
+ gm.reset();
}
};
@@ -336,9 +499,9 @@ typedef struct mpMotionRuntimeSingleton { // persistent runtime variables
float encoder_steps[MOTORS]; // encoder position in steps - ideally the same as commanded_steps
float following_error[MOTORS]; // difference between encoder_steps and commanded steps
- mpBlockRuntimeBuf_t *r; // what's running
- mpBlockRuntimeBuf_t *p; // what's being planned, p might == r
- mpBlockRuntimeBuf_t bf[2]; // the buffer
+ mpBlockRuntimeBuf_t *r; // block that is running
+ mpBlockRuntimeBuf_t *p; // block that is being planned, p might == r
+ mpBlockRuntimeBuf_t bf[2]; // buffer holding the two blocks
float entry_velocity; // entry values for the currently running block
@@ -435,7 +598,7 @@ void mp_plan_block_forward(mpBuf_t *bf);
void mp_calculate_ramps(mpBlockRuntimeBuf_t *block, mpBuf_t *bf, const float entry_velocity);
float mp_get_target_length(const float v_0, const float v_1, const mpBuf_t *bf);
float mp_get_target_velocity(const float v_0, const float L, const mpBuf_t *bf); // acceleration ONLY
-float mp_get_decel_velocity(const float v_0, const float L, const mpBuf_t *bf); // decelleration ONLY
+float mp_get_decel_velocity(const float v_0, const float L, const mpBuf_t *bf); // deceleration ONLY
float mp_find_t(const float v_0, const float v_1, const float L, const float totalL, const float initial_t, const float T);
float mp_calc_v(const float t, const float v_0, const float v_1); // compute the velocity along the curve accelerating from v_0 to v_1, at position t=[0,1]
@@ -444,8 +607,8 @@ float mp_calc_j(const float t, const float v_0, const float v_1, const float T);
//float mp_calc_l(const float t, const float v_0, const float v_1, const float T); // compute length over curve accelerating from v_0 to v_1, at position t=[0,1], total time T
// plan_exec.c functions
+stat_t mp_forward_plan(void);
stat_t mp_exec_move(void);
-stat_t mp_plan_move(void);
stat_t mp_exec_aline(mpBuf_t *bf);
void mp_exit_hold_state(void);
diff --git a/g2core/settings/settings_Printrbot_Simple_1608.h b/g2core/settings/settings_Printrbot_Simple_1608.h
index 7b24e154..d9c71c28 100755
--- a/g2core/settings/settings_Printrbot_Simple_1608.h
+++ b/g2core/settings/settings_Printrbot_Simple_1608.h
@@ -39,7 +39,7 @@
//**** GLOBAL / GENERAL SETTINGS ******************************************************
-#define JUNCTION_INTEGRATION_TIME 1.05 // cornering - between 0.10 and 2.00 (higher is faster)
+#define JUNCTION_INTEGRATION_TIME 1.15 // cornering - between 0.10 and 2.00 (higher is faster)
#define CHORDAL_TOLERANCE 0.01 // chordal accuracy for arc drawing (in mm)
#define SOFT_LIMIT_ENABLE 0 // 0=off, 1=on
@@ -94,11 +94,11 @@
// 80 steps/mm at 1/16 microstepping = 40 mm/rev
#define M1_MOTOR_MAP AXIS_X // 1ma
#define M1_STEP_ANGLE 1.8 // 1sa
-#define M1_TRAVEL_PER_REV 40.132 // 1tr
+#define M1_TRAVEL_PER_REV 40.011604 // 1tr
#define M1_MICROSTEPS 32 // 1mi 1,2,4,8,16,32
#define M1_POLARITY 0 // 1po 0=normal, 1=reversed
#define M1_POWER_MODE MOTOR_POWER_MODE // 1pm standard
-#define M1_POWER_LEVEL 0.5 // 1mp
+#define M1_POWER_LEVEL 0.35 // 1mp
// 80 steps/mm at 1/16 microstepping = 40 mm/rev
#define M3_MOTOR_MAP AXIS_Y
@@ -107,7 +107,7 @@
#define M3_MICROSTEPS 32
#define M3_POLARITY 0
#define M3_POWER_MODE MOTOR_POWER_MODE
-#define M3_POWER_LEVEL 0.5
+#define M3_POWER_LEVEL 0.35
// 2020 steps/mm at 1/16 microstepping = 1.58416 mm/rev
#define M2_MOTOR_MAP AXIS_Z
@@ -116,7 +116,7 @@
#define M2_MICROSTEPS 32
#define M2_POLARITY 1
#define M2_POWER_MODE MOTOR_POWER_MODE
-#define M2_POWER_LEVEL 0.4
+#define M2_POWER_LEVEL 0.3
// 96 steps/mm at 1/16 microstepping = 33.3333 mm/rev
#define M4_MOTOR_MAP AXIS_A
@@ -125,7 +125,7 @@
#define M4_MICROSTEPS 32
#define M4_POLARITY 1
#define M4_POWER_MODE MOTOR_POWER_MODE
-#define M4_POWER_LEVEL 0.4
+#define M4_POWER_LEVEL 0.3
// 96 steps/mm at 1/16 microstepping = 33.3333 mm/rev
#define M5_MOTOR_MAP AXIS_B
@@ -139,12 +139,12 @@
// *** axis settings **********************************************************************************
#define X_AXIS_MODE AXIS_STANDARD // xam see canonical_machine.h cmAxisMode for valid values
-#define X_VELOCITY_MAX 10000 // xvm G0 max velocity in mm/min
+#define X_VELOCITY_MAX 15000 // xvm G0 max velocity in mm/min
#define X_FEEDRATE_MAX X_VELOCITY_MAX // xfr G1 max feed rate in mm/min
#define X_TRAVEL_MIN 0 // xtn minimum travel - used by soft limits and homing
#define X_TRAVEL_MAX 215 // xtm travel between switches or crashes
-#define X_JERK_MAX 3000 // xjm yes, that's "100 billion" mm/(min^3)
-#define X_JERK_HIGH_SPEED 3000 // xjh
+#define X_JERK_MAX 6000 // xjm yes, that's "100 billion" mm/(min^3)
+#define X_JERK_HIGH_SPEED 6000 // xjh
#define X_HOMING_INPUT 4 // xhi input used for homing or 0 to disable
#define X_HOMING_DIRECTION 0 // xhd 0=search moves negative, 1= search moves positive
#define X_SEARCH_VELOCITY 2500 // xsv move in negative direction
@@ -153,12 +153,12 @@
#define X_ZERO_BACKOFF 0.5 // xzb mm
#define Y_AXIS_MODE AXIS_STANDARD
-#define Y_VELOCITY_MAX 10000
+#define Y_VELOCITY_MAX 15000
#define Y_FEEDRATE_MAX Y_VELOCITY_MAX
#define Y_TRAVEL_MIN 0
#define Y_TRAVEL_MAX 152.4
-#define Y_JERK_MAX 3000
-#define Y_JERK_HIGH_SPEED 3000
+#define Y_JERK_MAX 6000
+#define Y_JERK_HIGH_SPEED 6000
#define Y_HOMING_INPUT 1
#define Y_HOMING_DIRECTION 1
#define Y_SEARCH_VELOCITY 3000
@@ -185,7 +185,7 @@
* To calculate the speeds here, in Wolfram Alpha-speak:
*
* c=2*pi*r, r=0.609, d=c/360, s=((S*60)/d), S=40 for s
- * c=2*pi*r, r=5.30516476972984, d=c/360, s=((S*60)/d), S=40 for s
+ * c=2*pi*r, r=4.28394, d=c/360, s=((S*60)/d), S=40 for s
*
* Change r to A_RADIUS, and S to the desired speed, in mm/s or mm/s/s/s.
*
@@ -204,12 +204,12 @@
//#define A_VELOCITY_MAX 25920.0 // ~40 mm/s, 2,400 mm/min
//#define A_FEEDRATE_MAX 25920.0/2.0 // ~20 mm/s, 1,200 mm/min
#define A_VELOCITY_MAX 77760.0 // G0 rate ~120 mm/s, 2,400 mm/min
-#define A_FEEDRATE_MAX 6480 // ~10 mm/s
+#define A_FEEDRATE_MAX 16050 // ~10 mm/s
//#define A_FEEDRATE_MAX 9720.0 // 9720.0 = G1 rate ~15 mm/s, 900 mm/min
#define A_TRAVEL_MIN 0
#define A_TRAVEL_MAX 10
-#define A_JERK_MAX 81000 // 250 million mm/min^3 = 324000
-//#define A_JERK_MAX 162000 // 250 million mm/min^3 = 324000
+//#define A_JERK_MAX 81000 // 250 million mm/min^3 = 324000
+#define A_JERK_MAX 162000 // 250 million mm/min^3 = 324000
//#define A_JERK_MAX 324000 // 500 million mm/min^3 = 324000
//#define A_JERK_MAX 648000 // 1,000 million mm/min^3 = 648000
// * a million IF it's over a million
diff --git a/g2core/settings/settings_default.h b/g2core/settings/settings_default.h
index 233346c3..e0566ae7 100755
--- a/g2core/settings/settings_default.h
+++ b/g2core/settings/settings_default.h
@@ -58,6 +58,11 @@
// *** Machine configuration settings *** //
+#ifndef USB_SERIAL_PORTS_EXPOSED
+#define USB_SERIAL_PORTS_EXPOSED 1 // Valid options are 1 or 2, only!
+#endif
+
+
#ifndef JUNCTION_INTEGRATION_TIME
#define JUNCTION_INTEGRATION_TIME 0.75 // {jt: cornering - between 0.05 and 2.00 (max)
#endif
diff --git a/g2core/settings/settings_eggbot.h b/g2core/settings/settings_eggbot.h
new file mode 100755
index 00000000..4527a339
--- /dev/null
+++ b/g2core/settings/settings_eggbot.h
@@ -0,0 +1,401 @@
+/*
+ * settings_eggbot.h - settings for the EggBot
+ * This file is part of the g2core project
+ *
+ * Copyright (c) 2016 Alden S. Hart Jr.
+ * Copyright (c) 2016 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 .
+ *
+ * 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.
+ */
+
+/***********************************************************************/
+/**** TestV9 profile - naked board *************************************/
+/***********************************************************************/
+
+// ***> NOTE: The init message must be a single line with no CRs or LFs
+#define INIT_MESSAGE "Initializing configs to WaterColorBot v2 settings"
+
+#define JUNCTION_INTEGRATION_TIME 1.50 // cornering - usually between 0.5 and 2.0 (higher is faster)
+#define CHORDAL_TOLERANCE 0.01 // chordal accuracy for arc drawing (in mm)
+
+#define SOFT_LIMIT_ENABLE 0 // 0=off, 1=on
+#define HARD_LIMIT_ENABLE 1 // 0=off, 1=on
+#define SAFETY_INTERLOCK_ENABLE 1 // 0=off, 1=on
+
+#define SPINDLE_ENABLE_POLARITY 1 // 0=active low, 1=active high
+#define SPINDLE_DIR_POLARITY 0 // 0=clockwise is low, 1=clockwise is high
+#define SPINDLE_PAUSE_ON_HOLD true
+#define SPINDLE_DWELL_TIME 1.0
+
+#define COOLANT_MIST_POLARITY 1 // 0=active low, 1=active high
+#define COOLANT_FLOOD_POLARITY 1 // 0=active low, 1=active high
+#define COOLANT_PAUSE_ON_HOLD false
+
+// Communications and reporting settings
+
+#define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE
+#define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS
+
+#define TEXT_VERBOSITY TV_VERBOSE // one of: TV_SILENT, TV_VERBOSE
+#define JSON_VERBOSITY JV_MESSAGES // one of: JV_SILENT, JV_FOOTER, JV_CONFIGS, JV_MESSAGES, JV_LINENUM, JV_VERBOSE
+#define QUEUE_REPORT_VERBOSITY QR_OFF // one of: QR_OFF, QR_SINGLE, QR_TRIPLE
+
+#define STATUS_REPORT_VERBOSITY SR_FILTERED // one of: SR_OFF, SR_FILTERED, SR_VERBOSE
+#define STATUS_REPORT_MIN_MS 100 // milliseconds - enforces a viable minimum
+#define STATUS_REPORT_INTERVAL_MS 250 // milliseconds - set $SV=0 to disable
+
+//#define STATUS_REPORT_DEFAULTS "line","posx","posy","posz","posa","bcr","feed","vel","unit","coor","dist","admo","frmo","momo","stat"
+#define STATUS_REPORT_DEFAULTS "line","posx","posy","posz","feed","vel","momo","stat"
+
+// Alternate SRs
+//#define STATUS_REPORT_DEFAULTS "line","vel","mpox","mpoy","mpoz","mpoa","coor","ofsa","ofsx","ofsy","ofsz","dist","unit","stat","homz","homy","homx","momo"
+//#define STATUS_REPORT_DEFAULTS "_ts1","_cs1","_es1","_xs1","_fe1","line","posx","posy","posz","vel","stat"
+//#define STATUS_REPORT_DEFAULTS "line","posx","posy","posz","vel","_cs1","_es1","_xs1","_fe1","_cs2","_es2","_xs2","_fe2"
+//#define STATUS_REPORT_DEFAULTS "mpox","mpoy","mpoz","mpoa","ofsx","ofsy","ofsz","ofsa","unit","stat","coor","momo","dist","home","hold","macs","cycs","mots","plan","feed"
+//#define STATUS_REPORT_DEFAULTS "line","mpox","mpoy","mpoz","mpoa","ofsx","ofsy","ofsz","ofsa","stat","_cs1","_es1","_fe0","_fe1","_fe2","_fe3"
+//#define STATUS_REPORT_DEFAULTS "line","mpox","mpoy","mpoz","stat","_ts2","_ps2","_cs2","_es2","_fe2"
+//#define STATUS_REPORT_DEFAULTS "line","mpox","mpoy","mpoz","_cs3","_es3","_fe3","_xs3","_cs2","_es2","_fe2","_xs2","stat"
+//#define STATUS_REPORT_DEFAULTS "line","posx","posy","posz","posa","feed","vel","unit","coor","dist","frmo","momo","stat","_cs1","_es1","_xs1","_fe1"
+
+// Gcode startup defaults
+#define GCODE_DEFAULT_UNITS MILLIMETERS // MILLIMETERS or INCHES
+#define GCODE_DEFAULT_PLANE CANON_PLANE_XY // CANON_PLANE_XY, CANON_PLANE_XZ, or CANON_PLANE_YZ
+#define GCODE_DEFAULT_COORD_SYSTEM G54 // G54, G55, G56, G57, G58 or G59
+#define GCODE_DEFAULT_PATH_CONTROL PATH_CONTINUOUS
+#define GCODE_DEFAULT_DISTANCE_MODE ABSOLUTE_MODE
+
+// *** motor settings ************************************************************************************
+
+#define MOTOR_POWER_MODE MOTOR_POWERED_IN_CYCLE // default motor power mode (see cmMotorPowerMode in stepper.h)
+#define MOTOR_POWER_TIMEOUT 2.00 // motor power timeout in seconds
+
+#define MOTOR_POWER_MODE MOTOR_POWERED_IN_CYCLE // default motor power mode (see cmMotorPowerMode in stepper.h)
+#define MOTOR_POWER_TIMEOUT 2.00 // motor power timeout in seconds
+
+#define M1_MOTOR_MAP AXIS_Y // 1ma
+#define M1_STEP_ANGLE 1.8 // 1sa
+#define M1_TRAVEL_PER_REV 125.66370614 // 1tr
+#define M1_MICROSTEPS 32 // 1mi 1,2,4,8
+#define M1_POLARITY 1 // 1po 0=normal, 1=reversed
+#define M1_POWER_MODE MOTOR_POWER_MODE // 1pm standard
+#define M1_POWER_LEVEL 0.4
+
+#define M2_MOTOR_MAP AXIS_X
+#define M2_STEP_ANGLE 1.8
+#define M2_TRAVEL_PER_REV 125.66370614
+#define M2_MICROSTEPS 32
+#define M2_POLARITY 1
+#define M2_POWER_MODE MOTOR_POWER_MODE
+#define M2_POWER_LEVEL 0.4
+
+#define M3_MOTOR_MAP AXIS_Z
+ // This "stepper" is a hobby servo. Note that all hobby
+ // servo settings are per full servo range, instead of
+ // per revolution.
+#define M3_STEP_ANGLE 1.8 // hobby servos are simulated with 200 "full steps"
+#define M3_TRAVEL_PER_REV 26 // this is actually the full travel of the servo, not
+ // necessarily covering a revolution
+#define M3_MICROSTEPS 32 // the max step resolution for a hobby servo is 1/32
+#define M3_POLARITY 1
+#define M3_POWER_MODE MOTOR_ALWAYS_POWERED
+#define M3_POWER_LEVEL 0.50 // this is ignored
+
+#define M4_MOTOR_MAP AXIS_A
+#define M4_STEP_ANGLE 1.8
+#define M4_TRAVEL_PER_REV 360 // degrees moved per motor rev
+#define M4_MICROSTEPS 32
+#define M4_POLARITY 0
+#define M4_POWER_MODE MOTOR_POWER_MODE
+#define M4_POWER_LEVEL 0.6
+
+// *** axis settings **********************************************************************************
+
+#define JERK_MAX 20000
+
+#define X_AXIS_MODE AXIS_STANDARD // xam see canonical_machine.h cmAxisMode for valid values
+#define X_VELOCITY_MAX 50000 // xvm G0 max velocity in mm/min
+#define X_FEEDRATE_MAX X_VELOCITY_MAX // xfr G1 max feed rate in mm/min
+#define X_TRAVEL_MIN 0 // xtn minimum travel - used by soft limits and homing
+#define X_TRAVEL_MAX 400 // xtm maximum travel - used by soft limits and homing
+#define X_JERK_MAX JERK_MAX // xjm
+#define X_JERK_HIGH_SPEED X_JERK_MAX // xjh
+#define X_HOMING_INPUT 1 // xhi input used for homing or 0 to disable
+#define X_HOMING_DIRECTION 0 // xhd 0=search moves negative, 1= search moves positive
+#define X_SEARCH_VELOCITY 1000 // xsv move in negative direction
+#define X_LATCH_VELOCITY 100 // xlv mm/min
+#define X_LATCH_BACKOFF 10 // xlb mm
+#define X_ZERO_BACKOFF 2 // xzb mm
+
+#define Y_AXIS_MODE AXIS_STANDARD
+#define Y_VELOCITY_MAX 50000
+#define Y_FEEDRATE_MAX Y_VELOCITY_MAX
+#define Y_TRAVEL_MIN 0
+#define Y_TRAVEL_MAX 175
+#define Y_JERK_MAX JERK_MAX
+#define Y_JERK_HIGH_SPEED Y_JERK_MAX
+#define Y_HOMING_INPUT 3
+#define Y_HOMING_DIRECTION 0
+#define Y_SEARCH_VELOCITY 1000
+#define Y_LATCH_VELOCITY 100
+#define Y_LATCH_BACKOFF 10
+#define Y_ZERO_BACKOFF 2
+
+#define Z_AXIS_MODE AXIS_STANDARD
+#define Z_VELOCITY_MAX 15000
+#define Z_FEEDRATE_MAX Z_VELOCITY_MAX
+#define Z_TRAVEL_MIN 0
+#define Z_TRAVEL_MAX 75
+#define Z_JERK_MAX 5000
+#define Z_JERK_HIGH_SPEED Z_JERK_MAX
+#define Z_HOMING_INPUT 6
+#define Z_HOMING_DIRECTION 1
+#define Z_SEARCH_VELOCITY 600
+#define Z_LATCH_VELOCITY 100
+#define Z_LATCH_BACKOFF 10
+#define Z_ZERO_BACKOFF 2
+
+// Rotary values are chosen to make the motor react the same as X for testing
+/***************************************************************************************
+ * To calculate the speeds here, in Wolfram Alpha-speak:
+ *
+ * c=2*pi*r, r=0.609, d=c/360, s=((S*60)/d), S=40 for s
+ * c=2*pi*r, r=5.30516476972984, d=c/360, s=((S*60)/d), S=40 for s
+ *
+ * Change r to A_RADIUS, and S to the desired speed, in mm/s or mm/s/s/s.
+ *
+ * It will return s= as the value you want to enter.
+ *
+ * If the value is over 1 million, the code will divide it by 1 million,
+ * so you have to pre-multiply it by 1000000.0. (The value is in millions, btw.)
+ *
+ * Note that you need these to be floating point values, so always have a .0 at the end!
+ *
+ ***************************************************************************************/
+
+#define A_AXIS_MODE AXIS_RADIUS
+#define A_RADIUS 5.30516476972984
+//#define A_VELOCITY_MAX 25920.0 // ~40 mm/s, 2,400 mm/min
+//#define A_FEEDRATE_MAX 25920.0/2.0 // ~20 mm/s, 1,200 mm/min
+#define A_VELOCITY_MAX 77760.0 // G0 rate ~120 mm/s, 2,400 mm/min
+#define A_FEEDRATE_MAX 9720.0 // 9720.0 = G1 rate ~15 mm/s, 900 mm/min
+#define A_TRAVEL_MIN 0
+#define A_TRAVEL_MAX 10
+#define A_JERK_MAX 648000 // 1,000 million mm/min^3 = 648000
+ // * a million IF it's over a million
+ // c=2*pi*r, r=5.30516476972984, d=c/360, s=((1000*60)/d)
+#define A_HOMING_INPUT 0
+#define A_HOMING_DIRECTION 0
+#define A_SEARCH_VELOCITY 2000
+#define A_LATCH_VELOCITY 2000
+#define A_LATCH_BACKOFF 5
+#define A_ZERO_BACKOFF 2
+#define A_JERK_HIGH_SPEED A_JERK_MAX
+
+#define B_AXIS_MODE AXIS_DISABLED
+#define B_RADIUS 1
+#define B_VELOCITY_MAX 3600
+#define B_FEEDRATE_MAX B_VELOCITY_MAX
+#define B_TRAVEL_MIN 0
+#define B_TRAVEL_MAX -1
+//#define B_JERK_MAX 20000000
+#define B_JERK_MAX 20
+#define B_HOMING_INPUT 0
+#define B_HOMING_DIRECTION 0
+#define B_SEARCH_VELOCITY 600
+#define B_LATCH_VELOCITY 100
+#define B_LATCH_BACKOFF 10
+#define B_ZERO_BACKOFF 2
+#define B_JERK_HIGH_SPEED A_JERK_MAX
+
+
+//*** Input / output settings ***
+/*
+ IO_MODE_DISABLED
+ IO_ACTIVE_LOW aka NORMALLY_OPEN
+ IO_ACTIVE_HIGH aka NORMALLY_CLOSED
+
+ INPUT_ACTION_NONE
+ INPUT_ACTION_STOP
+ INPUT_ACTION_FAST_STOP
+ INPUT_ACTION_HALT
+ INPUT_ACTION_RESET
+
+ INPUT_FUNCTION_NONE
+ INPUT_FUNCTION_LIMIT
+ INPUT_FUNCTION_INTERLOCK
+ INPUT_FUNCTION_SHUTDOWN
+ INPUT_FUNCTION_PANIC
+*/
+// Inputs are defined for the g2ref(a) board
+// Xmn (board label)
+#define DI1_MODE IO_ACTIVE_HIGH
+#define DI1_ACTION INPUT_ACTION_NONE
+#define DI1_FUNCTION INPUT_FUNCTION_NONE
+
+// Xmax
+#define DI2_MODE IO_MODE_DISABLED
+#define DI2_ACTION INPUT_ACTION_NONE
+#define DI2_FUNCTION INPUT_FUNCTION_NONE
+
+// Ymin
+#define DI3_MODE IO_MODE_DISABLED
+#define DI3_ACTION INPUT_ACTION_NONE
+#define DI3_FUNCTION INPUT_FUNCTION_NONE
+
+// Ymax
+#define DI4_MODE IO_ACTIVE_HIGH
+#define DI4_ACTION INPUT_ACTION_NONE
+#define DI4_FUNCTION INPUT_FUNCTION_NONE
+
+// Zmin
+#define DI5_MODE IO_ACTIVE_LOW // Z probe
+#define DI5_ACTION INPUT_ACTION_NONE
+#define DI5_FUNCTION INPUT_FUNCTION_NONE
+
+// Zmax
+#define DI6_MODE IO_MODE_DISABLED
+#define DI6_ACTION INPUT_ACTION_STOP
+#define DI6_FUNCTION INPUT_FUNCTION_NONE
+
+// Shutdown (Amin on v9 board)
+#define DI7_MODE IO_MODE_DISABLED
+#define DI7_ACTION INPUT_ACTION_NONE
+#define DI7_FUNCTION INPUT_FUNCTION_NONE
+
+// High Voltage Z Probe In (Amax on v9 board)
+#define DI8_MODE IO_ACTIVE_LOW
+#define DI8_ACTION INPUT_ACTION_NONE
+#define DI8_FUNCTION INPUT_FUNCTION_NONE
+
+// Hardware interlock input
+#define DI9_MODE IO_MODE_DISABLED
+#define DI9_ACTION INPUT_ACTION_NONE
+#define DI9_FUNCTION INPUT_FUNCTION_NONE
+
+//Extruder1_PWM
+#define DO1_MODE IO_ACTIVE_HIGH
+
+//Extruder2_PWM
+#define DO2_MODE IO_ACTIVE_HIGH
+
+//Fan1A_PWM
+#define DO3_MODE IO_ACTIVE_HIGH
+
+//Fan1B_PWM
+#define DO4_MODE IO_ACTIVE_HIGH
+
+#define DO5_MODE IO_ACTIVE_HIGH
+#define DO6_MODE IO_ACTIVE_HIGH
+#define DO7_MODE IO_ACTIVE_HIGH
+#define DO8_MODE IO_ACTIVE_HIGH
+
+//SAFEin (Output) signal
+#define DO9_MODE IO_ACTIVE_HIGH
+
+#define DO10_MODE IO_ACTIVE_HIGH
+
+//Header Bed FET
+#define DO11_MODE IO_ACTIVE_HIGH
+
+//Indicator_LED
+#define DO12_MODE IO_ACTIVE_HIGH
+
+#define DO13_MODE IO_ACTIVE_HIGH
+
+
+/*** Extruders / Heaters ***/
+/*
+#define MIN_FAN_TEMP 40.0
+#define MAX_FAN_TEMP 150.0
+
+#define H1_DEFAULT_ENABLE true
+#define H1_DEFAULT_P 9.0
+#define H1_DEFAULT_I 0.12
+#define H1_DEFAULT_D 400.0
+
+#define H2_DEFAULT_ENABLE false
+#define H2_DEFAULT_P 9.0
+#define H2_DEFAULT_I 0.12
+#define H2_DEFAULT_D 400.0
+
+#define H3_DEFAULT_ENABLE false
+#define H3_DEFAULT_P 9.0
+#define H3_DEFAULT_I 0.12
+#define H3_DEFAULT_D 400.0
+*/
+
+
+// *** PWM SPINDLE CONTROL ***
+
+#define P1_PWM_FREQUENCY 100 // in Hz
+#define P1_CW_SPEED_LO 1000 // in RPM (arbitrary units)
+#define P1_CW_SPEED_HI 2000
+#define P1_CW_PHASE_LO 0.125 // phase [0..1]
+#define P1_CW_PHASE_HI 0.2
+#define P1_CCW_SPEED_LO 1000
+#define P1_CCW_SPEED_HI 2000
+#define P1_CCW_PHASE_LO 0.125
+#define P1_CCW_PHASE_HI 0.2
+#define P1_PWM_PHASE_OFF 0.1
+
+// *** DEFAULT COORDINATE SYSTEM OFFSETS ***
+
+#define G54_X_OFFSET 0 // G54 is traditionally set to all zeros
+#define G54_Y_OFFSET 0
+#define G54_Z_OFFSET 0
+#define G54_A_OFFSET 0
+#define G54_B_OFFSET 0
+#define G54_C_OFFSET 0
+
+#define G55_X_OFFSET (X_TRAVEL_MAX/2) // set to middle of table
+#define G55_Y_OFFSET (Y_TRAVEL_MAX/2)
+#define G55_Z_OFFSET 0
+#define G55_A_OFFSET 0
+#define G55_B_OFFSET 0
+#define G55_C_OFFSET 0
+
+#define G56_X_OFFSET 0
+#define G56_Y_OFFSET 0
+#define G56_Z_OFFSET 0
+#define G56_A_OFFSET 0
+#define G56_B_OFFSET 0
+#define G56_C_OFFSET 0
+
+#define G57_X_OFFSET 0
+#define G57_Y_OFFSET 0
+#define G57_Z_OFFSET 0
+#define G57_A_OFFSET 0
+#define G57_B_OFFSET 0
+#define G57_C_OFFSET 0
+
+#define G58_X_OFFSET 0
+#define G58_Y_OFFSET 0
+#define G58_Z_OFFSET 0
+#define G58_A_OFFSET 0
+#define G58_B_OFFSET 0
+#define G58_C_OFFSET 0
+
+#define G59_X_OFFSET 0
+#define G59_Y_OFFSET 0
+#define G59_Z_OFFSET 0
+#define G59_A_OFFSET 0
+#define G59_B_OFFSET 0
+#define G59_C_OFFSET 0
diff --git a/g2core/settings/settings_shapeoko375_Dual.h b/g2core/settings/settings_shapeoko375_Dual.h
new file mode 100644
index 00000000..b787d58c
--- /dev/null
+++ b/g2core/settings/settings_shapeoko375_Dual.h
@@ -0,0 +1,256 @@
+/*
+ * settings_shapeoko375.h - Shapeoko 375mm table
+ * This file is part of the TinyG project
+ *
+ * Copyright (c) 2010 - 2014 Alden S. Hart, Jr.
+ *
+ * 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 .
+ *
+ * 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.
+ */
+/* Note: The values in this file are the default settings that are loaded
+ * into a virgin EEPROM, and can be changed using the config commands.
+ * After initial load the EEPROM values (or changed values) are used.
+ *
+ * System and hardware settings that you shouldn't need to change
+ * are in system.h Application settings that also shouldn't need
+ * to be changed are in tinyg.h
+ */
+
+/***********************************************************************/
+/**** Shapeoko 375mm profile *******************************************/
+/***********************************************************************/
+
+// ***> NOTE: The init message must be a single line with no CRs or LFs
+#define INIT_MESSAGE "Initializing configs to Shapeoko 375mm Dual-Y profile"
+
+//**** GLOBAL / GENERAL SETTINGS ******************************************************
+
+// Machine configuration settings
+
+#define JUNCTION_INTEGRATION_TIME 0.75 // cornering - between 0.10 and 2.00 (higher is faster)
+#define CHORDAL_TOLERANCE 0.01 // chordal tolerance for arcs (in mm)
+
+#define SOFT_LIMIT_ENABLE 0 // 0=off, 1=on
+#define HARD_LIMIT_ENABLE 0 // 0=off, 1=on
+#define SAFETY_INTERLOCK_ENABLE 1 // 0=off, 1=on
+
+#define SPINDLE_ENABLE_POLARITY 1 // 0=active low, 1=active high
+#define SPINDLE_DIR_POLARITY 0 // 0=clockwise is low, 1=clockwise is high
+#define SPINDLE_PAUSE_ON_HOLD true
+#define SPINDLE_DWELL_TIME 1.0
+
+#define COOLANT_MIST_POLARITY 1 // 0=active low, 1=active high
+#define COOLANT_FLOOD_POLARITY 1 // 0=active low, 1=active high
+#define COOLANT_PAUSE_ON_HOLD false
+
+// Communications and reporting settings
+
+#define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE
+#define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS
+
+#define TEXT_VERBOSITY TV_VERBOSE // one of: TV_SILENT, TV_VERBOSE
+#define JSON_VERBOSITY JV_MESSAGES // one of: JV_SILENT, JV_FOOTER, JV_CONFIGS, JV_MESSAGES, JV_LINENUM, JV_VERBOSE
+#define QUEUE_REPORT_VERBOSITY QR_OFF // one of: QR_OFF, QR_SINGLE, QR_TRIPLE
+
+#define STATUS_REPORT_VERBOSITY SR_FILTERED // one of: SR_OFF, SR_FILTERED, SR_VERBOSE
+
+#define STATUS_REPORT_MIN_MS 100 // milliseconds - enforces a viable minimum
+#define STATUS_REPORT_INTERVAL_MS 250 // milliseconds - set $SV=0 to disable
+
+//#define STATUS_REPORT_DEFAULTS "line","posx","posy","posz","posa","bcr","feed","vel","unit","coor","dist","admo","frmo","momo","stat"
+#define STATUS_REPORT_DEFAULTS "line","posx","posy","posz","bcr","feed","vel","momo","stat"
+
+// Alternate SRs that report in drawable units
+//#define STATUS_REPORT_DEFAULTS "line","vel","mpox","mpoy","mpoz","mpoa","coor","ofsa","ofsx","ofsy","ofsz","dist","unit","stat","homz","homy","homx","momo"
+//#define STATUS_REPORT_DEFAULTS "_ts1","_cs1","_es1","_xs1","_fe1","line","posx","posy","posz","vel","stat"
+
+// Gcode startup defaults
+#define GCODE_DEFAULT_UNITS MILLIMETERS // MILLIMETERS or INCHES
+#define GCODE_DEFAULT_PLANE CANON_PLANE_XY // CANON_PLANE_XY, CANON_PLANE_XZ, or CANON_PLANE_YZ
+#define GCODE_DEFAULT_COORD_SYSTEM G54 // G54, G55, G56, G57, G58 or G59
+#define GCODE_DEFAULT_PATH_CONTROL PATH_CONTINUOUS
+#define GCODE_DEFAULT_DISTANCE_MODE ABSOLUTE_MODE
+
+
+// *** motor settings ************************************************************************************
+
+#define MOTOR_POWER_MODE MOTOR_POWERED_IN_CYCLE // default motor power mode (see cmMotorPowerMode in stepper.h)
+#define MOTOR_POWER_TIMEOUT 20.00 // motor power timeout in seconds
+
+// *** motor settings ***
+
+/* Mapping for dual gantry setup */
+#define M1_MOTOR_MAP AXIS_X // 1ma
+#define M1_STEP_ANGLE 1.8 // 1sa
+#define M1_TRAVEL_PER_REV 36.54 // 1tr
+#define M1_MICROSTEPS 32 // 1mi 1,2,4,8
+#define M1_POLARITY 0 // 1po 0=normal, 1=reversed
+#define M1_POWER_MODE MOTOR_POWER_MODE // 1pm TRUE=low power idle enabled
+#define M1_POWER_LEVEL 0.6
+
+#define M2_MOTOR_MAP AXIS_Y
+#define M2_STEP_ANGLE 1.8
+#define M2_TRAVEL_PER_REV 36.54
+#define M2_MICROSTEPS 32
+#define M2_POLARITY 1
+#define M2_POWER_MODE MOTOR_POWER_MODE
+#define M2_POWER_LEVEL 0.6
+
+#define M3_MOTOR_MAP AXIS_Y
+#define M3_STEP_ANGLE 1.8
+#define M3_TRAVEL_PER_REV 36.54
+#define M3_MICROSTEPS 32
+#define M3_POLARITY 0
+#define M3_POWER_MODE MOTOR_POWER_MODE
+#define M3_POWER_LEVEL 0.6
+
+#define M4_MOTOR_MAP AXIS_Z
+#define M4_STEP_ANGLE 1.8
+#define M4_TRAVEL_PER_REV 1.25
+#define M4_MICROSTEPS 32
+#define M4_POLARITY 0
+#define M4_POWER_MODE MOTOR_POWER_MODE
+#define M4_POWER_LEVEL 0.5
+
+// *** axis settings **********************************************************************************
+
+
+#define X_AXIS_MODE AXIS_STANDARD // xam see canonical_machine.h cmAxisMode for valid values
+#define X_VELOCITY_MAX 16000 // xvm G0 max velocity in mm/min
+#define X_FEEDRATE_MAX X_VELOCITY_MAX // xfr G1 max feed rate in mm/min
+#define X_TRAVEL_MIN 0 // xtn minimum travel - used by soft limits and homing
+#define X_TRAVEL_MAX 220 // xtm maximum travel - used by soft limits and homing
+#define X_JERK_MAX 5000 // xjm yes, that's "5 billion" mm/(min^3)
+#define X_JERK_HIGH_SPEED X_JERK_MAX // xjh
+#define X_HOMING_INPUT 1 // xhi input used for homing or 0 to disable
+#define X_HOMING_DIR 0 // xhd 0 to search to minimum, 1 to search to maximum
+#define X_SEARCH_VELOCITY 3000 // xsv minus means move to minimum switch
+#define X_LATCH_VELOCITY 100 // xlv mm/min
+#define X_LATCH_BACKOFF 4 // xlb mm
+#define X_ZERO_BACKOFF 0.5 // xzb mm
+
+#define Y_AXIS_MODE AXIS_STANDARD
+#define Y_VELOCITY_MAX 16000
+#define Y_FEEDRATE_MAX Y_VELOCITY_MAX
+#define Y_TRAVEL_MIN 0
+#define Y_TRAVEL_MAX 220
+#define Y_JERK_MAX 5000
+#define Y_JERK_HIGH_SPEED Y_JERK_MAX
+#define Y_HOMING_INPUT 3
+#define Y_HOMING_DIR 0
+#define Y_SEARCH_VELOCITY 3000
+#define Y_LATCH_VELOCITY 100
+#define Y_LATCH_BACKOFF 4
+#define Y_ZERO_BACKOFF 0.5
+
+#define Z_AXIS_MODE AXIS_STANDARD
+#define Z_VELOCITY_MAX 300
+#define Z_FEEDRATE_MAX Z_VELOCITY_MAX
+#define Z_TRAVEL_MIN 0
+#define Z_TRAVEL_MAX 100
+#define Z_JERK_MAX 25 // 50,000,000
+#define Z_JERK_HIGH_SPEED Z_JERK_MAX
+#define Z_HOMING_INPUT 5
+#define Z_HOMING_DIR 0
+#define Z_SEARCH_VELOCITY Z_VELOCITY_MAX
+#define Z_LATCH_VELOCITY 100
+#define Z_LATCH_BACKOFF 4
+#define Z_ZERO_BACKOFF 0.5
+
+//*** Input / output settings ***
+/*
+ IO_MODE_DISABLED
+ IO_ACTIVE_LOW aka NORMALLY_OPEN
+ IO_ACTIVE_HIGH aka NORMALLY_CLOSED
+
+ INPUT_ACTION_NONE
+ INPUT_ACTION_STOP
+ INPUT_ACTION_FAST_STOP
+ INPUT_ACTION_HALT
+ INPUT_ACTION_RESET
+
+ INPUT_FUNCTION_NONE
+ INPUT_FUNCTION_LIMIT
+ INPUT_FUNCTION_INTERLOCK
+ INPUT_FUNCTION_SHUTDOWN
+ INPUT_FUNCTION_PANIC
+ */
+// Xmin on v9 board
+#define DI1_MODE NORMALLY_CLOSED
+//#define DI1_ACTION INPUT_ACTION_STOP
+#define DI1_ACTION INPUT_ACTION_NONE
+#define DI1_FUNCTION INPUT_FUNCTION_LIMIT
+
+// Xmax
+#define DI2_MODE NORMALLY_CLOSED
+//#define DI2_ACTION INPUT_ACTION_STOP
+#define DI2_ACTION INPUT_ACTION_NONE
+#define DI2_FUNCTION INPUT_FUNCTION_LIMIT
+
+// Ymin
+#define DI3_MODE NORMALLY_CLOSED
+//#define DI3_ACTION INPUT_ACTION_STOP
+#define DI3_ACTION INPUT_ACTION_NONE
+#define DI3_FUNCTION INPUT_FUNCTION_LIMIT
+
+// Ymax
+#define DI4_MODE NORMALLY_CLOSED
+//#define DI4_ACTION INPUT_ACTION_STOP
+#define DI4_ACTION INPUT_ACTION_NONE
+#define DI4_FUNCTION INPUT_FUNCTION_LIMIT
+
+// Zmin
+#define DI5_MODE IO_ACTIVE_HIGH // Z probe
+#define DI5_ACTION INPUT_ACTION_NONE
+#define DI5_FUNCTION INPUT_FUNCTION_NONE
+
+// Zmax
+#define DI6_MODE NORMALLY_CLOSED
+//#define DI6_ACTION INPUT_ACTION_STOP
+#define DI6_ACTION INPUT_ACTION_NONE
+#define DI6_FUNCTION INPUT_FUNCTION_LIMIT
+
+// Amin
+#define DI7_MODE IO_MODE_DISABLED
+#define DI7_ACTION INPUT_ACTION_NONE
+#define DI7_FUNCTION INPUT_FUNCTION_NONE
+
+// Amax
+#define DI8_MODE IO_MODE_DISABLED
+#define DI8_ACTION INPUT_ACTION_NONE
+#define DI8_FUNCTION INPUT_FUNCTION_NONE
+
+// Hardware interlock input
+#define DI9_MODE IO_MODE_DISABLED
+#define DI9_ACTION INPUT_ACTION_NONE
+#define DI9_FUNCTION INPUT_FUNCTION_NONE
+
+// *** PWM SPINDLE CONTROL ***
+
+#define P1_PWM_FREQUENCY 5000 // in Hz
+#define P1_CW_SPEED_LO 3000 // in RPM (arbitrary units)
+#define P1_CW_SPEED_HI 12000
+#define P1_CW_PHASE_LO 0.15 // phase [0..1]
+#define P1_CW_PHASE_HI 0.81
+#define P1_CCW_SPEED_LO 0
+#define P1_CCW_SPEED_HI 0
+#define P1_CCW_PHASE_LO 0.1
+#define P1_CCW_PHASE_HI 0.1
+#define P1_PWM_PHASE_OFF 0.0
diff --git a/g2core/settings/settings_watercolorbot_v2.h b/g2core/settings/settings_watercolorbot_v2.h
new file mode 100755
index 00000000..2eb170a9
--- /dev/null
+++ b/g2core/settings/settings_watercolorbot_v2.h
@@ -0,0 +1,401 @@
+/*
+ * settings_watercolorbot_v2.h - settings for the WaterColorBot v2 (http://watercolorbot.com/)
+ * This file is part of the g2core project
+ *
+ * Copyright (c) 2016 Alden S. Hart Jr.
+ * Copyright (c) 2016 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 .
+ *
+ * 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.
+ */
+
+/***********************************************************************/
+/**** TestV9 profile - naked board *************************************/
+/***********************************************************************/
+
+// ***> NOTE: The init message must be a single line with no CRs or LFs
+#define INIT_MESSAGE "Initializing configs to WaterColorBot v2 settings"
+
+#define JUNCTION_INTEGRATION_TIME 1.50 // cornering - usually between 0.5 and 2.0 (higher is faster)
+#define CHORDAL_TOLERANCE 0.01 // chordal accuracy for arc drawing (in mm)
+
+#define SOFT_LIMIT_ENABLE 0 // 0=off, 1=on
+#define HARD_LIMIT_ENABLE 1 // 0=off, 1=on
+#define SAFETY_INTERLOCK_ENABLE 1 // 0=off, 1=on
+
+#define SPINDLE_ENABLE_POLARITY 1 // 0=active low, 1=active high
+#define SPINDLE_DIR_POLARITY 0 // 0=clockwise is low, 1=clockwise is high
+#define SPINDLE_PAUSE_ON_HOLD true
+#define SPINDLE_DWELL_TIME 1.0
+
+#define COOLANT_MIST_POLARITY 1 // 0=active low, 1=active high
+#define COOLANT_FLOOD_POLARITY 1 // 0=active low, 1=active high
+#define COOLANT_PAUSE_ON_HOLD false
+
+// Communications and reporting settings
+
+#define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE
+#define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS
+
+#define TEXT_VERBOSITY TV_VERBOSE // one of: TV_SILENT, TV_VERBOSE
+#define JSON_VERBOSITY JV_MESSAGES // one of: JV_SILENT, JV_FOOTER, JV_CONFIGS, JV_MESSAGES, JV_LINENUM, JV_VERBOSE
+#define QUEUE_REPORT_VERBOSITY QR_OFF // one of: QR_OFF, QR_SINGLE, QR_TRIPLE
+
+#define STATUS_REPORT_VERBOSITY SR_FILTERED // one of: SR_OFF, SR_FILTERED, SR_VERBOSE
+#define STATUS_REPORT_MIN_MS 100 // milliseconds - enforces a viable minimum
+#define STATUS_REPORT_INTERVAL_MS 250 // milliseconds - set $SV=0 to disable
+
+//#define STATUS_REPORT_DEFAULTS "line","posx","posy","posz","posa","bcr","feed","vel","unit","coor","dist","admo","frmo","momo","stat"
+#define STATUS_REPORT_DEFAULTS "line","posx","posy","posz","feed","vel","momo","stat"
+
+// Alternate SRs
+//#define STATUS_REPORT_DEFAULTS "line","vel","mpox","mpoy","mpoz","mpoa","coor","ofsa","ofsx","ofsy","ofsz","dist","unit","stat","homz","homy","homx","momo"
+//#define STATUS_REPORT_DEFAULTS "_ts1","_cs1","_es1","_xs1","_fe1","line","posx","posy","posz","vel","stat"
+//#define STATUS_REPORT_DEFAULTS "line","posx","posy","posz","vel","_cs1","_es1","_xs1","_fe1","_cs2","_es2","_xs2","_fe2"
+//#define STATUS_REPORT_DEFAULTS "mpox","mpoy","mpoz","mpoa","ofsx","ofsy","ofsz","ofsa","unit","stat","coor","momo","dist","home","hold","macs","cycs","mots","plan","feed"
+//#define STATUS_REPORT_DEFAULTS "line","mpox","mpoy","mpoz","mpoa","ofsx","ofsy","ofsz","ofsa","stat","_cs1","_es1","_fe0","_fe1","_fe2","_fe3"
+//#define STATUS_REPORT_DEFAULTS "line","mpox","mpoy","mpoz","stat","_ts2","_ps2","_cs2","_es2","_fe2"
+//#define STATUS_REPORT_DEFAULTS "line","mpox","mpoy","mpoz","_cs3","_es3","_fe3","_xs3","_cs2","_es2","_fe2","_xs2","stat"
+//#define STATUS_REPORT_DEFAULTS "line","posx","posy","posz","posa","feed","vel","unit","coor","dist","frmo","momo","stat","_cs1","_es1","_xs1","_fe1"
+
+// Gcode startup defaults
+#define GCODE_DEFAULT_UNITS MILLIMETERS // MILLIMETERS or INCHES
+#define GCODE_DEFAULT_PLANE CANON_PLANE_XY // CANON_PLANE_XY, CANON_PLANE_XZ, or CANON_PLANE_YZ
+#define GCODE_DEFAULT_COORD_SYSTEM G54 // G54, G55, G56, G57, G58 or G59
+#define GCODE_DEFAULT_PATH_CONTROL PATH_CONTINUOUS
+#define GCODE_DEFAULT_DISTANCE_MODE ABSOLUTE_MODE
+
+// *** motor settings ************************************************************************************
+
+#define MOTOR_POWER_MODE MOTOR_POWERED_IN_CYCLE // default motor power mode (see cmMotorPowerMode in stepper.h)
+#define MOTOR_POWER_TIMEOUT 2.00 // motor power timeout in seconds
+
+#define MOTOR_POWER_MODE MOTOR_POWERED_IN_CYCLE // default motor power mode (see cmMotorPowerMode in stepper.h)
+#define MOTOR_POWER_TIMEOUT 2.00 // motor power timeout in seconds
+
+#define M1_MOTOR_MAP AXIS_Y // 1ma
+#define M1_STEP_ANGLE 1.8 // 1sa
+#define M1_TRAVEL_PER_REV 91.5 // 1tr
+#define M1_MICROSTEPS 32 // 1mi 1,2,4,8
+#define M1_POLARITY 1 // 1po 0=normal, 1=reversed
+#define M1_POWER_MODE MOTOR_POWER_MODE // 1pm standard
+#define M1_POWER_LEVEL 0.4
+
+#define M2_MOTOR_MAP AXIS_X
+#define M2_STEP_ANGLE 1.8
+#define M2_TRAVEL_PER_REV 91.5
+#define M2_MICROSTEPS 32
+#define M2_POLARITY 0
+#define M2_POWER_MODE MOTOR_POWER_MODE
+#define M2_POWER_LEVEL 0.4
+
+#define M3_MOTOR_MAP AXIS_Z
+ // This "stepper" is a hobby servo. Note that all hobby
+ // servo settings are per full servo range, instead of
+ // per revolution.
+#define M3_STEP_ANGLE 1.8 // hobby servos are simulated with 200 "full steps"
+#define M3_TRAVEL_PER_REV 26 // this is actually the full travel of the servo, not
+ // necessarily covering a revolution
+#define M3_MICROSTEPS 32 // the max step resolution for a hobby servo is 1/32
+#define M3_POLARITY 1
+#define M3_POWER_MODE MOTOR_ALWAYS_POWERED
+#define M3_POWER_LEVEL 0.50 // this is ignored
+
+#define M4_MOTOR_MAP AXIS_A
+#define M4_STEP_ANGLE 1.8
+#define M4_TRAVEL_PER_REV 360 // degrees moved per motor rev
+#define M4_MICROSTEPS 32
+#define M4_POLARITY 0
+#define M4_POWER_MODE MOTOR_POWER_MODE
+#define M4_POWER_LEVEL 0.6
+
+// *** axis settings **********************************************************************************
+
+#define JERK_MAX 20000
+
+#define X_AXIS_MODE AXIS_STANDARD // xam see canonical_machine.h cmAxisMode for valid values
+#define X_VELOCITY_MAX 50000 // xvm G0 max velocity in mm/min
+#define X_FEEDRATE_MAX X_VELOCITY_MAX // xfr G1 max feed rate in mm/min
+#define X_TRAVEL_MIN 0 // xtn minimum travel - used by soft limits and homing
+#define X_TRAVEL_MAX 400 // xtm maximum travel - used by soft limits and homing
+#define X_JERK_MAX JERK_MAX // xjm
+#define X_JERK_HIGH_SPEED X_JERK_MAX // xjh
+#define X_HOMING_INPUT 1 // xhi input used for homing or 0 to disable
+#define X_HOMING_DIRECTION 0 // xhd 0=search moves negative, 1= search moves positive
+#define X_SEARCH_VELOCITY 1000 // xsv move in negative direction
+#define X_LATCH_VELOCITY 100 // xlv mm/min
+#define X_LATCH_BACKOFF 10 // xlb mm
+#define X_ZERO_BACKOFF 2 // xzb mm
+
+#define Y_AXIS_MODE AXIS_STANDARD
+#define Y_VELOCITY_MAX 50000
+#define Y_FEEDRATE_MAX Y_VELOCITY_MAX
+#define Y_TRAVEL_MIN 0
+#define Y_TRAVEL_MAX 175
+#define Y_JERK_MAX JERK_MAX
+#define Y_JERK_HIGH_SPEED Y_JERK_MAX
+#define Y_HOMING_INPUT 3
+#define Y_HOMING_DIRECTION 0
+#define Y_SEARCH_VELOCITY 1000
+#define Y_LATCH_VELOCITY 100
+#define Y_LATCH_BACKOFF 10
+#define Y_ZERO_BACKOFF 2
+
+#define Z_AXIS_MODE AXIS_STANDARD
+#define Z_VELOCITY_MAX 15000
+#define Z_FEEDRATE_MAX Z_VELOCITY_MAX
+#define Z_TRAVEL_MIN 0
+#define Z_TRAVEL_MAX 75
+#define Z_JERK_MAX 5000
+#define Z_JERK_HIGH_SPEED Z_JERK_MAX
+#define Z_HOMING_INPUT 6
+#define Z_HOMING_DIRECTION 1
+#define Z_SEARCH_VELOCITY 600
+#define Z_LATCH_VELOCITY 100
+#define Z_LATCH_BACKOFF 10
+#define Z_ZERO_BACKOFF 2
+
+// Rotary values are chosen to make the motor react the same as X for testing
+/***************************************************************************************
+ * To calculate the speeds here, in Wolfram Alpha-speak:
+ *
+ * c=2*pi*r, r=0.609, d=c/360, s=((S*60)/d), S=40 for s
+ * c=2*pi*r, r=5.30516476972984, d=c/360, s=((S*60)/d), S=40 for s
+ *
+ * Change r to A_RADIUS, and S to the desired speed, in mm/s or mm/s/s/s.
+ *
+ * It will return s= as the value you want to enter.
+ *
+ * If the value is over 1 million, the code will divide it by 1 million,
+ * so you have to pre-multiply it by 1000000.0. (The value is in millions, btw.)
+ *
+ * Note that you need these to be floating point values, so always have a .0 at the end!
+ *
+ ***************************************************************************************/
+
+#define A_AXIS_MODE AXIS_RADIUS
+#define A_RADIUS 5.30516476972984
+//#define A_VELOCITY_MAX 25920.0 // ~40 mm/s, 2,400 mm/min
+//#define A_FEEDRATE_MAX 25920.0/2.0 // ~20 mm/s, 1,200 mm/min
+#define A_VELOCITY_MAX 77760.0 // G0 rate ~120 mm/s, 2,400 mm/min
+#define A_FEEDRATE_MAX 9720.0 // 9720.0 = G1 rate ~15 mm/s, 900 mm/min
+#define A_TRAVEL_MIN 0
+#define A_TRAVEL_MAX 10
+#define A_JERK_MAX 648000 // 1,000 million mm/min^3 = 648000
+ // * a million IF it's over a million
+ // c=2*pi*r, r=5.30516476972984, d=c/360, s=((1000*60)/d)
+#define A_HOMING_INPUT 0
+#define A_HOMING_DIRECTION 0
+#define A_SEARCH_VELOCITY 2000
+#define A_LATCH_VELOCITY 2000
+#define A_LATCH_BACKOFF 5
+#define A_ZERO_BACKOFF 2
+#define A_JERK_HIGH_SPEED A_JERK_MAX
+
+#define B_AXIS_MODE AXIS_DISABLED
+#define B_RADIUS 1
+#define B_VELOCITY_MAX 3600
+#define B_FEEDRATE_MAX B_VELOCITY_MAX
+#define B_TRAVEL_MIN 0
+#define B_TRAVEL_MAX -1
+//#define B_JERK_MAX 20000000
+#define B_JERK_MAX 20
+#define B_HOMING_INPUT 0
+#define B_HOMING_DIRECTION 0
+#define B_SEARCH_VELOCITY 600
+#define B_LATCH_VELOCITY 100
+#define B_LATCH_BACKOFF 10
+#define B_ZERO_BACKOFF 2
+#define B_JERK_HIGH_SPEED A_JERK_MAX
+
+
+//*** Input / output settings ***
+/*
+ IO_MODE_DISABLED
+ IO_ACTIVE_LOW aka NORMALLY_OPEN
+ IO_ACTIVE_HIGH aka NORMALLY_CLOSED
+
+ INPUT_ACTION_NONE
+ INPUT_ACTION_STOP
+ INPUT_ACTION_FAST_STOP
+ INPUT_ACTION_HALT
+ INPUT_ACTION_RESET
+
+ INPUT_FUNCTION_NONE
+ INPUT_FUNCTION_LIMIT
+ INPUT_FUNCTION_INTERLOCK
+ INPUT_FUNCTION_SHUTDOWN
+ INPUT_FUNCTION_PANIC
+*/
+// Inputs are defined for the g2ref(a) board
+// Xmn (board label)
+#define DI1_MODE IO_ACTIVE_HIGH
+#define DI1_ACTION INPUT_ACTION_NONE
+#define DI1_FUNCTION INPUT_FUNCTION_NONE
+
+// Xmax
+#define DI2_MODE IO_MODE_DISABLED
+#define DI2_ACTION INPUT_ACTION_NONE
+#define DI2_FUNCTION INPUT_FUNCTION_NONE
+
+// Ymin
+#define DI3_MODE IO_MODE_DISABLED
+#define DI3_ACTION INPUT_ACTION_NONE
+#define DI3_FUNCTION INPUT_FUNCTION_NONE
+
+// Ymax
+#define DI4_MODE IO_ACTIVE_HIGH
+#define DI4_ACTION INPUT_ACTION_NONE
+#define DI4_FUNCTION INPUT_FUNCTION_NONE
+
+// Zmin
+#define DI5_MODE IO_ACTIVE_LOW // Z probe
+#define DI5_ACTION INPUT_ACTION_NONE
+#define DI5_FUNCTION INPUT_FUNCTION_NONE
+
+// Zmax
+#define DI6_MODE IO_MODE_DISABLED
+#define DI6_ACTION INPUT_ACTION_STOP
+#define DI6_FUNCTION INPUT_FUNCTION_NONE
+
+// Shutdown (Amin on v9 board)
+#define DI7_MODE IO_MODE_DISABLED
+#define DI7_ACTION INPUT_ACTION_NONE
+#define DI7_FUNCTION INPUT_FUNCTION_NONE
+
+// High Voltage Z Probe In (Amax on v9 board)
+#define DI8_MODE IO_ACTIVE_LOW
+#define DI8_ACTION INPUT_ACTION_NONE
+#define DI8_FUNCTION INPUT_FUNCTION_NONE
+
+// Hardware interlock input
+#define DI9_MODE IO_MODE_DISABLED
+#define DI9_ACTION INPUT_ACTION_NONE
+#define DI9_FUNCTION INPUT_FUNCTION_NONE
+
+//Extruder1_PWM
+#define DO1_MODE IO_ACTIVE_HIGH
+
+//Extruder2_PWM
+#define DO2_MODE IO_ACTIVE_HIGH
+
+//Fan1A_PWM
+#define DO3_MODE IO_ACTIVE_HIGH
+
+//Fan1B_PWM
+#define DO4_MODE IO_ACTIVE_HIGH
+
+#define DO5_MODE IO_ACTIVE_HIGH
+#define DO6_MODE IO_ACTIVE_HIGH
+#define DO7_MODE IO_ACTIVE_HIGH
+#define DO8_MODE IO_ACTIVE_HIGH
+
+//SAFEin (Output) signal
+#define DO9_MODE IO_ACTIVE_HIGH
+
+#define DO10_MODE IO_ACTIVE_HIGH
+
+//Header Bed FET
+#define DO11_MODE IO_ACTIVE_HIGH
+
+//Indicator_LED
+#define DO12_MODE IO_ACTIVE_HIGH
+
+#define DO13_MODE IO_ACTIVE_HIGH
+
+
+/*** Extruders / Heaters ***/
+/*
+#define MIN_FAN_TEMP 40.0
+#define MAX_FAN_TEMP 150.0
+
+#define H1_DEFAULT_ENABLE true
+#define H1_DEFAULT_P 9.0
+#define H1_DEFAULT_I 0.12
+#define H1_DEFAULT_D 400.0
+
+#define H2_DEFAULT_ENABLE false
+#define H2_DEFAULT_P 9.0
+#define H2_DEFAULT_I 0.12
+#define H2_DEFAULT_D 400.0
+
+#define H3_DEFAULT_ENABLE false
+#define H3_DEFAULT_P 9.0
+#define H3_DEFAULT_I 0.12
+#define H3_DEFAULT_D 400.0
+*/
+
+
+// *** PWM SPINDLE CONTROL ***
+
+#define P1_PWM_FREQUENCY 100 // in Hz
+#define P1_CW_SPEED_LO 1000 // in RPM (arbitrary units)
+#define P1_CW_SPEED_HI 2000
+#define P1_CW_PHASE_LO 0.125 // phase [0..1]
+#define P1_CW_PHASE_HI 0.2
+#define P1_CCW_SPEED_LO 1000
+#define P1_CCW_SPEED_HI 2000
+#define P1_CCW_PHASE_LO 0.125
+#define P1_CCW_PHASE_HI 0.2
+#define P1_PWM_PHASE_OFF 0.1
+
+// *** DEFAULT COORDINATE SYSTEM OFFSETS ***
+
+#define G54_X_OFFSET 0 // G54 is traditionally set to all zeros
+#define G54_Y_OFFSET 0
+#define G54_Z_OFFSET 0
+#define G54_A_OFFSET 0
+#define G54_B_OFFSET 0
+#define G54_C_OFFSET 0
+
+#define G55_X_OFFSET (X_TRAVEL_MAX/2) // set to middle of table
+#define G55_Y_OFFSET (Y_TRAVEL_MAX/2)
+#define G55_Z_OFFSET 0
+#define G55_A_OFFSET 0
+#define G55_B_OFFSET 0
+#define G55_C_OFFSET 0
+
+#define G56_X_OFFSET 0
+#define G56_Y_OFFSET 0
+#define G56_Z_OFFSET 0
+#define G56_A_OFFSET 0
+#define G56_B_OFFSET 0
+#define G56_C_OFFSET 0
+
+#define G57_X_OFFSET 0
+#define G57_Y_OFFSET 0
+#define G57_Z_OFFSET 0
+#define G57_A_OFFSET 0
+#define G57_B_OFFSET 0
+#define G57_C_OFFSET 0
+
+#define G58_X_OFFSET 0
+#define G58_Y_OFFSET 0
+#define G58_Z_OFFSET 0
+#define G58_A_OFFSET 0
+#define G58_B_OFFSET 0
+#define G58_C_OFFSET 0
+
+#define G59_X_OFFSET 0
+#define G59_Y_OFFSET 0
+#define G59_Z_OFFSET 0
+#define G59_A_OFFSET 0
+#define G59_B_OFFSET 0
+#define G59_C_OFFSET 0
diff --git a/g2core/stepper.cpp b/g2core/stepper.cpp
index 0652b099..f013059a 100644
--- a/g2core/stepper.cpp
+++ b/g2core/stepper.cpp
@@ -40,6 +40,20 @@
#include "controller.h"
#include "xio.h"
+/**** Debugging output with semihosting ****/
+
+#include "MotateDebug.h"
+
+// Unless debugging, this should always read "#if 0 && ..."
+// DON'T COMMIT with anything else!
+#if 0 && (IN_DEBUGGER == 1)
+template
+void stepper_debug(const char (&str)[len]) { Motate::debug.write(str); };
+#else
+template
+void stepper_debug(const char (&str)[len]) { ; };
+#endif
+
/**** Allocate structures ****/
stConfig_t st_cfg;
@@ -62,11 +76,19 @@ extern OutputPin debug_pin3;
//extern OutputPin debug_pin4;
dda_timer_type dda_timer {kTimerUpToMatch, FREQUENCY_DDA}; // stepper pulse generation
-dwell_timer_type dwell_timer {kTimerUpToMatch, FREQUENCY_DWELL}; // dwell timer
load_timer_type load_timer; // triggers load of next stepper segment
exec_timer_type exec_timer; // triggers calculation of next+1 stepper segment
fwd_plan_timer_type fwd_plan_timer; // triggers planning of next block
+// SystickEvent for handling dweels (must be registered before it is active)
+Motate::SysTickEvent dwell_systick_event {[&] {
+ if (--st_run.dwell_ticks_downcount == 0) {
+ SysTickTimer.unregisterEvent(&dwell_systick_event);
+ _load_move(); // load the next move at the current interrupt level
+ }
+}, nullptr};
+
+
/************************************************************************************
**** CODE **************************************************************************
************************************************************************************/
@@ -103,18 +125,15 @@ void stepper_init()
// If you need more pulse width you need to drop the DDA clock rate
dda_timer.setInterrupts(kInterruptOnOverflow | kInterruptPriorityHighest);
- // setup DWELL timer
- dwell_timer.setInterrupts(kInterruptOnOverflow | kInterruptPriorityHighest);
-
// setup software interrupt load timer
- load_timer.setInterrupts(kInterruptOnSoftwareTrigger | kInterruptPriorityMedium);
+ load_timer.setInterrupts(kInterruptOnSoftwareTrigger | kInterruptPriorityHigh);
// setup software interrupt exec timer & initial condition
- exec_timer.setInterrupts(kInterruptOnSoftwareTrigger | kInterruptPriorityLow);
+ exec_timer.setInterrupts(kInterruptOnSoftwareTrigger | kInterruptPriorityMedium);
st_pre.buffer_state = PREP_BUFFER_OWNED_BY_EXEC;
// setup software interrupt forward plan timer & initial condition
- fwd_plan_timer.setInterrupts(kInterruptOnSoftwareTrigger | kInterruptPriorityLowest);
+ fwd_plan_timer.setInterrupts(kInterruptOnSoftwareTrigger | kInterruptPriorityLow);
// setup motor power levels and apply power level to stepper drivers
for (uint8_t motor=0; motor
void dda_timer_type::interrupt()
{
- dda_timer.getInterruptCause(); // clear interrupt condition
+ dda_timer.getInterruptCause(); // clear interrupt condition
// clear all steps from the previous interrupt
// for (uint8_t motor=0; motor 0) {
// Motors[motor]->stepStart(); // turn step bit on
@@ -279,67 +298,52 @@ void dda_timer_type::interrupt()
// process DDAs for each motor
if ((st_run.mot[MOTOR_1].substep_accumulator += st_run.mot[MOTOR_1].substep_increment) > 0) {
- motor_1.stepStart(); // turn step bit on
+ motor_1.stepStart(); // turn step bit on
st_run.mot[MOTOR_1].substep_accumulator -= st_run.dda_ticks_X_substeps;
INCREMENT_ENCODER(MOTOR_1);
}
if ((st_run.mot[MOTOR_2].substep_accumulator += st_run.mot[MOTOR_2].substep_increment) > 0) {
- motor_2.stepStart(); // turn step bit on
+ motor_2.stepStart(); // turn step bit on
st_run.mot[MOTOR_2].substep_accumulator -= st_run.dda_ticks_X_substeps;
INCREMENT_ENCODER(MOTOR_2);
}
#if MOTORS > 2
if ((st_run.mot[MOTOR_3].substep_accumulator += st_run.mot[MOTOR_3].substep_increment) > 0) {
- motor_3.stepStart(); // turn step bit on
+ motor_3.stepStart(); // turn step bit on
st_run.mot[MOTOR_3].substep_accumulator -= st_run.dda_ticks_X_substeps;
INCREMENT_ENCODER(MOTOR_3);
}
#endif
#if MOTORS > 3
if ((st_run.mot[MOTOR_4].substep_accumulator += st_run.mot[MOTOR_4].substep_increment) > 0) {
- motor_4.stepStart(); // turn step bit on
+ motor_4.stepStart(); // turn step bit on
st_run.mot[MOTOR_4].substep_accumulator -= st_run.dda_ticks_X_substeps;
INCREMENT_ENCODER(MOTOR_4);
}
#endif
#if MOTORS > 4
if ((st_run.mot[MOTOR_5].substep_accumulator += st_run.mot[MOTOR_5].substep_increment) > 0) {
- motor_5.stepStart(); // turn step bit on
+ motor_5.stepStart(); // turn step bit on
st_run.mot[MOTOR_5].substep_accumulator -= st_run.dda_ticks_X_substeps;
INCREMENT_ENCODER(MOTOR_5);
}
#endif
#if MOTORS > 5
if ((st_run.mot[MOTOR_6].substep_accumulator += st_run.mot[MOTOR_6].substep_increment) > 0) {
- motor_6.stepStart(); // turn step bit on
+ motor_6.stepStart(); // turn step bit on
st_run.mot[MOTOR_6].substep_accumulator -= st_run.dda_ticks_X_substeps;
INCREMENT_ENCODER(MOTOR_6);
}
#endif
- // process end of segment
+ // Process end of segment.
+ // One more interrupt will occur to turn of any pulses set in this pass.
if (--st_run.dda_ticks_downcount == 0) {
- _load_move(); // load the next move at the current interrupt level
+ _load_move(); // load the next move at the current interrupt level
}
} // MOTATE_TIMER_INTERRUPT
} // namespace Motate
-/***** Dwell Interrupt Service Routine **************************************************
- * ISR - DDA timer interrupt routine - service ticks from DDA timer
- */
-
-namespace Motate { // Must define timer interrupts inside the Motate namespace
- template<>
- void dwell_timer_type::interrupt()
-{
- dwell_timer.getInterruptCause(); // read SR to clear interrupt condition
- if (--st_run.dda_ticks_downcount == 0) {
- dwell_timer.stop();
- _load_move();
- }
-}
-} // namespace Motate
-
/****************************************************************************************
* Exec sequencing code - computes and prepares next load segment
* st_request_exec_move() - SW interrupt to request to execute a move
@@ -348,9 +352,12 @@ namespace Motate { // Must define timer interrupts inside the Motate
void st_request_exec_move()
{
+ stepper_debug("e");
if (st_pre.buffer_state == PREP_BUFFER_OWNED_BY_EXEC) { // bother interrupting
exec_timer.setInterruptPending();
+ return;
}
+ stepper_debug("!\n");
}
namespace Motate { // Define timer inside Motate namespace
@@ -359,16 +366,21 @@ namespace Motate { // Define timer inside Motate namespace
{
exec_timer.getInterruptCause(); // clears the interrupt condition
if (st_pre.buffer_state == PREP_BUFFER_OWNED_BY_EXEC) {
+ stepper_debug("E>");
if (mp_exec_move() != STAT_NOOP) {
+ stepper_debug("E+\n");
st_pre.buffer_state = PREP_BUFFER_OWNED_BY_LOADER; // flip it back
st_request_load_move();
+ return;
}
+ stepper_debug("E-\n");
}
}
} // namespace Motate
-void st_request_plan_move()
+void st_request_forward_plan()
{
+ stepper_debug("p");
fwd_plan_timer.setInterruptPending();
}
@@ -376,10 +388,14 @@ namespace Motate { // Define timer inside Motate namespace
template<>
void fwd_plan_timer_type::interrupt()
{
- fwd_plan_timer.getInterruptCause(); // clears the interrupt condition
- if (mp_plan_move() != STAT_NOOP) { // We now have a move to exec.
+ fwd_plan_timer.getInterruptCause(); // clears the interrupt condition
+ stepper_debug("P>");
+ if (mp_forward_plan() != STAT_NOOP) { // We now have a move to exec.
+ stepper_debug("P+\n");
st_request_exec_move();
+ return;
}
+ stepper_debug("P-\n");
}
} // namespace Motate
@@ -398,7 +414,9 @@ void st_request_load_move()
if (st_runtime_isbusy()) { // don't request a load if the runtime is busy
return;
}
+ stepper_debug("l");
if (st_pre.buffer_state == PREP_BUFFER_OWNED_BY_LOADER) { // bother interrupting
+ stepper_debug("_");
load_timer.setInterruptPending();
}
}
@@ -408,7 +426,9 @@ namespace Motate { // Define timer inside Motate namespace
void load_timer_type::interrupt()
{
load_timer.getInterruptCause(); // read SR to clear interrupt condition
+ stepper_debug("L>");
_load_move();
+ stepper_debug("L+\n");
}
} // namespace Motate
@@ -454,8 +474,11 @@ static void _load_move()
#if (MOTORS > 5)
motor_6.motionStopped();
#endif
+ stepper_debug("•");
return;
- }
+ } // if (st_pre.buffer_state != PREP_BUFFER_OWNED_BY_LOADER)
+
+ stepper_debug("^");
// handle aline loads first (most common case) NB: there are no more lines, only alines
if (st_pre.block_type == BLOCK_TYPE_ALINE) {
@@ -500,7 +523,7 @@ static void _load_move()
} else { // Motor has 0 steps; might need to energize motor for power mode processing
motor_1.motionStopped();
- }
+ }
// accumulate counted steps to the step position and zero out counted steps for the segment currently being loaded
ACCUMULATE_ENCODER(MOTOR_1);
@@ -597,26 +620,25 @@ static void _load_move()
//**** do this last ****
- dda_timer.start(); // start the DDA timer if not already running
+ dda_timer.start(); // start the DDA timer if not already running
- // handle dwells
+ // handle dwells and commands
} else if (st_pre.block_type == BLOCK_TYPE_DWELL) {
- st_run.dda_ticks_downcount = st_pre.dda_ticks;
- dwell_timer.start();
+ st_run.dwell_ticks_downcount = st_pre.dwell_ticks;
- // handle synchronous commands
+ // We now use SysTick events to handle dwells
+ SysTickTimer.registerEvent(&dwell_systick_event);
+
+ // handle synchronous commands
} else if (st_pre.block_type == BLOCK_TYPE_COMMAND) {
mp_runtime_command(st_pre.bf);
-
- } // else null - WARNING - We cannot printf from here!! Causes crashes.
- else {
- _debug_trap("_load_move called when it's not needed?");
- }
+
+ } // else null - which is okay in many cases
// all other cases drop to here (e.g. Null moves after Mcodes skip to here)
st_pre.block_type = BLOCK_TYPE_NULL;
st_pre.buffer_state = PREP_BUFFER_OWNED_BY_EXEC; // we are done with the prep buffer - flip the flag back
- st_request_exec_move(); // exec and prep next move
+ st_request_exec_move(); // exec and prep next move
}
/***********************************************************************************
@@ -645,6 +667,7 @@ static void _load_move()
stat_t st_prep_line(float travel_steps[], float following_error[], float segment_time)
{
+ stepper_debug("😶");
// trap assertion failures and other conditions that would prevent queuing the line
if (st_pre.buffer_state != PREP_BUFFER_OWNED_BY_EXEC) { // never supposed to happen
return (cm_panic(STAT_INTERNAL_ERROR, "st_prep_line() prep sync error"));
@@ -723,6 +746,7 @@ stat_t st_prep_line(float travel_steps[], float following_error[], float segment
}
st_pre.block_type = BLOCK_TYPE_ALINE;
st_pre.buffer_state = PREP_BUFFER_OWNED_BY_LOADER; // signal that prep buffer is ready
+ stepper_debug("👍🏻");
return (STAT_OK);
}
@@ -754,8 +778,8 @@ void st_prep_command(void *bf)
void st_prep_dwell(float microseconds)
{
st_pre.block_type = BLOCK_TYPE_DWELL;
- //st_pre.dda_period = _f_to_period(FREQUENCY_DWELL);
- st_pre.dda_ticks = (uint32_t)((microseconds/1000000) * FREQUENCY_DWELL);
+ // we need dwell_ticks to be at least 1
+ st_pre.dwell_ticks = std::max((uint32_t)((microseconds/1000000) * FREQUENCY_DWELL), 1UL);
st_pre.buffer_state = PREP_BUFFER_OWNED_BY_LOADER; // signal that prep buffer is ready
}
diff --git a/g2core/stepper.h b/g2core/stepper.h
index 1c185796..eb585f59 100644
--- a/g2core/stepper.h
+++ b/g2core/stepper.h
@@ -26,13 +26,15 @@
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
+ * --- Stepper Operation Overview ---
+ *
* Coordinated motion (line drawing) is performed using a classic Bresenham DDA.
- * A number of additional steps are taken to optimize interpolation and pulse train
- * timing accuracy to minimize pulse jitter and make for very smooth motion and surface
+ * Additional steps are taken to optimize interpolation and pulse train timing
+ * accuracy to minimize pulse jitter and produce very smooth motion and surface
* finish.
*
* - The DDA is not used as a ramp for acceleration management. Acceleration is computed
- * upstream in the motion planner as 5th order (linear snap) equations. These
+ * upstream in the motion planner as 6th order (linear pop) equations. These
* generate accel/decel *segments* that are passed to the DDA for step output.
*
* - The DDA accepts and processes fractional motor steps as floating point numbers
@@ -379,7 +381,8 @@ typedef struct stRunMotor { // one per controlled motor
typedef struct stRunSingleton { // Stepper static values and axis parameters
magic_t magic_start; // magic number to test memory integrity
- uint32_t dda_ticks_downcount; // tick down-counter (unscaled)
+ uint32_t dda_ticks_downcount; // dda tick down-counter (unscaled)
+ uint32_t dwell_ticks_downcount; // dwell tick down-counter (unscaled)
uint32_t dda_ticks_X_substeps; // ticks multiplied by scaling factor
stRunMotor_t mot[MOTORS]; // runtime motor structures
magic_t magic_end;
@@ -413,8 +416,8 @@ typedef struct stPrepSingleton {
struct mpBuffer *bf; // static pointer to relevant buffer
blockType block_type; // move type (requires planner.h)
- //uint16_t dda_period; // DDA or dwell clock period setting (No longer used)
- uint32_t dda_ticks; // DDA or dwell ticks for the move
+ uint32_t dda_ticks; // DDA ticks for the move
+ uint32_t dwell_ticks; // dwell ticks remaining
uint32_t dda_ticks_X_substeps; // DDA ticks scaled by substep factor
stPrepMotor_t mot[MOTORS]; // prep time motor structs
magic_t magic_end;
@@ -566,7 +569,7 @@ stat_t st_clc(nvObj_t *nv);
void st_set_motor_power(const uint8_t motor);
stat_t st_motor_power_callback(void);
-void st_request_plan_move(void);
+void st_request_forward_plan(void);
void st_request_exec_move(void);
void st_request_load_move(void);
void st_prep_null(void);
diff --git a/g2core/xio.cpp b/g2core/xio.cpp
index 1b94aea7..cc9dc874 100755
--- a/g2core/xio.cpp
+++ b/g2core/xio.cpp
@@ -38,6 +38,7 @@
#include "report.h"
#include "controller.h"
#include "util.h"
+#include "settings.h"
#include "board_xio.h"
@@ -81,7 +82,7 @@ using Motate::TXBuffer;
/**** Structures ****/
-// We need a buffer to hold single character commands, like !~%
+// We need a buffer to hold single character commands, like !~%, ^x, etc.
// We also want it to have a NULL character, so we make it two characters.
char single_char_buffer[2] = " ";
@@ -100,11 +101,11 @@ struct xioDeviceWrapperBase { // C++ base class for device primit
// connection and device management
uint8_t caps; // bitfield for capabilities flags (these are persistent)
devflags_t flags; // bitfield for device state flags (these are not)
- devflags_t next_flags; // bitfield for next-state transitions
+ devflags_t next_flags; // bitfield for next-state transitions
// line reader functions
// uint16_t read_index; // index into line being read
-// const uint16_t read_buf_size; // static variable set at init time
+// const uint16_t read_buf_size; // static variable set at init time
// char read_buf[USB_LINE_BUFFER_SIZE]; // buffer for reading lines
// Internal use only:
@@ -223,16 +224,16 @@ struct xio_t {
/*
* write() - write a block to a device
+ *
+ * There are a few issues with this function that I don't know how to resolve right now:
+ * 1) If a device fails to write the data, or all the data, then it's ignored
+ * 2) Only the amount written by the *last* device to match (CTRL|ACTIVE) is returned.
+ *
+ * In the current environment, these are not foreseen to cause trouble since these
+ * are blocking writes and we expect to only really be writing to one device.
*/
size_t write(const char *buffer, size_t size)
{
- // There are a few issues with this function that I don't know how to resolve right now:
- // 1) If a device fails to write the data, or all the data, then it's ignored
- // 2) Only the amount written by the *last* device to match (CTRL|ACTIVE) is returned.
- //
- // In the current environment, these are not foreseen to cause trouble,
- // since these are blocking writes and we expect to only really be writing to one device.
-
size_t total_written = -1;
for (int8_t i = 0; i < _dev_count; ++i) {
if (DeviceWrappers[i]->isCtrlAndActive()) {
@@ -297,10 +298,10 @@ struct xio_t {
* DEV_IS_DATA bits in the device flag field. 'Flags' is loaded with the flags of
* the channel that was read on return, or 0 (DEV_FLAGS_CLEAR) if no line was returned.
*
- * size - Returns the size of the completed buffer, including the NUL termination character.
- * Lines may be returned truncated to the length of the serial input buffer if the text
- * from the physical device is longer than the read buffer for the device. The size value
- * provided as a calling argument is ignored (size doesn't matter).
+ * size - Returns the size of the completed buffer, including the NUL termination character.
+ * Lines may be returned truncated to the length of the serial input buffer if the text
+ * from the physical device is longer than the read buffer for the device. The size value
+ * provided as a calling argument is ignored (size doesn't matter).
*
* char * Returns a pointer to the buffer containing the line, or NULL (*0) if no text
*/
@@ -352,7 +353,7 @@ struct xio_t {
uint16_t magic_end;
};
-// Declare (but don't define) the xio singelton object now, define it later
+// Declare (but don't define) the xio singleton object now, define it later
// Why? xioDeviceWrapper uses it, but we need to define it to contain xioDeviceWrapper objects.
extern xio_t xio;
@@ -362,42 +363,12 @@ extern xio_t xio;
// See here for a discussion of what this means if you are not familiar with C++
// https://github.com/synthetos/g2/wiki/Dual-Endpoint-USB-Internals#c-classes-virtual-functions-and-inheritance
-
// LineRXBuffer takes the Motate RXBuffer (which handles "transfers", usually DMA), and adds G2 line-reading
// semantics to it.
template
-struct LineRXBuffer : RXBuffer<_size, owner_type, char> { // reserve size of 128bytes
+struct LineRXBuffer : RXBuffer<_size, owner_type, char> {
typedef RXBuffer<_size, owner_type, char> parent_type;
- // SUPPORTING STRUCTURES
- enum _LinesHeaderStatus_t {
- HEADER_FREE,
- HEADER_PREPPED,
- HEADER_FILLING,
- HEADER_FULL
- };
-
- struct _LinesHeader {
- _LinesHeaderStatus_t _status;
- bool _is_control;
- bool _is_processing;
-
- int16_t _line_count; // number of lines in this group of lines
- int16_t _read_offset; // start of the next line to read (first char past PROCESSING line)
-
- uint16_t _get_next_read_offset() { return ((_read_offset + 1) & (_size-1)); };
-
-
- // Convenience function for reset
- void reset() {
- _status = HEADER_FREE;
- _is_control = false;
- _line_count = 0;
- _read_offset = 0;
- };
- };
-
-
// Let's help the compiler clarify what we mean by a few things:
// (This is because of the templating, it needs a little extra clarification.)
using parent_type::_data;
@@ -412,80 +383,95 @@ struct LineRXBuffer : RXBuffer<_size, owner_type, char> { // reserve size of 128
// START OF LineRXBuffer PROPER
static_assert(((_header_count-1)&_header_count)==0, "_header_count must be 2^N");
- char _line_buffer[_line_buffer_size]; // hold excatly one line to return
+ char _line_buffer[_line_buffer_size]; // hold exactly one line to return
uint32_t _line_end_guard = 0xBEEF;
// General term usage:
// * "index" indicates it's in to _headers array
// * "offset" means it's a character in the _data array
-
- _LinesHeader _headers[_header_count]; // and array that we'll use as a ring buffer of _LinesHeaders
- uint8_t _first_header_index; // index into the array of the (current) first item of _headers
- uint8_t _write_header_index; // index into the array of the item of _headers we are writing too (unless it's FULL)
-
uint16_t _scan_offset; // offset into data of the last character scanned
uint16_t _line_start_offset; // offset into first character of the line
bool _at_start_of_line; // true if the last character scanned was the end of a line
+ uint16_t _lines_found; // count of complete non-control lines that were found during scanning.
+
volatile uint16_t _last_scan_offset; // DEBUGGING
LineRXBuffer(owner_type owner) : parent_type{owner} {};
- void init() { parent_type::init(); };
-
- uint8_t _get_next_write_header_index() { return ((_write_header_index + 1) & (_header_count-1)); };
- uint8_t _get_next_first_header_index() { return ((_first_header_index + 1) & (_header_count-1)); };
- uint8_t _get_next_header_index(uint8_t indx) { return ((indx + 1) & (_header_count-1)); };
-
- void _free_unused_space() {
- // Headers that were processing OR are now completely empty are now safe to clear,m
- // but only in order.
- auto _first_header = &_headers[_first_header_index];
- while ((_first_header->_is_processing) || ((_first_header->_status == HEADER_FULL) && (_first_header->_line_count == 0))) {
- _read_offset = _first_header->_read_offset;
- _first_header->_is_processing = false;
-
- if ((_first_header->_status == HEADER_FULL) && (_first_header->_line_count == 0)) {
- _first_header->reset();
- _first_header_index = _get_next_first_header_index();
-
- // Point _first_header to the new first header
- _first_header = &_headers[_first_header_index];
- } else {
- // We can only clear into the next one if we completely clear this one
- break;
- }
- }
-
- if ((_write_header_index == _first_header_index) && (_first_header->_status == HEADER_FREE)) {
- // PREP it
- _first_header->_status = HEADER_PREPPED;
-
- _at_start_of_line = true;
- _first_header->_read_offset = _line_start_offset;
-
- _restartTransfer();
- }
+ void init() {
+ parent_type::init();
+ _at_start_of_line = true;
};
- bool _check_write_header() {
- // _status cannot be PROCESSING, since we already cleared those in _free_unused_space()
- if (_headers[_write_header_index]._status == HEADER_FULL) {
- uint8_t _next_write_header_index = _get_next_write_header_index();
- if (_next_write_header_index == _first_header_index) { // we're full full
- return false;
- }
- _write_header_index = _next_write_header_index;
- auto _write_header = &_headers[_write_header_index];
- _write_header->_status = HEADER_PREPPED;
- _at_start_of_line = true;
- _write_header->_read_offset = _line_start_offset;
- }
- return true;
+ struct SkipSections {
+ struct SkipSection {
+ uint16_t start_offset; // the offset of the first character to skip
+ uint16_t end_offset; // the offset of the next character to read after skipping
+ };
+
+ static constexpr uint16_t _section_count = 16;
+ SkipSection _sections[_section_count];
+
+ uint8_t read_section_idx; // index of the first skip section to skip
+ uint8_t write_section_idx; // index of the next skip section to populate
+
+ bool is_full() {
+ return ((write_section_idx+1)&(_section_count-1)) == read_section_idx;
+ };
+ bool is_empty() {
+ return (write_section_idx == read_section_idx);
+ };
+
+ void add_skip(uint16_t start_offset, uint16_t end_offset) {
+ if (!is_empty()) {
+ uint8_t last_write_section_idx = write_section_idx;
+ if (write_section_idx == 0) {
+ last_write_section_idx = _section_count-1;
+ } else {
+ last_write_section_idx--;
+ }
+
+ if (_sections[last_write_section_idx].end_offset == start_offset) {
+ _sections[last_write_section_idx].end_offset = end_offset;
+ return;
+ }
+ }
+ _sections[write_section_idx].start_offset = start_offset;
+ _sections[write_section_idx].end_offset = end_offset;
+ write_section_idx = ((write_section_idx+1)&(_section_count-1));
+ };
+
+ void pop_skip() {
+ _sections[read_section_idx].start_offset = 0;
+ _sections[read_section_idx].end_offset = 0;
+
+ read_section_idx = ((read_section_idx+1)&(_section_count-1));
+ };
+
+ bool skip(volatile uint16_t &from) {
+ if (!is_empty()) {
+ SkipSection &next_skip = _sections[read_section_idx];
+
+ if (next_skip.start_offset == from) {
+ from = next_skip.end_offset;
+
+ pop_skip();
+ return true;
+ }
+ }
+ return false;
+ };
+
+// const SkipSection& next_skip() {
+// return _sections[read_section_idx];
+// }
};
+ SkipSections _skip_sections;
+
uint16_t _get_next_scan_offset() {
return ((_scan_offset + 1) & (_size-1));
}
@@ -494,103 +480,101 @@ struct LineRXBuffer : RXBuffer<_size, owner_type, char> { // reserve size of 128
return _canBeRead(_scan_offset);
};
-// // This function skips the "whitespace" at the BEGINNING of a line.
-// // This assumes we've already located the end of a line.
-// void _scan_past_line_start() {
-// while (_is_more_to_scan()) {
-// char c = _data[_scan_offset];
-// if (c == '\r' ||
-// c == '\n' ||
-// c == '\t' ||
-// c == ' ') {
-// _scan_offset = _get_next_scan_offset();
-// } else {
-// break;
-// }
-// } // end _skipping whitespace
-// };
+ /*
+ * _scan_buffer()
+ *
+ * Make a pass through the RX DMA buffer to locate any control lines, and count lines.
+ * Single character controls, like !, ~, %, and ^x are also considered control "lines"
+ *
+ * _scan_buffer() is called at the beginning of readline, and is effectively the first
+ * "phase" of readline.
+ *
+ * This function is designed to be able to exit from almost any point, and
+ * come back in and resume where it left off. This allows it to scan to the
+ * end of the buffer then exit. When the function is called next it picks up
+ * where it left off - i.e. avoiding rescanning the entire buffer multiple times.
+ *
+ * _scan_buffer() returns true if it finds a control line.
+ * The control line starts at the character at _line_start_offset and includes
+ * the characters up to _scan_offset-1. If there are multiple line-ending chars
+ * ("\r\n" for example) _scan_offset will point to the *first* one.
+ *
+ * With ASCII art (where "." means "invalid data" or "don't care"):
+ *
+ * Example 1 of _scan_buffer() == true:
+ * _data = "G0X10\n{jvm:5}\n{xvm:1200}\nG0Y10\nG1Z......"
+ * ^ ^
+ * | |
+ * _line_start_offset |
+ * _scan_offset
+ *
+ * Example 2 of _scan_buffer() == true:
+ * _data = "G0X10\n.........{xvm:1200}\nG0Y10\nG1Z......"
+ * ^ ^
+ * | |
+ * _line_start_offset |
+ * _scan_offset
+ *
+ * Example 2 of _scan_buffer() == true:
+ * _data = "G0X10\n!......"
+ * ^^
+ * ||
+ * _line_start_offset|
+ * _scan_offset
+ *
+ * For _scan_buffer() == false, IGNORE _line_start_offset and _scan_offset!!!
+ * Only use _read_offset, and use _lines_found>0 to determine if _data contains a line to return.
+ * Also note that _read_offset needs to be moved once the data is copied to _line_buffer!
+ */
+ /* Explanation of cases and how we handle it.
+ *
+ * Our first task in this loop is two-fold (done at the same time):
+ * A) Scan the RX DMA buffer for the next complete line, then classify the line.
+ * B) Scan for a single-character command (!~% ^D, etc), then classify as a control line.
+ *
+ * If we find a line that classifies as "control" then we return true and stop scanning.
+ *
+ * We also have a constraint that we may run out of characters at any time. This is OK,
+ * and enough state is kept that we can enter the function at any point with new
+ * characters added to the RX DMA buffer and get the same results.
+ *
+ * Another constraint is that lines MAY have single character commands embedded in them.
+ * In this case we need to un-embed them. Since we may not have the end of the line yet,
+ * we need to move the command to the beginning of the line.
+ *
+ * Note that _at_start_of_line means that we *just* parsed a character that is *at* the end of the line.
+ * So, for a \r\n sequence, _at_start_of_line will go true of the \r, and we'll see the \n and it'll stay
+ * true, then the first non \r or \n char will set it to false, and *then* start the next line.
+ */
-
- // Make a pass through the buffer to create headers for what has been read.
-
- // This function is designed to be able to exit from almost any point, and
- // come back in and resume where it left off. This allows it to scan to the
- // end of the buffer, then exit.
- void _scan_buffer() {
- _free_unused_space();
-
- if (!_check_write_header()) { return; }
-
- /* Explanation of cases and how we handle it.
- *
- * Our first task in this loop is two fold:
- * A) Scan for the next complete line, then classify the line.
- * B) Scan for a single-character command (!~% ^D, etc), then classify that as a line.
- *
- * Out next task is to then to manage the headers with this new information. We either:
- * 1) Add the line to the header, if the write header is FILLING and has the same classification.
- * 2) Add the line to the header, and classify it, if the write header is PENDING.
- * 3) Mark the current write header as FULL, and then (2) on the next one.
- *
- * We also have a constraint that we may run out of character at any time. This is okay, and enough
- * state is kept that we can enter the function at any point with new characters and get the same results.
- *
- * Another constraint is that lines MAY have single character commands embedded in them. In this case,
- * we need to un-embed them. Since we may not have the end of the line yet, we need to move the command
- * to the beginning of the line.
- *
- * Note that _at_start_of_line means that we *just* parsed a character that is *at* the end of the line.
- * So, for a \r\n sequence, _at_start_of_line will go true of the \r, and we'll see the \n and it'll stay
- * true, then the first non \r or \n char will set it to false, and *then* start the next line.
- *
- */
+ bool _scan_buffer() {
_last_scan_offset = _scan_offset;
while (_is_more_to_scan()) {
- auto write_header = &_headers[_write_header_index];
-
bool ends_line = false;
bool is_control = false;
- // Look for line endings
- // Classify the line
char c = _data[_scan_offset];
if (c == 0) {
_debug_trap("scan ran into NULL");
}
- if (c == '\r' ||
- c == '\n'
- ) {
-
- // We only mark ends_line for the first end-line char, and if
- // _at_start_of_line is already true, this is not the first.
- if (!_at_start_of_line) {
- ends_line = true;
+ // Look for line endings
+ if (c == '\r' || c == '\n') {
+ if (!_at_start_of_line) { // We only mark ends_line for the first end-line char, and if
+ ends_line = true; // _at_start_of_line is already true, this is not the first.
}
}
+ // Classify the line if it's a single character
else
- if ((c == '!') ||
- (c == '~') ||
- (c == ENQ) || // request ENQ/ack
- (c == CHAR_RESET) || // ^X - reset (aka cancel, terminate)
- (c == CHAR_ALARM) || // ^D - request job kill (end of transmission)
- (cm_has_hold() && c == '%') // flush (only in feedhold)
- ) {
-
- // Special case: if we're NOT _at_start_of_line, we need to move the
- // character to _line_start_offset. That means moving every character
- // forward one. THEN we back-track _scan_offset to this new start of line.
- if (!_at_start_of_line) {
- uint16_t copy_offset = _line_start_offset;
- while (copy_offset != _scan_offset) {
- uint16_t next_copy_offset = (copy_offset+1)&(_size-1);
- _data[next_copy_offset] = _data[copy_offset];
- copy_offset = next_copy_offset;
- }
- // Copy the single character to the first character
- _data[_line_start_offset] = c;
- }
+ if (_at_start_of_line &&
+ ((c == '!') ||
+ (c == '~') ||
+ (c == ENQ) || // request ENQ/ack
+ (c == CHAR_RESET) || // ^X - reset (aka cancel, terminate)
+ (c == CHAR_ALARM) || // ^D - request job kill (end of transmission)
+ (c == '%' && cm_has_hold()) // flush (only in feedhold or part of control header)
+ )) {
_line_start_offset = _scan_offset;
@@ -606,7 +590,14 @@ struct LineRXBuffer : RXBuffer<_size, owner_type, char> { // reserve size of 128
_at_start_of_line = false;
}
+ // bump the _scan_offset
+ _scan_offset = _get_next_scan_offset();
+
if (ends_line) {
+ // _scan_offset is now one past the end of the line,
+ // which means it is at the start of a new line
+ _at_start_of_line = true;
+
// Here we classify the line.
// If we are is_control is already true, it's an already classified
// single-character command.
@@ -620,149 +611,168 @@ struct LineRXBuffer : RXBuffer<_size, owner_type, char> { // reserve size of 128
// TODO ---
}
-
- if (write_header->_status == HEADER_PREPPED) {
- write_header->_is_control = is_control;
- write_header->_status = HEADER_FILLING;
- }
- else if (write_header->_is_control != is_control)
- {
- // This line goes into the next header.
- // Now we have to end this _write_header and grab another.
-
- write_header->_status = HEADER_FULL;
- if (!_check_write_header()) {
- // We just bail if there's not another header available.
- return;
+ if (is_control) { // we found a control
+ // Quick check for single-character with a \n after it
+ while (_is_more_to_scan() &&
+ ((_data[_scan_offset] == '\n') ||
+ (_data[_scan_offset] == '\r'))
+ )
+ {
+ _scan_offset = _get_next_scan_offset();
}
-
- // Update the pointer
- write_header = &_headers[_write_header_index];
- write_header->_is_control = is_control;
- write_header->_status = HEADER_FILLING;
+ return true;
+ } else { // we did find one more line, though.
+ _lines_found++;
}
-
- write_header->_line_count++;
- _at_start_of_line = true;
- }
-
- // We do this LAST. If we had to exit before this point,
- // we will evaluate the same character again.
- _scan_offset = _get_next_scan_offset();
+ } // if ends_line
} //while (_is_more_to_scan())
+ return false; // no control was found
};
-
-
- // This is the ONLY external interface in this class
+ /*
+ * readline()
+ *
+ * This is the ONLY external interface in this class
+ *
+ * Exit condition when a control is found: _line_start_offset and _scan_offset should be the same.
+ * If the control was the first char of the buffer it also moves the _data_offset, marking it as read
+ */
char *readline(bool control_only, uint16_t &line_size) {
- _scan_buffer();
+ // This is tricky: if we don't have room for more skip_sections, then we
+ // can't scan any more for controls. So we don't scan, amd hope some lines are read.
+ bool found_control = _skip_sections.is_full() ? false : _scan_buffer();
+ _restartTransfer();
- uint8_t search_header_index = _first_header_index;
- auto search_header = &_headers[search_header_index];
- bool found_control = false;
- do {
- if ((search_header->_status >= HEADER_FILLING) &&
- search_header->_is_control &&
- (search_header->_line_count > 0)) {
-
- found_control = true;
- break;
- }
- if (search_header_index == _write_header_index)
- {
- break;
- }
-
- search_header_index = _get_next_header_index(search_header_index);
- search_header = &_headers[search_header_index];
- } while (1);
-
-
- if (found_control) {
-
- // When we get here, search_header points to a valid header that we want to either:
- // A) Get a single-character command from and return it, OR
- // B) Get a full line from and return it.
-
- // For B, we handle that like any line. But the single chars have to have special
- // attention.
-
-
- char c = _data[search_header->_read_offset];
- if ((c == '!') ||
- (c == '~') ||
- (c == ENQ) || // request ENQ/ack
- (c == CHAR_RESET) || // ^X - reset (aka cancel, terminate)
- (c == CHAR_ALARM) || // ^D - request job kill (end of transmission)
- (cm_has_hold() && c == '%') // flush (only in feedhold)
- ) {
- line_size = 1;
-
- search_header->_read_offset = search_header->_get_next_read_offset();
- search_header->_line_count--;
- search_header->_is_processing = true;
-
- single_char_buffer[0] = c;
- single_char_buffer[1] = 0;
-
- return single_char_buffer;
- }
-
- // fall through to finding the end of the line in search_header
-
-
- } // end if (found_control)
- else {
-
- // Logic to determine that we can safely look at ONLY the first header:
- // • We always read all of the command lines first.
- // • We have already called _free_unused_space().
-
- search_header_index = _first_header_index;
- search_header = &_headers[search_header_index];
-
- if (control_only || search_header->_is_control || (search_header->_line_count == 0)) {
- if ((search_header->_line_count == 0)) {
- _restartTransfer();
- }
- line_size = 0;
- return nullptr;
- }
-
- } // end if (!found_control)
-
-
-
- // By the time we get here, search_header points to a valid header that we want to pull the first
- // full line from and return it.
-
-
- uint16_t read_offset = search_header->_read_offset;
char *dst_ptr = _line_buffer;
line_size = 0;
- if (_data[read_offset] == 0) {
+ if (found_control) {
+ // Optimization: if the control was found at the beginning of _data, we note that now
+ // and update the _read_offset when we update _line_start_offset
+ bool ctrl_is_at_beginning_of_data = (_line_start_offset == _read_offset);
+ if (!ctrl_is_at_beginning_of_data) {
+ _skip_sections.add_skip(_line_start_offset, _scan_offset);
+ }
+
+ // When we get here, _line_start_offset points to either:
+ // A) A single-character command, OR
+ // B) A full line.
+ // Either way, _scan_offset is one past the end, so we don't care which.
+
+ if (_data[_line_start_offset] == 0) {
+ _debug_trap("read ran into NULL");
+ }
+
+ // scan past any leftover CR or LF from the previous line
+ while ((_data[_line_start_offset] == '\n') || (_data[_line_start_offset] == '\r')) {
+ _line_start_offset = (_line_start_offset+1)&(_size-1);
+ if (_scan_offset == _line_start_offset) {
+ _debug_trap("read ran into scan (1)");
+ }
+ }
+
+ while ((_scan_offset != _line_start_offset) &&
+ (line_size < (_line_buffer_size - 2))) {
+// if (!_canBeRead(read_offset)) { // This test should NEVER fail.
+// _debug_trap("readline hit unreadable and shouldn't have!");
+// }
+
+ // copy the charater to _line_buffer
+ char c = _data[_line_start_offset];
+ *dst_ptr = c;
+
+ // update the line_size
+ line_size++;
+
+ // update read/write positions
+ dst_ptr++;
+ _line_start_offset = (_line_start_offset+1)&(_size-1);
+ }
+
+ // null-terminate the string
+ *dst_ptr = 0;
+
+ if (ctrl_is_at_beginning_of_data) {
+ _read_offset = _scan_offset;
+ } else {
+ // special case: if the return value is '%'
+ // then we actually consider everything before it to be read
+
+ if ('%' == _line_buffer[0]) {
+ // Things that must be managed here:
+ // * _read_offset -- we're skipping data
+ // * _lines_found -- we shouldn't have any lines "left"
+ // * _skip_sections -- there's nothing to skip, we just did
+
+ // Things that won't be changed (further):
+ // * _scan_offset -- we're not changing past where it's scanned
+ // * _line_start_offset -- we've already adjusted it
+ // * _at_start_of_line -- should always be true when we're here
+
+ // move the read buffer up to where we're scanning
+ _read_offset = _scan_offset;
+
+ // record that we have 0 lines (of data) in the buffer
+ _lines_found = 0;
+
+ // and clear out any skip sections we have
+ while (!_skip_sections.is_empty()) {
+ _skip_sections.pop_skip();
+ }
+ }
+ }
+
+// if (ctrl_is_at_beginning_of_data) {
+// // attempt to request more data
+// _restartTransfer();
+// }
+
+ return _line_buffer;
+ } // end if (found_control)
+
+ if (control_only) {
+ line_size = 0;
+ return nullptr;
+ }
+
+ if (_lines_found == 0) {
+ // nothing to return
+ line_size = 0;
+ return nullptr;
+ }
+
+
+ // By the time we get here, we know we have at least one line in _data.
+
+
+ if (_data[_read_offset] == 0) {
_debug_trap("read ran into NULL");
}
+ // skip sections will always start at the beginning of a line
+ _skip_sections.skip(_read_offset);
+
// scan past any leftover CR or LF from the previous line
- while ((_data[read_offset] == '\n') || (_data[read_offset] == '\r')) {
- read_offset = (read_offset+1)&(_size-1);
- if (_scan_offset == read_offset) {
- _debug_trap("read ran into scan (1)");
+ char c = _data[_read_offset];
+ while ((c == '\n') || (c == '\r')) {
+ _read_offset = (_read_offset+1)&(_size-1);
+ if (_scan_offset == _read_offset) {
+ _debug_trap("read ran into scan (2)");
}
+ // this also counts as the beginning of a line
+ _skip_sections.skip(_read_offset);
+ c = _data[_read_offset];
}
while (line_size < (_line_buffer_size - 2)) {
- if (!_canBeRead(read_offset)) { // This test should NEVER fail.
- _debug_trap("readline hit unreadable and shouldn't have!");
- }
- char c = _data[read_offset];
+// if (!_canBeRead(read_offset)) { // This test should NEVER fail.
+// _debug_trap("readline hit unreadable and shouldn't have!");
+// }
- if (
- c == '\r' ||
+ _read_offset = (_read_offset+1)&(_size-1);
+
+ if ( c == '\r' ||
c == '\n'
) {
@@ -774,31 +784,46 @@ struct LineRXBuffer : RXBuffer<_size, owner_type, char> { // reserve size of 128
// update read/write positions
dst_ptr++;
- read_offset = (read_offset+1)&(_size-1);
- if (_scan_offset == read_offset) {
- _debug_trap("read ran into scan (2)");
- }
+
+ c = _data[_read_offset];
}
- // previous character was last one of the line
- // update the header's next read position
- search_header->_read_offset = (read_offset+1)&(_size-1);
- // and line count
- search_header->_line_count--;
- // and processing flag
- search_header->_is_processing = true;
+ --_lines_found;
+ _restartTransfer();
+
+ // null-terminate the string
*dst_ptr = 0;
return _line_buffer;
- };
-};
+ }; // readline
+ // this is called from flushRead()
+ void flush() {
+ parent_type::flush();
+ _scan_offset = _read_offset;
+
+ // This is similar to the % "queue flush" handling above, except we flush
+ // the scan to the to the read (which was just set tot he write by the parent),
+ // not the other way around.
+
+ // record that we have 0 lines (of data) in the buffer
+ _lines_found = 0;
+
+ // and clear out any skip sections we have
+ while (!_skip_sections.is_empty()) {
+ _skip_sections.pop_skip();
+ }
+ }; // flush
+
+}; // LineRXBuffer
template
struct xioDeviceWrapper : xioDeviceWrapperBase { // describes a device for reading and writing
Device _dev;
- LineRXBuffer<512, Device> _rx_buffer;
- TXBuffer<512, Device> _tx_buffer;
+
+ // TODO - make _buffer_size, _header_count, and _line_buffer_size configurable
+ LineRXBuffer<1024, Device> _rx_buffer;
+ TXBuffer<1024, Device> _tx_buffer;
xioDeviceWrapper(Device dev, uint8_t _caps) : xioDeviceWrapperBase(_caps), _dev{dev}, _rx_buffer{_dev}, _tx_buffer{_dev}
{
@@ -928,7 +953,7 @@ struct xioDeviceWrapper : xioDeviceWrapperBase { // describes a device for re
} // flags & DEV_IS_CONNECTED
}
};
- };
+};
// ALLOCATIONS
// Declare a device wrapper class for SerialUSB and SerialUSB1
@@ -937,10 +962,12 @@ xioDeviceWrapper serialUSB0Wrapper {
&SerialUSB,
(DEV_CAN_READ | DEV_CAN_WRITE | DEV_CAN_BE_CTRL | DEV_CAN_BE_DATA)
};
+#if USB_SERIAL_PORTS_EXPOSED == 2
xioDeviceWrapper serialUSB1Wrapper {
&SerialUSB1,
(DEV_CAN_READ | DEV_CAN_WRITE | DEV_CAN_BE_CTRL | DEV_CAN_BE_DATA)
};
+#endif
#endif // XIO_HAS_USB
#if XIO_HAS_UART==1
xioDeviceWrapper serial0Wrapper {
@@ -954,7 +981,9 @@ xioDeviceWrapper serial0Wrapper {
xio_t xio = {
#if XIO_HAS_USB == 1
&serialUSB0Wrapper,
+#if USB_SERIAL_PORTS_EXPOSED == 2
&serialUSB1Wrapper,
+#endif
#endif // XIO_HAS_USB
#if XIO_HAS_UART == 1
&serial0Wrapper
@@ -982,8 +1011,10 @@ void xio_init()
#if XIO_HAS_USB == 1
serialUSB0Wrapper.init();
+#if USB_SERIAL_PORTS_EXPOSED == 2
serialUSB1Wrapper.init();
#endif
+#endif
#if XIO_HAS_UART == 1
serial0Wrapper.init();
#endif
@@ -1009,7 +1040,6 @@ size_t xio_write(const char *buffer, size_t size)
/*
* xio_readline() - read a complete line from a device
* xio_writeline() - write a complete line to control device
- * xio_flush_read() - flush read buffers
*
* Defers to xio.readline(), etc.
*/
@@ -1024,11 +1054,6 @@ int16_t xio_writeline(const char *buffer)
return xio.writeline(buffer);
}
-void xio_flush_read()
-{
- return xio.flushRead();
-}
-
bool xio_connected()
{
return xio.connected();
diff --git a/g2core/xio.h b/g2core/xio.h
index 671ba905..0369af21 100755
--- a/g2core/xio.h
+++ b/g2core/xio.h
@@ -113,7 +113,6 @@ enum xioSPIMode {
void xio_init(void);
stat_t xio_test_assertions(void);
-void xio_flush_read();
size_t xio_write(const uint8_t *buffer, size_t size);
char *xio_readline(devflags_t &flags, uint16_t &size);
int16_t xio_writeline(const char *buffer);