Add ByteBuffer interfaces to I2C and SPI.

Use these interfaces in ADXL345_I2C and ADXL345_SPI.

Change-Id: I62d37e80eb71cec9019dd095f3721e0e5d60bf3f
This commit is contained in:
Peter Johnson
2015-11-06 11:12:00 -08:00
parent af3facd1c2
commit e2a4556669
4 changed files with 203 additions and 35 deletions

View File

@@ -6,6 +6,9 @@
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj;
import java.nio.ByteOrder;
import java.nio.ByteBuffer;
import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tInstances;
import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
import edu.wpi.first.wpilibj.communication.UsageReporting;
@@ -121,17 +124,12 @@ public class ADXL345_I2C extends SensorBase implements Accelerometer, LiveWindow
* @return Acceleration of the ADXL345 in Gs.
*/
public double getAcceleration(Axes axis) {
byte[] rawAccel = new byte[2];
m_i2c.read(kDataRegister + axis.value, rawAccel.length, rawAccel);
ByteBuffer rawAccel = ByteBuffer.allocateDirect(2);
m_i2c.read(kDataRegister + axis.value, 2, rawAccel);
// Sensor is little endian... swap bytes
return accelFromBytes(rawAccel[0], rawAccel[1]);
}
private double accelFromBytes(byte first, byte second) {
short tempLow = (short) (first & 0xff);
short tempHigh = (short) ((second << 8) & 0xff00);
return (tempLow | tempHigh) * kGsPerLSB;
rawAccel.order(ByteOrder.LITTLE_ENDIAN);
return rawAccel.getShort(0) * kGsPerLSB;
}
/**
@@ -142,13 +140,14 @@ public class ADXL345_I2C extends SensorBase implements Accelerometer, LiveWindow
*/
public AllAxes getAccelerations() {
AllAxes data = new AllAxes();
byte[] rawData = new byte[6];
m_i2c.read(kDataRegister, rawData.length, rawData);
ByteBuffer rawData = ByteBuffer.allocateDirect(6);
m_i2c.read(kDataRegister, 6, rawData);
// Sensor is little endian... swap bytes
data.XAxis = accelFromBytes(rawData[0], rawData[1]);
data.YAxis = accelFromBytes(rawData[2], rawData[3]);
data.ZAxis = accelFromBytes(rawData[4], rawData[5]);
rawData.order(ByteOrder.LITTLE_ENDIAN);
data.XAxis = rawData.getShort(0) * kGsPerLSB;
data.YAxis = rawData.getShort(2) * kGsPerLSB;
data.ZAxis = rawData.getShort(4) * kGsPerLSB;
return data;
}

View File

@@ -155,14 +155,13 @@ public class ADXL345_SPI extends SensorBase implements Accelerometer, LiveWindow
* @return Acceleration of the ADXL345 in Gs.
*/
public double getAcceleration(ADXL345_SPI.Axes axis) {
byte[] transferBuffer = new byte[3];
transferBuffer[0] = (byte) ((kAddress_Read | kAddress_MultiByte | kDataRegister) + axis.value);
ByteBuffer transferBuffer = ByteBuffer.allocateDirect(3);
transferBuffer.put(0, (byte) ((kAddress_Read | kAddress_MultiByte | kDataRegister) + axis.value));
m_spi.transaction(transferBuffer, transferBuffer, 3);
ByteBuffer rawAccel = ByteBuffer.wrap(transferBuffer, 1, 2);
// Sensor is little endian
rawAccel.order(ByteOrder.LITTLE_ENDIAN);
transferBuffer.order(ByteOrder.LITTLE_ENDIAN);
return rawAccel.getShort() * kGsPerLSB;
return transferBuffer.getShort(1) * kGsPerLSB;
}
/**
@@ -173,18 +172,17 @@ public class ADXL345_SPI extends SensorBase implements Accelerometer, LiveWindow
*/
public ADXL345_SPI.AllAxes getAccelerations() {
ADXL345_SPI.AllAxes data = new ADXL345_SPI.AllAxes();
byte dataBuffer[] = new byte[7];
if (m_spi != null) {
ByteBuffer dataBuffer = ByteBuffer.allocateDirect(7);
// Select the data address.
dataBuffer[0] = (byte) (kAddress_Read | kAddress_MultiByte | kDataRegister);
dataBuffer.put(0, (byte) (kAddress_Read | kAddress_MultiByte | kDataRegister));
m_spi.transaction(dataBuffer, dataBuffer, 7);
ByteBuffer rawData = ByteBuffer.wrap(dataBuffer, 1, 6);
// Sensor is little endian... swap bytes
rawData.order(ByteOrder.LITTLE_ENDIAN);
dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
data.XAxis = rawData.getShort() * kGsPerLSB;
data.YAxis = rawData.getShort() * kGsPerLSB;
data.ZAxis = rawData.getShort() * kGsPerLSB;
data.XAxis = dataBuffer.getShort(1) * kGsPerLSB;
data.YAxis = dataBuffer.getShort(3) * kGsPerLSB;
data.ZAxis = dataBuffer.getShort(5) * kGsPerLSB;
}
return data;
}

