c7af41db615c0147b490491e4905559b9798fc53
[tv_i2c_hack] / src / i2c.c
1 #include <stdio.h>
2 #include <avr/io.h>
3 #include <avr/interrupt.h>
4 #include "i2c.h"
5
6 enum {
7         I2C_IDLE,
8         I2C_MASTER_WRITE,
9         I2C_MASTER_READ
10 };
11
12 enum {
13         ST_INVALID                      = 0,
14         ST_UNKNOWN                      = 0xf8,
15         ST_START                        = 0x08,
16         ST_REP_START            = 0x10,
17         ST_SLA_W_ACK            = 0x18,
18         ST_SLA_W_NACK           = 0x20,
19         ST_WR_ACK                       = 0x28,
20         ST_WR_NACK                      = 0x30,
21         ST_ARBLOST                      = 0x38,
22         ST_SLA_R_ACK            = 0x40,
23         ST_SLA_R_NACK           = 0x48,
24         ST_RD_ACK                       = 0x50,
25         ST_RD_NACK                      = 0x58,
26         ST_MATCH_W                      = 0x60,
27         ST_ARBLOST_MATCH        = 0x68,
28         ST_GENMATCH                     = 0x70,
29         ST_ARBLOST_GENMATCH = 0x78,
30         ST_SLAVE_RD_ACK         = 0x80,
31         ST_SLAVE_RD_NACK        = 0x88,
32         ST_SLAVE_GENRD_ACK      = 0x90,
33         ST_SLAVE_GENRD_NACK     = 0x98,
34         ST_SLAVE_STARTSTOP      = 0xa0,
35         ST_SLAVE_SLA_WR_ACK     = 0xa8,
36         ST_SLAVE_ARBLOST_WR_ACK = 0xb0,
37         ST_SLAVE_WR_ACK         = 0xb8,
38         ST_SLAVE_WR_NACK        = 0xc0,
39         ST_SLAVE_LAST_WR_ACK = 0xc8
40 };
41
42 enum {
43         EV_DEBUG,
44         EV_START,
45         EV_STOP,
46         EV_SLA_W,
47         EV_SLA_R,
48         EV_DATA
49 };
50
51 #define TWINT_BIT       (1 << TWINT)
52 #define TWEA_BIT        (1 << TWEA)
53 #define TWSTA_BIT       (1 << TWSTA)
54 #define TWSTO_BIT       (1 << TWSTO)
55 #define TWEN_BIT        (1 << TWEN)
56 #define TWIE_BIT        (1 << TWIE)
57
58 #define I2C_START()     (TWCR = TWINT_BIT | TWSTA_BIT | TWEN_BIT | TWIE_BIT)
59 #define I2C_STOP()      (TWCR = TWINT_BIT | TWSTO_BIT | TWEN_BIT | TWIE_BIT)
60 #define I2C_WRITE()     (TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT)
61 #define I2C_READ_ACK()  (TWCR = TWINT_BIT | TWEA_BIT | TWEN_BIT | TWIE_BIT)
62 #define I2C_READ_NACK() (TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT)
63
64 static unsigned char i2c_addr, i2c_subaddr;
65 static volatile unsigned char i2c_mode;
66 static int i2c_seq, i2c_ndata;
67 static unsigned char *i2c_data;
68
69 static void (*async_func)(void);
70
71 void i2c_init(void)
72 {
73         TWBR = 10;              /* 10 with 1x prescaler should make it about 100khz */
74         TWSR = 0;               /* prescaler 1x */
75         TWAR = 0;
76         TWAMR = 0;
77         TWCR = 0;               /* I2C disabled by default */
78
79         i2c_mode = I2C_IDLE;
80 }
81
82
83 void i2c_write(unsigned char addr, unsigned char subaddr, unsigned char *data, int ndata)
84 {
85         i2c_addr = addr & 0xfe;
86         i2c_subaddr = subaddr;
87         i2c_mode = I2C_MASTER_WRITE;
88         i2c_ndata = ndata;
89         i2c_data = data;
90         i2c_seq = 0;
91         I2C_START();
92 }
93
94 void i2c_read(unsigned char addr, unsigned char subaddr, unsigned char *buf, int size)
95 {
96         i2c_addr = addr & 0xfe;
97         i2c_subaddr = subaddr;
98         i2c_mode = I2C_MASTER_READ;
99         i2c_ndata = size;
100         i2c_data = buf;
101         i2c_seq = 0;
102         I2C_START();
103 }
104
105 void i2c_handle_send(void)
106 {
107         unsigned char state = TWSR & 0xf8;
108
109         /*LOGNUM(state);*/
110
111         switch(state) {
112         case ST_REP_START:      /* 0x10 */
113                 /* repeated start, same as start, but also increment i2c_seq */
114                 i2c_seq++;
115         case ST_START:  /* 0x8 */
116                 /* start initiated, write the slave address */
117                 //printf("DBG i2c SLA: %x\n", (unsigned int)i2c_addr);
118                 TWDR = i2c_addr;
119                 I2C_WRITE();
120                 break;
121
122         case ST_SLA_W_ACK:      /* 0x18 */
123                 /* slave addr sent and ACKed, send subaddr or data */
124                 if(i2c_seq == 0) {
125                         /* this is the first packet, send subaddr */
126                         i2c_seq++;
127                         TWDR = i2c_subaddr;
128                         I2C_WRITE();
129                 } else {
130                         if(i2c_ndata--) {
131                                 TWDR = *i2c_data++;
132                                 I2C_WRITE();
133                         } else {
134                                 /* done sending, send stop */
135                                 i2c_mode = I2C_IDLE;
136                                 I2C_STOP();
137                         }
138                 }
139                 break;
140
141         case ST_SLA_W_NACK:     /* 0x20 */
142                 /* slave addr sent but not ACKed, abort */
143                 i2c_mode = I2C_IDLE;
144                 I2C_STOP();
145                 printf("i2c: NACK after SLA+W\n");
146                 break;
147
148         case ST_WR_ACK: /* 0x28 */
149                 /* data (or subaddr) sent and ACKed, send more data (or restart) if available */
150 #if 0
151                 if(i2c_seq == 0) {
152                         /* subaddr was sent, send repeated start */
153                         I2C_START();
154                 } else {
155 #endif
156                         /* data was sent, send more data or stop */
157                         if(i2c_ndata--) {
158                                 TWDR = *i2c_data++;
159                                 I2C_WRITE();
160                         } else {
161                                 i2c_mode = I2C_IDLE;
162                                 I2C_STOP();
163                         }
164 //              }
165                 break;
166
167         case ST_WR_NACK:        /* 0x30 */
168                 /* data (or subaddr) sent but not ACKed */
169                 if(i2c_seq == 0) {
170                         /* NACK after subaddr, abort */
171                         printf("i2c: NACK after subaddr\n");
172                 } else {
173                         /* NACK after data */
174                         if(i2c_ndata) {
175                                 printf("i2c: NACK with %d pending\n", i2c_ndata);
176                         }
177                 }
178                 i2c_mode = I2C_IDLE;
179                 I2C_STOP();
180                 break;
181
182         case ST_ARBLOST:        /* 0x38 */
183                 /* arbitration lost */
184                 printf("i2c: arb lost\n");
185                 I2C_START();
186                 break;
187
188         case ST_INVALID:        /* 0 */
189                 printf("i2c: invalid start/stop\n");
190                 i2c_mode = I2C_IDLE;
191                 I2C_STOP();
192                 break;
193
194         default:
195                 printf("i2c: unexpected state (W): 0x%x\n", state);
196                 i2c_mode = I2C_IDLE;
197                 I2C_STOP();
198         }
199 }
200
201 void i2c_handle_recv(void)
202 {
203         unsigned char state = TWSR & 0xf8;
204
205         /*LOGNUM(state);*/
206
207         switch(state) {
208         case ST_START:          /* 0x8 */
209                 TWDR = i2c_addr;        /* start a *write* (we need to send the subaddress before reading) */
210                 I2C_WRITE();
211                 break;
212
213         case ST_REP_START:      /* 0x10 */
214                 /* repeated start, now we can issue the actual read */
215                 TWDR = i2c_addr | 1;
216                 I2C_WRITE();
217                 break;
218
219         case ST_SLA_W_ACK:      /* 0x18 */
220                 /* SLA+W means we just started the write part, need to send the subaddress */
221                 TWDR = i2c_subaddr;
222                 I2C_WRITE();
223                 break;
224
225         case ST_SLA_W_NACK:
226                 /* slave addr sent but not ACKed, abort */
227                 i2c_mode = I2C_IDLE;
228                 I2C_STOP();
229                 printf("i2c: NACK after SLA+W (R)\n");
230                 break;
231
232         case ST_WR_ACK:         /* 0x28 */
233                 /* the subaddress write was ACKed, rep-start to issue the read */
234                 I2C_START();
235                 break;
236
237         case ST_WR_NACK:
238                 /* the subaddress write was not ACKed, abort */
239                 i2c_mode = I2C_IDLE;
240                 I2C_STOP();
241                 printf("i2c: NACK after subaddr (R)\n");
242                 break;
243
244         case ST_SLA_R_ACK:      /* 0x40 */
245                 /* SLA+R was ACKed send ACK to start receiving */
246                 I2C_READ_ACK();
247                 break;
248
249         case ST_RD_ACK:         /* 0x50 */
250                 /* ... or last read was ACKed, again read next and ACK for more */
251                 *i2c_data++ = TWDR;
252                 if(--i2c_ndata > 0) {
253                         I2C_READ_ACK();
254                 } else {
255                         I2C_READ_NACK();
256                 }
257                 break;
258
259         case ST_SLA_R_NACK:     /* 0x48 */
260                 /* SLA+R was sent but ACK was not received, abort */
261                 i2c_mode = I2C_IDLE;
262                 I2C_STOP();
263                 printf("i2c: NACK after SLA+R\n");
264                 break;
265
266         case ST_RD_NACK:        /* 0x58 */
267                 /* read without ACK, we get this after we send a NACK, or this is the last byte (?) */
268                 if(i2c_ndata > 0) {
269                         *i2c_data++ = TWDR;
270                         i2c_ndata--;
271                 }
272                 i2c_mode = I2C_IDLE;
273                 I2C_STOP();
274                 break;
275
276         case ST_ARBLOST:        /* 0x38 */
277                 /* arbitration lost */
278                 printf("i2c: arb lost\n");
279                 I2C_START();
280                 break;
281
282         case ST_INVALID:        /* 0 */
283                 printf("i2c: invalid start/stop\n");
284                 i2c_mode = I2C_IDLE;
285                 I2C_STOP();
286                 break;
287
288         default:
289                 printf("i2c: unexpected state (R): 0x%x\n", state);
290                 i2c_mode = I2C_IDLE;
291                 I2C_STOP();
292         }
293 }
294
295 ISR(TWI_vect)
296 {
297         switch(i2c_mode) {
298         case I2C_MASTER_WRITE:
299                 i2c_handle_send();
300                 break;
301
302         case I2C_MASTER_READ:
303                 i2c_handle_recv();
304                 break;
305         }
306 }
307
308 void i2c_wait(void)
309 {
310         while(i2c_mode);
311 }
312
313 void i2c_async(void (*donecb)(void))
314 {
315         async_func = donecb;
316 }
317
318 void i2c_check_async(void)
319 {
320         if(async_func && i2c_mode == I2C_IDLE) {
321                 async_func();
322                 async_func = 0;
323         }
324 }
325
326 void i2c_hold(void)
327 {
328         TWCR = 0;               /* make sure the AVR i2c hardware is disabled */
329         DDRC = 0x20;    /* ... and drive SCL pin low */
330         PORTC = 0;
331 }
332
333 void i2c_release(void)
334 {
335         DDRC = 0;
336 }
337
338 void i2c_abort(void)
339 {
340         if(i2c_mode != I2C_IDLE) {
341                 i2c_mode = I2C_IDLE;
342                 I2C_STOP();
343         }
344 }