-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathWire.cpp
364 lines (289 loc) · 9.08 KB
/
Wire.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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
/*
SPI.cpp - LinuxDuino I2C library
Copyright (c) 2016 Jorge Garza <[email protected]>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include "Wire.h"
// All functions of unistd.h must be called like this: unistd::the_function()
namespace unistd {
#include <unistd.h>
};
/////////////////////////////////////////////
// WireLinux class (I2C) //
////////////////////////////////////////////
char I2C_DRIVER_NAME[128] = ""; // "" means search for any I2C device
//// Private methods ///
uint8_t WireLinux::rxBuffer[BUFFER_LENGTH];
uint8_t WireLinux::rxBufferIndex = 0;
uint8_t WireLinux::rxBufferLength = 0;
uint8_t WireLinux::txBuffer[BUFFER_LENGTH];
uint8_t WireLinux::txBufferIndex = 0;
uint8_t WireLinux::txBufferLength = 0;
uint8_t WireLinux::transmitting = 0;
int WireLinux::i2c_write_bytes(int file, uint8_t *txBuff, size_t numBytes)
{
int bytes_written = 0;
if (numBytes == 0) {
return bytes_written;
} else {
bytes_written = unistd::write(file, txBuff, numBytes);
if ( bytes_written < 0) {
// errno == 5 (Input/Output error) means I2C cables
// may not be connected properly.
// Make noise about everything else except errno == 5.
if (errno != 5 ) {
fprintf(stderr, "%s(): i2c write error: %s \n",
__func__, strerror (errno));
}
}
}
return bytes_written;
}
int WireLinux::i2c_read_bytes(int file, uint8_t *rxBuff, size_t numBytes)
{
int bytes_read = 0;
if (numBytes == 0) {
return bytes_read;
} else {
bytes_read = unistd::read(file, rxBuff, numBytes);
if ( bytes_read < 0) {
// errno == 5 (Input/Output error) means I2C cables
// may not be connected properly.
// Make noise about everything else except errno == 5.
if (errno != 5 ) {
fprintf(stderr, "%s(): i2c read error: %s \n",
__func__, strerror (errno));
}
}
}
return bytes_read;
}
//// Public methods ////
//Constructor
WireLinux::WireLinux()
{
fd = -1;
}
void WireLinux::begin()
{
begin(I2C_DRIVER_NAME);
}
// Initialize the Wire library
void WireLinux::begin(const char *i2cDeviceName)
{
FILE *fn, *fp;
char path[1024];
char *filename;
char *i2cDevice;
char *tmpi2cDevice = NULL;
// If I2C device name is empty, then search for the first available i2c in /dev/
// else try to open the given device name
if (strcmp(i2cDeviceName, "") == 0) {
// Process the command below to search for i2c-1 device driver excistance
fn = popen("/bin/ls /dev/ | /bin/grep i2c-1" , "r");
if (fn == NULL) {
fprintf(stderr, "%s(): failed to run command "
"\"/bin/ls /dev/ | /bin/grep i2c-1\"\n",__func__);
exit(1);
}
// Process the command below to search for i2c-x devices drivers excistance,
// where x = 0, 1, etc
fp = popen("/bin/ls /dev/ | /bin/grep i2c-" , "r");
if (fp == NULL) {
fprintf(stderr, "%s(): failed to run command "
"\"/bin/ls /dev/ | /bin/grep i2c-\"\n",__func__);
exit(1);
}
// If i2c-1 exists (RPI main i2c) then set it to open,
// else set any other existant i2c-x like i2c-0 for old RPI revisions
if (fgets(path, sizeof(path)-1, fn) != NULL) {
// Set i2c-1 device
asprintf(&tmpi2cDevice, "i2c-1");
} else {
// Set i2c-x device
while (fgets(path, sizeof(path)-1, fp) != NULL) {
asprintf(&tmpi2cDevice, "%s", path);
break;
}
}
// If no I2C device driver is enabled or installed then exit.
if (tmpi2cDevice == NULL) {
fprintf(stderr, "%s(): filed to locate any \"i2c-x\" device driver in /dev/. "
"please install or enable a i2c interface in your board \n",__func__);
exit(1);
}
// Set i2c-x device found to /dev/i2c-x fromat
asprintf(&i2cDevice, "/dev/%s", tmpi2cDevice);
} else {
// Set user given i2c device name
asprintf(&i2cDevice, "%s", i2cDeviceName);
}
// Open i2c device name (e.g /dev/i2c-x)
asprintf(&filename,"%s", i2cDevice);
fd = open(filename, O_RDWR);
if (fd < 0) {
fprintf(stderr, "%s(): error openning I2C device %s: %s\n",
__func__, filename, strerror (errno));
exit(1);
}
rxBufferIndex = 0;
rxBufferLength = 0;
txBufferIndex = 0;
txBufferLength = 0;
}
void WireLinux::end()
{
unistd::close(fd);
fd = -1;
}
// Initialize the Wire library with a slave address
/*
void WireLinux::begin(uint8_t address)
{
// TODO, Still reading documentation for linux new I2c slave support
}
*/
uint8_t WireLinux::requestFrom(uint8_t address, uint8_t quantity)
{
if (fd < 0) {
fprintf(stderr,
"%s(): initialize I2C first with Wire.begin() \n",
__func__);
exit(1);
}
if (ioctl(fd, I2C_SLAVE, address) < 0) {
fprintf(stderr, "%s(): ioctl error: %s\n",
__func__, strerror (errno));
exit(1);
}
// clamp to buffer length
if(quantity > BUFFER_LENGTH){
quantity = BUFFER_LENGTH;
}
// perform blocking read into buffer
uint8_t read = i2c_read_bytes(fd, rxBuffer, quantity);
// set rx buffer iterator vars
rxBufferIndex = 0;
rxBufferLength = read;
return read;
}
//Begin a transmission to the I2C slave device with the given address
void WireLinux::beginTransmission(uint8_t address)
{
if (fd < 0) {
fprintf(stderr,
"%s(): initialize I2C first with Wire.begin() \n", __func__);
exit(1);
}
if (ioctl(fd, I2C_SLAVE, address) < 0) {
fprintf(stderr, "%s(): ioctl error: %s\n", __func__, strerror (errno));
exit(1);
}
// indicate that we are transmitting
transmitting = 1;
// reset tx buffer iterator vars
txBufferIndex = 0;
txBufferLength = 0;
}
// Writes data to the I2C, returns bytes written.
size_t WireLinux::write(uint8_t data)
{
if (transmitting) {
// in master transmitter mode
// don't bother if buffer is full
if (txBufferLength >= BUFFER_LENGTH) {
return 0;
}
// put byte in tx buffer
txBuffer[txBufferIndex] = data;
++txBufferIndex;
// update amount in buffer
txBufferLength = txBufferIndex;
} else {
// in slave send mode
// reply to master
i2c_write_bytes(fd, &data, 1);
}
return 1;
}
// Writes data to the I2C in form of string, returns bytes written.
size_t WireLinux::write(const char *str)
{
size_t byteswritten = 0;
for (size_t i = 0; i < strlen(str) ; i++) {
// If transmitting data >= BUFFER_LENGTH, then break.
if (write(str[i]) == 0) {
break;
}
byteswritten++;
}
return byteswritten;
}
// Writes data to the I2C, returns bytes written.
size_t WireLinux::write(uint8_t *data, size_t quantity)
{
size_t byteswritten = 0;
if (transmitting) {
// in master transmitter mode
for(size_t i = 0; i < quantity; ++i){
write(data[i]);
}
byteswritten = quantity;
} else {
// in slave send mode
// reply to master
byteswritten = i2c_write_bytes(fd, data, quantity);
}
return byteswritten;
}
int WireLinux::available(void)
{
return rxBufferLength - rxBufferIndex;
}
int WireLinux::read(void)
{
int value = -1;
// get each successive byte on each call
if (rxBufferIndex < rxBufferLength) {
value = rxBuffer[rxBufferIndex];
++rxBufferIndex;
}
return value;
}
uint8_t WireLinux::endTransmission()
{
uint8_t ret;
if (fd < 0) {
fprintf(stderr, "%s(): initialize I2C first with Wire.begin() \n", __func__);
exit(1);
}
// Transmit Data
ret = i2c_write_bytes(fd, txBuffer, txBufferLength);
// reset tx buffer iterator vars
txBufferIndex = 0;
txBufferLength = 0;
// indicate that we are done transmitting
transmitting = 0;
return ret;
}
WireLinux Wire = WireLinux();