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 }