001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */ 003/* Open Source Software - may be modified and shared by FRC teams. The code */ 004/* must be accompanied by the FIRST BSD license file in the root directory of */ 005/* the project. */ 006/*----------------------------------------------------------------------------*/ 007 008package edu.wpi.first.wpilibj; 009 010import java.nio.ByteBuffer; 011 012import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType; 013import edu.wpi.first.wpilibj.hal.HAL; 014import edu.wpi.first.wpilibj.hal.I2CJNI; 015import edu.wpi.first.wpilibj.util.BoundaryException; 016 017import static java.util.Objects.requireNonNull; 018 019/** 020 * I2C bus interface class. 021 * 022 * <p>This class is intended to be used by sensor (and other I2C device) drivers. It probably should 023 * not be used directly. 024 */ 025public class I2C { 026 public enum Port { 027 kOnboard(0), kMXP(1); 028 029 @SuppressWarnings("MemberName") 030 public final int value; 031 032 Port(int value) { 033 this.value = value; 034 } 035 } 036 037 private final int m_port; 038 private final int m_deviceAddress; 039 040 /** 041 * Constructor. 042 * 043 * @param port The I2C port the device is connected to. 044 * @param deviceAddress The address of the device on the I2C bus. 045 */ 046 public I2C(Port port, int deviceAddress) { 047 m_port = port.value; 048 m_deviceAddress = deviceAddress; 049 050 I2CJNI.i2CInitialize((byte) port.value); 051 052 HAL.report(tResourceType.kResourceType_I2C, deviceAddress); 053 } 054 055 /** 056 * Destructor. 057 */ 058 public void free() { 059 I2CJNI.i2CClose(m_port); 060 } 061 062 /** 063 * Generic transaction. 064 * 065 * <p>This is a lower-level interface to the I2C hardware giving you more control over each 066 * transaction. 067 * 068 * @param dataToSend Buffer of data to send as part of the transaction. 069 * @param sendSize Number of bytes to send as part of the transaction. 070 * @param dataReceived Buffer to read data into. 071 * @param receiveSize Number of bytes to read from the device. 072 * @return Transfer Aborted... false for success, true for aborted. 073 */ 074 public synchronized boolean transaction(byte[] dataToSend, int sendSize, 075 byte[] dataReceived, int receiveSize) { 076 if (dataToSend.length < sendSize) { 077 throw new IllegalArgumentException("dataToSend is too small, must be at least " + sendSize); 078 } 079 if (dataReceived.length < receiveSize) { 080 throw new IllegalArgumentException( 081 "dataReceived is too small, must be at least " + receiveSize); 082 } 083 return I2CJNI.i2CTransactionB(m_port, (byte) m_deviceAddress, dataToSend, 084 (byte) sendSize, dataReceived, (byte) receiveSize) < 0; 085 } 086 087 /** 088 * Generic transaction. 089 * 090 * <p>This is a lower-level interface to the I2C hardware giving you more control over each 091 * transaction. 092 * 093 * @param dataToSend Buffer of data to send as part of the transaction. 094 * @param sendSize Number of bytes to send as part of the transaction. 095 * @param dataReceived Buffer to read data into. 096 * @param receiveSize Number of bytes to read from the device. 097 * @return Transfer Aborted... false for success, true for aborted. 098 */ 099 public synchronized boolean transaction(ByteBuffer dataToSend, int sendSize, 100 ByteBuffer dataReceived, int receiveSize) { 101 if (dataToSend.hasArray() && dataReceived.hasArray()) { 102 return transaction(dataToSend.array(), sendSize, dataReceived.array(), receiveSize); 103 } 104 if (!dataToSend.isDirect()) { 105 throw new IllegalArgumentException("dataToSend must be a direct buffer"); 106 } 107 if (dataToSend.capacity() < sendSize) { 108 throw new IllegalArgumentException("dataToSend is too small, must be at least " + sendSize); 109 } 110 if (!dataReceived.isDirect()) { 111 throw new IllegalArgumentException("dataReceived must be a direct buffer"); 112 } 113 if (dataReceived.capacity() < receiveSize) { 114 throw new IllegalArgumentException( 115 "dataReceived is too small, must be at least " + receiveSize); 116 } 117 118 return I2CJNI.i2CTransaction(m_port, (byte) m_deviceAddress, dataToSend, 119 (byte) sendSize, dataReceived, (byte) receiveSize) < 0; 120 } 121 122 /** 123 * Attempt to address a device on the I2C bus. 124 * 125 * <p>This allows you to figure out if there is a device on the I2C bus that responds to the 126 * address specified in the constructor. 127 * 128 * @return Transfer Aborted... false for success, true for aborted. 129 */ 130 public boolean addressOnly() { 131 return transaction(new byte[0], (byte) 0, new byte[0], (byte) 0); 132 } 133 134 /** 135 * Execute a write transaction with the device. 136 * 137 * <p>Write a single byte to a register on a device and wait until the transaction is complete. 138 * 139 * @param registerAddress The address of the register on the device to be written. 140 * @param data The byte to write to the register on the device. 141 * @return Transfer Aborted... false for success, true for aborted. 142 */ 143 public synchronized boolean write(int registerAddress, int data) { 144 byte[] buffer = new byte[2]; 145 buffer[0] = (byte) registerAddress; 146 buffer[1] = (byte) data; 147 return I2CJNI.i2CWriteB(m_port, (byte) m_deviceAddress, buffer, 148 (byte) buffer.length) < 0; 149 } 150 151 /** 152 * Execute a write transaction with the device. 153 * 154 * <p>Write multiple bytes to a register on a device and wait until the transaction is complete. 155 * 156 * @param data The data to write to the device. 157 * @return Transfer Aborted... false for success, true for aborted. 158 */ 159 public synchronized boolean writeBulk(byte[] data) { 160 return writeBulk(data, data.length); 161 } 162 163 /** 164 * Execute a write transaction with the device. 165 * 166 * <p>Write multiple bytes to a register on a device and wait until the transaction is complete. 167 * 168 * @param data The data to write to the device. 169 * @param size The number of data bytes to write. 170 * @return Transfer Aborted... false for success, true for aborted. 171 */ 172 public synchronized boolean writeBulk(byte[] data, int size) { 173 if (data.length < size) { 174 throw new IllegalArgumentException( 175 "buffer is too small, must be at least " + size); 176 } 177 return I2CJNI.i2CWriteB(m_port, (byte) m_deviceAddress, data, (byte) size) < 0; 178 } 179 180 /** 181 * Execute a write transaction with the device. 182 * 183 * <p>Write multiple bytes to a register on a device and wait until the transaction is complete. 184 * 185 * @param data The data to write to the device. 186 * @param size The number of data bytes to write. 187 * @return Transfer Aborted... false for success, true for aborted. 188 */ 189 public synchronized boolean writeBulk(ByteBuffer data, int size) { 190 if (data.hasArray()) { 191 return writeBulk(data.array(), size); 192 } 193 if (!data.isDirect()) { 194 throw new IllegalArgumentException("must be a direct buffer"); 195 } 196 if (data.capacity() < size) { 197 throw new IllegalArgumentException( 198 "buffer is too small, must be at least " + size); 199 } 200 201 return I2CJNI.i2CWrite(m_port, (byte) m_deviceAddress, data, (byte) size) < 0; 202 } 203 204 /** 205 * Execute a read transaction with the device. 206 * 207 * <p>Read bytes from a device. Most I2C devices will auto-increment the register pointer 208 * internally allowing you to read consecutive registers on a device in a single transaction. 209 * 210 * @param registerAddress The register to read first in the transaction. 211 * @param count The number of bytes to read in the transaction. 212 * @param buffer A pointer to the array of bytes to store the data read from the device. 213 * @return Transfer Aborted... false for success, true for aborted. 214 */ 215 public boolean read(int registerAddress, int count, byte[] buffer) { 216 requireNonNull(buffer, "Null return buffer was given"); 217 218 if (count < 1) { 219 throw new BoundaryException("Value must be at least 1, " + count + " given"); 220 } 221 if (buffer.length < count) { 222 throw new IllegalArgumentException("buffer is too small, must be at least " + count); 223 } 224 225 byte[] registerAddressArray = new byte[1]; 226 registerAddressArray[0] = (byte) registerAddress; 227 228 return transaction(registerAddressArray, registerAddressArray.length, buffer, count); 229 } 230 231 private ByteBuffer m_readDataToSendBuffer = null; 232 233 /** 234 * Execute a read transaction with the device. 235 * 236 * <p>Read bytes from a device. Most I2C devices will auto-increment the register pointer 237 * internally allowing you to read consecutive registers on a device in a single transaction. 238 * 239 * @param registerAddress The register to read first in the transaction. 240 * @param count The number of bytes to read in the transaction. 241 * @param buffer A buffer to store the data read from the device. 242 * @return Transfer Aborted... false for success, true for aborted. 243 */ 244 public boolean read(int registerAddress, int count, ByteBuffer buffer) { 245 if (count < 1) { 246 throw new BoundaryException("Value must be at least 1, " + count + " given"); 247 } 248 249 if (buffer.hasArray()) { 250 return read(registerAddress, count, buffer.array()); 251 } 252 253 if (!buffer.isDirect()) { 254 throw new IllegalArgumentException("must be a direct buffer"); 255 } 256 if (buffer.capacity() < count) { 257 throw new IllegalArgumentException("buffer is too small, must be at least " + count); 258 } 259 260 synchronized (this) { 261 if (m_readDataToSendBuffer == null) { 262 m_readDataToSendBuffer = ByteBuffer.allocateDirect(1); 263 } 264 m_readDataToSendBuffer.put(0, (byte) registerAddress); 265 266 return transaction(m_readDataToSendBuffer, 1, buffer, count); 267 } 268 } 269 270 /** 271 * Execute a read only transaction with the device. 272 * 273 * <p>Read bytes from a device. This method does not write any data to prompt the device. 274 * 275 * @param buffer A pointer to the array of bytes to store the data read from the device. 276 * @param count The number of bytes to read in the transaction. 277 * @return Transfer Aborted... false for success, true for aborted. 278 */ 279 public boolean readOnly(byte[] buffer, int count) { 280 requireNonNull(buffer, "Null return buffer was given"); 281 if (count < 1) { 282 throw new BoundaryException("Value must be at least 1, " + count + " given"); 283 } 284 if (buffer.length < count) { 285 throw new IllegalArgumentException("buffer is too small, must be at least " + count); 286 } 287 288 return I2CJNI.i2CReadB(m_port, (byte) m_deviceAddress, buffer, 289 (byte) count) < 0; 290 } 291 292 /** 293 * Execute a read only transaction with the device. 294 * 295 * <p>Read bytes from a device. This method does not write any data to prompt the device. 296 * 297 * @param buffer A pointer to the array of bytes to store the data read from the device. 298 * @param count The number of bytes to read in the transaction. 299 * @return Transfer Aborted... false for success, true for aborted. 300 */ 301 public boolean readOnly(ByteBuffer buffer, int count) { 302 if (count < 1) { 303 throw new BoundaryException("Value must be at least 1, " + count 304 + " given"); 305 } 306 307 if (buffer.hasArray()) { 308 return readOnly(buffer.array(), count); 309 } 310 311 if (!buffer.isDirect()) { 312 throw new IllegalArgumentException("must be a direct buffer"); 313 } 314 if (buffer.capacity() < count) { 315 throw new IllegalArgumentException("buffer is too small, must be at least " + count); 316 } 317 318 return I2CJNI.i2CRead(m_port, (byte) m_deviceAddress, buffer, (byte) count) 319 < 0; 320 } 321 322 /* 323 * Send a broadcast write to all devices on the I2C bus. 324 * 325 * <p>This is not currently implemented! 326 * 327 * @param registerAddress The register to write on all devices on the bus. 328 * @param data The value to write to the devices. 329 */ 330 // public void broadcast(int registerAddress, int data) { 331 // } 332 333 /** 334 * Verify that a device's registers contain expected values. 335 * 336 * <p>Most devices will have a set of registers that contain a known value that can be used to 337 * identify them. This allows an I2C device driver to easily verify that the device contains the 338 * expected value. 339 * 340 * @param registerAddress The base register to start reading from the device. 341 * @param count The size of the field to be verified. 342 * @param expected A buffer containing the values expected from the device. 343 * @return true if the sensor was verified to be connected 344 * @pre The device must support and be configured to use register auto-increment. 345 */ 346 public boolean verifySensor(int registerAddress, int count, 347 byte[] expected) { 348 // TODO: Make use of all 7 read bytes 349 byte[] dataToSend = new byte[1]; 350 351 byte[] deviceData = new byte[4]; 352 for (int i = 0, curRegisterAddress = registerAddress; 353 i < count; i += 4, curRegisterAddress += 4) { 354 int toRead = count - i < 4 ? count - i : 4; 355 // Read the chunk of data. Return false if the sensor does not 356 // respond. 357 dataToSend[0] = (byte) curRegisterAddress; 358 if (transaction(dataToSend, 1, deviceData, toRead)) { 359 return false; 360 } 361 362 for (byte j = 0; j < toRead; j++) { 363 if (deviceData[j] != expected[i + j]) { 364 return false; 365 } 366 } 367 } 368 return true; 369 } 370}