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 /*----------------------------------------------------------------------------*/ 007 package edu.wpi.first.wpilibj; 008 009 import edu.wpi.first.wpilibj.communication.UsageReporting; 010 import edu.wpi.first.wpilibj.util.BoundaryException; 011 012 /** 013 * I2C bus interface class. 014 * 015 * This class is intended to be used by sensor (and other I2C device) drivers. 016 * It probably should not be used directly. 017 * 018 * It is constructed by calling DigitalModule::GetI2C() on a DigitalModule object. 019 */ 020 public class I2C extends SensorBase { 021 022 private DigitalModule m_module; 023 private int m_deviceAddress; 024 private boolean m_compatibilityMode; 025 026 /** 027 * Constructor. 028 * 029 * @param module The Digital Module to which the device is connected. 030 * @param deviceAddress The address of the device on the I2C bus. 031 */ 032 public I2C(DigitalModule module, int deviceAddress) { 033 if (module == null) { 034 throw new NullPointerException("Digital Module given was null"); 035 } 036 m_module = module; 037 m_deviceAddress = deviceAddress; 038 m_compatibilityMode = true; 039 040 UsageReporting.report(UsageReporting.kResourceType_I2C, deviceAddress, module.getModuleNumber()-1); 041 } 042 043 /** 044 * Destructor. 045 */ 046 public void free() { 047 } 048 049 /** 050 * Generic transaction. 051 * 052 * This is a lower-level interface to the I2C hardware giving you more control over each transaction. 053 * 054 * @param dataToSend Buffer of data to send as part of the transaction. 055 * @param sendSize Number of bytes to send as part of the transaction. [0..6] 056 * @param dataReceived Buffer to read data into. 057 * @param receiveSize Number of bytes to read from the device. [0..7] 058 * @return Transfer Aborted... false for success, true for aborted. 059 */ 060 public synchronized boolean transaction(byte[] dataToSend, int sendSize, byte[] dataReceived, int receiveSize) { 061 BoundaryException.assertWithinBounds(sendSize, 0, 6); 062 BoundaryException.assertWithinBounds(receiveSize, 0, 7); 063 064 long data = 0; 065 long dataHigh = 0; 066 int i; 067 for (i = 0; i < sendSize && i < 4; i++) { 068 data |= ((long) dataToSend[i] & 0x000000FF) << (8 * i); 069 } 070 for (; i < sendSize; i++) { 071 dataHigh |= ((long) dataToSend[i] & 0x000000FF) << (8 * (i - 4)); 072 } 073 074 boolean aborted = true; 075 076 m_module.m_fpgaDIO.writeI2CConfig_Address(m_deviceAddress); 077 m_module.m_fpgaDIO.writeI2CConfig_BytesToWrite(sendSize); 078 m_module.m_fpgaDIO.writeI2CConfig_BytesToRead(receiveSize); 079 if (sendSize > 0) { 080 m_module.m_fpgaDIO.writeI2CDataToSend(data); 081 } 082 if (sendSize > 4) { 083 m_module.m_fpgaDIO.writeI2CConfig_DataToSendHigh((int) dataHigh); 084 } 085 m_module.m_fpgaDIO.writeI2CConfig_BitwiseHandshake(m_compatibilityMode); 086 byte transaction = m_module.m_fpgaDIO.readI2CStatus_Transaction(); 087 m_module.m_fpgaDIO.strobeI2CStart(); 088 while (transaction == m_module.m_fpgaDIO.readI2CStatus_Transaction()) { 089 Timer.delay(.001); 090 } 091 while (!m_module.m_fpgaDIO.readI2CStatus_Done()) { 092 Timer.delay(.001); 093 } 094 aborted = m_module.m_fpgaDIO.readI2CStatus_Aborted(); 095 if (receiveSize > 0) { 096 data = m_module.m_fpgaDIO.readI2CDataReceived(); 097 } 098 if (receiveSize > 4) { 099 dataHigh = m_module.m_fpgaDIO.readI2CStatus_DataReceivedHigh(); 100 } 101 102 103 for (i = 0; i < receiveSize && i < 4; i++) { 104 dataReceived[i] = (byte) ((data >> (8 * i)) & 0xFF); 105 } 106 for (; i < receiveSize; i++) { 107 dataReceived[i] = (byte) ((dataHigh >> (8 * (i - 4))) & 0xFF); 108 } 109 return aborted; 110 } 111 112 /** 113 * Attempt to address a device on the I2C bus. 114 * 115 * This allows you to figure out if there is a device on the I2C bus that 116 * responds to the address specified in the constructor. 117 * 118 * @return Transfer Aborted... false for success, true for aborted. 119 */ 120 public boolean addressOnly() { 121 return transaction(null, (byte) 0, null, (byte) 0); 122 } 123 124 /** 125 * Execute a write transaction with the device. 126 * 127 * Write a single byte to a register on a device and wait until the 128 * transaction is complete. 129 * 130 * @param registerAddress The address of the register on the device to be written. 131 * @param data The byte to write to the register on the device. 132 */ 133 public synchronized boolean write(int registerAddress, int data) { 134 byte[] buffer = new byte[2]; 135 buffer[0] = (byte) registerAddress; 136 buffer[1] = (byte) data; 137 return transaction(buffer, buffer.length, null, 0); 138 } 139 140 /** 141 * Execute a read transaction with the device. 142 * 143 * Read 1 to 7 bytes from a device. 144 * Most I2C devices will auto-increment the register pointer internally 145 * allowing you to read up to 7 consecutive registers on a device in a 146 * single transaction. 147 * 148 * @param registerAddress The register to read first in the transaction. 149 * @param count The number of bytes to read in the transaction. [1..7] 150 * @param buffer A pointer to the array of bytes to store the data read from the device. 151 * @return Transfer Aborted... false for success, true for aborted. 152 */ 153 public boolean read(int registerAddress, int count, byte[] buffer) { 154 BoundaryException.assertWithinBounds(count, 1, 7); 155 if (buffer == null) { 156 throw new NullPointerException("Null return buffer was given"); 157 } 158 byte[] registerAddressArray = new byte[1]; 159 registerAddressArray[0] = (byte) registerAddress; 160 161 return transaction(registerAddressArray, registerAddressArray.length, buffer, count); 162 } 163 164 /** 165 * Send a broadcast write to all devices on the I2C bus. 166 * 167 * This is not currently implemented! 168 * 169 * @param registerAddress The register to write on all devices on the bus. 170 * @param data The value to write to the devices. 171 */ 172 public void broadcast(int registerAddress, int data) { 173 } 174 175 /** 176 * SetCompatabilityMode 177 * 178 * Enables bitwise clock skewing detection. This will reduce the I2C interface speed, 179 * but will allow you to communicate with devices that skew the clock at abnormal times. 180 * Compatability mode is enabled by default. 181 * 182 * @param enable Enable compatability mode for this sensor or not. 183 */ 184 public void setCompatabilityMode(boolean enable) { 185 m_compatibilityMode = enable; 186 UsageReporting.report(UsageReporting.kResourceType_I2C, m_deviceAddress, m_module.getModuleNumber()-1, "C"); 187 } 188 189 /** 190 * Verify that a device's registers contain expected values. 191 * 192 * Most devices will have a set of registers that contain a known value that 193 * can be used to identify them. This allows an I2C device driver to easily 194 * verify that the device contains the expected value. 195 * 196 * @pre The device must support and be configured to use register auto-increment. 197 * 198 * @param registerAddress The base register to start reading from the device. 199 * @param count The size of the field to be verified. 200 * @param expected A buffer containing the values expected from the device. 201 * @return true if the sensor was verified to be connected 202 */ 203 public boolean verifySensor(int registerAddress, int count, byte[] expected) { 204 // TODO: Make use of all 7 read bytes 205 byte[] deviceData = new byte[4]; 206 for (int i = 0, curRegisterAddress = registerAddress; i < count; i += 4, curRegisterAddress += 4) { 207 int toRead = count - i < 4 ? count - i : 4; 208 // Read the chunk of data. Return false if the sensor does not respond. 209 if (read(curRegisterAddress, toRead, deviceData)) { 210 return false; 211 } 212 213 for (byte j = 0; j < toRead; j++) { 214 if (deviceData[j] != expected[i + j]) { 215 return false; 216 } 217 } 218 } 219 return true; 220 } 221 }