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