forked from teledyne-e2v/ModuleControl-EM2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathi2c_caiman.cpp
More file actions
400 lines (328 loc) · 11.4 KB
/
i2c_caiman.cpp
File metadata and controls
400 lines (328 loc) · 11.4 KB
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
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include "i2c_caiman.hpp"
/* I2C default delay */
#define I2C_DEFAULT_DELAY 1
/* I2C internal address max length */
#define INT_ADDR_MAX_BYTES 8
/* I2C page max bytes */
#define PAGE_MAX_BYTES 4096
#define GET_I2C_DELAY(delay) ((delay) == 0 ? I2C_DEFAULT_DELAY : (delay))
#define GET_I2C_FLAGS(tenbit, flags) ((tenbit) ? ((flags) | I2C_M_TEN) : (flags))
#define GET_WRITE_SIZE(addr, remain, page_bytes) ((addr) + (remain) > (page_bytes) ? (page_bytes) - (addr) : remain)
static void i2c_delay(unsigned char delay);
/* Command Codes */
#define READ_MEM_CMD 0x0800
#define WRITE_MEM_CMD 0x0802
/* Acknowledge Codes */
#define READ_MEM_ACK 0x0801
#define WRITE_MEM_ACK 0x0803
/* Status Codes */
#define STATUS_SUCCESS 0x0000
#define STATUS_INVALID_CMD 0x0010
#define STATUS_INVALID_ACCESS 0x0021
#define STATUS_OUT_OF_RANGE 0x0022
#define STATUS_INCORRECT_SIZE 0x0024
#define STATUS_INCORRECT_ADDR 0x0025
#define STATUS_BUSY 0x0040
/*
** @brief : Open i2c bus
** #bus_name : i2c bus name such as: /dev/i2c-1
** @return : failed return -1, success return i2c bus fd
*/
int i2c_open(const char *bus_name)
{
int fd;
/* Open i2c-bus devcice */
if ((fd = open(bus_name, O_RDWR)) == -1)
{
return -1;
}
return fd;
}
void i2c_close(int bus)
{
close(bus);
}
/*
** @brief : Initialize I2CDevice with defualt value
** #device : I2CDevice struct
*/
void i2c_init_device(I2CDevice *device)
{
/* 7 bit device address */
device->tenbit = 0;
/* 1ms delay */
device->delay = 1;
/* 8 bytes per page */
device->page_bytes = 8;
/* 1 byte internal(word) address */
device->iaddr_bytes = 1;
}
/*
** @brief : Get I2CDevice struct desc
** #device : I2CDevice struct
** #buf : Description message buffer
** #size : #buf size
** @return : return i2c device desc
*/
char *i2c_get_device_desc(const I2CDevice *device, char *buf, size_t size)
{
memset(buf, 0, size);
snprintf(buf, size, "Device address: 0x%x, tenbit: %s, internal(word) address: %ld bytes, page max %d bytes, delay: %dms",
device->addr, device->tenbit ? "True" : "False", device->iaddr_bytes, device->page_bytes, device->delay);
return buf;
}
/*
** @brief : read #len bytes data from #device #iaddr to #buf
** #device : I2CDevice struct, must call i2c_device_init first
** #iaddr : i2c_device internal address will read data from this address, no address set zero
** #buf : i2c data will read to here
** #len : how many data to read, lenght must less than or equal to buf size
** @return : success return read data length, failed -1
*/
ssize_t i2c_caiman_read(const I2CDevice *device, uint32_t iaddr, void *buf, size_t len, void *status)
{
ssize_t cnt;
unsigned char addr[INT_ADDR_MAX_BYTES];
unsigned char delay = GET_I2C_DELAY(device->delay);
uint32_t regAddrHeader =((uint32_t)len << 16) | (uint32_t)READ_MEM_CMD; // Command code READ_MEM_CMD (0x0800) and length in high 16 bit
uint64_t regAddrRead = ((uint64_t)iaddr << 32) | (uint64_t)regAddrHeader;
unsigned char readbuf[len+4]; // creates read buffer with extra 4 bytes for status code
ssize_t size = sizeof(readbuf);
memset(readbuf, 0, size);
unsigned char statusbuf[4]; // creates status buffer to store the first 4 bytes which is status code
ssize_t size_status = sizeof(statusbuf);
memset(statusbuf, 0, size_status);
/* Set i2c slave address */
if (i2c_select(device->bus, device->addr, device->tenbit) == -1)
{
return -1;
}
/* Convert i2c internal address */
memset(addr, 0, sizeof(addr));
// i2c_iaddr_convert(iaddr, device->iaddr_bytes, addr);
i2c_iaddr_convert(regAddrRead, device->iaddr_bytes , addr);
/* Write internal address to device */
if (write(device->bus, addr, device->iaddr_bytes) != device->iaddr_bytes)
{
perror("Write i2c internal address error");
return -1;
}
/* Wait a while */
i2c_delay(delay);
/* Read count bytes data from int_addr specify address */
if ((cnt = read(device->bus, readbuf, size)) == -1)
{
perror("Read i2c data error");
return -1;
}
/* Print full buffer for debug */
// printf("Read Buffer=");
// for (int i = 0; i < size; i++) { printf("%02x ", readbuf[i]); }
// printf("\n");
/* Check status codes */
memcpy(statusbuf, readbuf, size_status); // copy the first 4 bytes which is status code to status buffer
uint16_t ack = (statusbuf[1] << 8) | statusbuf[0];
uint16_t code = (statusbuf[3] << 8) | statusbuf[2];
memcpy(status, statusbuf, size_status); //return status code to caller
if(ack != READ_MEM_ACK)
{
fprintf(stderr, "I2C read error, aknowledge code: 0x%04X\n", ack);
return -1;
}
if(code != STATUS_SUCCESS)
{
fprintf(stderr, "I2C read error, status code: 0x%04X\n", code);
return -1;
}
/* Copy data to buf */
memcpy(buf, readbuf + 4, len); // skip the first 4 bytes which is status code
return cnt-4;
}
/*
** @brief : write #buf data to i2c #device #iaddr address
** #device : I2CDevice struct, must call i2c_device_init first
** #iaddr : i2c_device internal address, no address set zero
** #buf : data will write to i2c device
** #len : buf data length without '/0'
** @return : success return write data length, failed -1
*/
ssize_t i2c_caiman_write(const I2CDevice *device, uint32_t iaddr, const void *buf, size_t len)
{
ssize_t remain = len;
size_t cnt = 0;
const unsigned char *buffer = (unsigned char *)buf;
unsigned char delay = GET_I2C_DELAY(device->delay);
// unsigned char tmp_buf[PAGE_MAX_BYTES + INT_ADDR_MAX_BYTES];
unsigned char addr[INT_ADDR_MAX_BYTES];
uint32_t regAddrHeader =((uint32_t)len << 16) | (uint32_t)WRITE_MEM_CMD; // Command code WRITE_MEM_CMD (0x0802) and length in high 16 bit
uint64_t regAddrWrite = ((uint64_t)iaddr << 32) | (uint64_t)regAddrHeader;
// unsigned char databuf[len]; // creates write buffer for
// ssize_t size = sizeof(databuf);
// memset(databuf, 0, size);
/* Set i2c slave address */
if (i2c_select(device->bus, device->addr, device->tenbit) == -1)
{
return -1;
}
/* Convert i2c internal address */
// memset(addr, 0, sizeof(addr));
// // i2c_iaddr_convert(iaddr, device->iaddr_bytes, addr);
// i2c_iaddr_convert(regAddrWrite, device->iaddr_bytes , addr);
size_t size = INT_ADDR_MAX_BYTES + len; // total size is address length and write buffer length
unsigned char writebuf[size];
memset(writebuf, 0, sizeof(writebuf));
/* Convert i2c internal address */
i2c_iaddr_convert(regAddrWrite, device->iaddr_bytes, writebuf);
/* Copy data to tmp_buf */
memcpy(writebuf + INT_ADDR_MAX_BYTES, buffer, size);
/* Print full buffer for debug */
printf("Write Buffer=");
for (int i = 0; i < size; i++) { printf("%02x ", writebuf[i]); }
printf("\n");
/* Write to buf content to i2c device length is address length and write buffer length */
if ((unsigned int)(write(device->bus, writebuf, size)) != size)
{
perror("I2C write error:");
return -1;
}
return 0;
}
ssize_t i2c_caiman_status(const I2CDevice *device, void *status, size_t len, bool print)
{
ssize_t cnt;
unsigned char readbuf[len]; // creates read buffer with extra 4 bytes for status code
ssize_t size = sizeof(readbuf);
memset(readbuf, 0, size);
unsigned char statusbuf[4]; // creates status buffer to store the first 4 bytes which is status code
ssize_t size_status = sizeof(statusbuf);
memset(statusbuf, 0, size_status);
/* Set i2c slave address */
if (i2c_select(device->bus, device->addr, device->tenbit) == -1)
{
return -1;
}
/* Read count bytes data from int_addr specify address */
if ((cnt = read(device->bus, readbuf, size)) == -1)
{
perror("Read i2c data error");
return -1;
}
/* Check status codes */
memcpy(statusbuf, readbuf, size_status); // copy the first 4 bytes which is status code to status buffer
uint16_t ack = (statusbuf[1] << 8) | statusbuf[0];
uint16_t code = (statusbuf[3] << 8) | statusbuf[2];
if(print)
{
fprintf(stderr, "I2C read aknowledge code: 0x%04X ", ack);
switch (ack)
{
case READ_MEM_ACK:
fprintf(stderr, "(READ_MEM_ACK) ");
break;
case WRITE_MEM_ACK:
fprintf(stderr, "(WRITE_MEM_ACK) ");
break;
default:
fprintf(stderr, "(Unknown ACK) ");
}
fprintf(stderr, "\n");
fprintf(stderr, "I2C read status code: 0x%04X", code);
switch (code)
{
case STATUS_SUCCESS:
fprintf(stderr, "(STATUS_SUCCESS) ");
break;
case STATUS_INVALID_CMD:
fprintf(stderr, "(STATUS_INVALID_CMD) ");
break;
case STATUS_INVALID_ACCESS:
fprintf(stderr, "(STATUS_INVALID_ACCESS) ");
break;
case STATUS_OUT_OF_RANGE:
fprintf(stderr, "(STATUS_OUT_OF_RANGE) ");
break;
case STATUS_INCORRECT_SIZE:
fprintf(stderr, "(STATUS_INCORRECT_SIZE) ");
break;
case STATUS_INCORRECT_ADDR:
fprintf(stderr, "(STATUS_INCORRECT_ADDR) ");
break;
case STATUS_BUSY:
fprintf(stderr, "(STATUS_BUSY) ");
break;
default:
fprintf(stderr, "(Unknown Status) ");
}
fprintf(stderr, "\n");
}
/* Copy data to buf */
memcpy(status, readbuf, len);
return cnt;
}
/*
** @brief : i2c internal address convert
** #iaddr : i2c device internal address
** #len : i2c device internal address length
** #addr : save convert address
*/
void i2c_iaddr_convert(uint64_t iaddr, unsigned int len, unsigned char *addr)
{
union
{
uint64_t iaddr;
unsigned char caddr[INT_ADDR_MAX_BYTES];
} convert;
/* I2C internal address order is big-endian, same with network order */
// convert.iaddr = htonl(iaddr); //htonl only convert 32 bit, but iaddr may be 64 bit, so we need to convert it by ourselves
for (unsigned int k = 0; k < INT_ADDR_MAX_BYTES; ++k)
{
// convert.caddr[INT_ADDR_MAX_BYTES - 1 - k] = (iaddr >> (8 * k)) & 0xFF;
convert.caddr[k] = (iaddr >> (8 * k)) & 0xFF; // finally don't need to swap the bytes
}
/* Copy address to addr buffer */
int i = len - 1;
int j = INT_ADDR_MAX_BYTES - 1;
while (i >= 0 && j >= 0)
{
addr[i--] = convert.caddr[j--];
}
}
/*
** @brief : Select i2c address @i2c bus
** #bus : i2c bus fd
** #dev_addr : i2c device address
** #tenbit : i2c device address is tenbit
** #return : success return 0, failed return -1
*/
int i2c_select(int bus, unsigned long dev_addr, unsigned long tenbit)
{
/* Set i2c device address bit */
if (ioctl(bus, I2C_TENBIT, tenbit))
{
perror("Set I2C_TENBIT failed");
return -1;
}
/* Set i2c device as slave ans set it address */
if (ioctl(bus, I2C_SLAVE, dev_addr))
{
perror("Set i2c device address failed");
return -1;
}
return 0;
}
/*
** @brief : i2c delay
** #msec : milliscond to be delay
*/
static void i2c_delay(unsigned char msec)
{
usleep(msec * 1e3);
}