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