-
Notifications
You must be signed in to change notification settings - Fork 1
/
hal_i2c.c
318 lines (275 loc) · 9.67 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
/**************************************************************************************************
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.
**************************************************************************************************/
/* ------------------------------------------------------------------------------------------------
* Includes
* ------------------------------------------------------------------------------------------------
*/
#include "hal_assert.h"
#include "hal_board_cfg.h"
#include "hal_i2c.h"
/* ------------------------------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------------------------------
*/
#define I2C_ENS1 BV(6)
#define I2C_STA BV(5)
#define I2C_STO BV(4)
#define I2C_SI BV(3)
#define I2C_AA BV(2)
#define I2C_MST_RD_BIT BV(0) // Master RD/WRn bit to be OR'ed with Slave address.
#define I2C_CLOCK_MASK 0x83
#define I2C_PXIFG P2IFG
#define I2C_IF P2IF
#define I2C_IE BV(1)
/* ------------------------------------------------------------------------------------------------
* Typedefs
* ------------------------------------------------------------------------------------------------
*/
typedef enum
{
// HAL_I2C_MASTER mode statuses.
mstStarted = 0x08,
mstRepStart = 0x10,
mstAddrAckW = 0x18,
mstAddrNackW = 0x20,
mstDataAckW = 0x28,
mstDataNackW = 0x30,
mstLostArb = 0x38,
mstAddrAckR = 0x40,
mstAddrNackR = 0x48,
mstDataAckR = 0x50,
mstDataNackR = 0x58,
} i2cStatus_t;
/* ------------------------------------------------------------------------------------------------
* Macros
* ------------------------------------------------------------------------------------------------
*/
#define I2C_WRAPPER_DISABLE() st( I2CWC = 0x00; )
#define I2C_CLOCK_RATE(x) st( I2CCFG &= ~I2C_CLOCK_MASK; \
I2CCFG |= x; )
#define I2C_SET_NACK() st( I2CCFG &= ~I2C_AA; )
#define I2C_SET_ACK() st( I2CCFG |= I2C_AA; )
// Enable I2C bus
#define I2C_ENABLE() st( I2CCFG |= (I2C_ENS1); )
#define I2C_DISABLE() st( I2CCFG &= ~(I2C_ENS1); )
// Must clear SI before setting STA and then STA must be manually cleared.
#define I2C_STRT() st ( \
I2CCFG &= ~I2C_SI; \
I2CCFG |= I2C_STA; \
while ((I2CCFG & I2C_SI) == 0); \
I2CCFG &= ~I2C_STA; \
)
// Must set STO before clearing SI.
#define I2C_STOP() st ( \
I2CCFG |= I2C_STO; \
I2CCFG &= ~I2C_SI; \
while ((I2CCFG & I2C_STO) != 0); \
)
// Stop clock-stretching and then read when it arrives.
#define I2C_READ(_X_) st ( \
I2CCFG &= ~I2C_SI; \
while ((I2CCFG & I2C_SI) == 0); \
(_X_) = I2CDATA; \
)
// First write new data and then stop clock-stretching.
#define I2C_WRITE(_X_) st ( \
I2CDATA = (_X_); \
I2CCFG &= ~I2C_SI; \
while ((I2CCFG & I2C_SI) == 0); \
)
/* ------------------------------------------------------------------------------------------------
* Local Variables
* ------------------------------------------------------------------------------------------------
*/
static uint8 i2cAddr; // Target Slave address pre-shifted up by one leaving RD/WRn LSB as zero.
/**************************************************************************************************
* @fn i2cMstStrt
*
* @brief Attempt to send an I2C bus START and Slave Address as an I2C bus Master.
*
* input parameters
*
* @param RD_WRn - The LSB of the Slave Address as Read/~Write.
*
* @return The I2C status of the START request or of the Slave Address Ack.
*/
static uint8 i2cMstStrt(uint8 RD_WRn)
{
I2C_STRT();
if (I2CSTAT == mstStarted) /* A start condition has been transmitted */
{
I2C_WRITE(i2cAddr | RD_WRn);
}
return I2CSTAT;
}
/**************************************************************************************************
* @fn HalI2CInit
*
* @brief Initialize the I2C bus as a Master.
*
* input parameters
*
* @param address - I2C slave address.
* @param clockRate - I2C clock rate.
*
* output parameters
*
* None.
*
* @return None.
*/
void HalI2CInit(uint8 address, i2cClock_t clockRate)
{
i2cAddr = address << 1;
I2C_WRAPPER_DISABLE();
I2CADDR = 0; // no multi master support at this time
I2C_CLOCK_RATE(clockRate);
I2C_ENABLE();
}
/**************************************************************************************************
* @fn HalI2CRead
*
* @brief Read from the I2C bus as a Master.
*
* input parameters
*
* @param len - Number of bytes to read.
* @param pBuf - Pointer to the data buffer to put read bytes.
*
* output parameters
*
* None.
*
* @return The number of bytes successfully read.
*/
uint8 HalI2CRead(uint8 len, uint8 *pBuf)
{
uint8 cnt = 0;
if (i2cMstStrt(I2C_MST_RD_BIT) != mstAddrAckR)
{
len = 0;
}
// All bytes are ACK'd except for the last one which is NACK'd. If only
// 1 byte is being read, a single NACK will be sent. Thus, we only want
// to enable ACK if more than 1 byte is going to be read.
if (len > 1)
{
I2C_SET_ACK();
}
while (len > 0)
{
// slave devices require NACK to be sent after reading last byte
if (len == 1)
{
I2C_SET_NACK();
}
// read a byte from the I2C interface
I2C_READ(*pBuf++);
cnt++;
len--;
if (I2CSTAT != mstDataAckR)
{
if (I2CSTAT != mstDataNackR)
{
// something went wrong, so don't count last byte
cnt--;
}
break;
}
}
I2C_STOP();
return cnt;
}
/**************************************************************************************************
* @fn HalI2CWrite
*
* @brief Write to the I2C bus as a Master.
*
* input parameters
*
* @param len - Number of bytes to write.
* @param pBuf - Pointer to the data buffer to write.
*
* output parameters
*
* None.
*
* @return The number of bytes successfully written.
*/
uint8 HalI2CWrite(uint8 len, uint8 *pBuf)
{
if (i2cMstStrt(0) != mstAddrAckW)
{
len = 0;
}
for (uint8 cnt = 0; cnt < len; cnt++)
{
I2C_WRITE(*pBuf++);
if (I2CSTAT != mstDataAckW)
{
if (I2CSTAT == mstDataNackW)
{
len = cnt + 1;
}
else
{
len = cnt;
}
break;
}
}
I2C_STOP();
return len;
}
/**************************************************************************************************
* @fn HalI2CDisable
*
* @brief Places the I2C bus in inactive mode
*
* input parameters
*
* None.
*
* output parameters
*
* None.
*
* @return None.
*/
void HalI2CDisable(void)
{
I2C_DISABLE();
}
/*********************************************************************
*********************************************************************/