diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java index 174d4616cd..e94b0a8e4f 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/ADXL345_I2C.java @@ -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; } diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java index 561102e55d..1ab46f8f41 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/ADXL345_SPI.java @@ -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; } diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/I2C.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/I2C.java index 2efdc4da4c..40ac136858 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/I2C.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/I2C.java @@ -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; } } diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/SPI.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/SPI.java index a97f845929..fded29e50c 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/SPI.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/SPI.java @@ -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); + } }