001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) FIRST 2008-2012. 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/*----------------------------------------------------------------------------*/ 007package edu.wpi.first.wpilibj; 008 009import java.nio.ByteOrder; 010import java.nio.IntBuffer; 011import java.nio.ByteBuffer; 012 013 014import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType; 015import edu.wpi.first.wpilibj.communication.UsageReporting; 016import edu.wpi.first.wpilibj.hal.HALLibrary; 017import edu.wpi.first.wpilibj.hal.HALUtil; 018import edu.wpi.first.wpilibj.hal.I2CJNI; 019import edu.wpi.first.wpilibj.util.BoundaryException; 020 021/** 022 * I2C bus interface class. 023 * 024 * This class is intended to be used by sensor (and other I2C device) drivers. 025 * It probably should not be used directly. 026 * 027 */ 028public class I2C extends SensorBase { 029 public enum Port {kOnboard(0), kMXP(1); 030 private int value; 031 032 private Port(int value){ 033 this.value = value; 034 } 035 036 public int getValue(){ 037 return this.value; 038 } 039 }; 040 041 private Port m_port; 042 private int m_deviceAddress; 043 044 /** 045 * Constructor. 046 * 047 * @param port The I2C port the device is connected to. 048 * @param deviceAddress 049 * The address of the device on the I2C bus. 050 */ 051 public I2C(Port port, int deviceAddress) { 052 ByteBuffer status = ByteBuffer.allocateDirect(4); 053 status.order(ByteOrder.LITTLE_ENDIAN); 054 055 m_port = port; 056 m_deviceAddress = deviceAddress; 057 058 I2CJNI.i2CInitialize((byte)m_port.getValue(), status.asIntBuffer()); 059 HALUtil.checkStatus(status.asIntBuffer()); 060 061 UsageReporting.report(tResourceType.kResourceType_I2C, deviceAddress); 062 } 063 064 /** 065 * Destructor. 066 */ 067 public void free() { 068 } 069 070 /** 071 * Generic transaction. 072 * 073 * This is a lower-level interface to the I2C hardware giving you more 074 * control over each transaction. 075 * 076 * @param dataToSend 077 * Buffer of data to send as part of the transaction. 078 * @param sendSize 079 * Number of bytes to send as part of the transaction. [0..6] 080 * @param dataReceived 081 * Buffer to read data into. 082 * @param receiveSize 083 * Number of bytes to read from the device. [0..7] 084 * @return Transfer Aborted... false for success, true for aborted. 085 */ 086 public synchronized boolean transaction(byte[] dataToSend, int sendSize, 087 byte[] dataReceived, int receiveSize) { 088 boolean aborted = true; 089 090 ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(sendSize); 091 dataToSendBuffer.put(dataToSend); 092 ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(receiveSize); 093 094 aborted = I2CJNI 095 .i2CTransaction((byte) m_port.getValue(), (byte) m_deviceAddress, 096 dataToSendBuffer, (byte) sendSize, 097 dataReceivedBuffer, (byte) receiveSize) != 0; 098 /*if (status.get() == HALUtil.PARAMETER_OUT_OF_RANGE) { 099 if (sendSize > 6) { 100 throw new BoundaryException(BoundaryException.getMessage( 101 sendSize, 0, 6)); 102 } else if (receiveSize > 7) { 103 throw new BoundaryException(BoundaryException.getMessage( 104 receiveSize, 0, 7)); 105 } else { 106 throw new RuntimeException( 107 HALLibrary.PARAMETER_OUT_OF_RANGE_MESSAGE); 108 } 109 } 110 HALUtil.checkStatus(status);*/ 111 if(receiveSize > 0 && dataReceived != null) 112 { 113 dataReceivedBuffer.get(dataReceived); 114 } 115 return aborted; 116 } 117 118 /** 119 * Attempt to address a device on the I2C bus. 120 * 121 * This allows you to figure out if there is a device on the I2C bus that 122 * responds to the address specified in the constructor. 123 * 124 * @return Transfer Aborted... false for success, true for aborted. 125 */ 126 public boolean addressOnly() { 127 return transaction(null, (byte) 0, null, (byte) 0); 128 } 129 130 /** 131 * Execute a write transaction with the device. 132 * 133 * Write a single byte to a register on a device and wait until the 134 * transaction is complete. 135 * 136 * @param registerAddress 137 * The address of the register on the device to be written. 138 * @param data 139 * The byte to write to the register on the device. 140 */ 141 public synchronized boolean write(int registerAddress, int data) { 142 byte[] buffer = new byte[2]; 143 buffer[0] = (byte) registerAddress; 144 buffer[1] = (byte) data; 145 146 ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(2); 147 dataToSendBuffer.put(buffer); 148 149 return I2CJNI.i2CWrite((byte)m_port.getValue(), (byte) m_deviceAddress, dataToSendBuffer, (byte)buffer.length) < 0; 150 } 151 152 /** 153 * Execute a write transaction with the device. 154 * 155 * Write multiple bytes to a register on a device and wait until the 156 * transaction is complete. 157 * 158 * @param data 159 * The data to write to the device. 160 */ 161 public synchronized boolean writeBulk(byte[] data) { 162 ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(data.length); 163 dataToSendBuffer.put(data); 164 165 return I2CJNI.i2CWrite((byte)m_port.getValue(), (byte) m_deviceAddress, dataToSendBuffer, (byte)data.length) < 0; 166 } 167 168 /** 169 * Execute a read transaction with the device. 170 * 171 * Read 1 to 7 bytes from a device. Most I2C devices will auto-increment the 172 * register pointer internally allowing you to read up to 7 consecutive 173 * registers on a device in a single transaction. 174 * 175 * @param registerAddress 176 * The register to read first in the transaction. 177 * @param count 178 * The number of bytes to read in the transaction. [1..7] 179 * @param buffer 180 * A pointer to the array of bytes to store the data read from 181 * the device. 182 * @return Transfer Aborted... false for success, true for aborted. 183 */ 184 public boolean read(int registerAddress, int count, byte[] buffer) { 185 BoundaryException.assertWithinBounds(count, 1, 7); 186 if (buffer == null) { 187 throw new NullPointerException("Null return buffer was given"); 188 } 189 byte[] registerAddressArray = new byte[1]; 190 registerAddressArray[0] = (byte) registerAddress; 191 192 return transaction(registerAddressArray, registerAddressArray.length, 193 buffer, count); 194 } 195 196 /** 197 * Execute a read only transaction with the device. 198 * 199 * Read 1 to 7 bytes from a device. This method does not write any data to prompt 200 * the device. 201 * 202 * @param buffer 203 * A pointer to the array of bytes to store the data read from 204 * the device. 205 * @param count 206 * The number of bytes to read in the transaction. [1..7] 207 * @return Transfer Aborted... false for success, true for aborted. 208 */ 209 public boolean readOnly(byte[] buffer, int count) { 210 BoundaryException.assertWithinBounds(count, 1, 7); 211 if (buffer == null) { 212 throw new NullPointerException("Null return buffer was given"); 213 } 214 215 ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(count); 216 217 int retVal = I2CJNI.i2CRead((byte)m_port.getValue(), (byte) m_deviceAddress, dataReceivedBuffer, (byte)count); 218 dataReceivedBuffer.get(buffer); 219 return retVal < 0; 220 } 221 222 /** 223 * Send a broadcast write to all devices on the I2C bus. 224 * 225 * This is not currently implemented! 226 * 227 * @param registerAddress 228 * The register to write on all devices on the bus. 229 * @param data 230 * The value to write to the devices. 231 */ 232 public void broadcast(int registerAddress, int data) { 233 } 234 235 /** 236 * Verify that a device's registers contain expected values. 237 * 238 * Most devices will have a set of registers that contain a known value that 239 * can be used to identify them. This allows an I2C device driver to easily 240 * verify that the device contains the expected value. 241 * 242 * @pre The device must support and be configured to use register 243 * auto-increment. 244 * 245 * @param registerAddress 246 * The base register to start reading from the device. 247 * @param count 248 * The size of the field to be verified. 249 * @param expected 250 * A buffer containing the values expected from the device. 251 * @return true if the sensor was verified to be connected 252 */ 253 public boolean verifySensor(int registerAddress, int count, byte[] expected) { 254 // TODO: Make use of all 7 read bytes 255 byte[] deviceData = new byte[4]; 256 for (int i = 0, curRegisterAddress = registerAddress; i < count; i += 4, curRegisterAddress += 4) { 257 int toRead = count - i < 4 ? count - i : 4; 258 // Read the chunk of data. Return false if the sensor does not 259 // respond. 260 if (read(curRegisterAddress, toRead, deviceData)) { 261 return false; 262 } 263 264 for (byte j = 0; j < toRead; j++) { 265 if (deviceData[j] != expected[i + j]) { 266 return false; 267 } 268 } 269 } 270 return true; 271 } 272}