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    }