-
Notifications
You must be signed in to change notification settings - Fork 0
/
I2C.cpp
204 lines (188 loc) · 6.38 KB
/
I2C.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2008. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#include "I2C.h"
#include "DigitalModule.h"
#include "Synchronized.h"
#include "Utility.h"
#include "WPIStatus.h"
SEM_ID I2C::m_semaphore = NULL;
/**
* Constructor.
*
* @param module The Digital Module to which the device is conneted.
* @param deviceAddress The address of the device on the I2C bus.
*/
I2C::I2C(DigitalModule *module, UINT8 deviceAddress)
: m_module (module)
, m_deviceAddress (deviceAddress)
{
if (m_semaphore == NULL)
{
m_semaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
}
}
/**
* Destructor.
*/
I2C::~I2C()
{
}
/**
* Generic transaction.
*
* This is a lower-level interface to the I2C hardware giving you more control over each transaction.
*
* @param dataToSend Buffer of data to send as part of the transaction.
* @param sendSize Number of bytes to send as part of the transaction. [0..6]
* @param dataReceived Buffer to read data into.
* @param receiveSize Number of byted to read from the device. [0..7]
* @return Transfer Aborted... false for success, true for aborted.
*/
bool I2C::Transaction(UINT8 *dataToSend, UINT8 sendSize, UINT8 *dataReceived, UINT8 receiveSize)
{
if (sendSize > 6)
{
wpi_fatal(I2CByteCountError);
return true;
}
if (receiveSize > 7)
{
wpi_fatal(I2CByteCountError);
return true;
}
UINT32 data=0;
UINT32 dataHigh=0;
UINT32 i;
for(i=0; i<sendSize && i<sizeof(data); i++)
{
data |= (UINT32)dataToSend[i] << (8*i);
}
for(; i<sendSize; i++)
{
dataHigh |= (UINT32)dataToSend[i] << (8*(i-sizeof(data)));
}
bool aborted = true;
{
Synchronized sync(m_semaphore);
m_module->m_fpgaDIO->writeI2CConfig_Address(m_deviceAddress, &status);
m_module->m_fpgaDIO->writeI2CConfig_BytesToWrite(sendSize, &status);
m_module->m_fpgaDIO->writeI2CConfig_BytesToRead(receiveSize, &status);
if (sendSize > 0) m_module->m_fpgaDIO->writeI2CDataToSend(data, &status);
if (sendSize > sizeof(data)) m_module->m_fpgaDIO->writeI2CConfig_DataToSendHigh(dataHigh, &status);
UINT8 transaction = m_module->m_fpgaDIO->readI2CStatus_Transaction(&status);
m_module->m_fpgaDIO->strobeI2CStart(&status);
while(transaction == m_module->m_fpgaDIO->readI2CStatus_Transaction(&status)) taskDelay(1);
while(!m_module->m_fpgaDIO->readI2CStatus_Done(&status)) taskDelay(1);
aborted = m_module->m_fpgaDIO->readI2CStatus_Aborted(&status);
if (receiveSize > 0) data = m_module->m_fpgaDIO->readI2CDataReceived(&status);
if (receiveSize > sizeof(data)) dataHigh = m_module->m_fpgaDIO->readI2CStatus_DataReceivedHigh(&status);
}
for(i=0; i<receiveSize && i<sizeof(data); i++)
{
dataReceived[i] = (data >> (8*i)) & 0xFF;
}
for(; i<receiveSize; i++)
{
dataReceived[i] = (dataHigh >> (8*(i-sizeof(data)))) & 0xFF;
}
return aborted;
}
/**
* Attempt to address a device on the I2C bus.
*
* This allows you to figure out if there is a device on the I2C bus that
* responds to the address specified in the constructor.
*
* @return Transfer Aborted... false for success, true for aborted.
*/
bool I2C::AddressOnly()
{
return Transaction(NULL, 0, NULL, 0);
}
/**
* Execute a write transaction with the device.
*
* Write a single byte to a register on a device and wait until the
* transaction is complete.
*
* @param registerAddress The address of the register on the device to be written.
* @param data The byte to write to the register on the device.
* @return Transfer Aborted... false for success, true for aborted.
*/
bool I2C::Write(UINT8 registerAddress, UINT8 data)
{
UINT8 buffer[2];
buffer[0] = registerAddress;
buffer[1] = data;
return Transaction(buffer, sizeof(buffer), NULL, 0);
}
/**
* Execute a read transaction with the device.
*
* Read 1 to 7 bytes from a device.
* Most I2C devices will auto-increment the register pointer internally
* allowing you to read up to 7 consecutive registers on a device in a
* single transaction.
*
* @param registerAddress The register to read first in the transaction.
* @param count The number of bytes to read in the transaction. [1..7]
* @param buffer A pointer to the array of bytes to store the data read from the device.
* @return Transfer Aborted... false for success, true for aborted.
*/
bool I2C::Read(UINT8 registerAddress, UINT8 count, UINT8 *buffer)
{
if (count < 1 || count > 7)
{
wpi_fatal(I2CByteCountError);
return true;
}
if (buffer == NULL)
{
wpi_fatal(NullParameter);
return true;
}
return Transaction(®isterAddress, sizeof(registerAddress), buffer, count);
}
/**
* Send a broadcast write to all devices on the I2C bus.
*
* This is not currently implemented!
*
* @param registerAddress The register to write on all devices on the bus.
* @param data The value to write to the devices.
*/
void I2C::Broadcast(UINT8 registerAddress, UINT8 data)
{
}
/**
* Verify that a device's registers contain expected values.
*
* Most devices will have a set of registers that contain a known value that
* can be used to identify them. This allows an I2C device driver to easily
* verify that the device contains the expected value.
*
* @pre The device must support and be configured to use register auto-increment.
*
* @param registerAddress The base register to start reading from the device.
* @param count The size of the field to be verified.
* @param expected A buffer containing the values expected from the device.
*/
bool I2C::VerifySensor(UINT8 registerAddress, UINT8 count, const UINT8 *expected)
{
// TODO: Make use of all 7 read bytes
UINT8 deviceData[4];
for (UINT8 i=0, curRegisterAddress = registerAddress; i < count; i+=4, curRegisterAddress+=4)
{
UINT8 toRead = count - i < 4 ? count - i : 4;
// Read the chunk of data. Return false if the sensor does not respond.
if (Read(curRegisterAddress, toRead, deviceData)) return false;
for (UINT8 j=0; j<toRead; j++)
{
if(deviceData[j] != expected[i + j]) return false;
}
}
return true;
}