holding SCL low after setting my saturation value is rather crude, but
[tv_i2c_hack] / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <avr/io.h>
5 #include <avr/interrupt.h>
6 #include <util/delay.h>
7 #include "serial.h"
8
9 enum {
10         I2C_IDLE,
11         I2C_MASTER_SEND,
12         I2C_MASTER_RECV
13 };
14
15 #define TWINT_BIT       (1 << TWINT)
16 #define TWSTA_BIT       (1 << TWSTA)
17 #define TWSTO_BIT       (1 << TWSTO)
18 #define TWEN_BIT        (1 << TWEN)
19 #define TWIE_BIT        (1 << TWIE)
20
21 #define I2C_START()     (TWCR = TWINT_BIT | TWSTA_BIT | TWEN_BIT | TWIE_BIT)
22 #define I2C_STOP()      (TWCR = TWINT_BIT | TWSTO_BIT | TWEN_BIT | TWIE_BIT)
23 #define I2C_WRITE()     (TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT)
24
25 unsigned char i2c_addr, i2c_subaddr;
26 volatile unsigned char i2c_mode;
27 int i2c_seq, i2c_ndata;
28 unsigned char *i2c_data;
29 int i2c_retry_delay;
30
31 #define EVQ_SIZE        64
32 uint16_t evq[EVQ_SIZE];
33 volatile int evq_wr, evq_rd;
34
35 #define LOGNUM(x)       \
36         do { \
37                 evq[evq_wr] = x; \
38                 evq_wr = (evq_wr + 1) & (EVQ_SIZE - 1); \
39                 if(evq_wr == evq_rd) { \
40                         evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1); \
41                 } \
42         } while(0)
43
44 /* serial input buffer */
45 static char input[64];
46 static unsigned char inp_cidx;
47
48 static unsigned char data[8];
49
50 static void proc_cmd(char *input);
51 void i2c_write(unsigned char addr, unsigned char subaddr, int ndata, unsigned char *data);
52
53 int main(void)
54 {
55         /* tri-state everything and disable pullups */
56         DDRB = 1;       /* B0: activity LED */
57         DDRC = 0x20; /* SCL output so we can hold the clock low when disabling i2c */
58         DDRD = 0;
59         PORTB = 0;
60         PORTC = 0;
61         PORTD = 0;
62
63         TWBR = 10;              /* 10 with 1x prescaler should make it about 100khz */
64         TWSR = 0;               /* prescaler 1x */
65         TWAR = 0x8a;            /* let's monitor for commands going to the jungle */
66         TWAMR = 0;
67         TWCR = (1 << TWEN) | (1 << TWIE); /* enable i2c, don't ack, enable intr */
68
69         i2c_mode = I2C_IDLE;
70
71         init_serial(38400);
72         sei();
73
74         printf("Starting i2c hack\n");
75
76         for(;;) {
77                 //uint16_t val = 0xffff;
78
79                 if(have_input()) {
80                         int c = getchar();
81                         putchar(c);
82
83                         if(c == '\r' || c == '\n') {
84                                 putchar('\n');
85                                 input[inp_cidx] = 0;
86                                 proc_cmd(input);
87                                 inp_cidx = 0;
88                         } else if(inp_cidx < sizeof input - 1) {
89                                 input[inp_cidx++] = c;
90                         }
91                 }
92
93                 /* read from queue and send over the serial port */
94                 /*
95                 cli();
96                 if(evq_wr != evq_rd) {
97                         val = evq[evq_rd];
98                         evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1);
99                 }
100                 sei();
101
102                 if(val != 0xffff) {
103                         printf("s(%u): %x\n", (unsigned int)(val >> 8), (unsigned int)(val & 0xff));
104                 }
105                 */
106                 /*
107                 _delay_us(100);
108                 data[0] = 0x3f;
109                 i2c_write(0x8a, 0x1c, 1, data);
110                 */
111         }
112         return 0;
113 }
114
115 static void proc_cmd(char *input)
116 {
117         if(strcmp(input, "rgb") == 0) {
118                 printf("OK sending RGB switch command\n");
119
120                 /* i2c addr 8b: jungle
121                  *  subaddr 2b: Control 1 reg
122                  *  bit 1: YUV
123                  */
124                 data[0] = 2;
125                 i2c_write(0x8a, 0x2b, 1, data);
126
127         } else if(memcmp(input, "vol ", 4) == 0) {
128                 int vol = atoi(input + 4);
129                 if(vol < 1 || vol > 63) {
130                         printf("ERR invalid vol (%s)\n", input + 4);
131                 } else {
132                         data[0] = vol;
133                         i2c_write(0x8a, 0x1f, 1, data);
134                         printf("OK volume: %d\n", vol);
135                 }
136
137         } else if(strcmp(input, "zoom") == 0) {
138                 data[0] = 0x3f;
139                 i2c_write(0x8a, 0x10, 1, data);
140                 printf("OK zoom in\n");
141
142         } else if(strcmp(input, "unzoom") == 0) {
143                 data[0] = 0x20;
144                 i2c_write(0x8a, 0x10, 1, data);
145                 printf("OK unzoom\n");
146
147         } else if(memcmp(input, "sat", 3) == 0) {
148                 printf("OK saturate\n");
149                 data[0] = atoi(input + 3);
150                 if(data[0] <= 0 || data[0] > 0x3f) {
151                         data[0] = 0x1f;
152                 }
153                 i2c_write(0x8a, 0x1c, 1, data);
154                 while(i2c_mode);
155                 TWCR = 0;
156
157         } else if(strcmp(input, "abort") == 0) {
158                 if(i2c_mode != I2C_IDLE) {
159                         i2c_mode = I2C_IDLE;
160                         I2C_STOP();
161                         printf("OK aborting i2c op\n");
162                 } else {
163                         printf("ERR i2c is idle\n");
164                 }
165
166         } else {
167                 printf("ERR invalid command (%s)\n", input);
168         }
169 }
170
171 void i2c_write(unsigned char addr, unsigned char subaddr, int ndata, unsigned char *data)
172 {
173         i2c_addr = addr & 0xfe;
174         i2c_subaddr = subaddr;
175         i2c_mode = I2C_MASTER_SEND;
176         i2c_ndata = ndata;
177         i2c_data = data;
178         i2c_seq = 0;
179         I2C_START();
180 }
181
182 void i2c_handle_send(void)
183 {
184         unsigned char state = TWSR & 0xf8;
185
186         //printf("DBG i2c state (%x)\n", (unsigned int)state);
187         LOGNUM(state);
188
189         switch(state) {
190         case 0x10:
191                 /* repeated start, same as start, but also increment i2c_seq */
192                 i2c_seq++;
193         case 0x8:
194                 /* start initiated, write the slave address */
195                 //printf("DBG i2c SLA: %x\n", (unsigned int)i2c_addr);
196                 TWDR = i2c_addr;
197                 I2C_WRITE();
198                 break;
199
200         case 0x18:
201                 /* slave addr sent and ACKed, send subaddr or data */
202                 if(i2c_seq == 0) {
203                         /* this is the first packet, send subaddr */
204                         i2c_seq++;
205                         LOGNUM(0x100 | i2c_subaddr);
206                         TWDR = i2c_subaddr;
207                         I2C_WRITE();
208                 } else {
209                         if(i2c_ndata--) {
210                                 LOGNUM(0x200 | *i2c_data);
211                                 TWDR = *i2c_data++;
212                                 I2C_WRITE();
213                         } else {
214                                 /* done sending, send stop */
215                                 i2c_mode = I2C_IDLE;
216                                 I2C_STOP();
217                         }
218                 }
219                 break;
220
221         case 0x20:
222                 /* slave addr sent but not ACKed, abort */
223                 i2c_mode = I2C_IDLE;
224                 I2C_STOP();
225                 printf("i2c: NACK after SLA+W\n");
226                 break;
227
228         case 0x28:
229                 /* data (or subaddr) sent and ACKed, send more data (or restart) if available */
230 #if 0
231                 if(i2c_seq == 0) {
232                         /* subaddr was sent, send repeated start */
233                         I2C_START();
234                 } else {
235 #endif
236                         /* data was sent, send more data or stop */
237                         if(i2c_ndata--) {
238                                 LOGNUM(0x200 | *i2c_data);
239                                 TWDR = *i2c_data++;
240                                 I2C_WRITE();
241                         } else {
242                                 i2c_mode = I2C_IDLE;
243                                 I2C_STOP();
244                         }
245 //              }
246                 break;
247
248         case 0x30:
249                 /* data (or subaddr) sent but not ACKed */
250                 if(i2c_seq == 0) {
251                         /* NACK after subaddr, abort */
252                         printf("i2c: NACK after subaddr\n");
253                 } else {
254                         /* NACK after data */
255                         if(i2c_ndata) {
256                                 printf("i2c: NACK with %d data packets pending\n", i2c_ndata);
257                         }
258                 }
259                 i2c_mode = I2C_IDLE;
260                 I2C_STOP();
261                 break;
262
263         case 0x38:
264                 /* arbitration lost */
265                 printf("i2c: arbitration lost\n");
266                 I2C_START();
267                 break;
268
269         case 0:
270                 printf("i2c: invalid start/stop\n");
271                 i2c_mode = I2C_IDLE;
272                 I2C_STOP();
273                 break;
274
275         default:
276                 printf("i2c: unexpected state (W): 0x%x\n", state);
277                 i2c_mode = I2C_IDLE;
278                 I2C_STOP();
279         }
280 }
281
282 void i2c_handle_recv(void)
283 {
284         printf("i2c: recv unimplemented\n");
285 }
286
287 ISR(TWI_vect)
288 {
289         uint16_t ev;
290
291         switch(i2c_mode) {
292         case I2C_MASTER_SEND:
293                 i2c_handle_send();
294                 break;
295
296         case I2C_MASTER_RECV:
297                 i2c_handle_recv();
298                 break;
299
300         default:
301                 /* log traffic on the bus */
302                 ev = (uint16_t)(TWSR & 0xf8) << 8;
303                 switch(TWSR & 0xf8) {
304                 case 0x80:      /* own SLA+W received, ack */
305                 case 0x88:      /* own SLA+W recv, no-ack */
306                 case 0x90:      /* general recv, ack */
307                 case 0x98:      /* general recv, no-ack */
308                         ev |= TWDR;
309                         break;
310
311                 default:
312                         break;
313                 }
314
315                 /* append data to the event queue */
316                 TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT;
317
318                 evq[evq_wr] = ev;
319                 evq_wr = (evq_wr + 1) & (EVQ_SIZE - 1);
320                 if(evq_wr == evq_rd) {
321                         evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1);
322                 }
323
324                 PORTB = 1;
325         }
326 }