View File

@@ -88,6 +88,36 @@ public class I2C extends SensorBase {
return aborted;
}
/**
* Generic transaction.
*
* This is a lower-level interface to the I2C hardware giving you more control
* over each transaction.
*
* @param dataToSend Buffer of data to send as part of the transaction. Must
* be allocated using ByteBuffer.allocateDirect().
* @param sendSize Number of bytes to send as part of the transaction.
* @param dataReceived Buffer to read data into. Must be allocated using
* ByteBuffer.allocateDirect().
* @param receiveSize Number of bytes to read from the device.
* @return Transfer Aborted... false for success, true for aborted.
*/
public synchronized boolean transaction(ByteBuffer dataToSend, int sendSize, ByteBuffer dataReceived,
int receiveSize) {
boolean aborted = true;
if (!dataToSend.isDirect())
throw new IllegalArgumentException("dataToSend must be a direct buffer");
if (dataToSend.capacity() < sendSize)
throw new IllegalArgumentException("dataToSend is too small, must be at least " + sendSize);
if (!dataReceived.isDirect())
throw new IllegalArgumentException("dataReceived must be a direct buffer");
if (dataReceived.capacity() < receiveSize)
throw new IllegalArgumentException("dataReceived is too small, must be at least " + receiveSize);
return I2CJNI.i2CTransaction((byte) m_port.getValue(), (byte) m_deviceAddress, dataToSend, (byte) sendSize, dataReceived, (byte) receiveSize) != 0;
}
/**
* Attempt to address a device on the I2C bus.
*
@@ -97,7 +127,7 @@ public class I2C extends SensorBase {
* @return Transfer Aborted... false for success, true for aborted.
*/
public boolean addressOnly() {
return transaction(null, (byte) 0, null, (byte) 0);
return transaction((byte[]) null, (byte) 0, (byte[]) null, (byte) 0);
}
/**
@@ -138,6 +168,25 @@ public class I2C extends SensorBase {
(byte) data.length) < 0;
}
/**
* Execute a write transaction with the device.
*
* Write multiple bytes to a register on a device and wait until the
* transaction is complete.
*
* @param data The data to write to the device. Must be created using
* ByteBuffer.allocateDirect().
*/
public synchronized boolean writeBulk(ByteBuffer data, int size) {
if (!data.isDirect())
throw new IllegalArgumentException("must be a direct buffer");
if (data.capacity() < size)
throw new IllegalArgumentException("buffer is too small, must be at least " + size);
return I2CJNI.i2CWrite((byte) m_port.getValue(), (byte) m_deviceAddress, data,
(byte) size) < 0;
}
/**
* Execute a read transaction with the device.
*
@@ -166,6 +215,36 @@ public class I2C extends SensorBase {
return transaction(registerAddressArray, registerAddressArray.length, buffer, count);
}
/**
* Execute a read transaction with the device.
*
* Read bytes from a device. Most I2C devices will auto-increment the
* register pointer internally allowing you to read consecutive
* registers on a device in a single transaction.
*
* @param registerAddress The register to read first in the transaction.
* @param count The number of bytes to read in the transaction.
* @param buffer A buffer to store the data read from the device. Must be
* created using ByteBuffer.allocateDirect().
* @return Transfer Aborted... false for success, true for aborted.
*/
public boolean read(int registerAddress, int count, ByteBuffer buffer) {
if (count < 1) {
throw new BoundaryException("Value must be at least 1, " + count +
" given");
}
if (!buffer.isDirect())
throw new IllegalArgumentException("must be a direct buffer");
if (buffer.capacity() < count)
throw new IllegalArgumentException("buffer is too small, must be at least " + count);
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(1);
dataToSendBuffer.put(0, (byte) registerAddress);
return transaction(dataToSendBuffer, 1, buffer, count);
}
/**
* Execute a read only transaction with the device.
*
@@ -196,6 +275,32 @@ public class I2C extends SensorBase {
return retVal < 0;
}
/**
* Execute a read only transaction with the device.
*
* Read bytes from a device. This method does not write any data to prompt
* the device.
*
* @param buffer A pointer to the array of bytes to store the data read from
* the device. Must be created using ByteBuffer.allocateDirect().
* @param count The number of bytes to read in the transaction.
* @return Transfer Aborted... false for success, true for aborted.
*/
public boolean readOnly(ByteBuffer buffer, int count) {
if (count < 1) {
throw new BoundaryException("Value must be at least 1, " + count +
" given");
}
if (!buffer.isDirect())
throw new IllegalArgumentException("must be a direct buffer");
if (buffer.capacity() < count)
throw new IllegalArgumentException("buffer is too small, must be at least " + count);
return I2CJNI.i2CRead((byte) m_port.getValue(), (byte) m_deviceAddress, buffer,
(byte) count) < 0;
}
/**
* Send a broadcast write to all devices on the I2C bus.
*
@@ -223,18 +328,21 @@ public class I2C extends SensorBase {
*/
public boolean verifySensor(int registerAddress, int count, byte[] expected) {
// TODO: Make use of all 7 read bytes
byte[] deviceData = new byte[4];
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(1);
ByteBuffer deviceData = ByteBuffer.allocateDirect(4);
for (int i = 0, curRegisterAddress = registerAddress; i < count; i += 4, curRegisterAddress +=
4) {
int toRead = count - i < 4 ? count - i : 4;
// Read the chunk of data. Return false if the sensor does not
// respond.
if (read(curRegisterAddress, toRead, deviceData)) {
dataToSendBuffer.put(0, (byte) curRegisterAddress);
if (transaction(dataToSendBuffer, 1, deviceData, toRead)) {
return false;
}
for (byte j = 0; j < toRead; j++) {
if (deviceData[j] != expected[i + j]) {
if (deviceData.get(j) != expected[i + j]) {
return false;
}
}

View File

@@ -143,11 +143,27 @@ public class SPI extends SensorBase {
* MISO input during the transfer into the receive FIFO.
*/
public int write(byte[] dataToSend, int size) {
int retVal = 0;
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size);
dataToSendBuffer.put(dataToSend);
retVal = SPIJNI.spiWrite(m_port, dataToSendBuffer, (byte) size);
return retVal;
return SPIJNI.spiWrite(m_port, dataToSendBuffer, (byte) size);
}
/**
* Write data to the slave device. Blocks until there is space in the output
* FIFO.
*
* If not running in output only mode, also saves the data received on the
* MISO input during the transfer into the receive FIFO.
*
* @param dataToSend The buffer containing the data to send. Must be created
* using ByteBuffer.allocateDirect().
*/
public int write(ByteBuffer dataToSend, int size) {
if (!dataToSend.isDirect())
throw new IllegalArgumentException("must be a direct buffer");
if (dataToSend.capacity() < size)
throw new IllegalArgumentException("buffer is too small, must be at least " + size);
return SPIJNI.spiWrite(m_port, dataToSend, (byte) size);
}
/**
@@ -174,6 +190,33 @@ public class SPI extends SensorBase {
return retVal;
}
/**
* Read a word from the receive FIFO.
*
* Waits for the current transfer to complete if the receive FIFO is empty.
*
* If the receive FIFO is empty, there is no active transfer, and initiate is
* false, errors.
*
* @param initiate If true, this function pushes "0" into the transmit buffer
* and initiates a transfer. If false, this function assumes that data
* is already in the receive FIFO from a previous write.
*
* @param received The buffer to be filled with the received data. Must be
* created using ByteBuffer.allocateDirect().
*/
public int read(boolean initiate, ByteBuffer dataReceived, int size) {
if (!dataReceived.isDirect())
throw new IllegalArgumentException("must be a direct buffer");
if (dataReceived.capacity() < size)
throw new IllegalArgumentException("buffer is too small, must be at least " + size);
if (initiate) {
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size);
return SPIJNI.spiTransaction(m_port, dataToSendBuffer, dataReceived, (byte) size);
}
return SPIJNI.spiRead(m_port, dataReceived, (byte) size);
}
/**
* Perform a simultaneous read/write transaction with the device
*
@@ -182,12 +225,32 @@ public class SPI extends SensorBase {
* @param size The length of the transaction, in bytes
*/
public int transaction(byte[] dataToSend, byte[] dataReceived, int size) {
int retVal = 0;
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(size);
dataToSendBuffer.put(dataToSend);
ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(size);
retVal = SPIJNI.spiTransaction(m_port, dataToSendBuffer, dataReceivedBuffer, (byte) size);
int retVal = SPIJNI.spiTransaction(m_port, dataToSendBuffer, dataReceivedBuffer, (byte) size);
dataReceivedBuffer.get(dataReceived);
return retVal;
}
/**
* Perform a simultaneous read/write transaction with the device
*
* @param dataToSend The data to be written out to the device. Must be
* created using ByteBuffer.allocateDirect().
* @param dataReceived Buffer to receive data from the device. Must be
* created using ByteBuffer.allocateDirect().
* @param size The length of the transaction, in bytes
*/
public int transaction(ByteBuffer dataToSend, ByteBuffer dataReceived, int size) {
if (!dataToSend.isDirect())
throw new IllegalArgumentException("dataToSend must be a direct buffer");
if (dataToSend.capacity() < size)
throw new IllegalArgumentException("dataToSend is too small, must be at least " + size);
if (!dataReceived.isDirect())
throw new IllegalArgumentException("dataReceived must be a direct buffer");
if (dataReceived.capacity() < size)
throw new IllegalArgumentException("dataReceived is too small, must be at least " + size);
return SPIJNI.spiTransaction(m_port, dataToSend, dataReceived, (byte) size);
}
}