From 0cdfc45df874354265b3be910b52c41398de79ca Mon Sep 17 00:00:00 2001 From: Andrzej Grzesik Date: Thu, 19 Jul 2012 12:30:35 +0200 Subject: [PATCH] Added IOIO sources from App-IOIO0323 --- proguard.cfg | 5 + .../bluetooth/BluetoothIOIOConnection.java | 128 +++ .../BluetoothIOIOConnectionBootstrap.java | 97 ++ src/main/java/ioio/lib/api/AnalogInput.java | 225 +++++ src/main/java/ioio/lib/api/Closeable.java | 6 + src/main/java/ioio/lib/api/DigitalInput.java | 143 +++ src/main/java/ioio/lib/api/DigitalOutput.java | 120 +++ src/main/java/ioio/lib/api/IOIO.java | 695 ++++++++++++++ .../java/ioio/lib/api/IOIOConnection.java | 45 + src/main/java/ioio/lib/api/IOIOFactory.java | 95 ++ src/main/java/ioio/lib/api/IcspMaster.java | 145 +++ src/main/java/ioio/lib/api/PulseInput.java | 325 +++++++ src/main/java/ioio/lib/api/PwmOutput.java | 126 +++ src/main/java/ioio/lib/api/SpiMaster.java | 215 +++++ src/main/java/ioio/lib/api/TwiMaster.java | 148 +++ src/main/java/ioio/lib/api/Uart.java | 107 +++ .../exception/ConnectionLostException.java | 45 + .../exception/IncompatibilityException.java | 41 + .../api/exception/OutOfResourceException.java | 14 + src/main/java/ioio/lib/impl/AbstractPin.java | 47 + .../java/ioio/lib/impl/AbstractResource.java | 78 ++ .../java/ioio/lib/impl/AnalogInputImpl.java | 182 ++++ src/main/java/ioio/lib/impl/Constants.java | 46 + .../java/ioio/lib/impl/DigitalInputImpl.java | 94 ++ .../java/ioio/lib/impl/DigitalOutputImpl.java | 58 ++ .../lib/impl/FlowControlledOutputStream.java | 127 +++ .../lib/impl/FlowControlledPacketSender.java | 119 +++ src/main/java/ioio/lib/impl/IOIOImpl.java | 720 +++++++++++++++ src/main/java/ioio/lib/impl/IOIOProtocol.java | 857 ++++++++++++++++++ .../java/ioio/lib/impl/IcspMasterImpl.java | 137 +++ src/main/java/ioio/lib/impl/IncapImpl.java | 111 +++ .../java/ioio/lib/impl/IncomingState.java | 463 ++++++++++ .../java/ioio/lib/impl/ModuleAllocator.java | 106 +++ .../java/ioio/lib/impl/PinFunctionMap.java | 76 ++ src/main/java/ioio/lib/impl/PwmImpl.java | 104 +++ .../java/ioio/lib/impl/QueueInputStream.java | 125 +++ .../ioio/lib/impl/SocketIOIOConnection.java | 141 +++ .../impl/SocketIOIOConnectionBootstrap.java | 64 ++ .../java/ioio/lib/impl/SpiMasterImpl.java | 199 ++++ .../java/ioio/lib/impl/TwiMasterImpl.java | 171 ++++ src/main/java/ioio/lib/impl/UartImpl.java | 109 +++ .../ioio/lib/spi/IOIOConnectionBootstrap.java | 42 + .../ioio/lib/spi/IOIOConnectionFactory.java | 22 + .../lib/spi/NoRuntimeSupportException.java | 9 + .../java/ioio/lib/util/BaseIOIOLooper.java | 49 + .../ioio/lib/util/IOIOApplicationHelper.java | 166 ++++ .../ioio/lib/util/IOIOConnectionRegistry.java | 140 +++ src/main/java/ioio/lib/util/IOIOLooper.java | 57 ++ .../ioio/lib/util/IOIOLooperProvider.java | 37 + .../util/android/ContextWrapperDependent.java | 42 + .../android/IOIOAndroidApplicationHelper.java | 68 ++ 51 files changed, 7491 insertions(+) create mode 100644 src/main/java/ioio/lib/android/bluetooth/BluetoothIOIOConnection.java create mode 100644 src/main/java/ioio/lib/android/bluetooth/BluetoothIOIOConnectionBootstrap.java create mode 100644 src/main/java/ioio/lib/api/AnalogInput.java create mode 100644 src/main/java/ioio/lib/api/Closeable.java create mode 100644 src/main/java/ioio/lib/api/DigitalInput.java create mode 100644 src/main/java/ioio/lib/api/DigitalOutput.java create mode 100755 src/main/java/ioio/lib/api/IOIO.java create mode 100644 src/main/java/ioio/lib/api/IOIOConnection.java create mode 100644 src/main/java/ioio/lib/api/IOIOFactory.java create mode 100644 src/main/java/ioio/lib/api/IcspMaster.java create mode 100644 src/main/java/ioio/lib/api/PulseInput.java create mode 100644 src/main/java/ioio/lib/api/PwmOutput.java create mode 100644 src/main/java/ioio/lib/api/SpiMaster.java create mode 100644 src/main/java/ioio/lib/api/TwiMaster.java create mode 100644 src/main/java/ioio/lib/api/Uart.java create mode 100644 src/main/java/ioio/lib/api/exception/ConnectionLostException.java create mode 100644 src/main/java/ioio/lib/api/exception/IncompatibilityException.java create mode 100644 src/main/java/ioio/lib/api/exception/OutOfResourceException.java create mode 100644 src/main/java/ioio/lib/impl/AbstractPin.java create mode 100644 src/main/java/ioio/lib/impl/AbstractResource.java create mode 100644 src/main/java/ioio/lib/impl/AnalogInputImpl.java create mode 100755 src/main/java/ioio/lib/impl/Constants.java create mode 100644 src/main/java/ioio/lib/impl/DigitalInputImpl.java create mode 100644 src/main/java/ioio/lib/impl/DigitalOutputImpl.java create mode 100644 src/main/java/ioio/lib/impl/FlowControlledOutputStream.java create mode 100644 src/main/java/ioio/lib/impl/FlowControlledPacketSender.java create mode 100755 src/main/java/ioio/lib/impl/IOIOImpl.java create mode 100755 src/main/java/ioio/lib/impl/IOIOProtocol.java create mode 100644 src/main/java/ioio/lib/impl/IcspMasterImpl.java create mode 100644 src/main/java/ioio/lib/impl/IncapImpl.java create mode 100644 src/main/java/ioio/lib/impl/IncomingState.java create mode 100644 src/main/java/ioio/lib/impl/ModuleAllocator.java create mode 100644 src/main/java/ioio/lib/impl/PinFunctionMap.java create mode 100644 src/main/java/ioio/lib/impl/PwmImpl.java create mode 100644 src/main/java/ioio/lib/impl/QueueInputStream.java create mode 100644 src/main/java/ioio/lib/impl/SocketIOIOConnection.java create mode 100644 src/main/java/ioio/lib/impl/SocketIOIOConnectionBootstrap.java create mode 100644 src/main/java/ioio/lib/impl/SpiMasterImpl.java create mode 100644 src/main/java/ioio/lib/impl/TwiMasterImpl.java create mode 100644 src/main/java/ioio/lib/impl/UartImpl.java create mode 100644 src/main/java/ioio/lib/spi/IOIOConnectionBootstrap.java create mode 100644 src/main/java/ioio/lib/spi/IOIOConnectionFactory.java create mode 100644 src/main/java/ioio/lib/spi/NoRuntimeSupportException.java create mode 100644 src/main/java/ioio/lib/util/BaseIOIOLooper.java create mode 100644 src/main/java/ioio/lib/util/IOIOApplicationHelper.java create mode 100644 src/main/java/ioio/lib/util/IOIOConnectionRegistry.java create mode 100644 src/main/java/ioio/lib/util/IOIOLooper.java create mode 100644 src/main/java/ioio/lib/util/IOIOLooperProvider.java create mode 100644 src/main/java/ioio/lib/util/android/ContextWrapperDependent.java create mode 100644 src/main/java/ioio/lib/util/android/IOIOAndroidApplicationHelper.java diff --git a/proguard.cfg b/proguard.cfg index ab4f7af3..e22e6753 100644 --- a/proguard.cfg +++ b/proguard.cfg @@ -36,6 +36,11 @@ -keep public class roboguice.** +-keep public class ioio.** +-keepclassmembers class ioio.** { + *; +} + -keepclassmembers class * { void onEvent(...); } diff --git a/src/main/java/ioio/lib/android/bluetooth/BluetoothIOIOConnection.java b/src/main/java/ioio/lib/android/bluetooth/BluetoothIOIOConnection.java new file mode 100644 index 00000000..c8ab8a0c --- /dev/null +++ b/src/main/java/ioio/lib/android/bluetooth/BluetoothIOIOConnection.java @@ -0,0 +1,128 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ + +package ioio.lib.android.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSocket; +import android.util.Log; +import ioio.lib.api.IOIOConnection; +import ioio.lib.api.exception.ConnectionLostException; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.UUID; + +public class BluetoothIOIOConnection implements IOIOConnection +{ + private static final String TAG = "BluetoothIOIOConnection"; + private BluetoothSocket socket_ = null; + private boolean disconnect_ = false; + private final BluetoothDevice device_; + private final String name_; + private final String address_; + + public BluetoothIOIOConnection(BluetoothDevice device) { + device_ = device; + name_ = device.getName(); + address_ = device.getAddress(); + } + + @Override + public void waitForConnect() throws ConnectionLostException { + synchronized (this) { + if (disconnect_) { + throw new ConnectionLostException(); + } + try { + socket_ = createSocket(device_); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } + // keep trying to connect as long as we're not aborting + while (true) { + try { + Log.v(TAG, "Attempting to connect to Bluetooth device: " + name_); + socket_.connect(); + Log.v(TAG, "Established connection to device " + name_ + + " address: " + address_); + break; // if we got here, we're connected + } catch (Exception e) { + if (disconnect_) { + throw new ConnectionLostException(e); + } + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + } + } + } + } + + public static BluetoothSocket createSocket(final BluetoothDevice device) + throws IOException + { + return device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")); + } + + @Override + public synchronized void disconnect() { + if (disconnect_) { + return; + } + Log.v(TAG, "Client initiated disconnect"); + disconnect_ = true; + if (socket_ != null) { + try { + socket_.close(); + } catch (IOException e) { + } + } + } + + @Override + public InputStream getInputStream() throws ConnectionLostException { + try { + return socket_.getInputStream(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } + + @Override + public OutputStream getOutputStream() throws ConnectionLostException { + try { + return socket_.getOutputStream(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } +} diff --git a/src/main/java/ioio/lib/android/bluetooth/BluetoothIOIOConnectionBootstrap.java b/src/main/java/ioio/lib/android/bluetooth/BluetoothIOIOConnectionBootstrap.java new file mode 100644 index 00000000..35ff1b3d --- /dev/null +++ b/src/main/java/ioio/lib/android/bluetooth/BluetoothIOIOConnectionBootstrap.java @@ -0,0 +1,97 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ + +package ioio.lib.android.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.util.Log; +import ioio.lib.api.IOIOConnection; +import ioio.lib.spi.IOIOConnectionBootstrap; +import ioio.lib.spi.IOIOConnectionFactory; +import ioio.lib.spi.NoRuntimeSupportException; + +import java.util.Collection; +import java.util.Set; + +public class BluetoothIOIOConnectionBootstrap implements + IOIOConnectionBootstrap +{ + + private static final String TAG = "BluetoothIOIOConnectionDiscovery"; + private final BluetoothAdapter adapter_; + + public BluetoothIOIOConnectionBootstrap() throws NoRuntimeSupportException + { + try { + adapter_ = BluetoothAdapter.getDefaultAdapter(); + if (adapter_ != null) { + return; + } + } catch (Throwable e) { + } + throw new NoRuntimeSupportException( + "Bluetooth is not supported on this device."); + } + + @Override + public void getFactories(Collection result) { + try { + Set bondedDevices = adapter_.getBondedDevices(); + for (final BluetoothDevice device : bondedDevices) { + if (device.getName().startsWith("IOIO")) { + result.add(new IOIOConnectionFactory() { + @Override + public String getType() { + return BluetoothIOIOConnection.class + .getCanonicalName(); + } + + @Override + public Object getExtra() { + return new Object[] { device.getName(), + device.getAddress() }; + } + + @Override + public IOIOConnection createConnection() { + return new BluetoothIOIOConnection(device); + } + }); + } + } + } catch (SecurityException e) { + Log.e(TAG, + "Did you forget to declare uses-permission of android.permission.BLUETOOTH?"); + throw e; + } catch (NoClassDefFoundError e) { + Log.w(TAG, "Bluetooth is not supported on this device.", e); + } + } +} diff --git a/src/main/java/ioio/lib/api/AnalogInput.java b/src/main/java/ioio/lib/api/AnalogInput.java new file mode 100644 index 00000000..4fdd02b2 --- /dev/null +++ b/src/main/java/ioio/lib/api/AnalogInput.java @@ -0,0 +1,225 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ + +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +/** + * A pin used for analog input. + *

+ * An analog input pin can be used to measure voltage. AnalogInput instances are + * obtained by calling {@link ioio.lib.api.IOIO#openAnalogInput(int)}. + *

+ * Floating-point values scaled from 0 to 1 can be obtained by calling + * {@link #read()}. Absolute voltage levels can be obtained by calling + * {@link #getVoltage()}. + *

+ * The instance is alive since its creation. The first {@link #read()} call + * block for a few milliseconds until the initial value is updated. If the + * connection with the IOIO drops at any point, the instance transitions to a + * disconnected state, in which every attempt to use the pin (except + * {@link #close()}) will throw a {@link ioio.lib.api.exception.ConnectionLostException}. Whenever + * {@link #close()} is invoked the instance may no longer be used. Any resources + * associated with it are freed and can be reused. + *

+ * Typical usage: + * + *

+ * {@code
+ * AnalogInput potentiometer = ioio.openAnalogInput(40);
+ * float value = potentiometer.read();
+ * ...
+ * potentiometer.close();  // pin 40 can now be used for something else.
+ * }
+ * 
+ *

+ * An alternate usage allows reading periodically sampled data without missing + * samples. The {@link #setBuffer(int)} method must first be called, for setting + * up an internal buffer for queuing samples. Then, samples can be obtained by + * calling {@link #readBuffered()} or {@link #getVoltageBuffered()}. These + * methods will block until a sample is available. If this is undesirable, the + * {@link #available()} method can be called first to check how many samples are + * ready in the buffer. In case the buffer overflows, as result of the client + * not reading fast enough, old samples will be dropped, and the client can + * check {@link #getOverflowCount()} to determine how many samples have been + * lost. The sample rate used for capturing samples can be obtained by calling + * {@link #getSampleRate()}. + *

+ * The non-buffered versions of the read methods will still behave normally when + * buffering is enabled. The {@link #read()} and {@link #getVoltage()} methods + * will always return the most recent value, regardless of the buffer state. + *

+ * Typical usage: + * + *

+ * AnalogInput potentiometer = ioio.openAnalogInput(40);
+ * potentiometer.setBuffer(256);
+ * for (int i = 0; i < 1024; ++i) {
+ *   // next line will block until at least one sample is available
+ *   float sample = potentiometer.readBuffered();
+ *   ...
+ * }
+ * ...
+ * potentiometer.close();  // pin 40 can now be used for something else.
+ * 
+ * + * @see ioio.lib.api.IOIO#openAnalogInput(int) + */ +public interface AnalogInput extends Closeable { + /** + * Gets the analog input reading, as an absolute voltage in Volt units. + *

+ * It typically takes a few milliseconds between when the instance is + * created and until the first value can be read. In this case, the method + * may block shortly. If this is a problem, the calling thread can be + * interrupted. + *

+ * If a scaled value is desired, consider using {@link #read()}. + * + * @return The voltage, in Volt units. + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO is lost. + * @see #read() + */ + public float getVoltage() throws InterruptedException, + ConnectionLostException; + + /** + * Gets the maximum value against which {@link #read()} values are scaled. + * + * @return The voltage, in Volts. + */ + public float getReference(); + + /** + * Gets the analog input reading, as a scaled real value between 0 and 1. + *

+ * It typically takes a few milliseconds between when the instance is + * created and until the first value can be read. In this case, the method + * may block shortly. If this is a problem, the calling thread can be + * interrupted. + *

+ * If an absolute value is desired, consider using {@link #getVoltage()}. + * + * @return The voltage, in scaled units. + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO is lost. + * @see #getVoltage() + */ + public float read() throws InterruptedException, ConnectionLostException; + + /** + * Initializes or destroys an internal buffer, used for queuing sampled + * data. When called with a positive argument, an internal buffer will be + * created, and start storing sampled data. The client can then call + * {@link #readBuffered()} or {@link #getVoltageBuffered()} for obtaining + * buffered samples. + *

+ * When called with argument of 0, the internal buffer is destroyed. + * + * @param capacity + * The maximum number of unread samples that can be buffered + * before overflow occurs. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO is lost. + */ + public void setBuffer(int capacity) throws ConnectionLostException; + + /** + * Gets the number of samples that have been dropped as result of overflow, + * since {@link #setBuffer(int)} has been called. + * + * @return The number of dropped samples. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO is lost. + */ + public int getOverflowCount() throws ConnectionLostException; + + /** + * Gets the number of samples currently in the buffer. Reading that many + * samples is guaranteed not to block. + * + * @return The number of samples available in the buffer. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO is lost. + */ + public int available() throws ConnectionLostException; + + /** + * Read a sample from the internal buffer. This method will block until at + * least one sample is available, the instance is closed (via + * {@link #close()}), the thread is interrupted (via + * {@link Thread#interrupt()} or connection is lost. {@link #setBuffer(int)} + * must be called prior to this method for setting up an internal buffer for + * storing samples. + * + * @see #getVoltageBuffered() + * @return The earliest (oldest) sample available in the buffer, scaled to + * the range [0,1]. + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO is lost. + */ + public float readBuffered() throws InterruptedException, + ConnectionLostException; + + /** + * Read a sample from the internal buffer. This method will block until at + * least one sample is available, the instance is closed (via + * {@link #close()}), the thread is interrupted (via + * {@link Thread#interrupt()} or connection is lost. {@link #setBuffer(int)} + * must be called prior to this method for setting up an internal buffer for + * storing samples. + * + * @see #readBuffered() + * @return The earliest (oldest) sample available in the buffer, in Volt + * units. + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO is lost. + */ + public float getVoltageBuffered() throws InterruptedException, + ConnectionLostException; + + /** + * Gets the sample rate used for obtaining buffered samples. + * + * @return The sample rate, in Hz units. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO is lost. + */ + public float getSampleRate() throws ConnectionLostException; +} diff --git a/src/main/java/ioio/lib/api/Closeable.java b/src/main/java/ioio/lib/api/Closeable.java new file mode 100644 index 00000000..a53373a4 --- /dev/null +++ b/src/main/java/ioio/lib/api/Closeable.java @@ -0,0 +1,6 @@ +package ioio.lib.api; + +public interface Closeable +{ + void close(); +} diff --git a/src/main/java/ioio/lib/api/DigitalInput.java b/src/main/java/ioio/lib/api/DigitalInput.java new file mode 100644 index 00000000..4b384b4d --- /dev/null +++ b/src/main/java/ioio/lib/api/DigitalInput.java @@ -0,0 +1,143 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +import java.io.Closeable; + +/** + * A pin used for digital input. + *

+ * A digital input pin can be used to read logic-level signals. DigitalInput + * instances are obtained by calling {@link ioio.lib.api.IOIO#openDigitalInput(ioio.lib.api.DigitalInput.Spec)}. + *

+ * The value of the pin is obtained by calling {@link #read()}. It is also + * possible for the client to block until a certain level is sensed, by using + * {@link #waitForValue(boolean)}. + *

+ * The instance is alive since its creation. The first {@link #read()} call + * block for a few milliseconds until the initial value is updated. If the + * connection with the IOIO drops at any point, the instance transitions to a + * disconnected state, in which every attempt to use the pin (except + * {@link #close()}) will throw a {@link ioio.lib.api.exception.ConnectionLostException}. Whenever + * {@link #close()} is invoked the instance may no longer be used. Any resources + * associated with it are freed and can be reused. + *

+ * Typical usage: + * + *

+ * DigitalInput button = ioio.openDigitalInput(10);  // used an external pull-up
+ * button.waitForValue(false);  // wait for press
+ * ...
+ * button.close();  // pin 10 can now be used for something else.
+ * 
+ */ +public interface DigitalInput extends Closeable +{ + /** + * A digital input pin specification, used when opening digital inputs. + */ + static public class Spec { + /** Input pin mode. */ + public enum Mode { + /** + * Pin is floating. When the pin is left disconnected the value + * sensed is undefined. Use this mode when an external pull-up or + * pull-down resistor is used or when interfacing push-pull type + * logic circuits. + */ + FLOATING, + /** + * Internal pull-up resistor is used. When the pin is left + * disconnected, a logical "HIGH" (true) will be sensed. This is + * useful for interfacing with open drain circuits or for + * interacting with a switch connected between the pin and ground. + */ + PULL_UP, + /** + * Internal pull-down resistor is used. When the pin is left + * disconnected, a logical "LOW" (false) will be sensed. This is + * useful for interacting with a switch connected between the pin + * and Vdd. + */ + PULL_DOWN + } + + /** The pin number, as labeled on the board. */ + public int pin; + /** The pin mode. */ + public Mode mode; + + /** + * Constructor. + * + * @param pin + * Pin number, as labeled on the board. + * @param mode + * Pin mode. + */ + public Spec(int pin, Mode mode) { + this.pin = pin; + this.mode = mode; + } + + /** Shorthand for Spec(pin, Mode.FLOATING). */ + public Spec(int pin) { + this(pin, Mode.FLOATING); + } + } + + /** + * Read the value sensed on the pin. May block for a few milliseconds if + * called right after creation of the instance. If this is a problem, the + * calling thread may be interrupted. + * + * @return True for logical "HIGH", false for logical "LOW". + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO has been lost. + */ + public boolean read() throws InterruptedException, ConnectionLostException; + + /** + * Block until a desired logical level is sensed. The calling thread can be + * interrupted for aborting this operation. + * + * @param value + * The desired logical level. true for "HIGH", false for "LOW". + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO has been lost. + */ + public void waitForValue(boolean value) throws InterruptedException, + ConnectionLostException; +} diff --git a/src/main/java/ioio/lib/api/DigitalOutput.java b/src/main/java/ioio/lib/api/DigitalOutput.java new file mode 100644 index 00000000..d9a89d30 --- /dev/null +++ b/src/main/java/ioio/lib/api/DigitalOutput.java @@ -0,0 +1,120 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +/** + * A pin used for digital output. + *

+ * A digital input pin can be used to generate logic-level signals. + * DigitalOutput instances are obtained by calling + * {@link ioio.lib.api.IOIO#openDigitalOutput(ioio.lib.api.DigitalOutput.Spec, boolean)}. + *

+ * The value of the pin is set by calling {@link #write(boolean)}. + *

+ * The instance is alive since its creation. If the connection with the IOIO + * drops at any point, the instance transitions to a disconnected state, in + * which every attempt to use the pin (except {@link #close()}) will throw a + * {@link ioio.lib.api.exception.ConnectionLostException}. Whenever {@link #close()} is invoked the + * instance may no longer be used. Any resources associated with it are freed + * and can be reused. + *

+ * Typical usage: + * + *

+ * DigitalOutput led = ioio.openDigitalInput(2);  // LED anode on pin 2.
+ * led.write(true);  // turn LED on.
+ * ...
+ * led.close();  // pin 2 can now be used for something else.
+ * 
+ */ +public interface DigitalOutput extends Closeable { + /** + * A digital output pin specification, used when opening digital outputs. + */ + public static class Spec { + /** Output pin mode. */ + public enum Mode { + /** + * Pin operates in push-pull mode, i.e. a logical "HIGH" is + * represented by a voltage of Vdd on the pin and a logical "LOW" by + * a voltage of 0 (ground). + */ + NORMAL, + /** + * Pin operates in open-drain mode, i.e. a logical "HIGH" is + * represented by a high impedance on the pin (as if it is + * disconnected) and a logical "LOW" by a voltage of 0 (ground). + * This mode is most commonly used for generating 5V logical signal + * on a 3.3V pin: 5V tolerant pins must be used; a pull-up resistor + * is connected between the pin and 5V, and the pin is used in open- + * drain mode. + */ + OPEN_DRAIN, + } + + /** The pin number, as labeled on the board. */ + public int pin; + /** The pin mode. */ + public Mode mode; + + /** + * Constructor. + * + * @param pin + * Pin number, as labeled on the board. + * @param mode + * Pin mode. + */ + public Spec(int pin, Mode mode) { + this.pin = pin; + this.mode = mode; + } + + /** + * Shorthand for Spec(pin, Mode.NORMAL). + * + * @see ioio.lib.api.DigitalOutput.Spec#Spec(int, ioio.lib.api.DigitalOutput.Spec.Mode) + */ + public Spec(int pin) { + this(pin, Mode.NORMAL); + } + } + + /** + * Set the output of the pin. + * + * @param val + * The output. true is logical "HIGH", false is logical "LOW". + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO has been lost. + */ + public void write(boolean val) throws ConnectionLostException; +} diff --git a/src/main/java/ioio/lib/api/IOIO.java b/src/main/java/ioio/lib/api/IOIO.java new file mode 100755 index 00000000..f012de3f --- /dev/null +++ b/src/main/java/ioio/lib/api/IOIO.java @@ -0,0 +1,695 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api; + +import ioio.lib.api.PulseInput.PulseMode; +import ioio.lib.api.TwiMaster.Rate; +import ioio.lib.api.Uart.Parity; +import ioio.lib.api.Uart.StopBits; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.api.exception.IncompatibilityException; +import ioio.lib.api.exception.OutOfResourceException; + +/** + * This interface provides control over all the IOIO board functions. + *

+ * An instance of this interface is typically obtained by using the + * {@link IOIOFactory} class. Initially, a connection should be established, by + * calling {@link #waitForConnect()}. This method will block until the board is + * connected an a connection has been established. + *

+ * During the connection process, this library verifies that the IOIO firmware + * is compatible with the required version. If not, {@link #waitForConnect()} + * will throw a {@link IncompatibilityException}, putting the {@link ioio.lib.api.IOIO} + * instance in a "zombie" state: nothing could be done with it except calling + * {@link #disconnect()}, or waiting for the physical connection to drop via + * {@link #waitForDisconnect()}. + *

+ * As soon as a connection is established, the IOIO can be used, typically, by + * calling the openXXX() functions to obtain additional interfaces for + * controlling specific function of the board. + *

+ * Whenever a connection is lost as a result of physically disconnecting the + * board or as a result of calling {@link #disconnect()}, this instance and all + * the interfaces obtained from it become invalid, and will throw a + * {@link ioio.lib.api.exception.ConnectionLostException} on every operation. Once the connection is + * lost, those instances cannot be recycled, but rather it is required to create + * new ones and wait for a connection again. + *

+ * Initially all pins are tri-stated (floating), and all functions are disabled. + * Whenever a connection is lost or dropped, the board will immediately return + * to the this initial state. + *

+ * Typical usage: + * + *

+ * IOIO ioio = IOIOFactory.create();
+ * try {
+ *   ioio.waitForConnect();
+ *   DigitalOutput out = ioio.openDigitalOutput(10);
+ *   out.write(true);
+ *   ...
+ * } catch (ConnectionLostException e) {
+ * } catch (Exception e) {
+ *   ioio.disconnect();
+ * } finally {
+ *   ioio.waitForDisconnect();
+ * }
+ * 
+ * + * @see IOIOFactory#create() + */ +public interface IOIO +{ + /** An invalid pin number. */ + public static final int INVALID_PIN = -1; + /** The pin number used to designate the on-board 'stat' LED. */ + public static final int LED_PIN = 0; + + /** + * A versioned component in the system. + * + * @see ioio.lib.api.IOIO#getImplVersion(ioio.lib.api.IOIO.VersionType) + */ + public enum VersionType { + /** Hardware version. */ + HARDWARE_VER, + /** Bootloader version. */ + BOOTLOADER_VER, + /** Application layer firmware version. */ + APP_FIRMWARE_VER, + /** IOIOLib version. */ + IOIOLIB_VER + } + + /** + * A state of a IOIO instance. + */ + public enum State { + /** Connection not yet established. */ + INIT, + /** Connected. */ + CONNECTED, + /** Connection established, incompatible firmware detected. */ + INCOMPATIBLE, + /** Disconnected. Instance is useless. */ + DEAD + } + + /** + * Establishes connection with the IOIO board. + *

+ * This method is blocking until connection is established. This method can + * be aborted by calling {@link #disconnect()}. In this case, it will throw + * a {@link ioio.lib.api.exception.ConnectionLostException}. + * + * @throws ioio.lib.api.exception.ConnectionLostException + * An error occurred during connection or disconnect() has been + * called during connection. The instance state is disconnected. + * @throws IncompatibilityException + * An incompatible board firmware of hardware has been detected. + * The instance state is disconnected. + * @see #disconnect() + * @see #waitForDisconnect() + */ + public void waitForConnect() throws ConnectionLostException, + IncompatibilityException; + + /** + * Closes the connection to the board, or aborts a connection process + * started with waitForConnect(). + *

+ * Once this method is called, this IOIO instance and all the instances + * obtain from it become invalid and will throw an exception on every + * operation. + *

+ * This method is asynchronous, i.e. it returns immediately, but it is not + * guaranteed that all connection-related resources has already been freed + * and can be reused upon return. In cases when this is important, client + * can call {@link #waitForDisconnect()}, which will block until all + * resources have been freed. + */ + public void disconnect(); + + /** + * Blocks until IOIO has been disconnected and all connection-related + * resources have been freed, so that a new connection can be attempted. + * + * @throws InterruptedException + * When interrupt() has been called on this thread. This might + * mean that an immediate attempt to create and connect a new + * IOIO object might fail for resource contention. + * @see #disconnect() + * @see #waitForConnect() + */ + public void waitForDisconnect() throws InterruptedException; + + /** + * Gets the connections state. + * + * @return The connection state. + */ + public State getState(); + + /** + * Resets the entire state (returning to initial state), without dropping + * the connection. + *

+ * It is equivalent to calling {@link java.io.Closeable#close()} on every interface + * obtained from this instance. A connection must have been established + * prior to calling this method, by invoking {@link #waitForConnect()}. + * + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @see #hardReset() + */ + public void softReset() throws ConnectionLostException; + + /** + * Equivalent to disconnecting and reconnecting the board power supply. + *

+ * The connection will be dropped and not reestablished. Full boot sequence + * will take place, so firmware upgrades can be performed. A connection must + * have been established prior to calling this method, by invoking + * {@link #waitForConnect()}. + * + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @see #softReset() + */ + public void hardReset() throws ConnectionLostException; + + /** + * Query the implementation version of the system's components. The + * implementation version uniquely identifies a hardware revision or a + * software build. Returned version IDs are always 8-character long, + * according to the IOIO versioning system: first 4 characters are the + * version authority and last 4 characters are the revision. + * + * @param v + * The component whose version we query. + * @return An 8-character implementation version ID. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + */ + public String getImplVersion(VersionType v) throws ConnectionLostException; + + /** + * Open a pin for digital input. + *

+ * A digital input pin can be used to read logic-level signals. The pin will + * operate in this mode until close() is invoked on the returned interface. + * It is illegal to open a pin that has already been opened and has not been + * closed. A connection must have been established prior to calling this + * method, by invoking {@link #waitForConnect()}. + * + * @param spec + * Pin specification, consisting of the pin number, as labeled on + * the board, and the mode, which determines whether the pin will + * be floating, pull-up or pull-down. See + * {@link DigitalInput.Spec.Mode} for more information. + * @return Interface of the assigned pin. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @see DigitalInput + */ + public DigitalInput openDigitalInput(DigitalInput.Spec spec) + throws ConnectionLostException; + + /** + * Shorthand for openDigitalInput(new DigitalInput.Spec(pin)). + * + * @see #openDigitalInput(ioio.lib.api.DigitalInput.Spec) + */ + public DigitalInput openDigitalInput(int pin) + throws ConnectionLostException; + + /** + * Shorthand for openDigitalInput(new DigitalInput.Spec(pin, mode)). + * + * @see #openDigitalInput(ioio.lib.api.DigitalInput.Spec) + */ + public DigitalInput openDigitalInput(int pin, DigitalInput.Spec.Mode mode) + throws ConnectionLostException; + + /** + * Open a pin for digital output. + *

+ * A digital output pin can be used to generate logic-level signals. The pin + * will operate in this mode until close() is invoked on the returned + * interface. It is illegal to open a pin that has already been opened and + * has not been closed. A connection must have been established prior to + * calling this method, by invoking {@link #waitForConnect()}. + * + * @param spec + * Pin specification, consisting of the pin number, as labeled on + * the board, and the mode, which determines whether the pin will + * be normal or open-drain. See {@link DigitalOutput.Spec.Mode} + * for more information. + * @param startValue + * The initial logic level this pin will generate as soon at it + * is open. + * @return Interface of the assigned pin. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @see DigitalOutput + */ + public DigitalOutput openDigitalOutput(DigitalOutput.Spec spec, + boolean startValue) throws ConnectionLostException; + + /** + * Shorthand for openDigitalOutput(new DigitalOutput.Spec(pin, mode), + * startValue). + * + * @see #openDigitalOutput(ioio.lib.api.DigitalOutput.Spec, boolean) + */ + public DigitalOutput openDigitalOutput(int pin, + DigitalOutput.Spec.Mode mode, boolean startValue) + throws ConnectionLostException; + + /** + * Shorthand for openDigitalOutput(new DigitalOutput.Spec(pin), startValue). + * Pin mode will be "normal" (as opposed to "open-drain". + * + * @see #openDigitalOutput(ioio.lib.api.DigitalOutput.Spec, boolean) + */ + public DigitalOutput openDigitalOutput(int pin, boolean startValue) + throws ConnectionLostException; + + /** + * Shorthand for openDigitalOutput(new DigitalOutput.Spec(pin), false). Pin + * mode will be "normal" (as opposed to "open-drain". + * + * @see #openDigitalOutput(ioio.lib.api.DigitalOutput.Spec, boolean) + */ + public DigitalOutput openDigitalOutput(int pin) + throws ConnectionLostException; + + /** + * Open a pin for analog input. + *

+ * An analog input pin can be used to measure voltage. Note that not every + * pin can be used as an analog input. See board documentation for the legal + * pins and permitted voltage range. + *

+ * The pin will operate in this mode until close() is invoked on the + * returned interface. It is illegal to open a pin that has already been + * opened and has not been closed. A connection must have been established + * prior to calling this method, by invoking {@link #waitForConnect()}. + * + * @param pin + * Pin number, as labeled on the board. + * @return Interface of the assigned pin. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @see AnalogInput + */ + public AnalogInput openAnalogInput(int pin) throws ConnectionLostException; + + /** + * Open a pin for PWM (Pulse-Width Modulation) output. + *

+ * A PWM pin produces a logic-level PWM signal. These signals are typically + * used for simulating analog outputs for controlling the intensity of LEDs, + * the rotation speed of motors, etc. They are also frequently used for + * controlling hobby servo motors. + *

+ * Note that not every pin can be used as PWM output. In addition, the total + * number of concurrent PWM modules in use is limited. See board + * documentation for the legal pins and limit on concurrent usage. + *

+ * The pin will operate in this mode until close() is invoked on the + * returned interface. It is illegal to open a pin that has already been + * opened and has not been closed. A connection must have been established + * prior to calling this method, by invoking {@link #waitForConnect()}. + * + * @param spec + * Pin specification, consisting of the pin number, as labeled on + * the board, and the mode, which determines whether the pin will + * be normal or open-drain. See {@link DigitalOutput.Spec.Mode} + * for more information. + * @param freqHz + * PWM frequency, in Hertz. + * @return Interface of the assigned pin. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @throws OutOfResourceException + * This is a runtime exception, so it is not necessary to catch + * it if the client guarantees that the total number of + * concurrent PWM resources is not exceeded. + * @see PwmOutput + */ + public PwmOutput openPwmOutput(DigitalOutput.Spec spec, int freqHz) + throws ConnectionLostException; + + /** + * Shorthand for openPwmOutput(new DigitalOutput.Spec(pin), freqHz). + * + * @see #openPwmOutput(ioio.lib.api.DigitalOutput.Spec, int) + */ + public PwmOutput openPwmOutput(int pin, int freqHz) + throws ConnectionLostException; + + /** + * Open a pin for pulse input. + *

+ * The pulse input module is quite flexible. It enables several kinds of + * timing measurements on a digital signal: pulse width measurement + * (positive or negative pulse), and frequency of a periodic signal. + *

+ * Note that not every pin can be used as pulse input. In addition, the + * total number of concurrent pulse input modules in use is limited. See + * board documentation for the legal pins and limit on concurrent usage. + *

+ * The pin will operate in this mode until close() is invoked on the + * returned interface. It is illegal to open a pin that has already been + * opened and has not been closed. A connection must have been established + * prior to calling this method, by invoking {@link #waitForConnect()}. + * + * @param spec + * Pin specification, consisting of the pin number, as labeled on + * the board, and the mode, which determines whether the pin will + * be floating, pull-up or pull-down. See + * {@link DigitalInput.Spec.Mode} for more information. + * @param rate + * The clock rate to use for timing the signal. A faster clock + * rate will result in better precision but will only be able to + * measure narrow pulses / high frequencies. + * @param mode + * The mode in which to operate. Determines whether the module + * will measure pulse durations or frequency. + * @param doublePrecision + * Whether to open a double-precision pulse input module. Double- + * precision modules enable reading of much longer pulses and + * lower frequencies with high accuracy than single precision + * modules. However, their number is limited, so when possible, + * and if the resources are all needed, use single-precision. For + * more details on the exact spec of single- vs. double- + * precision, see {@link PulseInput}. + * @return An instance of the {@link PulseInput}, which can be used to + * obtain the data. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @throws OutOfResourceException + * This is a runtime exception, so it is not necessary to catch + * it if the client guarantees that the total number of + * concurrent PWM resources is not exceeded. + * @see PulseInput + */ + public PulseInput openPulseInput(DigitalInput.Spec spec, + PulseInput.ClockRate rate, PulseInput.PulseMode mode, + boolean doublePrecision) throws ConnectionLostException; + + /** + * Shorthand for openPulseInput(new DigitalInput.Spec(pin), rate, mode, + * true), i.e. opens a double-precision, 16MHz pulse input on the given pin + * with the given mode. + * + * @see #openPulseInput(ioio.lib.api.DigitalInput.Spec, + * ioio.lib.api.PulseInput.ClockRate, + * ioio.lib.api.PulseInput.PulseMode, boolean) + */ + public PulseInput openPulseInput(int pin, PulseMode mode) + throws ConnectionLostException; + + /** + * Open a UART module, enabling a bulk transfer of byte buffers. + *

+ * UART is a very common hardware communication protocol, enabling full- + * duplex, asynchronous point-to-point data transfer. It typically serves + * for opening consoles or as a basis for higher-level protocols, such as + * MIDI RS-232, and RS-485. + *

+ * Note that not every pin can be used for UART RX or TX. In addition, the + * total number of concurrent UART modules in use is limited. See board + * documentation for the legal pins and limit on concurrent usage. + *

+ * The UART module will operate, and the pins will work in their respective + * modes until close() is invoked on the returned interface. It is illegal + * to use pins that have already been opened and has not been closed. A + * connection must have been established prior to calling this method, by + * invoking {@link #waitForConnect()}. + * + * @param rx + * Pin specification for the RX pin, consisting of the pin + * number, as labeled on the board, and the mode, which + * determines whether the pin will be floating, pull-up or + * pull-down. See {@link DigitalInput.Spec.Mode} for more + * information. null can be passed to designate that we do not + * want RX input to this module. + * @param tx + * Pin specification for the TX pin, consisting of the pin + * number, as labeled on the board, and the mode, which + * determines whether the pin will be normal or open-drain. See + * {@link DigitalOutput.Spec.Mode} for more information. null can + * be passed to designate that we do not want TX output to this + * module. + * @param baud + * The clock frequency of the UART module in Hz. + * @param parity + * The parity mode, as in {@link Parity}. + * @param stopbits + * Number of stop bits, as in {@link StopBits}. + * @return Interface of the assigned module. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @throws OutOfResourceException + * This is a runtime exception, so it is not necessary to catch + * it if the client guarantees that the total number of + * concurrent UART resources is not exceeded. + * @see Uart + */ + public Uart openUart(DigitalInput.Spec rx, DigitalOutput.Spec tx, int baud, + Parity parity, StopBits stopbits) throws ConnectionLostException; + + /** + * Shorthand for + * {@link #openUart(DigitalInput.Spec, DigitalOutput.Spec, int, Uart.Parity, Uart.StopBits)} + * , where the input pins use their default specs. {@link #INVALID_PIN} can + * be used on either pin if a TX- or RX-only UART is needed. + * + * @see #openUart(DigitalInput.Spec, DigitalOutput.Spec, int, Uart.Parity, + * Uart.StopBits) + */ + public Uart openUart(int rx, int tx, int baud, Parity parity, + StopBits stopbits) throws ConnectionLostException; + + /** + * Open a SPI master module, enabling communication with multiple + * SPI-enabled slave modules. + *

+ * SPI is a common hardware communication protocol, enabling full-duplex, + * synchronous point-to-multi-point data transfer. It requires MOSI, MISO + * and CLK lines shared by all nodes, as well as a SS line per slave, + * connected between this slave and a respective pin on the master. The MISO + * line should operate in pull-up mode, using either the internal pull-up or + * an external resistor. + *

+ * Note that not every pin can be used for SPI MISO, MOSI or CLK. In + * addition, the total number of concurrent SPI modules in use is limited. + * See board documentation for the legal pins and limit on concurrent usage. + *

+ * The SPI module will operate, and the pins will work in their respective + * modes until close() is invoked on the returned interface. It is illegal + * to use pins that have already been opened and has not been closed. A + * connection must have been established prior to calling this method, by + * invoking {@link #waitForConnect()}. + * + * @param miso + * Pin specification for the MISO (Master In Slave Out) pin, + * consisting of the pin number, as labeled on the board, and the + * mode, which determines whether the pin will be floating, + * pull-up or pull-down. See {@link DigitalInput.Spec.Mode} for + * more information. + * @param mosi + * Pin specification for the MOSI (Master Out Slave In) pin, + * consisting of the pin number, as labeled on the board, and the + * mode, which determines whether the pin will be normal or + * open-drain. See {@link DigitalOutput.Spec.Mode} for more + * information. + * @param clk + * Pin specification for the CLK pin, consisting of the pin + * number, as labeled on the board, and the mode, which + * determines whether the pin will be normal or open-drain. See + * {@link DigitalOutput.Spec.Mode} for more information. + * @param slaveSelect + * An array of pin specifications for each of the slaves' SS + * (Slave Select) pin. The index of this array designates the + * slave index, used later to refer to this slave. The spec is + * consisting of the pin number, as labeled on the board, and the + * mode, which determines whether the pin will be normal or + * open-drain. See {@link DigitalOutput.Spec.Mode} for more + * information. + * @param config + * The configuration of the SPI module. See + * {@link SpiMaster.Config} for details. + * @return Interface of the assigned module. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @throws OutOfResourceException + * This is a runtime exception, so it is not necessary to catch + * it if the client guarantees that the total number of + * concurrent SPI resources is not exceeded. + * @see SpiMaster + */ + public SpiMaster openSpiMaster(DigitalInput.Spec miso, + DigitalOutput.Spec mosi, DigitalOutput.Spec clk, + DigitalOutput.Spec[] slaveSelect, SpiMaster.Config config) + throws ConnectionLostException; + + /** + * Shorthand for + * {@link #openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config)} + * , where the pins are all open with the default modes and default + * configuration values are used. + * + * @see #openSpiMaster(ioio.lib.api.DigitalInput.Spec, + * ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, + * ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config) + */ + public SpiMaster openSpiMaster(int miso, int mosi, int clk, + int[] slaveSelect, SpiMaster.Rate rate) + throws ConnectionLostException; + + /** + * Shorthand for + * {@link #openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config)} + * , where the MISO pins is opened with pull up, and the other pins are open + * with the default modes and default configuration values are used. In this + * version, a single slave is used. + * + * @see #openSpiMaster(ioio.lib.api.DigitalInput.Spec, + * ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, + * ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config) + */ + public SpiMaster openSpiMaster(int miso, int mosi, int clk, + int slaveSelect, SpiMaster.Rate rate) + throws ConnectionLostException; + + /** + * Open a TWI (Two-Wire Interface, such as I2C/SMBus) master module, + * enabling communication with multiple TWI-enabled slave modules. + *

+ * TWI is a common hardware communication protocol, enabling half-duplex, + * synchronous point-to-multi-point data transfer. It requires a physical + * connection of two lines (SDA, SCL) shared by all the bus nodes, where the + * SDA is open-drain and externally pulled-up. + *

+ * Note that there is a fixed number of TWI modules, and the pins they use + * are static. Client has to make sure these pins are not already opened + * before calling this method. See board documentation for the number of + * modules and the respective pins they use. + *

+ * The TWI module will operate, and the pins will work in their respective + * modes until close() is invoked on the returned interface. It is illegal + * to use pins that have already been opened and has not been closed. A + * connection must have been established prior to calling this method, by + * invoking {@link #waitForConnect()}. + * + * @param twiNum + * The TWI module index to use. Will also determine the pins + * used. + * @param rate + * The clock rate. Can be 100KHz / 400KHz / 1MHz. + * @param smbus + * When true, will use SMBus voltage levels. When false, I2C + * voltage levels. + * @return Interface of the assigned module. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + * @see TwiMaster + */ + public TwiMaster openTwiMaster(int twiNum, Rate rate, boolean smbus) + throws ConnectionLostException; + + /** + * Open an ICSP channel, enabling Flash programming of an external PIC MCU, + * and in particular, another IOIO board. + *

+ * ICSP (In-Circuit Serial Programming) is a protocol intended for + * programming of PIC MCUs. It is a serial protocol over three wires: PGC + * (clock), PGD (data) and MCLR (reset), where PGC and MCLR are controlled + * by the master and PGD is shared by the master and slave, depending on the + * transaction state. + *

+ * Note that there is only one ICSP modules, and the pins it uses are + * static. Client has to make sure that the ICSP module is not already in + * use, as well as those dedicated pins. See board documentation for the + * actual pins used for ICSP. + * + * @return Interface of the ICSP module. + * @see ioio.lib.api.IcspMaster + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + */ + public IcspMaster openIcspMaster() throws ConnectionLostException; + + /** + * Start a batch of operations. This is strictly an optimization and will + * not change functionality: if the client knows that a sequence of several + * IOIO operations are going to be performed immediately following each + * other, a call to {@link #beginBatch()} before the sequence and + * {@link #endBatch()} after the sequence will cause the operations to be + * grouped into one transfer to the IOIO, thus reducing latency. A matching + * {@link #endBatch()} operation must always follow, or otherwise no + * operation will ever be actually executed. {@link #beginBatch()} / + * {@link #endBatch()} blocks may be nested - the transfer will occur when + * the outermost {@link #endBatch()} is invoked. Note that it is not + * guaranteed that no transfers will happen while inside a batch - it should + * be treated as a hint. Code running inside the block must be quick as it + * blocks all transfers to the IOIO, including those performed from + * other threads. + * + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + */ + public void beginBatch() throws ConnectionLostException; + + /** + * End a batch of operations. For explanation, see {@link #beginBatch()}. + * + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection was lost before or during the execution of this + * method. + */ + public void endBatch() throws ConnectionLostException; +} diff --git a/src/main/java/ioio/lib/api/IOIOConnection.java b/src/main/java/ioio/lib/api/IOIOConnection.java new file mode 100644 index 00000000..31cc5468 --- /dev/null +++ b/src/main/java/ioio/lib/api/IOIOConnection.java @@ -0,0 +1,45 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +import java.io.InputStream; +import java.io.OutputStream; + +public interface IOIOConnection +{ + void waitForConnect() throws ConnectionLostException; + + void disconnect(); + + InputStream getInputStream() throws ConnectionLostException; + + OutputStream getOutputStream() throws ConnectionLostException; +} diff --git a/src/main/java/ioio/lib/api/IOIOFactory.java b/src/main/java/ioio/lib/api/IOIOFactory.java new file mode 100644 index 00000000..0defb10c --- /dev/null +++ b/src/main/java/ioio/lib/api/IOIOFactory.java @@ -0,0 +1,95 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api; + +import android.util.Log; +import ioio.lib.impl.IOIOImpl; +import ioio.lib.spi.IOIOConnectionFactory; +import ioio.lib.util.IOIOConnectionRegistry; + +import java.util.Collection; +import java.util.NoSuchElementException; + +/** + * Factory class for creating instances of the IOIO interface. + *

+ * This class acts as the single entry-point to the IOIO API. It creates the + * bootstrapping between a specific implementation of the IOIO interface and any + * dependencies it might have, such as the underlying connection logic. + *

+ * Typical usage: + * + *

+ * IOIO ioio = IOIOFactory.create();
+ * try {
+ *   ioio.waitForConnect();
+ *   ...
+ *   ioio.disconnect();
+ * } catch (ConnectionLostException e) {
+ * } finally {
+ *   ioio.waitForDisconnect();
+ * }
+ * 
+ */ +public class IOIOFactory +{ + /** + * Create a IOIO instance. This specific implementation creates a IOIO + * instance which works with the actual IOIO board connected via a TCP + * connection (typically over a wired USB connection). + * + * @return The IOIO instance. + */ + public static IOIO create() { + Collection factories = IOIOConnectionRegistry + .getConnectionFactories(); + try { + return create(factories.iterator().next().createConnection()); + } catch (NoSuchElementException e) { + Log.e(TAG, "No connection is available. This shouldn't happen."); + throw e; + } + } + + /** + * Create a IOIO instance with a user-provided underlying connection class. + * This method should be used for establishing a non-standard connection to + * the IOIO board. + * + * @param connection + * An instance of a IOIO connection. + * + * @return The IOIO instance. + */ + public static IOIO create(IOIOConnection connection) { + return new IOIOImpl(connection); + } + + private static final String TAG = "IOIOFactory"; +} diff --git a/src/main/java/ioio/lib/api/IcspMaster.java b/src/main/java/ioio/lib/api/IcspMaster.java new file mode 100644 index 00000000..b6f6b866 --- /dev/null +++ b/src/main/java/ioio/lib/api/IcspMaster.java @@ -0,0 +1,145 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +/** + * An interface for controlling an ICSP channel, enabling Flash programming of + * an external PIC MCU, and in particular, another IOIO board. + *

+ * ICSP (In-Circuit Serial Programming) is a protocol intended for programming + * of PIC MCUs. It is a serial protocol over three wires: PGC (clock), PGD + * (data) and MCLR (reset), where PGC and MCLR are controlled by the master and + * PGD is shared by the master and slave, depending on the transaction state. + * IcspMaster instances are obtained by calling {@link IOIO#openIcspMaster()}. + *

+ * This interface is very low level: it allows direct access to the atomic + * operations of the ICSP protocol: + *

    + *
  • Enter / exit programming mode ( {@link #enterProgramming()} / + * {@link #exitProgramming()}, respectively).
  • + *
  • Executing a single instruction on the slave MCU ( + * {@link #executeInstruction(int)}).
  • + *
  • Reading the value of the VISI register of the slave MCU into a read queue + * ({@link #readVisi()}).
  • + *
+ *

+ * The ICSP module uses fixed pins for its lines. See the user guide for details + * for your specific board. ICSP is a special feature, introduced for the + * purpose of programming a IOIO board with another IOIO board. It does not + * necessarily play nicely when used concurrently with other features, in the + * sense that it may introduce latencies in other modules. It is thus + * recommended not to use ICSP in conjunction with latency-sensitive features. + *

+ * The instance is alive since its creation. If the connection with the IOIO + * drops at any point, the instance transitions to a disconnected state, in + * which every attempt to use it (except {@link #close()}) will throw a + * {@link ioio.lib.api.exception.ConnectionLostException}. Whenever {@link #close()} is invoked the + * instance may no longer be used. Any resources associated with it are freed + * and can be reused. + *

+ * Typical usage: + * + *

+ * {@code
+ * IcspMaster icsp = ioio.openIcspMaster();
+ * icsp.enterProgramming();
+ * icsp.executeInstruction(0x212340);  // mov #0x1234, w0
+ * icsp.executeInstruction(0x883C20);  // mov w0, 0x784 (VISI)
+ * icsp.executeInstruction(0x000000);  // nop
+ * icsp.readVisi();
+ * int visi = icsp.waitVisiResult();   // should read 0x1234
+ * icsp.exitProgramming();
+ * icsp.close();                       // free ICSP module and pins
+ * }
+ * + * @see IOIO#openIcspMaster() + */ +public interface IcspMaster extends Closeable { + /** + * Initiate a sequence that will put the slave device in programming mode. + * This sequence is necessary for executing instructions and reading + * register values. + * + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection to the IOIO has been lost. + */ + public void enterProgramming() throws ConnectionLostException; + + /** + * Initiate a sequence that will put the slave device out of programming + * mode. It will be held in reset. + * + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection to the IOIO has been lost. + */ + public void exitProgramming() throws ConnectionLostException; + + /** + * Execute a single instruction on the slave MCU. + * + * @param instruction + * a 24-bit PIC instruction. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection to the IOIO has been lost. + */ + public void executeInstruction(int instruction) + throws ConnectionLostException; + + /** + * Request a read of the VISI register on the slave MCU. This is an + * asynchronous call, in which the 16-bit result is obtained by + * {@link #waitVisiResult()}. + * This method may block if the read queue on the IOIO is full, but this + * should be for short periods only. + * + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection to the IOIO has been lost. + * @throws InterruptedException + * Interrupted while blocking. + */ + public void readVisi() throws ConnectionLostException, InterruptedException; + + /** + * Wait and return a result of a call to {@link #readVisi()}. + * Results will be returned in the same order as requested. + * + * The call will block until there is data, until interrupted, or until + * connection to the IOIO has been lost. + * + * @return The result - an unsigned 16-bit number. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection to the IOIO has been lost. + * @throws InterruptedException + * Interrupted while blocking. + */ + public int waitVisiResult() throws ConnectionLostException, + InterruptedException; +} diff --git a/src/main/java/ioio/lib/api/PulseInput.java b/src/main/java/ioio/lib/api/PulseInput.java new file mode 100644 index 00000000..768fb1d2 --- /dev/null +++ b/src/main/java/ioio/lib/api/PulseInput.java @@ -0,0 +1,325 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +import java.io.Closeable; + +/** + * An interface for pulse width and frequency measurements of digital signals. + *

+ * PulseInput (commonly known as "input capture") is a versatile module which + * enables extraction of various timing information from a digital signal. There + * are two main use cases: pulse duration measurement and frequency measurement. + * In pulse width measurement we measure the duration of a positive ("high") or + * negative ("low") pulse, i.e. the elapsed time between a rise and a fall or + * vice versa. This mode is useful, for example, for decoding a PWM signal or + * measuring the delay of a sonar return signal. In frequency measurement we + * measure the duration between a rising edge to the following rising edge. This + * gives us a momentary reading of a signal's frequency or period. This is + * commonly used, for example, in conjunction with an optical or magnetic sensor + * for measuring a turning shaft's speed. + *

+ * {@link ioio.lib.api.PulseInput} instances are obtained by calling + * {@link ioio.lib.api.IOIO#openPulseInput(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.PulseInput.ClockRate, ioio.lib.api.PulseInput.PulseMode, boolean)} + * . When created, some important configuration decisions have to be made: the + * precision (single or double), the clock rate and the mode of operation. Modes + * are straightforward: {@link ioio.lib.api.PulseInput.PulseMode#POSITIVE} is used for measuring a + * positive pulse, {@link ioio.lib.api.PulseInput.PulseMode#NEGATIVE} a negative pulse, and + * {@link ioio.lib.api.PulseInput.PulseMode#FREQ} / {@link ioio.lib.api.PulseInput.PulseMode#FREQ_SCALE_4} / + * {@link ioio.lib.api.PulseInput.PulseMode#FREQ_SCALE_16} are used for measuring frequency. The + * difference between the three scaling modes is that without scaling, the + * frequency is determined by measurement of a single + * (rising-edge-to-rising-edge) period. In x4 scaling, 4 consecutive periods are + * measured and the time is divided by 4, providing some smoothing as well as + * better resolution. Similarly for x16 scaling. Note that scaling affects the + * range of signals to be measured, as discussed below. + *

+ * The choice of single vs. double-precision is important to understand: IOIO + * internally uses either 16-bit counters or 32-bit counters for the timing. 16- + * counters force us to either limit the maximum duration (and the minimum + * frequency) or compromise accuracy as compared to 32-bit counters. However, if + * you need many concurrent pulse measurements in your application, you may have + * no choice but to use single-precision. + *

+ * The clock rate selection is important (and even critical when working in + * single-precision) and requires the user to make some assumptions about the + * nature of the measured signal. The higher the clock rate, the more precise + * the measurement, but the longest pulse that can be measured decreases (or + * lowest frequency that can be measured increases). Using the scaling option + * when operating in frequency mode also affects these sizes. combinations. It + * is always recommended to choose the most precise mode, which exceeds the + * maximum expected pulse width (or inverse frequency). If a pulse is received + * whom duration exceeds the longest allowed pulse, it will be "folded" into the + * valid range and product garbage readings. + *

+ * The following table (sorted by longest pulse) summarizes all possible clock / + * mode combinations. The table applies for single-precision operation. + * For double-precision, simply multiply the longest pulse by 65536 and divide + * the lowest frequency by the same amount. Interestingly, the number written in + * [ms] units in the longest pulse column, roughly corresponds to the same + * number in minutes when working with double precsion, since 1[min] = + * 60000[ms]. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ClockScalingResolutionLongest pulseLowest frequency
62.5KHz116us1.048s0.95Hz
250KHz14us262.1ms3.81Hz
62.5KHz44us262.1ms3.81Hz
250KHz41us65.54ms15.26Hz
62.5KHz161us65.54ms15.26Hz
2MHz1500ns32.77ms30.52Hz
250KHz16250us16.38ms61.0Hz
2MHz4125ns8.192ms122.1Hz
16MHz162.5ns4.096ms244.1Hz
2MHz1631.25ns2.048ms488.3Hz
16MHz415.6ns1.024ms976.6Hz
16MHz163.9ns256us3.906KHz
+ * + *

+ * In some applications it is desirable to measure every incoming pulse rather + * than repetitively query the result of the last measurement. For that purpose + * the {@link #waitPulseGetDuration()} method exists: every incoming pulse width + * is pushed into a small internal queue from which it can be read. The client + * waits for data to be available, then reads it and data that comes in in the + * meanwhile is stored. The queue has limited size, so it is important to read + * quickly if no pulses are to be lost. Note that once a pulse is detected, the + * next one must have its leading edge at least 5ms after the leading edge of + * the current one, or else it will be skipped. This throttling has been + * introduced on purpose, in order to prevent saturation the communication + * channel when the input signal is very high frequency. Effectively, this means + * that the maximum sample rate is 200Hz. This rate has been chosen as it + * enables measure R/C servo signals without missing pulses. + * + *

+ * Typical usage (servo signal pulse width measurement): + * + *

+ * {@code
+ * // Open pulse input at 16MHz, double-precision
+ * PulseInput in = ioio.openPulseInput(3, PulseMode.POSITIVE);
+ * ...
+ * float widthSec = in.getDuration();
+ * OR:
+ * float widthSec = in.waitPulseGetDuration();
+ * ...
+ * in.close();  // pin 3 can now be used for something else.
+ * }
+ * 
+ * + *

+ * Typical usage (frequency measurement): + * + *

+ * {@code
+ * // Signal is known to be slightly over 150Hz. Single precision can be used.
+ * PulseInput in = ioio.openPulseInput(3,
+ *                                     ClockRate.RATE_2MHz,
+ *                                     PulseMode.FREQ_SCALE_4,
+ *                                     false);
+ * ...
+ * float freqHz = in.getFrequency();
+ * ...
+ * in.close();  // pin 3 can now be used for something else.
+ * }
+ * 
+ */ +public interface PulseInput extends Closeable +{ + /** An enumeration for describing the module's operating mode. */ + public enum PulseMode { + /** Positive pulse measurement (rising-edge-to-falling-edge). */ + POSITIVE(1), + /** Negative pulse measurement (falling-edge-to-rising-edge). */ + NEGATIVE(1), + /** Frequency measurement (rising-edge-to-rising-edge). */ + FREQ(1), + /** Frequency measurement (rising-edge-to-rising-edge) with 4x scaling. */ + FREQ_SCALE_4(4), + /** Frequency measurement (rising-edge-to-rising-edge) with 16x scaling. */ + FREQ_SCALE_16(16); + + /** The scaling factor as an integer. */ + public final int scaling; + + private PulseMode(int s) { + scaling = s; + } + } + + /** Suported clock rate enum. */ + public enum ClockRate { + /** 16MHz */ + RATE_16MHz(16000000), + /** 2MHz */ + RATE_2MHz(2000000), + /** 250KHz */ + RATE_250KHz(250000), + /** 62.5KHz */ + RATE_62KHz(62500); + + /** The value in Hertz units. */ + public final int hertz; + + private ClockRate(int h) { + hertz = h; + } + } + + /** + * Gets the pulse duration in case of pulse measurement mode, or the period + * in case of frequency mode. When scaling is used, this is compensated for + * here, so the duration of a single cycle will be returned. + *

+ * The first call to this method may block shortly until the first data + * update arrives. The client may interrupt the calling thread. + * + * @return The duration, in seconds. + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO has been lost. + */ + public float getDuration() throws InterruptedException, + ConnectionLostException; + + /** + * Reads a single measurement from the queue. If the queue is empty, will + * block until more data arrives. The calling thread may be interrupted in + * order to abort the call. See interface documentation for further + * explanation regarding the read queue. + *

+ * This method may not be used if the interface has was opened in frequency + * mode. + * + * @return The duration, in seconds. + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO has been lost. + */ + public float waitPulseGetDuration() throws InterruptedException, + ConnectionLostException; + + /** + * Gets the momentary frequency of the measured signal. When scaling is + * used, this is compensated for here, so the true frequency of the signal + * will be returned. + *

+ * The first call to this method may block shortly until the first data + * update arrives. The client may interrupt the calling thread. + * + * @return The frequency, in Hz. + * @throws InterruptedException + * The calling thread has been interrupted. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection with the IOIO has been lost. + */ + public float getFrequency() throws InterruptedException, + ConnectionLostException; +} diff --git a/src/main/java/ioio/lib/api/PwmOutput.java b/src/main/java/ioio/lib/api/PwmOutput.java new file mode 100644 index 00000000..5534e1a0 --- /dev/null +++ b/src/main/java/ioio/lib/api/PwmOutput.java @@ -0,0 +1,126 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +/** + * A pin used for PWM (Pulse-Width Modulation) output. + *

+ * A PWM pin produces a logic-level PWM signal. These signals are typically used + * for simulating analog outputs for controlling the intensity of LEDs, the + * rotation speed of motors, etc. They are also frequently used for controlling + * hobby servo motors. PwmOutput instances are obtained by calling + * {@link ioio.lib.api.IOIO#openPwmOutput(ioio.lib.api.DigitalOutput.Spec, int)}. + *

+ * When used for motors and LEDs, a frequency of several KHz is typically used, + * where there is a trade-off between switching power-loses and smoothness of + * operation. The pulse width is typically set by specifying the duty cycle, + * with the {@link #setDutyCycle(float)} method. A duty cycle of 0 is "off", a + * duty cycle of 1 is "on", and every intermediate value produces an + * intermediate intensity. Please note that any devices consuming more than 20mA + * of current (e.g. motors) should not by directly connected the the IOIO pins, + * but rather through an amplification circuit suited for the specific load. + *

+ * When used for hobby servos, the PWM signal is rather used for encoding of the + * desired angle the motor should go to. By standard, a 100Hz signal is used and + * the pulse width is varied between 1ms and 2ms (corresponding to both extremes + * of the shaft angle), using {@link #setPulseWidth(int)}. + *

+ * The instance is alive since its creation. If the connection with the IOIO + * drops at any point, the instance transitions to a disconnected state, in + * which every attempt to use the pin (except {@link #close()}) will throw a + * {@link ioio.lib.api.exception.ConnectionLostException}. Whenever {@link #close()} is invoked the + * instance may no longer be used. Any resources associated with it are freed + * and can be reused. + *

+ * Typical usage (fading LED): + * + *

+ * PwmOutput servo = ioio.openPwmOutput(12, 1000);  // LED anode on pin 12
+ * ...
+ * servo.setDutyCycle(0.0f);  // LED off
+ * ...
+ * servo.setDutyCycle(0.5f);  // 50% intensity
+ * ...
+ * servo.setDutyCycle(1.0f);  // 100% intensity
+ * ...
+ * servo.close();  // pin 12 can now be used for something else.
+ * 
+ *

+ * Typical usage (servo): + * + *

+ * PwmOutput servo = ioio.openPwmOutput(12, 100);
+ * ...
+ * servo.setPulseWidth(1000);  // 1000us = 1ms = one extreme
+ * ...
+ * servo.setPulseWidth(1500);  // 1500us = 1.5ms = center
+ * ...
+ * servo.setPulseWidth(2000);  // 2000us = 2ms = other extreme
+ * ...
+ * servo.close();  // pin 12 can now be used for something else.
+ * 
+ * + * @see ioio.lib.api.IOIO#openPwmOutput(ioio.lib.api.DigitalOutput.Spec, int) + */ +public interface PwmOutput extends Closeable { + /** + * Sets the duty cycle of the PWM output. The duty cycle is defined to be + * the pulse width divided by the total cycle period. For absolute control + * of the pulse with, consider using {@link #setPulseWidth(int)}. + * + * @param dutyCycle + * The duty cycle, as a real value from 0.0 to 1.0. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection to the IOIO has been lost. + * @see #setPulseWidth(int) + */ + public void setDutyCycle(float dutyCycle) throws ConnectionLostException; + + /** + * Sets the pulse width of the PWM output. The pulse width is duration of + * the high-time within a single period of the signal. For relative control + * of the pulse with, consider using {@link #setDutyCycle(float)}. + * + * @param pulseWidthUs + * The pulse width, in microsecond units. + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection to the IOIO has been lost. + * @see #setDutyCycle(float) + */ + public void setPulseWidth(int pulseWidthUs) throws ConnectionLostException; + + /** + * The same as {@link #setPulseWidth(int)}, but with sub-microsecond + * precision. + */ + public void setPulseWidth(float pulseWidthUs) + throws ConnectionLostException; +} diff --git a/src/main/java/ioio/lib/api/SpiMaster.java b/src/main/java/ioio/lib/api/SpiMaster.java new file mode 100644 index 00000000..3e8a7330 --- /dev/null +++ b/src/main/java/ioio/lib/api/SpiMaster.java @@ -0,0 +1,215 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +import java.io.Closeable; + +/** + * An interface for controlling an SPI module, in SPI bus-master mode, enabling + * communication with multiple SPI-enabled slave modules. + *

+ * SPI is a common hardware communication protocol, enabling full-duplex, + * synchronous point-to-multi-point data transfer. It requires MOSI, MISO and + * CLK lines shared by all nodes, as well as a SS line per slave, connected + * between this slave and a respective pin on the master. The MISO line should + * operate in pull-up mode, using either the internal pull-up or an external + * resistor. SpiMaster instances are obtained by calling + * {@link ioio.lib.api.IOIO#openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config)}. + *

+ * The SPI protocol is comprised of simultaneous sending and receiving of data + * between the bus master and a single slave. By the very nature of this + * protocol, the amount of bytes sent is equal to the amount of bytes received. + * However, by padding the sent data with garbage data, and by ignoring the + * leading bytes of the received data arbitrarily-sized packets can be sent and + * received. + *

+ * A very common practice for SPI-based slave devices (although not always the + * case), is to have a fixed request and response length and a fixed lag between + * them, based on the request type. For example, an SPI-based sensor may define + * the the protocol for obtaining its measured value is by sending a 2-byte + * message, whereas the returned 3-byte value has its first byte overlapping the + * second value of the response, as illustrated below: + * + *

+ * Master: M1   M2   GG   GG
+ * Slave:  GG   S1   S2   S3
+ * 
+ * + * M1, M2: the master's request
+ * S1, S2, S3: the slave's response
+ * GG: garbage bytes used for padding. + *

+ * The IOIO SPI interface supports such fixed length message protocols using a + * single method, {@link #writeRead(int, byte[], int, int, byte[], int)}, which + * gets the request data, and the lengths of the request, the response and the + * total transaction bytes. + * + *

+ * The instance is alive since its creation. If the connection with the IOIO + * drops at any point, the instance transitions to a disconnected state, in + * which every attempt to use it (except {@link #close()}) will throw a + * {@link ioio.lib.api.exception.ConnectionLostException}. Whenever {@link #close()} is invoked the + * instance may no longer be used. Any resources associated with it are freed + * and can be reused. + *

+ * Typical usage (single slave, as per the example above): + * + *

+ * {@code
+ * // MISO, MOSI, CLK, SS on pins 3, 4, 5, 6, respectively.
+ * SpiMaster spi = ioio.openSpiMaster(3, 4, 5, 6, SpiMaster.Rate.RATE_125K);
+ * final byte[] request = new byte[]{ 0x23, 0x45 };
+ * final byte[] response = new byte[3];
+ * spi.writeRead(request, 2, 4, response, 3);
+ * ...
+ * spi.close();  // free SPI module and pins
+ * }
+ * 
+ * + * @see ioio.lib.api.IOIO#openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, + * ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config) + */ +public interface SpiMaster extends Closeable +{ + /** Possible data rates for SPI, in Hz. */ + enum Rate { + RATE_31K, RATE_35K, RATE_41K, RATE_50K, RATE_62K, RATE_83K, RATE_125K, RATE_142K, RATE_166K, RATE_200K, RATE_250K, RATE_333K, RATE_500K, RATE_571K, RATE_666K, RATE_800K, RATE_1M, RATE_1_3M, RATE_2M, RATE_2_2M, RATE_2_6M, RATE_3_2M, RATE_4M, RATE_5_3M, RATE_8M + } + + /** An object that can be waited on for asynchronous calls. */ + public interface Result { + /** + * Wait until the asynchronous call which returned this instance is + * complete. + * + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection with the IOIO has been lost. + * @throws InterruptedException + * This operation has been interrupted. + */ + public void waitReady() throws ConnectionLostException, + InterruptedException; + } + + /** SPI configuration structure. */ + static class Config { + /** Data rate. */ + public Rate rate; + /** Whether to invert clock polarity. */ + public boolean invertClk; + /** + * Whether to do the input and output sampling on the trailing clock + * edge. + */ + public boolean sampleOnTrailing; + + /** + * Constructor. + * + * @param rate + * Data rate. + * @param invertClk + * Whether to invert clock polarity. + * @param sampleOnTrailing + * Whether to do the input and output sampling on the + * trailing clock edge. + */ + public Config(Rate rate, boolean invertClk, boolean sampleOnTrailing) { + this.rate = rate; + this.invertClk = invertClk; + this.sampleOnTrailing = sampleOnTrailing; + } + + /** + * Constructor with common defaults. Equivalent to Config(rate, false, + * false) + * + * @see ioio.lib.api.SpiMaster.Config#Config(ioio.lib.api.SpiMaster.Rate, boolean, boolean) + */ + public Config(Rate rate) { + this(rate, false, false); + } + } + + /** + * Perform a single SPI transaction which includes optional transmission and + * optional reception of data to a single slave. This is a blocking + * operation that can take a few milliseconds to a few tens of milliseconds. + * To abort this operation, client can interrupt the blocked thread. If + * readSize is 0, the call returns immediately. + * + * @param slave + * The slave index. It is determined by the index of its + * slave-select pin, as per the array passed to + * {@link ioio.lib.api.IOIO#openSpiMaster(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec, ioio.lib.api.DigitalOutput.Spec[], ioio.lib.api.SpiMaster.Config)} + * . + * @param writeData + * A byte array of data to write. May be null if writeSize is 0. + * @param writeSize + * Number of bytes to write. Valid values are 0 to totalSize. + * @param totalSize + * Total transaction length, in bytes. Valid values are 1 to 64. + * @param readData + * An array where the response is to be stored. May be null if + * readSize is 0. + * @param readSize + * The number of expected response bytes. Valid values are 0 to + * totalSize. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection to the IOIO has been lost. + * @throws InterruptedException + * Calling thread has been interrupted. + */ + public void writeRead(int slave, byte[] writeData, int writeSize, + int totalSize, byte[] readData, int readSize) + throws ConnectionLostException, InterruptedException; + + /** + * Shorthand for {@link #writeRead(int, byte[], int, int, byte[], int)} for + * the single-slave case. + * + * @see #writeRead(int, byte[], int, int, byte[], int) + */ + public void writeRead(byte[] writeData, int writeSize, int totalSize, + byte[] readData, int readSize) throws ConnectionLostException, + InterruptedException; + + /** + * The same as {@link #writeRead(int, byte[], int, int, byte[], int)}, but + * returns immediately and returns a {@link ioio.lib.api.SpiMaster.Result} object that can be + * waited on. If readSize is 0, the result object is ready immediately. + * + * @see #writeRead(int, byte[], int, int, byte[], int) + */ + public Result writeReadAsync(int slave, byte[] writeData, int writeSize, + int totalSize, byte[] readData, int readSize) + throws ConnectionLostException; +} diff --git a/src/main/java/ioio/lib/api/TwiMaster.java b/src/main/java/ioio/lib/api/TwiMaster.java new file mode 100644 index 00000000..fceb3017 --- /dev/null +++ b/src/main/java/ioio/lib/api/TwiMaster.java @@ -0,0 +1,148 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +/** + * An interface for controlling a TWI module, in TWI bus-master mode, enabling + * communication with multiple TWI-enabled slave modules. + *

+ * TWI (Two-Wire Interface) is a common hardware communication protocol, + * enabling half-duplex, synchronous point-to-multi-point data transfer. It + * requires a physical connection of two lines (SDA, SCL) shared by all the bus + * nodes, where the SDA is open-drain and externally pulled-up. TwiMaster + * instances are obtained by calling + * {@link ioio.lib.api.IOIO#openTwiMaster(int, ioio.lib.api.TwiMaster.Rate, boolean)}. + *

+ * TWI is the generic name for the specific I2C and SMBus protocols, differing + * mostly by the voltage levels they require. This module supports both. + *

+ * A TWI transaction is comprised of optional sending of a data buffer from the + * master to a single slave, followed by an optional reception of a data buffer + * from that slave. Slaves are designated by addresses, which may be 7-bit + * (common) or 10-bit (less common). TWI transactions may fail, as a result of + * the slave not responding or as result of the slave NACK'ing the request. Such + * a transaction is executed using the + * {@link #writeRead(int, boolean, byte[], int, byte[], int)} method. + *

+ * The instance is alive since its creation. If the connection with the IOIO + * drops at any point, the instance transitions to a disconnected state, in + *

+ * The instance is alive since its creation. If the connection with the IOIO + * drops at any point, the instance transitions to a disconnected state, in + * which every attempt to use it (except {@link #close()}) will throw a + * {@link ioio.lib.api.exception.ConnectionLostException}. Whenever {@link #close()} is invoked the + * instance may no longer be used. Any resources associated with it are freed + * and can be reused. + *

+ * Typical usage: + * + *

+ * {@code
+ * // Uses the SDA1/SCL1 pins, I2C volatege levels at 100KHz.
+ * TwiMaster twi = ioio.openTwiMaster(1, TwiMaster.RATE_100KHz, false);
+ * final byte[] request = new byte[]{ 0x23, 0x45 };
+ * final byte[] response = new byte[3];
+ * if (twi.writeRead(0x19, false, request, 2, response, 3)) {
+ *   // response is valid
+ *   ...
+ * } else {
+ *   // handle error
+ * }
+ * twi.close();  // free TWI module and pins
+ * }
+ * + * @see ioio.lib.api.IOIO#openTwiMaster(int, ioio.lib.api.TwiMaster.Rate, boolean) + */ +public interface TwiMaster extends Closeable { + enum Rate { + RATE_100KHz, RATE_400KHz, RATE_1MHz + } + + /** An object that can be waited on for asynchronous calls. */ + public interface Result { + /** + * Wait until the asynchronous call which returned this instance is + * complete. + * + * @return true if TWI transaction succeeded. + * + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection with the IOIO has been lost. + * @throws InterruptedException + * This operation has been interrupted. + */ + public boolean waitReady() throws ConnectionLostException, + InterruptedException; + } + + /** + * Perform a single TWI transaction which includes optional transmission and + * optional reception of data to a single slave. This is a blocking + * operation that can take a few milliseconds to a few tens of milliseconds. + * To abort this operation, client can interrupt the blocked thread. + * + * @param address + * The slave address, either 7-bit or 10-bit. Note that in some + * TWI device documentation the documented addresses are actually + * 2x the address values used here, as they regard the trailing + * 0-bit as part of the address. + * @param tenBitAddr + * Whether this is a 10-bit addressing mode. + * @param writeData + * The request data. + * @param writeSize + * The number of bytes to write. Valid value are 0-255. + * @param readData + * The array where the response should be stored. + * @param readSize + * The expected number of response bytes. Valid value are 0-255. + * @return Whether operation succeeded. + * @throws ioio.lib.api.exception.ConnectionLostException + * Connection to the IOIO has been lost. + * @throws InterruptedException + * Calling thread has been interrupted. + */ + public boolean writeRead(int address, boolean tenBitAddr, byte[] writeData, + int writeSize, byte[] readData, int readSize) + throws ConnectionLostException, InterruptedException; + + /** + * Asynchronous version of + * {@link #writeRead(int, boolean, byte[], int, byte[], int)}. Returns + * immediately and provides a {@link ioio.lib.api.TwiMaster.Result} object on which the client can + * wait for the result. + * + * @see #writeRead(int, boolean, byte[], int, byte[], int) + */ + public Result writeReadAsync(int address, boolean tenBitAddr, + byte[] writeData, int writeSize, byte[] readData, int readSize) + throws ConnectionLostException; +} diff --git a/src/main/java/ioio/lib/api/Uart.java b/src/main/java/ioio/lib/api/Uart.java new file mode 100644 index 00000000..52387738 --- /dev/null +++ b/src/main/java/ioio/lib/api/Uart.java @@ -0,0 +1,107 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api; + +import java.io.Closeable; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * An interface for controlling a UART module. + *

+ * UART is a very common hardware communication protocol, enabling full- duplex, + * asynchronous point-to-point data transfer. It typically serves for opening + * consoles or as a basis for higher-level protocols, such as MIDI, RS-232 and + * RS-485. Uart instances are obtained by calling + * {@link ioio.lib.api.IOIO#openUart(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, int, ioio.lib.api.Uart.Parity, ioio.lib.api.Uart.StopBits)}. + *

+ * The UART protocol is completely symmetric - there is no "master" and "slave" + * at this layer. Each end may send any number of bytes at arbitrary times, + * making it very useful for terminals and terminal-controllable devices. + *

+ * Working with UART is very intuitive - it just provides a standard InputStream + * and/or OutputStream. Optionally, one could create a read-only or write-only + * UART instances, by passing null (or INVALID_PIN) for either TX or RX pins. + *

+ * The instance is alive since its creation. If the connection with the IOIO + * drops at any point, the instance transitions to a disconnected state, which + * every attempt to use it (except {@link #close()}) will throw a + * {@link ioio.lib.api.exception.ConnectionLostException}. Whenever {@link #close()} is invoked the + * instance may no longer be used. Any resources associated with it are freed + * and can be reused. + *

+ * Typical usage: + * + *

+ * Uart uart = ioio.openUart(3, 4, 19200, Parity.NONE, StopBits.ONE);
+ * InputStream in = uart.getInputStream();
+ * OutputStream out = uart.getOutputStream();
+ * out.write(new String("Hello").getBytes());
+ * int i = in.read();  // blocking
+ * ...
+ * uart.close();  // free UART module and pins
+ * 
+ * + * @see ioio.lib.api.IOIO#openUart(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.DigitalOutput.Spec, int, ioio.lib.api.Uart.Parity, + * ioio.lib.api.Uart.StopBits) + */ +public interface Uart extends Closeable +{ + /** Parity-bit mode. */ + enum Parity { + /** No parity. */ + NONE, + /** Even parity. */ + EVEN, + /** Odd parity. */ + ODD + } + + /** Number of stop-bits. */ + enum StopBits { + /** One stop bit. */ + ONE, + /** Two stop bits. */ + TWO + } + + /** + * Gets the input stream. + * + * @return An input stream. + */ + public InputStream getInputStream(); + + /** + * Gets the output stream. + * + * @return An output stream. + */ + public OutputStream getOutputStream(); +} diff --git a/src/main/java/ioio/lib/api/exception/ConnectionLostException.java b/src/main/java/ioio/lib/api/exception/ConnectionLostException.java new file mode 100644 index 00000000..2461b402 --- /dev/null +++ b/src/main/java/ioio/lib/api/exception/ConnectionLostException.java @@ -0,0 +1,45 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api.exception; + +/** + * Thrown when the connection between Android and IOIO has been lost or + * disconnected. + */ +public class ConnectionLostException extends Exception { + private static final long serialVersionUID = 7422862446246046772L; + + public ConnectionLostException(Exception e) { + super(e); + } + + public ConnectionLostException() { + super("Connection lost"); + } +} \ No newline at end of file diff --git a/src/main/java/ioio/lib/api/exception/IncompatibilityException.java b/src/main/java/ioio/lib/api/exception/IncompatibilityException.java new file mode 100644 index 00000000..355c273d --- /dev/null +++ b/src/main/java/ioio/lib/api/exception/IncompatibilityException.java @@ -0,0 +1,41 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.api.exception; + +/** + * Thrown when an incompatibility of system components is detected, such as when + * an unsupported board hardware or software version is encountered. + */ +public class IncompatibilityException extends Exception { + private static final long serialVersionUID = -613939818063932627L; + + public IncompatibilityException(String msg) { + super(msg); + } +} diff --git a/src/main/java/ioio/lib/api/exception/OutOfResourceException.java b/src/main/java/ioio/lib/api/exception/OutOfResourceException.java new file mode 100644 index 00000000..6a5cd9c1 --- /dev/null +++ b/src/main/java/ioio/lib/api/exception/OutOfResourceException.java @@ -0,0 +1,14 @@ +package ioio.lib.api.exception; + +/** + * The IOIO board does not have anymore of the requested resource. This + * exceptions do not need to be handled if the client guarantees that the limits + * on concurrent resource usage are never exceeded. + */ +public class OutOfResourceException extends RuntimeException { + private static final long serialVersionUID = -4482605241361881899L; + + public OutOfResourceException(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/src/main/java/ioio/lib/impl/AbstractPin.java b/src/main/java/ioio/lib/impl/AbstractPin.java new file mode 100644 index 00000000..723a1b9e --- /dev/null +++ b/src/main/java/ioio/lib/impl/AbstractPin.java @@ -0,0 +1,47 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import ioio.lib.api.exception.ConnectionLostException; + +abstract class AbstractPin extends AbstractResource { + protected final int pinNum_; + + AbstractPin(IOIOImpl ioio, int pinNum) throws ConnectionLostException + { + super(ioio); + pinNum_ = pinNum; + } + + @Override + synchronized public void close() { + super.close(); + ioio_.closePin(pinNum_); + } +} diff --git a/src/main/java/ioio/lib/impl/AbstractResource.java b/src/main/java/ioio/lib/impl/AbstractResource.java new file mode 100644 index 00000000..864011d5 --- /dev/null +++ b/src/main/java/ioio/lib/impl/AbstractResource.java @@ -0,0 +1,78 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import ioio.lib.api.Closeable; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.impl.IncomingState.DisconnectListener; + +class AbstractResource implements Closeable, DisconnectListener +{ + enum State { + OPEN, + CLOSED, + DISCONNECTED + } + + protected State state_ = State.OPEN; + protected final IOIOImpl ioio_; + + public AbstractResource(IOIOImpl ioio) throws ConnectionLostException + { + ioio_ = ioio; + } + + @Override + synchronized public void disconnected() { + if (state_ != State.CLOSED) { + state_ = State.DISCONNECTED; + } + } + + @Override + synchronized public void close() { + if (state_ == State.CLOSED) { + throw new IllegalStateException("Trying to use a closed resouce"); + } else if (state_ == State.DISCONNECTED) { + state_ = State.CLOSED; + return; + } + state_ = State.CLOSED; + ioio_.removeDisconnectListener(this); + } + + protected synchronized void checkState() throws ConnectionLostException + { + if (state_ == State.CLOSED) { + throw new IllegalStateException("Trying to use a closed resouce"); + } else if (state_ == State.DISCONNECTED) { + throw new ConnectionLostException(); + } + } +} diff --git a/src/main/java/ioio/lib/impl/AnalogInputImpl.java b/src/main/java/ioio/lib/impl/AnalogInputImpl.java new file mode 100644 index 00000000..a086efdb --- /dev/null +++ b/src/main/java/ioio/lib/impl/AnalogInputImpl.java @@ -0,0 +1,182 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import ioio.lib.api.AnalogInput; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.impl.IncomingState.InputPinListener; + +import java.io.IOException; + +class AnalogInputImpl extends AbstractPin implements AnalogInput, + InputPinListener +{ + private int value_; + private boolean valid_ = false; + + short[] buffer_; + int bufferSize_; + int bufferCapacity_; + int bufferReadCursor_; + int bufferWriteCursor_; + int bufferOverflowCount_ = 0; + + AnalogInputImpl(IOIOImpl ioio, int pin) throws ConnectionLostException + { + super(ioio, pin); + } + + @Override + public float getVoltage() throws InterruptedException, + ConnectionLostException { + return read() * getReference(); + } + + @Override + public float getReference() { + return 3.3f; + } + + @Override + synchronized public void setValue(int value) { + // Log.v("AnalogInputImpl", "Pin " + pinNum_ + " value is " + value); + assert (value >= 0 && value < 1024); + value_ = value; + if (!valid_) { + valid_ = true; + notifyAll(); + } + bufferPush((short) value); + } + + @Override + synchronized public float read() throws InterruptedException, + ConnectionLostException { + checkState(); + while (!valid_ && state_ == State.OPEN) { + wait(); + } + checkState(); + return (float) value_ / 1023.0f; + } + + @Override + public synchronized void disconnected() { + super.disconnected(); + notifyAll(); + } + + @Override + public synchronized void close() { + super.close(); + try { + ioio_.protocol_.setAnalogInSampling(pinNum_, false); + } catch (IOException e) { + } + } + + @Override + public synchronized void setBuffer(int capacity) + throws ConnectionLostException { + checkState(); + if (capacity <= 0) { + buffer_ = null; + } else { + buffer_ = new short[capacity]; + } + bufferCapacity_ = capacity; + bufferSize_ = 0; + bufferReadCursor_ = 0; + bufferWriteCursor_ = 0; + bufferOverflowCount_ = 0; + } + + @Override + public float readBuffered() throws InterruptedException, + ConnectionLostException { + checkState(); + return (float) bufferPull() / 1023.0f; + } + + @Override + public float getVoltageBuffered() throws InterruptedException, + ConnectionLostException { + return readBuffered() * getReference(); + } + + private void bufferPush(short value) { + if (buffer_ == null) { + return; + } + if (bufferSize_ == bufferCapacity_) { + ++bufferOverflowCount_; + } else { + ++bufferSize_; + } + buffer_[bufferWriteCursor_++] = value; + if (bufferWriteCursor_ == bufferCapacity_) { + bufferWriteCursor_ = 0; + } + notifyAll(); + } + + private synchronized short bufferPull() throws InterruptedException, + ConnectionLostException + { + if (buffer_ == null) { + throw new IllegalStateException( + "Need to call setBuffer() before reading buffered values."); + } + while (bufferSize_ == 0 && state_ == State.OPEN) { + wait(); + } + checkState(); + short result = buffer_[bufferReadCursor_++]; + if (bufferReadCursor_ == bufferCapacity_) { + bufferReadCursor_ = 0; + } + --bufferSize_; + return result; + } + + @Override + public int getOverflowCount() throws ConnectionLostException { + return bufferOverflowCount_; + } + + @Override + public float getSampleRate() throws ConnectionLostException { + return 1000.0f; + } + + @Override + public int available() throws ConnectionLostException { + return bufferSize_; + } +} diff --git a/src/main/java/ioio/lib/impl/Constants.java b/src/main/java/ioio/lib/impl/Constants.java new file mode 100755 index 00000000..43ed75fb --- /dev/null +++ b/src/main/java/ioio/lib/impl/Constants.java @@ -0,0 +1,46 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +class Constants +{ + static final int NUM_PINS = 49; + static final int NUM_ANALOG_PINS = 16; + static final int NUM_PWM_MODULES = 9; + static final int NUM_UART_MODULES = 4; + static final int NUM_SPI_MODULES = 3; + static final int NUM_TWI_MODULES = 3; + static final int[] INCAP_MODULES_DOUBLE = new int[] { 0, 2, 4}; + static final int[] INCAP_MODULES_SINGLE = new int[] { 6, 7, 8}; + static final int BUFFER_SIZE = 1024; + static final int PACKET_BUFFER_SIZE = 256; + + static final int[][] TWI_PINS = new int[][] {{ 4, 5 }, { 47, 48 }, { 26, 25 }}; + static final int[] ICSP_PINS = new int[] { 36, 37, 38 }; +} diff --git a/src/main/java/ioio/lib/impl/DigitalInputImpl.java b/src/main/java/ioio/lib/impl/DigitalInputImpl.java new file mode 100644 index 00000000..48776124 --- /dev/null +++ b/src/main/java/ioio/lib/impl/DigitalInputImpl.java @@ -0,0 +1,94 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import ioio.lib.api.DigitalInput; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.impl.IncomingState.InputPinListener; + +import java.io.IOException; + +class DigitalInputImpl extends AbstractPin implements DigitalInput, + InputPinListener +{ + private boolean value_; + private boolean valid_ = false; + + DigitalInputImpl(IOIOImpl ioio, int pin) throws ConnectionLostException + { + super(ioio, pin); + } + + @Override + synchronized public void setValue(int value) { + // Log.v("DigitalInputImpl", "Pin " + pinNum_ + " value is " + value); + assert (value == 0 || value == 1); + value_ = (value == 1); + if (!valid_) { + valid_ = true; + } + notifyAll(); + } + + @Override + synchronized public void waitForValue(boolean value) + throws InterruptedException, ConnectionLostException { + checkState(); + while ((!valid_ || value_ != value) && state_ != State.DISCONNECTED) { + wait(); + } + checkState(); + } + + @Override + synchronized public void close() { + super.close(); + try { + ioio_.protocol_.setChangeNotify(pinNum_, false); + } catch (IOException e) { + } + } + + @Override + synchronized public boolean read() throws InterruptedException, + ConnectionLostException { + checkState(); + while (!valid_ && state_ != State.DISCONNECTED) { + wait(); + } + checkState(); + return value_; + } + + @Override + public synchronized void disconnected() { + super.disconnected(); + notifyAll(); + } +} diff --git a/src/main/java/ioio/lib/impl/DigitalOutputImpl.java b/src/main/java/ioio/lib/impl/DigitalOutputImpl.java new file mode 100644 index 00000000..27d6b398 --- /dev/null +++ b/src/main/java/ioio/lib/impl/DigitalOutputImpl.java @@ -0,0 +1,58 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import ioio.lib.api.DigitalOutput; +import ioio.lib.api.exception.ConnectionLostException; + +import java.io.IOException; + +class DigitalOutputImpl extends AbstractPin implements DigitalOutput +{ + boolean value_; + + DigitalOutputImpl(IOIOImpl ioio, int pin, boolean startValue) throws ConnectionLostException + { + super(ioio, pin); + value_ = startValue; + } + + @Override + synchronized public void write(boolean val) throws ConnectionLostException { + checkState(); + if (val != value_) { + try { + ioio_.protocol_.setDigitalOutLevel(pinNum_, val); + value_ = val; + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } + } +} diff --git a/src/main/java/ioio/lib/impl/FlowControlledOutputStream.java b/src/main/java/ioio/lib/impl/FlowControlledOutputStream.java new file mode 100644 index 00000000..6c16e452 --- /dev/null +++ b/src/main/java/ioio/lib/impl/FlowControlledOutputStream.java @@ -0,0 +1,127 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +class FlowControlledOutputStream extends OutputStream { + interface Sender { + void send(byte[] data, int size); + } + + private final Sender sender_; + private final BlockingQueue queue_ = new ArrayBlockingQueue( + Constants.BUFFER_SIZE); + private final FlushThread thread_ = new FlushThread(); + private final int maxPacket_; + private final byte[] packet_; + + private int readyToSend_ = 0; + private boolean closed_ = false; + + public FlowControlledOutputStream(Sender sender, int maxPacket) { + sender_ = sender; + maxPacket_ = maxPacket; + packet_ = new byte[maxPacket]; + thread_.start(); + } + + @Override + synchronized public void flush() throws IOException { + try { + while (!closed_ && !queue_.isEmpty()) { + wait(); + } + } catch (InterruptedException e) { + throw new IOException("Interrupted"); + } + if (closed_) { + throw new IOException("Stream has been closed"); + } + } + + @Override + synchronized public void write(int oneByte) throws IOException { + try { + while (!closed_ && !queue_.offer((byte) oneByte)) { + wait(); + } + } catch (InterruptedException e) { + throw new IOException("Interrupted"); + } + if (closed_) { + throw new IOException("Stream has been closed"); + } + notifyAll(); + } + + synchronized public void readyToSend(int numBytes) { + readyToSend_ += numBytes; + notifyAll(); + } + + @Override + synchronized public void close() { + if (closed_) { + return; + } + closed_ = true; + notifyAll(); + thread_.interrupt(); + } + + class FlushThread extends Thread { + @Override + public void run() { + super.run(); + try { + while (true) { + int toSend; + synchronized (FlowControlledOutputStream.this) { + while (readyToSend_ == 0 || queue_.isEmpty()) { + FlowControlledOutputStream.this.wait(); + } + toSend = Math.min(maxPacket_, + Math.min(readyToSend_, queue_.size())); + for (int i = 0; i < toSend; ++i) { + packet_[i] = queue_.remove(); + } + readyToSend_ -= toSend; + FlowControlledOutputStream.this.notifyAll(); + } + sender_.send(packet_, toSend); + } + } catch (InterruptedException e) { + } + } + } +} diff --git a/src/main/java/ioio/lib/impl/FlowControlledPacketSender.java b/src/main/java/ioio/lib/impl/FlowControlledPacketSender.java new file mode 100644 index 00000000..82cfb0bf --- /dev/null +++ b/src/main/java/ioio/lib/impl/FlowControlledPacketSender.java @@ -0,0 +1,119 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import java.io.IOException; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +class FlowControlledPacketSender +{ + interface Packet { + int getSize(); + } + + interface Sender { + void send(Packet packet); + } + + private final Sender sender_; + private final BlockingQueue queue_ = new ArrayBlockingQueue( + Constants.PACKET_BUFFER_SIZE); + private final FlushThread thread_ = new FlushThread(); + + private int readyToSend_ = 0; + private boolean closed_ = false; + + public FlowControlledPacketSender(Sender sender) { + sender_ = sender; + thread_.start(); + } + + synchronized public void flush() throws IOException { + try { + while (!closed_ && !queue_.isEmpty()) { + wait(); + } + } catch (InterruptedException e) { + throw new IOException("Interrupted"); + } + if (closed_) { + throw new IllegalStateException("Stream has been closed"); + } + } + + synchronized public void write(Packet packet) throws IOException { + try { + while (!closed_ && !queue_.offer(packet)) { + wait(); + } + } catch (InterruptedException e) { + throw new IOException("Interrupted"); + } + if (closed_) { + throw new IllegalStateException("Stream has been closed"); + } + notifyAll(); + } + + synchronized public void readyToSend(int numBytes) { + readyToSend_ += numBytes; + notifyAll(); + } + + synchronized public void close() { + closed_ = true; + thread_.interrupt(); + } + + synchronized public void kill() { + thread_.interrupt(); + } + + class FlushThread extends Thread { + @Override + public void run() { + super.run(); + try { + while (true) { + synchronized (FlowControlledPacketSender.this) { + while (queue_.isEmpty() + || readyToSend_ < queue_.peek().getSize()) { + FlowControlledPacketSender.this.wait(); + } + FlowControlledPacketSender.this.notifyAll(); + readyToSend_ -= queue_.peek().getSize(); + } + sender_.send(queue_.remove()); + } + } catch (InterruptedException e) { + } + } + } +} diff --git a/src/main/java/ioio/lib/impl/IOIOImpl.java b/src/main/java/ioio/lib/impl/IOIOImpl.java new file mode 100755 index 00000000..8d019cbf --- /dev/null +++ b/src/main/java/ioio/lib/impl/IOIOImpl.java @@ -0,0 +1,720 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import android.util.Log; +import ioio.lib.api.AnalogInput; +import ioio.lib.api.DigitalInput; +import ioio.lib.api.DigitalInput.Spec; +import ioio.lib.api.DigitalInput.Spec.Mode; +import ioio.lib.api.DigitalOutput; +import ioio.lib.api.IOIO; +import ioio.lib.api.IOIOConnection; +import ioio.lib.api.IcspMaster; +import ioio.lib.api.PulseInput; +import ioio.lib.api.PulseInput.ClockRate; +import ioio.lib.api.PulseInput.PulseMode; +import ioio.lib.api.PwmOutput; +import ioio.lib.api.SpiMaster; +import ioio.lib.api.TwiMaster; +import ioio.lib.api.TwiMaster.Rate; +import ioio.lib.api.Uart; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.api.exception.IncompatibilityException; +import ioio.lib.impl.IOIOProtocol.PwmScale; +import ioio.lib.impl.IncomingState.DisconnectListener; + +import java.io.IOException; + +public class IOIOImpl implements IOIO, DisconnectListener { + private static final String TAG = "IOIOImpl"; + private boolean disconnect_ = false; + + private static final byte[] REQUIRED_INTERFACE_ID = new byte[] { 'I', 'O', + 'I', 'O', '0', '0', '0', '3' }; + + private final IOIOConnection connection_; + private final IncomingState incomingState_ = new IncomingState(); + private final boolean openPins_[] = new boolean[Constants.NUM_PINS]; + private final boolean openTwi_[] = new boolean[Constants.NUM_TWI_MODULES]; + private boolean openIcsp_ = false; + private final ModuleAllocator pwmAllocator_ = new ModuleAllocator( + Constants.NUM_PWM_MODULES, "PWM"); + private final ModuleAllocator uartAllocator_ = new ModuleAllocator( + Constants.NUM_UART_MODULES, "UART"); + private final ModuleAllocator spiAllocator_ = new ModuleAllocator( + Constants.NUM_SPI_MODULES, "SPI"); + private final ModuleAllocator incapAllocatorDouble_ = new ModuleAllocator( + Constants.INCAP_MODULES_DOUBLE, "INCAP_DOUBLE"); + private final ModuleAllocator incapAllocatorSingle_ = new ModuleAllocator( + Constants.INCAP_MODULES_SINGLE, "INCAP_SINGLE"); + IOIOProtocol protocol_; + private State state_ = State.INIT; + + public IOIOImpl(IOIOConnection con) { + connection_ = con; + } + + @Override + public void waitForConnect() throws ConnectionLostException, + IncompatibilityException { + if (state_ == State.CONNECTED) { + return; + } + if (state_ == State.DEAD) { + throw new ConnectionLostException(); + } + addDisconnectListener(this); + Log.d(TAG, "Waiting for IOIO connection"); + try { + try { + Log.v(TAG, "Waiting for underlying connection"); + connection_.waitForConnect(); + synchronized (this) { + if (disconnect_) { + throw new ConnectionLostException(); + } + protocol_ = new IOIOProtocol(connection_.getInputStream(), + connection_.getOutputStream(), incomingState_); + // Once this block exits, a disconnect will also involve + // softClose(). + } + } catch (ConnectionLostException e) { + incomingState_.handleConnectionLost(); + throw e; + } + Log.v(TAG, "Waiting for handshake"); + incomingState_.waitConnectionEstablished(); + Log.v(TAG, "Querying for required interface ID"); + checkInterfaceVersion(); + Log.v(TAG, "Required interface ID is supported"); + state_ = State.CONNECTED; + Log.i(TAG, "IOIO connection established"); + } catch (ConnectionLostException e) { + Log.d(TAG, "Connection lost / aborted"); + state_ = State.DEAD; + throw e; + } catch (InterruptedException e) { + Log.e(TAG, "Unexpected exception", e); + } + } + + @Override + public synchronized void disconnect() { + Log.d(TAG, "Client requested disconnect."); + if (disconnect_) { + return; + } + disconnect_ = true; + try { + if (protocol_ != null) { + protocol_.softClose(); + } + } catch (IOException e) { + Log.e(TAG, "Soft close failed", e); + } + connection_.disconnect(); + } + + @Override + public synchronized void disconnected() { + state_ = State.DEAD; + if (disconnect_) { + return; + } + Log.d(TAG, "Physical disconnect."); + disconnect_ = true; + // The IOIOConnection doesn't necessarily know about the disconnect + connection_.disconnect(); + } + + @Override + public void waitForDisconnect() throws InterruptedException { + incomingState_.waitDisconnect(); + } + + @Override + public State getState() { + return state_; + } + + private void checkInterfaceVersion() throws IncompatibilityException, + ConnectionLostException, InterruptedException { + try { + protocol_.checkInterface(REQUIRED_INTERFACE_ID); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + if (!incomingState_.waitForInterfaceSupport()) { + state_ = State.INCOMPATIBLE; + Log.e(TAG, "Required interface ID is not supported"); + throw new IncompatibilityException( + "IOIO firmware does not support required firmware: " + + new String(REQUIRED_INTERFACE_ID)); + } + } + + synchronized void removeDisconnectListener(DisconnectListener listener) { + incomingState_.removeDisconnectListener(listener); + } + + synchronized void addDisconnectListener(DisconnectListener listener) + throws ConnectionLostException + { + incomingState_.addDisconnectListener(listener); + } + + synchronized void closePin(int pin) { + try { + checkState(); + if (!openPins_[pin]) { + throw new IllegalStateException("Pin not open: " + pin); + } + protocol_.setPinDigitalIn(pin, DigitalInput.Spec.Mode.FLOATING); + openPins_[pin] = false; + } catch (IOException e) { + } catch (ConnectionLostException e) { + } + } + + synchronized void closePwm(int pwmNum) { + try { + checkState(); + pwmAllocator_.releaseModule(pwmNum); + protocol_.setPwmPeriod(pwmNum, 0, PwmScale.SCALE_1X); + } catch (IOException e) { + } catch (ConnectionLostException e) { + } + } + + synchronized void closeUart(int uartNum) { + try { + checkState(); + uartAllocator_.releaseModule(uartNum); + protocol_.uartClose(uartNum); + } catch (IOException e) { + } catch (ConnectionLostException e) { + } + } + + synchronized void closeTwi(int twiNum) { + try { + checkState(); + if (!openTwi_[twiNum]) { + throw new IllegalStateException("TWI not open: " + twiNum); + } + openTwi_[twiNum] = false; + openPins_[Constants.TWI_PINS[twiNum][0]] = false; + openPins_[Constants.TWI_PINS[twiNum][1]] = false; + protocol_.i2cClose(twiNum); + } catch (IOException e) { + } catch (ConnectionLostException e) { + } + } + + synchronized void closeIcsp() { + try { + checkState(); + if (!openIcsp_) { + throw new IllegalStateException("ICSP not open"); + } + openIcsp_ = false; + openPins_[Constants.ICSP_PINS[0]] = false; + openPins_[Constants.ICSP_PINS[1]] = false; + protocol_.icspClose(); + } catch (ConnectionLostException e) { + } catch (IOException e) { + } + } + + synchronized void closeSpi(int spiNum) { + try { + checkState(); + spiAllocator_.releaseModule(spiNum); + protocol_.spiClose(spiNum); + } catch (IOException e) { + } catch (ConnectionLostException e) { + } + } + + synchronized void closeIncap(int incapNum, boolean doublePrecision) { + try { + checkState(); + if (doublePrecision) { + incapAllocatorDouble_.releaseModule(incapNum); + } else { + incapAllocatorSingle_.releaseModule(incapNum); + } + protocol_.incapClose(incapNum); + } catch (IOException e) { + } catch (ConnectionLostException e) { + } + } + + @Override + synchronized public void softReset() throws ConnectionLostException { + checkState(); + try { + protocol_.softReset(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } + + @Override + synchronized public void hardReset() throws ConnectionLostException { + checkState(); + try { + protocol_.hardReset(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } + + @Override + public String getImplVersion(VersionType v) throws ConnectionLostException { + if (state_ == State.INIT) { + throw new IllegalStateException( + "Connection has not yet been established"); + } + switch (v) { + case HARDWARE_VER: + return incomingState_.hardwareId_; + case BOOTLOADER_VER: + return incomingState_.bootloaderId_; + case APP_FIRMWARE_VER: + return incomingState_.firmwareId_; + case IOIOLIB_VER: + return "IOIO0323"; + } + return null; + } + + @Override + public DigitalInput openDigitalInput(int pin) + throws ConnectionLostException + { + return openDigitalInput(new DigitalInput.Spec(pin)); + } + + @Override + public DigitalInput openDigitalInput(int pin, Mode mode) + throws ConnectionLostException + { + return openDigitalInput(new DigitalInput.Spec(pin, mode)); + } + + @Override + synchronized public DigitalInput openDigitalInput(DigitalInput.Spec spec) + throws ConnectionLostException + { + checkState(); + PinFunctionMap.checkValidPin(spec.pin); + checkPinFree(spec.pin); + DigitalInputImpl result = new DigitalInputImpl(this, spec.pin); + addDisconnectListener(result); + openPins_[spec.pin] = true; + incomingState_.addInputPinListener(spec.pin, result); + try { + protocol_.setPinDigitalIn(spec.pin, spec.mode); + protocol_.setChangeNotify(spec.pin, true); + } catch (IOException e) { + result.close(); + throw new ConnectionLostException(e); + } + return result; + } + + @Override + public DigitalOutput openDigitalOutput(int pin, + ioio.lib.api.DigitalOutput.Spec.Mode mode, boolean startValue) + throws ConnectionLostException + { + return openDigitalOutput(new DigitalOutput.Spec(pin, mode), startValue); + } + + @Override + synchronized public DigitalOutput openDigitalOutput( + DigitalOutput.Spec spec, boolean startValue) + throws ConnectionLostException + { + checkState(); + PinFunctionMap.checkValidPin(spec.pin); + checkPinFree(spec.pin); + DigitalOutputImpl result = new DigitalOutputImpl(this, spec.pin, startValue); + addDisconnectListener(result); + openPins_[spec.pin] = true; + try { + protocol_.setPinDigitalOut(spec.pin, startValue, spec.mode); + } catch (IOException e) { + result.close(); + throw new ConnectionLostException(e); + } + return result; + } + + @Override + public DigitalOutput openDigitalOutput(int pin, boolean startValue) + throws ConnectionLostException + { + return openDigitalOutput(new DigitalOutput.Spec(pin), startValue); + } + + @Override + public DigitalOutput openDigitalOutput(int pin) + throws ConnectionLostException + { + return openDigitalOutput(new DigitalOutput.Spec(pin), false); + } + + @Override + synchronized public AnalogInput openAnalogInput(int pin) + throws ConnectionLostException + { + checkState(); + PinFunctionMap.checkSupportsAnalogInput(pin); + checkPinFree(pin); + AnalogInputImpl result = new AnalogInputImpl(this, pin); + addDisconnectListener(result); + openPins_[pin] = true; + incomingState_.addInputPinListener(pin, result); + try { + protocol_.setPinAnalogIn(pin); + protocol_.setAnalogInSampling(pin, true); + } catch (IOException e) { + result.close(); + throw new ConnectionLostException(e); + } + return result; + } + + @Override + public PwmOutput openPwmOutput(int pin, int freqHz) + throws ConnectionLostException + { + return openPwmOutput(new DigitalOutput.Spec(pin), freqHz); + } + + @Override + synchronized public PwmOutput openPwmOutput(DigitalOutput.Spec spec, + int freqHz) throws ConnectionLostException + { + checkState(); + PinFunctionMap.checkSupportsPeripheralOutput(spec.pin); + checkPinFree(spec.pin); + int pwmNum = pwmAllocator_.allocateModule(); + + int scale = 0; + float baseUs; + int period; + while (true) { + final int clk = 16000000 / PwmScale.values()[scale].scale; + period = clk / freqHz; + if (period <= 65536) { + baseUs = 1000000.0f / clk; + break; + } + if (++scale >= PwmScale.values().length) { + throw new IllegalArgumentException("Frequency too low: " + + freqHz); + } + } + + PwmImpl pwm = new PwmImpl(this, spec.pin, pwmNum, period, baseUs); + addDisconnectListener(pwm); + openPins_[spec.pin] = true; + try { + protocol_.setPinDigitalOut(spec.pin, false, spec.mode); + protocol_.setPinPwm(spec.pin, pwmNum, true); + protocol_.setPwmPeriod(pwmNum, period - 1, + PwmScale.values()[scale]); + } catch (IOException e) { + pwm.close(); + throw new ConnectionLostException(e); + } + return pwm; + } + + @Override + public Uart openUart(int rx, int tx, int baud, Uart.Parity parity, + Uart.StopBits stopbits) throws ConnectionLostException + { + return openUart(rx == INVALID_PIN ? null : new DigitalInput.Spec(rx), + tx == INVALID_PIN ? null : new DigitalOutput.Spec(tx), baud, + parity, stopbits); + } + + @Override + synchronized public Uart openUart(DigitalInput.Spec rx, + DigitalOutput.Spec tx, int baud, Uart.Parity parity, + Uart.StopBits stopbits) throws ConnectionLostException + { + checkState(); + if (rx != null) { + PinFunctionMap.checkSupportsPeripheralInput(rx.pin); + checkPinFree(rx.pin); + } + if (tx != null) { + PinFunctionMap.checkSupportsPeripheralOutput(tx.pin); + checkPinFree(tx.pin); + } + int rxPin = rx != null ? rx.pin : INVALID_PIN; + int txPin = tx != null ? tx.pin : INVALID_PIN; + int uartNum = uartAllocator_.allocateModule(); + UartImpl uart = new UartImpl(this, txPin, rxPin, uartNum); + addDisconnectListener(uart); + incomingState_.addUartListener(uartNum, uart); + try { + if (rx != null) { + openPins_[rx.pin] = true; + protocol_.setPinDigitalIn(rx.pin, rx.mode); + protocol_.setPinUart(rx.pin, uartNum, false, true); + } + if (tx != null) { + openPins_[tx.pin] = true; + protocol_.setPinDigitalOut(tx.pin, true, tx.mode); + protocol_.setPinUart(tx.pin, uartNum, true, true); + } + boolean speed4x = true; + int rate = Math.round(4000000.0f / baud) - 1; + if (rate > 65535) { + speed4x = false; + rate = Math.round(1000000.0f / baud) - 1; + } + protocol_.uartConfigure(uartNum, rate, speed4x, stopbits, parity); + } catch (IOException e) { + uart.close(); + throw new ConnectionLostException(e); + } + return uart; + } + + @Override + synchronized public TwiMaster openTwiMaster(int twiNum, Rate rate, + boolean smbus) throws ConnectionLostException + { + checkState(); + checkTwiFree(twiNum); + checkPinFree(Constants.TWI_PINS[twiNum][0]); + checkPinFree(Constants.TWI_PINS[twiNum][1]); + openPins_[Constants.TWI_PINS[twiNum][0]] = true; + openPins_[Constants.TWI_PINS[twiNum][1]] = true; + openTwi_[twiNum] = true; + TwiMasterImpl twi = new TwiMasterImpl(this, twiNum); + addDisconnectListener(twi); + incomingState_.addTwiListener(twiNum, twi); + try { + protocol_.i2cConfigureMaster(twiNum, rate, smbus); + } catch (IOException e) { + twi.close(); + throw new ConnectionLostException(e); + } + return twi; + } + + @Override + synchronized public IcspMaster openIcspMaster() + throws ConnectionLostException + { + checkState(); + checkIcspFree(); + checkPinFree(Constants.ICSP_PINS[0]); + checkPinFree(Constants.ICSP_PINS[1]); + checkPinFree(Constants.ICSP_PINS[2]); + openPins_[Constants.ICSP_PINS[0]] = true; + openPins_[Constants.ICSP_PINS[1]] = true; + openPins_[Constants.ICSP_PINS[2]] = true; + openIcsp_ = true; + IcspMasterImpl icsp = new IcspMasterImpl(this); + addDisconnectListener(icsp); + incomingState_.addIcspListener(icsp); + try { + protocol_.icspOpen(); + } catch (IOException e) { + icsp.close(); + throw new ConnectionLostException(e); + } + return icsp; + } + + @Override + public SpiMaster openSpiMaster(int miso, int mosi, int clk, + int slaveSelect, SpiMaster.Rate rate) + throws ConnectionLostException + { + return openSpiMaster(miso, mosi, clk, new int[] { slaveSelect }, rate); + } + + @Override + public SpiMaster openSpiMaster(int miso, int mosi, int clk, + int[] slaveSelect, SpiMaster.Rate rate) + throws ConnectionLostException + { + DigitalOutput.Spec[] slaveSpecs = new DigitalOutput.Spec[slaveSelect.length]; + for (int i = 0; i < slaveSelect.length; ++i) { + slaveSpecs[i] = new DigitalOutput.Spec(slaveSelect[i]); + } + return openSpiMaster(new DigitalInput.Spec(miso, Mode.PULL_UP), + new DigitalOutput.Spec(mosi), new DigitalOutput.Spec(clk), + slaveSpecs, new SpiMaster.Config(rate)); + } + + @Override + synchronized public SpiMaster openSpiMaster(DigitalInput.Spec miso, + DigitalOutput.Spec mosi, DigitalOutput.Spec clk, + DigitalOutput.Spec[] slaveSelect, SpiMaster.Config config) + throws ConnectionLostException + { + checkState(); + int ssPins[] = new int[slaveSelect.length]; + checkPinFree(miso.pin); + PinFunctionMap.checkSupportsPeripheralInput(miso.pin); + checkPinFree(mosi.pin); + PinFunctionMap.checkSupportsPeripheralOutput(mosi.pin); + checkPinFree(clk.pin); + PinFunctionMap.checkSupportsPeripheralOutput(clk.pin); + for (int i = 0; i < slaveSelect.length; ++i) { + checkPinFree(slaveSelect[i].pin); + ssPins[i] = slaveSelect[i].pin; + } + + int spiNum = spiAllocator_.allocateModule(); + SpiMasterImpl spi = new SpiMasterImpl(this, spiNum, mosi.pin, miso.pin, + clk.pin, ssPins); + addDisconnectListener(spi); + + openPins_[miso.pin] = true; + openPins_[mosi.pin] = true; + openPins_[clk.pin] = true; + for (int i = 0; i < slaveSelect.length; ++i) { + openPins_[slaveSelect[i].pin] = true; + } + + incomingState_.addSpiListener(spiNum, spi); + try { + protocol_.setPinDigitalIn(miso.pin, miso.mode); + protocol_.setPinSpi(miso.pin, 1, true, spiNum); + protocol_.setPinDigitalOut(mosi.pin, true, mosi.mode); + protocol_.setPinSpi(mosi.pin, 0, true, spiNum); + protocol_.setPinDigitalOut(clk.pin, config.invertClk, clk.mode); + protocol_.setPinSpi(clk.pin, 2, true, spiNum); + for (DigitalOutput.Spec spec : slaveSelect) { + protocol_.setPinDigitalOut(spec.pin, true, spec.mode); + } + protocol_.spiConfigureMaster(spiNum, config); + } catch (IOException e) { + spi.close(); + throw new ConnectionLostException(e); + } + return spi; + } + + @Override + public PulseInput openPulseInput(Spec spec, ClockRate rate, PulseMode mode, + boolean doublePrecision) throws ConnectionLostException + { + checkState(); + checkPinFree(spec.pin); + PinFunctionMap.checkSupportsPeripheralInput(spec.pin); + int incapNum = doublePrecision ? incapAllocatorDouble_.allocateModule() + : incapAllocatorSingle_.allocateModule(); + IncapImpl incap = new IncapImpl(this, mode, incapNum, spec.pin, + rate.hertz, mode.scaling, doublePrecision); + addDisconnectListener(incap); + incomingState_.addIncapListener(incapNum, incap); + openPins_[spec.pin] = true; + try { + protocol_.setPinDigitalIn(spec.pin, spec.mode); + protocol_.setPinIncap(spec.pin, incapNum, true); + protocol_.incapConfigure(incapNum, doublePrecision, + mode.ordinal() + 1, rate.ordinal()); + } catch (IOException e) { + incap.close(); + throw new ConnectionLostException(e); + } + return incap; + } + + @Override + public PulseInput openPulseInput(int pin, PulseMode mode) + throws ConnectionLostException + { + return openPulseInput(new DigitalInput.Spec(pin), ClockRate.RATE_16MHz, + mode, true); + } + + private void checkPinFree(int pin) { + if (openPins_[pin]) { + throw new IllegalArgumentException("Pin already open: " + pin); + } + } + + private void checkTwiFree(int twi) { + if (openTwi_[twi]) { + throw new IllegalArgumentException("TWI already open: " + twi); + } + } + + private void checkIcspFree() { + if (openIcsp_) { + throw new IllegalArgumentException("ICSP already open"); + } + } + + private void checkState() throws ConnectionLostException + { + if (state_ == State.DEAD) { + throw new ConnectionLostException(); + } + if (state_ == State.INCOMPATIBLE) { + throw new IllegalStateException( + "Incompatibility has been reported - IOIO cannot be used"); + } + if (state_ != State.CONNECTED) { + throw new IllegalStateException( + "Connection has not yet been established"); + } + } + + @Override + public synchronized void beginBatch() throws ConnectionLostException { + checkState(); + protocol_.beginBatch(); + } + + @Override + public synchronized void endBatch() throws ConnectionLostException { + checkState(); + try { + protocol_.endBatch(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } +} diff --git a/src/main/java/ioio/lib/impl/IOIOProtocol.java b/src/main/java/ioio/lib/impl/IOIOProtocol.java new file mode 100755 index 00000000..868766d4 --- /dev/null +++ b/src/main/java/ioio/lib/impl/IOIOProtocol.java @@ -0,0 +1,857 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import android.util.Log; +import ioio.lib.api.DigitalInput; +import ioio.lib.api.DigitalOutput; +import ioio.lib.api.SpiMaster; +import ioio.lib.api.TwiMaster.Rate; +import ioio.lib.api.Uart; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +class IOIOProtocol +{ + static final int HARD_RESET = 0x00; + static final int ESTABLISH_CONNECTION = 0x00; + static final int SOFT_RESET = 0x01; + static final int CHECK_INTERFACE = 0x02; + static final int CHECK_INTERFACE_RESPONSE = 0x02; + static final int SET_PIN_DIGITAL_OUT = 0x03; + static final int SET_DIGITAL_OUT_LEVEL = 0x04; + static final int REPORT_DIGITAL_IN_STATUS = 0x04; + static final int SET_PIN_DIGITAL_IN = 0x05; + static final int REPORT_PERIODIC_DIGITAL_IN_STATUS = 0x05; + static final int SET_CHANGE_NOTIFY = 0x06; + static final int REGISTER_PERIODIC_DIGITAL_SAMPLING = 0x07; + static final int SET_PIN_PWM = 0x08; + static final int SET_PWM_DUTY_CYCLE = 0x09; + static final int SET_PWM_PERIOD = 0x0A; + static final int SET_PIN_ANALOG_IN = 0x0B; + static final int REPORT_ANALOG_IN_STATUS = 0x0B; + static final int SET_ANALOG_IN_SAMPLING = 0x0C; + static final int REPORT_ANALOG_IN_FORMAT = 0x0C; + static final int UART_CONFIG = 0x0D; + static final int UART_STATUS = 0x0D; + static final int UART_DATA = 0x0E; + static final int SET_PIN_UART = 0x0F; + static final int UART_REPORT_TX_STATUS = 0x0F; + static final int SPI_CONFIGURE_MASTER = 0x10; + static final int SPI_STATUS = 0x10; + static final int SPI_MASTER_REQUEST = 0x11; + static final int SPI_DATA = 0x11; + static final int SET_PIN_SPI = 0x12; + static final int SPI_REPORT_TX_STATUS = 0x12; + static final int I2C_CONFIGURE_MASTER = 0x13; + static final int I2C_STATUS = 0x13; + static final int I2C_WRITE_READ = 0x14; + static final int I2C_RESULT = 0x14; + static final int I2C_REPORT_TX_STATUS = 0x15; + static final int ICSP_SIX = 0x16; + static final int ICSP_REPORT_RX_STATUS = 0x16; + static final int ICSP_REGOUT = 0x17; + static final int ICSP_RESULT = 0x17; + static final int ICSP_PROG_ENTER = 0x18; + static final int ICSP_PROG_EXIT = 0x19; + static final int ICSP_CONFIG = 0x1A; + static final int INCAP_CONFIGURE = 0x1B; + static final int INCAP_STATUS = 0x1B; + static final int SET_PIN_INCAP = 0x1C; + static final int INCAP_REPORT = 0x1C; + static final int SOFT_CLOSE = 0x1D; + + static final int[] SCALE_DIV = new int[] { + 0x1F, // 31.25 + 0x1E, // 35.714 + 0x1D, // 41.667 + 0x1C, // 50 + 0x1B, // 62.5 + 0x1A, // 83.333 + 0x17, // 125 + 0x16, // 142.857 + 0x15, // 166.667 + 0x14, // 200 + 0x13, // 250 + 0x12, // 333.333 + 0x0F, // 500 + 0x0E, // 571.429 + 0x0D, // 666.667 + 0x0C, // 800 + 0x0B, // 1000 + 0x0A, // 1333.333 + 0x07, // 2000 + 0x06, // 2285.714 + 0x05, // 2666.667 + 0x04, // 3200 + 0x03, // 4000 + 0x02, // 5333.333 + 0x01 // 8000 + }; + + private static final String TAG = "IOIOProtocol"; + + enum PwmScale { + SCALE_1X(1, 0), SCALE_8X(8, 3), SCALE_64X(64, 2), SCALE_256X(256, 1); + + public final int scale; + private final int encoding; + + PwmScale(int scale, int encoding) { + this.scale = scale; + this.encoding = encoding; + } + } + + private byte[] outbuf_ = new byte[256]; + private int pos_ = 0; + private int batchCounter_ = 0; + + private void writeByte(int b) throws IOException { + assert (b >= 0 && b < 256); + if (pos_ == outbuf_.length) { + // buffer is full + flush(); + } + //Log.v(TAG, "sending: 0x" + Integer.toHexString(b)); + outbuf_[pos_++] = (byte) b; + } + + public synchronized void beginBatch() { + ++batchCounter_; + } + + public synchronized void endBatch() throws IOException { + if (--batchCounter_ == 0) { + flush(); + } + } + + private void flush() throws IOException { + try { + out_.write(outbuf_, 0, pos_); + } finally { + pos_ = 0; + } + } + + private void writeTwoBytes(int i) throws IOException { + writeByte(i & 0xFF); + writeByte(i >> 8); + } + + private void writeThreeBytes(int i) throws IOException { + writeByte(i & 0xFF); + writeByte((i >> 8) & 0xFF); + writeByte((i >> 16) & 0xFF); + } + + synchronized public void hardReset() throws IOException { + beginBatch(); + writeByte(HARD_RESET); + writeByte('I'); + writeByte('O'); + writeByte('I'); + writeByte('O'); + endBatch(); + } + + synchronized public void softReset() throws IOException { + beginBatch(); + writeByte(SOFT_RESET); + endBatch(); + } + + synchronized public void softClose() throws IOException { + beginBatch(); + writeByte(SOFT_CLOSE); + endBatch(); + } + + synchronized public void checkInterface(byte[] interfaceId) + throws IOException { + if (interfaceId.length != 8) { + throw new IllegalArgumentException( + "interface ID must be exactly 8 bytes long"); + } + beginBatch(); + writeByte(CHECK_INTERFACE); + for (int i = 0; i < 8; ++i) { + writeByte(interfaceId[i]); + } + endBatch(); + } + + synchronized public void setDigitalOutLevel(int pin, boolean level) + throws IOException { + beginBatch(); + writeByte(SET_DIGITAL_OUT_LEVEL); + writeByte(pin << 2 | (level ? 1 : 0)); + endBatch(); + } + + synchronized public void setPinPwm(int pin, int pwmNum, boolean enable) + throws IOException { + beginBatch(); + writeByte(SET_PIN_PWM); + writeByte(pin & 0x3F); + writeByte((enable ? 0x80 : 0x00) | (pwmNum & 0x0F)); + endBatch(); + } + + synchronized public void setPwmDutyCycle(int pwmNum, int dutyCycle, + int fraction) throws IOException { + beginBatch(); + writeByte(SET_PWM_DUTY_CYCLE); + writeByte(pwmNum << 2 | fraction); + writeTwoBytes(dutyCycle); + endBatch(); + } + + synchronized public void setPwmPeriod(int pwmNum, int period, PwmScale scale) + throws IOException { + beginBatch(); + writeByte(SET_PWM_PERIOD); + writeByte(((scale.encoding & 0x02) << 6) | (pwmNum << 1) + | (scale.encoding & 0x01)); + writeTwoBytes(period); + endBatch(); + } + + synchronized public void setPinIncap(int pin, int incapNum, boolean enable) + throws IOException { + beginBatch(); + writeByte(SET_PIN_INCAP); + writeByte(pin); + writeByte(incapNum | (enable ? 0x80 : 0x00)); + endBatch(); + } + + synchronized public void incapClose(int incapNum) throws IOException { + beginBatch(); + writeByte(INCAP_CONFIGURE); + writeByte(incapNum); + writeByte(0x00); + endBatch(); + } + + synchronized public void incapConfigure(int incapNum, boolean double_prec, + int mode, int clock) throws IOException { + beginBatch(); + writeByte(INCAP_CONFIGURE); + writeByte(incapNum); + writeByte((double_prec ? 0x80 : 0x00) | (mode << 3) | clock); + endBatch(); + } + + synchronized public void i2cWriteRead(int i2cNum, boolean tenBitAddr, + int address, int writeSize, int readSize, byte[] writeData) + throws IOException { + beginBatch(); + writeByte(I2C_WRITE_READ); + writeByte(((address >> 8) << 6) | (tenBitAddr ? 0x20 : 0x00) | i2cNum); + writeByte(address & 0xFF); + writeByte(writeSize); + writeByte(readSize); + for (int i = 0; i < writeSize; ++i) { + writeByte(((int) writeData[i]) & 0xFF); + } + endBatch(); + } + + synchronized public void setPinDigitalOut(int pin, boolean value, + DigitalOutput.Spec.Mode mode) throws IOException { + beginBatch(); + writeByte(SET_PIN_DIGITAL_OUT); + writeByte((pin << 2) + | (mode == DigitalOutput.Spec.Mode.OPEN_DRAIN ? 0x01 : 0x00) + | (value ? 0x02 : 0x00)); + endBatch(); + } + + synchronized public void setPinDigitalIn(int pin, + DigitalInput.Spec.Mode mode) throws IOException { + int pull = 0; + if (mode == DigitalInput.Spec.Mode.PULL_UP) { + pull = 1; + } else if (mode == DigitalInput.Spec.Mode.PULL_DOWN) { + pull = 2; + } + beginBatch(); + writeByte(SET_PIN_DIGITAL_IN); + writeByte((pin << 2) | pull); + endBatch(); + } + + synchronized public void setChangeNotify(int pin, boolean changeNotify) + throws IOException { + beginBatch(); + writeByte(SET_CHANGE_NOTIFY); + writeByte((pin << 2) | (changeNotify ? 0x01 : 0x00)); + endBatch(); + } + + synchronized public void registerPeriodicDigitalSampling(int pin, + int freqScale) throws IOException { + // TODO: implement + } + + synchronized public void setPinAnalogIn(int pin) throws IOException { + beginBatch(); + writeByte(SET_PIN_ANALOG_IN); + writeByte(pin); + endBatch(); + } + + synchronized public void setAnalogInSampling(int pin, boolean enable) + throws IOException { + beginBatch(); + writeByte(SET_ANALOG_IN_SAMPLING); + writeByte((enable ? 0x80 : 0x00) | (pin & 0x3F)); + endBatch(); + } + + synchronized public void uartData(int uartNum, int numBytes, byte data[]) + throws IOException { + if (numBytes > 64) { + throw new IllegalArgumentException( + "A maximum of 64 bytes can be sent in one uartData message. Got: " + + numBytes); + } + beginBatch(); + writeByte(UART_DATA); + writeByte((numBytes - 1) | uartNum << 6); + for (int i = 0; i < numBytes; ++i) { + writeByte(((int) data[i]) & 0xFF); + } + endBatch(); + } + + synchronized public void uartConfigure(int uartNum, int rate, + boolean speed4x, Uart.StopBits stopbits, Uart.Parity parity) + throws IOException { + int parbits = parity == Uart.Parity.EVEN ? 1 + : (parity == Uart.Parity.ODD ? 2 : 0); + beginBatch(); + writeByte(UART_CONFIG); + writeByte((uartNum << 6) | (speed4x ? 0x08 : 0x00) + | (stopbits == Uart.StopBits.TWO ? 0x04 : 0x00) | parbits); + writeTwoBytes(rate); + endBatch(); + } + + synchronized public void uartClose(int uartNum) throws IOException { + beginBatch(); + writeByte(UART_CONFIG); + writeByte(uartNum << 6); + writeTwoBytes(0); + endBatch(); + } + + synchronized public void setPinUart(int pin, int uartNum, boolean tx, + boolean enable) throws IOException { + beginBatch(); + writeByte(SET_PIN_UART); + writeByte(pin); + writeByte((enable ? 0x80 : 0x00) | (tx ? 0x40 : 0x00) | uartNum); + endBatch(); + } + + synchronized public void spiConfigureMaster(int spiNum, + SpiMaster.Config config) throws IOException { + beginBatch(); + writeByte(SPI_CONFIGURE_MASTER); + writeByte((spiNum << 5) | SCALE_DIV[config.rate.ordinal()]); + writeByte((config.sampleOnTrailing ? 0x00 : 0x02) + | (config.invertClk ? 0x01 : 0x00)); + endBatch(); + } + + synchronized public void spiClose(int spiNum) throws IOException { + beginBatch(); + writeByte(SPI_CONFIGURE_MASTER); + writeByte(spiNum << 5); + writeByte(0x00); + endBatch(); + } + + synchronized public void setPinSpi(int pin, int mode, boolean enable, + int spiNum) throws IOException { + beginBatch(); + writeByte(SET_PIN_SPI); + writeByte(pin); + writeByte((1 << 4) | (mode << 2) | spiNum); + endBatch(); + } + + synchronized public void spiMasterRequest(int spiNum, int ssPin, + byte data[], int dataBytes, int totalBytes, int responseBytes) + throws IOException { + final boolean dataNeqTotal = (dataBytes != totalBytes); + final boolean resNeqTotal = (responseBytes != totalBytes); + beginBatch(); + writeByte(SPI_MASTER_REQUEST); + writeByte((spiNum << 6) | ssPin); + writeByte((dataNeqTotal ? 0x80 : 0x00) | (resNeqTotal ? 0x40 : 0x00) + | totalBytes - 1); + if (dataNeqTotal) { + writeByte(dataBytes); + } + if (resNeqTotal) { + writeByte(responseBytes); + } + for (int i = 0; i < dataBytes; ++i) { + writeByte(((int) data[i]) & 0xFF); + } + endBatch(); + } + + synchronized public void i2cConfigureMaster(int i2cNum, Rate rate, + boolean smbusLevels) throws IOException { + int rateBits = (rate == Rate.RATE_1MHz ? 3 + : (rate == Rate.RATE_400KHz ? 2 : 1)); + beginBatch(); + writeByte(I2C_CONFIGURE_MASTER); + writeByte((smbusLevels ? 0x80 : 0) | (rateBits << 5) | i2cNum); + endBatch(); + } + + synchronized public void i2cClose(int i2cNum) throws IOException { + beginBatch(); + writeByte(I2C_CONFIGURE_MASTER); + writeByte(i2cNum); + endBatch(); + } + + public void icspOpen() throws IOException { + beginBatch(); + writeByte(ICSP_CONFIG); + writeByte(0x01); + endBatch(); + } + + public void icspClose() throws IOException { + beginBatch(); + writeByte(ICSP_CONFIG); + writeByte(0x00); + endBatch(); + } + + public void icspEnter() throws IOException { + beginBatch(); + writeByte(ICSP_PROG_ENTER); + endBatch(); + } + + public void icspExit() throws IOException { + beginBatch(); + writeByte(ICSP_PROG_EXIT); + endBatch(); + } + + public void icspSix(int instruction) throws IOException { + beginBatch(); + writeByte(ICSP_SIX); + writeThreeBytes(instruction); + endBatch(); + } + + public void icspRegout() throws IOException { + beginBatch(); + writeByte(ICSP_REGOUT); + endBatch(); + } + + public interface IncomingHandler { + public void handleEstablishConnection(byte[] hardwareId, + byte[] bootloaderId, byte[] firmwareId); + + public void handleConnectionLost(); + + public void handleSoftReset(); + + public void handleCheckInterfaceResponse(boolean supported); + + public void handleSetChangeNotify(int pin, boolean changeNotify); + + public void handleReportDigitalInStatus(int pin, boolean level); + + public void handleRegisterPeriodicDigitalSampling(int pin, int freqScale); + + public void handleReportPeriodicDigitalInStatus(int frameNum, + boolean values[]); + + public void handleAnalogPinStatus(int pin, boolean open); + + public void handleReportAnalogInStatus(int pins[], int values[]); + + public void handleUartOpen(int uartNum); + + public void handleUartClose(int uartNum); + + public void handleUartData(int uartNum, int numBytes, byte data[]); + + public void handleUartReportTxStatus(int uartNum, int bytesRemaining); + + public void handleSpiOpen(int spiNum); + + public void handleSpiClose(int spiNum); + + public void handleSpiData(int spiNum, int ssPin, byte data[], + int dataBytes); + + public void handleSpiReportTxStatus(int spiNum, int bytesRemaining); + + public void handleI2cOpen(int i2cNum); + + public void handleI2cClose(int i2cNum); + + public void handleI2cResult(int i2cNum, int size, byte[] data); + + public void handleI2cReportTxStatus(int spiNum, int bytesRemaining); + + void handleIcspOpen(); + + void handleIcspClose(); + + void handleIcspReportRxStatus(int bytesRemaining); + + void handleIcspResult(int size, byte[] data); + + public void handleIncapReport(int incapNum, int size, byte[] data); + + public void handleIncapClose(int incapNum); + + public void handleIncapOpen(int incapNum); + } + + class IncomingThread extends Thread { + private int readOffset_ = 0; + private int validBytes_ = 0; + private byte[] inbuf_ = new byte[64]; + + private int[] analogFramePins_ = new int[0]; + private Set removedPins_ = new HashSet( + Constants.NUM_ANALOG_PINS); + private Set addedPins_ = new HashSet( + Constants.NUM_ANALOG_PINS); + + private void findDelta(int[] newPins) { + removedPins_.clear(); + addedPins_.clear(); + for (int i : analogFramePins_) { + removedPins_.add(i); + } + for (int i : newPins) { + addedPins_.add(i); + } + for (Iterator it = removedPins_.iterator(); it.hasNext();) { + Integer current = it.next(); + if (addedPins_.contains(current)) { + it.remove(); + addedPins_.remove(current); + } + } + } + + private void fillBuf() throws IOException { + try { + validBytes_ = in_.read(inbuf_, 0, inbuf_.length); + if (validBytes_ <= 0) { + throw new IOException("Unexpected stream closure"); + } + //Log.v(TAG, "received " + validBytes_ + " bytes"); + readOffset_ = 0; + } catch (IOException e) { + Log.i(TAG, "IOIO disconnected"); + throw e; + } + } + + private int readByte() throws IOException { + if (readOffset_ == validBytes_) { + fillBuf(); + } + int b = inbuf_[readOffset_++]; + b &= 0xFF; // make unsigned + //Log.v(TAG, "received: 0x" + Integer.toHexString(b)); + return b; + } + + private void readBytes(int size, byte[] buffer) throws IOException { + for (int i = 0; i < size; ++i) { + buffer[i] = (byte) readByte(); + } + } + + @Override + public void run() { + super.run(); + setPriority(MAX_PRIORITY); + int arg1; + int arg2; + int numPins; + int size; + byte[] data = new byte[256]; + try { + while (true) { + switch (arg1 = readByte()) { + case ESTABLISH_CONNECTION: + if (readByte() != 'I' || readByte() != 'O' + || readByte() != 'I' || readByte() != 'O') { + throw new IOException( + "Bad establish connection magic"); + } + byte[] hardwareId = new byte[8]; + byte[] bootloaderId = new byte[8]; + byte[] firmwareId = new byte[8]; + readBytes(8, hardwareId); + readBytes(8, bootloaderId); + readBytes(8, firmwareId); + + handler_.handleEstablishConnection(hardwareId, + bootloaderId, firmwareId); + break; + + case SOFT_RESET: + handler_.handleSoftReset(); + break; + + case REPORT_DIGITAL_IN_STATUS: + arg1 = readByte(); + handler_.handleReportDigitalInStatus(arg1 >> 2, + (arg1 & 0x01) == 1); + break; + + case SET_CHANGE_NOTIFY: + arg1 = readByte(); + handler_.handleSetChangeNotify(arg1 >> 2, + (arg1 & 0x01) == 1); + break; + + case REGISTER_PERIODIC_DIGITAL_SAMPLING: + // TODO: implement + break; + + case REPORT_PERIODIC_DIGITAL_IN_STATUS: + // TODO: implement + break; + + case REPORT_ANALOG_IN_FORMAT: + numPins = readByte(); + int[] newFormat = new int[numPins]; + for (int i = 0; i < numPins; ++i) { + newFormat[i] = readByte(); + } + findDelta(newFormat); + for (Integer i : removedPins_) { + handler_.handleAnalogPinStatus(i, false); + } + for (Integer i : addedPins_) { + handler_.handleAnalogPinStatus(i, true); + } + analogFramePins_ = newFormat; + break; + + case REPORT_ANALOG_IN_STATUS: + numPins = analogFramePins_.length; + int header = 0; + int[] values = new int[numPins]; + for (int i = 0; i < numPins; ++i) { + if (i % 4 == 0) { + header = readByte(); + } + values[i] = (readByte() << 2) | (header & 0x03); + header >>= 2; + } + handler_.handleReportAnalogInStatus(analogFramePins_, + values); + break; + + case UART_REPORT_TX_STATUS: + arg1 = readByte(); + arg2 = readByte(); + handler_.handleUartReportTxStatus(arg1 & 0x03, + (arg1 >> 2) | (arg2 << 6)); + break; + + case UART_DATA: + arg1 = readByte(); + for (int i = 0; i < (arg1 & 0x3F) + 1; ++i) { + data[i] = (byte) readByte(); + } + handler_.handleUartData(arg1 >> 6, (arg1 & 0x3F) + 1, + data); + break; + + case UART_STATUS: + arg1 = readByte(); + if ((arg1 & 0x80) != 0) { + handler_.handleUartOpen(arg1 & 0x03); + } else { + handler_.handleUartClose(arg1 & 0x03); + } + break; + + case SPI_DATA: + arg1 = readByte(); + arg2 = readByte(); + for (int i = 0; i < (arg1 & 0x3F) + 1; ++i) { + data[i] = (byte) readByte(); + } + handler_.handleSpiData(arg1 >> 6, arg2 & 0x3F, data, + (arg1 & 0x3F) + 1); + break; + + case SPI_REPORT_TX_STATUS: + arg1 = readByte(); + arg2 = readByte(); + handler_.handleSpiReportTxStatus(arg1 & 0x03, + (arg1 >> 2) | (arg2 << 6)); + break; + + case SPI_STATUS: + arg1 = readByte(); + if ((arg1 & 0x80) != 0) { + handler_.handleSpiOpen(arg1 & 0x03); + } else { + handler_.handleSpiClose(arg1 & 0x03); + } + break; + + case I2C_STATUS: + arg1 = readByte(); + if ((arg1 & 0x80) != 0) { + handler_.handleI2cOpen(arg1 & 0x03); + } else { + handler_.handleI2cClose(arg1 & 0x03); + } + break; + + case I2C_RESULT: + arg1 = readByte(); + arg2 = readByte(); + if (arg2 != 0xFF) { + for (int i = 0; i < arg2; ++i) { + data[i] = (byte) readByte(); + } + } + handler_.handleI2cResult(arg1 & 0x03, arg2, data); + break; + + case I2C_REPORT_TX_STATUS: + arg1 = readByte(); + arg2 = readByte(); + handler_.handleI2cReportTxStatus(arg1 & 0x03, + (arg1 >> 2) | (arg2 << 6)); + break; + + case CHECK_INTERFACE_RESPONSE: + arg1 = readByte(); + handler_.handleCheckInterfaceResponse((arg1 & 0x01) == 1); + break; + + case ICSP_REPORT_RX_STATUS: + arg1 = readByte(); + arg2 = readByte(); + handler_.handleIcspReportRxStatus(arg1 | (arg2 << 8)); + break; + + case ICSP_RESULT: + data[0] = (byte) readByte(); + data[1] = (byte) readByte(); + handler_.handleIcspResult(2, data); + break; + + case ICSP_CONFIG: + arg1 = readByte(); + if ((arg1 & 0x01) == 1) { + handler_.handleIcspOpen(); + } else { + handler_.handleIcspClose(); + } + break; + + case INCAP_STATUS: + arg1 = readByte(); + if ((arg1 & 0x80) != 0) { + handler_.handleIncapOpen(arg1 & 0x0F); + } else { + handler_.handleIncapClose(arg1 & 0x0F); + } + break; + + case INCAP_REPORT: + arg1 = readByte(); + size = arg1 >> 6; + if (size == 0) { + size = 4; + } + readBytes(size, data); + handler_.handleIncapReport(arg1 & 0x0F, size, data); + break; + + case SOFT_CLOSE: + Log.d(TAG, "Received soft close."); + throw new IOException("Soft close"); + + + default: + in_.close(); + IOException e = new IOException( + "Received unexpected command: 0x" + + Integer.toHexString(arg1)); + Log.e("IOIOProtocol", "Protocol error", e); + throw e; + } + } + } catch (IOException e) { + handler_.handleConnectionLost(); + } + } + } + + private final InputStream in_; + private final OutputStream out_; + private final IncomingHandler handler_; + private final IncomingThread thread_ = new IncomingThread(); + + public IOIOProtocol(InputStream in, OutputStream out, + IncomingHandler handler) { + in_ = in; + out_ = out; + handler_ = handler; + thread_.start(); + } +} diff --git a/src/main/java/ioio/lib/impl/IcspMasterImpl.java b/src/main/java/ioio/lib/impl/IcspMasterImpl.java new file mode 100644 index 00000000..10e6fd18 --- /dev/null +++ b/src/main/java/ioio/lib/impl/IcspMasterImpl.java @@ -0,0 +1,137 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import ioio.lib.api.IcspMaster; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.impl.IncomingState.DataModuleListener; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.Queue; + +class IcspMasterImpl extends AbstractResource implements IcspMaster, + DataModuleListener +{ + private Queue resultQueue_ = new LinkedList(); + private int rxRemaining_ = 0; + + public IcspMasterImpl(IOIOImpl ioio) throws ConnectionLostException + { + super(ioio); + } + + @Override + synchronized public void dataReceived(byte[] data, int size) { + assert (size == 2); + int result = (byteToInt(data[1]) << 8) | byteToInt(data[0]); + resultQueue_.add(result); + notifyAll(); + } + + @Override + synchronized public void reportAdditionalBuffer(int bytesToAdd) { + rxRemaining_ += bytesToAdd; + notifyAll(); + } + + @Override + synchronized public void enterProgramming() throws ConnectionLostException { + checkState(); + try { + ioio_.protocol_.icspEnter(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } + + @Override + synchronized public void exitProgramming() throws ConnectionLostException { + checkState(); + try { + ioio_.protocol_.icspExit(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } + + @Override + synchronized public void executeInstruction(int instruction) + throws ConnectionLostException { + checkState(); + try { + ioio_.protocol_.icspSix(instruction); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } + + @Override + synchronized public void readVisi() throws ConnectionLostException, + InterruptedException { + checkState(); + while (rxRemaining_ < 2 && state_ == State.OPEN) { + wait(); + } + checkState(); + rxRemaining_ -= 2; + try { + ioio_.protocol_.icspRegout(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } + + @Override + synchronized public void close() { + super.close(); + ioio_.closeIcsp(); + } + + @Override + public synchronized void disconnected() { + super.disconnected(); + notifyAll(); + } + + private static int byteToInt(byte b) { + return ((int) b) & 0xFF; + } + + @Override + public synchronized int waitVisiResult() throws ConnectionLostException, + InterruptedException { + checkState(); + while (resultQueue_.isEmpty() && state_ == State.OPEN) { + wait(); + } + checkState(); + return resultQueue_.remove(); + } +} diff --git a/src/main/java/ioio/lib/impl/IncapImpl.java b/src/main/java/ioio/lib/impl/IncapImpl.java new file mode 100644 index 00000000..f5c2b32c --- /dev/null +++ b/src/main/java/ioio/lib/impl/IncapImpl.java @@ -0,0 +1,111 @@ +package ioio.lib.impl; + +import ioio.lib.api.PulseInput; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.impl.IncomingState.DataModuleListener; + +import java.util.LinkedList; +import java.util.Queue; + +class IncapImpl extends AbstractPin implements DataModuleListener, + PulseInput +{ + private static final int MAX_QUEUE_LEN = 32; + private final PulseMode mode_; + private final int incapNum_; + private long lastDuration_; + private final float timeBase_; + private final boolean doublePrecision_; + private boolean valid_ = false; + // TODO: a fixed-size array would have been much better than a linked list. + private Queue pulseQueue_ = new LinkedList(); + + public IncapImpl(IOIOImpl ioio, PulseMode mode, int incapNum, int pin, + int clockRate, int scale, boolean doublePrecision) + throws ConnectionLostException + { + super(ioio, pin); + mode_ = mode; + incapNum_ = incapNum; + timeBase_ = 1.0f / (scale * clockRate); + doublePrecision_ = doublePrecision; + } + + @Override + public float getFrequency() throws InterruptedException, + ConnectionLostException { + if (mode_ != PulseMode.FREQ && mode_ != PulseMode.FREQ_SCALE_4 + && mode_ != PulseMode.FREQ_SCALE_16) { + throw new IllegalStateException( + "Cannot query frequency when module was not opened in frequency mode."); + } + return 1.0f / getDuration(); + } + + @Override + public synchronized float getDuration() throws InterruptedException, + ConnectionLostException { + checkState(); + while (!valid_) { + wait(); + checkState(); + } + return timeBase_ * lastDuration_; + } + + @Override + public synchronized float waitPulseGetDuration() + throws InterruptedException, ConnectionLostException { + if (mode_ != PulseMode.POSITIVE && mode_ != PulseMode.NEGATIVE) { + throw new IllegalStateException( + "Cannot wait for pulse when module was not opened in pulse mode."); + } + checkState(); + while (pulseQueue_.isEmpty() && state_ == State.OPEN) { + wait(); + } + checkState(); + return timeBase_ * pulseQueue_.remove(); + } + + @Override + public synchronized void dataReceived(byte[] data, int size) { + lastDuration_ = ByteArrayToLong(data, size); + if (pulseQueue_.size() == MAX_QUEUE_LEN) { + pulseQueue_.remove(); + } + pulseQueue_.add(lastDuration_); + valid_ = true; + notifyAll(); + } + + private static long ByteArrayToLong(byte[] data, int size) { + long result = 0; + int i = size; + while (i-- > 0) { + result <<= 8; + result |= ((int) data[i]) & 0xFF; + } + if (result == 0) { + result = 1 << (size * 8); + } + return result; + } + + @Override + public synchronized void reportAdditionalBuffer(int bytesToAdd) { + } + + @Override + public synchronized void close() { + ioio_.closeIncap(incapNum_, doublePrecision_); + super.close(); + } + + @Override + public synchronized void disconnected() { + notifyAll(); + super.disconnected(); + } + +} diff --git a/src/main/java/ioio/lib/impl/IncomingState.java b/src/main/java/ioio/lib/impl/IncomingState.java new file mode 100644 index 00000000..b8e51627 --- /dev/null +++ b/src/main/java/ioio/lib/impl/IncomingState.java @@ -0,0 +1,463 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import android.util.Log; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.impl.IOIOProtocol.IncomingHandler; + +import java.util.HashSet; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; + +class IncomingState implements IncomingHandler { + enum ConnectionState { + INIT, ESTABLISHED, CONNECTED, DISCONNECTED, UNSUPPORTED_IID + } + + interface InputPinListener { + void setValue(int value); + } + + interface DisconnectListener { + void disconnected(); + } + + interface DataModuleListener { + void dataReceived(byte[] data, int size); + + void reportAdditionalBuffer(int bytesToAdd); + } + + class InputPinState { + private Queue listeners_ = new ConcurrentLinkedQueue(); + private boolean currentOpen_ = false; + + void pushListener(InputPinListener listener) { + listeners_.add(listener); + } + + void closeCurrentListener() { + if (currentOpen_) { + currentOpen_ = false; + listeners_.remove(); + } + } + + void openNextListener() { + assert (!listeners_.isEmpty()); + if (!currentOpen_) { + currentOpen_ = true; + } + } + + void setValue(int v) { + assert (currentOpen_); + listeners_.peek().setValue(v); + } + } + + class DataModuleState { + private Queue listeners_ = new ConcurrentLinkedQueue(); + private boolean currentOpen_ = false; + + void pushListener(DataModuleListener listener) { + listeners_.add(listener); + } + + void closeCurrentListener() { + if (currentOpen_) { + currentOpen_ = false; + listeners_.remove(); + } + } + + void openNextListener() { + assert (!listeners_.isEmpty()); + if (!currentOpen_) { + currentOpen_ = true; + } + } + + void dataReceived(byte[] data, int size) { + assert (currentOpen_); + listeners_.peek().dataReceived(data, size); + } + + public void reportAdditionalBuffer(int bytesRemaining) { + assert (currentOpen_); + listeners_.peek().reportAdditionalBuffer(bytesRemaining); + } + } + + private final InputPinState[] intputPinStates_ = new InputPinState[Constants.NUM_PINS]; + private final DataModuleState[] uartStates_ = new DataModuleState[Constants.NUM_UART_MODULES]; + private final DataModuleState[] twiStates_ = new DataModuleState[Constants.NUM_TWI_MODULES]; + private final DataModuleState[] spiStates_ = new DataModuleState[Constants.NUM_SPI_MODULES]; + private final DataModuleState[] incapStates_ = new DataModuleState[2 + * Constants.INCAP_MODULES_DOUBLE.length + + Constants.INCAP_MODULES_SINGLE.length]; + private final DataModuleState icspState_ = new DataModuleState(); + private final Set disconnectListeners_ = new HashSet(); + private ConnectionState connection_ = ConnectionState.INIT; + public String hardwareId_; + public String bootloaderId_; + public String firmwareId_; + + public IncomingState() { + for (int i = 0; i < intputPinStates_.length; ++i) { + intputPinStates_[i] = new InputPinState(); + } + for (int i = 0; i < uartStates_.length; ++i) { + uartStates_[i] = new DataModuleState(); + } + for (int i = 0; i < twiStates_.length; ++i) { + twiStates_[i] = new DataModuleState(); + } + for (int i = 0; i < spiStates_.length; ++i) { + spiStates_[i] = new DataModuleState(); + } + for (int i = 0; i < incapStates_.length; ++i) { + incapStates_[i] = new DataModuleState(); + } + } + + synchronized public void waitConnectionEstablished() + throws InterruptedException, ConnectionLostException + { + while (connection_ == ConnectionState.INIT) { + wait(); + } + if (connection_ == ConnectionState.DISCONNECTED) { + throw new ConnectionLostException(); + } + } + + synchronized public boolean waitForInterfaceSupport() + throws InterruptedException, ConnectionLostException + { + if (connection_ == ConnectionState.INIT) { + throw new IllegalStateException( + "Have to connect before waiting for interface support"); + } + while (connection_ == ConnectionState.ESTABLISHED) { + wait(); + } + if (connection_ == ConnectionState.DISCONNECTED) { + throw new ConnectionLostException(); + } + return connection_ == ConnectionState.CONNECTED; + } + + synchronized public void waitDisconnect() throws InterruptedException { + while (connection_ != ConnectionState.DISCONNECTED) { + wait(); + } + } + + public void addInputPinListener(int pin, InputPinListener listener) { + intputPinStates_[pin].pushListener(listener); + } + + public void addUartListener(int uartNum, DataModuleListener listener) { + uartStates_[uartNum].pushListener(listener); + } + + public void addTwiListener(int twiNum, DataModuleListener listener) { + twiStates_[twiNum].pushListener(listener); + } + + public void addIncapListener(int incapNum, DataModuleListener listener) { + incapStates_[incapNum].pushListener(listener); + } + + public void addIcspListener(DataModuleListener listener) { + icspState_.pushListener(listener); + } + + public void addSpiListener(int spiNum, DataModuleListener listener) { + spiStates_[spiNum].pushListener(listener); + } + + synchronized public void addDisconnectListener(DisconnectListener listener) + throws ConnectionLostException + { + checkNotDisconnected(); + disconnectListeners_.add(listener); + } + + synchronized public void removeDisconnectListener( + DisconnectListener listener) { + if (connection_ != ConnectionState.DISCONNECTED) { + disconnectListeners_.remove(listener); + } + } + + @Override + public void handleConnectionLost() { + // logMethod("handleConnectionLost"); + synchronized (this) { + connection_ = ConnectionState.DISCONNECTED; + } + for (DisconnectListener listener : disconnectListeners_) { + listener.disconnected(); + } + disconnectListeners_.clear(); + synchronized (this) { + notifyAll(); + } + } + + @Override + public void handleSoftReset() { + // logMethod("handleSoftReset"); + for (InputPinState pinState : intputPinStates_) { + pinState.closeCurrentListener(); + } + for (DataModuleState uartState : uartStates_) { + uartState.closeCurrentListener(); + } + for (DataModuleState twiState : twiStates_) { + twiState.closeCurrentListener(); + } + for (DataModuleState spiState : spiStates_) { + spiState.closeCurrentListener(); + } + for (DataModuleState incapState : incapStates_) { + incapState.closeCurrentListener(); + } + icspState_.closeCurrentListener(); + } + + @Override + synchronized public void handleCheckInterfaceResponse(boolean supported) { + // logMethod("handleCheckInterfaceResponse", supported); + connection_ = supported ? ConnectionState.CONNECTED + : ConnectionState.UNSUPPORTED_IID; + notifyAll(); + } + + @Override + public void handleSetChangeNotify(int pin, boolean changeNotify) { + // logMethod("handleSetChangeNotify", pin, changeNotify); + if (changeNotify) { + intputPinStates_[pin].openNextListener(); + } else { + intputPinStates_[pin].closeCurrentListener(); + } + } + + @Override + public void handleRegisterPeriodicDigitalSampling(int pin, int freqScale) { + // logMethod("handleRegisterPeriodicDigitalSampling", pin, freqScale); + assert (false); + } + + @Override + public void handleAnalogPinStatus(int pin, boolean open) { + // logMethod("handleAnalogPinStatus", pin, open); + if (open) { + intputPinStates_[pin].openNextListener(); + } else { + intputPinStates_[pin].closeCurrentListener(); + } + } + + @Override + public void handleUartData(int uartNum, int numBytes, byte[] data) { + // logMethod("handleUartData", uartNum, numBytes, data); + uartStates_[uartNum].dataReceived(data, numBytes); + } + + @Override + public void handleUartOpen(int uartNum) { + // logMethod("handleUartOpen", uartNum); + uartStates_[uartNum].openNextListener(); + } + + @Override + public void handleUartClose(int uartNum) { + // logMethod("handleUartClose", uartNum); + uartStates_[uartNum].closeCurrentListener(); + } + + @Override + public void handleSpiOpen(int spiNum) { + // logMethod("handleSpiOpen", spiNum); + spiStates_[spiNum].openNextListener(); + } + + @Override + public void handleSpiClose(int spiNum) { + // logMethod("handleSpiClose", spiNum); + spiStates_[spiNum].closeCurrentListener(); + } + + @Override + public void handleI2cOpen(int i2cNum) { + // logMethod("handleI2cOpen", i2cNum); + twiStates_[i2cNum].openNextListener(); + } + + @Override + public void handleI2cClose(int i2cNum) { + // logMethod("handleI2cClose", i2cNum); + twiStates_[i2cNum].closeCurrentListener(); + } + + @Override + public void handleIcspOpen() { + // logMethod("handleIcspOpen"); + icspState_.openNextListener(); + } + + @Override + public void handleIcspClose() { + // logMethod("handleIcspClose"); + icspState_.closeCurrentListener(); + } + + @Override + public void handleEstablishConnection(byte[] hardwareId, + byte[] bootloaderId, byte[] firmwareId) { + hardwareId_ = new String(hardwareId); + bootloaderId_ = new String(bootloaderId); + firmwareId_ = new String(firmwareId); + + Log.i("IncomingState", "IOIO Connection established. Hardware ID: " + + hardwareId_ + " Bootloader ID: " + bootloaderId_ + + " Firmware ID: " + firmwareId_); + synchronized (this) { + connection_ = ConnectionState.ESTABLISHED; + notifyAll(); + } + } + + @Override + public void handleUartReportTxStatus(int uartNum, int bytesRemaining) { + // logMethod("handleUartReportTxStatus", uartNum, bytesRemaining); + uartStates_[uartNum].reportAdditionalBuffer(bytesRemaining); + } + + @Override + public void handleI2cReportTxStatus(int i2cNum, int bytesRemaining) { + // logMethod("handleI2cReportTxStatus", i2cNum, bytesRemaining); + twiStates_[i2cNum].reportAdditionalBuffer(bytesRemaining); + } + + @Override + public void handleSpiData(int spiNum, int ssPin, byte[] data, int dataBytes) { + // logMethod("handleSpiData", spiNum, ssPin, data, dataBytes); + spiStates_[spiNum].dataReceived(data, dataBytes); + } + + @Override + public void handleIcspReportRxStatus(int bytesRemaining) { + // logMethod("handleIcspReportRxStatus", bytesRemaining); + icspState_.reportAdditionalBuffer(bytesRemaining); + } + + @Override + public void handleReportDigitalInStatus(int pin, boolean level) { + // logMethod("handleReportDigitalInStatus", pin, level); + intputPinStates_[pin].setValue(level ? 1 : 0); + } + + @Override + public void handleReportPeriodicDigitalInStatus(int frameNum, + boolean[] values) { + // logMethod("handleReportPeriodicDigitalInStatus", frameNum, values); + } + + @Override + public void handleReportAnalogInStatus(int pins[], int values[]) { + // logMethod("handleReportAnalogInStatus", pins, values); + for (int i = 0; i < pins.length; ++i) { + intputPinStates_[pins[i]].setValue(values[i]); + } + } + + @Override + public void handleSpiReportTxStatus(int spiNum, int bytesRemaining) { + // logMethod("handleSpiReportTxStatus", spiNum, bytesRemaining); + spiStates_[spiNum].reportAdditionalBuffer(bytesRemaining); + } + + @Override + public void handleI2cResult(int i2cNum, int size, byte[] data) { + // logMethod("handleI2cResult", i2cNum, size, data); + twiStates_[i2cNum].dataReceived(data, size); + } + + @Override + public void handleIncapReport(int incapNum, int size, byte[] data) { + // logMethod("handleIncapReport", incapNum, size, data); + incapStates_[incapNum].dataReceived(data, size); + } + + @Override + public void handleIncapClose(int incapNum) { + // logMethod("handleIncapClose", incapNum); + incapStates_[incapNum].closeCurrentListener(); + } + + @Override + public void handleIncapOpen(int incapNum) { + // logMethod("handleIncapOpen", incapNum); + incapStates_[incapNum].openNextListener(); + } + + @Override + public void handleIcspResult(int size, byte[] data) { + // logMethod("handleIcspResult", size, data); + icspState_.dataReceived(data, size); + } + + private void checkNotDisconnected() throws ConnectionLostException + { + if (connection_ == ConnectionState.DISCONNECTED) { + throw new ConnectionLostException(); + } + } + +// private void logMethod(String name, Object... args) { +// StringBuffer msg = new StringBuffer(name); +// msg.append('('); +// for (int i = 0; i < args.length; ++i) { +// if (i != 0) { +// msg.append(", "); +// } +// msg.append(args[i]); +// } +// msg.append(')'); +// +// Log.v("IncomingState", msg.toString()); +// } +} diff --git a/src/main/java/ioio/lib/impl/ModuleAllocator.java b/src/main/java/ioio/lib/impl/ModuleAllocator.java new file mode 100644 index 00000000..5f55b20b --- /dev/null +++ b/src/main/java/ioio/lib/impl/ModuleAllocator.java @@ -0,0 +1,106 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import ioio.lib.api.exception.OutOfResourceException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +/** + * Utility to allocate and assign unique module ids. + * A module id is requested via {@link #allocateModule()} + * and released via {@link #releaseModule(int)}. + * + * @author birmiwal + */ +class ModuleAllocator { + private final Set availableModuleIds_; + private final Set allocatedModuleIds_; + private final String name_; + + public ModuleAllocator(Collection availableModuleIds, String name) { + this.availableModuleIds_ = new TreeSet(availableModuleIds); + allocatedModuleIds_ = new HashSet(); + name_ = name; + } + + public ModuleAllocator(int[] availableModuleIds, String name) { + this(getList(availableModuleIds), name); + } + + public ModuleAllocator(int maxModules, String name) { + this(getList(maxModules), name); + } + + private static Collection getList(int maxModules) { + List availableModuleIds = new ArrayList(); + for (int i = 0; i < maxModules; i++) { + availableModuleIds.add(i); + } + return availableModuleIds; + } + + private static Collection getList(int[] array) { + List availableModuleIds = new ArrayList(array.length); + for (int i : array) { + availableModuleIds.add(i); + } + return availableModuleIds; + } + + /** + * @return a module id that was allocated, or {@code null} if nothing was available + */ + public synchronized Integer allocateModule() { + if (availableModuleIds_.isEmpty()) { + throw new OutOfResourceException("No more resources of the requested type: " + name_); + } + Integer moduleId = availableModuleIds_.iterator().next(); + availableModuleIds_.remove(moduleId); + allocatedModuleIds_.add(moduleId); + return moduleId; + } + + /** + * @param moduleId the moduleId to be released; throws {@link IllegalArgumentException} if + * a moduleId is re-returned, or an invalid moduleId is provided + */ + public synchronized void releaseModule(int moduleId) { + if (!allocatedModuleIds_.contains(moduleId)) { + throw new IllegalArgumentException("moduleId: " + moduleId+ "; not yet allocated"); + } + availableModuleIds_.add(moduleId); + allocatedModuleIds_.remove(moduleId); + } +} diff --git a/src/main/java/ioio/lib/impl/PinFunctionMap.java b/src/main/java/ioio/lib/impl/PinFunctionMap.java new file mode 100644 index 00000000..4361ee5e --- /dev/null +++ b/src/main/java/ioio/lib/impl/PinFunctionMap.java @@ -0,0 +1,76 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + + +class PinFunctionMap { + private static final boolean[] PERIPHERAL_OUT = new boolean[] { true, + false, false, true, true, true, true, true, false, false, true, + true, true, true, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + true, true, false, true, true, true, true, true, true, true, false, + false, false, false, true, true, true, true }; + + private static final boolean[] PERIPHERAL_IN = new boolean[] { true, + false, false, true, true, true, true, true, false, true, true, + true, true, true, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + true, true, false, true, true, true, true, true, true, true, false, + false, false, false, true, true, true, true }; + + static void checkSupportsAnalogInput(int pin) { + checkValidPin(pin); + if (pin < 31 || pin > 46) { + throw new IllegalArgumentException("Pin " + pin + + " does not support analog input"); + } + } + + static void checkSupportsPeripheralInput(int pin) { + checkValidPin(pin); + if (!PERIPHERAL_IN[pin]) { + throw new IllegalArgumentException("Pin " + pin + + " does not support peripheral input"); + } + } + + static void checkSupportsPeripheralOutput(int pin) { + checkValidPin(pin); + if (!PERIPHERAL_OUT[pin]) { + throw new IllegalArgumentException("Pin " + pin + + " does not support peripheral output"); + } + } + + static void checkValidPin(int pin) { + if (pin < 0 || pin > 48) { + throw new IllegalArgumentException("Illegal pin: " + pin); + } + } +} diff --git a/src/main/java/ioio/lib/impl/PwmImpl.java b/src/main/java/ioio/lib/impl/PwmImpl.java new file mode 100644 index 00000000..38e91211 --- /dev/null +++ b/src/main/java/ioio/lib/impl/PwmImpl.java @@ -0,0 +1,104 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import ioio.lib.api.PwmOutput; +import ioio.lib.api.exception.ConnectionLostException; + +import java.io.IOException; + +class PwmImpl extends AbstractResource implements PwmOutput +{ + private final int pwmNum_; + private final int pinNum_; + private final float baseUs_; + private final int period_; + + public PwmImpl(IOIOImpl ioio, int pinNum, int pwmNum, int period, + float baseUs) throws ConnectionLostException + { + super(ioio); + pwmNum_ = pwmNum; + pinNum_ = pinNum; + baseUs_ = baseUs; + period_ = period; + } + + @Override + public synchronized void close() { + super.close(); + ioio_.closePwm(pwmNum_); + ioio_.closePin(pinNum_); + } + + @Override + public void setDutyCycle(float dutyCycle) throws ConnectionLostException { + assert (dutyCycle <= 1 && dutyCycle >= 0); + setPulseWidthInClocks(period_ * dutyCycle); + } + + @Override + public void setPulseWidth(int pulseWidthUs) throws ConnectionLostException { + setPulseWidth((float) pulseWidthUs); + } + + @Override + public void setPulseWidth(float pulseWidthUs) + throws ConnectionLostException { + assert (pulseWidthUs >= 0); + float p = pulseWidthUs / baseUs_; + setPulseWidthInClocks(p); + } + + synchronized private void setPulseWidthInClocks(float p) + throws ConnectionLostException + { + checkState(); + if (p > period_) { + p = period_; + } + int pw; + int fraction; + p -= 1; // period parameter is one less than the actual period length + // yes, there is 0 and then 2 (no 1) - this is not a bug, that + // is how the hardware PWM module works. + if (p < 1) { + pw = 0; + fraction = 0; + } else { + pw = (int) p; + fraction = ((int) p * 4) & 0x03; + } + try { + ioio_.protocol_.setPwmDutyCycle(pwmNum_, pw, fraction); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } +} diff --git a/src/main/java/ioio/lib/impl/QueueInputStream.java b/src/main/java/ioio/lib/impl/QueueInputStream.java new file mode 100644 index 00000000..15707789 --- /dev/null +++ b/src/main/java/ioio/lib/impl/QueueInputStream.java @@ -0,0 +1,125 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; + +class QueueInputStream extends InputStream { + private enum State { + OPEN, CLOSED, KILLED + }; + + private final Queue queue_ = new ArrayBlockingQueue( + Constants.BUFFER_SIZE); + private State state_ = State.OPEN; + + @Override + synchronized public int read() throws IOException { + try { + while (state_ == State.OPEN && queue_.isEmpty()) { + wait(); + } + if (state_ == State.KILLED) { + throw new IOException("Stream has been closed"); + } + if (state_ == State.CLOSED && queue_.isEmpty()) { + return -1; + } + return ((int) queue_.remove()) & 0xFF; + } catch (InterruptedException e) { + throw new IOException("Interrupted"); + } + } + + @Override + synchronized public int read(byte[] b, int off, int len) throws IOException { + if (len == 0) { + return 0; + } + try { + while (state_ == State.OPEN && queue_.isEmpty()) { + wait(); + } + if (state_ == State.KILLED) { + throw new IOException("Stream has been closed"); + } + if (state_ == State.CLOSED && queue_.isEmpty()) { + return -1; + } + if (len > queue_.size()) { + len = queue_.size(); + } + for (int i = 0; i < len; ++i) { + b[off++] = queue_.remove(); + } + return len; + } catch (InterruptedException e) { + throw new IOException("Interrupted"); + } + } + + synchronized public void write(byte[] data, int size) { + for (int i = 0; i < size; ++i) { + if (queue_.size() == Constants.BUFFER_SIZE) { + Log.e("QueueInputStream", "Buffer overflow, discarding data"); + break; + } + queue_.add(data[i]); + } + notifyAll(); + } + + @Override + synchronized public int available() throws IOException { + return queue_.size(); + } + + @Override + synchronized public void close() { + if (state_ != State.OPEN) { + return; + } + state_ = State.CLOSED; + notifyAll(); + } + + synchronized public void kill() { + if (state_ != State.OPEN) { + return; + } + state_ = State.KILLED; + notifyAll(); + } + +} diff --git a/src/main/java/ioio/lib/impl/SocketIOIOConnection.java b/src/main/java/ioio/lib/impl/SocketIOIOConnection.java new file mode 100644 index 00000000..bb2326a5 --- /dev/null +++ b/src/main/java/ioio/lib/impl/SocketIOIOConnection.java @@ -0,0 +1,141 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import android.util.Log; +import ioio.lib.api.IOIOConnection; +import ioio.lib.api.exception.ConnectionLostException; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; + +public class SocketIOIOConnection implements IOIOConnection +{ + private static final String TAG = "SocketIOIOConnection"; + private final int port_; + private ServerSocket server_ = null; + private Socket socket_ = null; + private boolean disconnect_ = false; + private boolean server_owned_by_connect_ = true; + private boolean socket_owned_by_connect_ = true; + + public SocketIOIOConnection(int port) { + port_ = port; + } + + @Override + public void waitForConnect() throws ConnectionLostException { + try { + synchronized (this) { + if (disconnect_) { + throw new ConnectionLostException(); + } + Log.v(TAG, "Creating server socket"); + server_ = new ServerSocket(port_); + server_owned_by_connect_ = false; + } + Log.v(TAG, "Waiting for TCP connection"); + socket_ = server_.accept(); + Log.v(TAG, "TCP connected"); + synchronized (this) { + if (disconnect_) { + socket_.close(); + throw new ConnectionLostException(); + } + socket_owned_by_connect_ = false; + } + } catch (IOException e) { + synchronized (this) { + disconnect_ = true; + if (server_owned_by_connect_ && server_ != null) { + try { + server_.close(); + } catch (IOException e1) { + Log.e(TAG, "Unexpected exception", e1); + } + } + if (socket_owned_by_connect_ && socket_ != null) { + try { + socket_.close(); + } catch (IOException e1) { + Log.e(TAG, "Unexpected exception", e1); + } + } + if (e instanceof SocketException && e.getMessage().equals("Permission denied")) { + Log.e(TAG, "Did you forget to declare uses-permission of android.permission.INTERNET?"); + } + throw new ConnectionLostException(e); + } + } + } + + @Override + synchronized public void disconnect() { + if (disconnect_) { + return; + } + Log.v(TAG, "Client initiated disconnect"); + disconnect_ = true; + if (!server_owned_by_connect_) { + try { + server_.close(); + } catch (IOException e1) { + Log.e(TAG, "Unexpected exception", e1); + } + } + if (!socket_owned_by_connect_) { + try { + socket_.shutdownOutput(); + } catch (IOException e1) { + } + } + } + + @Override + public InputStream getInputStream() throws ConnectionLostException { + try { + return socket_.getInputStream(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } + + @Override + public OutputStream getOutputStream() throws ConnectionLostException { + try { + return socket_.getOutputStream(); + } catch (IOException e) { + throw new ConnectionLostException(e); + } + } +} diff --git a/src/main/java/ioio/lib/impl/SocketIOIOConnectionBootstrap.java b/src/main/java/ioio/lib/impl/SocketIOIOConnectionBootstrap.java new file mode 100644 index 00000000..7610b092 --- /dev/null +++ b/src/main/java/ioio/lib/impl/SocketIOIOConnectionBootstrap.java @@ -0,0 +1,64 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ + +package ioio.lib.impl; + +import ioio.lib.api.IOIOConnection; +import ioio.lib.spi.IOIOConnectionBootstrap; +import ioio.lib.spi.IOIOConnectionFactory; + +import java.util.Collection; + +public class SocketIOIOConnectionBootstrap implements IOIOConnectionBootstrap +{ + /** The TCP port used for communicating with the IOIO board. */ + public static final int IOIO_PORT = 4545; + + @Override + public void getFactories(Collection result) { + result.add(new IOIOConnectionFactory() { + private Integer port_ = new Integer(IOIO_PORT); + + @Override + public String getType() { + return SocketIOIOConnection.class.getCanonicalName(); + } + + @Override + public Object getExtra() { + return port_; + } + + @Override + public IOIOConnection createConnection() { + return new SocketIOIOConnection(IOIO_PORT); + } + }); + } +} diff --git a/src/main/java/ioio/lib/impl/SpiMasterImpl.java b/src/main/java/ioio/lib/impl/SpiMasterImpl.java new file mode 100644 index 00000000..a407e87f --- /dev/null +++ b/src/main/java/ioio/lib/impl/SpiMasterImpl.java @@ -0,0 +1,199 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import android.util.Log; +import ioio.lib.api.SpiMaster; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.impl.FlowControlledPacketSender.Packet; +import ioio.lib.impl.FlowControlledPacketSender.Sender; +import ioio.lib.impl.IncomingState.DataModuleListener; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +class SpiMasterImpl extends AbstractResource implements SpiMaster, + DataModuleListener, Sender { + public class SpiResult implements Result + { + boolean ready_; + final byte[] data_; + + SpiResult(byte[] data) { + data_ = data; + } + + @Override + public synchronized void waitReady() throws ConnectionLostException, + InterruptedException { + while (!ready_ && state_ != State.DISCONNECTED) { + wait(); + } + checkState(); + } + } + + class OutgoingPacket implements Packet { + int writeSize_; + byte[] writeData_; + int ssPin_; + int readSize_; + int totalSize_; + + @Override + public int getSize() { + return writeSize_ + 4; + } + } + + private final Queue pendingRequests_ = new ConcurrentLinkedQueue(); + private final FlowControlledPacketSender outgoing_ = new FlowControlledPacketSender( + this); + + private final int spiNum_; + private final Map ssPinToIndex_; + private final int[] indexToSsPin_; + private final int mosiPinNum_; + private final int misoPinNum_; + private final int clkPinNum_; + + SpiMasterImpl(IOIOImpl ioio, int spiNum, int mosiPinNum, int misoPinNum, + int clkPinNum, int[] ssPins) throws ConnectionLostException + { + super(ioio); + spiNum_ = spiNum; + mosiPinNum_ = mosiPinNum; + misoPinNum_ = misoPinNum; + clkPinNum_ = clkPinNum; + indexToSsPin_ = ssPins.clone(); + ssPinToIndex_ = new HashMap(ssPins.length); + for (int i = 0; i < ssPins.length; ++i) { + ssPinToIndex_.put(ssPins[i], i); + } + } + + @Override + synchronized public void disconnected() { + super.disconnected(); + outgoing_.kill(); + for (SpiResult tr : pendingRequests_) { + synchronized (tr) { + tr.notify(); + } + } + } + + @Override + public void writeRead(int slave, byte[] writeData, int writeSize, + int totalSize, byte[] readData, int readSize) + throws ConnectionLostException, InterruptedException { + Result result = writeReadAsync(slave, writeData, writeSize, + totalSize, readData, readSize); + result.waitReady(); + } + + @Override + public SpiResult writeReadAsync(int slave, byte[] writeData, + int writeSize, int totalSize, byte[] readData, int readSize) + throws ConnectionLostException { + checkState(); + SpiResult result = new SpiResult(readData); + + OutgoingPacket p = new OutgoingPacket(); + p.writeSize_ = writeSize; + p.writeData_ = writeData; + p.readSize_ = readSize; + p.ssPin_ = indexToSsPin_[slave]; + p.totalSize_ = totalSize; + + if (p.readSize_ > 0) { + synchronized (this) { + pendingRequests_.add(result); + } + } else { + result.ready_ = true; + } + try { + outgoing_.write(p); + } catch (IOException e) { + Log.e("SpiMasterImpl", "Exception caught", e); + } + return result; + } + + @Override + public void writeRead(byte[] writeData, int writeSize, int totalSize, + byte[] readData, int readSize) throws ConnectionLostException, + InterruptedException { + writeRead(0, writeData, writeSize, totalSize, readData, readSize); + } + + @Override + public void dataReceived(byte[] data, int size) { + SpiResult result = pendingRequests_.remove(); + synchronized (result) { + result.ready_ = true; + System.arraycopy(data, 0, result.data_, 0, size); + result.notify(); + } + } + + @Override + public void reportAdditionalBuffer(int bytesRemaining) { + outgoing_.readyToSend(bytesRemaining); + } + + @Override + synchronized public void close() { + super.close(); + outgoing_.close(); + ioio_.closeSpi(spiNum_); + ioio_.closePin(mosiPinNum_); + ioio_.closePin(misoPinNum_); + ioio_.closePin(clkPinNum_); + for (int pin : indexToSsPin_) { + ioio_.closePin(pin); + } + } + + @Override + public void send(Packet packet) { + OutgoingPacket p = (OutgoingPacket) packet; + try { + ioio_.protocol_.spiMasterRequest(spiNum_, p.ssPin_, p.writeData_, + p.writeSize_, p.totalSize_, p.readSize_); + } catch (IOException e) { + Log.e("SpiImpl", "Caught exception", e); + } + } + +} diff --git a/src/main/java/ioio/lib/impl/TwiMasterImpl.java b/src/main/java/ioio/lib/impl/TwiMasterImpl.java new file mode 100644 index 00000000..9c2d5836 --- /dev/null +++ b/src/main/java/ioio/lib/impl/TwiMasterImpl.java @@ -0,0 +1,171 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import android.util.Log; +import ioio.lib.api.TwiMaster; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.impl.FlowControlledPacketSender.Packet; +import ioio.lib.impl.FlowControlledPacketSender.Sender; +import ioio.lib.impl.IncomingState.DataModuleListener; + +import java.io.IOException; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +class TwiMasterImpl extends AbstractResource implements TwiMaster, + DataModuleListener, Sender { + class TwiResult implements Result + { + boolean ready_ = false; + boolean success_; + final byte[] data_; + + public TwiResult(byte[] data) { + data_ = data; + } + + @Override + public synchronized boolean waitReady() throws ConnectionLostException, + InterruptedException { + while (!ready_ && state_ != State.DISCONNECTED) { + wait(); + } + checkState(); + return success_; + } + } + + class OutgoingPacket implements Packet { + int writeSize_; + byte[] writeData_; + boolean tenBitAddr_; + int addr_; + int readSize_; + + @Override + public int getSize() { + return writeSize_ + 4; + } + + } + + private final Queue pendingRequests_ = new ConcurrentLinkedQueue(); + private final FlowControlledPacketSender outgoing_ = new FlowControlledPacketSender( + this); + private final int twiNum_; + + TwiMasterImpl(IOIOImpl ioio, int twiNum) throws ConnectionLostException + { + super(ioio); + twiNum_ = twiNum; + } + + @Override + synchronized public void disconnected() { + super.disconnected(); + outgoing_.kill(); + for (TwiResult tr : pendingRequests_) { + synchronized (tr) { + tr.notify(); + } + } + } + + @Override + public boolean writeRead(int address, boolean tenBitAddr, byte[] writeData, + int writeSize, byte[] readData, int readSize) + throws ConnectionLostException, InterruptedException { + Result result = writeReadAsync(address, tenBitAddr, writeData, + writeSize, readData, readSize); + return result.waitReady(); + } + + @Override + public Result writeReadAsync(int address, boolean tenBitAddr, + byte[] writeData, int writeSize, byte[] readData, int readSize) + throws ConnectionLostException + { + checkState(); + TwiResult result = new TwiResult(readData); + + OutgoingPacket p = new OutgoingPacket(); + p.writeSize_ = writeSize; + p.writeData_ = writeData; + p.tenBitAddr_ = tenBitAddr; + p.readSize_ = readSize; + p.addr_ = address; + + synchronized (this) { + pendingRequests_.add(result); + try { + outgoing_.write(p); + } catch (IOException e) { + Log.e("SpiMasterImpl", "Exception caught", e); + } + } + return result; + } + + @Override + public void dataReceived(byte[] data, int size) { + TwiResult result = pendingRequests_.remove(); + synchronized (result) { + result.ready_ = true; + result.success_ = (size != 0xFF); + if (result.success_) { + System.arraycopy(data, 0, result.data_, 0, size); + } + result.notify(); + } + } + + @Override + public void reportAdditionalBuffer(int bytesRemaining) { + outgoing_.readyToSend(bytesRemaining); + } + + @Override + synchronized public void close() { + super.close(); + outgoing_.close(); + ioio_.closeTwi(twiNum_); + } + + @Override + public void send(Packet packet) { + OutgoingPacket p = (OutgoingPacket) packet; + try { + ioio_.protocol_.i2cWriteRead(twiNum_, p.tenBitAddr_, p.addr_, + p.writeSize_, p.readSize_, p.writeData_); + } catch (IOException e) { + Log.e("TwiImpl", "Caught exception", e); + } + } +} diff --git a/src/main/java/ioio/lib/impl/UartImpl.java b/src/main/java/ioio/lib/impl/UartImpl.java new file mode 100644 index 00000000..d8d364bb --- /dev/null +++ b/src/main/java/ioio/lib/impl/UartImpl.java @@ -0,0 +1,109 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.impl; + +import android.util.Log; +import ioio.lib.api.IOIO; +import ioio.lib.api.Uart; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.impl.FlowControlledOutputStream.Sender; +import ioio.lib.impl.IncomingState.DataModuleListener; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +class UartImpl extends AbstractResource implements DataModuleListener, Sender, Uart +{ + private static final int MAX_PACKET = 64; + + private final int uartNum_; + private final int rxPinNum_; + private final int txPinNum_; + private final FlowControlledOutputStream outgoing_ = new FlowControlledOutputStream(this, MAX_PACKET); + private final QueueInputStream incoming_ = new QueueInputStream(); + + public UartImpl(IOIOImpl ioio, int txPin, int rxPin, int uartNum) throws ConnectionLostException + { + super(ioio); + uartNum_ = uartNum; + rxPinNum_ = rxPin; + txPinNum_ = txPin; + } + + @Override + public void dataReceived(byte[] data, int size) { + incoming_.write(data, size); + } + + @Override + public void send(byte[] data, int size) { + try { + ioio_.protocol_.uartData(uartNum_, size, data); + } catch (IOException e) { + Log.e("UartImpl", e.getMessage()); + } + } + + @Override + synchronized public void close() { + super.close(); + incoming_.close(); + outgoing_.close(); + ioio_.closeUart(uartNum_); + if (rxPinNum_ != IOIO.INVALID_PIN) { + ioio_.closePin(rxPinNum_); + } + if (txPinNum_ != IOIO.INVALID_PIN) { + ioio_.closePin(txPinNum_); + } + } + + @Override + synchronized public void disconnected() { + super.disconnected(); + incoming_.kill(); + outgoing_.close(); + } + + @Override + public InputStream getInputStream() { + return incoming_; + } + + @Override + public OutputStream getOutputStream() { + return outgoing_; + } + + @Override + public void reportAdditionalBuffer(int bytesRemaining) { + outgoing_.readyToSend(bytesRemaining); + } +} diff --git a/src/main/java/ioio/lib/spi/IOIOConnectionBootstrap.java b/src/main/java/ioio/lib/spi/IOIOConnectionBootstrap.java new file mode 100644 index 00000000..9691117f --- /dev/null +++ b/src/main/java/ioio/lib/spi/IOIOConnectionBootstrap.java @@ -0,0 +1,42 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ + +package ioio.lib.spi; + +import java.util.Collection; + +/** + * Implementing class must have a default constructor. The default constructor + * must throw a NoRuntimeSupportException in case the required libraries for + * implementing the connections are not available in run-time. + */ +public interface IOIOConnectionBootstrap +{ + public void getFactories(Collection result); +} diff --git a/src/main/java/ioio/lib/spi/IOIOConnectionFactory.java b/src/main/java/ioio/lib/spi/IOIOConnectionFactory.java new file mode 100644 index 00000000..8286ac80 --- /dev/null +++ b/src/main/java/ioio/lib/spi/IOIOConnectionFactory.java @@ -0,0 +1,22 @@ +package ioio.lib.spi; + +import ioio.lib.api.IOIOConnection; + +public interface IOIOConnectionFactory +{ + /** + * A unique name of the connection type. Typically a fully-qualified + * name of the connection class. + */ + public String getType(); + + /** + * Extra information on the connection. This is specific to the + * connection type. For example, for a Bluetooth connection, this is an + * array containing the name and the Bluetooth address of the remote + * IOIO. + */ + public Object getExtra(); + + public IOIOConnection createConnection(); +} \ No newline at end of file diff --git a/src/main/java/ioio/lib/spi/NoRuntimeSupportException.java b/src/main/java/ioio/lib/spi/NoRuntimeSupportException.java new file mode 100644 index 00000000..7ad92a5d --- /dev/null +++ b/src/main/java/ioio/lib/spi/NoRuntimeSupportException.java @@ -0,0 +1,9 @@ +package ioio.lib.spi; + +public class NoRuntimeSupportException extends RuntimeException { + private static final long serialVersionUID = -6559208663699429514L; + + public NoRuntimeSupportException(String desc) { + super(desc); + } +} \ No newline at end of file diff --git a/src/main/java/ioio/lib/util/BaseIOIOLooper.java b/src/main/java/ioio/lib/util/BaseIOIOLooper.java new file mode 100644 index 00000000..c87ab7dc --- /dev/null +++ b/src/main/java/ioio/lib/util/BaseIOIOLooper.java @@ -0,0 +1,49 @@ +package ioio.lib.util; + +import ioio.lib.api.IOIO; +import ioio.lib.api.exception.ConnectionLostException; + +/** + * A convenience implementation of {@link IOIOLooper}. + * + * This base class provides no-op implementations for all methods and provides + * the {@link #ioio_} field for subclasses. + * + */ +public class BaseIOIOLooper implements IOIOLooper +{ + protected IOIO ioio_; + + @Override + public final void setup(IOIO ioio) throws ConnectionLostException, + InterruptedException { + ioio_ = ioio; + setup(); + } + + /** + * This method will be called as soon as connection to the IOIO has been + * established. Typically, this will include opening pins and modules using + * the openXXX() methods of the {@link #ioio_} field. + * + * @throws ioio.lib.api.exception.ConnectionLostException + * The connection to the IOIO has been lost. + * @throws InterruptedException + * The thread has been interrupted. + */ + protected void setup() throws ConnectionLostException, InterruptedException { + } + + @Override + public void loop() throws ConnectionLostException, InterruptedException { + Thread.sleep(20); + } + + @Override + public void disconnected() { + } + + @Override + public void incompatible() { + } +} \ No newline at end of file diff --git a/src/main/java/ioio/lib/util/IOIOApplicationHelper.java b/src/main/java/ioio/lib/util/IOIOApplicationHelper.java new file mode 100644 index 00000000..6e1530a5 --- /dev/null +++ b/src/main/java/ioio/lib/util/IOIOApplicationHelper.java @@ -0,0 +1,166 @@ +package ioio.lib.util; + +import android.util.Log; +import ioio.lib.api.IOIO; +import ioio.lib.api.IOIOFactory; +import ioio.lib.api.exception.ConnectionLostException; +import ioio.lib.api.exception.IncompatibilityException; +import ioio.lib.spi.IOIOConnectionBootstrap; +import ioio.lib.spi.IOIOConnectionFactory; + +import java.util.Collection; +import java.util.LinkedList; + +/** + * A helper class for creating different kinds of IOIO based applications. + * + * This class implements a common life-cycle for applications interacting with + * IOIO devices. + *

+ * When the application starts, call {@link #start()}, which will in turn + * attempt to create a thread for each possible IOIO connection channel. Each + * thread will have a respective {@link IOIOLooper}, which the client provides, + * through which the client gets context for working with the IOIO. + *

+ * When the application exits, call {@link #stop()}, which will disconnect all + * open connections and will abort and join all the threads. + * + */ +public class IOIOApplicationHelper +{ + /** + * An abstract class, which facilitates a thread dedicated for communication + * with a single physical IOIO device. + */ + static private class IOIOThread extends Thread { + protected IOIO ioio_; + private boolean abort_ = false; + private boolean connected_ = true; + private final IOIOLooper looper_; + private final IOIOConnectionFactory connectionFactory_; + + IOIOThread(IOIOLooper looper, IOIOConnectionFactory factory) { + looper_ = looper; + connectionFactory_ = factory; + } + + @Override + public final void run() { + super.run(); + while (!abort_) { + try { + synchronized (this) { + if (abort_) { + break; + } + ioio_ = IOIOFactory.create(connectionFactory_ + .createConnection()); + } + } catch (Exception e) { + Log.e(TAG, "Failed to create IOIO, aborting IOIOThread!"); + return; + } + // if we got here, we have a ioio_! + try { + ioio_.waitForConnect(); + connected_ = true; + looper_.setup(ioio_); + while (!abort_ && ioio_.getState() == IOIO.State.CONNECTED) { + looper_.loop(); + } + } catch (ConnectionLostException e) { + } catch (InterruptedException e) { + ioio_.disconnect(); + } catch (IncompatibilityException e) { + Log.e(TAG, "Incompatible IOIO firmware", e); + looper_.incompatible(); + // nothing to do - just wait until physical + // disconnection + } catch (Exception e) { + Log.e(TAG, "Unexpected exception caught", e); + ioio_.disconnect(); + break; + } finally { + try { + ioio_.waitForDisconnect(); + } catch (InterruptedException e1) { + } + synchronized (this) { + ioio_ = null; + } + if (connected_) { + looper_.disconnected(); + connected_ = false; + } + } + } + Log.d(TAG, "IOIOThread is exiting"); + } + + /** Not relevant to subclasses. */ + public synchronized final void abort() { + abort_ = true; + if (ioio_ != null) { + ioio_.disconnect(); + } + if (connected_) { + interrupt(); + } + } + } + + protected static final String TAG = "IOIOAndroidApplicationHelper"; + protected final IOIOLooperProvider looperProvider_; + private Collection threads_ = new LinkedList(); + protected Collection bootstraps_ = IOIOConnectionRegistry + .getBootstraps(); + + public IOIOApplicationHelper(IOIOLooperProvider provider) { + looperProvider_ = provider; + } + + protected void abortAllThreads() { + for (IOIOThread thread : threads_) { + thread.abort(); + } + } + + protected void joinAllThreads() throws InterruptedException { + for (IOIOThread thread : threads_) { + thread.join(); + } + } + + protected void createAllThreads() { + threads_.clear(); + Collection factories = IOIOConnectionRegistry + .getConnectionFactories(); + for (IOIOConnectionFactory factory : factories) { + IOIOLooper looper = looperProvider_.createIOIOLooper( + factory.getType(), factory.getExtra()); + if (looper != null) { + threads_.add(new IOIOThread(looper, factory)); + } + } + } + + protected void startAllThreads() { + for (IOIOThread thread : threads_) { + thread.start(); + } + } + + public void start() { + createAllThreads(); + startAllThreads(); + } + + public void stop() { + abortAllThreads(); + try { + joinAllThreads(); + } catch (InterruptedException e) { + } + } + +} \ No newline at end of file diff --git a/src/main/java/ioio/lib/util/IOIOConnectionRegistry.java b/src/main/java/ioio/lib/util/IOIOConnectionRegistry.java new file mode 100644 index 00000000..c5452c2d --- /dev/null +++ b/src/main/java/ioio/lib/util/IOIOConnectionRegistry.java @@ -0,0 +1,140 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ +package ioio.lib.util; + +import android.util.Log; +import ioio.lib.api.IOIOFactory; +import ioio.lib.spi.IOIOConnectionBootstrap; +import ioio.lib.spi.IOIOConnectionFactory; +import ioio.lib.spi.NoRuntimeSupportException; + +import java.util.Collection; +import java.util.LinkedList; + +/** + * A utility class for managing available connection types to IOIO. + *

+ * For advanced usage only! + *

+ * This class facilitates dynamic linkage and instantiation of different IOIO + * connection types. {@link ioio.lib.spi.IOIOConnectionBootstrap} classes enable creation of + * {@link ioio.lib.spi.IOIOConnectionFactory} instances, from which concrete + * {@link ioio.lib.api.IOIOConnection}s are created. The binding to + * {@link ioio.lib.spi.IOIOConnectionBootstrap} is dynamic, thus enabling linkage to succeed + * with or without those bootstraps. Likewise, during runtime, the absence of + * bootstraps is handled gracefully. + *

+ * A typical usage will call {@link #addBootstraps(String[])} with a list of + * class names to be sought from a static initializer block. It may then call + * {@link #getBootstraps()} to obtain any bootstrap classes that are available + * in runtime, in case the bootstrap classes themselves need some runtime + * handling. Last, the {@link #getConnectionFactories()} will return a + * collection of {@link ioio.lib.spi.IOIOConnectionFactory}, each representing one possible + * communication channel to a IOIO. + */ +public class IOIOConnectionRegistry +{ + /** + * Get all available connection specifications. This is a list of all + * currently available communication channels in which a IOIO may be + * available. The client typically passes elements of this collection to + * {@link IOIOFactory#create(ioio.lib.api.IOIOConnection)}, possibly after filtering + * based on the specification's properties. + * + * @return A collection of specifications. + */ + public static Collection getConnectionFactories() + { + Collection result = new LinkedList(); + for (IOIOConnectionBootstrap bootstrap : bootstraps_) + { + bootstrap.getFactories(result); + } + return result; + } + + /** + * For advanced usage only! Used for special runtime handling of bootstrap + * classes. + * + * @return The bootstraps. + */ + public static Collection getBootstraps() + { + return bootstraps_; + } + + /** + * For advanced usage only! Add platform-specific connection bootstrap + * classes. Call before calling {@link #getConnectionFactories()}. + */ + public static void addBootstraps(String[] classNames) + { + for (String className : classNames) + { + addBootstrap(className); + } + } + + private static final String TAG = "IOIOConnectionRegistry"; + + private static Collection bootstraps_; + + static + { + bootstraps_ = new LinkedList(); + String[] classNames = new String[]{"ioio.lib.impl.SocketIOIOConnectionBootstrap"}; + addBootstraps(classNames); + } + + private static void addBootstrap(String className) + { + try + { + Class bootstrapClass = Class + .forName(className).asSubclass( + IOIOConnectionBootstrap.class); + bootstraps_.add(bootstrapClass.newInstance()); + Log.d(TAG, "Successfully added bootstrap class: " + className); + } + catch (ClassNotFoundException e) + { + Log.d(TAG, "Bootstrap class not found: " + className + ". Not adding."); + } + catch (NoRuntimeSupportException e) + { + Log.d(TAG, "No runtime support for: " + className + ". Not adding."); + } + catch (Throwable e) + { + Log.e(TAG, "Exception caught while attempting to initialize accessory connection factory", e); + } + } + +} diff --git a/src/main/java/ioio/lib/util/IOIOLooper.java b/src/main/java/ioio/lib/util/IOIOLooper.java new file mode 100644 index 00000000..027c0600 --- /dev/null +++ b/src/main/java/ioio/lib/util/IOIOLooper.java @@ -0,0 +1,57 @@ +package ioio.lib.util; + +import ioio.lib.api.IOIO; +import ioio.lib.api.exception.ConnectionLostException; + +/** + * A handler implementing interaction with a single IOIO over a single + * connection period. The interface utilizes a basic workflow for working with a + * IOIO instance: as soon as a connection is established, {@link #setup(IOIO)} + * will be called. Then, the {@link #loop()} method will be called repeatedly as + * long as the connection is alive. Last, the {@link #disconnected()} method + * will be called upon losing the connection (as result of physical + * disconnection, closing the application, etc). In case a IOIO with an + * incompatible firmware is encountered, {@link #incompatible()} will be called + * instead of {@link #setup(IOIO)}, and the IOIO instance is entirely useless, + * until eventually {@link #disconnected()} gets called. + * + */ +public interface IOIOLooper +{ + /** + * Subclasses should override this method for performing operations to be + * done once as soon as IOIO communication is established. + */ + public abstract void setup(IOIO ioio) throws ConnectionLostException, + InterruptedException; + + /** + * Subclasses should override this method for performing operations to be + * done repetitively as long as IOIO communication persists. Typically, this + * will be the main logic of the application, processing inputs and + * producing outputs. + */ + public abstract void loop() throws ConnectionLostException, + InterruptedException; + + /** + * Subclasses should override this method for performing operations to be + * done once as soon as IOIO communication is lost or closed. Typically, + * this will include GUI changes corresponding to the change. This method + * will only be called if setup() has been called. The ioio argument passed + * to {@link #setup(IOIO)} must not be used from within this method - it is + * invalid. This method should not block for long, since it may cause an + * ANR. + */ + public abstract void disconnected(); + + /** + * Subclasses should override this method for performing operations to be + * done if an incompatible IOIO firmware is detected. The ioio argument + * passed to {@link #setup(IOIO)} must not be used from within this method - + * it is invalid. This method will only be called once, until a compatible + * IOIO is connected (i.e. {@link #setup(IOIO)} gets called). + */ + public abstract void incompatible(); + +} \ No newline at end of file diff --git a/src/main/java/ioio/lib/util/IOIOLooperProvider.java b/src/main/java/ioio/lib/util/IOIOLooperProvider.java new file mode 100644 index 00000000..c4dea637 --- /dev/null +++ b/src/main/java/ioio/lib/util/IOIOLooperProvider.java @@ -0,0 +1,37 @@ +package ioio.lib.util; + +/** + * An entity that provides {@link ioio.lib.util.IOIOLooper} instances on demand, per + * connection specifications. + */ +public interface IOIOLooperProvider +{ + + /** + * Subclasses should implement this method by returning an implementation of + * {@link ioio.lib.util.IOIOLooper}. The caller provide enough information to uniquely + * identify the connection, through the parameters. null may be + * returned if the client is not interested to create a thread for this + * IOIO. This can be used in order to filter out unwanted connections, for + * example if the application is only intended for wireless connection, any + * wired connection attempts may be rejected, thus saving resources used for + * listening for incoming wired connections. + * + * @param connectionType + * A unique name of the connection type. Typically, the + * fully-qualified name of the connection class used to connect + * to the IOIO. + * @param extra + * A connection-type-specific object with extra information on + * the specific connection. Should provide information that + * enables distinguishing between different IOIO instances using + * the same connection class. For example, a Bluetooth connection + * type, might have the remote IOIO's Bluetooth name as extra. + * + * @return An implementation of {@link ioio.lib.util.IOIOLooper}, or null to + * skip. + */ + public abstract IOIOLooper createIOIOLooper(String connectionType, + Object extra); + +} \ No newline at end of file diff --git a/src/main/java/ioio/lib/util/android/ContextWrapperDependent.java b/src/main/java/ioio/lib/util/android/ContextWrapperDependent.java new file mode 100644 index 00000000..be71a602 --- /dev/null +++ b/src/main/java/ioio/lib/util/android/ContextWrapperDependent.java @@ -0,0 +1,42 @@ +/* + * Copyright 2011 Ytai Ben-Tsvi. 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 "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 ARSHAN POURSOHI 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. + */ + +package ioio.lib.util.android; + +import android.content.ContextWrapper; + + +public interface ContextWrapperDependent +{ + public void onCreate(ContextWrapper wrapper); + public void onDestroy(); + public void open(); + public void reopen(); + public void close(); +} \ No newline at end of file diff --git a/src/main/java/ioio/lib/util/android/IOIOAndroidApplicationHelper.java b/src/main/java/ioio/lib/util/android/IOIOAndroidApplicationHelper.java new file mode 100644 index 00000000..7eb0f55d --- /dev/null +++ b/src/main/java/ioio/lib/util/android/IOIOAndroidApplicationHelper.java @@ -0,0 +1,68 @@ +package ioio.lib.util.android; + +import android.content.ContextWrapper; +import ioio.lib.spi.IOIOConnectionBootstrap; +import ioio.lib.util.IOIOApplicationHelper; +import ioio.lib.util.IOIOConnectionRegistry; +import ioio.lib.util.IOIOLooperProvider; + +public class IOIOAndroidApplicationHelper extends IOIOApplicationHelper +{ + static { + IOIOConnectionRegistry + .addBootstraps(new String[]{ + "ioio.lib.android.accessory.AccessoryConnectionBootstrap", + "ioio.lib.android.bluetooth.BluetoothIOIOConnectionBootstrap"}); + } + + private final ContextWrapper contextWrapper_; + + public IOIOAndroidApplicationHelper(ContextWrapper wrapper, IOIOLooperProvider provider) { + super(provider); + contextWrapper_ = wrapper; + } + + public void create() { + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).onCreate(contextWrapper_); + } + } + } + + public void destroy() { + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).onDestroy(); + } + } + } + + @Override + public void start() { + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).open(); + } + } + super.start(); + } + + @Override + public void stop() { + super.stop(); + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).close(); + } + } + } + + public void restart() { + for (IOIOConnectionBootstrap bootstrap : bootstraps_) { + if (bootstrap instanceof ContextWrapperDependent) { + ((ContextWrapperDependent) bootstrap).reopen(); + } + } + } +}