-
-
Notifications
You must be signed in to change notification settings - Fork 14
/
hal_i2c.c
executable file
·570 lines (471 loc) · 16.7 KB
/
hal_i2c.c
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
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
/**************************************************************************************************
Filename: hal_i2c.c
Revised: $Date: 2012-09-21 06:30:38 -0700 (Fri, 21 Sep 2012) $
Revision: $Revision: 31581 $
Description: This module defines the HAL I2C API for the CC2541ST. It
implements the I2C master.
Copyright 2012 Texas Instruments Incorporated. All rights reserved.
IMPORTANT: Your use of this Software is limited to those specific rights
granted under the terms of a software license agreement between the user
who downloaded the software, his/her employer (which must be your employer)
and Texas Instruments Incorporated (the "License"). You may not use this
Software unless you agree to abide by the terms of the License. The License
limits your use, and you acknowledge, that the Software may not be modified,
copied or distributed unless embedded on a Texas Instruments microcontroller
or used solely and exclusively in conjunction with a Texas Instruments radio
frequency transceiver, which is integrated into your product. Other than for
the foregoing purpose, you may not use, reproduce, copy, prepare derivative
works of, modify, distribute, perform, display or sell this Software and/or
its documentation for any purpose.
YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
PROVIDED �AS IS� WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
(INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
Should you have any questions regarding your right to use this Software,
contact Texas Instruments Incorporated at www.TI.com.
**************************************************************************************************/
#include "hal_i2c.h"
#include "Debug.h"
#include "ioCC2530.h"
#include "zcomdef.h"
#include "utils.h"
#define STATIC static
#if !defined HAL_I2C_RETRY_CNT
#define HAL_I2C_RETRY_CNT 3
#endif
// the default cofiguration below uses P0.6 for SDA and P0.5 for SCL.
// change these as needed.
#ifndef OCM_CLK_PORT
#define OCM_CLK_PORT 0
#endif
#ifndef OCM_DATA_PORT
#define OCM_DATA_PORT 0
#endif
#ifndef OCM_CLK_PIN
#define OCM_CLK_PIN 5
#endif
#ifndef OCM_DATA_PIN
#define OCM_DATA_PIN 6
#endif
#define OCM_ADDRESS (0xA0)
#define SMB_ACK (0)
#define SMB_NAK (1)
#define SEND_STOP (0)
#define NOSEND_STOP (1)
#define SEND_START (0)
#define NOSEND_START (1)
// device specific as to where the 17th address bit goes...
// ************************* MACROS ************************************
#undef P
// OCM port I/O defintions
#define OCM_SCL BNAME(OCM_CLK_PORT, OCM_CLK_PIN)
#define OCM_SDA BNAME(OCM_DATA_PORT, OCM_DATA_PIN)
#define OCM_DATA_HIGH() { IO_DIR_PORT_PIN(OCM_DATA_PORT, OCM_DATA_PIN, IO_IN); }
#define OCM_DATA_LOW() \
{ \
IO_DIR_PORT_PIN(OCM_DATA_PORT, OCM_DATA_PIN, IO_OUT); \
OCM_SDA = 0; \
}
STATIC void hali2cSend(uint8 *buffer, uint16 len, uint8 sendStart, uint8 sendStop);
STATIC _Bool hali2cSendByte(uint8 dByte);
STATIC void hali2cWrite(bool dBit);
STATIC void hali2cClock(bool dir);
STATIC void hali2cStart(void);
STATIC void hali2cStop(void);
STATIC void hali2cReceive(uint8 address, uint8 *buffer, uint16 len);
STATIC uint8 hali2cReceiveByte(void);
STATIC _Bool hali2cRead(void);
STATIC void hali2cSendDeviceAddress(uint8 address);
STATIC __near_func void hali2cWait(uint8);
static void hali2cGroudPins(void);
void hali2cGroudPins(void) {
IO_DIR_PORT_PIN(OCM_DATA_PORT, OCM_DATA_PIN, IO_IN);
IO_DIR_PORT_PIN(OCM_CLK_PORT, OCM_CLK_PIN, IO_IN);
}
STATIC uint8 s_xmemIsInit;
/*********************************************************************
* @fn HalI2CInit
* @brief Initializes two-wire serial I/O bus
* @param void
* @return void
*/
void HalI2CInit(void) {
if (!s_xmemIsInit) {
s_xmemIsInit = 1;
// // Set port pins as inputs
// IO_DIR_PORT_PIN(OCM_CLK_PORT, OCM_CLK_PIN, IO_IN);
// IO_DIR_PORT_PIN(OCM_DATA_PORT, OCM_DATA_PIN, IO_IN);
// //
// // Set for general I/O operation
// IO_FUNC_PORT_PIN(OCM_CLK_PORT, OCM_CLK_PIN, IO_GIO);
// IO_FUNC_PORT_PIN(OCM_DATA_PORT, OCM_DATA_PIN, IO_GIO);
// //
// // Set I/O mode for pull-up/pull-down
// IO_IMODE_PORT_PIN(OCM_CLK_PORT, OCM_CLK_PIN, IO_PUD);
// IO_IMODE_PORT_PIN(OCM_DATA_PORT, OCM_DATA_PIN, IO_PUD);
// // Set pins to pull-up
// IO_PUD_PORT(OCM_CLK_PORT, IO_PUP);
// IO_PUD_PORT(OCM_DATA_PORT, IO_PUP);
}
}
int8 HalI2CReceive(uint8 address, uint8 *buf, uint16 len) {
hali2cReceive(address, buf, len);
return 0;
}
int8 HalI2CSend(uint8 address, uint8 *buf, uint16 len) {
// begin the write sequence with the address byte
hali2cSendDeviceAddress(address);
hali2cSend(buf, len, NOSEND_START, SEND_STOP);
return 0;
}
/*********************************************************************
* @fn hali2cSend
* @brief Sends buffer contents to SM-Bus device
* @param buffer - ptr to buffered data to send
* @param len - number of bytes in buffer
* @param sendStart - whether or not to send start condition.
* @param sendStop - whether or not to send stop condition.
* @return void
*/
STATIC void hali2cSend(uint8 *buffer, uint16 len, uint8 sendStart, uint8 sendStop) {
uint16 i;
uint8 retry = HAL_I2C_RETRY_CNT;
if (!len) {
return;
}
if (sendStart == SEND_START) {
hali2cStart();
}
for (i = 0; i < len; i++) {
do {
if (hali2cSendByte(buffer[i])) // takes care of ack polling
{
break;
}
} while (--retry);
}
if (sendStop == SEND_STOP) {
hali2cStop();
}
}
/*********************************************************************
* @fn hali2cSendByte
* @brief Serialize and send one byte to SM-Bus device
* @param dByte - data byte to send
* @return ACK status - 0=none, 1=received
*/
STATIC _Bool hali2cSendByte(uint8 dByte) {
uint8 i;
for (i = 0; i < 8; i++) {
// Send the MSB
hali2cWrite(dByte & 0x80);
// Next bit into MSB
dByte <<= 1;
}
// need clock low so if the SDA transitions on the next statement the
// slave doesn't stop. Also give opportunity for slave to set SDA
hali2cClock(0);
OCM_DATA_HIGH(); // set to input to receive ack...
hali2cClock(1);
hali2cWait(1);
return (!OCM_SDA); // Return ACK status
}
/*********************************************************************
* @fn hali2cWrite
* @brief Send one bit to SM-Bus device
* @param dBit - data bit to clock onto SM-Bus
* @return void
*/
STATIC void hali2cWrite(bool dBit) {
hali2cClock(0);
hali2cWait(1);
if (dBit) {
OCM_DATA_HIGH();
} else {
OCM_DATA_LOW();
}
hali2cClock(1);
hali2cWait(1);
}
/*********************************************************************
* @fn hali2cClock
* @brief Clocks the SM-Bus. If a negative edge is going out, the
* I/O pin is set as an output and driven low. If a positive
* edge is going out, the pin is set as an input and the pin
* pull-up drives the line high. This way, the slave device
* can hold the node low if longer setup time is desired.
* @param dir - clock line direction
* @return void
*/
STATIC void hali2cClock(bool dir) {
uint8 maxWait = 10;
if (dir) {
IO_DIR_PORT_PIN(OCM_CLK_PORT, OCM_CLK_PIN, IO_IN);
/* Wait until clock is high */
while (!OCM_SCL && maxWait) {
hali2cWait(1);
maxWait -= 1;
}
} else {
IO_DIR_PORT_PIN(OCM_CLK_PORT, OCM_CLK_PIN, IO_OUT);
OCM_SCL = 0;
}
hali2cWait(1);
}
/*********************************************************************
* @fn hali2cStart
* @brief Initiates SM-Bus communication. Makes sure that both the
* clock and data lines of the SM-Bus are high. Then the data
* line is set high and clock line is set low to start I/O.
* @param void
* @return void
*/
STATIC void hali2cStart(void) {
uint8 retry = HAL_I2C_RETRY_CNT;
// set SCL to input but with pull-up. if slave is pulling down it will stay down.
hali2cClock(1);
do {
// wait for slave to release clock line...
if (OCM_SCL) // wait until the line is high...
{
break;
}
hali2cWait(1);
} while (--retry);
// SCL low to set SDA high so the transition will be correct.
hali2cClock(0);
OCM_DATA_HIGH(); // SDA high
hali2cClock(1); // set up for transition
hali2cWait(1);
OCM_DATA_LOW(); // start
hali2cWait(1);
hali2cClock(0);
}
/*********************************************************************
* @fn hali2cStop
* @brief Terminates SM-Bus communication. Waits unitl the data line
* is low and the clock line is high. Then sets the data line
* high, keeping the clock line high to stop I/O.
* @param void
* @return void
*/
STATIC void hali2cStop(void) {
// Wait for clock high and data low
hali2cClock(0);
OCM_DATA_LOW(); // force low with SCL low
hali2cWait(1);
hali2cClock(1);
OCM_DATA_HIGH(); // stop condition
hali2cWait(1);
hali2cGroudPins();
}
/*********************************************************************
* @fn hali2cWait
* @brief Wastes a an amount of time.
* @param count: down count in busy-wait
* @return void
*/
STATIC __near_func void hali2cWait(uint8 count) {
while (count--) {
asm("NOP");
}
}
/*********************************************************************
* @fn hali2cReceiveByte
* @brief Read the 8 data bits.
* @param void
* @return character read
*/
STATIC uint8 hali2cReceiveByte() {
int8 i, rval = 0;
for (i = 7; i >= 0; --i) {
if (hali2cRead()) {
rval |= 1 << i;
}
}
return rval;
}
/**************************************************************************************************
**************************************************************************************************/
/*********************************************************************
* @fn hali2cReceive
* @brief reads data into a buffer
* @param address: linear address on part from which to read
* @param buffer: target array for read characters
* @param len: max number of characters to read
* @return void
*/
STATIC void hali2cReceive(uint8 address, uint8 *buffer, uint16 len) {
// uint8 ch;
uint16 i;
if (!len) {
return;
}
hali2cSendDeviceAddress(address);
// ch = OCM_ADDRESS_BYTE(0, OCM_READ);
// hali2cSend(&ch, 1, SEND_START, NOSEND_STOP);
for (i = 0; i < len - 1; i++) {
// SCL may be high. set SCL low. If SDA goes high when input
// mode is set the slave won't see a STOP
hali2cClock(0);
OCM_DATA_HIGH();
buffer[i] = hali2cReceiveByte();
hali2cWrite(SMB_ACK); // write leaves SCL high
}
// condition SDA one more time...
hali2cClock(0);
OCM_DATA_HIGH();
buffer[i] = hali2cReceiveByte();
hali2cWrite(SMB_NAK);
hali2cStop();
}
/*********************************************************************
* @fn hali2cRead
* @brief Toggle the clock line to let the slave set the data line.
* Then read the data line.
* @param void
* @return TRUE if bit read is 1 else FALSE
*/
STATIC _Bool hali2cRead(void) {
// SCL low to let slave set SDA. SCL high for SDA
// valid and then get bit
hali2cClock(0);
hali2cWait(1);
hali2cClock(1);
hali2cWait(1);
return OCM_SDA;
}
/*********************************************************************
* @fn hali2cSendDeviceAddress
* @brief Send onlythe device address. Do ack polling
*
* @param void
* @return none
*/
STATIC void hali2cSendDeviceAddress(uint8 address) {
uint8 retry = HAL_I2C_RETRY_CNT;
do {
hali2cStart();
if (hali2cSendByte(address)) // do ack polling...
{
break;
}
} while (--retry);
}
// http://e2e.ti.com/support/wireless-connectivity/zigbee-and-thread/f/158/t/140917
/*********************************************************************
* @fn I2C_ReadMultByte
* @brief reads data into a buffer
* @param address: linear address on part from which to read
* @param reg: internal register address on part read from
* @param buffer: target array for read characters
* @param len: max number of bytes to read
*/
int8 I2C_ReadMultByte(uint8 address, uint8 reg, uint8 *buffer, uint16 len) {
uint16 i = 0;
uint8 _address = 0;
if (!len) {
return I2C_ERROR;
}
/* Send START condition */
hali2cStart();
/* Set direction of transmission */
// Reset the address bit0 for write
// _address &= OCM_WRITE;
_address = ((address << 1) | OCM_WRITE);
/* Send Address and get acknowledgement from slave*/
if (!hali2cSendByte(_address)) {
return I2C_ERROR;
}
/* Send internal register to read from to */
if (!hali2cSendByte(reg))
return I2C_ERROR;
/* Send RESTART condition */
hali2cStart();
/* Set direction of transmission */
// Reset the address bit0 for read
// _address |= OCM_READ;
_address = ((address << 1) | OCM_READ);
/* Send Address and get acknowledgement from slave*/
if (!hali2cSendByte(_address))
return I2C_ERROR;
while (len) {
// SCL may be high. set SCL low. If SDA goes high when input
// mode is set the slave won't see a STOP
hali2cClock(0);
OCM_DATA_HIGH();
buffer[i] = hali2cReceiveByte();
// Acknowledgement if not sending last byte
if (len > 1) {
hali2cWrite(SMB_ACK); // write leaves SCL high
}
// increment buffer register
i++;
// Decrement the read bytes counter
len--;
}
// condition SDA one more time...
hali2cClock(0);
OCM_DATA_HIGH();
hali2cWrite(SMB_NAK);
hali2cStop();
// condition SDA one more time...
// hali2cClock(0);
// OCM_DATA_HIGH();
// buffer[i] = hali2cReceiveByte();
// hali2cWrite(SMB_NAK);
// hali2cStop();
return I2C_SUCCESS;
}
/*********************************************************************
* @fn I2C_WriteMultByte
* @brief reads data into a buffer
* @param address: linear address on part from which to read
* @param reg: internal register address on part read from
* @param buffer: target array for read characters
* @param len: max number of bytes to read
*/
int8 I2C_WriteMultByte(uint8 address, uint8 reg, uint8 *buffer, uint16 len) {
uint16 i = 0;
uint8 _address = 0;
if (!len) {
return I2C_ERROR;
}
/* Send START condition */
hali2cStart();
// return I2C_ERROR;
/* Set direction of transmission */
// Reset the address bit0 for write
// _address &= OCM_WRITE;
_address = ((address << 1) | OCM_WRITE);
/* Send Address and get acknowledgement from slave*/
if (!hali2cSendByte(_address))
return I2C_ERROR;
/* Send internal register to read from to */
if (!hali2cSendByte(reg))
return I2C_ERROR;
/* Write data into register */
// read bytes of data into buffer
while (len) {
// SCL may be high. set SCL low. If SDA goes high when input
// mode is set the slave won't see a STOP
hali2cClock(0);
OCM_DATA_HIGH();
/* Send Address and get acknowledgement from slave*/
if (!hali2cSendByte(buffer[i]))
return I2C_ERROR;
// increment buffer register
i++;
// Decrement the read bytes counter
len--;
}
hali2cStop();
return I2C_SUCCESS;
}