/*
* 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.
* <p>
* 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)}.
* <p>
* 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.
* <p>
* 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:
*
* <pre>
* Master: M1 M2 GG GG
* Slave: GG S1 S2 S3
* </pre>
*
* M1, M2: the master's request<br>
* S1, S2, S3: the slave's response<br>
* GG: garbage bytes used for padding.
* <p>
* 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.
*
* <p>
* 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.
* <p>
* Typical usage (single slave, as per the example above):
*
* <pre>
* {@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
* }
* </pre>
*
* @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;
}