jerrym | f157933 | 2013-02-07 01:56:28 +0000 | [diff] [blame^] | 1 | /*----------------------------------------------------------------------------*/
|
| 2 | /* Copyright (c) FIRST 2008. All Rights Reserved. */
|
| 3 | /* Open Source Software - may be modified and shared by FRC teams. The code */
|
| 4 | /* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
|
| 5 | /*----------------------------------------------------------------------------*/
|
| 6 |
|
| 7 | #include "I2C.h"
|
| 8 | #include "DigitalModule.h"
|
| 9 | #include "NetworkCommunication/UsageReporting.h"
|
| 10 | #include "Synchronized.h"
|
| 11 | #include "WPIErrors.h"
|
| 12 | #include <taskLib.h>
|
| 13 |
|
| 14 | SEM_ID I2C::m_semaphore = NULL;
|
| 15 | UINT32 I2C::m_objCount = 0;
|
| 16 |
|
| 17 | /**
|
| 18 | * Constructor.
|
| 19 | *
|
| 20 | * @param module The Digital Module to which the device is conneted.
|
| 21 | * @param deviceAddress The address of the device on the I2C bus.
|
| 22 | */
|
| 23 | I2C::I2C(DigitalModule *module, UINT8 deviceAddress)
|
| 24 | : m_module (module)
|
| 25 | , m_deviceAddress (deviceAddress)
|
| 26 | , m_compatibilityMode (false)
|
| 27 | {
|
| 28 | if (m_semaphore == NULL)
|
| 29 | {
|
| 30 | m_semaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
|
| 31 | }
|
| 32 | m_objCount++;
|
| 33 |
|
| 34 | nUsageReporting::report(nUsageReporting::kResourceType_I2C, deviceAddress, module->GetNumber() - 1);
|
| 35 | }
|
| 36 |
|
| 37 | /**
|
| 38 | * Destructor.
|
| 39 | */
|
| 40 | I2C::~I2C()
|
| 41 | {
|
| 42 | m_objCount--;
|
| 43 | if (m_objCount <= 0)
|
| 44 | {
|
| 45 | semDelete(m_semaphore);
|
| 46 | m_semaphore = NULL;
|
| 47 | }
|
| 48 | }
|
| 49 |
|
| 50 | /**
|
| 51 | * Generic transaction.
|
| 52 | *
|
| 53 | * This is a lower-level interface to the I2C hardware giving you more control over each transaction.
|
| 54 | *
|
| 55 | * @param dataToSend Buffer of data to send as part of the transaction.
|
| 56 | * @param sendSize Number of bytes to send as part of the transaction. [0..6]
|
| 57 | * @param dataReceived Buffer to read data into.
|
| 58 | * @param receiveSize Number of byted to read from the device. [0..7]
|
| 59 | * @return Transfer Aborted... false for success, true for aborted.
|
| 60 | */
|
| 61 | bool I2C::Transaction(UINT8 *dataToSend, UINT8 sendSize, UINT8 *dataReceived, UINT8 receiveSize)
|
| 62 | {
|
| 63 | if (sendSize > 6)
|
| 64 | {
|
| 65 | wpi_setWPIErrorWithContext(ParameterOutOfRange, "sendSize");
|
| 66 | return true;
|
| 67 | }
|
| 68 | if (receiveSize > 7)
|
| 69 | {
|
| 70 | wpi_setWPIErrorWithContext(ParameterOutOfRange, "receiveSize");
|
| 71 | return true;
|
| 72 | }
|
| 73 |
|
| 74 | UINT32 data=0;
|
| 75 | UINT32 dataHigh=0;
|
| 76 | UINT32 i;
|
| 77 | for(i=0; i<sendSize && i<sizeof(data); i++)
|
| 78 | {
|
| 79 | data |= (UINT32)dataToSend[i] << (8*i);
|
| 80 | }
|
| 81 | for(; i<sendSize; i++)
|
| 82 | {
|
| 83 | dataHigh |= (UINT32)dataToSend[i] << (8*(i-sizeof(data)));
|
| 84 | }
|
| 85 |
|
| 86 | bool aborted = true;
|
| 87 | tRioStatusCode localStatus = NiFpga_Status_Success;
|
| 88 | {
|
| 89 | Synchronized sync(m_semaphore);
|
| 90 | m_module->m_fpgaDIO->writeI2CConfig_Address(m_deviceAddress, &localStatus);
|
| 91 | m_module->m_fpgaDIO->writeI2CConfig_BytesToWrite(sendSize, &localStatus);
|
| 92 | m_module->m_fpgaDIO->writeI2CConfig_BytesToRead(receiveSize, &localStatus);
|
| 93 | if (sendSize > 0) m_module->m_fpgaDIO->writeI2CDataToSend(data, &localStatus);
|
| 94 | if (sendSize > sizeof(data)) m_module->m_fpgaDIO->writeI2CConfig_DataToSendHigh(dataHigh, &localStatus);
|
| 95 | m_module->m_fpgaDIO->writeI2CConfig_BitwiseHandshake(m_compatibilityMode, &localStatus);
|
| 96 | UINT8 transaction = m_module->m_fpgaDIO->readI2CStatus_Transaction(&localStatus);
|
| 97 | m_module->m_fpgaDIO->strobeI2CStart(&localStatus);
|
| 98 | while(transaction == m_module->m_fpgaDIO->readI2CStatus_Transaction(&localStatus)) taskDelay(1);
|
| 99 | while(!m_module->m_fpgaDIO->readI2CStatus_Done(&localStatus)) taskDelay(1);
|
| 100 | aborted = m_module->m_fpgaDIO->readI2CStatus_Aborted(&localStatus);
|
| 101 | if (receiveSize > 0) data = m_module->m_fpgaDIO->readI2CDataReceived(&localStatus);
|
| 102 | if (receiveSize > sizeof(data)) dataHigh = m_module->m_fpgaDIO->readI2CStatus_DataReceivedHigh(&localStatus);
|
| 103 | }
|
| 104 | wpi_setError(localStatus);
|
| 105 |
|
| 106 | for(i=0; i<receiveSize && i<sizeof(data); i++)
|
| 107 | {
|
| 108 | dataReceived[i] = (data >> (8*i)) & 0xFF;
|
| 109 | }
|
| 110 | for(; i<receiveSize; i++)
|
| 111 | {
|
| 112 | dataReceived[i] = (dataHigh >> (8*(i-sizeof(data)))) & 0xFF;
|
| 113 | }
|
| 114 | return aborted;
|
| 115 | }
|
| 116 |
|
| 117 | /**
|
| 118 | * Attempt to address a device on the I2C bus.
|
| 119 | *
|
| 120 | * This allows you to figure out if there is a device on the I2C bus that
|
| 121 | * responds to the address specified in the constructor.
|
| 122 | *
|
| 123 | * @return Transfer Aborted... false for success, true for aborted.
|
| 124 | */
|
| 125 | bool I2C::AddressOnly()
|
| 126 | {
|
| 127 | return Transaction(NULL, 0, NULL, 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 The address of the register on the device to be written.
|
| 137 | * @param data The byte to write to the register on the device.
|
| 138 | * @return Transfer Aborted... false for success, true for aborted.
|
| 139 | */
|
| 140 | bool I2C::Write(UINT8 registerAddress, UINT8 data)
|
| 141 | {
|
| 142 | UINT8 buffer[2];
|
| 143 | buffer[0] = registerAddress;
|
| 144 | buffer[1] = data;
|
| 145 | return Transaction(buffer, sizeof(buffer), NULL, 0);
|
| 146 | }
|
| 147 |
|
| 148 | /**
|
| 149 | * Execute a read transaction with the device.
|
| 150 | *
|
| 151 | * Read 1 to 7 bytes from a device.
|
| 152 | * Most I2C devices will auto-increment the register pointer internally
|
| 153 | * allowing you to read up to 7 consecutive registers on a device in a
|
| 154 | * single transaction.
|
| 155 | *
|
| 156 | * @param registerAddress The register to read first in the transaction.
|
| 157 | * @param count The number of bytes to read in the transaction. [1..7]
|
| 158 | * @param buffer A pointer to the array of bytes to store the data read from the device.
|
| 159 | * @return Transfer Aborted... false for success, true for aborted.
|
| 160 | */
|
| 161 | bool I2C::Read(UINT8 registerAddress, UINT8 count, UINT8 *buffer)
|
| 162 | {
|
| 163 | if (count < 1 || count > 7)
|
| 164 | {
|
| 165 | wpi_setWPIErrorWithContext(ParameterOutOfRange, "count");
|
| 166 | return true;
|
| 167 | }
|
| 168 | if (buffer == NULL)
|
| 169 | {
|
| 170 | wpi_setWPIErrorWithContext(NullParameter, "buffer");
|
| 171 | return true;
|
| 172 | }
|
| 173 |
|
| 174 | return Transaction(®isterAddress, sizeof(registerAddress), buffer, count);
|
| 175 | }
|
| 176 |
|
| 177 | /**
|
| 178 | * Send a broadcast write to all devices on the I2C bus.
|
| 179 | *
|
| 180 | * This is not currently implemented!
|
| 181 | *
|
| 182 | * @param registerAddress The register to write on all devices on the bus.
|
| 183 | * @param data The value to write to the devices.
|
| 184 | */
|
| 185 | void I2C::Broadcast(UINT8 registerAddress, UINT8 data)
|
| 186 | {
|
| 187 | }
|
| 188 |
|
| 189 | /**
|
| 190 | * SetCompatibilityMode
|
| 191 | *
|
| 192 | * Enables bitwise clock skewing detection. This will reduce the I2C interface speed,
|
| 193 | * but will allow you to communicate with devices that skew the clock at abnormal times.
|
| 194 | *
|
| 195 | * @param enable Enable compatibility mode for this sensor or not.
|
| 196 | */
|
| 197 | void I2C::SetCompatibilityMode(bool enable)
|
| 198 | {
|
| 199 | m_compatibilityMode = enable;
|
| 200 |
|
| 201 | const char *cm = NULL;
|
| 202 | if (m_compatibilityMode) cm = "C";
|
| 203 | nUsageReporting::report(nUsageReporting::kResourceType_I2C, m_deviceAddress, m_module->GetNumber() - 1, cm);
|
| 204 | }
|
| 205 |
|
| 206 | /**
|
| 207 | * Verify that a device's registers contain expected values.
|
| 208 | *
|
| 209 | * Most devices will have a set of registers that contain a known value that
|
| 210 | * can be used to identify them. This allows an I2C device driver to easily
|
| 211 | * verify that the device contains the expected value.
|
| 212 | *
|
| 213 | * @pre The device must support and be configured to use register auto-increment.
|
| 214 | *
|
| 215 | * @param registerAddress The base register to start reading from the device.
|
| 216 | * @param count The size of the field to be verified.
|
| 217 | * @param expected A buffer containing the values expected from the device.
|
| 218 | */
|
| 219 | bool I2C::VerifySensor(UINT8 registerAddress, UINT8 count, const UINT8 *expected)
|
| 220 | {
|
| 221 | // TODO: Make use of all 7 read bytes
|
| 222 | UINT8 deviceData[4];
|
| 223 | for (UINT8 i=0, curRegisterAddress = registerAddress; i < count; i+=4, curRegisterAddress+=4)
|
| 224 | {
|
| 225 | UINT8 toRead = count - i < 4 ? count - i : 4;
|
| 226 | // Read the chunk of data. Return false if the sensor does not respond.
|
| 227 | if (Read(curRegisterAddress, toRead, deviceData)) return false;
|
| 228 |
|
| 229 | for (UINT8 j=0; j<toRead; j++)
|
| 230 | {
|
| 231 | if(deviceData[j] != expected[i + j]) return false;
|
| 232 | }
|
| 233 | }
|
| 234 | return true;
|
| 235 | }
|
| 236 |
|