Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions pi4j-core/src/main/java/com/pi4j/io/SerialCircuitIO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.pi4j.io;

/**
* A shared interface for SPI and I2C serial communication, centered around the
* "writeThenRead" method that performs "connected" write and read operations.
*
* <p>For SPI, this means that the chip select keeps active between the write and
* the read operation.
*
* <p>For I2C, this means that the operation is treated as an atomic
* multibyte-register read.
*
* <p>If this strong connection between write and read is not intended, please use
* separate write and read calls.
*
* <p>The write and read calls here delegate to writeThenRead with the write or
* read buffer null in order to require only one native implementation for
* all cases.
*/
public interface SerialCircuitIO extends AutoCloseable {

/** Reads the full data array. Returns the size of the buffer for historic reasons. */
default int read(byte[] data) {
return read(data, 0, data.length);
}

/**
* Read 'length' bytes into 'buffer' at offset 'offset'.
* Returns 'length' for historical reasons.
*/
default int read(byte[] buffer, int offset, int length) {
writeThenRead(null, 0, 0, 0, buffer, offset, length);
return length;
}

/** Writes the full byte array. Returns the buffer size for historical reasons. */
default int write(byte[] buffer) {
return write(buffer, 0, buffer.length);
}

/**
* Writes 'length' bytes from the 'buffer' at offset 'offset'.
* Returns 'length' for historical reasons.
*/
default int write(byte[] buffer, int offset, int length) {
writeThenRead(buffer, offset, length, 0, null, 0, 0);
return length;
}

default void writeThenRead(byte[] writeBuffer, byte[] readBuffer) {
writeThenRead(writeBuffer, 0, writeBuffer.length, 0, readBuffer, 0, readBuffer.length);
}

/**
* This function writes bytes to the device and then reads bytes from the device.
* Write data is taken from the 'writeBuffer' byte array from the given 'writeOffset' index to
* the specified 'writeLength' (number of bytes).
*
* <p>Data read back from the device is then copied to the 'readBuffer' byte array starting
* at the given 'readOffset' using the 'readLength' (number of bytes).
* The 'buffer' and 'read' byte array must be at least the size of their defined 'length' + 'offset'.
* <p>
* In addition. both the write and read operation can provide a configurable delay
* allowing the effected chip to complete any processing. The write operation and the read
* operation each have an individual delay value.
* *
*
* @param writeBuffer The write buffer. If null, only a read operation is performed.
* @param writeOffset The start offset in the write buffer
* @param writeLength The number of bytes to write.
* @param readDelayNanos Delay between write and read in usecs
* @param readBuffer The read buffer. If null, only a write operation is performed
* @param readOffset The start offset in the read buffer.
* @param readLength The number of bytes to read.
*/
void writeThenRead(byte[] writeBuffer, int writeOffset, int writeLength, int readDelayNanos, byte[] readBuffer, int readOffset, int readLength);
}
155 changes: 35 additions & 120 deletions pi4j-core/src/main/java/com/pi4j/io/i2c/I2C.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
import com.pi4j.io.IO;
import com.pi4j.io.IODataReader;
import com.pi4j.io.IODataWriter;
import com.pi4j.io.SerialCircuitIO;

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.Callable;

