forked from openLRSng/openLRSng
-
Notifications
You must be signed in to change notification settings - Fork 0
/
I2C.h
309 lines (274 loc) · 8.49 KB
/
I2C.h
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
#include <util/twi.h>
#include <Arduino.h>
#define I2C_FREQ 200000
// API
void myI2C_init(uint8_t enable_pullup);
// Initialize slave operation
void myI2C_slaveSetup(uint8_t address, uint8_t mask, uint8_t enableGCall,
uint8_t (*handler)(uint8_t *data, uint8_t flags));
// Flag values given to slaveHandler
#define MYI2C_SLAVE_ISTX 0x01 // slave handler should 'send' a byte
#define MYI2C_SLAVE_ISFIRST 0x02 // first byte after addressing (normally register number)
// Master mode transfer functions
uint8_t myI2C_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t flags);
uint8_t myI2C_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t flags);
// flag values used by master
#define MYI2C_WAIT 0x01 // do not return until transfer is done
#define MYI2C_NOSTOP 0x02 // do not release bus after transaction
#define MYI2C_NOTIMEOUT 0x04
// slave handler routine, should return desired ACK status
// *data - pointter to fetch/store data
// flags - see above
// RETVAL 0 - no more data wanted/available
// 1 - OK to continue transfer
uint8_t (*myI2C_slaveHandler)(uint8_t *data, uint8_t flags) = NULL;
// Internal state data
volatile uint8_t myI2C_slaRw; // slave address & RW bit, used in master mode
volatile uint8_t *myI2C_dataPtr; // TX/RX data ptr
volatile uint8_t myI2C_dataCnt; // data countter
#define MYI2C_DONTSTOP 0x01 // do not release bus after operation
#define MYI2C_REPSTART 0x02 // going to use repeated start on next transfer
#define MYI2C_BUSY 0x04 // transfer ongoing
volatile uint8_t myI2C_flags;
volatile uint8_t myI2C_error;
uint16_t myI2C_timeout = 5000; // default 2ms
void myI2C_init(uint8_t enablePullup)
{
digitalWrite(SDA, enablePullup?1:0);
digitalWrite(SCL, enablePullup?1:0);
myI2C_dataCnt=0;
myI2C_flags=0;
TWSR |= ~(_BV(TWPS0)|_BV(TWPS1));
TWBR = ((F_CPU / I2C_FREQ) - 16) / 2;
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
}
void myI2C_slaveSetup(uint8_t address, uint8_t mask, uint8_t enableGCall,
uint8_t (*handler)(uint8_t *data, uint8_t flags))
{
TWAR = (address << 1) | (enableGCall?1:0);
TWAMR = (mask << 1);
myI2C_slaveHandler = handler;
}
void myI2C_reply(uint8_t ack)
{
if(ack) {
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
} else {
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
}
}
void myI2C_stop(void)
{
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO);
while(TWCR & _BV(TWSTO)) {
continue;
}
}
void myI2C_releaseBus(void)
{
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
}
void myI2C_recover(void)
{
TWCR = 0;
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
myI2C_flags = 0;
}
// ISR //
SIGNAL(TWI_vect)
{
uint8_t slack=0;
// Serial.println(TW_STATUS,16);
switch(TW_STATUS) {
// All Master
case TW_START: // sent start condition
case TW_REP_START: // sent repeated start condition
// copy device address and r/w bit to output register and ack
TWDR = myI2C_slaRw;
myI2C_reply(1);
break;
// Master Transmitter
case TW_MT_SLA_ACK: // slave receiver acked address
case TW_MT_DATA_ACK: // slave receiver acked data
// if there is data to send, send it, otherwise stop
if(myI2C_dataCnt--) {
// copy data to output register and ack
TWDR = *(myI2C_dataPtr++);
myI2C_reply(1);
} else {
myI2C_flags &= ~MYI2C_BUSY;
if (myI2C_flags&MYI2C_DONTSTOP) {
myI2C_flags &= ~MYI2C_DONTSTOP;
myI2C_flags |= MYI2C_REPSTART;
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN);
} else {
myI2C_stop();
}
}
break;
case TW_MT_SLA_NACK: // address sent, nack received
myI2C_error = 2;
myI2C_flags &= ~MYI2C_BUSY;
myI2C_stop();
break;
case TW_MT_DATA_NACK: // data sent, nack received
myI2C_error = 3;
myI2C_flags &= ~MYI2C_BUSY;
myI2C_stop();
break;
case TW_MT_ARB_LOST: // lost bus arbitration
myI2C_error = 4;
myI2C_flags &= ~MYI2C_BUSY;
myI2C_releaseBus();
break;
// Master Receiver
case TW_MR_DATA_ACK: // data received, ack sent
// put byte into buffer
*(myI2C_dataPtr++) = TWDR;
myI2C_dataCnt--;
case TW_MR_SLA_ACK: // address sent, ack received
// ack if more bytes are expected, otherwise nack
if (myI2C_dataCnt>1) {
myI2C_reply(1);
} else {
myI2C_reply(0);
}
break;
case TW_MR_DATA_NACK: // data received, nack sent
// put final byte into buffer
*(myI2C_dataPtr++) = TWDR;
myI2C_dataCnt--;
myI2C_flags &= ~MYI2C_BUSY;
if (myI2C_flags&MYI2C_DONTSTOP) {
myI2C_flags &= ~MYI2C_DONTSTOP;
myI2C_flags |= MYI2C_REPSTART;
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN);
} else {
myI2C_stop();
}
break;
case TW_MR_SLA_NACK: // address sent, nack received
myI2C_error = 5;
myI2C_flags &= ~MYI2C_BUSY;
myI2C_stop();
break;
// TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case
// Slave Receiver
case TW_SR_SLA_ACK: // addressed, returned ack
case TW_SR_GCALL_ACK: // addressed generally, returned ack
case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack
case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack
myI2C_dataCnt = 0;
myI2C_reply(1);
break;
case TW_SR_DATA_ACK: // data received, returned ack
case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack
if (myI2C_slaveHandler) {
uint8_t data=TWDR;
myI2C_reply(myI2C_slaveHandler(&data, (myI2C_dataCnt++==0)?MYI2C_SLAVE_ISFIRST:0));
} else {
myI2C_reply(0);
}
break;
case TW_SR_STOP: // stop or repeated start condition received
myI2C_stop();
myI2C_releaseBus();
break;
case TW_SR_DATA_NACK: // data received, returned nack
case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack
// nack back at master
myI2C_reply(0);
break;
// Slave Transmitter
case TW_ST_SLA_ACK: // addressed, returned ack
case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack
slack=1;
// just fall thru to data sending
case TW_ST_DATA_ACK: // byte sent, ack returned
if (myI2C_slaveHandler) {
uint8_t data,ret;
ret = myI2C_slaveHandler(&data, (slack?MYI2C_SLAVE_ISFIRST:0)|MYI2C_SLAVE_ISTX);
TWDR = data;
myI2C_reply(ret); // reply with ACK or NACK depending on slave callback
} else {
myI2C_reply(0);
}
break;
case TW_ST_DATA_NACK: // received nack, we are done
case TW_ST_LAST_DATA: // received ack, but we are done already!
// ack future responses
myI2C_reply(1);
break;
// All
case TW_NO_INFO: // no state information
break;
case TW_BUS_ERROR: // bus error, illegal stop/start
//twi_error = TW_BUS_ERROR;
myI2C_stop();
break;
}
}
uint8_t myI2C_wait(uint16_t timeout)
{
uint8_t ret;
uint32_t start = micros();
while (myI2C_flags & MYI2C_BUSY) {
if ((timeout) && ((micros() - start) > timeout)) {
myI2C_recover();
return 1;
}
}
ret = myI2C_error;
myI2C_error=0;
return ret;
}
uint8_t myI2C_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t flags)
{
uint8_t ret;
if ((ret = myI2C_wait((flags & MYI2C_NOTIMEOUT) ? 0 : myI2C_timeout))) {
return ret;
}
myI2C_error = 0;
myI2C_dataCnt = length;
myI2C_dataPtr = data;
myI2C_slaRw = TW_WRITE | (address << 1);
myI2C_flags |= MYI2C_BUSY | ((flags&MYI2C_NOSTOP)?MYI2C_DONTSTOP:0);
if (myI2C_flags & MYI2C_REPSTART) {
myI2C_flags &= ~MYI2C_REPSTART;
TWDR = myI2C_slaRw;
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE);
} else {
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA);
}
if (flags & MYI2C_WAIT) {
if ((ret = myI2C_wait((flags & MYI2C_NOTIMEOUT) ? 0 : myI2C_timeout))) {
return ret;
}
}
return 0;
}
uint8_t myI2C_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t flags)
{
uint8_t ret;
if ((ret=myI2C_wait((flags & MYI2C_NOTIMEOUT) ? 0 : myI2C_timeout))) {
return ret;
}
myI2C_error = 0;
myI2C_dataCnt = length;
myI2C_dataPtr = data;
myI2C_slaRw = TW_READ | (address << 1);
myI2C_flags |= MYI2C_BUSY | ((flags&MYI2C_NOSTOP)?MYI2C_DONTSTOP:0);
if (myI2C_flags & MYI2C_REPSTART) {
myI2C_flags &= ~MYI2C_REPSTART;
TWDR = myI2C_slaRw;
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE);
} else {
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA);
}
if (flags & MYI2C_WAIT) {
if ((ret=myI2C_wait((flags & MYI2C_NOTIMEOUT) ? 0 : myI2C_timeout))) {
return ret;
}
}
return 0;
}