I2C: Provide byte[] JNI interfaces.

This avoids a direct byte buffer allocation on every read/write/transaction
on the byte[] variants.

Changes HAL I2C interfaces to use const for dataToSend.
This commit is contained in:
Peter Johnson
2017-11-15 21:41:58 -08:00
parent 6307d41002
commit 9021b37fd2
8 changed files with 161 additions and 58 deletions

View File

@@ -95,7 +95,7 @@ void HAL_InitializeI2C(HAL_I2CPort port, int32_t* status) {
* @return >= 0 on success or -1 on transfer abort.
*/
int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress,
uint8_t* dataToSend, int32_t sendSize,
const uint8_t* dataToSend, int32_t sendSize,
uint8_t* dataReceived, int32_t receiveSize) {
if (port > 1) {
// Set port out of range error here
@@ -106,7 +106,7 @@ int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress,
msgs[0].addr = deviceAddress;
msgs[0].flags = 0;
msgs[0].len = sendSize;
msgs[0].buf = dataToSend;
msgs[0].buf = const_cast<uint8_t*>(dataToSend);
msgs[1].addr = deviceAddress;
msgs[1].flags = I2C_M_RD;
msgs[1].len = receiveSize;
@@ -137,7 +137,7 @@ int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress,
* @return >= 0 on success or -1 on transfer abort.
*/
int32_t HAL_WriteI2C(HAL_I2CPort port, int32_t deviceAddress,
uint8_t* dataToSend, int32_t sendSize) {
const uint8_t* dataToSend, int32_t sendSize) {
if (port > 1) {
// Set port out of range error here
return -1;
@@ -147,7 +147,7 @@ int32_t HAL_WriteI2C(HAL_I2CPort port, int32_t deviceAddress,
msg.addr = deviceAddress;
msg.flags = 0;
msg.len = sendSize;
msg.buf = dataToSend;
msg.buf = const_cast<uint8_t*>(dataToSend);
struct i2c_rdwr_ioctl_data rdwr;
rdwr.msgs = &msg;

View File

@@ -17,10 +17,10 @@ extern "C" {
void HAL_InitializeI2C(HAL_I2CPort port, int32_t* status);
int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress,
uint8_t* dataToSend, int32_t sendSize,
const uint8_t* dataToSend, int32_t sendSize,
uint8_t* dataReceived, int32_t receiveSize);
int32_t HAL_WriteI2C(HAL_I2CPort port, int32_t deviceAddress,
uint8_t* dataToSend, int32_t sendSize);
const uint8_t* dataToSend, int32_t sendSize);
int32_t HAL_ReadI2C(HAL_I2CPort port, int32_t deviceAddress, uint8_t* buffer,
int32_t count);
void HAL_CloseI2C(HAL_I2CPort port);

View File

@@ -16,14 +16,14 @@ void HAL_InitializeI2C(HAL_I2CPort port, int32_t* status) {
SimI2CData[port].SetInitialized(true);
}
int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress,
uint8_t* dataToSend, int32_t sendSize,
const uint8_t* dataToSend, int32_t sendSize,
uint8_t* dataReceived, int32_t receiveSize) {
SimI2CData[port].Write(deviceAddress, dataToSend, sendSize);
SimI2CData[port].Read(deviceAddress, dataReceived, receiveSize);
return 0;
}
int32_t HAL_WriteI2C(HAL_I2CPort port, int32_t deviceAddress,
uint8_t* dataToSend, int32_t sendSize) {
const uint8_t* dataToSend, int32_t sendSize) {
SimI2CData[port].Write(deviceAddress, dataToSend, sendSize);
return 0;
}

View File

@@ -97,10 +97,11 @@ void I2CData::CancelWriteCallback(int32_t uid) {
m_writeCallbacks = CancelCallback(m_writeCallbacks, uid);
}
void I2CData::Write(int32_t deviceAddress, uint8_t* dataToSend,
void I2CData::Write(int32_t deviceAddress, const uint8_t* dataToSend,
int32_t sendSize) {
std::lock_guard<wpi::mutex> lock(m_dataMutex);
InvokeCallback(m_writeCallbacks, "Write", dataToSend, sendSize);
InvokeCallback(m_writeCallbacks, "Write", const_cast<uint8_t*>(dataToSend),
sendSize);
}
void I2CData::Read(int32_t deviceAddress, uint8_t* buffer, int32_t count) {
std::lock_guard<wpi::mutex> lock(m_dataMutex);

View File

@@ -35,7 +35,8 @@ class I2CData {
int32_t RegisterWriteCallback(HAL_BufferCallback callback, void* param);
void CancelWriteCallback(int32_t uid);
void Write(int32_t deviceAddress, uint8_t* dataToSend, int32_t sendSize);
void Write(int32_t deviceAddress, const uint8_t* dataToSend,
int32_t sendSize);
void Read(int32_t deviceAddress, uint8_t* buffer, int32_t count);
void ResetData();

View File

@@ -72,20 +72,15 @@ public class I2C extends SensorBase {
*/
public synchronized boolean transaction(byte[] dataToSend, int sendSize,
byte[] dataReceived, int receiveSize) {
final int status;
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(sendSize);
if (sendSize > 0 && dataToSend != null) {
dataToSendBuffer.put(dataToSend);
if (dataToSend.length < sendSize) {
throw new IllegalArgumentException("dataToSend is too small, must be at least " + sendSize);
}
ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(receiveSize);
status = I2CJNI.i2CTransaction(m_port, (byte) m_deviceAddress, dataToSendBuffer,
(byte) sendSize, dataReceivedBuffer, (byte) receiveSize);
if (receiveSize > 0 && dataReceived != null) {
dataReceivedBuffer.get(dataReceived, 0, receiveSize);
if (dataReceived.length < receiveSize) {
throw new IllegalArgumentException(
"dataReceived is too small, must be at least " + receiveSize);
}
return status < 0;
return I2CJNI.i2CTransactionB(m_port, (byte) m_deviceAddress, dataToSend,
(byte) sendSize, dataReceived, (byte) receiveSize) < 0;
}
/**
@@ -94,16 +89,17 @@ public class I2C extends SensorBase {
* <p>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 dataToSend Buffer of data to send as part of the transaction.
* @param sendSize Number of bytes to send as part of the transaction.
* @param dataReceived Buffer to read data into. Must be allocated using {@link
* ByteBuffer#allocateDirect(int)}.
* @param dataReceived Buffer to read data into.
* @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) {
if (dataToSend.hasArray() && dataReceived.hasArray()) {
return transaction(dataToSend.array(), sendSize, dataReceived.array(), receiveSize);
}
if (!dataToSend.isDirect()) {
throw new IllegalArgumentException("dataToSend must be a direct buffer");
}
@@ -131,7 +127,7 @@ public class I2C extends SensorBase {
* @return Transfer Aborted... false for success, true for aborted.
*/
public boolean addressOnly() {
return transaction((byte[]) null, (byte) 0, null, (byte) 0);
return transaction(new byte[0], (byte) 0, new byte[0], (byte) 0);
}
/**
@@ -147,12 +143,8 @@ public class I2C extends SensorBase {
byte[] buffer = new byte[2];
buffer[0] = (byte) registerAddress;
buffer[1] = (byte) data;
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(2);
dataToSendBuffer.put(buffer);
return I2CJNI.i2CWrite(m_port, (byte) m_deviceAddress, dataToSendBuffer,
(byte) buffer.length) < 0;
return I2CJNI.i2CWriteB(m_port, (byte) m_deviceAddress, buffer,
(byte) buffer.length) < 0;
}
/**
@@ -164,11 +156,7 @@ public class I2C extends SensorBase {
* @return Transfer Aborted... false for success, true for aborted.
*/
public synchronized boolean writeBulk(byte[] data) {
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(data.length);
dataToSendBuffer.put(data);
return I2CJNI.i2CWrite(m_port, (byte) m_deviceAddress, dataToSendBuffer,
(byte) data.length) < 0;
return writeBulk(data, data.length);
}
/**
@@ -176,10 +164,31 @@ public class I2C extends SensorBase {
*
* <p>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().
* @param data The data to write to the device.
* @param size The number of data bytes to write.
* @return Transfer Aborted... false for success, true for aborted.
*/
public synchronized boolean writeBulk(byte[] data, int size) {
if (data.length < size) {
throw new IllegalArgumentException(
"buffer is too small, must be at least " + size);
}
return I2CJNI.i2CWriteB(m_port, (byte) m_deviceAddress, data, (byte) size) < 0;
}
/**
* Execute a write transaction with the device.
*
* <p>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.
* @param size The number of data bytes to write.
* @return Transfer Aborted... false for success, true for aborted.
*/
public synchronized boolean writeBulk(ByteBuffer data, int size) {
if (data.hasArray()) {
return writeBulk(data.array(), size);
}
if (!data.isDirect()) {
throw new IllegalArgumentException("must be a direct buffer");
}
@@ -208,6 +217,9 @@ public class I2C extends SensorBase {
if (count < 1) {
throw new BoundaryException("Value must be at least 1, " + count + " given");
}
if (buffer.length < count) {
throw new IllegalArgumentException("buffer is too small, must be at least " + count);
}
byte[] registerAddressArray = new byte[1];
registerAddressArray[0] = (byte) registerAddress;
@@ -215,6 +227,8 @@ public class I2C extends SensorBase {
return transaction(registerAddressArray, registerAddressArray.length, buffer, count);
}
private ByteBuffer m_readDataToSendBuffer = null;
/**
* Execute a read transaction with the device.
*
@@ -223,8 +237,7 @@ public class I2C extends SensorBase {
*
* @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().
* @param buffer A buffer to store the data read from the device.
* @return Transfer Aborted... false for success, true for aborted.
*/
public boolean read(int registerAddress, int count, ByteBuffer buffer) {
@@ -232,6 +245,10 @@ public class I2C extends SensorBase {
throw new BoundaryException("Value must be at least 1, " + count + " given");
}
if (buffer.hasArray()) {
return read(registerAddress, count, buffer.array());
}
if (!buffer.isDirect()) {
throw new IllegalArgumentException("must be a direct buffer");
}
@@ -239,10 +256,14 @@ public class I2C extends SensorBase {
throw new IllegalArgumentException("buffer is too small, must be at least " + count);
}
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(1);
dataToSendBuffer.put(0, (byte) registerAddress);
synchronized (this) {
if (m_readDataToSendBuffer == null) {
m_readDataToSendBuffer = ByteBuffer.allocateDirect(1);
}
m_readDataToSendBuffer.put(0, (byte) registerAddress);
return transaction(dataToSendBuffer, 1, buffer, count);
return transaction(m_readDataToSendBuffer, 1, buffer, count);
}
}
/**
@@ -259,13 +280,12 @@ public class I2C extends SensorBase {
if (count < 1) {
throw new BoundaryException("Value must be at least 1, " + count + " given");
}
if (buffer.length < count) {
throw new IllegalArgumentException("buffer is too small, must be at least " + count);
}
ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(count);
int retVal = I2CJNI.i2CRead(m_port, (byte) m_deviceAddress, dataReceivedBuffer,
(byte) count);
dataReceivedBuffer.get(buffer);
return retVal < 0;
return I2CJNI.i2CReadB(m_port, (byte) m_deviceAddress, buffer,
(byte) count) < 0;
}
/**
@@ -273,8 +293,7 @@ public class I2C extends SensorBase {
*
* <p>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 buffer A pointer to the array of bytes to store the data read from the device.
* @param count The number of bytes to read in the transaction.
* @return Transfer Aborted... false for success, true for aborted.
*/
@@ -284,6 +303,10 @@ public class I2C extends SensorBase {
+ " given");
}
if (buffer.hasArray()) {
return readOnly(buffer.array(), count);
}
if (!buffer.isDirect()) {
throw new IllegalArgumentException("must be a direct buffer");
}
@@ -322,21 +345,21 @@ public class I2C extends SensorBase {
public boolean verifySensor(int registerAddress, int count,
byte[] expected) {
// TODO: Make use of all 7 read bytes
ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(1);
byte[] dataToSend = new byte[1];
ByteBuffer deviceData = ByteBuffer.allocateDirect(4);
byte[] deviceData = new byte[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.
dataToSendBuffer.put(0, (byte) curRegisterAddress);
if (transaction(dataToSendBuffer, 1, deviceData, toRead)) {
dataToSend[0] = (byte) curRegisterAddress;
if (transaction(dataToSend, 1, deviceData, toRead)) {
return false;
}
for (byte j = 0; j < toRead; j++) {
if (deviceData.get(j) != expected[i + j]) {
if (deviceData[j] != expected[i + j]) {
return false;
}
}

View File

@@ -16,10 +16,18 @@ public class I2CJNI extends JNIWrapper {
public static native int i2CTransaction(int port, byte address, ByteBuffer dataToSend,
byte sendSize, ByteBuffer dataReceived, byte receiveSize);
public static native int i2CTransactionB(int port, byte address, byte[] dataToSend,
byte sendSize, byte[] dataReceived, byte receiveSize);
public static native int i2CWrite(int port, byte address, ByteBuffer dataToSend, byte sendSize);
public static native int i2CWriteB(int port, byte address, byte[] dataToSend, byte sendSize);
public static native int i2CRead(int port, byte address, ByteBuffer dataReceived,
byte receiveSize);
public static native int i2CReadB(int port, byte address, byte[] dataReceived,
byte receiveSize);
public static native void i2CClose(int port);
}

View File

@@ -13,8 +13,10 @@
#include "HAL/I2C.h"
#include "HALUtil.h"
#include "support/jni_util.h"
using namespace frc;
using namespace wpi::java;
// set the logging level
TLogLevel i2cJNILogLevel = logWARNING;
@@ -69,6 +71,32 @@ JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_I2CJNI_i2CTransaction(
return returnValue;
}
/*
* Class: edu_wpi_first_wpilibj_hal_I2CJNI
* Method: i2CTransactionB
* Signature: (IB[BB[BB)I
*/
JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_I2CJNI_i2CTransactionB(
JNIEnv* env, jclass, jint port, jbyte address, jbyteArray dataToSend,
jbyte sendSize, jbyteArray dataReceived, jbyte receiveSize) {
I2CJNI_LOG(logDEBUG) << "Calling I2CJNI i2CTransactionB";
I2CJNI_LOG(logDEBUG) << "Port = " << port;
I2CJNI_LOG(logDEBUG) << "Address = " << (jint)address;
I2CJNI_LOG(logDEBUG) << "SendSize = " << (jint)sendSize;
llvm::SmallVector<uint8_t, 128> recvBuf;
recvBuf.resize(receiveSize);
I2CJNI_LOG(logDEBUG) << "ReceiveSize = " << (jint)receiveSize;
jint returnValue =
HAL_TransactionI2C(static_cast<HAL_I2CPort>(port), address,
reinterpret_cast<const uint8_t *>(
JByteArrayRef(env, dataToSend).array().data()),
sendSize, recvBuf.data(), receiveSize);
env->SetByteArrayRegion(dataReceived, 0, receiveSize,
reinterpret_cast<const jbyte *>(recvBuf.data()));
I2CJNI_LOG(logDEBUG) << "ReturnValue = " << returnValue;
return returnValue;
}
/*
* Class: edu_wpi_first_wpilibj_hal_I2CJNI
* Method: i2CWrite
@@ -92,6 +120,27 @@ JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_I2CJNI_i2CWrite(
return returnValue;
}
/*
* Class: edu_wpi_first_wpilibj_hal_I2CJNI
* Method: i2CWriteB
* Signature: (IB[BB)I
*/
JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_I2CJNI_i2CWriteB(
JNIEnv* env, jclass, jint port, jbyte address, jbyteArray dataToSend,
jbyte sendSize) {
I2CJNI_LOG(logDEBUG) << "Calling I2CJNI i2CWrite";
I2CJNI_LOG(logDEBUG) << "Port = " << port;
I2CJNI_LOG(logDEBUG) << "Address = " << (jint)address;
I2CJNI_LOG(logDEBUG) << "SendSize = " << (jint)sendSize;
jint returnValue =
HAL_WriteI2C(static_cast<HAL_I2CPort>(port), address,
reinterpret_cast<const uint8_t *>(
JByteArrayRef(env, dataToSend).array().data()),
sendSize);
I2CJNI_LOG(logDEBUG) << "ReturnValue = " << (jint)returnValue;
return returnValue;
}
/*
* Class: edu_wpi_first_wpilibj_hal_I2CJNI
* Method: i2CRead
@@ -112,6 +161,27 @@ JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_I2CJNI_i2CRead(
return returnValue;
}
/*
* Class: edu_wpi_first_wpilibj_hal_I2CJNI
* Method: i2CReadB
* Signature: (IB[BB)I
*/
JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_I2CJNI_i2CReadB(
JNIEnv* env, jclass, jint port, jbyte address, jbyteArray dataReceived,
jbyte receiveSize) {
I2CJNI_LOG(logDEBUG) << "Calling I2CJNI i2CRead";
I2CJNI_LOG(logDEBUG) << "Port = " << port;
I2CJNI_LOG(logDEBUG) << "Address = " << address;
I2CJNI_LOG(logDEBUG) << "ReceiveSize = " << receiveSize;
llvm::SmallVector<uint8_t, 128> recvBuf;
recvBuf.resize(receiveSize);
jint returnValue = HAL_ReadI2C(static_cast<HAL_I2CPort>(port), address, recvBuf.data(), receiveSize);
env->SetByteArrayRegion(dataReceived, 0, receiveSize,
reinterpret_cast<const jbyte *>(recvBuf.data()));
I2CJNI_LOG(logDEBUG) << "ReturnValue = " << returnValue;
return returnValue;
}
/*
* Class: edu_wpi_first_wpilibj_hal_I2CJNI
* Method: i2CClose