diff --git a/README.md b/README.md
index 88ccc74..7664f60 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,21 @@
# Triggerscope
Micro-Manager-specific firmware for the Triggerscope.
-The Triggerscope (http://arc.austinblanco.com/product/triggerscope-3/)
+The Triggerscope (https://advancedresearch-consulting.com/product/triggerscope-4/)
is an Arduino-based device build and sold by "Advanced Research Consulting"
-(https://arc.austinblanco.com/) that provides analog and digital outputs.
+(https://advancedresearch-consulting.com/) that provides analog and digital outputs.
This firmware - in combination with the Micro-Manager "TriggerscopeMM" device adapter,
allows for control of 16 analog and 16 digital outputs. Outputs can
transition between settings triggered by a Trigger input
signal at micro-second time scales. For full documentation
-of the use of this firmare, see: https://micro-manager.org/wiki/TriggerScopeMM
+of the use of this firmware, see: https://micro-manager.org/wiki/TriggerScopeMM
+
+# Firmware versions
+TriggerScope version 3 boards (serial numbers 1932-1970) must use the TriggerScope_V3 firmware.
+
+TriggerScope version 4 boards (serial numbers 1971-2117) must use the TriggerScope_V4 firmware.
+
+TriggerScope version 4B boards (serial numbers 2118 and later) must use the TriggerScope_V4 firmware.
+
+An extension of the firmware which allows overdrive pulses to be applied at the beginning of the analog signal is also available for V4 and V4B boards.
\ No newline at end of file
diff --git a/src/Overdrive/README.md b/src/Overdrive/README.md
new file mode 100644
index 0000000..587aebd
--- /dev/null
+++ b/src/Overdrive/README.md
@@ -0,0 +1,21 @@
+# Overview
+This firmware allows voltage overdrive and underdrive pulses to be applied at the beginning of the analog output signal as illustrated below:
.
+
+Overdrive pulses can be incorporated into DAC sequences. The voltage and duration of each overdrive pulse can be independently programmed.
+
+Extensions of the original firmware to enable overdrive pulses include:
+ - POV: "Program Over Voltage".
+ This command sets the voltage of the overdrive pulse
+ - POD: "Program OverVoltage Delay".
+ This command sets the duration of the overdrive pulse. After this time expires, the voltage on the specified pin will set to the state specified in the PAS list.
+ - BAD (sets delay in blanking mode of analog output) and BAL (sets length of analog output pulse in blanking mode) commands are deprecated.
+
+See command info using "?\n" command for details on use.
+
+This functionality will be available in the TriggerScopeMM Micro-manager device adapter in an upcoming update.
+
+Ensure the correct firmware version is used for a given board serial number, as described earlier.
+
+For more information on serial # or if you have errors please contact ARC at advancedreseach-consulting.com
+
+Special thanks to the Mehta Lab at the Chan Zuckerburg Biohub for supporting these extensions!
\ No newline at end of file
diff --git a/src/Overdrive/TriggerScope_V4/Adafruit_MCP23017.cpp b/src/Overdrive/TriggerScope_V4/Adafruit_MCP23017.cpp
new file mode 100644
index 0000000..79d193c
--- /dev/null
+++ b/src/Overdrive/TriggerScope_V4/Adafruit_MCP23017.cpp
@@ -0,0 +1,332 @@
+/*!
+ * @file Adafruit_MCP23017.cpp
+ *
+ * @mainpage Adafruit MCP23017 Library
+ *
+ * @section intro_sec Introduction
+ *
+ * This is a library for the MCP23017 i2c port expander
+ *
+ * These displays use I2C to communicate, 2 pins are required to
+ * interface
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing
+ * products from Adafruit!
+ *
+ * @section author Author
+ *
+ * Written by Limor Fried/Ladyada for Adafruit Industries.
+ *
+ * @section license License
+ *
+ * BSD license, all text above must be included in any redistribution
+ */
+
+#ifdef __AVR
+#include
+#elif defined(ESP8266)
+#include
+#endif
+#include "Adafruit_MCP23017.h"
+
+#if ARDUINO >= 100
+#include "Arduino.h"
+#else
+#include "WProgram.h"
+#endif
+
+// minihelper to keep Arduino backward compatibility
+static inline void wiresend(uint8_t x, TwoWire *theWire) {
+#if ARDUINO >= 100
+ theWire->write((uint8_t)x);
+#else
+ theWire->send(x);
+#endif
+}
+
+static inline uint8_t wirerecv(TwoWire *theWire) {
+#if ARDUINO >= 100
+ return theWire->read();
+#else
+ return theWire->receive();
+#endif
+}
+
+/**
+ * Bit number associated to a give Pin
+ */
+uint8_t Adafruit_MCP23017::bitForPin(uint8_t pin) { return pin % 8; }
+
+/**
+ * Register address, port dependent, for a given PIN
+ */
+uint8_t Adafruit_MCP23017::regForPin(uint8_t pin, uint8_t portAaddr,
+ uint8_t portBaddr) {
+ return (pin < 8) ? portAaddr : portBaddr;
+}
+
+/**
+ * Reads a given register
+ */
+uint8_t Adafruit_MCP23017::readRegister(uint8_t addr) {
+ // read the current GPINTEN
+ _wire->beginTransmission(MCP23017_ADDRESS | i2caddr);
+ wiresend(addr, _wire);
+ _wire->endTransmission();
+ _wire->requestFrom(MCP23017_ADDRESS | i2caddr, 1);
+ return wirerecv(_wire);
+}
+
+/**
+ * Writes a given register
+ */
+void Adafruit_MCP23017::writeRegister(uint8_t regAddr, uint8_t regValue) {
+ // Write the register
+ _wire->beginTransmission(MCP23017_ADDRESS | i2caddr);
+ wiresend(regAddr, _wire);
+ wiresend(regValue, _wire);
+ _wire->endTransmission();
+}
+
+/**
+ * Helper to update a single bit of an A/B register.
+ * - Reads the current register value
+ * - Writes the new register value
+ */
+void Adafruit_MCP23017::updateRegisterBit(uint8_t pin, uint8_t pValue,
+ uint8_t portAaddr,
+ uint8_t portBaddr) {
+ uint8_t regValue;
+ uint8_t regAddr = regForPin(pin, portAaddr, portBaddr);
+ uint8_t bit = bitForPin(pin);
+ regValue = readRegister(regAddr);
+
+ // set the value for the particular bit
+ bitWrite(regValue, bit, pValue);
+
+ writeRegister(regAddr, regValue);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+/*!
+ * Initializes the MCP23017 given its HW selected address, see datasheet for
+ * Address selection.
+ * @param addr Selected address
+ * @param theWire the I2C object to use, defaults to &Wire
+ */
+void Adafruit_MCP23017::begin(uint8_t addr, TwoWire *theWire) {
+ if (addr > 7) {
+ addr = 7;
+ }
+ i2caddr = addr;
+ _wire = theWire;
+
+ _wire->begin();
+
+ // set defaults!
+ // all inputs on port A and B
+ writeRegister(MCP23017_IODIRA, 0xff);
+ writeRegister(MCP23017_IODIRB, 0xff);
+}
+
+/**
+ * Initializes the default MCP23017, with 000 for the configurable part of the
+ * address
+ * @param theWire the I2C object to use, defaults to &Wire
+ */
+void Adafruit_MCP23017::begin(TwoWire *theWire) { begin(0, theWire); }
+
+/**
+ * Sets the pin mode to either INPUT or OUTPUT
+ * @param p Pin to set
+ * @param d Mode to set the pin
+ */
+void Adafruit_MCP23017::pinMode(uint8_t p, uint8_t d) {
+ updateRegisterBit(p, (d == INPUT), MCP23017_IODIRA, MCP23017_IODIRB);
+}
+
+/**
+ * Reads all 16 pins (port A and B) into a single 16 bits variable.
+ * @return Returns the 16 bit variable representing all 16 pins
+ */
+uint16_t Adafruit_MCP23017::readGPIOAB() {
+ uint16_t ba = 0;
+ uint8_t a;
+
+ // read the current GPIO output latches
+ _wire->beginTransmission(MCP23017_ADDRESS | i2caddr);
+ wiresend(MCP23017_GPIOA, _wire);
+ _wire->endTransmission();
+
+ _wire->requestFrom(MCP23017_ADDRESS | i2caddr, 2);
+ a = wirerecv(_wire);
+ ba = wirerecv(_wire);
+ ba <<= 8;
+ ba |= a;
+
+ return ba;
+}
+
+/**
+ * Read a single port, A or B, and return its current 8 bit value.
+ * @param b Decided what gpio to use. Should be 0 for GPIOA, and 1 for GPIOB.
+ * @return Returns the b bit value of the port
+ */
+uint8_t Adafruit_MCP23017::readGPIO(uint8_t b) {
+
+ // read the current GPIO output latches
+ _wire->beginTransmission(MCP23017_ADDRESS | i2caddr);
+ if (b == 0)
+ wiresend(MCP23017_GPIOA, _wire);
+ else {
+ wiresend(MCP23017_GPIOB, _wire);
+ }
+ _wire->endTransmission();
+
+ _wire->requestFrom(MCP23017_ADDRESS | i2caddr, 1);
+ return wirerecv(_wire);
+}
+
+/**
+ * Writes all the pins in one go. This method is very useful if you are
+ * implementing a multiplexed matrix and want to get a decent refresh rate.
+ */
+void Adafruit_MCP23017::writeGPIOAB(uint16_t ba) {
+ _wire->beginTransmission(MCP23017_ADDRESS | i2caddr);
+ wiresend(MCP23017_GPIOA, _wire);
+ wiresend(ba & 0xFF, _wire);
+ wiresend(ba >> 8, _wire);
+ _wire->endTransmission();
+}
+
+/*!
+ * @brief Writes to a pin on the MCP23017
+ * @param pin Pin to write to
+ * @param d What to write to the pin
+ */
+void Adafruit_MCP23017::digitalWrite(uint8_t pin, uint8_t d) {
+ uint8_t gpio;
+ uint8_t bit = bitForPin(pin);
+
+ // read the current GPIO output latches
+ uint8_t regAddr = regForPin(pin, MCP23017_OLATA, MCP23017_OLATB);
+ gpio = readRegister(regAddr);
+
+ // set the pin and direction
+ bitWrite(gpio, bit, d);
+
+ // write the new GPIO
+ regAddr = regForPin(pin, MCP23017_GPIOA, MCP23017_GPIOB);
+ writeRegister(regAddr, gpio);
+}
+
+/*!
+ * @brief Enables the pull-up resistor on the specified pin
+ * @param p Pin to set
+ * @param d Value to set the pin
+ */
+void Adafruit_MCP23017::pullUp(uint8_t p, uint8_t d) {
+ updateRegisterBit(p, d, MCP23017_GPPUA, MCP23017_GPPUB);
+}
+
+/*!
+ * @brief Reads the specified pin
+ * @param pin Pin to read
+ * @return Value of the pin
+ */
+uint8_t Adafruit_MCP23017::digitalRead(uint8_t pin) {
+ uint8_t bit = bitForPin(pin);
+ uint8_t regAddr = regForPin(pin, MCP23017_GPIOA, MCP23017_GPIOB);
+ return (readRegister(regAddr) >> bit) & 0x1;
+}
+
+/**
+ * Configures the interrupt system. both port A and B are assigned the same
+ * configuration.
+ * @param mirroring Mirroring will OR both INTA and INTB pins.
+ * @param openDrain Opendrain will set the INT pin to value or open drain.
+ * @param polarity polarity will set LOW or HIGH on interrupt.
+ * Default values after Power On Reset are: (false, false, LOW)
+ * If you are connecting the INTA/B pin to arduino 2/3, you should configure the
+ * interupt handling as FALLING with the default configuration.
+ */
+void Adafruit_MCP23017::setupInterrupts(uint8_t mirroring, uint8_t openDrain,
+ uint8_t polarity) {
+ // configure the port A
+ uint8_t ioconfValue = readRegister(MCP23017_IOCONA);
+ bitWrite(ioconfValue, 6, mirroring);
+ bitWrite(ioconfValue, 2, openDrain);
+ bitWrite(ioconfValue, 1, polarity);
+ writeRegister(MCP23017_IOCONA, ioconfValue);
+
+ // Configure the port B
+ ioconfValue = readRegister(MCP23017_IOCONB);
+ bitWrite(ioconfValue, 6, mirroring);
+ bitWrite(ioconfValue, 2, openDrain);
+ bitWrite(ioconfValue, 1, polarity);
+ writeRegister(MCP23017_IOCONB, ioconfValue);
+}
+
+/**
+ * Set's up a pin for interrupt. uses arduino MODEs: CHANGE, FALLING, RISING.
+ *
+ * Note that the interrupt condition finishes when you read the information
+ * about the port / value that caused the interrupt or you read the port itself.
+ * Check the datasheet can be confusing.
+ * @param pin Pin to set
+ * @param mode Mode to set the pin
+ *
+ */
+void Adafruit_MCP23017::setupInterruptPin(uint8_t pin, uint8_t mode) {
+
+ // set the pin interrupt control (0 means change, 1 means compare against
+ // given value);
+ updateRegisterBit(pin, (mode != CHANGE), MCP23017_INTCONA, MCP23017_INTCONB);
+ // if the mode is not CHANGE, we need to set up a default value, different
+ // value triggers interrupt
+
+ // In a RISING interrupt the default value is 0, interrupt is triggered when
+ // the pin goes to 1. In a FALLING interrupt the default value is 1, interrupt
+ // is triggered when pin goes to 0.
+ updateRegisterBit(pin, (mode == FALLING), MCP23017_DEFVALA, MCP23017_DEFVALB);
+
+ // enable the pin for interrupt
+ updateRegisterBit(pin, HIGH, MCP23017_GPINTENA, MCP23017_GPINTENB);
+}
+
+/*!
+ * @brief Gets the last interrupt pin
+ * @return Returns the last interrupt pin
+ */
+uint8_t Adafruit_MCP23017::getLastInterruptPin() {
+ uint8_t intf;
+
+ // try port A
+ intf = readRegister(MCP23017_INTFA);
+ for (int i = 0; i < 8; i++)
+ if (bitRead(intf, i))
+ return i;
+
+ // try port B
+ intf = readRegister(MCP23017_INTFB);
+ for (int i = 0; i < 8; i++)
+ if (bitRead(intf, i))
+ return i + 8;
+
+ return MCP23017_INT_ERR;
+}
+/*!
+ * @brief Gets the value of the last interrupt pin
+ * @return Returns the value of the last interrupt pin
+ */
+uint8_t Adafruit_MCP23017::getLastInterruptPinValue() {
+ uint8_t intPin = getLastInterruptPin();
+ if (intPin != MCP23017_INT_ERR) {
+ uint8_t intcapreg = regForPin(intPin, MCP23017_INTCAPA, MCP23017_INTCAPB);
+ uint8_t bit = bitForPin(intPin);
+ return (readRegister(intcapreg) >> bit) & (0x01);
+ }
+
+ return MCP23017_INT_ERR;
+}
diff --git a/src/Overdrive/TriggerScope_V4/Adafruit_MCP23017.h b/src/Overdrive/TriggerScope_V4/Adafruit_MCP23017.h
new file mode 100644
index 0000000..dde203c
--- /dev/null
+++ b/src/Overdrive/TriggerScope_V4/Adafruit_MCP23017.h
@@ -0,0 +1,91 @@
+/*!
+ * @file Adafruit_MCP23017.h
+ */
+
+#ifndef _Adafruit_MCP23017_H_
+#define _Adafruit_MCP23017_H_
+
+// Don't forget the Wire library
+#ifndef ARDUINO_AVR_GEMMA
+// TinyWireM is now part of
+// Adafruit version of Wire Library, so this
+// will work with Adafruit ATtiny85's
+// But Arduino Gemma doesn't use that library
+// We do NOT want to include Wire if it's an arduino Gemma
+#include
+#else
+#include
+#define Wire TinyWireM
+#endif
+
+/*!
+ * @brief MCP23017 main class
+ */
+class Adafruit_MCP23017 {
+public:
+ void begin(uint8_t addr, TwoWire *theWire = &Wire);
+ void begin(TwoWire *theWire = &Wire);
+
+ void pinMode(uint8_t p, uint8_t d);
+ void digitalWrite(uint8_t p, uint8_t d);
+ void pullUp(uint8_t p, uint8_t d);
+ uint8_t digitalRead(uint8_t p);
+
+ void writeGPIOAB(uint16_t);
+ uint16_t readGPIOAB();
+ uint8_t readGPIO(uint8_t b);
+
+ void setupInterrupts(uint8_t mirroring, uint8_t open, uint8_t polarity);
+ void setupInterruptPin(uint8_t p, uint8_t mode);
+ uint8_t getLastInterruptPin();
+ uint8_t getLastInterruptPinValue();
+
+private:
+ uint8_t i2caddr;
+ TwoWire *_wire; //!< pointer to a TwoWire object
+
+ uint8_t bitForPin(uint8_t pin);
+ uint8_t regForPin(uint8_t pin, uint8_t portAaddr, uint8_t portBaddr);
+
+ uint8_t readRegister(uint8_t addr);
+ void writeRegister(uint8_t addr, uint8_t value);
+
+ /**
+ * Utility private method to update a register associated with a pin (whether
+ * port A/B) reads its value, updates the particular bit, and writes its
+ * value.
+ */
+ void updateRegisterBit(uint8_t p, uint8_t pValue, uint8_t portAaddr,
+ uint8_t portBaddr);
+};
+
+#define MCP23017_ADDRESS 0x20 //!< MCP23017 Address
+
+// registers
+#define MCP23017_IODIRA 0x00 //!< I/O direction register A
+#define MCP23017_IPOLA 0x02 //!< Input polarity port register A
+#define MCP23017_GPINTENA 0x04 //!< Interrupt-on-change pins A
+#define MCP23017_DEFVALA 0x06 //!< Default value register A
+#define MCP23017_INTCONA 0x08 //!< Interrupt-on-change control register A
+#define MCP23017_IOCONA 0x0A //!< I/O expander configuration register A
+#define MCP23017_GPPUA 0x0C //!< GPIO pull-up resistor register A
+#define MCP23017_INTFA 0x0E //!< Interrupt flag register A
+#define MCP23017_INTCAPA 0x10 //!< Interrupt captured value for port register A
+#define MCP23017_GPIOA 0x12 //!< General purpose I/O port register A
+#define MCP23017_OLATA 0x14 //!< Output latch register 0 A
+
+#define MCP23017_IODIRB 0x01 //!< I/O direction register B
+#define MCP23017_IPOLB 0x03 //!< Input polarity port register B
+#define MCP23017_GPINTENB 0x05 //!< Interrupt-on-change pins B
+#define MCP23017_DEFVALB 0x07 //!< Default value register B
+#define MCP23017_INTCONB 0x09 //!< Interrupt-on-change control register B
+#define MCP23017_IOCONB 0x0B //!< I/O expander configuration register B
+#define MCP23017_GPPUB 0x0D //!< GPIO pull-up resistor register B
+#define MCP23017_INTFB 0x0F //!< Interrupt flag register B
+#define MCP23017_INTCAPB 0x11 //!< Interrupt captured value for port register B
+#define MCP23017_GPIOB 0x13 //!< General purpose I/O port register B
+#define MCP23017_OLATB 0x15 //!< Output latch register 0 B
+
+#define MCP23017_INT_ERR 255 //!< Interrupt error
+
+#endif
diff --git a/src/Overdrive/TriggerScope_V4/Linduino.h b/src/Overdrive/TriggerScope_V4/Linduino.h
new file mode 100644
index 0000000..85ee651
--- /dev/null
+++ b/src/Overdrive/TriggerScope_V4/Linduino.h
@@ -0,0 +1,109 @@
+//! @todo Review this file.
+/*
+Linduino.h
+
+This file contains the hardware definitions for the Linduino.
+
+REVISION HISTORY
+$Revision: 1906 $
+$Date: 2013-08-26 15:09:18 -0700 (Mon, 26 Aug 2013) $
+
+Copyright (c) 2013, Linear Technology Corp.(LTC)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of Linear Technology Corp.
+
+The Linear Technology Linduino is not affiliated with the official Arduino team.
+However, the Linduino is only possible because of the Arduino team's commitment
+to the open-source community. Please, visit http://www.arduino.cc and
+http://store.arduino.cc , and consider a purchase that will help fund their
+ongoing work.
+*/
+
+//! @defgroup Linduino Linduino: Linear Technology Arduino-Compatible Demonstration Board
+
+/*! @file
+ @ingroup Linduino
+ @ingroup QuikEval
+ Header File for Linduino Libraries and Demo Code
+*/
+
+#ifndef LINDUINO_H
+#define LINDUINO_H
+
+//! @name LINDUINO PIN ASSIGNMENTS
+//! @{
+
+#define QUIKEVAL_GPIO 9 //!< Linduino QuikEval GPIO pin (QuikEval connector pin 14) connects to Arduino pin 9
+#define QUIKEVAL_CS SS //!< QuikEval CS pin (SPI chip select on QuikEval connector pin 6) connects to Arduino SS pin.
+#define QUIKEVAL_MUX_MODE_PIN 8 /*!< QUIKEVAL_MUX_MODE_PIN defines the control pin for the QuikEval MUX.
+The I2C port's SCL and the SPI port's SCK signals share the same pin on the Linduino's QuikEval connector.
+Additionally, the I2C port's SDA and the SPI port's MOSI signals share the same pin on the Linduino's QuikEval connector.
+The pair of pins connected to the QuikEval connector is switched using a MUX on the Linduino board.
+The control pin to switch the MUX is defined as QUIKEVAL_MUX_MODE_PIN (Arduino pin 8). */
+//! @}
+
+// Macros
+//! Set "pin" low
+//! @param pin pin to be driven LOW
+#define output_low(pin) digitalWrite(pin, LOW)
+//! Set "pin" high
+//! @param pin pin to be driven HIGH
+#define output_high(pin) digitalWrite(pin, HIGH)
+//! Return the state of pin "pin"
+//! @param pin pin to be read (HIGH or LOW).
+//! @return the state of pin "pin"
+#define input(pin) digitalRead(pin)
+
+//! @todo Make a note about whether Arduino/Linduino is Big Endian or Little Endian. Raspberry Pi appears to be the opposite.
+//! This union splits one int16_t (16-bit signed integer) or uint16_t (16-bit unsigned integer)
+//! into two uint8_t's (8-bit unsigned integers) and vice versa.
+ union LT_union_int16_2bytes
+ {
+ int16_t LT_int16; //!< 16-bit signed integer to be converted to two bytes
+ uint16_t LT_uint16; //!< 16-bit unsigned integer to be converted to two bytes
+ uint8_t LT_byte[2]; //!< 2 bytes (unsigned 8-bit integers) to be converted to a 16-bit signed or unsigned integer
+ };
+
+//! @todo Make a note about whether Arduino/Linduino is Big Endian or Little Endian. Raspberry Pi appears to be the opposite.
+//! This union splits one int32_t (32-bit signed integer) or uint32_t (32-bit unsigned integer)
+//! into four uint8_t's (8-bit unsigned integers) and vice versa.
+union LT_union_int32_4bytes
+{
+ int32_t LT_int32; //!< 32-bit signed integer to be converted to four bytes
+ uint32_t LT_uint32; //!< 32-bit unsigned integer to be converted to four bytes
+ uint8_t LT_byte[4]; //!< 4 bytes (unsigned 8-bit integers) to be converted to a 32-bit signed or unsigned integer
+};
+
+//! @todo Make a note about whether Arduino/Linduino is Big Endian or Little Endian. Raspberry Pi appears to be the opposite.
+//! This union splits one float into four uint8_t's (8-bit unsigned integers) and vice versa.
+union LT_union_float_4bytes
+{
+ float LT_float; //!< float to be converted to four bytes
+ uint8_t LT_byte[4]; //!< 4 bytes (unsigned 8-bit integers) to be converted to a float
+};
+
+
+#endif // LINDUINO_H
diff --git a/src/Overdrive/TriggerScope_V4/README.md b/src/Overdrive/TriggerScope_V4/README.md
new file mode 100644
index 0000000..4c6f0ea
--- /dev/null
+++ b/src/Overdrive/TriggerScope_V4/README.md
@@ -0,0 +1,4 @@
+# Installation
+This firmware is intended for TriggerScope V4 boards (serial numbers 1971-2117).
+
+To install this firmware, you will first need to download and install the [TeensyDuino package](https://www.pjrc.com/teensy/td_download.html), and the [Arduino IDE](https://www.arduino.cc/en/software). Then power your TriggerScope, connect the USB cable to your computer, and open the file TriggerScope_V4/TriggerScope_V4.ino in the [Arduino IDE](https://www.arduino.cc/en/software). Select the port under which your device appears (Tools > Port), and select the Teensy 4.1 (Tools > Board). Press the "verify" button, and - if compilation succeeds - press the Upload button.
diff --git a/src/Overdrive/TriggerScope_V4/TriggerScope_V4.ino b/src/Overdrive/TriggerScope_V4/TriggerScope_V4.ino
new file mode 100644
index 0000000..5935a6e
--- /dev/null
+++ b/src/Overdrive/TriggerScope_V4/TriggerScope_V4.ino
@@ -0,0 +1,1936 @@
+/******************************************
+ Trigger Scope v. 604MM for Arduino microscope control by
+ ADVANCED RESEARCH CONSULTING @ 2015
+ Regents of the University of California, 2020
+
+ Command set:
+
+ Commands to the device are in ASCI and terminated with "\n".
+ Returns can be multiple lines, terminated with "\r\n" (carriage retun / new line).
+ Error messages start with "!ERROR_" followed by the command causing the error, semi-colon
+ and error message (example: "!ERROR_RANGE: Command out of range, DAC=1-16, Range = 1-5...")
+
+ "*" - prints ID (example: "ARC TRIGGERSCOPE 16 R3C v.604-MM"
+ "?" - pirnts message listing available commands and their parameters
+ ----------------------analog output functions-------------------------
+ "SAO" - sets the voltage of specified DAC. Expected format: "DAC1-4236"
+ where 1 is the line nr, and 4236 the new value to be send to the DAC.
+ Resulting voltage depends on bit range of the DAC, and the voltage range
+ Returns: "!DAC1,4236" or error message (not starting with !DAC) on failure.
+ "PAN" - Queries the number of analog output states that can be pre-programmed.
+ Format: "PANn".
+ Where n=1-16 (DAC1-16).
+ Returns: "!PANn,q" where q is the maximum number of states that can be programmed
+ "PAO" - Sends sequence of analog output states to be used in triggered sequences.
+ Format: "PAOn-s-o1-o2-on", where
+ - n=1-16 for DAC 1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (0-65535) to be consecutively inserted in the list.
+ Returns: "!PA0n-s-q" where q is the number of values successfully inserted in the internal buffer.
+ "POV" - Sets an array of overvoltage values for use before typical state transition.
+ Format: "POVn-s-o1-o2-on", where
+ - n=1-16 for DAC 1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (0-65535) to be consecutively inserted in the list.
+ Returns: "!PA0n-s-q" where q is the number of values successfully inserted in the internal buffer.
+
+ "POD" - Sets an array of overvoltage delay times for use with POV commands.
+ Format: "POVn-s-o1-o2-on", where
+ - n=1-16 for DAC 1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (0-4294967295) to be consecutively inserted in the list.
+ Returns: "!PA0n-s-q" where q is the number of values successfully inserted in the internal buffer.
+
+ "PAC" - Clears the sequence of ananlog states
+ Format: "PACn", where n is the DAC pinNr (1-16)
+ "PAS" - Starts triggered transitions in analog output state as pre-loaded in PAO
+ Format: "PASn-s-t" where
+ - n=1-16 for DAC 1-16
+ = s=0 or 1 where 0 stops and 1 starts triggered transitions
+ - t=0 or 1 and determines whether transition will happen on the falling (0) or rising (1) edge
+ of a pulse on the input pin
+"BAO" - Activates blanking mode of the analog output. Output state will be coupled to the
+ state of the input trigger. If input trigger is active, the DAC will be active (
+ as set with SDA), if input trigger is inactive, the output will go (which may not be 0V, depending on the range).
+ Format: "BAOn-s-t"
+ - n=1-16 for DAC 1-16
+ - s=0 or 1 where 0 stops and 1 starts balnking mode
+ - t translates state of the input pin to its activity for blanking. If t = 0,
+ DAC will be 0 when input trigger pin is low. t = 1: DAC will be active when input trigger is low.
+ "SAR" - Sets the output range in volts of the DAC. Example: "SAR2-1" where 2
+ specified the DAC number, and 1 the range. Allowed ranges are number 1-5:
+ 1: 0-5V
+ 2: 0-10V
+ 3: -5-+5V
+ 4: -10-+10V
+ 5: -2-+2V
+ Returns: "!RANGE2,1"
+-------------------------digital output functions------------------------
+"SDO" - sets TTL outputs of pins1-8 and 9-16 in a single operation
+ Format: DOn-o
+ Where n=0 or 1. 0 address pins 1-8, 1 goes to pins 9-16
+ 0 is digital output bitmask, 0: all off, 1: pin 1 on, 2: pin 2 on, 3: pins 1&2 on, etc..
+ Example: DO1,4 sets pin 3 high and pins 1,2, 4-8 low
+ Returns: "!DOn-o" or error message ("!ERROR_DO: ") on failure.
+"PDN" - Queries the number of digital output states that can be pre-programmed.
+ Format: "PDNn".
+ Where n=0 or 1. 0 address pins 1-8, 1 goes to pins 9-16
+ Returns: "!PDNn-q" where q is the maximum number of states that can be programmed
+"PDO" - Sends sequence of digital output states to be used in triggered sequences.
+ Format: "PDOn-s-o1-o2-on", where
+ - n=0 or 1 and directs the command to either pins1-8 or 9-16,
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values to be consecutively inserted in the list.
+ Returns: "!PD0n-s-q" where q is the number of values successfully inserted in the internal buffer.
+"PDC" - Clears the sequence of digital states
+ Format: "PDCn", where n is the pingroup, 0=pins1-8, 1=pins9-16
+"PDS" - Starts triggered transitions in digital output state as pre-loaded in PDO
+ Format: "PDSn-s-t" where
+ - n=0 or 1 and directs the command to either pins 1-8 or 9-16
+ = s=0 or 1 where 0 stops and 1 starts triggered transitions
+ - t=0 or 1 and determines whether transition will happen on the falling (0) or rising (1) edge
+ of a pulse on the input pin
+"BDO" - Activates blanking mode of the digital output. Output state will be coupled to the
+ state of the input trigger. If input trigger is active, the pingroup will be active (
+ as set with DO), if input trigger is inactive, the pingroup will go low.
+ Format: "BDOn-s-t"
+ where n is the pingroup (0 or 1) and s switched blanking mode on (1) or off (0).
+ t translates state of the input pin to its activity for blanking. If t = 0,
+ output pins will be low when input trigger pin is low. When t = 1, output pins
+ will be active when input trigger is low.
+ "SSL" - Set Signal LEDs.
+ In some cases, setting signal LEDs slows down triggered operation, so offer the option to
+ not set them.
+ Format: "SSLn" where
+ - n - 0 ("Off") or 1 ("On")
+ Signal LEDs are On by default
+
+
+
+*****************************
+Contact Advanced Research Consulting for Driver libraries! www.advancedresearch.consulting
+ ******************************/
+#include
+#include
+#include
+#include "Linduino.h"
+#include "Adafruit_MCP23017.h"
+
+#define focus 15 //sets focus line #
+#define pwrLed 11 //POWER indication
+#define dacLed 12 //POWER indication
+#define ttlLed 13 //POWER indication
+#define trigLed 14 //POWER indication
+#define readyLed 15 //POWER indication
+#define ttlblock2OE 10
+#define ttlblock2DIR 8
+#define ttlblock1OE 9
+#define ttlblock1DIR 7
+
+#define NR_DACS 16
+#define NR_DAC_STATES 1200
+#define NR_DO_STATES 1200
+
+
+String idname = "ARC TRIGGERSCOPE 16 R3C Board 4 v.613-MM";
+
+const char* helpString = "Available commands: \r\n"
+ "SAOn-s - sets DACs. n = DAC number (1-16), s = value (0-65535)\r\n"
+ "PANn - queries the number of programmable analogoutput states, n = DAC pin number (1-16)\r\n"
+ "PAOn-s-o1-o2-on - sends sequence of programmable analog output states, n = DAC pin number (1-16)\r\n"
+ " s = 0-based index in the sequence to start inserting, \r\n"
+ " o = comma separated series of output values (0-65535)\r\n"
+ "POVn-s-o1-o2-on - sends sequence of analog overdrive states, n = DAC pin number (1-16)\r\n"
+ " s = 0-based index in the sequence to start inserting, \r\n"
+ " o = comma separated series of output values (0-65535)\r\n"
+ "PODn-s-o1-o2-on - sends sequence of overdrive durations in microseconds, n = DAC pin number (1-16)\r\n"
+ " s = 0-based index in the sequence to start inserting, \r\n"
+ " o = comma separated series of delay times (0-(0-4294967295))\r\n"
+
+ "PACn - clears the sequence of analog states, n = DAC pin number (1-16)\r\n"
+ "PASn-s-t - starts triggered transition in programmable analog output states as programmed in PAO\r\n"
+ " n = DAC pin number (1-16), s = 0 (stops) or 1 (starts), \r\n"
+ " t = transition on falling (0) or rising (1) edge of input trigger.\r\n"
+ "BAOn-s-t starts blanking mode of ananlog output. n = DAC pin number (1-16), \r\n"
+ " s = 0 (stops) or 1 (starts), t = output low when trigger low (0), or inverse (1)\r\n"
+
+
+ "SARn-s - sets DAC voltages range. n = DAC number (1-16), s= 1:0-5V 2:0-10V 3:-5-+5V 4: -10-+10V 5:-2-+2V\r\n"
+ "SDOn-s - sets digital output state, n = 0(pins1-8) or 1(pins9-16), s binary mask 0-255\r\n"
+ "PDNn - queries the number of programmable digital output states, n = pin group 0(1-8) or 1 (9-16)\r\n"
+ "PDOm-s-o1-o2-on - sends sequence of programmable digital output states, n = pin group 0(1-8) or 1(9-16)\r\n"
+ " s = 0-based index in the sequence to start inserting\r\n"
+ " o = comma separated series of output states (0-255)\r\n"
+ "PDCn - clears the sequence of digital states, n = pin group 0(1-8) or 1 (9-16)\r\n"
+ "PDSn-s-t - starts triggered transitions in digital output state as pre-loaded in PDO, \r\n"
+ " n = pin group 0(1-8) or 1 (9-16), s = 0 stops 1 starts, \r\n"
+ " t = transition on falling (0) or rising (1) edge of input trigger.\r\n"
+ "BDn-s-t - sync digital output with input trigger. n = pin group 0(1-8) or 1 (9-16)\r\n"
+ " s = 0 stops 1 starts, t = output low when trigger low (0), or inverse (1)\r\n"
+ "SSLn - switches use of signal LEDs. n=0 (Off) or n=1 (On)\r\n"
+ "\r\n"; // empty line to signal end of help textd
+
+
+const char sep = '-';
+
+//new IC assignments and SD card access for V4 board
+Adafruit_MCP23017 mcp; //create mux object
+File myFile; //create settings file
+const int chipSelect = BUILTIN_SDCARD; //set SD card access CS line
+
+//set up menu modes and vars
+byte opMode = 1; //sets operation mode, 1=MANUAL control, 2 = PC CONTROL, 3=TTL Slave via Programmed Steps
+boolean trigArmed=false; //enables while loop for just the high speed trig sequencing
+unsigned long debugT=0; //debugger flag - not used in a few versions
+unsigned long trigInterval; //tracks frequency of inputs from camera or external source
+int trigStep=0; //optionally used to sequence more than 1 PROG line at a time, not fully implemented
+String inputString = ""; // a string to hold incoming data
+boolean stringComplete = false; // whether the string is complete
+
+//PIN ASSIGNMENTS
+const byte DAC[NR_DACS] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; //MOVE THESE FOR CUSTOMERS IF NEEDED!
+const byte ttl[NR_DACS] = {5,6,7,8,14,15,16,17,20,0,1,2,3,4,5,6}; //ttl pin #'s
+const byte trig[4] = {0,1,2,3};
+
+/*HIGH SPEED PROGRAMMING MODE MEMORY BLOCK*/
+int dacArray[NR_DAC_STATES][NR_DACS] = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}; // DACprogram list
+uint8_t ttlArray[NR_DO_STATES][2] = {{0,0}}; // digital output states program list
+int ttlArrayMaxIndex[2] = {0, 0}; // maintains the max index in the array that was set
+int ttlArrayIndex[2] = {0, 0}; // keeps track of current position in the array
+int dacArrayMaxIndex[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+int dacArrayIndex[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+int dacOverdriveIndex[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+uint32_t dacBlankDelay[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+uint32_t dacBlankDuration[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+uint32_t dacBlankExpire[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+boolean useSignalLEDs_ = true;
+
+// data structures to be assembled from blanking settings above that have a time-ordered sequence of events
+int dacBlankEventsNr = 0;
+// there can be a delay and a duration for each blanking event
+uint32_t dacBlankEventNextWait[2 * NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+uint8_t dacBlankEventPinNr[2 * NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+// 0 : off, 1: set normal state, 2: set value from dacArray
+uint8_t dacBlankEventState[2 * NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+byte pinGroupState[2] = {0, 0};
+byte pinGroupStoredState[2] = {0, 0};
+bool pinGroupBlanking[2] = {false, false};
+bool pinGroupBlankOnLow[2] = {true, true};
+bool pinGroupSequencing[2] = {false, false};
+byte pinGroupSequenceMode[2] = {0, 0}; // 0: falling edge, 1: rising edge
+int dacState[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+int dacStoredState[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+bool dacBlanking[16] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false};
+bool dacBlankOnLow[16] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false};
+bool dacSequencing[16] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false};
+byte dacSequenceMode[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+int povArray[NR_DAC_STATES][NR_DACS] = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}; // DAC overdrive value list
+uint32_t podArray[NR_DAC_STATES][NR_DACS] = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}; // overdrive delay on state
+uint32_t podExpire[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //timers for real overdrive expire
+int lastDac[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //holes the last DAC value and used for implement on timer expire
+
+bool triggerPinState = false;
+
+int delArray[500]; //time delay array for high speed sequences
+int focArray[6]; //array for focus stacks = start, step, #loops,direction,slave,current step
+uint16_t ttlState = 0;
+// boolean ttlActive[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+int timeCycles = 1; //used for high speed switching inside of a loop
+int runCycles = 0; //holds running position vs total cycles for timelapse
+
+volatile boolean inTrigger=false;
+byte program=0; //this sets the active program # used in the Mode 3 high speed sequencer
+byte maxProgram=0; //this holds the maximum program value entered, and is used to set the maxium sequence step.
+uint32_t maxDel = 0; //used fro maximum delay on state inside sequencing - AB addition
+// byte stepMode = 1; //1 = waits for TTL IN, 2=runs continually
+// unsigned long timeOut = 1000; //timeout for sequence (set to 10 seconds by default)
+// unsigned long tStart = 0; //sequence start timer
+// unsigned long trigLedTimer = 0;
+// boolean reportTime = 0;
+// boolean umArmIgnore = 0; //this handles micromanagers multiple ARM commands which are issued back-to-back on MDA acqtivation. Ignores after a short wait.
+// boolean usepwm = false;
+// byte pChannel =0; //number of channels micromanager has attempted to control
+// byte lastPT=20;
+// byte trigMode = 2; //0 = LOW 1 = HIGH 2 = RISING 3 = FALLING 4 = CHANGE
+
+bool error = false;
+
+const char* saoErrorString = "!ERROR_SAO: Format: SAOn-s n=1-16 (DAC1-16), s=value 0-65535";
+const char* sarErrorString = "!ERROR_SAR: Format: SARn-s n=1-16 (DAC=1-16), s=1:0-5V 2:0-10V 3:-5-+5V 4:-10-+10V 5:-2-+2V";
+const char* panErrorString = "!ERROR_PAN: Format: PANn n=1-16 (DAC1-16)";
+const char* paoErrorString = "!ERROR_PAO: Format: PAOn-s-01-02-0n n=1-16 (DAC1-16), s= >=0 (position), 0n=values 0-65535";
+const char* pacErrorString = "!ERROR_PAC: Format: PACn n=1-16 (DAC1-16)";
+const char* baoErrorString = "!ERROR_BAO: Format: BAOn-s-t n=1-16 (DAC1-16), s blank 0(off) or 1(on), t 0 (blank on low) or 1 (blank on high)";
+const char* povErrorString = "!ERROR_POV: Format: POVn-s-01-02-0n n=1-16 (DAC1-16), s= >=0 (position), 0n=values 0-65535";
+const char* podErrorString = "!ERROR_POD: Format: PODn-s-01-02-0n n=1-16 (DAC1-16), s= >=0 (position), 0n=values 0-4294967295";
+const char* pasErrorString = "!ERROR_PAS: Format: PASn-s-t n=1-16 (DAC1-16) s 0=stop 1=start, t=transition on falling(0) or rising(1) edge";
+const char* sdoErrorString = "!ERROR_SDO: Format: SDOn-s n=pingroup 0-1, s=value 0-255";
+const char* pdnErrorString = "!ERROR_PDN: Format: PDNn n=pingroup 0-1";
+const char* pdoErrorString = "!ERROR_PDO: Format: PDOn-s-01-02-0n n=pingroup 0-1, s=position, 0n=values 0-255";
+const char* bdoErrorString = "!ERROR_BDO: Format: BDOn-s-t n=pingroup 0-1, s blank 0(off) or 1(on), t 0 (blank on low) or 1 (blank on high)";
+const char* pdcErrorString = "!ERROR_PDC: Format: PDCn n=pinGroup(1/2)";
+const char* pdsErrorString = "!ERROR_PDS: Format: PDSn-s-t n=pinGroup(1/2) s 0=stop 1=start, t=transition on falling(0) or rising(1) edge";
+const char* sslErrorString = "!ERROR_SSL: Format: SSLn n=0 (Off) or 1 (On)";
+const char* generalErrorString = "ERROR_UNKNOWN_COMMAND";
+
+
+void setup()
+{
+ mcp.begin(0x27); //turn on MUX comms
+
+ for(int i=0;i<16;++i) { //configure MCP pins as outputs
+ mcp.pinMode(i, OUTPUT);
+ mcp.digitalWrite(i,LOW);
+ }
+ mcp.digitalWrite(pwrLed,HIGH); //indicate setup complete
+
+ //configure TTL outputs 5-12
+ mcp.digitalWrite(ttlblock2OE,LOW); //low for enable
+ mcp.digitalWrite(ttlblock2DIR,HIGH); //high to enable 3.3v -> 5V output
+
+ //configure TTL outputs 13-16 & TRIG 1-4
+ mcp.digitalWrite(ttlblock1OE,LOW); //low for enable
+ mcp.digitalWrite(ttlblock1DIR,LOW); //high to enable 3.3v -> 5V output
+ delay(10);
+ //configureTrigger(trigMode); //will attach interrupt
+ for(byte i=0;i<9;++i) { pinMode(ttl[i],OUTPUT); digitalWrite(ttl[i],LOW); } //SET OUTPUT PINS ON TTL AND CAMERA LINES
+ for(byte i=9;i<16;++i) {
+ mcp.pinMode(ttl[i],OUTPUT);
+ delay(5);
+ mcp.digitalWrite(ttl[i],LOW);
+ delay(10);
+ } //SET OUTPUT PINS ON TTL AND CAMERA LINES
+
+ Serial.begin(115200); // start serial @ 115,200 baud
+ while (!Serial) { ; } // wait for serial port
+
+ //read from SD card
+ //Serial.print("Reading Settings...");
+ if (!SD.begin(chipSelect)) {
+ //Serial.println("SD Read Failure. Contact ARC");
+ return;
+ }
+ myFile = SD.open("tgs.txt", FILE_WRITE);
+ if (myFile) {
+ //Serial.print("Writing to test.txt...");
+ myFile.println("testing 1, 2, 3.");
+ myFile.close();
+ Serial.println("done.");
+ }
+ else {
+ // if the file didn't open, print an error:
+ //Serial.println("SD Read Failure. Contact ARC");
+ }
+
+ /***Dac startup ***/
+ pinMode(9,OUTPUT); //CLR pin must stay high!
+ digitalWrite(9,LOW); //CLR Stays high ALWAYAS
+ delay(50);
+ digitalWrite(9,HIGH); //CLR Stays high ALWAYAS
+ delay(50);
+
+ SPI.begin();
+ pinMode(10,OUTPUT); // DAC CS
+ SPI.beginTransaction(SPISettings(30000000, MSBFIRST, SPI_MODE0)); //teensy can do 30000000 !!
+
+ //Drive All DACs & TTLs to 0
+ for(int i=1;i<=16;++i) {
+ setTTL(i,0);
+ setDac(i, 0);
+ }
+ opMode=3; //HYBRID mode default HYBRID=3 / MANUAL=0 /
+ delay(100);
+ Serial.println(idname); //issue identifier so software knows we are running
+ // configureTrigger(trigMode); //will attach interrupt
+ mcp.digitalWrite(15,HIGH); //indicate setup complete
+ triggerPinState = digitalReadFast(trig[0]);
+
+}
+
+void loop()
+{
+ //************************ DEVICE CONTROL & COMMAND CODE ***********************//
+ //************************ SERIAL COMMUNICATION CODE ******************///
+ /*
+ if(inTrigger && reportTime) //reports time delay from previous interrupt
+ {
+ Serial.print("@TIME =");
+ Serial.println(trigInterval);
+ inTrigger = false;
+ }
+ */
+ for (byte i = 0; i < NR_DACS; i++) // investigates if an overdrive or delay timer is enabled
+ {
+ if (podExpire[i] != 0)
+ { // delay is still on
+ if (podExpire[i] < micros())
+ { // time has expired
+ setDac(i, lastDac[i]); //issue normal on state value
+ podExpire[i] = 0; //remove exire timer
+ }
+ }
+ }
+ if (triggerPinState != digitalReadFast(trig[0]))
+ {
+ triggerPinState = ! triggerPinState;
+ if (useSignalLEDs_)
+ {
+ digitalWriteDirect(trigLed, triggerPinState);
+ }
+ for (byte i = 0; i < NR_DACS; i++) // todo: optimize by ordering an array with sequenceable DACS and only cycle through those
+ {
+ if (dacSequencing[i])
+ {
+ if (dacSequenceMode[i] == triggerPinState)
+ {
+ dacState[i] = dacArray[dacArrayIndex[i]][i];
+ dacArrayIndex[i]++;
+
+ if (dacArrayIndex[i] == dacArrayMaxIndex[i]) { dacArrayIndex[i] = 0; }
+ if (!dacBlanking[i])
+ {
+ if(povArray[dacArrayIndex[i]][i] ) //if pov has an overdrive value
+ {
+ setDac(i, povArray[dacArrayIndex[i]][i]); //issue overdrive
+ podExpire[i] = micros() + podArray[dacArrayIndex[i]][i]; //set disable timer
+ lastDac[i] = dacState[i]; //save the normal dac output value for later update
+ }
+ else{
+ setDac(i, dacState[i]);
+ }
+ }
+
+ }
+ }
+ if (dacBlanking[i]) {
+ dacBlankOnLow[i] == triggerPinState ? setDac(i, dacState[i]) : setDac(i, 0);
+ }
+ }
+
+ for (byte i = 0; i < 2; i++)
+ {
+ if (pinGroupSequencing[i])
+ {
+ if (pinGroupSequenceMode[i] == triggerPinState)
+ {
+ pinGroupState[i] = ttlArray[ttlArrayIndex[i]][i];
+ ttlArrayIndex[i]++;
+ if (ttlArrayIndex[i] == ttlArrayMaxIndex[i]) { ttlArrayIndex[i] = 0; }
+ if (!pinGroupBlanking[i])
+ {
+ setPinGroup(i, pinGroupState[i]);
+ } // if we are blanking, fall through to the code below to set
+ }
+ }
+ if (pinGroupBlanking[i])
+ {
+ pinGroupBlankOnLow[i] == triggerPinState ? setPinGroup(i, pinGroupState[i]) :
+ setPinGroup(i, 0);
+ }
+ }
+ //delayMicroseconds(maxDel); //AB addition
+ for (byte i = 0; i < NR_DACS; i++) // todo: optimize by ordering an array with sequenceable DACS and only cycle through those
+ {
+ if (dacSequencing[i])
+ {
+ if(dacBlankDuration[i] > 0){ //AB addition
+ setDac(i, 0);
+ }
+ }
+ }
+
+ }
+
+ if (stringComplete) // There is a new line terminated string ready to be processed
+ {
+ digitalWrite(readyLed,LOW);
+
+ String command = inputString.substring(0,3);
+
+ if (inputString == "?\n")
+ {
+ Serial.println(helpString);
+ }
+
+ else if(inputString == "*\n")
+ {
+ Serial.println(idname);
+ } //return ID line if queried
+
+ // SAO command - Set DAC
+ // Expected format: "SAO1-4236" where 1 is the line nr, and 4236 the new value to be send to the DAC
+ else if(command == "SAO")
+ {
+ error = false;
+ byte dacNum = 0; // initialize to error conditino
+ int offset = 5;
+ if (inputString[4] == sep)
+ {
+ dacNum = atoi(&inputString[3]);
+ } else if (inputString[5] == sep)
+ {
+ dacNum = atoi(inputString.substring(3,5).c_str());
+ offset++;
+ } else
+ {
+ error = true;
+ }
+ if (dacNum < 1 || dacNum > 16)
+ {
+ error = true;
+ }
+
+ int value = atoi(inputString.substring(offset).c_str());
+ if (value < 0 || value > 65535)
+ {
+ error = true;
+ }
+
+ if (!error) //if inputs are valid
+ {
+ Serial.print("!SAO"); //print recieve message to operator
+ Serial.print(dacNum); //print recieve message to operator
+ Serial.print(sep);
+ Serial.println(value);
+ dacState[dacNum - 1] = value;
+ setDacCheckBlanking(dacNum - 1);
+ } else
+ {
+ Serial.println(saoErrorString);
+ }
+ }
+
+ // PAN -
+ // Queries the number of analog output states that can be pre-programmed.
+ // Format: "PANn", n is 1-6 for DAC1-16.
+ // Returns: "!PANn-q" where q is the maximum number of states that can be programmed
+ else if (command == "PAN")
+ {
+ byte dac = atoi(&inputString[3]);
+ if (dac < 1 || dac > 16)
+ {
+ Serial.println(panErrorString);
+ } else
+ {
+ Serial.print("!PAN");
+ Serial.print(dac);
+ Serial.print(sep);
+ Serial.println(NR_DAC_STATES);
+ }
+ }
+
+ /*
+ *
+ *"PAO" - Sends sequence of analog output states to be used in triggered sequences.
+ Format: "PAOn,s,o1,o2,on", where
+ - n=1-16 for DACS1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (0-65535) to be consecutively inserted in the list.
+ Returns: "!PA0n,s,q" where q is the number of values successfully inserted in the internal buffer.
+ */
+ else if (command == "PAO")
+ {
+ error = false;
+ int n = 0;
+ int s = 0;
+ int dacNr = 0;
+ unsigned int scp = 5;
+ if (inputString[4] == sep) { dacNr = inputString.substring(3,4).toInt(); }
+ else if (inputString[5] == sep){ dacNr = inputString.substring(3,5).toInt(); scp = 6; }
+ else { error = true; }
+ if (dacNr < 1 || dacNr > 16) { error = true; }
+ unsigned int ecp = scp + 1;
+ if (!error)
+ {
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if (ecp < inputString.length()) { s = inputString.substring(scp, ecp).toInt(); }
+ else { error = true; }
+ while (!error && ecp < inputString.length())
+ {
+ scp = ecp;
+ ecp++;
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if ( (ecp - scp) > 1)
+ {
+ int val = inputString.substring(scp, ecp).toInt();
+ if (val < 0 || val > 65535)
+ {
+ error = true;
+ } else
+ {
+ dacArray[n + s][dacNr-1] = val;
+ n++;
+ int index = n+s;
+ if (index > dacArrayMaxIndex[dacNr-1])
+ {
+ dacArrayMaxIndex[dacNr-1] = index;
+ }
+ }
+ }
+ }
+ }
+
+ if (!error)
+ {
+ char out[20];
+ sprintf(out, "!PAO%d%c%d%c%d%c%d", dacNr, sep, s, sep, n, sep, dacArrayMaxIndex[dacNr-1]);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(paoErrorString);
+ }
+ }
+
+
+ /*
+ * "PAC" - Clears the sequence of ananlog states
+ Format: "PACn", where n is the DAC pinNr (1-16)
+ */
+ else if (command == "PAC")
+ {
+ error = false;
+ if (inputString.length() == 5 || inputString.length() == 6)
+ {
+ int dacNr = inputString.substring(3).toInt();
+ if (dacNr < 1 || dacNr > 16) { error = true; }
+ if (!error)
+ {
+ dacSequencing[dacNr - 1] = false;
+ dacArrayIndex[dacNr - 1] = 0;
+ dacArrayMaxIndex[dacNr - 1] = 0;
+ dacBlankDelay[dacNr - 1] = 0;
+ dacBlankDuration[dacNr - 1] = 0;
+ clearPAO(dacNr - 1);
+ clearPOD(dacNr - 1);//addition for overdrive
+ clearPOV(dacNr - 1);//addition for overdrive
+ for ( byte d = 0;d 16 || inputString[scp] != sep)
+ {
+ error = true;
+ }
+ int state = inputString.substring(scp+1,scp+2).toInt();
+ if (state < 0 || state > 1) { error = true; }
+ int rising = inputString.substring(scp+3).toInt();
+ if (rising < 0 || rising > 1) { error = true; }
+ if (!error)
+ {
+ dacSequencing[dacNr - 1] = (boolean) state;
+ dacSequenceMode[dacNr - 1] = rising;
+ if (state)
+ {
+ dacStoredState[dacNr - 1] = dacState[dacNr - 1];
+ dacArrayIndex[dacNr - 1] = 0;
+ if (!rising) { // if we trigger on the falling edge, set initial state now, and advance counter here
+ setDac(dacNr -1, dacArray[dacArrayIndex[dacNr - 1]][dacNr - 1]); // Check blanking?
+ dacArrayIndex[dacNr - 1]++;
+ }
+ } else
+ {
+ dacState[dacNr - 1] = dacStoredState[dacNr - 1];
+ }
+ char out[20];
+ sprintf(out, "!PAS%d%c%d%c%d", dacNr, sep, state, sep, rising);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(pasErrorString);
+ }
+
+ }
+
+
+
+
+ /*
+ *
+ *"POV" - Sends sequence of analog overdrive states to be used in triggered sequences.
+ Format: "POVn,s,o1,o2,on", where
+ - n=1-16 for DACS1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (0-65535) to be consecutively inserted in the list.
+ Returns: "!POVn,s,q" where q is the number of values successfully inserted in the internal buffer.
+ */
+ else if (command == "POV")
+ {
+ error = false;
+ int n = 0;
+ int s = 0;
+ int dacNr = 0;
+ unsigned int scp = 5;
+ if (inputString[4] == sep) { dacNr = inputString.substring(3,4).toInt(); }
+ else if (inputString[5] == sep){ dacNr = inputString.substring(3,5).toInt(); scp = 6; }
+ else { error = true; }
+ if (dacNr < 1 || dacNr > 16) { error = true; }
+ unsigned int ecp = scp + 1;
+ if (!error)
+ {
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if (ecp < inputString.length()) { s = inputString.substring(scp, ecp).toInt(); }
+ else { error = true; }
+ while (!error && ecp < inputString.length())
+ {
+ scp = ecp;
+ ecp++;
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if ( (ecp - scp) > 1)
+ {
+ int val = inputString.substring(scp, ecp).toInt();
+ if (val < 0 || val > 65535)
+ {
+ error = true;
+ } else
+ {
+ povArray[n + s][dacNr-1] = val;
+ n++;
+ int index = n+s;
+ if (index > dacArrayMaxIndex[dacNr-1])
+ {
+ dacArrayMaxIndex[dacNr-1] = index;
+ }
+ }
+ }
+ }
+ }
+
+ if (!error)
+ {
+ char out[20];
+ sprintf(out, "!POV%d%c%d%c%d%c%d", dacNr, sep, s, sep, n, sep, dacArrayMaxIndex[dacNr-1]);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(povErrorString);
+ }
+ }
+
+ /*
+ *
+ *"POD" - Sends sequence of delay times for anaog overdrive staates specified in POV.
+ Format: "PODn,s,o1,o2,on", where
+ - n=1-16 for DACS1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (uInt32_t) to be consecutively inserted in the list.
+ Returns: "!PODn,s,q" where q is the number of values successfully inserted in the internal buffer.
+ */
+ else if (command == "POD")
+ {
+ error = false;
+ int n = 0;
+ int s = 0;
+ int dacNr = 0;
+ unsigned int scp = 5;
+ if (inputString[4] == sep) { dacNr = inputString.substring(3,4).toInt(); }
+ else if (inputString[5] == sep){ dacNr = inputString.substring(3,5).toInt(); scp = 6; }
+ else { error = true; }
+ if (dacNr < 1 || dacNr > 16) { error = true; }
+ unsigned int ecp = scp + 1;
+ if (!error)
+ {
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if (ecp < inputString.length()) { s = inputString.substring(scp, ecp).toInt(); }
+ else { error = true; }
+ while (!error && ecp < inputString.length())
+ {
+ scp = ecp;
+ ecp++;
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if ( (ecp - scp) > 1)
+ {
+ int val = inputString.substring(scp, ecp).toInt();
+ if (val < 0 || val > 65535)
+ {
+ error = true;
+ } else
+ {
+ podArray[n + s][dacNr-1] = val;
+ n++;
+ int index = n+s;
+ if (index > dacArrayMaxIndex[dacNr-1])
+ {
+ dacArrayMaxIndex[dacNr-1] = index;
+ }
+ }
+ }
+ }
+ }
+
+ if (!error)
+ {
+ char out[20];
+ sprintf(out, "!POD%d%c%d%c%d%c%d", dacNr, sep, s, sep, n, sep, dacArrayMaxIndex[dacNr-1]);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(podErrorString);
+ }
+ }
+
+
+
+ /*
+ "BAO" - Activates blanking mode of the analog output. Output state will be coupled to the
+ state of the input trigger. If input trigger is active, the DAC will be active (
+ as set with SDA), if input trigger is inactive, the output will go (which may not be 0V, depending on the range).
+ Format: "BAOn,s,t"
+ - n=1-16 for DACS1-16
+ - s=0 or 1 where 0 stops and 1 starts balnking mode
+ - t translates state of the input pin to its activity for blanking. If t = 0,
+ DAC will be 0 when input trigger pin is low. t = 1: DAC will be active when input trigger is low.
+ */
+ else if (command == "BAO")
+ {
+ error = false;
+ int dacNr = 0;
+ int scp = 5;
+ if (inputString[4] == sep) { dacNr = inputString.substring(3,4).toInt(); }
+ else if (inputString[5] == sep) { dacNr = inputString.substring(3,5).toInt(); scp = 6; }
+ else { error = true; }
+ if (dacNr < 1 || dacNr > 16){ error = true; }
+ int state = inputString.substring(scp, scp+1).toInt();
+ if (state < 0 || state > 1) { error = true; }
+ if (inputString[scp+1] != sep) { error = true; }
+ int mode = inputString.substring(scp+2).toInt();
+ if (mode < 0 || mode > 1) { error = true; }
+ if (!error)
+ {
+ dacBlanking[dacNr - 1] = state == 1;
+ dacBlankOnLow[dacNr - 1] = mode == 0;
+ setDacCheckBlanking(dacNr - 1);
+ char out[20];
+ sprintf(out, "!BAO%d%c%d%c%d", dacNr, sep, state, sep, mode);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(baoErrorString);
+ }
+ }
+
+
+ // SDO
+ // sets TTL outputs of pins1-8 and 9-16 in a single operation
+ // Format: SDOn,o
+ // Where n=0 or 1. 0 address pins 1-8, 1 goes to pins 9-16
+ // 0 is digital output bitmask, 0: all off, 1: pin 1 on, 2: pin 2 on, 3: pins 1&2 on, etc..
+ // Example: DO1,4 sets pin 3 high and pins 1,2, 4-8 low
+ else if (command == "SDO")
+ {
+ byte pinGroup = atoi(&inputString[3]);
+ if (pinGroup < 0 || pinGroup > 1)
+ {
+ Serial.println(sdoErrorString);
+ } else
+ {
+ int value = atoi(inputString.substring(5).c_str());
+ if (value < 0 || value > 255)
+ {
+ Serial.println(sdoErrorString);
+ } else
+ {
+ Serial.print("!SDO");
+ Serial.print(pinGroup);
+ Serial.print(sep);
+ Serial.println(value);
+ pinGroupState[pinGroup] = value;
+ setPinGroupCheckBlanking(pinGroup);
+ }
+ }
+ }
+
+ // PDN -
+ // Queries the number of digital output states that can be pre-programmed.
+ // Format: "PDNn".
+ // Returns: "!PDNn,q" where q is the maximum number of states that can be programmed
+ else if (command == "PDN")
+ {
+ byte pinGroup = atoi(&inputString[3]);
+ if (pinGroup < 0 || pinGroup > 1)
+ {
+ Serial.println(pdnErrorString);
+ } else
+ {
+ Serial.print("!PDN");
+ Serial.print(pinGroup);
+ Serial.print(sep);
+ Serial.println(NR_DO_STATES);
+ }
+ }
+
+ //"PDO" - Sends sequence of digital output states to be used in triggered sequences.
+ // Format: "PDOn-s-o1-o2-on", where
+ // - n=1 or 2 and directs the command to eitherpins1-8 or 9-16,
+ // - s: position in the sequence to start inserting values. First position is 1.
+ // - o1, o2, etc... values to be consecutively inserted in the list.
+ // Returns: "!PD0n,s,q" where q is the number of values successfully inserted in the internal buffer.
+ else if (command == "PDO")
+ {
+ error = false;
+ int s = 0;
+ int n = 0;
+ int pinGroup = inputString.substring(3,4).toInt();
+ if (pinGroup < 0 || pinGroup > 1)
+ {
+ error = true;
+ }
+ if (inputString[4] != sep)
+ {
+ error = true;
+ }
+ unsigned int scp = 5;
+ unsigned int ecp = 6;
+ if (!error)
+ {
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if (ecp < inputString.length())
+ {
+ s = inputString.substring(scp, ecp).toInt();
+ }
+ else
+ {
+ error = true;
+ }
+ while (!error && ecp < inputString.length())
+ {
+ scp = ecp;
+ ecp++;
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if ( (ecp - scp) > 1)
+ {
+ int val = inputString.substring(scp, ecp).toInt();
+ if (val < 0 || val > 255)
+ {
+ error = true;
+ } else
+ {
+ ttlArray[n + s][pinGroup] = (byte) val;
+ n++;
+ int index = n+s;
+ if (index > ttlArrayMaxIndex[pinGroup])
+ {
+ ttlArrayMaxIndex[pinGroup] = index;
+ }
+ }
+ }
+ }
+ }
+
+ if (!error)
+ {
+ char out[20];
+ sprintf(out, "!PDO%d%c%d%c%d%c%d", pinGroup, sep, s, sep, n, sep, ttlArrayMaxIndex[pinGroup]);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(pdoErrorString);
+ }
+ }
+
+ /*
+ * "PDC" - Clears the sequence of digital states
+ Format: "PDCn", where n is the pingroup, 0=pins1-8, 1=pins9-16
+ */
+ else if (command == "PDC")
+ {
+ error = false;
+ if (inputString.length() == 5)
+ {
+ int pinGroup = inputString.substring(3,4).toInt();
+ if (pinGroup < 0 || pinGroup > 1) { error = true; }
+ if (!error)
+ {
+ pinGroupSequencing[pinGroup] = false;
+ ttlArrayIndex[pinGroup] = 0;
+ ttlArrayMaxIndex[pinGroup] = 0;
+ clearPDO(pinGroup);
+ char out[20];
+ sprintf(out, "!PDC%d", pinGroup);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(pdcErrorString);
+ }
+ }
+
+ /*
+ "PDS" - Starts and stops triggered transitions in digital output state as pre-loaded in PDO
+ Format: "PDSn,s,t" where
+ - n=0 or 1 and directs the command to either pins 1-8 or 9-16
+ = s=0 or 1 where 0 stops and 1 starts triggered transitions
+ - t=0 or 1 and determines whether transition will happen on the falling (0) or rising (1) edge
+ of a pulse on the input pin
+ */
+ else if (command == "PDS")
+ {
+ error = false;
+ if (inputString.length() == 9)
+ {
+ int pinGroup = inputString.substring(3,4).toInt();
+ if (pinGroup < 0 || pinGroup > 1) { error = true; }
+ int state = inputString.substring(5,6).toInt();
+ if (state < 0 || state > 1) { error = true; }
+ int rising = inputString.substring(7,8).toInt();
+ if (rising < 0 || rising > 1) { error = true; }
+ if (!error)
+ {
+ pinGroupSequencing[pinGroup] = (boolean) state;
+ pinGroupSequenceMode[pinGroup] = rising;
+ if (state)
+ {
+ pinGroupStoredState[pinGroup] = pinGroupState[pinGroup];
+ ttlArrayIndex[pinGroup] = 0;
+ if (!rising)
+ { // if we trigger on the falling edge, set initial state now, and advance counter here
+ setPinGroup(pinGroup, ttlArray[ttlArrayIndex[pinGroup]][pinGroup]); // Check blanking?
+ ttlArrayIndex[pinGroup]++;
+ }
+ } else
+ {
+ pinGroupState[pinGroup] = pinGroupStoredState[pinGroup];
+ }
+ char out[20];
+ sprintf(out, "!PDS%d%c%d%c%d", pinGroup, sep, state, sep, rising);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(pdsErrorString);
+ }
+ }
+
+ /**
+ * "BDO" - Activates blanking mode of the digital output. Output state will be coupled to the
+ * state of the input trigger. If input trigger is active, the pingroup will be active (
+ * as set with DO), if input trigger is inactive, the pingroup will go low.
+ * Format: "BDOn,s,t" where n is the pingroup (0 or 1), s switches blanking mode on (1)
+ * or off (0), and t sets the mode (0 blank on low, 1 blank on high)
+ */
+ else if (command == "BDO")
+ {
+ error = false;
+ if (inputString.length() == 9)
+ {
+ int pinGroup = inputString.substring(3,4).toInt();
+ if (pinGroup < 0 || pinGroup > 1) { error = true; }
+ byte state = inputString.substring(5,6).toInt();
+ if (state < 0 || state > 1) { error = true; }
+ byte mode = inputString.substring(7,8).toInt();
+ if (mode < 0 || mode > 1) { error = true; }
+ if (!error)
+ {
+ pinGroupBlanking[pinGroup] = state == 1;
+ pinGroupBlankOnLow[pinGroup] = mode == 0;
+ setPinGroupCheckBlanking(pinGroup);
+ char out[20];
+ sprintf(out, "!BDO%d%c%d%c%d", pinGroup, sep, state, sep, mode);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(bdoErrorString);
+ }
+ }
+
+ /**
+ * Sets voltage range of DACS
+ */
+ else if(command == "SAR")
+ {
+ error = false;
+ byte dacNum = inputString.substring(3).toInt();
+ byte pp = 5;
+ if(dacNum >9)
+ {
+ pp=6;
+ }
+ byte rangeVal = inputString.substring(pp).toInt();
+ if(rangeVal < 1 || rangeVal > 5)
+ {
+ error = true;
+ } //force to max range
+ if(dacNum < 1 || dacNum > 16)
+ {
+ error = true;
+ } //confirm if input channel range is valid
+ if(!error) //if range is OK perform command
+ {
+ Serial.print("!SAR"); //print recieve message to operator
+ Serial.print(dacNum);
+ Serial.print(sep);
+ Serial.println(rangeVal);
+ setDacRange(dacNum-1,rangeVal-1);
+ // 0 the output
+ int value = 0;
+ if (rangeVal > 2)
+ {
+ value = 65535 / 2;
+ }
+ dacState[dacNum - 1] = value;
+ setDac(dacNum - 1, value);
+ } else
+ {
+ Serial.println(sarErrorString);
+ }
+ }
+
+ // Set Signal LED flag
+ else if (command == "SSL")
+ {
+ error = false;
+ byte result = inputString.substring(3).toInt();;
+ if (result == 0)
+ {
+ useSignalLEDs_ = false;
+ digitalWrite(dacLed, 0);
+ digitalWrite(ttlLed, 0);
+ digitalWriteDirect(trigLed,0);
+ }
+ else if (result == 1)
+ {
+ useSignalLEDs_ = true;
+ // TODO: make sure the LEDs are set correctly?
+ }
+ else error = true;
+ if (!error)
+ {
+ Serial.print("!SSL");
+ Serial.println(result);
+ } else
+ {
+ Serial.println(sslErrorString);
+ }
+ }
+
+ //status commands
+ else if(inputString == "STAT?\n") {debug(); }
+ else if(inputString == "TEST?\n") {diagTest(); }
+ else if(inputString == "CLEAR_ALL\n") {clearTable(); }
+ else if(inputString.substring(0,9) == "CLEAR_DAC") {clearDac(); }
+
+ else
+ {
+ Serial.println(generalErrorString);
+ }
+
+
+ clearSerial();
+ mcp.digitalWrite(readyLed,HIGH);
+ } //EXIT LOOP FOR SERIAL HERE
+
+
+/*********************This block runs the high speed control interface *********************/
+
+/****checks the acquisition order
+ * mode 0 == channel first eg set ch1 stweep Z
+ * mode 1 == Z first EG step Z then Ch1 Ch2 then Step Z ...
+ */
+
+/*
+ while(trigArmed)
+ { // just sit here and wait for the next command until armed is off, which can only happen @ end of sequence
+ unsigned long tStart = millis() + timeOut; //set timeout position
+ unsigned long tLed = 0;
+ // focus commands = start, step, #loops,direction,slave,current step
+ //if(program == 0) {inTrigger=true; } //force a first step
+
+ if( inTrigger )
+ { //we recieved a trigger from our source
+ // and is mode 0, and is at 0 position OR
+ // and is mode 1, and is at any position OR
+ // if focus isn't enabled
+ */
+ /*
+ * When should a channel update be issued? When
+ * focus is ON, and mode is sweep per channel (0) AND F position is 0
+ * focus is ON, and mode is slave to channel (1)
+ * focus is off any time
+ */
+
+ // boolean runUpdate = true;
+ /*
+ if( (focArray[1] != 0) && (focArray[4] == 0) ){ //if we are using the focus and focus will sweep through a single channel setting
+ if( focArray[5] == 0 ) { runUpdate = true;} //AND if the focus is at position 0 (start of sweep)
+ }
+
+ if( (focArray[1] !=0) && (focArray[4] == 1)) { runUpdate = true; } // Case where channel is switching each Z plane
+ if(focArray[1] == 0) {runUpdate=true;} //Case where no focus block used so update the channel
+ */
+ /*
+ //do DAC and TTL control stuff, not focus stuff though
+ if(runUpdate) {
+ byte walker=0;
+ for(walker = 0 ; walker < 15 ; ++walker ){ //sets DACs 1-16
+ dac_write(10,0, DAC [walker], dacArray [program] [walker]); // Set DAC Lines
+ digitalWriteDirect( ttl [walker] , ttlArray[program] [walker] ); //set TTL lines
+ }
+ digitalWriteDirect( ttl [walker] , ttlArray[program] [walker] ); //set 16th TTL line -
+ }
+ ++program;
+*/
+ /* THIS MOVES AROUND THE Z FOCUS
+ * in this case, we assume a trigger was recieved, but we should only mess with focus stuff if it's on and if it's needed
+ * here we only want to update the focus position in the case that EITHER
+ * Mode = 0 Channel 1 - Z 1, Z2, Z3
+ * mode = 1 Z 1 - CH1, CH2, Z2, Ch1, Ch2
+ */
+/*
+ if( (focArray[1] != 0) && ( focArray[4]==0 )){ fastFocus(); } // if any focus array is active, and MODE = SWEEP OVER CHANNEL
+ if( (focArray[1] != 0) && ( focArray[4]==1 )){ // in this case sweep is active and MODE = STEP AFTER CHANNEL
+ if((program == maxProgram-1) && (focArray[5] <= focArray[2])) {fastFocus();}
+ }
+
+ delay(delArray[program]); //wait for specified delay
+
+ if( focArray[1] == 0) {++program;} //if not using focus at all
+ if( (focArray[1] != 0) && (focArray[4] == 1) ) { //focus is used but in step mode
+ ++program;
+ if( (program > maxProgram) && (focArray[5] != 0) ) { //because we are stepping the focus, program must be reset to 0 in this case we know the focus has not completed, so we can reset the main program array position
+ program=0;
+ }
+ }
+ */
+ } //END OF TRIGGER RESPONSE
+
+ /*
+ inTrigger=false; //turn off trigger
+ tStart = millis() + timeOut; //update timeout delta for next run
+
+ //Done moving stuff, now wait for the next input
+ while(!inTrigger){ //wait for next pulse
+ if(millis() > tStart) {
+ trigArmed=false;
+ program = maxProgram+1;
+ Serial.println("Timeout Exceeded");
+ allOff();
+ break;
+ }
+ //we hit the timeout so end the sequence
+ if(tLed < millis() )
+ {
+ digitalWrite(readyLed,!digitalRead(readyLed));
+ tLed = millis() + 30;
+ }
+ }
+
+ if(program > maxProgram) { //end and cleanup
+ ++runCycles;
+ if(runCycles == timeCycles) { //we are done
+ trigArmed=false;
+ program=0;
+ allOff();
+ Serial.println("!SAFE");
+ digitalWrite(readyLed,HIGH);
+ }
+ if(runCycles < timeCycles) {program = 0;}
+ }
+ } //close trigarmed
+} //close main loop
+*/
+
+
+
+
+void allOff()
+{
+ for(byte walker=0;walker<15;++walker)
+ { //sets DACs 1-16
+ dac_write(10,0, DAC [walker], 0); // Set DAC Lines
+ }
+ setPinGroup(0,0);
+ setPinGroup(1,0);
+}
+
+
+void clearSerial()
+{
+ //STUFF TO CLEAN OUT THE SERIAL LINE
+ inputString = ""; // clear the string:
+ stringComplete = false;
+}
+
+/*PC DAC CONTROL*/
+void setDac(byte dNum,int dVal)
+{
+ dac_write(10,0, dNum, dVal); // Send dac_code
+ //led indication
+ if (useSignalLEDs_)
+ {
+ int dacSum = 0;
+ for (byte d=0; d < 16; d++)
+ {
+ dacSum += dacState[d];
+ }
+ mcp.digitalWrite(dacLed, dacSum > 0);
+ }
+}
+
+// sets the output state of the given pin number
+void setTTL(byte t1, boolean t2)
+{
+ byte pin = t1 - 1;
+ if(pin < 9) {
+ digitalWriteFast(ttl[pin], t2);
+ }
+ if(pin > 8) {
+ mcp.digitalWrite(ttl[pin], t2);
+ }
+ if(t2)
+ {
+ bitSet(ttlState, pin);
+ } else
+ {
+ bitClear(ttlState, pin);
+ }
+ if (useSignalLEDs_)
+ {
+ mcp.digitalWrite(ttlLed, ttlState > 0);
+ }
+}
+
+void setDacCheckBlanking(byte dacNr)
+{
+ if (dacBlanking[dacNr])
+ {
+ triggerPinState = digitalReadFast(trig[0]);
+ dacBlankOnLow[dacNr] == triggerPinState ?
+ setDac(dacNr, dacState[dacNr]) : setDac(dacNr, 0);
+ } else {
+ setDac(dacNr, dacState[dacNr]);
+ }
+}
+
+
+void setPinGroupCheckBlanking(byte pinGroup)
+{
+ if (pinGroupBlanking[pinGroup])
+ {
+ triggerPinState = digitalReadFast(trig[0]);
+ pinGroupBlankOnLow[pinGroup] == triggerPinState ?
+ setPinGroup(pinGroup, pinGroupState[pinGroup]) : setPinGroup(pinGroup, 0);
+ } else {
+ setPinGroup(pinGroup, pinGroupState[pinGroup]);
+ }
+}
+
+inline void setPinGroup(byte, byte) __attribute__((always_inline));
+// sets pins 1-8 (pinGroup 0) or 9-16 (pinGroup 1) according to the mask in value
+inline void setPinGroup(byte pinGroup, byte value)
+{
+ byte adder = (pinGroup) * 8;
+ for (byte b = 0; b < 8; b++)
+ {
+ if( b+adder < 9 ) {
+ digitalWriteFast(ttl[b + adder], (((value) >> (b)) & 0x01) );
+ }
+
+ else if( b+adder > 8) {
+ mcp.digitalWrite(ttl[b+adder],(((value) >> (b)) & 0x01));
+ }
+
+ //Serial.print("TTL " );
+ //Serial.print(b + adder);
+ //Serial.print(" , ");
+ //Serial.println((((value) >> (b)) & 0x01));
+
+ }
+ if (pinGroup == 1)
+ {
+ ttlState = (ttlState & 0xff00) | value;
+ } else
+ {
+ ttlState = (ttlState & 0x00ff) | (value << 8);
+ }
+ if (useSignalLEDs_)
+ {
+ mcp.digitalWrite(ttlLed, ttlState > 0);
+ }
+}
+
+/*
+ SerialEvent occurs whenever a new data comes in the hardware serial RX. This
+ routine is run between each time loop() runs, so using delay inside loop can
+ delay response. Multiple bytes of data may be available.
+*/
+void serialEvent() {
+ trigArmed = false;
+ while (Serial.available()) {
+ // get the new byte:
+ char inChar = (char)Serial.read();
+ // add it to the inputString:
+ inputString += inChar;
+ // if the incoming character is a newline, set a flag
+ // so the main loop can do something about it:
+ if (inChar == '\n') {
+ stringComplete = true;
+ }
+ }
+}
+
+/*INTERRUPT CODE FOR TTL INPUT ***************/
+/*
+void sigIn() //sigin is the input response line - this recieves the trigger input from an external source
+{
+ //if(!trigArmed)
+ //{
+ digitalWrite(trigLed,!digitalRead(trigLed));
+ //}
+ inTrigger=true;
+}
+
+void configureTrigger(byte tOption)
+{
+ trigMode = tOption; //assign input value to global
+ switch (trigMode) {
+ case 0:
+ attachInterrupt(trig[0],sigIn,LOW);
+ break;
+ case 1:
+ attachInterrupt(trig[0],sigIn,HIGH);
+ break;
+ case 2:
+ attachInterrupt(trig[0],sigIn,RISING);
+ break;
+ case 3:
+ attachInterrupt(trig[0],sigIn,FALLING);
+ break;
+ case 4:
+ attachInterrupt(trig[0],sigIn,CHANGE);
+ break;
+ }
+}
+*/
+
+void debug()
+{
+ if(debugT < millis() ) //if this is the first time debug has run, this will execute, otherwise has enough time elapsed?
+ {
+ Serial.print("Input Trigger = ");
+ Serial.println(digitalReadFast(trig[0]));
+ //Serial.print(", OpMode = ");
+ //Serial.println(opMode);
+
+ // REPORT TTL
+ Serial.print("TTL: ");
+ for(int i=0;i<16;++i)
+ {
+ char sOut[100];
+ if(i<9) {sprintf(sOut,"%d=%d,",i+1, digitalRead(ttl[i]));}
+ else if(i>8) {sprintf(sOut,"%d=%d,",i+1, mcp.digitalRead(ttl[i]));}
+ Serial.print(sOut);
+ }
+ Serial.println("");
+
+ //REPORT DAC
+ Serial.print("DAC:");
+ for(int i=0;i<16;++i)
+ {
+ char sOut[200];
+ sprintf(sOut,"%d=%u,",i+1, dacState[i]);
+ Serial.print(sOut); //used to print sOut
+ }
+
+ Serial.println();
+ Serial.println("Digital output buffers:");
+ for (int pg = 0; pg < 2; pg++)
+ {
+ if (pg == 0) Serial.print("Pins 1-8 buffer size 1-8: ");
+ else if (pg == 1) Serial.print("Pins 9-16 buffer size: ");
+ char sOut[6];
+ sprintf(sOut, "%d", ttlArrayMaxIndex[pg]);
+ Serial.println(sOut);
+ for (int i = 0; i < ttlArrayMaxIndex[pg]; i++)
+ {
+ sprintf(sOut, "%u ", ttlArray[i][pg]);
+ Serial.print(sOut);
+ }
+ if (ttlArrayMaxIndex[pg] > 0) { Serial.println(); }
+ }
+
+ Serial.println("DAC buffers:");
+ for (int dac = 0; dac < NR_DACS; dac++)
+ {
+ char sOut[25];
+ sprintf(sOut, "DAC buffer size: %d", dacArrayMaxIndex[dac]);
+ Serial.println(sOut);
+ for (int i = 0; i < dacArrayMaxIndex[dac]; i++)
+ {
+ sprintf(sOut, "%u ", dacArray[i][dac]);
+ Serial.print(sOut);
+ }
+ if (dacArrayMaxIndex[dac] > 0) { Serial.println(); }
+ }
+
+ }
+ // send an empty line so that the receiver know we are done
+ Serial.println("");
+}
+
+ /*
+ //Report program arrays
+ if(opMode == 3)
+ {
+ Serial.println("Sequencer Programming Status");
+ Serial.print("MaxProgram = ");
+ Serial.println(maxProgram);
+ Serial.print("PCHANNEL = ");
+ Serial.println(pChannel);
+ //report DACs
+ Serial.println("PROG, DAC1, DAC2, DAC3, DAC4, DAC5, DAC6, DAC7, DAC8, DAC9,DAC10,DAC11,DAC12,DAC13,DAC14,DAC15,DAC16/FOCUS");
+ for(int p=0;p= 0; i--)
+ rx[i] = SPI.transfer(tx[i]); //! 2) Read and send byte array
+
+ output_high(cs_pin); //! 3) Pull CS high
+}
+
+inline void digitalWriteDirect(int, boolean) __attribute__((always_inline));
+
+void digitalWriteDirect(int pin, boolean val){
+ if(pin < 9) {digitalWriteFast(pin,val);}
+ else if(pin>8) {mcp.digitalWrite(pin,val);}
+}
+
+/*
+inline int digitalReadDirect(int) __attribute__((always_inline));
+
+int digitalReadDirect(int pin){
+ return !!(g_APinDescription[pin].pPort -> PIO_PDSR & g_APinDescription[pin].ulPin);
+}
+
+inline int digitalReadOutputPin(int) __attribute__((always_inline));
+
+int digitalReadOutputPin(int pin){
+ return !!(g_APinDescription[pin].pPort -> PIO_ODSR & g_APinDescription[pin].ulPin);
+}
+*/
+
+
+
+/*******************LICENSING INFO**************
+
+ * Copyright (c) 2018, Advanced Research Consulting
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ *
+ *******************/
\ No newline at end of file
diff --git a/src/Overdrive/TriggerScope_V4B/Linduino.h b/src/Overdrive/TriggerScope_V4B/Linduino.h
new file mode 100644
index 0000000..85ee651
--- /dev/null
+++ b/src/Overdrive/TriggerScope_V4B/Linduino.h
@@ -0,0 +1,109 @@
+//! @todo Review this file.
+/*
+Linduino.h
+
+This file contains the hardware definitions for the Linduino.
+
+REVISION HISTORY
+$Revision: 1906 $
+$Date: 2013-08-26 15:09:18 -0700 (Mon, 26 Aug 2013) $
+
+Copyright (c) 2013, Linear Technology Corp.(LTC)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of Linear Technology Corp.
+
+The Linear Technology Linduino is not affiliated with the official Arduino team.
+However, the Linduino is only possible because of the Arduino team's commitment
+to the open-source community. Please, visit http://www.arduino.cc and
+http://store.arduino.cc , and consider a purchase that will help fund their
+ongoing work.
+*/
+
+//! @defgroup Linduino Linduino: Linear Technology Arduino-Compatible Demonstration Board
+
+/*! @file
+ @ingroup Linduino
+ @ingroup QuikEval
+ Header File for Linduino Libraries and Demo Code
+*/
+
+#ifndef LINDUINO_H
+#define LINDUINO_H
+
+//! @name LINDUINO PIN ASSIGNMENTS
+//! @{
+
+#define QUIKEVAL_GPIO 9 //!< Linduino QuikEval GPIO pin (QuikEval connector pin 14) connects to Arduino pin 9
+#define QUIKEVAL_CS SS //!< QuikEval CS pin (SPI chip select on QuikEval connector pin 6) connects to Arduino SS pin.
+#define QUIKEVAL_MUX_MODE_PIN 8 /*!< QUIKEVAL_MUX_MODE_PIN defines the control pin for the QuikEval MUX.
+The I2C port's SCL and the SPI port's SCK signals share the same pin on the Linduino's QuikEval connector.
+Additionally, the I2C port's SDA and the SPI port's MOSI signals share the same pin on the Linduino's QuikEval connector.
+The pair of pins connected to the QuikEval connector is switched using a MUX on the Linduino board.
+The control pin to switch the MUX is defined as QUIKEVAL_MUX_MODE_PIN (Arduino pin 8). */
+//! @}
+
+// Macros
+//! Set "pin" low
+//! @param pin pin to be driven LOW
+#define output_low(pin) digitalWrite(pin, LOW)
+//! Set "pin" high
+//! @param pin pin to be driven HIGH
+#define output_high(pin) digitalWrite(pin, HIGH)
+//! Return the state of pin "pin"
+//! @param pin pin to be read (HIGH or LOW).
+//! @return the state of pin "pin"
+#define input(pin) digitalRead(pin)
+
+//! @todo Make a note about whether Arduino/Linduino is Big Endian or Little Endian. Raspberry Pi appears to be the opposite.
+//! This union splits one int16_t (16-bit signed integer) or uint16_t (16-bit unsigned integer)
+//! into two uint8_t's (8-bit unsigned integers) and vice versa.
+ union LT_union_int16_2bytes
+ {
+ int16_t LT_int16; //!< 16-bit signed integer to be converted to two bytes
+ uint16_t LT_uint16; //!< 16-bit unsigned integer to be converted to two bytes
+ uint8_t LT_byte[2]; //!< 2 bytes (unsigned 8-bit integers) to be converted to a 16-bit signed or unsigned integer
+ };
+
+//! @todo Make a note about whether Arduino/Linduino is Big Endian or Little Endian. Raspberry Pi appears to be the opposite.
+//! This union splits one int32_t (32-bit signed integer) or uint32_t (32-bit unsigned integer)
+//! into four uint8_t's (8-bit unsigned integers) and vice versa.
+union LT_union_int32_4bytes
+{
+ int32_t LT_int32; //!< 32-bit signed integer to be converted to four bytes
+ uint32_t LT_uint32; //!< 32-bit unsigned integer to be converted to four bytes
+ uint8_t LT_byte[4]; //!< 4 bytes (unsigned 8-bit integers) to be converted to a 32-bit signed or unsigned integer
+};
+
+//! @todo Make a note about whether Arduino/Linduino is Big Endian or Little Endian. Raspberry Pi appears to be the opposite.
+//! This union splits one float into four uint8_t's (8-bit unsigned integers) and vice versa.
+union LT_union_float_4bytes
+{
+ float LT_float; //!< float to be converted to four bytes
+ uint8_t LT_byte[4]; //!< 4 bytes (unsigned 8-bit integers) to be converted to a float
+};
+
+
+#endif // LINDUINO_H
diff --git a/src/Overdrive/TriggerScope_V4B/README.md b/src/Overdrive/TriggerScope_V4B/README.md
new file mode 100644
index 0000000..2c4d30d
--- /dev/null
+++ b/src/Overdrive/TriggerScope_V4B/README.md
@@ -0,0 +1,4 @@
+# Installation
+This firmware is intended for TriggerScope V4B boards (serial numbers 2118 and later).
+
+To install this firmware, you will first need to download and install the [TeensyDuino package](https://www.pjrc.com/teensy/td_download.html), and the [Arduino IDE](https://www.arduino.cc/en/software). Then power your TriggerScope, connect the USB cable to your computer, and open the file TriggerScope_613MM/TriggerScope_613MM.ino in the [Arduino IDE](https://www.arduino.cc/en/software). Select the port under which your device appears (Tools > Port), and select the Teensy 4.1 (Tools > Board). Press the "verify" button, and - if compilation succeeds - press the Upload button.
diff --git a/src/Overdrive/TriggerScope_V4B/TriggerScope_V4B.ino b/src/Overdrive/TriggerScope_V4B/TriggerScope_V4B.ino
new file mode 100644
index 0000000..211f0f1
--- /dev/null
+++ b/src/Overdrive/TriggerScope_V4B/TriggerScope_V4B.ino
@@ -0,0 +1,1917 @@
+/******************************************
+ Trigger Scope v. 604MM for Arduino microscope control by
+ ADVANCED RESEARCH CONSULTING @ 2015
+ Regents of the University of California, 2020
+
+
+
+ Command set:
+
+ Commands to the device are in ASCI and terminated with "\n".
+ Returns can be multiple lines, terminated with "\r\n" (carriage retun / new line).
+ Error messages start with "!ERROR_" followed by the command causing the error, semi-colon
+ and error message (example: "!ERROR_RANGE: Command out of range, DAC=1-16, Range = 1-5...")
+
+ "*" - prints ID (example: "ARC TRIGGERSCOPE 16 R3C v.604-MM"
+ "?" - pirnts message listing available commands and their parameters
+ ----------------------analog output functions-------------------------
+ "SAO" - sets the voltage of specified DAC. Expected format: "DAC1-4236"
+ where 1 is the line nr, and 4236 the new value to be send to the DAC.
+ Resulting voltage depends on bit range of the DAC, and the voltage range
+ Returns: "!DAC1,4236" or error message (not starting with !DAC) on failure.
+ "PAN" - Queries the number of analog output states that can be pre-programmed.
+ Format: "PANn".
+ Where n=1-16 (DAC1-16).
+ Returns: "!PANn,q" where q is the maximum number of states that can be programmed
+ "PAO" - Sends sequence of analog output states to be used in triggered sequences.
+ Format: "PAOn-s-o1-o2-on", where
+ - n=1-16 for DAC 1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (0-65535) to be consecutively inserted in the list.
+ Returns: "!PA0n-s-q" where q is the number of values successfully inserted in the internal buffer.
+ "POV" - Sets an array of overvoltage values for use before typical state transition.
+ Format: "POVn-s-o1-o2-on", where
+ - n=1-16 for DAC 1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (0-65535) to be consecutively inserted in the list.
+ Returns: "!POVn-s-q" where q is the number of values successfully inserted in the internal buffer.
+ "POD" - Sets an array of overvoltage delay times for use with POV commands.
+ Format: "PODn-s-o1-o2-on", where
+ - n=1-16 for DAC 1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (0-4,294,967,295) to be consecutively inserted in the list.
+ Returns: "!PODn-s-q" where q is the number of values successfully inserted in the internal buffer.
+
+ "PAC" - Clears the sequence of ananlog states
+ Format: "PACn", where n is the DAC pinNr (1-16)
+ "PAS" - Starts triggered transitions in analog output state as pre-loaded in PAO
+ Format: "PASn-s-t" where
+ - n=1-16 for DAC 1-16
+ = s=0 or 1 where 0 stops and 1 starts triggered transitions
+ - t=0 or 1 and determines whether transition will happen on the falling (0) or rising (1) edge
+ of a pulse on the input pin
+ "BAO" - Activates blanking mode of the analog output. Output state will be coupled to the
+ state of the input trigger. If input trigger is active, the DAC will be active (
+ as set with SDA), if input trigger is inactive, the output will go (which may not be 0V, depending on the range).
+ Format: "BAOn-s-t"
+ - n=1-16 for DAC 1-16
+ - s=0 or 1 where 0 stops and 1 starts balnking mode
+ - t translates state of the input pin to its activity for blanking. If t = 0,
+ DAC will be 0 when input trigger pin is low. t = 1: DAC will be active when input trigger is low.
+ "SAR" - Sets the output range in volts of the DAC. Example: "SAR2-1" where 2
+ specified the DAC number, and 1 the range. Allowed ranges are number 1-5:
+ 1: 0-5V
+ 2: 0-10V
+ 3: -5-+5V
+ 4: -10-+10V
+ 5: -2-+2V
+ Returns: "!RANGE2,1"
+-------------------------digital output functions------------------------
+"SDO" - sets TTL outputs of pins1-8 and 9-16 in a single operation
+ Format: DOn-o
+ Where n=0 or 1. 0 address pins 1-8, 1 goes to pins 9-16
+ 0 is digital output bitmask, 0: all off, 1: pin 1 on, 2: pin 2 on, 3: pins 1&2 on, etc..
+ Example: DO1,4 sets pin 3 high and pins 1,2, 4-8 low
+ Returns: "!DOn-o" or error message ("!ERROR_DO: ") on failure.
+"PDN" - Queries the number of digital output states that can be pre-programmed.
+ Format: "PDNn".
+ Where n=0 or 1. 0 address pins 1-8, 1 goes to pins 9-16
+ Returns: "!PDNn-q" where q is the maximum number of states that can be programmed
+"PDO" - Sends sequence of digital output states to be used in triggered sequences.
+ Format: "PDOn-s-o1-o2-on", where
+ - n=0 or 1 and directs the command to either pins1-8 or 9-16,
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values to be consecutively inserted in the list.
+ Returns: "!PD0n-s-q" where q is the number of values successfully inserted in the internal buffer.
+"PDC" - Clears the sequence of digital states
+ Format: "PDCn", where n is the pingroup, 0=pins1-8, 1=pins9-16
+"PDS" - Starts triggered transitions in digital output state as pre-loaded in PDO
+ Format: "PDSn-s-t" where
+ - n=0 or 1 and directs the command to either pins 1-8 or 9-16
+ = s=0 or 1 where 0 stops and 1 starts triggered transitions
+ - t=0 or 1 and determines whether transition will happen on the falling (0) or rising (1) edge
+ of a pulse on the input pin
+"BDO" - Activates blanking mode of the digital output. Output state will be coupled to the
+ state of the input trigger. If input trigger is active, the pingroup will be active (
+ as set with DO), if input trigger is inactive, the pingroup will go low.
+ Format: "BDOn-s-t"
+ where n is the pingroup (0 or 1) and s switched blanking mode on (1) or off (0).
+ t translates state of the input pin to its activity for blanking. If t = 0,
+ output pins will be low when input trigger pin is low. When t = 1, output pins
+ will be active when input trigger is low.
+ "SSL" - Set Signal LEDs.
+ In some cases, setting signal LEDs slows down triggered operation, so offer the option to
+ not set them.
+ Format: "SSLn" where
+ - n - 0 ("Off") or 1 ("On")
+ Signal LEDs are On by default
+
+
+
+*****************************
+Contact Advanced Research Consulting for Driver libraries! www.advancedresearch.consulting
+ ******************************/
+#include
+#include
+#include
+#include "Linduino.h"
+
+#define focus 15 //sets focus line #
+#define pwrLed 28 //POWER indication
+#define dacLed 29 //DAC indication
+#define ttlLed 34 //TTL indication
+#define trigLed 35 //TRIGGER indication
+#define readyLed 32 //READY indication
+#define ttlblock2OE 23
+#define ttlblock2DIR 24
+#define ttlblock1OE 21
+#define ttlblock1DIR 22
+#define ttlblock3OE 37
+#define ttlblock3DIR 36
+
+#define NR_DACS 16
+#define NR_DAC_STATES 1200
+#define NR_DO_STATES 1200
+
+
+String idname = "ARC TRIGGERSCOPE 16 R4 Board 4B v.620-MM";
+
+const char* helpString = "Available commands: \r\n"
+ "SAOn-s - sets DACs. n = DAC number (1-16), s = value (0-65535)\r\n"
+ "PANn - queries the number of programmable analogoutput states, n = DAC pin number (1-16)\r\n"
+ "PAOn-s-o1-o2-on - sends sequence of programmable analog output states, n = DAC pin number (1-16)\r\n"
+ " s = 0-based index in the sequence to start inserting, \r\n"
+ " o = comma separated series of output values (0-65535)\r\n"
+ "POVn-s-o1-o2-on - sends sequence of analog overdrive states, n = DAC pin number (1-16)\r\n"
+ " s = 0-based index in the sequence to start inserting, \r\n"
+ " o = comma separated series of output values (0-65535)\r\n"
+ "PODn-s-o1-o2-on - sends sequence of overdrive durations in microseconds, n = DAC pin number (1-16)\r\n"
+ " s = 0-based index in the sequence to start inserting, \r\n"
+ " o = comma separated series of delay times (0-(0-4294967295))\r\n"
+
+ "PACn - clears the sequence of analog states, n = DAC pin number (1-16)\r\n"
+ "PASn-s-t - starts triggered transition in programmable analog output states as programmed in PAO\r\n"
+ " n = DAC pin number (1-16), s = 0 (stops) or 1 (starts), \r\n"
+ " t = transition on falling (0) or rising (1) edge of input trigger.\r\n"
+ "BAOn-s-t starts blanking mode of ananlog output. n = DAC pin number (1-16), \r\n"
+ " s = 0 (stops) or 1 (starts), t = output low when trigger low (0), or inverse (1)\r\n"
+ "SARn-s - sets DAC voltages range. n = DAC number (1-16), s= 1:0-5V 2:0-10V 3:-5-+5V 4: -10-+10V 5:-2-+2V\r\n"
+ "SDOn-s - sets digital output state, n = 0(pins1-8) or 1(pins9-16), s binary mask 0-255\r\n"
+ "PDNn - queries the number of programmable digital output states, n = pin group 0(1-8) or 1 (9-16)\r\n"
+ "PDOm-s-o1-o2-on - sends sequence of programmable digital output states, n = pin group 0(1-8) or 1(9-16)\r\n"
+ " s = 0-based index in the sequence to start inserting\r\n"
+ " o = comma separated series of output states (0-255)\r\n"
+ "PDCn - clears the sequence of digital states, n = pin group 0(1-8) or 1 (9-16)\r\n"
+ "PDSn-s-t - starts triggered transitions in digital output state as pre-loaded in PDO, \r\n"
+ " n = pin group 0(1-8) or 1 (9-16), s = 0 stops 1 starts, \r\n"
+ " t = transition on falling (0) or rising (1) edge of input trigger.\r\n"
+ "BDn-s-t - sync digital output with input trigger. n = pin group 0(1-8) or 1 (9-16)\r\n"
+ " s = 0 stops 1 starts, t = output low when trigger low (0), or inverse (1)\r\n"
+ "SSLn - switches use of signal LEDs. n=0 (Off) or n=1 (On)\r\n"
+ "\r\n"; // empty line to signal end of help textd
+
+
+const char sep = '-';
+
+//new IC assignments and SD card access for V4 board
+File myFile; //create settings file
+const int chipSelect = BUILTIN_SDCARD; //set SD card access CS line
+
+//set up menu modes and vars
+byte opMode = 1; //sets operation mode, 1=MANUAL control, 2 = PC CONTROL, 3=TTL Slave via Programmed Steps
+boolean trigArmed=false; //enables while loop for just the high speed trig sequencing
+unsigned long debugT=0; //debugger flag - not used in a few versions
+unsigned long trigInterval; //tracks frequency of inputs from camera or external source
+int trigStep=0; //optionally used to sequence more than 1 PROG line at a time, not fully implemented
+String inputString = ""; // a string to hold incoming data
+boolean stringComplete = false; // whether the string is complete
+
+//PIN ASSIGNMENTS
+const byte DAC[NR_DACS] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; //MOVE THESE FOR CUSTOMERS IF NEEDED!
+const byte ttl[NR_DACS] = {5,6,7,8,14,15,16,17,20,25,26,27,38,39,40,41}; //ttl pin #'s
+const byte trig[4] = {0,1,2,3};
+
+/*HIGH SPEED PROGRAMMING MODE MEMORY BLOCK*/
+int dacArray[NR_DAC_STATES][NR_DACS] = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}; // DACprogram list
+uint8_t ttlArray[NR_DO_STATES][2] = {{0,0}}; // digital output states program list
+int ttlArrayMaxIndex[2] = {0, 0}; // maintains the max index in the array that was set
+int ttlArrayIndex[2] = {0, 0}; // keeps track of current position in the array
+int dacArrayMaxIndex[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+int dacArrayIndex[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+uint32_t dacBlankDelay[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+uint32_t dacBlankDuration[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+boolean useSignalLEDs_ = true;
+
+// data structures to be assembled from blanking settings above that have a time-ordered sequence of events
+int dacBlankEventsNr = 0;
+// there can be a delay and a duration for each blanking event
+uint32_t dacBlankEventNextWait[2 * NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+uint8_t dacBlankEventPinNr[2 * NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+// 0 : off, 1: set normal state, 2: set value from dacArray
+uint8_t dacBlankEventState[2 * NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+byte pinGroupState[2] = {0, 0};
+byte pinGroupStoredState[2] = {0, 0};
+bool pinGroupBlanking[2] = {false, false};
+bool pinGroupBlankOnLow[2] = {true, true};
+bool pinGroupSequencing[2] = {false, false};
+byte pinGroupSequenceMode[2] = {0, 0}; // 0: falling edge, 1: rising edge
+int dacState[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+int dacStoredState[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+bool dacBlanking[16] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false};
+bool dacBlankOnLow[16] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false};
+bool dacSequencing[16] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false};
+byte dacSequenceMode[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+int povArray[NR_DAC_STATES][NR_DACS] = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}; // DAC overdrive value list
+uint32_t podArray[NR_DAC_STATES][NR_DACS] = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}; // overdrive delay on state
+uint32_t podExpire[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //timers for real overdrive expire
+int lastDac[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //holes the last DAC value and used for implement on timer expire
+
+bool triggerPinState = false;
+
+int delArray[500]; //time delay array for high speed sequences
+int focArray[6]; //array for focus stacks = start, step, #loops,direction,slave,current step
+uint16_t ttlState = 0;
+// boolean ttlActive[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+int timeCycles = 1; //used for high speed switching inside of a loop
+int runCycles = 0; //holds running position vs total cycles for timelapse
+
+volatile boolean inTrigger=false;
+byte program=0; //this sets the active program # used in the Mode 3 high speed sequencer
+byte maxProgram=0; //this holds the maximum program value entered, and is used to set the maxium sequence step.
+// byte stepMode = 1; //1 = waits for TTL IN, 2=runs continually
+// unsigned long timeOut = 1000; //timeout for sequence (set to 10 seconds by default)
+// unsigned long tStart = 0; //sequence start timer
+// unsigned long trigLedTimer = 0;
+// boolean reportTime = 0;
+// boolean umArmIgnore = 0; //this handles micromanagers multiple ARM commands which are issued back-to-back on MDA acqtivation. Ignores after a short wait.
+// boolean usepwm = false;
+// byte pChannel =0; //number of channels micromanager has attempted to control
+// byte lastPT=20;
+// byte trigMode = 2; //0 = LOW 1 = HIGH 2 = RISING 3 = FALLING 4 = CHANGE
+
+bool error = false;
+
+const char* saoErrorString = "!ERROR_SAO: Format: SAOn-s n=1-16 (DAC1-16), s=value 0-65535";
+const char* sarErrorString = "!ERROR_SAR: Format: SARn-s n=1-16 (DAC=1-16), s=1:0-5V 2:0-10V 3:-5-+5V 4:-10-+10V 5:-2-+2V";
+const char* panErrorString = "!ERROR_PAN: Format: PANn n=1-16 (DAC1-16)";
+const char* paoErrorString = "!ERROR_PAO: Format: PAOn-s-01-02-0n n=1-16 (DAC1-16), s= >=0 (position), 0n=values 0-65535";
+const char* pacErrorString = "!ERROR_PAC: Format: PACn n=1-16 (DAC1-16)";
+const char* pasErrorString = "!ERROR_PAS: Format: PASn-s-t n=1-16 (DAC1-16) s 0=stop 1=start, t=transition on falling(0) or rising(1) edge";
+const char* baoErrorString = "!ERROR_BAO: Format: BAOn-s-t n=1-16 (DAC1-16), s blank 0(off) or 1(on), t 0 (blank on low) or 1 (blank on high)";
+const char* povErrorString = "!ERROR_POV: Format: POVn-s-01-02-0n n=1-16 (DAC1-16), s= >=0 (position), 0n=values 0-65535";
+const char* podErrorString = "!ERROR_POD: Format: PODn-s-01-02-0n n=1-16 (DAC1-16), s= >=0 (position), 0n=values 0-4294967295";
+const char* sdoErrorString = "!ERROR_SDO: Format: SDOn-s n=pingroup 0-1, s=value 0-255";
+const char* pdnErrorString = "!ERROR_PDN: Format: PDNn n=pingroup 0-1";
+const char* pdoErrorString = "!ERROR_PDO: Format: PDOn-s-01-02-0n n=pingroup 0-1, s=position, 0n=values 0-255";
+const char* bdoErrorString = "!ERROR_BDO: Format: BDOn-s-t n=pingroup 0-1, s blank 0(off) or 1(on), t 0 (blank on low) or 1 (blank on high)";
+const char* pdcErrorString = "!ERROR_PDC: Format: PDCn n=pinGroup(1/2)";
+const char* pdsErrorString = "!ERROR_PDS: Format: PDSn-s-t n=pinGroup(1/2) s 0=stop 1=start, t=transition on falling(0) or rising(1) edge";
+const char* sslErrorString = "!ERROR_SSL: Format: SSLn n=0 (Off) or 1 (On)";
+const char* generalErrorString = "ERROR_UNKNOWN_COMMAND";
+
+
+
+void setup()
+{
+
+ //indicate power is on for Teensy
+ pinMode(pwrLed,OUTPUT);
+ digitalWrite(pwrLed,HIGH);
+
+ //configure LED pins for output
+ pinMode(trigLed,OUTPUT);
+ pinMode(ttlLed,OUTPUT);
+ pinMode(dacLed,OUTPUT);
+
+ //Configure TTL outputs 13-16
+ pinMode(ttlblock2OE,OUTPUT);
+ digitalWrite(ttlblock2OE,LOW); //low for enable
+ pinMode(ttlblock2DIR,OUTPUT);
+ digitalWrite(ttlblock2DIR,HIGH); //HIGH to enable 3.3v -> 5V output
+
+ //configure TTL outputs 5-12
+ pinMode(ttlblock1OE,OUTPUT);
+ digitalWrite(ttlblock1OE,LOW); //*** DISABLED FOR BOARDS PRODUCED 12/21 DUE TO LED CONFLICT. low for enable
+ pinMode(ttlblock1DIR,OUTPUT);
+ digitalWrite(ttlblock1DIR,HIGH); //HIGH to enable 3.3v -> 5V output
+
+ //Configure TTL IN for TRIG 1-4
+ pinMode(ttlblock3OE,OUTPUT);
+ digitalWrite(ttlblock3OE,LOW); //low for enable
+ pinMode(ttlblock3DIR,OUTPUT);
+ digitalWrite(ttlblock3DIR,LOW); //LOW to enable 5v -> 3.3V input
+
+ delay(10);
+ //configureTrigger(trigMode); //will attach interrupt
+ for(byte i=0;i<16;++i) { pinMode(ttl[i],OUTPUT); digitalWrite(ttl[i],LOW); } //SET OUTPUT PINS ON TTL AND CAMERA LINES
+
+ Serial.begin(115200); // start serial @ 115,200 baud
+ while (!Serial) { ; } // wait for serial port
+
+ //read from SD card
+ //Serial.print("Reading Settings...");
+ if (!SD.begin(chipSelect)) {
+ //Serial.println("SD Read Failure. Contact ARC");
+ return;
+ }
+ myFile = SD.open("tgs.txt", FILE_WRITE);
+ if (myFile) {
+ //Serial.print("Writing to test.txt...");
+ myFile.println("testing 1, 2, 3.");
+ myFile.close();
+ Serial.println("done.");
+ }
+ else {
+ // if the file didn't open, print an error:
+ //Serial.println("SD Read Failure. Contact ARC");
+ }
+
+ /***Dac startup ***/
+ pinMode(9,OUTPUT); //CLR pin must stay high!
+ digitalWrite(9,LOW); //CLR Stays high ALWAYAS
+ delay(50);
+ digitalWrite(9,HIGH); //CLR Stays high ALWAYAS
+ delay(50);
+
+ SPI.begin();
+ pinMode(10,OUTPUT); // DAC CS
+ SPI.beginTransaction(SPISettings(30000000, MSBFIRST, SPI_MODE0)); //teensy can do 30000000 !!
+
+ //Drive All DACs & TTLs to 0
+ for(int i=1;i<=16;++i) {
+ setTTL(i,0);
+ setDac(i, 0);
+ }
+ opMode=3; //HYBRID mode default HYBRID=3 / MANUAL=0 /
+ delay(100);
+ Serial.println(idname); //issue identifier so software knows we are running
+ // configureTrigger(trigMode); //will attach interrupt
+ pinMode(readyLed,OUTPUT);
+ digitalWrite(readyLed,HIGH); //indicate setup complete
+ triggerPinState = digitalReadFast(trig[0]);
+
+}
+
+void loop()
+{
+ //************************ DEVICE CONTROL & COMMAND CODE ***********************//
+ //************************ SERIAL COMMUNICATION CODE ******************///
+ /*
+ if(inTrigger && reportTime) //reports time delay from previous interrupt
+ {
+ Serial.print("@TIME =");
+ Serial.println(trigInterval);
+ inTrigger = false;
+ }
+ */
+ for (byte i = 0; i < NR_DACS; i++) // investigates if an overdrive or delay timer is enabled
+ {
+ if (podExpire[i] != 0)
+ { // delay is still on
+ if (podExpire[i] < micros())
+ { // time has expired
+ setDac(i, lastDac[i]); //issue normal on state value
+ podExpire[i] = 0; //remove exire timer
+ }
+ }
+ }
+
+ if (triggerPinState != digitalReadFast(trig[0]))
+ {
+ triggerPinState = ! triggerPinState;
+ if (useSignalLEDs_)
+ {
+ digitalWriteDirect(trigLed, triggerPinState);
+ }
+ for (byte i = 0; i < NR_DACS; i++) // todo: optimize by ordering an array with sequenceable DACS and only cycle through those
+ {
+ if (dacSequencing[i])
+ {
+ if (dacSequenceMode[i] == triggerPinState)
+ {
+ dacState[i] = dacArray[dacArrayIndex[i]][i];
+ dacArrayIndex[i]++;
+
+ if (dacArrayIndex[i] == dacArrayMaxIndex[i]) { dacArrayIndex[i] = 0; }
+ if (!dacBlanking[i])
+ {
+ if(povArray[dacArrayIndex[i]][i] ) //if pov has an overdrive value
+ {
+ setDac(i, povArray[dacArrayIndex[i]][i]); //issue overdrive
+ podExpire[i] = micros() + podArray[dacArrayIndex[i]][i]; //set disable timer
+ lastDac[i] = dacState[i]; //save the normal dac output value for later update
+ }
+ else{
+ setDac(i, dacState[i]);
+ }
+ }
+
+ }
+ }
+ if (dacBlanking[i]) {
+ dacBlankOnLow[i] == triggerPinState ? setDac(i, dacState[i]) : setDac(i, 0);
+ }
+ }
+
+ for (byte i = 0; i < 2; i++)
+ {
+ if (pinGroupSequencing[i])
+ {
+ if (pinGroupSequenceMode[i] == triggerPinState)
+ {
+ pinGroupState[i] = ttlArray[ttlArrayIndex[i]][i];
+ ttlArrayIndex[i]++;
+ if (ttlArrayIndex[i] == ttlArrayMaxIndex[i]) { ttlArrayIndex[i] = 0; }
+ if (!pinGroupBlanking[i])
+ {
+ setPinGroup(i, pinGroupState[i]);
+ } // if we are blanking, fall through to the code below to set
+ }
+ }
+ if (pinGroupBlanking[i])
+ {
+ pinGroupBlankOnLow[i] == triggerPinState ? setPinGroup(i, pinGroupState[i]) :
+ setPinGroup(i, 0);
+ }
+ }
+
+
+ }
+
+ if (stringComplete) // There is a new line terminated string ready to be processed
+ {
+ digitalWrite(readyLed,LOW);
+
+ String command = inputString.substring(0,3);
+
+ if (inputString == "?\n")
+ {
+ Serial.println(helpString);
+ }
+
+ else if(inputString == "*\n")
+ {
+ Serial.println(idname);
+ } //return ID line if queried
+
+ // SAO command - Set DAC
+ // Expected format: "SAO1-4236" where 1 is the line nr, and 4236 the new value to be send to the DAC
+ else if(command == "SAO")
+ {
+ error = false;
+ byte dacNum = 0; // initialize to error conditino
+ int offset = 5;
+ if (inputString[4] == sep)
+ {
+ dacNum = atoi(&inputString[3]);
+ } else if (inputString[5] == sep)
+ {
+ dacNum = atoi(inputString.substring(3,5).c_str());
+ offset++;
+ } else
+ {
+ error = true;
+ }
+ if (dacNum < 1 || dacNum > 16)
+ {
+ error = true;
+ }
+
+ int value = atoi(inputString.substring(offset).c_str());
+ if (value < 0 || value > 65535)
+ {
+ error = true;
+ }
+
+ if (!error) //if inputs are valid
+ {
+ Serial.print("!SAO"); //print recieve message to operator
+ Serial.print(dacNum); //print recieve message to operator
+ Serial.print(sep);
+ Serial.println(value);
+ dacState[dacNum - 1] = value;
+ setDacCheckBlanking(dacNum - 1);
+ } else
+ {
+ Serial.println(saoErrorString);
+ }
+ }
+
+ // PAN -
+ // Queries the number of analog output states that can be pre-programmed.
+ // Format: "PANn", n is 1-6 for DAC1-16.
+ // Returns: "!PANn-q" where q is the maximum number of states that can be programmed
+ else if (command == "PAN")
+ {
+ byte dac = atoi(&inputString[3]);
+ if (dac < 1 || dac > 16)
+ {
+ Serial.println(panErrorString);
+ } else
+ {
+ Serial.print("!PAN");
+ Serial.print(dac);
+ Serial.print(sep);
+ Serial.println(NR_DAC_STATES);
+ }
+ }
+ /*
+ "BAO" - Activates blanking mode of the analog output. Output state will be coupled to the
+ state of the input trigger. If input trigger is active, the DAC will be active (
+ as set with SDA), if input trigger is inactive, the output will go (which may not be 0V, depending on the range).
+ Format: "BAOn,s,t"
+ - n=1-16 for DACS1-16
+ - s=0 or 1 where 0 stops and 1 starts balnking mode
+ - t translates state of the input pin to its activity for blanking. If t = 0,
+ DAC will be 0 when input trigger pin is low. t = 1: DAC will be active when input trigger is low.
+ */
+ else if (command == "BAO")
+ {
+ error = false;
+ int dacNr = 0;
+ int scp = 5;
+ if (inputString[4] == sep) { dacNr = inputString.substring(3,4).toInt(); }
+ else if (inputString[5] == sep) { dacNr = inputString.substring(3,5).toInt(); scp = 6; }
+ else { error = true; }
+ if (dacNr < 1 || dacNr > 16){ error = true; }
+ int state = inputString.substring(scp, scp+1).toInt();
+ if (state < 0 || state > 1) { error = true; }
+ if (inputString[scp+1] != sep) { error = true; }
+ int mode = inputString.substring(scp+2).toInt();
+ if (mode < 0 || mode > 1) { error = true; }
+ if (!error)
+ {
+ dacBlanking[dacNr - 1] = state == 1;
+ dacBlankOnLow[dacNr - 1] = mode == 0;
+ setDacCheckBlanking(dacNr - 1);
+ char out[20];
+ sprintf(out, "!BAO%d%c%d%c%d", dacNr, sep, state, sep, mode);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(baoErrorString);
+ }
+ }
+
+
+
+
+ /*
+ *
+ *"POV" - Sends sequence of analog overdrive states to be used in triggered sequences.
+ Format: "POVn,s,o1,o2,on", where
+ - n=1-16 for DACS1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (0-65535) to be consecutively inserted in the list.
+ Returns: "!POVn,s,q" where q is the number of values successfully inserted in the internal buffer.
+ */
+ else if (command == "POV")
+ {
+ error = false;
+ int n = 0;
+ int s = 0;
+ int dacNr = 0;
+ unsigned int scp = 5;
+ if (inputString[4] == sep) { dacNr = inputString.substring(3,4).toInt(); }
+ else if (inputString[5] == sep){ dacNr = inputString.substring(3,5).toInt(); scp = 6; }
+ else { error = true; }
+ if (dacNr < 1 || dacNr > 16) { error = true; }
+ unsigned int ecp = scp + 1;
+ if (!error)
+ {
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if (ecp < inputString.length()) { s = inputString.substring(scp, ecp).toInt(); }
+ else { error = true; }
+ while (!error && ecp < inputString.length())
+ {
+ scp = ecp;
+ ecp++;
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if ( (ecp - scp) > 1)
+ {
+ int val = inputString.substring(scp, ecp).toInt();
+ if (val < 0 || val > 65535)
+ {
+ error = true;
+ } else
+ {
+ povArray[n + s][dacNr-1] = val;
+ n++;
+ int index = n+s;
+ if (index > dacArrayMaxIndex[dacNr-1])
+ {
+ dacArrayMaxIndex[dacNr-1] = index;
+ }
+ }
+ }
+ }
+ }
+
+ if (!error)
+ {
+ char out[20];
+ sprintf(out, "!POV%d%c%d%c%d%c%d", dacNr, sep, s, sep, n, sep, dacArrayMaxIndex[dacNr-1]);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(povErrorString);
+ }
+ }
+
+ /*
+ *
+ *"POD" - Sends sequence of delay times for anaog overdrive staates specified in POV.
+ Format: "PODn,s,o1,o2,on", where
+ - n=1-16 for DACS1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (uInt32_t) to be consecutively inserted in the list.
+ Returns: "!PODn,s,q" where q is the number of values successfully inserted in the internal buffer.
+ */
+ else if (command == "POD")
+ {
+ error = false;
+ int n = 0;
+ int s = 0;
+ int dacNr = 0;
+ unsigned int scp = 5;
+ if (inputString[4] == sep) { dacNr = inputString.substring(3,4).toInt(); }
+ else if (inputString[5] == sep){ dacNr = inputString.substring(3,5).toInt(); scp = 6; }
+ else { error = true; }
+ if (dacNr < 1 || dacNr > 16) { error = true; }
+ unsigned int ecp = scp + 1;
+ if (!error)
+ {
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if (ecp < inputString.length()) { s = inputString.substring(scp, ecp).toInt(); }
+ else { error = true; }
+ while (!error && ecp < inputString.length())
+ {
+ scp = ecp;
+ ecp++;
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if ( (ecp - scp) > 1)
+ {
+ int val = inputString.substring(scp, ecp).toInt();
+ if (val < 0 || val > 65535)
+ {
+ error = true;
+ } else
+ {
+ podArray[n + s][dacNr-1] = val;
+ n++;
+ int index = n+s;
+ if (index > dacArrayMaxIndex[dacNr-1])
+ {
+ dacArrayMaxIndex[dacNr-1] = index;
+ }
+ }
+ }
+ }
+ }
+
+ if (!error)
+ {
+ char out[20];
+ sprintf(out, "!POD%d%c%d%c%d%c%d", dacNr, sep, s, sep, n, sep, dacArrayMaxIndex[dacNr-1]);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(podErrorString);
+ }
+ }
+
+ /*
+ *
+ *"PAO" - Sends sequence of analog output states to be used in triggered sequences.
+ Format: "PAOn,s,o1,o2,on", where
+ - n=1-16 for DACS1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (0-65535) to be consecutively inserted in the list.
+ Returns: "!PA0n,s,q" where q is the number of values successfully inserted in the internal buffer.
+ */
+ else if (command == "PAO")
+ {
+ error = false;
+ int n = 0;
+ int s = 0;
+ int dacNr = 0;
+ unsigned int scp = 5;
+ if (inputString[4] == sep) { dacNr = inputString.substring(3,4).toInt(); }
+ else if (inputString[5] == sep){ dacNr = inputString.substring(3,5).toInt(); scp = 6; }
+ else { error = true; }
+ if (dacNr < 1 || dacNr > 16) { error = true; }
+ unsigned int ecp = scp + 1;
+ if (!error)
+ {
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if (ecp < inputString.length()) { s = inputString.substring(scp, ecp).toInt(); }
+ else { error = true; }
+ while (!error && ecp < inputString.length())
+ {
+ scp = ecp;
+ ecp++;
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if ( (ecp - scp) > 1)
+ {
+ int val = inputString.substring(scp, ecp).toInt();
+ if (val < 0 || val > 65535)
+ {
+ error = true;
+ } else
+ {
+ dacArray[n + s][dacNr-1] = val;
+ n++;
+ int index = n+s;
+ if (index > dacArrayMaxIndex[dacNr-1])
+ {
+ dacArrayMaxIndex[dacNr-1] = index;
+ }
+ }
+ }
+ }
+ }
+
+ if (!error)
+ {
+ char out[20];
+ sprintf(out, "!PAO%d%c%d%c%d%c%d", dacNr, sep, s, sep, n, sep, dacArrayMaxIndex[dacNr-1]);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(paoErrorString);
+ }
+ }
+
+
+ /*
+ * "PAC" - Clears the sequence of ananlog states
+ Format: "PACn", where n is the DAC pinNr (1-16)
+ */
+ else if (command == "PAC")
+ {
+ error = false;
+ if (inputString.length() == 5 || inputString.length() == 6)
+ {
+ int dacNr = inputString.substring(3).toInt();
+ if (dacNr < 1 || dacNr > 16) { error = true; }
+ if (!error)
+ {
+ dacSequencing[dacNr - 1] = false;
+ dacArrayIndex[dacNr - 1] = 0;
+ dacArrayMaxIndex[dacNr - 1] = 0;
+ dacBlankDelay[dacNr - 1] = 0;
+ dacBlankDuration[dacNr - 1] = 0;
+ clearPAO(dacNr - 1);
+ clearPOD(dacNr - 1);//addition for overdrive
+ clearPOV(dacNr - 1);//addition for overdrive
+ for ( byte d = 0;d 16 || inputString[scp] != sep)
+ {
+ error = true;
+ }
+ int state = inputString.substring(scp+1,scp+2).toInt();
+ if (state < 0 || state > 1) { error = true; }
+ int rising = inputString.substring(scp+3).toInt();
+ if (rising < 0 || rising > 1) { error = true; }
+ if (!error)
+ {
+ dacSequencing[dacNr - 1] = (boolean) state;
+ dacSequenceMode[dacNr - 1] = rising;
+ if (state)
+ {
+ dacStoredState[dacNr - 1] = dacState[dacNr - 1];
+ dacArrayIndex[dacNr - 1] = 0;
+ if (!rising) { // if we trigger on the falling edge, set initial state now, and advance counter here
+ setDac(dacNr -1, dacArray[dacArrayIndex[dacNr - 1]][dacNr - 1]); // Check blanking?
+ dacArrayIndex[dacNr - 1]++;
+ }
+ } else
+ {
+ dacState[dacNr - 1] = dacStoredState[dacNr - 1];
+ }
+ char out[20];
+ sprintf(out, "!PAS%d%c%d%c%d", dacNr, sep, state, sep, rising);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(pasErrorString);
+ }
+ }
+
+ // SDO
+ // sets TTL outputs of pins 1-8 and 9-16 in a single operation
+ // Format: SDOn,o
+ // Where n=0 or 1. 0 address pins 1-8, 1 goes to pins 9-16
+ // 0 is digital output bitmask, 0: all off, 1: pin 1 on, 2: pin 2 on, 3: pins 1&2 on, etc..
+ // Example: DO1,4 sets pin 3 high and pins 1,2, 4-8 low
+ else if (command == "SDO")
+ {
+ byte pinGroup = atoi(&inputString[3]);
+ if (pinGroup < 0 || pinGroup > 1)
+ {
+ Serial.println(sdoErrorString);
+ } else
+ {
+ int value = atoi(inputString.substring(5).c_str());
+ if (value < 0 || value > 255)
+ {
+ Serial.println(sdoErrorString);
+ } else
+ {
+ Serial.print("!SDO");
+ Serial.print(pinGroup);
+ Serial.print(sep);
+ Serial.println(value);
+ pinGroupState[pinGroup] = value;
+ setPinGroupCheckBlanking(pinGroup);
+ }
+ }
+ }
+
+ // PDN -
+ // Queries the number of digital output states that can be pre-programmed.
+ // Format: "PDNn".
+ // Returns: "!PDNn,q" where q is the maximum number of states that can be programmed
+ else if (command == "PDN")
+ {
+ byte pinGroup = atoi(&inputString[3]);
+ if (pinGroup < 0 || pinGroup > 1)
+ {
+ Serial.println(pdnErrorString);
+ } else
+ {
+ Serial.print("!PDN");
+ Serial.print(pinGroup);
+ Serial.print(sep);
+ Serial.println(NR_DO_STATES);
+ }
+ }
+
+ //"PDO" - Sends sequence of digital output states to be used in triggered sequences.
+ // Format: "PDOn-s-o1-o2-on", where
+ // - n=1 or 2 and directs the command to eitherpins1-8 or 9-16,
+ // - s: position in the sequence to start inserting values. First position is 1.
+ // - o1, o2, etc... values to be consecutively inserted in the list.
+ // Returns: "!PD0n,s,q" where q is the number of values successfully inserted in the internal buffer.
+ else if (command == "PDO")
+ {
+ error = false;
+ int s = 0;
+ int n = 0;
+ int pinGroup = inputString.substring(3,4).toInt();
+ if (pinGroup < 0 || pinGroup > 1)
+ {
+ error = true;
+ }
+ if (inputString[4] != sep)
+ {
+ error = true;
+ }
+ unsigned int scp = 5;
+ unsigned int ecp = 6;
+ if (!error)
+ {
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if (ecp < inputString.length())
+ {
+ s = inputString.substring(scp, ecp).toInt();
+ }
+ else
+ {
+ error = true;
+ }
+ while (!error && ecp < inputString.length())
+ {
+ scp = ecp;
+ ecp++;
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if ( (ecp - scp) > 1)
+ {
+ int val = inputString.substring(scp, ecp).toInt();
+ if (val < 0 || val > 255)
+ {
+ error = true;
+ } else
+ {
+ ttlArray[n + s][pinGroup] = (byte) val;
+ n++;
+ int index = n+s;
+ if (index > ttlArrayMaxIndex[pinGroup])
+ {
+ ttlArrayMaxIndex[pinGroup] = index;
+ }
+ }
+ }
+ }
+ }
+
+ if (!error)
+ {
+ char out[20];
+ sprintf(out, "!PDO%d%c%d%c%d%c%d", pinGroup, sep, s, sep, n, sep, ttlArrayMaxIndex[pinGroup]);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(pdoErrorString);
+ }
+ }
+
+ /*
+ * "PDC" - Clears the sequence of digital states
+ Format: "PDCn", where n is the pingroup, 0=pins1-8, 1=pins9-16
+ */
+ else if (command == "PDC")
+ {
+ error = false;
+ if (inputString.length() == 5)
+ {
+ int pinGroup = inputString.substring(3,4).toInt();
+ if (pinGroup < 0 || pinGroup > 1) { error = true; }
+ if (!error)
+ {
+ pinGroupSequencing[pinGroup] = false;
+ ttlArrayIndex[pinGroup] = 0;
+ ttlArrayMaxIndex[pinGroup] = 0;
+ clearPDO(pinGroup);
+ char out[20];
+ sprintf(out, "!PDC%d", pinGroup);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(pdcErrorString);
+ }
+ }
+
+ /*
+ "PDS" - Starts and stops triggered transitions in digital output state as pre-loaded in PDO
+ Format: "PDSn,s,t" where
+ - n=0 or 1 and directs the command to either pins 1-8 or 9-16
+ = s=0 or 1 where 0 stops and 1 starts triggered transitions
+ - t=0 or 1 and determines whether transition will happen on the falling (0) or rising (1) edge
+ of a pulse on the input pin
+ */
+ else if (command == "PDS")
+ {
+ error = false;
+ if (inputString.length() == 9)
+ {
+ int pinGroup = inputString.substring(3,4).toInt();
+ if (pinGroup < 0 || pinGroup > 1) { error = true; }
+ int state = inputString.substring(5,6).toInt();
+ if (state < 0 || state > 1) { error = true; }
+ int rising = inputString.substring(7,8).toInt();
+ if (rising < 0 || rising > 1) { error = true; }
+ if (!error)
+ {
+ pinGroupSequencing[pinGroup] = (boolean) state;
+ pinGroupSequenceMode[pinGroup] = rising;
+ if (state)
+ {
+ pinGroupStoredState[pinGroup] = pinGroupState[pinGroup];
+ ttlArrayIndex[pinGroup] = 0;
+ if (!rising)
+ { // if we trigger on the falling edge, set initial state now, and advance counter here
+ setPinGroup(pinGroup, ttlArray[ttlArrayIndex[pinGroup]][pinGroup]); // Check blanking?
+ ttlArrayIndex[pinGroup]++;
+ }
+ } else
+ {
+ pinGroupState[pinGroup] = pinGroupStoredState[pinGroup];
+ }
+ char out[20];
+ sprintf(out, "!PDS%d%c%d%c%d", pinGroup, sep, state, sep, rising);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(pdsErrorString);
+ }
+ }
+
+ /**
+ * "BDO" - Activates blanking mode of the digital output. Output state will be coupled to the
+ * state of the input trigger. If input trigger is active, the pingroup will be active (
+ * as set with DO), if input trigger is inactive, the pingroup will go low.
+ * Format: "BDOn,s,t" where n is the pingroup (0 or 1), s switches blanking mode on (1)
+ * or off (0), and t sets the mode (0 blank on low, 1 blank on high)
+ */
+ else if (command == "BDO")
+ {
+ error = false;
+ if (inputString.length() == 9)
+ {
+ int pinGroup = inputString.substring(3,4).toInt();
+ if (pinGroup < 0 || pinGroup > 1) { error = true; }
+ byte state = inputString.substring(5,6).toInt();
+ if (state < 0 || state > 1) { error = true; }
+ byte mode = inputString.substring(7,8).toInt();
+ if (mode < 0 || mode > 1) { error = true; }
+ if (!error)
+ {
+ pinGroupBlanking[pinGroup] = state == 1;
+ pinGroupBlankOnLow[pinGroup] = mode == 0;
+ setPinGroupCheckBlanking(pinGroup);
+ char out[20];
+ sprintf(out, "!BDO%d%c%d%c%d", pinGroup, sep, state, sep, mode);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(bdoErrorString);
+ }
+ }
+
+ /**
+ * Sets voltage range of DACS
+ */
+ else if(command == "SAR")
+ {
+ error = false;
+ byte dacNum = inputString.substring(3).toInt();
+ byte pp = 5;
+ if(dacNum >9)
+ {
+ pp=6;
+ }
+ byte rangeVal = inputString.substring(pp).toInt();
+ if(rangeVal < 1 || rangeVal > 5)
+ {
+ error = true;
+ } //force to max range
+ if(dacNum < 1 || dacNum > 16)
+ {
+ error = true;
+ } //confirm if input channel range is valid
+ if(!error) //if range is OK perform command
+ {
+ Serial.print("!SAR"); //print recieve message to operator
+ Serial.print(dacNum);
+ Serial.print(sep);
+ Serial.println(rangeVal);
+ setDacRange(dacNum-1,rangeVal-1);
+ // 0 the output
+ int value = 0;
+ if (rangeVal > 2)
+ {
+ value = 65535 / 2;
+ }
+ dacState[dacNum - 1] = value;
+ setDac(dacNum - 1, value);
+ } else
+ {
+ Serial.println(sarErrorString);
+ }
+ }
+
+ // Set Signal LED flag
+ else if (command == "SSL")
+ {
+ error = false;
+ byte result = inputString.substring(3).toInt();;
+ if (result == 0)
+ {
+ useSignalLEDs_ = false;
+ digitalWrite(dacLed, 0);
+ digitalWrite(ttlLed, 0);
+ digitalWriteDirect(trigLed,0);
+ }
+ else if (result == 1)
+ {
+ useSignalLEDs_ = true;
+ // TODO: make sure the LEDs are set correctly?
+ }
+ else error = true;
+ if (!error)
+ {
+ Serial.print("!SSL");
+ Serial.println(result);
+ } else
+ {
+ Serial.println(sslErrorString);
+ }
+ }
+
+ //status commands
+ else if(inputString == "STAT?\n") {debug(); }
+ else if(inputString == "TEST?\n") {diagTest(); }
+ else if(inputString == "CLEAR_ALL\n") {clearTable(); }
+ else if(inputString.substring(0,9) == "CLEAR_DAC") {clearDac(); }
+
+ else
+ {
+ Serial.println(generalErrorString);
+ }
+
+
+ clearSerial();
+ digitalWrite(readyLed,HIGH);
+ } //EXIT LOOP FOR SERIAL HERE
+
+
+/*********************This block runs the high speed control interface *********************/
+
+/****checks the acquisition order
+ * mode 0 == channel first eg set ch1 stweep Z
+ * mode 1 == Z first EG step Z then Ch1 Ch2 then Step Z ...
+ */
+
+/*
+ while(trigArmed)
+ { // just sit here and wait for the next command until armed is off, which can only happen @ end of sequence
+ unsigned long tStart = millis() + timeOut; //set timeout position
+ unsigned long tLed = 0;
+ // focus commands = start, step, #loops,direction,slave,current step
+ //if(program == 0) {inTrigger=true; } //force a first step
+
+ if( inTrigger )
+ { //we recieved a trigger from our source
+ // and is mode 0, and is at 0 position OR
+ // and is mode 1, and is at any position OR
+ // if focus isn't enabled
+ */
+ /*
+ * When should a channel update be issued? When
+ * focus is ON, and mode is sweep per channel (0) AND F position is 0
+ * focus is ON, and mode is slave to channel (1)
+ * focus is off any time
+ */
+
+ // boolean runUpdate = true;
+ /*
+ if( (focArray[1] != 0) && (focArray[4] == 0) ){ //if we are using the focus and focus will sweep through a single channel setting
+ if( focArray[5] == 0 ) { runUpdate = true;} //AND if the focus is at position 0 (start of sweep)
+ }
+
+ if( (focArray[1] !=0) && (focArray[4] == 1)) { runUpdate = true; } // Case where channel is switching each Z plane
+ if(focArray[1] == 0) {runUpdate=true;} //Case where no focus block used so update the channel
+ */
+ /*
+ //do DAC and TTL control stuff, not focus stuff though
+ if(runUpdate) {
+ byte walker=0;
+ for(walker = 0 ; walker < 15 ; ++walker ){ //sets DACs 1-16
+ dac_write(10,0, DAC [walker], dacArray [program] [walker]); // Set DAC Lines
+ digitalWriteDirect( ttl [walker] , ttlArray[program] [walker] ); //set TTL lines
+ }
+ digitalWriteDirect( ttl [walker] , ttlArray[program] [walker] ); //set 16th TTL line -
+ }
+ ++program;
+*/
+ /* THIS MOVES AROUND THE Z FOCUS
+ * in this case, we assume a trigger was recieved, but we should only mess with focus stuff if it's on and if it's needed
+ * here we only want to update the focus position in the case that EITHER
+ * Mode = 0 Channel 1 - Z 1, Z2, Z3
+ * mode = 1 Z 1 - CH1, CH2, Z2, Ch1, Ch2
+ */
+/*
+ if( (focArray[1] != 0) && ( focArray[4]==0 )){ fastFocus(); } // if any focus array is active, and MODE = SWEEP OVER CHANNEL
+ if( (focArray[1] != 0) && ( focArray[4]==1 )){ // in this case sweep is active and MODE = STEP AFTER CHANNEL
+ if((program == maxProgram-1) && (focArray[5] <= focArray[2])) {fastFocus();}
+ }
+
+ delay(delArray[program]); //wait for specified delay
+
+ if( focArray[1] == 0) {++program;} //if not using focus at all
+ if( (focArray[1] != 0) && (focArray[4] == 1) ) { //focus is used but in step mode
+ ++program;
+ if( (program > maxProgram) && (focArray[5] != 0) ) { //because we are stepping the focus, program must be reset to 0 in this case we know the focus has not completed, so we can reset the main program array position
+ program=0;
+ }
+ }
+ */
+ } //END OF TRIGGER RESPONSE
+
+ /*
+ inTrigger=false; //turn off trigger
+ tStart = millis() + timeOut; //update timeout delta for next run
+
+ //Done moving stuff, now wait for the next input
+ while(!inTrigger){ //wait for next pulse
+ if(millis() > tStart) {
+ trigArmed=false;
+ program = maxProgram+1;
+ Serial.println("Timeout Exceeded");
+ allOff();
+ break;
+ }
+ //we hit the timeout so end the sequence
+ if(tLed < millis() )
+ {
+ digitalWrite(readyLed,!digitalRead(readyLed));
+ tLed = millis() + 30;
+ }
+ }
+
+ if(program > maxProgram) { //end and cleanup
+ ++runCycles;
+ if(runCycles == timeCycles) { //we are done
+ trigArmed=false;
+ program=0;
+ allOff();
+ Serial.println("!SAFE");
+ digitalWrite(readyLed,HIGH);
+ }
+ if(runCycles < timeCycles) {program = 0;}
+ }
+ } //close trigarmed
+} //close main loop
+*/
+
+
+void allOff()
+{
+ for(byte walker=0;walker<15;++walker)
+ { //sets DACs 1-16
+ dac_write(10,0, DAC [walker], 0); // Set DAC Lines
+ }
+ setPinGroup(0,0);
+ setPinGroup(1,0);
+}
+
+
+void clearSerial()
+{
+ //STUFF TO CLEAN OUT THE SERIAL LINE
+ inputString = ""; // clear the string:
+ stringComplete = false;
+}
+
+void setDac(byte dNum,int dVal)
+{
+ dac_write(10,0, dNum, dVal); // Send dac_code
+ //led indication
+ if (useSignalLEDs_)
+ {
+ int dacSum = 0;
+ for (byte d=0; d < 16; d++)
+ {
+ dacSum += dacState[d];
+ }
+ digitalWrite(dacLed, dacSum > 0);
+ }
+}
+
+// sets the output state of the given pin number
+void setTTL(byte t1, boolean t2)
+{
+ byte pin = t1 - 1;
+ digitalWriteFast(ttl[pin], t2);
+ if(t2)
+ {
+ bitSet(ttlState, pin);
+ } else
+ {
+ bitClear(ttlState, pin);
+ }
+ if (useSignalLEDs_)
+ {
+ digitalWriteFast(ttlLed, ttlState > 0);
+ }
+}
+
+void setDacCheckBlanking(byte dacNr)
+{
+ if (dacBlanking[dacNr])
+ {
+ triggerPinState = digitalReadFast(trig[0]);
+ dacBlankOnLow[dacNr] == triggerPinState ?
+ setDac(dacNr, dacState[dacNr]) : setDac(dacNr, 0);
+ } else {
+ setDac(dacNr, dacState[dacNr]);
+ }
+}
+
+
+void setPinGroupCheckBlanking(byte pinGroup)
+{
+ if (pinGroupBlanking[pinGroup])
+ {
+ triggerPinState = digitalReadFast(trig[0]);
+ pinGroupBlankOnLow[pinGroup] == triggerPinState ?
+ setPinGroup(pinGroup, pinGroupState[pinGroup]) : setPinGroup(pinGroup, 0);
+ } else {
+ setPinGroup(pinGroup, pinGroupState[pinGroup]);
+ }
+}
+
+inline void setPinGroup(byte, byte) __attribute__((always_inline));
+// sets pins 1-8 (pinGroup 0) or 9-16 (pinGroup 1) according to the mask in value
+inline void setPinGroup(byte pinGroup, byte value)
+{
+ byte adder = (pinGroup) * 8;
+ for (byte b = 0; b < 8; b++)
+ {
+ if( b+adder < 9 ) {
+ digitalWriteFast(ttl[b + adder], (((value) >> (b)) & 0x01) );
+ }
+
+ else if( b+adder > 8) {
+ digitalWriteFast(ttl[b+adder],(((value) >> (b)) & 0x01));
+ }
+
+ //Serial.print("TTL " );
+ //Serial.print(b + adder);
+ //Serial.print(" , ");
+ //Serial.println((((value) >> (b)) & 0x01));
+
+ }
+ if (pinGroup == 1)
+ {
+ ttlState = (ttlState & 0xff00) | value;
+ } else
+ {
+ ttlState = (ttlState & 0x00ff) | (value << 8);
+ }
+ if (useSignalLEDs_)
+ {
+ digitalWriteFast(ttlLed, ttlState > 0);
+ }
+}
+
+/*
+ SerialEvent occurs whenever a new data comes in the hardware serial RX. This
+ routine is run between each time loop() runs, so using delay inside loop can
+ delay response. Multiple bytes of data may be available.
+*/
+void serialEvent() {
+ trigArmed = false;
+ while (Serial.available()) {
+ // get the new byte:
+ char inChar = (char)Serial.read();
+ // add it to the inputString:
+ inputString += inChar;
+ // if the incoming character is a newline, set a flag
+ // so the main loop can do something about it:
+ if (inChar == '\n') {
+ stringComplete = true;
+ }
+ }
+}
+
+/*INTERRUPT CODE FOR TTL INPUT ***************/
+/*
+void sigIn() //sigin is the input response line - this recieves the trigger input from an external source
+{
+ //if(!trigArmed)
+ //{
+ digitalWrite(trigLed,!digitalRead(trigLed));
+ //}
+ inTrigger=true;
+}
+
+void configureTrigger(byte tOption)
+{
+ trigMode = tOption; //assign input value to global
+ switch (trigMode) {
+ case 0:
+ attachInterrupt(trig[0],sigIn,LOW);
+ break;
+ case 1:
+ attachInterrupt(trig[0],sigIn,HIGH);
+ break;
+ case 2:
+ attachInterrupt(trig[0],sigIn,RISING);
+ break;
+ case 3:
+ attachInterrupt(trig[0],sigIn,FALLING);
+ break;
+ case 4:
+ attachInterrupt(trig[0],sigIn,CHANGE);
+ break;
+ }
+}
+*/
+
+void debug()
+{
+ if(debugT < millis() ) //if this is the first time debug has run, this will execute, otherwise has enough time elapsed?
+ {
+ Serial.print("Input Trigger = ");
+ Serial.println(digitalReadFast(trig[0]));
+ //Serial.print(", OpMode = ");
+ //Serial.println(opMode);
+
+ // REPORT TTL
+ Serial.print("TTL: ");
+ for(int i=0;i<16;++i)
+ {
+ char sOut[100];
+ sprintf(sOut,"%d=%d,",i+1, digitalRead(ttl[i]));
+ Serial.print(sOut);
+ }
+ Serial.println("");
+
+ //REPORT DAC
+ Serial.print("DAC:");
+ for(int i=0;i<16;++i)
+ {
+ char sOut[200];
+ sprintf(sOut,"%d=%u,",i+1, dacState[i]);
+ Serial.print(sOut); //used to print sOut
+ }
+
+ Serial.println();
+ Serial.println("Digital output buffers:");
+ for (int pg = 0; pg < 2; pg++)
+ {
+ if (pg == 0) Serial.print("Pins 1-8 buffer size 1-8: ");
+ else if (pg == 1) Serial.print("Pins 9-16 buffer size: ");
+ char sOut[6];
+ sprintf(sOut, "%d", ttlArrayMaxIndex[pg]);
+ Serial.println(sOut);
+ for (int i = 0; i < ttlArrayMaxIndex[pg]; i++)
+ {
+ sprintf(sOut, "%u ", ttlArray[i][pg]);
+ Serial.print(sOut);
+ }
+ if (ttlArrayMaxIndex[pg] > 0) { Serial.println(); }
+ }
+
+ Serial.println("DAC buffers:");
+ for (int dac = 0; dac < NR_DACS; dac++)
+ {
+ char sOut[25];
+ sprintf(sOut, "DAC buffer size: %d", dacArrayMaxIndex[dac]);
+ Serial.println(sOut);
+ for (int i = 0; i < dacArrayMaxIndex[dac]; i++)
+ {
+ sprintf(sOut, "%u ", dacArray[i][dac]);
+ Serial.print(sOut);
+ }
+ if (dacArrayMaxIndex[dac] > 0) { Serial.println(); }
+ }
+
+ }
+ // send an empty line so that the receiver know we are done
+ Serial.println("");
+}
+
+ /*
+ //Report program arrays
+ if(opMode == 3)
+ {
+ Serial.println("Sequencer Programming Status");
+ Serial.print("MaxProgram = ");
+ Serial.println(maxProgram);
+ Serial.print("PCHANNEL = ");
+ Serial.println(pChannel);
+ //report DACs
+ Serial.println("PROG, DAC1, DAC2, DAC3, DAC4, DAC5, DAC6, DAC7, DAC8, DAC9,DAC10,DAC11,DAC12,DAC13,DAC14,DAC15,DAC16/FOCUS");
+ for(int p=0;p= 0; i--)
+ rx[i] = SPI.transfer(tx[i]); //! 2) Read and send byte array
+
+ output_high(cs_pin); //! 3) Pull CS high
+}
+
+inline void digitalWriteDirect(int, boolean) __attribute__((always_inline));
+
+void digitalWriteDirect(int pin, boolean val){
+ digitalWriteFast(pin,val);
+}
+
+/*
+inline int digitalReadDirect(int) __attribute__((always_inline));
+
+int digitalReadDirect(int pin){
+ return !!(g_APinDescription[pin].pPort -> PIO_PDSR & g_APinDescription[pin].ulPin);
+}
+
+inline int digitalReadOutputPin(int) __attribute__((always_inline));
+
+int digitalReadOutputPin(int pin){
+ return !!(g_APinDescription[pin].pPort -> PIO_ODSR & g_APinDescription[pin].ulPin);
+}
+*/
+
+
+
+/*******************LICENSING INFO**************
+
+ * Copyright (c) 2018, Advanced Research Consulting
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ *
+ *******************/
\ No newline at end of file
diff --git a/src/Overdrive/diagram.png b/src/Overdrive/diagram.png
new file mode 100644
index 0000000..58d94a5
Binary files /dev/null and b/src/Overdrive/diagram.png differ
diff --git a/src/TriggerScope_V3/README.md b/src/TriggerScope_V3/README.md
index c4d9b34..373b7a8 100644
--- a/src/TriggerScope_V3/README.md
+++ b/src/TriggerScope_V3/README.md
@@ -1,16 +1,4 @@
-# Triggerscope
-Micro-Manager-specific firmware for the Triggerscope.
+# Installation
+This firmware is intended for TriggerScope V3 boards (serial numbers 1932-1970).
-The [Triggerscope 3](http://arc.austinblanco.com/product/triggerscope-3/)
-is an Arduino-based device build and sold by "Advanced Research Consulting"
-(https://arc.austinblanco.com/) that provides analog and digital outputs.
-
-This firmware - in combination with the Micro-Manager "TriggerscopeMM" device adapter,
-allows for control of 16 analog and 16 digital outputs. Outputs can
-transition between settings triggered by a Trigger input
-signal at micro-second time scales. For full documentation
-of the use of this firmare, see: https://micro-manager.org/wiki/TriggerScopeMM
-
-To install this firmware, you download and install the [Arduino IDE](https://www.arduino.cc/en/software). Then power your TriggerScope, connect the USB cable to your computer, and open the file TriggerScope_V3/TriggerScope_V3.ino in the [Arduino IDE](https://www.arduino.cc/en/software). Select the port under which your device appears (Tools > Port), and select the Arduino Due Programmin port (Tools > Board). Press the "verify" button, and - if compilation succeeds - press the Upload button.
-
-Some timing measurements using the "blanking" mode for TTL1-8 in Micro-Manager: It takes about 10-13 micro-seconds for the TTL output to respond to a change in the input trigger. Adding sequences to DACs or other outputs will likely increase this delay.
+To install this firmware, you download and install the [Arduino IDE](https://www.arduino.cc/en/software). Then power your TriggerScope, connect the USB cable to your computer, and open the file TriggerScope_V3/TriggerScope_V3.ino in the [Arduino IDE](https://www.arduino.cc/en/software). Select the port under which your device appears (Tools > Port), and select the Arduino Due Programming port (Tools > Board). Press the "verify" button, and - if compilation succeeds - press the Upload button.
\ No newline at end of file
diff --git a/src/TriggerScope_V4/README.md b/src/TriggerScope_V4/README.md
index 76bce99..4c6f0ea 100644
--- a/src/TriggerScope_V4/README.md
+++ b/src/TriggerScope_V4/README.md
@@ -1,14 +1,4 @@
-# Triggerscope
-Micro-Manager-specific firmware for the Triggerscope.
+# Installation
+This firmware is intended for TriggerScope V4 boards (serial numbers 1971-2117).
-The [Triggerscope 4](http://arc.austinblanco.com/product/triggerscope-4/)
-is an Arduino-based device build and sold by "Advanced Research Consulting"
-(https://arc.austinblanco.com/) that provides analog and digital outputs.
-
-This firmware - in combination with the Micro-Manager "TriggerscopeMM" device adapter,
-allows for control of 16 analog and 16 digital outputs. Outputs can
-transition between settings triggered by a Trigger input
-signal at micro-second time scales. For full documentation
-of the use of this firmare, see: https://micro-manager.org/wiki/TriggerScopeMM
-
-To install this firmware, you will first need to download and install the [TeensyDuino package](https://www.pjrc.com/teensy/td_download.html), and the [Arduino IDE](https://www.arduino.cc/en/software). Then power your TriggerScope, connect the USB cable to your computer, and open the file TriggerScope_613MM/TriggerScope_613MM.ino in the [Arduino IDE](https://www.arduino.cc/en/software). Select the port under which your device appears (Tools > Port), and select the Teensy 4.1 (Tools > Board). Press the "verify" button, and - if compilation succeeds - press the Upload button.
+To install this firmware, you will first need to download and install the [TeensyDuino package](https://www.pjrc.com/teensy/td_download.html), and the [Arduino IDE](https://www.arduino.cc/en/software). Then power your TriggerScope, connect the USB cable to your computer, and open the file TriggerScope_V4/TriggerScope_V4.ino in the [Arduino IDE](https://www.arduino.cc/en/software). Select the port under which your device appears (Tools > Port), and select the Teensy 4.1 (Tools > Board). Press the "verify" button, and - if compilation succeeds - press the Upload button.
diff --git a/src/TriggerScope_V4B/Linduino.h b/src/TriggerScope_V4B/Linduino.h
new file mode 100644
index 0000000..85ee651
--- /dev/null
+++ b/src/TriggerScope_V4B/Linduino.h
@@ -0,0 +1,109 @@
+//! @todo Review this file.
+/*
+Linduino.h
+
+This file contains the hardware definitions for the Linduino.
+
+REVISION HISTORY
+$Revision: 1906 $
+$Date: 2013-08-26 15:09:18 -0700 (Mon, 26 Aug 2013) $
+
+Copyright (c) 2013, Linear Technology Corp.(LTC)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of Linear Technology Corp.
+
+The Linear Technology Linduino is not affiliated with the official Arduino team.
+However, the Linduino is only possible because of the Arduino team's commitment
+to the open-source community. Please, visit http://www.arduino.cc and
+http://store.arduino.cc , and consider a purchase that will help fund their
+ongoing work.
+*/
+
+//! @defgroup Linduino Linduino: Linear Technology Arduino-Compatible Demonstration Board
+
+/*! @file
+ @ingroup Linduino
+ @ingroup QuikEval
+ Header File for Linduino Libraries and Demo Code
+*/
+
+#ifndef LINDUINO_H
+#define LINDUINO_H
+
+//! @name LINDUINO PIN ASSIGNMENTS
+//! @{
+
+#define QUIKEVAL_GPIO 9 //!< Linduino QuikEval GPIO pin (QuikEval connector pin 14) connects to Arduino pin 9
+#define QUIKEVAL_CS SS //!< QuikEval CS pin (SPI chip select on QuikEval connector pin 6) connects to Arduino SS pin.
+#define QUIKEVAL_MUX_MODE_PIN 8 /*!< QUIKEVAL_MUX_MODE_PIN defines the control pin for the QuikEval MUX.
+The I2C port's SCL and the SPI port's SCK signals share the same pin on the Linduino's QuikEval connector.
+Additionally, the I2C port's SDA and the SPI port's MOSI signals share the same pin on the Linduino's QuikEval connector.
+The pair of pins connected to the QuikEval connector is switched using a MUX on the Linduino board.
+The control pin to switch the MUX is defined as QUIKEVAL_MUX_MODE_PIN (Arduino pin 8). */
+//! @}
+
+// Macros
+//! Set "pin" low
+//! @param pin pin to be driven LOW
+#define output_low(pin) digitalWrite(pin, LOW)
+//! Set "pin" high
+//! @param pin pin to be driven HIGH
+#define output_high(pin) digitalWrite(pin, HIGH)
+//! Return the state of pin "pin"
+//! @param pin pin to be read (HIGH or LOW).
+//! @return the state of pin "pin"
+#define input(pin) digitalRead(pin)
+
+//! @todo Make a note about whether Arduino/Linduino is Big Endian or Little Endian. Raspberry Pi appears to be the opposite.
+//! This union splits one int16_t (16-bit signed integer) or uint16_t (16-bit unsigned integer)
+//! into two uint8_t's (8-bit unsigned integers) and vice versa.
+ union LT_union_int16_2bytes
+ {
+ int16_t LT_int16; //!< 16-bit signed integer to be converted to two bytes
+ uint16_t LT_uint16; //!< 16-bit unsigned integer to be converted to two bytes
+ uint8_t LT_byte[2]; //!< 2 bytes (unsigned 8-bit integers) to be converted to a 16-bit signed or unsigned integer
+ };
+
+//! @todo Make a note about whether Arduino/Linduino is Big Endian or Little Endian. Raspberry Pi appears to be the opposite.
+//! This union splits one int32_t (32-bit signed integer) or uint32_t (32-bit unsigned integer)
+//! into four uint8_t's (8-bit unsigned integers) and vice versa.
+union LT_union_int32_4bytes
+{
+ int32_t LT_int32; //!< 32-bit signed integer to be converted to four bytes
+ uint32_t LT_uint32; //!< 32-bit unsigned integer to be converted to four bytes
+ uint8_t LT_byte[4]; //!< 4 bytes (unsigned 8-bit integers) to be converted to a 32-bit signed or unsigned integer
+};
+
+//! @todo Make a note about whether Arduino/Linduino is Big Endian or Little Endian. Raspberry Pi appears to be the opposite.
+//! This union splits one float into four uint8_t's (8-bit unsigned integers) and vice versa.
+union LT_union_float_4bytes
+{
+ float LT_float; //!< float to be converted to four bytes
+ uint8_t LT_byte[4]; //!< 4 bytes (unsigned 8-bit integers) to be converted to a float
+};
+
+
+#endif // LINDUINO_H
diff --git a/src/TriggerScope_V4B/README.md b/src/TriggerScope_V4B/README.md
new file mode 100644
index 0000000..2c4d30d
--- /dev/null
+++ b/src/TriggerScope_V4B/README.md
@@ -0,0 +1,4 @@
+# Installation
+This firmware is intended for TriggerScope V4B boards (serial numbers 2118 and later).
+
+To install this firmware, you will first need to download and install the [TeensyDuino package](https://www.pjrc.com/teensy/td_download.html), and the [Arduino IDE](https://www.arduino.cc/en/software). Then power your TriggerScope, connect the USB cable to your computer, and open the file TriggerScope_613MM/TriggerScope_613MM.ino in the [Arduino IDE](https://www.arduino.cc/en/software). Select the port under which your device appears (Tools > Port), and select the Teensy 4.1 (Tools > Board). Press the "verify" button, and - if compilation succeeds - press the Upload button.
diff --git a/src/TriggerScope_V4B/TriggerScope_V4B.ino b/src/TriggerScope_V4B/TriggerScope_V4B.ino
new file mode 100644
index 0000000..374c9f9
--- /dev/null
+++ b/src/TriggerScope_V4B/TriggerScope_V4B.ino
@@ -0,0 +1,1793 @@
+/******************************************
+ Trigger Scope v. 604MM for Arduino microscope control by
+ ADVANCED RESEARCH CONSULTING @ 2015
+ Regents of the University of California, 2020
+
+
+
+ Command set:
+
+ Commands to the device are in ASCI and terminated with "\n".
+ Returns can be multiple lines, terminated with "\r\n" (carriage retun / new line).
+ Error messages start with "!ERROR_" followed by the command causing the error, semi-colon
+ and error message (example: "!ERROR_RANGE: Command out of range, DAC=1-16, Range = 1-5...")
+
+ "*" - prints ID (example: "ARC TRIGGERSCOPE 16 R3C v.604-MM"
+ "?" - pirnts message listing available commands and their parameters
+ ----------------------analog output functions-------------------------
+ "SAO" - sets the voltage of specified DAC. Expected format: "DAC1-4236"
+ where 1 is the line nr, and 4236 the new value to be send to the DAC.
+ Resulting voltage depends on bit range of the DAC, and the voltage range
+ Returns: "!DAC1,4236" or error message (not starting with !DAC) on failure.
+"PAN" - Queries the number of analog output states that can be pre-programmed.
+ Format: "PANn".
+ Where n=1-16 (DAC1-16).
+ Returns: "!PANn,q" where q is the maximum number of states that can be programmed
+ "PAO" - Sends sequence of analog output states to be used in triggered sequences.
+ Format: "PAOn-s-o1-o2-on", where
+ - n=1-16 for DAC 1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (0-65535) to be consecutively inserted in the list.
+ Returns: "!PA0n-s-q" where q is the number of values successfully inserted in the internal buffer.
+ "PAC" - Clears the sequence of ananlog states
+ Format: "PACn", where n is the DAC pinNr (1-16)
+ "PAS" - Starts triggered transitions in analog output state as pre-loaded in PAO
+ Format: "PASn-s-t" where
+ - n=1-16 for DAC 1-16
+ = s=0 or 1 where 0 stops and 1 starts triggered transitions
+ - t=0 or 1 and determines whether transition will happen on the falling (0) or rising (1) edge
+ of a pulse on the input pin
+"BAO" - Activates blanking mode of the analog output. Output state will be coupled to the
+ state of the input trigger. If input trigger is active, the DAC will be active (
+ as set with SDA), if input trigger is inactive, the output will go (which may not be 0V, depending on the range).
+ Format: "BAOn-s-t"
+ - n=1-16 for DAC 1-16
+ - s=0 or 1 where 0 stops and 1 starts balnking mode
+ - t translates state of the input pin to its activity for blanking. If t = 0,
+ DAC will be 0 when input trigger pin is low. t = 1: DAC will be active when input trigger is low.
+"BAD" - Sets a delay in blanking mode of analog output. When the trigger is received to activate the analog output,
+ wait for the set amount of time (in micro-seconds) to actually activate the analog output. This command will only
+ have an effect in combination iwth blanking mode (BAO).
+ Format: "BADn-t"
+ - n=1-16 for DAC 1-16
+ - t=delay between trigger and actually activating the output in micro-seconds (default is 0 for no delay,
+ no negative numbers allowed, maximum delay is 4,294,967,295)
+"BAL" - Sets the lenght of analog output "pulse" in blanking mode" in micro-seconds. After start of the pulse (determined
+ by BAO and BAD, output will go to 0V after the given number of microseonds, or when the input trigger so dictates
+ (whichever comes first).
+ Format: "BALn-t"
+ - n=1-16 for DAC 1-16
+ - t=maximum duration of the output pulse in microseconds (0 for no limit, maximum value is 4,294,967,295)
+ "SAR" - Sets the output range in volts of the DAC. Example: "SAR2-1" where 2
+ specified the DAC number, and 1 the range. Allowed ranges are number 1-5:
+ 1: 0-5V
+ 2: 0-10V
+ 3: -5-+5V
+ 4: -10-+10V
+ 5: -2-+2V
+ Returns: "!RANGE2,1"
+-------------------------digital output functions------------------------
+"SDO" - sets TTL outputs of pins1-8 and 9-16 in a single operation
+ Format: DOn-o
+ Where n=0 or 1. 0 address pins 1-8, 1 goes to pins 9-16
+ 0 is digital output bitmask, 0: all off, 1: pin 1 on, 2: pin 2 on, 3: pins 1&2 on, etc..
+ Example: DO1,4 sets pin 3 high and pins 1,2, 4-8 low
+ Returns: "!DOn-o" or error message ("!ERROR_DO: ") on failure.
+"PDN" - Queries the number of digital output states that can be pre-programmed.
+ Format: "PDNn".
+ Where n=0 or 1. 0 address pins 1-8, 1 goes to pins 9-16
+ Returns: "!PDNn-q" where q is the maximum number of states that can be programmed
+"PDO" - Sends sequence of digital output states to be used in triggered sequences.
+ Format: "PDOn-s-o1-o2-on", where
+ - n=0 or 1 and directs the command to either pins1-8 or 9-16,
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values to be consecutively inserted in the list.
+ Returns: "!PD0n-s-q" where q is the number of values successfully inserted in the internal buffer.
+"PDC" - Clears the sequence of digital states
+ Format: "PDCn", where n is the pingroup, 0=pins1-8, 1=pins9-16
+"PDS" - Starts triggered transitions in digital output state as pre-loaded in PDO
+ Format: "PDSn-s-t" where
+ - n=0 or 1 and directs the command to either pins 1-8 or 9-16
+ = s=0 or 1 where 0 stops and 1 starts triggered transitions
+ - t=0 or 1 and determines whether transition will happen on the falling (0) or rising (1) edge
+ of a pulse on the input pin
+"BDO" - Activates blanking mode of the digital output. Output state will be coupled to the
+ state of the input trigger. If input trigger is active, the pingroup will be active (
+ as set with DO), if input trigger is inactive, the pingroup will go low.
+ Format: "BDOn-s-t"
+ where n is the pingroup (0 or 1) and s switched blanking mode on (1) or off (0).
+ t translates state of the input pin to its activity for blanking. If t = 0,
+ output pins will be low when input trigger pin is low. When t = 1, output pins
+ will be active when input trigger is low.
+ "SSL" - Set Signal LEDs.
+ In some cases, setting signal LEDs slows down triggered operation, so offer the option to
+ not set them.
+ Format: "SSLn" where
+ - n - 0 ("Off") or 1 ("On")
+ Signal LEDs are On by default
+
+
+
+*****************************
+Contact Advanced Research Consulting for Driver libraries! www.advancedresearch.consulting
+ ******************************/
+#include
+#include
+#include
+#include "Linduino.h"
+
+#define focus 15 //sets focus line #
+#define pwrLed 28 //POWER indication
+#define dacLed 29 //DAC indication
+#define ttlLed 34 //TTL indication
+#define trigLed 35 //TRIGGER indication
+#define readyLed 32 //READY indication
+#define ttlblock2OE 23
+#define ttlblock2DIR 24
+#define ttlblock1OE 21
+#define ttlblock1DIR 22
+#define ttlblock3OE 37
+#define ttlblock3DIR 36
+
+#define NR_DACS 16
+#define NR_DAC_STATES 1200
+#define NR_DO_STATES 1200
+
+
+String idname = "ARC TRIGGERSCOPE 16 R4 Board 4B v.620-MM";
+
+const char* helpString = "Available commands: \r\n"
+ "SAOn-s - sets DACs. n = DAC number (1-16), s = value (0-65535)\r\n"
+ "PANn - queries the number of programmable analogoutput states, n = DAC pin number (1-16)\r\n"
+ "PAOn-s-o1-o2-on - sends sequence of programmable analog output states, n = DAC pin number (1-16)\r\n"
+ " s = 0-based index in the sequence to start inserting, \r\n"
+ " o = comma separated series of output values (0-65535)\r\n"
+ "PACn - clears the sequence of analog states, n = DAC pin number (1-16)\r\n"
+ "PASn-s-t - starts triggered transition in programmable analog output states as programmed in PAO\r\n"
+ " n = DAC pin number (1-16), s = 0 (stops) or 1 (starts), \r\n"
+ " t = transition on falling (0) or rising (1) edge of input trigger.\r\n"
+ "BAOn-s-t starts blanking mode of ananlog output. n = DAC pin number (1-16), \r\n"
+ " s = 0 (stops) or 1 (starts), t = output low when trigger low (0), or inverse (1)\r\n"
+ "BADn-t sets delay between blanking transition and setting analog output.\r\n"
+ " n = DAC pin number (1-16), t = delay in micro-seconds\r\n"
+ "BALn-t sets maximum duration of the analog output in blanking mode.\r\n"
+ " n = DAC pin number (1-16), t = maximumi duration in microseconds.\r\n"
+ "SARn-s - sets DAC voltages range. n = DAC number (1-16), s= 1:0-5V 2:0-10V 3:-5-+5V 4: -10-+10V 5:-2-+2V\r\n"
+ "SDOn-s - sets digital output state, n = 0(pins1-8) or 1(pins9-16), s binary mask 0-255\r\n"
+ "PDNn - queries the number of programmable digital output states, n = pin group 0(1-8) or 1 (9-16)\r\n"
+ "PDOm-s-o1-o2-on - sends sequence of programmable digital output states, n = pin group 0(1-8) or 1(9-16)\r\n"
+ " s = 0-based index in the sequence to start inserting\r\n"
+ " o = comma separated series of output states (0-255)\r\n"
+ "PDCn - clears the sequence of digital states, n = pin group 0(1-8) or 1 (9-16)\r\n"
+ "PDSn-s-t - starts triggered transitions in digital output state as pre-loaded in PDO, \r\n"
+ " n = pin group 0(1-8) or 1 (9-16), s = 0 stops 1 starts, \r\n"
+ " t = transition on falling (0) or rising (1) edge of input trigger.\r\n"
+ "BDn-s-t - sync digital output with input trigger. n = pin group 0(1-8) or 1 (9-16)\r\n"
+ " s = 0 stops 1 starts, t = output low when trigger low (0), or inverse (1)\r\n"
+ "SSLn - switches use of signal LEDs. n=0 (Off) or n=1 (On)\r\n"
+ "\r\n"; // empty line to signal end of help textd
+
+
+const char sep = '-';
+
+//new IC assignments and SD card access for V4 board
+File myFile; //create settings file
+const int chipSelect = BUILTIN_SDCARD; //set SD card access CS line
+
+//set up menu modes and vars
+byte opMode = 1; //sets operation mode, 1=MANUAL control, 2 = PC CONTROL, 3=TTL Slave via Programmed Steps
+boolean trigArmed=false; //enables while loop for just the high speed trig sequencing
+unsigned long debugT=0; //debugger flag - not used in a few versions
+unsigned long trigInterval; //tracks frequency of inputs from camera or external source
+int trigStep=0; //optionally used to sequence more than 1 PROG line at a time, not fully implemented
+String inputString = ""; // a string to hold incoming data
+boolean stringComplete = false; // whether the string is complete
+
+//PIN ASSIGNMENTS
+const byte DAC[NR_DACS] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; //MOVE THESE FOR CUSTOMERS IF NEEDED!
+const byte ttl[NR_DACS] = {5,6,7,8,14,15,16,17,20,25,26,27,38,39,40,41}; //ttl pin #'s
+const byte trig[4] = {0,1,2,3};
+
+/*HIGH SPEED PROGRAMMING MODE MEMORY BLOCK*/
+int dacArray[NR_DAC_STATES][NR_DACS] = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}; // DACprogram list
+uint8_t ttlArray[NR_DO_STATES][2] = {{0,0}}; // digital output states program list
+int ttlArrayMaxIndex[2] = {0, 0}; // maintains the max index in the array that was set
+int ttlArrayIndex[2] = {0, 0}; // keeps track of current position in the array
+int dacArrayMaxIndex[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+int dacArrayIndex[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+uint32_t dacBlankDelay[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+uint32_t dacBlankDuration[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+boolean useSignalLEDs_ = true;
+
+// data structures to be assembled from blanking settings above that have a time-ordered sequence of events
+int dacBlankEventsNr = 0;
+// there can be a delay and a duration for each blanking event
+uint32_t dacBlankEventNextWait[2 * NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+uint8_t dacBlankEventPinNr[2 * NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+// 0 : off, 1: set normal state, 2: set value from dacArray
+uint8_t dacBlankEventState[2 * NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+byte pinGroupState[2] = {0, 0};
+byte pinGroupStoredState[2] = {0, 0};
+bool pinGroupBlanking[2] = {false, false};
+bool pinGroupBlankOnLow[2] = {true, true};
+bool pinGroupSequencing[2] = {false, false};
+byte pinGroupSequenceMode[2] = {0, 0}; // 0: falling edge, 1: rising edge
+int dacState[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+int dacStoredState[NR_DACS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+bool dacBlanking[16] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false};
+bool dacBlankOnLow[16] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false};
+bool dacSequencing[16] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false};
+byte dacSequenceMode[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+bool triggerPinState = false;
+
+int delArray[500]; //time delay array for high speed sequences
+int focArray[6]; //array for focus stacks = start, step, #loops,direction,slave,current step
+uint16_t ttlState = 0;
+// boolean ttlActive[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+int timeCycles = 1; //used for high speed switching inside of a loop
+int runCycles = 0; //holds running position vs total cycles for timelapse
+
+volatile boolean inTrigger=false;
+byte program=0; //this sets the active program # used in the Mode 3 high speed sequencer
+byte maxProgram=0; //this holds the maximum program value entered, and is used to set the maxium sequence step.
+// byte stepMode = 1; //1 = waits for TTL IN, 2=runs continually
+// unsigned long timeOut = 1000; //timeout for sequence (set to 10 seconds by default)
+// unsigned long tStart = 0; //sequence start timer
+// unsigned long trigLedTimer = 0;
+// boolean reportTime = 0;
+// boolean umArmIgnore = 0; //this handles micromanagers multiple ARM commands which are issued back-to-back on MDA acqtivation. Ignores after a short wait.
+// boolean usepwm = false;
+// byte pChannel =0; //number of channels micromanager has attempted to control
+// byte lastPT=20;
+// byte trigMode = 2; //0 = LOW 1 = HIGH 2 = RISING 3 = FALLING 4 = CHANGE
+
+bool error = false;
+
+const char* saoErrorString = "!ERROR_SAO: Format: SAOn-s n=1-16 (DAC1-16), s=value 0-65535";
+const char* sarErrorString = "!ERROR_SAR: Format: SARn-s n=1-16 (DAC=1-16), s=1:0-5V 2:0-10V 3:-5-+5V 4:-10-+10V 5:-2-+2V";
+const char* panErrorString = "!ERROR_PAN: Format: PANn n=1-16 (DAC1-16)";
+const char* paoErrorString = "!ERROR_PAO: Format: PAOn-s-01-02-0n n=1-16 (DAC1-16), s= >=0 (position), 0n=values 0-65535";
+const char* pacErrorString = "!ERROR_PAC: Format: PACn n=1-16 (DAC1-16)";
+const char* pasErrorString = "!ERROR_PAS: Format: PASn-s-t n=1-16 (DAC1-16) s 0=stop 1=start, t=transition on falling(0) or rising(1) edge";
+const char* baoErrorString = "!ERROR_BAO: Format: BAOn-s-t n=1-16 (DAC1-16), s blank 0(off) or 1(on), t 0 (blank on low) or 1 (blank on high)";
+const char* badErrorString = "!ERROR_BAD: Format: BADn-t n=1-16 (DAC1-16), t = 0 - 2147483647";
+const char* balErrorString = "!ERROR_BAD: Format: BALn-t n=1-16 (DAC1-16), t = 0 - 2147483647";
+const char* sdoErrorString = "!ERROR_SDO: Format: SDOn-s n=pingroup 0-1, s=value 0-255";
+const char* pdnErrorString = "!ERROR_PDN: Format: PDNn n=pingroup 0-1";
+const char* pdoErrorString = "!ERROR_PDO: Format: PDOn-s-01-02-0n n=pingroup 0-1, s=position, 0n=values 0-255";
+const char* bdoErrorString = "!ERROR_BDO: Format: BDOn-s-t n=pingroup 0-1, s blank 0(off) or 1(on), t 0 (blank on low) or 1 (blank on high)";
+const char* pdcErrorString = "!ERROR_PDC: Format: PDCn n=pinGroup(1/2)";
+const char* pdsErrorString = "!ERROR_PDS: Format: PDSn-s-t n=pinGroup(1/2) s 0=stop 1=start, t=transition on falling(0) or rising(1) edge";
+const char* sslErrorString = "!ERROR_SSL: Format: SSLn n=0 (Off) or 1 (On)";
+const char* generalErrorString = "ERROR_UNKNOWN_COMMAND";
+
+
+void setup()
+{
+
+ //indicate power is on for Teensy
+ pinMode(pwrLed,OUTPUT);
+ digitalWrite(pwrLed,HIGH);
+
+ //configure LED pins for output
+ pinMode(trigLed,OUTPUT);
+ pinMode(ttlLed,OUTPUT);
+ pinMode(dacLed,OUTPUT);
+
+ //Configure TTL outputs 13-16
+ pinMode(ttlblock2OE,OUTPUT);
+ digitalWrite(ttlblock2OE,LOW); //low for enable
+ pinMode(ttlblock2DIR,OUTPUT);
+ digitalWrite(ttlblock2DIR,HIGH); //HIGH to enable 3.3v -> 5V output
+
+ //configure TTL outputs 5-12
+ pinMode(ttlblock1OE,OUTPUT);
+ digitalWrite(ttlblock1OE,LOW); //*** DISABLED FOR BOARDS PRODUCED 12/21 DUE TO LED CONFLICT. low for enable
+ pinMode(ttlblock1DIR,OUTPUT);
+ digitalWrite(ttlblock1DIR,HIGH); //HIGH to enable 3.3v -> 5V output
+
+ //Configure TTL IN for TRIG 1-4
+ pinMode(ttlblock3OE,OUTPUT);
+ digitalWrite(ttlblock3OE,LOW); //low for enable
+ pinMode(ttlblock3DIR,OUTPUT);
+ digitalWrite(ttlblock3DIR,LOW); //LOW to enable 5v -> 3.3V input
+
+ delay(10);
+ //configureTrigger(trigMode); //will attach interrupt
+ for(byte i=0;i<16;++i) { pinMode(ttl[i],OUTPUT); digitalWrite(ttl[i],LOW); } //SET OUTPUT PINS ON TTL AND CAMERA LINES
+
+ Serial.begin(115200); // start serial @ 115,200 baud
+ while (!Serial) { ; } // wait for serial port
+
+ //read from SD card
+ //Serial.print("Reading Settings...");
+ if (!SD.begin(chipSelect)) {
+ //Serial.println("SD Read Failure. Contact ARC");
+ return;
+ }
+ myFile = SD.open("tgs.txt", FILE_WRITE);
+ if (myFile) {
+ //Serial.print("Writing to test.txt...");
+ myFile.println("testing 1, 2, 3.");
+ myFile.close();
+ Serial.println("done.");
+ }
+ else {
+ // if the file didn't open, print an error:
+ //Serial.println("SD Read Failure. Contact ARC");
+ }
+
+ /***Dac startup ***/
+ pinMode(9,OUTPUT); //CLR pin must stay high!
+ digitalWrite(9,LOW); //CLR Stays high ALWAYAS
+ delay(50);
+ digitalWrite(9,HIGH); //CLR Stays high ALWAYAS
+ delay(50);
+
+ SPI.begin();
+ pinMode(10,OUTPUT); // DAC CS
+ SPI.beginTransaction(SPISettings(30000000, MSBFIRST, SPI_MODE0)); //teensy can do 30000000 !!
+
+ //Drive All DACs & TTLs to 0
+ for(int i=1;i<=16;++i) {
+ setTTL(i,0);
+ setDac(i, 0);
+ }
+ opMode=3; //HYBRID mode default HYBRID=3 / MANUAL=0 /
+ delay(100);
+ Serial.println(idname); //issue identifier so software knows we are running
+ // configureTrigger(trigMode); //will attach interrupt
+ pinMode(readyLed,OUTPUT);
+ digitalWrite(readyLed,HIGH); //indicate setup complete
+ triggerPinState = digitalReadFast(trig[0]);
+
+}
+
+void loop()
+{
+ //************************ DEVICE CONTROL & COMMAND CODE ***********************//
+ //************************ SERIAL COMMUNICATION CODE ******************///
+ /*
+ if(inTrigger && reportTime) //reports time delay from previous interrupt
+ {
+ Serial.print("@TIME =");
+ Serial.println(trigInterval);
+ inTrigger = false;
+ }
+ */
+
+ if (triggerPinState != digitalReadFast(trig[0]))
+ {
+ triggerPinState = ! triggerPinState;
+ if (useSignalLEDs_)
+ {
+ digitalWriteDirect(trigLed, triggerPinState);
+ }
+ for (byte i = 0; i < NR_DACS; i++) // todo: optimize by ordering an array with sequenceable DACS and only cycle through those
+ {
+ if (dacSequencing[i])
+ {
+ if (dacSequenceMode[i] == triggerPinState)
+ {
+ dacState[i] = dacArray[dacArrayIndex[i]][i];
+ dacArrayIndex[i]++;
+ if (dacArrayIndex[i] == dacArrayMaxIndex[i]) { dacArrayIndex[i] = 0; }
+ if (!dacBlanking[i])
+ {
+ setDac(i, dacState[i]);
+ }
+ }
+ }
+ if (dacBlanking[i]) {
+ dacBlankOnLow[i] == triggerPinState ? setDac(i, dacState[i]) : setDac(i, 0);
+ }
+ }
+
+ for (byte i = 0; i < 2; i++)
+ {
+ if (pinGroupSequencing[i])
+ {
+ if (pinGroupSequenceMode[i] == triggerPinState)
+ {
+ pinGroupState[i] = ttlArray[ttlArrayIndex[i]][i];
+ ttlArrayIndex[i]++;
+ if (ttlArrayIndex[i] == ttlArrayMaxIndex[i]) { ttlArrayIndex[i] = 0; }
+ if (!pinGroupBlanking[i])
+ {
+ setPinGroup(i, pinGroupState[i]);
+ } // if we are blanking, fall through to the code below to set
+ }
+ }
+ if (pinGroupBlanking[i])
+ {
+ pinGroupBlankOnLow[i] == triggerPinState ? setPinGroup(i, pinGroupState[i]) :
+ setPinGroup(i, 0);
+ }
+ }
+
+
+ }
+
+ if (stringComplete) // There is a new line terminated string ready to be processed
+ {
+ digitalWrite(readyLed,LOW);
+
+ String command = inputString.substring(0,3);
+
+ if (inputString == "?\n")
+ {
+ Serial.println(helpString);
+ }
+
+ else if(inputString == "*\n")
+ {
+ Serial.println(idname);
+ } //return ID line if queried
+
+ // SAO command - Set DAC
+ // Expected format: "SAO1-4236" where 1 is the line nr, and 4236 the new value to be send to the DAC
+ else if(command == "SAO")
+ {
+ error = false;
+ byte dacNum = 0; // initialize to error conditino
+ int offset = 5;
+ if (inputString[4] == sep)
+ {
+ dacNum = atoi(&inputString[3]);
+ } else if (inputString[5] == sep)
+ {
+ dacNum = atoi(inputString.substring(3,5).c_str());
+ offset++;
+ } else
+ {
+ error = true;
+ }
+ if (dacNum < 1 || dacNum > 16)
+ {
+ error = true;
+ }
+
+ int value = atoi(inputString.substring(offset).c_str());
+ if (value < 0 || value > 65535)
+ {
+ error = true;
+ }
+
+ if (!error) //if inputs are valid
+ {
+ Serial.print("!SAO"); //print recieve message to operator
+ Serial.print(dacNum); //print recieve message to operator
+ Serial.print(sep);
+ Serial.println(value);
+ dacState[dacNum - 1] = value;
+ setDacCheckBlanking(dacNum - 1);
+ } else
+ {
+ Serial.println(saoErrorString);
+ }
+ }
+
+ // PAN -
+ // Queries the number of analog output states that can be pre-programmed.
+ // Format: "PANn", n is 1-6 for DAC1-16.
+ // Returns: "!PANn-q" where q is the maximum number of states that can be programmed
+ else if (command == "PAN")
+ {
+ byte dac = atoi(&inputString[3]);
+ if (dac < 1 || dac > 16)
+ {
+ Serial.println(panErrorString);
+ } else
+ {
+ Serial.print("!PAN");
+ Serial.print(dac);
+ Serial.print(sep);
+ Serial.println(NR_DAC_STATES);
+ }
+ }
+
+ /*
+ *
+ *"PAO" - Sends sequence of analog output states to be used in triggered sequences.
+ Format: "PAOn,s,o1,o2,on", where
+ - n=1-16 for DACS1-16
+ - s: position in the sequence to start inserting values. First position is 0.
+ - o1, o2, etc... values (0-65535) to be consecutively inserted in the list.
+ Returns: "!PA0n,s,q" where q is the number of values successfully inserted in the internal buffer.
+ */
+ else if (command == "PAO")
+ {
+ error = false;
+ int n = 0;
+ int s = 0;
+ int dacNr = 0;
+ unsigned int scp = 5;
+ if (inputString[4] == sep) { dacNr = inputString.substring(3,4).toInt(); }
+ else if (inputString[5] == sep){ dacNr = inputString.substring(3,5).toInt(); scp = 6; }
+ else { error = true; }
+ if (dacNr < 1 || dacNr > 16) { error = true; }
+ unsigned int ecp = scp + 1;
+ if (!error)
+ {
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if (ecp < inputString.length()) { s = inputString.substring(scp, ecp).toInt(); }
+ else { error = true; }
+ while (!error && ecp < inputString.length())
+ {
+ scp = ecp;
+ ecp++;
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if ( (ecp - scp) > 1)
+ {
+ int val = inputString.substring(scp, ecp).toInt();
+ if (val < 0 || val > 65535)
+ {
+ error = true;
+ } else
+ {
+ dacArray[n + s][dacNr-1] = val;
+ n++;
+ int index = n+s;
+ if (index > dacArrayMaxIndex[dacNr-1])
+ {
+ dacArrayMaxIndex[dacNr-1] = index;
+ }
+ }
+ }
+ }
+ }
+
+ if (!error)
+ {
+ char out[20];
+ sprintf(out, "!PAO%d%c%d%c%d%c%d", dacNr, sep, s, sep, n, sep, dacArrayMaxIndex[dacNr-1]);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(paoErrorString);
+ }
+ }
+
+
+ /*
+ * "PAC" - Clears the sequence of ananlog states
+ Format: "PACn", where n is the DAC pinNr (1-16)
+ */
+ else if (command == "PAC")
+ {
+ error = false;
+ if (inputString.length() == 5 || inputString.length() == 6)
+ {
+ int dacNr = inputString.substring(3).toInt();
+ if (dacNr < 1 || dacNr > 16) { error = true; }
+ if (!error)
+ {
+ dacSequencing[dacNr - 1] = false;
+ dacArrayIndex[dacNr - 1] = 0;
+ dacArrayMaxIndex[dacNr - 1] = 0;
+ clearPAO(dacNr - 1);
+ char out[20];
+ sprintf(out, "!PAC%d", dacNr);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(pacErrorString);
+ }
+ }
+
+ /*
+ "PAS" - Starts and stops triggered transitions in analog output state as pre-loaded in PAO
+ Format: "PASn,s,t" where
+ - n=1-16 (DAC 1-16)
+ = s=0 or 1 where 0 stops and 1 starts triggered transitions
+ - t=0 or 1 and determines whether transition will happen on the falling (0) or rising (1) edge
+ of a pulse on the input pin
+ */
+ else if (command == "PAS")
+ {
+ error = false;
+ if (inputString.length() == 9 || inputString.length() == 10)
+ {
+ error = false;
+ int dacNr = 0;
+ int scp = 4;
+ if (inputString[scp] == sep) { dacNr = inputString.substring(3,scp).toInt(); }
+ else if (inputString[scp+1] == sep)
+ {
+ scp++;
+ dacNr = inputString.substring(3,scp).toInt();
+ }
+ if (dacNr < 1 || dacNr > 16 || inputString[scp] != sep)
+ {
+ error = true;
+ }
+ int state = inputString.substring(scp+1,scp+2).toInt();
+ if (state < 0 || state > 1) { error = true; }
+ int rising = inputString.substring(scp+3).toInt();
+ if (rising < 0 || rising > 1) { error = true; }
+ if (!error)
+ {
+ dacSequencing[dacNr - 1] = (boolean) state;
+ dacSequenceMode[dacNr - 1] = rising;
+ if (state)
+ {
+ dacStoredState[dacNr - 1] = dacState[dacNr - 1];
+ dacArrayIndex[dacNr - 1] = 0;
+ if (!rising) { // if we trigger on the falling edge, set initial state now, and advance counter here
+ setDac(dacNr -1, dacArray[dacArrayIndex[dacNr - 1]][dacNr - 1]); // Check blanking?
+ dacArrayIndex[dacNr - 1]++;
+ }
+ } else
+ {
+ dacState[dacNr - 1] = dacStoredState[dacNr - 1];
+ }
+ char out[20];
+ sprintf(out, "!PAS%d%c%d%c%d", dacNr, sep, state, sep, rising);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(pasErrorString);
+ }
+
+ }
+
+ /*
+ "BAO" - Activates blanking mode of the analog output. Output state will be coupled to the
+ state of the input trigger. If input trigger is active, the DAC will be active (
+ as set with SDA), if input trigger is inactive, the output will go (which may not be 0V, depending on the range).
+ Format: "BAOn,s,t"
+ - n=1-16 for DACS1-16
+ - s=0 or 1 where 0 stops and 1 starts balnking mode
+ - t translates state of the input pin to its activity for blanking. If t = 0,
+ DAC will be 0 when input trigger pin is low. t = 1: DAC will be active when input trigger is low.
+ */
+ else if (command == "BAO")
+ {
+ error = false;
+ int dacNr = 0;
+ int scp = 5;
+ if (inputString[4] == sep) { dacNr = inputString.substring(3,4).toInt(); }
+ else if (inputString[5] == sep) { dacNr = inputString.substring(3,5).toInt(); scp = 6; }
+ else { error = true; }
+ if (dacNr < 1 || dacNr > 16){ error = true; }
+ int state = inputString.substring(scp, scp+1).toInt();
+ if (state < 0 || state > 1) { error = true; }
+ if (inputString[scp+1] != sep) { error = true; }
+ int mode = inputString.substring(scp+2).toInt();
+ if (mode < 0 || mode > 1) { error = true; }
+ if (!error)
+ {
+ dacBlanking[dacNr - 1] = state == 1;
+ dacBlankOnLow[dacNr - 1] = mode == 0;
+ setDacCheckBlanking(dacNr - 1);
+ char out[20];
+ sprintf(out, "!BAO%d%c%d%c%d", dacNr, sep, state, sep, mode);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(baoErrorString);
+ }
+ }
+
+ /*
+ "BAD" - Sets a delay in blanking mode of analog output. When the trigger is received to activate the analog output,
+ wait for the set amount of time (in micro-seconds) to actually activate the analog output. This command will only
+ have an effect in combination iwth blanking mode (BAO).
+ Format: "BADn-t"
+ - n=1-16 for DAC 1-16
+ - t=delay between trigger and actually activating the output in micro-seconds (default is 0 for no delay,
+ no negative numbers allowed, maximum delay is 2,147,483,647)
+ */
+ else if (command == "BAD")
+ {
+ error = false;
+ int dacNr = 0;
+ int scp = 5;
+ if (inputString[4] == sep) { dacNr = inputString.substring(3,4).toInt(); }
+ else if (inputString[5] == sep) { dacNr = inputString.substring(3,5).toInt(); scp = 6; }
+ else { error = true; }
+ if (dacNr < 1 || dacNr > 16){ error = true; }
+ uint32_t duration = inputString.substring(scp).toInt();
+ if (!error)
+ {
+ dacBlankDelay[dacNr - 1] = duration;
+ Serial.print("!BAD");
+ Serial.print(dacNr);
+ Serial.print(sep);
+ Serial.println(duration);
+ } else
+ {
+ Serial.println(badErrorString);
+ }
+ }
+
+ /*
+ * "BAL" - Sets the lenght of analog output "pulse" in blanking mode" in micro-seconds. After start of the pulse (determined
+ by BAO and BAD, output will go to 0V after the given number of microseonds, or when the input trigger so dictates
+ (whichever comes first).
+ Format: "BALn-t"
+ - n=1-16 for DAC 1-16
+ - t=maximum duration of the output pulse in microseconds (0 for no limit, maximum value is 2,147,483,647)
+ */
+ else if (command == "BAL")
+ {
+ error = false;
+ int dacNr = 0;
+ int scp = 5;
+ if (inputString[4] == sep) { dacNr = inputString.substring(3,4).toInt(); }
+ else if (inputString[5] == sep) { dacNr = inputString.substring(3,5).toInt(); scp = 6; }
+ else { error = true; }
+ if (dacNr < 1 || dacNr > 16){ error = true; }
+ uint32_t duration = inputString.substring(scp).toInt();
+ if (!error)
+ {
+ dacBlankDuration[dacNr - 1] = duration;
+ Serial.print("!BAL");
+ Serial.print(dacNr);
+ Serial.print(sep);
+ Serial.println(duration);
+ } else
+ {
+ Serial.println(balErrorString);
+ }
+ }
+
+ // SDO
+ // sets TTL outputs of pins1-8 and 9-16 in a single operation
+ // Format: SDOn,o
+ // Where n=0 or 1. 0 address pins 1-8, 1 goes to pins 9-16
+ // 0 is digital output bitmask, 0: all off, 1: pin 1 on, 2: pin 2 on, 3: pins 1&2 on, etc..
+ // Example: DO1,4 sets pin 3 high and pins 1,2, 4-8 low
+ else if (command == "SDO")
+ {
+ byte pinGroup = atoi(&inputString[3]);
+ if (pinGroup < 0 || pinGroup > 1)
+ {
+ Serial.println(sdoErrorString);
+ } else
+ {
+ int value = atoi(inputString.substring(5).c_str());
+ if (value < 0 || value > 255)
+ {
+ Serial.println(sdoErrorString);
+ } else
+ {
+ Serial.print("!SDO");
+ Serial.print(pinGroup);
+ Serial.print(sep);
+ Serial.println(value);
+ pinGroupState[pinGroup] = value;
+ setPinGroupCheckBlanking(pinGroup);
+ }
+ }
+ }
+
+ // PDN -
+ // Queries the number of digital output states that can be pre-programmed.
+ // Format: "PDNn".
+ // Returns: "!PDNn,q" where q is the maximum number of states that can be programmed
+ else if (command == "PDN")
+ {
+ byte pinGroup = atoi(&inputString[3]);
+ if (pinGroup < 0 || pinGroup > 1)
+ {
+ Serial.println(pdnErrorString);
+ } else
+ {
+ Serial.print("!PDN");
+ Serial.print(pinGroup);
+ Serial.print(sep);
+ Serial.println(NR_DO_STATES);
+ }
+ }
+
+ //"PDO" - Sends sequence of digital output states to be used in triggered sequences.
+ // Format: "PDOn-s-o1-o2-on", where
+ // - n=1 or 2 and directs the command to eitherpins1-8 or 9-16,
+ // - s: position in the sequence to start inserting values. First position is 1.
+ // - o1, o2, etc... values to be consecutively inserted in the list.
+ // Returns: "!PD0n,s,q" where q is the number of values successfully inserted in the internal buffer.
+ else if (command == "PDO")
+ {
+ error = false;
+ int s = 0;
+ int n = 0;
+ int pinGroup = inputString.substring(3,4).toInt();
+ if (pinGroup < 0 || pinGroup > 1)
+ {
+ error = true;
+ }
+ if (inputString[4] != sep)
+ {
+ error = true;
+ }
+ unsigned int scp = 5;
+ unsigned int ecp = 6;
+ if (!error)
+ {
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if (ecp < inputString.length())
+ {
+ s = inputString.substring(scp, ecp).toInt();
+ }
+ else
+ {
+ error = true;
+ }
+ while (!error && ecp < inputString.length())
+ {
+ scp = ecp;
+ ecp++;
+ while (inputString[ecp - 1] != sep && ecp < inputString.length())
+ {
+ ecp++;
+ }
+ if ( (ecp - scp) > 1)
+ {
+ int val = inputString.substring(scp, ecp).toInt();
+ if (val < 0 || val > 255)
+ {
+ error = true;
+ } else
+ {
+ ttlArray[n + s][pinGroup] = (byte) val;
+ n++;
+ int index = n+s;
+ if (index > ttlArrayMaxIndex[pinGroup])
+ {
+ ttlArrayMaxIndex[pinGroup] = index;
+ }
+ }
+ }
+ }
+ }
+
+ if (!error)
+ {
+ char out[20];
+ sprintf(out, "!PDO%d%c%d%c%d%c%d", pinGroup, sep, s, sep, n, sep, ttlArrayMaxIndex[pinGroup]);
+ Serial.println(out);
+ } else
+ {
+ Serial.println(pdoErrorString);
+ }
+ }
+
+ /*
+ * "PDC" - Clears the sequence of digital states
+ Format: "PDCn", where n is the pingroup, 0=pins1-8, 1=pins9-16
+ */
+ else if (command == "PDC")
+ {
+ error = false;
+ if (inputString.length() == 5)
+ {
+ int pinGroup = inputString.substring(3,4).toInt();
+ if (pinGroup < 0 || pinGroup > 1) { error = true; }
+ if (!error)
+ {
+ pinGroupSequencing[pinGroup] = false;
+ ttlArrayIndex[pinGroup] = 0;
+ ttlArrayMaxIndex[pinGroup] = 0;
+ clearPDO(pinGroup);
+ char out[20];
+ sprintf(out, "!PDC%d", pinGroup);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(pdcErrorString);
+ }
+ }
+
+ /*
+ "PDS" - Starts and stops triggered transitions in digital output state as pre-loaded in PDO
+ Format: "PDSn,s,t" where
+ - n=0 or 1 and directs the command to either pins 1-8 or 9-16
+ = s=0 or 1 where 0 stops and 1 starts triggered transitions
+ - t=0 or 1 and determines whether transition will happen on the falling (0) or rising (1) edge
+ of a pulse on the input pin
+ */
+ else if (command == "PDS")
+ {
+ error = false;
+ if (inputString.length() == 9)
+ {
+ int pinGroup = inputString.substring(3,4).toInt();
+ if (pinGroup < 0 || pinGroup > 1) { error = true; }
+ int state = inputString.substring(5,6).toInt();
+ if (state < 0 || state > 1) { error = true; }
+ int rising = inputString.substring(7,8).toInt();
+ if (rising < 0 || rising > 1) { error = true; }
+ if (!error)
+ {
+ pinGroupSequencing[pinGroup] = (boolean) state;
+ pinGroupSequenceMode[pinGroup] = rising;
+ if (state)
+ {
+ pinGroupStoredState[pinGroup] = pinGroupState[pinGroup];
+ ttlArrayIndex[pinGroup] = 0;
+ if (!rising)
+ { // if we trigger on the falling edge, set initial state now, and advance counter here
+ setPinGroup(pinGroup, ttlArray[ttlArrayIndex[pinGroup]][pinGroup]); // Check blanking?
+ ttlArrayIndex[pinGroup]++;
+ }
+ } else
+ {
+ pinGroupState[pinGroup] = pinGroupStoredState[pinGroup];
+ }
+ char out[20];
+ sprintf(out, "!PDS%d%c%d%c%d", pinGroup, sep, state, sep, rising);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(pdsErrorString);
+ }
+ }
+
+ /**
+ * "BDO" - Activates blanking mode of the digital output. Output state will be coupled to the
+ * state of the input trigger. If input trigger is active, the pingroup will be active (
+ * as set with DO), if input trigger is inactive, the pingroup will go low.
+ * Format: "BDOn,s,t" where n is the pingroup (0 or 1), s switches blanking mode on (1)
+ * or off (0), and t sets the mode (0 blank on low, 1 blank on high)
+ */
+ else if (command == "BDO")
+ {
+ error = false;
+ if (inputString.length() == 9)
+ {
+ int pinGroup = inputString.substring(3,4).toInt();
+ if (pinGroup < 0 || pinGroup > 1) { error = true; }
+ byte state = inputString.substring(5,6).toInt();
+ if (state < 0 || state > 1) { error = true; }
+ byte mode = inputString.substring(7,8).toInt();
+ if (mode < 0 || mode > 1) { error = true; }
+ if (!error)
+ {
+ pinGroupBlanking[pinGroup] = state == 1;
+ pinGroupBlankOnLow[pinGroup] = mode == 0;
+ setPinGroupCheckBlanking(pinGroup);
+ char out[20];
+ sprintf(out, "!BDO%d%c%d%c%d", pinGroup, sep, state, sep, mode);
+ Serial.println(out);
+ }
+ } else
+ {
+ error = true;
+ }
+ if (error)
+ {
+ Serial.println(bdoErrorString);
+ }
+ }
+
+ /**
+ * Sets voltage range of DACS
+ */
+ else if(command == "SAR")
+ {
+ error = false;
+ byte dacNum = inputString.substring(3).toInt();
+ byte pp = 5;
+ if(dacNum >9)
+ {
+ pp=6;
+ }
+ byte rangeVal = inputString.substring(pp).toInt();
+ if(rangeVal < 1 || rangeVal > 5)
+ {
+ error = true;
+ } //force to max range
+ if(dacNum < 1 || dacNum > 16)
+ {
+ error = true;
+ } //confirm if input channel range is valid
+ if(!error) //if range is OK perform command
+ {
+ Serial.print("!SAR"); //print recieve message to operator
+ Serial.print(dacNum);
+ Serial.print(sep);
+ Serial.println(rangeVal);
+ setDacRange(dacNum-1,rangeVal-1);
+ // 0 the output
+ int value = 0;
+ if (rangeVal > 2)
+ {
+ value = 65535 / 2;
+ }
+ dacState[dacNum - 1] = value;
+ setDac(dacNum - 1, value);
+ } else
+ {
+ Serial.println(sarErrorString);
+ }
+ }
+
+ // Set Signal LED flag
+ else if (command == "SSL")
+ {
+ error = false;
+ byte result = inputString.substring(3).toInt();;
+ if (result == 0)
+ {
+ useSignalLEDs_ = false;
+ digitalWrite(dacLed, 0);
+ digitalWrite(ttlLed, 0);
+ digitalWriteDirect(trigLed,0);
+ }
+ else if (result == 1)
+ {
+ useSignalLEDs_ = true;
+ // TODO: make sure the LEDs are set correctly?
+ }
+ else error = true;
+ if (!error)
+ {
+ Serial.print("!SSL");
+ Serial.println(result);
+ } else
+ {
+ Serial.println(sslErrorString);
+ }
+ }
+
+ //status commands
+ else if(inputString == "STAT?\n") {debug(); }
+ else if(inputString == "TEST?\n") {diagTest(); }
+ else if(inputString == "CLEAR_ALL\n") {clearTable(); }
+ else if(inputString.substring(0,9) == "CLEAR_DAC") {clearDac(); }
+
+ else
+ {
+ Serial.println(generalErrorString);
+ }
+
+
+ clearSerial();
+ digitalWrite(readyLed,HIGH);
+ } //EXIT LOOP FOR SERIAL HERE
+
+
+/*********************This block runs the high speed control interface *********************/
+
+/****checks the acquisition order
+ * mode 0 == channel first eg set ch1 stweep Z
+ * mode 1 == Z first EG step Z then Ch1 Ch2 then Step Z ...
+ */
+
+/*
+ while(trigArmed)
+ { // just sit here and wait for the next command until armed is off, which can only happen @ end of sequence
+ unsigned long tStart = millis() + timeOut; //set timeout position
+ unsigned long tLed = 0;
+ // focus commands = start, step, #loops,direction,slave,current step
+ //if(program == 0) {inTrigger=true; } //force a first step
+
+ if( inTrigger )
+ { //we recieved a trigger from our source
+ // and is mode 0, and is at 0 position OR
+ // and is mode 1, and is at any position OR
+ // if focus isn't enabled
+ */
+ /*
+ * When should a channel update be issued? When
+ * focus is ON, and mode is sweep per channel (0) AND F position is 0
+ * focus is ON, and mode is slave to channel (1)
+ * focus is off any time
+ */
+
+ // boolean runUpdate = true;
+ /*
+ if( (focArray[1] != 0) && (focArray[4] == 0) ){ //if we are using the focus and focus will sweep through a single channel setting
+ if( focArray[5] == 0 ) { runUpdate = true;} //AND if the focus is at position 0 (start of sweep)
+ }
+
+ if( (focArray[1] !=0) && (focArray[4] == 1)) { runUpdate = true; } // Case where channel is switching each Z plane
+ if(focArray[1] == 0) {runUpdate=true;} //Case where no focus block used so update the channel
+ */
+ /*
+ //do DAC and TTL control stuff, not focus stuff though
+ if(runUpdate) {
+ byte walker=0;
+ for(walker = 0 ; walker < 15 ; ++walker ){ //sets DACs 1-16
+ dac_write(10,0, DAC [walker], dacArray [program] [walker]); // Set DAC Lines
+ digitalWriteDirect( ttl [walker] , ttlArray[program] [walker] ); //set TTL lines
+ }
+ digitalWriteDirect( ttl [walker] , ttlArray[program] [walker] ); //set 16th TTL line -
+ }
+ ++program;
+*/
+ /* THIS MOVES AROUND THE Z FOCUS
+ * in this case, we assume a trigger was recieved, but we should only mess with focus stuff if it's on and if it's needed
+ * here we only want to update the focus position in the case that EITHER
+ * Mode = 0 Channel 1 - Z 1, Z2, Z3
+ * mode = 1 Z 1 - CH1, CH2, Z2, Ch1, Ch2
+ */
+/*
+ if( (focArray[1] != 0) && ( focArray[4]==0 )){ fastFocus(); } // if any focus array is active, and MODE = SWEEP OVER CHANNEL
+ if( (focArray[1] != 0) && ( focArray[4]==1 )){ // in this case sweep is active and MODE = STEP AFTER CHANNEL
+ if((program == maxProgram-1) && (focArray[5] <= focArray[2])) {fastFocus();}
+ }
+
+ delay(delArray[program]); //wait for specified delay
+
+ if( focArray[1] == 0) {++program;} //if not using focus at all
+ if( (focArray[1] != 0) && (focArray[4] == 1) ) { //focus is used but in step mode
+ ++program;
+ if( (program > maxProgram) && (focArray[5] != 0) ) { //because we are stepping the focus, program must be reset to 0 in this case we know the focus has not completed, so we can reset the main program array position
+ program=0;
+ }
+ }
+ */
+ } //END OF TRIGGER RESPONSE
+
+ /*
+ inTrigger=false; //turn off trigger
+ tStart = millis() + timeOut; //update timeout delta for next run
+
+ //Done moving stuff, now wait for the next input
+ while(!inTrigger){ //wait for next pulse
+ if(millis() > tStart) {
+ trigArmed=false;
+ program = maxProgram+1;
+ Serial.println("Timeout Exceeded");
+ allOff();
+ break;
+ }
+ //we hit the timeout so end the sequence
+ if(tLed < millis() )
+ {
+ digitalWrite(readyLed,!digitalRead(readyLed));
+ tLed = millis() + 30;
+ }
+ }
+
+ if(program > maxProgram) { //end and cleanup
+ ++runCycles;
+ if(runCycles == timeCycles) { //we are done
+ trigArmed=false;
+ program=0;
+ allOff();
+ Serial.println("!SAFE");
+ digitalWrite(readyLed,HIGH);
+ }
+ if(runCycles < timeCycles) {program = 0;}
+ }
+ } //close trigarmed
+} //close main loop
+*/
+
+
+
+
+void allOff()
+{
+ for(byte walker=0;walker<15;++walker)
+ { //sets DACs 1-16
+ dac_write(10,0, DAC [walker], 0); // Set DAC Lines
+ }
+ setPinGroup(0,0);
+ setPinGroup(1,0);
+}
+
+
+void clearSerial()
+{
+ //STUFF TO CLEAN OUT THE SERIAL LINE
+ inputString = ""; // clear the string:
+ stringComplete = false;
+}
+
+/*PC DAC CONTROL*/
+void setDac(byte dNum,int dVal)
+{
+ dac_write(10,0, dNum, dVal); // Send dac_code
+ //led indication
+ if (useSignalLEDs_)
+ {
+ int dacSum = 0;
+ for (byte d=0; d < 16; d++)
+ {
+ dacSum += dacState[d];
+ }
+ digitalWrite(dacLed, dacSum > 0);
+ }
+}
+
+// sets the output state of the given pin number
+void setTTL(byte t1, boolean t2)
+{
+ byte pin = t1 - 1;
+ digitalWriteFast(ttl[pin], t2);
+ if(t2)
+ {
+ bitSet(ttlState, pin);
+ } else
+ {
+ bitClear(ttlState, pin);
+ }
+ if (useSignalLEDs_)
+ {
+ digitalWriteFast(ttlLed, ttlState > 0);
+ }
+}
+
+void setDacCheckBlanking(byte dacNr)
+{
+ if (dacBlanking[dacNr])
+ {
+ triggerPinState = digitalReadFast(trig[0]);
+ dacBlankOnLow[dacNr] == triggerPinState ?
+ setDac(dacNr, dacState[dacNr]) : setDac(dacNr, 0);
+ } else {
+ setDac(dacNr, dacState[dacNr]);
+ }
+}
+
+
+void setPinGroupCheckBlanking(byte pinGroup)
+{
+ if (pinGroupBlanking[pinGroup])
+ {
+ triggerPinState = digitalReadFast(trig[0]);
+ pinGroupBlankOnLow[pinGroup] == triggerPinState ?
+ setPinGroup(pinGroup, pinGroupState[pinGroup]) : setPinGroup(pinGroup, 0);
+ } else {
+ setPinGroup(pinGroup, pinGroupState[pinGroup]);
+ }
+}
+
+inline void setPinGroup(byte, byte) __attribute__((always_inline));
+// sets pins 1-8 (pinGroup 0) or 9-16 (pinGroup 1) according to the mask in value
+inline void setPinGroup(byte pinGroup, byte value)
+{
+ byte adder = (pinGroup) * 8;
+ for (byte b = 0; b < 8; b++)
+ {
+ if( b+adder < 9 ) {
+ digitalWriteFast(ttl[b + adder], (((value) >> (b)) & 0x01) );
+ }
+
+ else if( b+adder > 8) {
+ digitalWriteFast(ttl[b+adder],(((value) >> (b)) & 0x01));
+ }
+
+ //Serial.print("TTL " );
+ //Serial.print(b + adder);
+ //Serial.print(" , ");
+ //Serial.println((((value) >> (b)) & 0x01));
+
+ }
+ if (pinGroup == 1)
+ {
+ ttlState = (ttlState & 0xff00) | value;
+ } else
+ {
+ ttlState = (ttlState & 0x00ff) | (value << 8);
+ }
+ if (useSignalLEDs_)
+ {
+ digitalWriteFast(ttlLed, ttlState > 0);
+ }
+}
+
+/*
+ SerialEvent occurs whenever a new data comes in the hardware serial RX. This
+ routine is run between each time loop() runs, so using delay inside loop can
+ delay response. Multiple bytes of data may be available.
+*/
+void serialEvent() {
+ trigArmed = false;
+ while (Serial.available()) {
+ // get the new byte:
+ char inChar = (char)Serial.read();
+ // add it to the inputString:
+ inputString += inChar;
+ // if the incoming character is a newline, set a flag
+ // so the main loop can do something about it:
+ if (inChar == '\n') {
+ stringComplete = true;
+ }
+ }
+}
+
+/*INTERRUPT CODE FOR TTL INPUT ***************/
+/*
+void sigIn() //sigin is the input response line - this recieves the trigger input from an external source
+{
+ //if(!trigArmed)
+ //{
+ digitalWrite(trigLed,!digitalRead(trigLed));
+ //}
+ inTrigger=true;
+}
+
+void configureTrigger(byte tOption)
+{
+ trigMode = tOption; //assign input value to global
+ switch (trigMode) {
+ case 0:
+ attachInterrupt(trig[0],sigIn,LOW);
+ break;
+ case 1:
+ attachInterrupt(trig[0],sigIn,HIGH);
+ break;
+ case 2:
+ attachInterrupt(trig[0],sigIn,RISING);
+ break;
+ case 3:
+ attachInterrupt(trig[0],sigIn,FALLING);
+ break;
+ case 4:
+ attachInterrupt(trig[0],sigIn,CHANGE);
+ break;
+ }
+}
+*/
+
+void debug()
+{
+ if(debugT < millis() ) //if this is the first time debug has run, this will execute, otherwise has enough time elapsed?
+ {
+ Serial.print("Input Trigger = ");
+ Serial.println(digitalReadFast(trig[0]));
+ //Serial.print(", OpMode = ");
+ //Serial.println(opMode);
+
+ // REPORT TTL
+ Serial.print("TTL: ");
+ for(int i=0;i<16;++i)
+ {
+ char sOut[100];
+ sprintf(sOut,"%d=%d,",i+1, digitalRead(ttl[i]));
+ Serial.print(sOut);
+ }
+ Serial.println("");
+
+ //REPORT DAC
+ Serial.print("DAC:");
+ for(int i=0;i<16;++i)
+ {
+ char sOut[200];
+ sprintf(sOut,"%d=%u,",i+1, dacState[i]);
+ Serial.print(sOut); //used to print sOut
+ }
+
+ Serial.println();
+ Serial.println("Digital output buffers:");
+ for (int pg = 0; pg < 2; pg++)
+ {
+ if (pg == 0) Serial.print("Pins 1-8 buffer size 1-8: ");
+ else if (pg == 1) Serial.print("Pins 9-16 buffer size: ");
+ char sOut[6];
+ sprintf(sOut, "%d", ttlArrayMaxIndex[pg]);
+ Serial.println(sOut);
+ for (int i = 0; i < ttlArrayMaxIndex[pg]; i++)
+ {
+ sprintf(sOut, "%u ", ttlArray[i][pg]);
+ Serial.print(sOut);
+ }
+ if (ttlArrayMaxIndex[pg] > 0) { Serial.println(); }
+ }
+
+ Serial.println("DAC buffers:");
+ for (int dac = 0; dac < NR_DACS; dac++)
+ {
+ char sOut[25];
+ sprintf(sOut, "DAC buffer size: %d", dacArrayMaxIndex[dac]);
+ Serial.println(sOut);
+ for (int i = 0; i < dacArrayMaxIndex[dac]; i++)
+ {
+ sprintf(sOut, "%u ", dacArray[i][dac]);
+ Serial.print(sOut);
+ }
+ if (dacArrayMaxIndex[dac] > 0) { Serial.println(); }
+ }
+
+ }
+ // send an empty line so that the receiver know we are done
+ Serial.println("");
+}
+
+ /*
+ //Report program arrays
+ if(opMode == 3)
+ {
+ Serial.println("Sequencer Programming Status");
+ Serial.print("MaxProgram = ");
+ Serial.println(maxProgram);
+ Serial.print("PCHANNEL = ");
+ Serial.println(pChannel);
+ //report DACs
+ Serial.println("PROG, DAC1, DAC2, DAC3, DAC4, DAC5, DAC6, DAC7, DAC8, DAC9,DAC10,DAC11,DAC12,DAC13,DAC14,DAC15,DAC16/FOCUS");
+ for(int p=0;p= 0; i--)
+ rx[i] = SPI.transfer(tx[i]); //! 2) Read and send byte array
+
+ output_high(cs_pin); //! 3) Pull CS high
+}
+
+inline void digitalWriteDirect(int, boolean) __attribute__((always_inline));
+
+void digitalWriteDirect(int pin, boolean val){
+ digitalWriteFast(pin,val);
+}
+
+/*
+inline int digitalReadDirect(int) __attribute__((always_inline));
+
+int digitalReadDirect(int pin){
+ return !!(g_APinDescription[pin].pPort -> PIO_PDSR & g_APinDescription[pin].ulPin);
+}
+
+inline int digitalReadOutputPin(int) __attribute__((always_inline));
+
+int digitalReadOutputPin(int pin){
+ return !!(g_APinDescription[pin].pPort -> PIO_ODSR & g_APinDescription[pin].ulPin);
+}
+*/
+
+
+
+/*******************LICENSING INFO**************
+
+ * Copyright (c) 2018, Advanced Research Consulting
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ *
+ *******************/