/**
Expand All @@ -43,7 +44,7 @@
* @version $Id: $Id
*/
public interface I2C
extends IO<I2C, I2CConfig, I2CProvider>, IODataWriter, IODataReader, I2CRegisterDataReaderWriter, AutoCloseable {
extends IO<I2C, I2CConfig, I2CProvider>, IODataWriter, IODataReader, I2CRegisterDataReaderWriter, SerialCircuitIO, AutoCloseable {

/**
* <p>close.</p>
Expand Down Expand Up @@ -106,137 +107,51 @@ default int getDevice() {
return device();
}


/**
* Method to perform a write of the writeBuffer, and then a read into the readBuffer
*
* @param writeBuffer the buffer to write
* @param readBuffer the buffer to read into
*
* @return the number of bytes read
*
* Note This method uses two atomic operations. start write bytes stop start read bytes stop
* To accomplish this work in a single atomic operation
* see {@link I2CRegisterDataReader#readRegister(byte[], ByteBuffer, int, int)}
*
* Method to write the writeBuffer, and then a read into the readBuffer
* in a single atomic operation.
*
* @param writeBuffer the buffer to write respecting the given length and offset
* @param writeOffset the offset of the array to write
* @param writeSize the number of bytes to write
* @param readDelayNanos delay after writing before reading; currently ignored for i2c.
* @param readBuffer the buffer into which to read the bytes
* @param readOffset the offset in the read buffer at which to insert the read bytes
* @param readSize the number of bytes to read
*/
default int writeThenRead(byte[] writeBuffer, byte[] readBuffer) {
return writeThenRead(writeBuffer, writeBuffer.length, 0, readBuffer, readBuffer.length, 0);
default void writeThenRead(byte[] writeBuffer, int writeOffset, int writeSize, int readDelayNanos, byte[] readBuffer, int readOffset, int readSize) {
// Ideally, new implementations support this call directly.
// however, we can emulate it using the multi-byte register call
if (writeSize != writeBuffer.length) {
writeBuffer = Arrays.copyOfRange(writeBuffer, writeOffset, writeOffset + writeSize);
}
readRegister(writeBuffer, readBuffer, readOffset, readSize);
}

// --------------------
// Disambiguation
// ---------------------

/**
* Method to perform a write of the writeBuffer, and then a read into the readBuffer
*
* @param writeSize the number of bytes to write
* @param writeOffset the offset of the array to write
* @param writeBuffer the buffer to write respecting the given length and offset
* @param readSize the number of bytes to read
* @param readOffset the offset in the read buffer at which to insert the read bytes
* @param readBuffer the buffer into which to read the bytes
*
* @return the number of bytes read
*
* Note This method uses two atomic operations. start write bytes stop start read bytes stop
* To accomplish this work in a single atomic operation
* see {@link I2CRegisterDataReader#readRegister(byte[], ByteBuffer, int, int)}
*/
default int writeThenRead(byte[] writeBuffer, int writeSize, int writeOffset, byte[] readBuffer, int readSize,
int readOffset) {
return internalWriteThenRead(writeBuffer, writeSize, writeOffset, readBuffer, readSize, readOffset);
@Override
default int read(byte[] data) {
return SerialCircuitIO.super.read(data);
}


@Deprecated
/**
* Method to perform a write of the writeBuffer, and then a read into the readBuffer
*
* @param writeBuffer the buffer to write
* @param readBuffer the buffer to read into
*
* @return the number of bytes read
*
* Note This method uses two atomic operations. start write bytes stop start read bytes stop
* To accomplish this work in a single atomic operation
* see {@link I2CRegisterDataReader#readRegister(byte[], ByteBuffer, int, int)}
*
*/
default int writeRead(byte[] writeBuffer, byte[] readBuffer) {
return writeRead(writeBuffer, writeBuffer.length, 0, readBuffer, readBuffer.length, 0);
@Override
default int read(byte[] data, int offset, int length) {
return SerialCircuitIO.super.read(data, offset, length);
}

@Deprecated
/**
* Method to perform a write of the writeBuffer, and then a read into the readBuffer
*
* @param writeSize the number of bytes to write
* @param writeOffset the offset of the array to write
* @param writeBuffer the buffer to write respecting the given length and offset
* @param readSize the number of bytes to read
* @param readOffset the offset in the read buffer at which to insert the read bytes
* @param readBuffer the buffer into which to read the bytes
*
* @return the number of bytes read
*
* Note This method uses two atomic operations. start write bytes stop start read bytes stop
* To accomplish this work in a single atomic operation
* see {@link I2CRegisterDataReader#readRegister(byte[], ByteBuffer, int, int)}
*/
default int writeRead(byte[] writeBuffer, int writeSize, int writeOffset, byte[] readBuffer, int readSize,
int readOffset) {
return internalWriteThenRead(writeBuffer, writeSize, writeOffset, readBuffer, readSize, readOffset);
@Override
default int write(byte[] data) {
return SerialCircuitIO.super.write(data);
}

/**
* Method to perform a write of the given buffer, and then a read into the given buffer
*
* @param writeSize the number of bytes to write
* @param writeOffset the offset of the array to write
* @param writeBuffer the buffer to write respecting the given length and offset
* @param readSize the number of bytes to read
* @param readOffset the offset in the read buffer at which to insert the read bytes
* @param readBuffer the buffer into which to read the bytes
*
* @return the number of bytes read
*
* Note This method uses two atomic operations. start write bytes stop start read bytes stop
* To accomplish this work in a single atomic operation
* see {@link I2CRegisterDataReader#readRegister(byte[], ByteBuffer, int, int)}
*/
default int internalWriteThenRead(byte[] writeBuffer, int writeSize, int writeOffset, byte[] readBuffer, int readSize,
int readOffset) {

// Check bounds for writeBuffer
if (writeOffset < 0) {
throw new IndexOutOfBoundsException("Write offset cannot be negative!");
}
if (writeOffset + writeSize > writeBuffer.length) {
throw new IndexOutOfBoundsException(
String.format("Write operation out of bounds. Write buffer length is %d. Yet write offset + size is=%d",
writeBuffer.length, writeOffset + writeSize));
}

// Check bounds for readBuffer
if (readOffset < 0) {
throw new IndexOutOfBoundsException("Read offset cannot be negative!");
}
if (readOffset + readSize > readBuffer.length) {
throw new IndexOutOfBoundsException(
String.format("Read operation out of bounds. Read buffer length is %d. Yet read offset + size is=%d",
readBuffer.length, readOffset + readSize));
}

return execute(() -> {
int written = write(writeBuffer, writeOffset, writeSize);
if (written != writeSize) {
throw new IllegalStateException(
"Expected to write " + writeSize + " bytes but only wrote " + written + " bytes");
}
return read(readBuffer, readOffset, readSize);
});
@Override
default int write(byte[] data, int offset, int length) {
return SerialCircuitIO.super.write(data, offset, length);
}


/**
* Get an encapsulated interface for reading and writing to a specific I2C device register
*
Expand Down
71 changes: 40 additions & 31 deletions pi4j-core/src/main/java/com/pi4j/io/spi/Spi.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.pi4j.io.IO;
import com.pi4j.io.IODataReader;
import com.pi4j.io.IODataWriter;
import com.pi4j.io.SerialCircuitIO;

import java.nio.ByteBuffer;

Expand All @@ -39,7 +40,7 @@
* @author Robert Savage (<a href="http://www.savagehomeautomation.com">http://www.savagehomeautomation.com</a>)
* @version $Id: $Id
*/
public interface Spi extends IO<Spi, SpiConfig, SpiProvider>, AutoCloseable, IODataWriter, IODataReader {
public interface Spi extends IO<Spi, SpiConfig, SpiProvider>, AutoCloseable, IODataWriter, IODataReader, SerialCircuitIO {
/**
* Constant <code>DEFAULT_BUS</code>
*/
Expand Down Expand Up @@ -384,6 +385,30 @@ default int transfer(ByteBuffer buffer, int offset, int length) {
return length;
}

// --------------------
// Disambiguation
// ---------------------

@Override
default int read(byte[] data) {
return SerialCircuitIO.super.read(data);
}

@Override
default int read(byte[] data, int offset, int length) {
return SerialCircuitIO.super.read(data, offset, length);
}

@Override
default int write(byte[] data) {
return SerialCircuitIO.super.write(data);
}

@Override
default int write(byte[] data, int offset, int length) {
return SerialCircuitIO.super.write(data, offset, length);
}

// ------------------------------------------------------------------------------------
// writeThenRead
// ------------------------------------------------------------------------------------
Expand All @@ -397,21 +422,7 @@ default int transfer(ByteBuffer buffer, int offset, int length) {
* @param read Buffer to contain read data
*/
default void writeThenRead(byte[] write, byte[] read) {
writeThenRead(write, 0, write.length, (short) 0, read, 0, read.length, (short) 0);
}

/**
* This function writes bytes to the SPI device and then reads bytes from the device
* Write data is taken from the 'buffer' byte array, data
* read back from the SPI device is then copied to the 'read' byte array
*
* @param write the array of bytes to write to the SPI device
* @param writeLength Number bytes written from buffer to the SPI
* @param read Buffer to contain read data
* @param readlength the number of bytes to read (read &amp; read))
*/
default void writeThenRead(byte[] write, int writeLength, byte[] read, int readlength) {
writeThenRead(write, 0, writeLength, (short) 0, read, 0, readlength, (short) 0);
writeThenRead(write, 0, write.length, (short) 0, read, 0, read.length);
}


Expand All @@ -427,22 +438,20 @@ default void writeThenRead(byte[] write, int writeLength, byte[] read, int readl
* allowing the effected SPI chip to complete any processing. The write operation and the read
* operation each have an individual delay value.
*
* @param write the array of bytes to write to the SPI device and to store read data
* back from the SPI device
* @param writeOffset the starting offset position in the provided buffer to
* start writing to the SPI device from and the position
* used as the starting offset position to place data bytes
* read back from the SPI device.
* @param writeLength Number of bytes written from write buffer
* @param writeDelayUsecs Delay after SPI write record processed by kernel before the
* read SPI record is processed. Value in usecs.
* @param read Buffer to contain read data
* @param readOffset Offset within the read buffer to begin placing read data
* @param readLength The number of bytes to transfer/exchange (read &amp; read))
* @param readDelayUsecs Delay after SPI read record processed by kernel before the
* read SPI record is processed. Value in usecs.
* @param write the array of bytes to write to the SPI device and to store read data
* back from the SPI device
* @param writeOffset the starting offset position in the provided buffer to
* start writing to the SPI device from and the position
* used as the starting offset position to place data bytes
* read back from the SPI device.
* @param writeLength Number of bytes written from write buffer
* @param readDelayNanos Delay after SPI write record processed by kernel before the
* read SPI record is processed. Value in nanoseconds.
* @param read Buffer to contain read data
* @param readOffset Offset within the read buffer to begin placing read data
* @param readLength The number of bytes to transfer/exchange (read &amp; read))
*/
default void writeThenRead(byte[] write, int writeOffset, int writeLength, short writeDelayUsecs, byte[] read, int readOffset, int readLength, short readDelayUsecs) {
default void writeThenRead(byte[] write, int writeOffset, int writeLength, int readDelayNanos, byte[] read, int readOffset, int readLength) {
throw new IllegalStateException("writeThenRead Not supported in this provider. \n See https://www.pi4j.com/documentation/providers/");
}
}
Loading