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