IR receiver works, next up decoding
[tv_i2c_hack] / src / 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 #include "i2c.h"
9
10 /* TODO
11  * IR decoding, connected to PD2
12  * enable/disable with switch on PCINT1
13  * some of the buttons multiplexed on ADC0
14  */
15 #define PD_IR   2
16 #define PD_ENSW 3
17
18 #define EVQ_SIZE        64
19 uint16_t evq[EVQ_SIZE];
20 volatile int evq_wr, evq_rd;
21
22 #define LOGNUM(x)       EVLOG(EV_DEBUG, (x))
23
24 #define EVLOG(ev, data) \
25         do { \
26                 if(ev != EV_DEBUG || dbgmode) { \
27                         evq[evq_wr] = ((uint16_t)(ev) << 8) | (data); \
28                         evq_wr = (evq_wr + 1) & (EVQ_SIZE - 1); \
29                         if(evq_wr == evq_rd) { \
30                                 evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1); \
31                         } \
32                 } \
33         } while(0)
34
35 /* serial input buffer */
36 static char input[64];
37 static unsigned char inp_cidx;
38
39 static unsigned char data[16];
40
41 static uint16_t dump_addr;
42 static int dump_count;
43 static int dbgmode;
44
45 static volatile unsigned char pending;
46 static volatile unsigned char enable, prev_enable;
47 static volatile uint16_t ir_input;
48
49 static void proc_cmd(char *input);
50 static void printstat(void);
51 static void printdump(void);
52
53
54 int main(void)
55 {
56         unsigned char pend;
57
58         /* tri-state everything and disable pullups */
59         DDRB = 1;       /* B0: activity LED */
60         DDRC = 0;       /* I2C pins as inputs */
61         DDRD = 0;
62         PORTB = 0;
63         PORTC = 0;
64         PORTD = 1 << PD_ENSW;
65
66         /* setup external interrupts */
67         EICRA = 0x6;    /* INT0 falling edge, INT1 both */
68         EIMSK = 3;              /* enable INT0 and INT1 */
69
70         /* we'll use the timer 0 compare interrupt for sampling the IR input pulses
71          * we need to sample every 889us or 1124.86 Hz
72          * interrupt frequency = F_CPU / (prescaler * (1 - OCRnx))
73          * 14745600 / (64.0 * (1.0 + 204)) -> 1123.9 Hz
74          */
75         TCCR0A = 2;             /* clear on compare match */
76         TCCR0B = 3;             /* clock select: ioclock / 64 <- prescaler */
77         OCR0A = 192;    /* count top */
78
79         /* read initial enable switch state */
80         enable = (~PIND >> PD_ENSW) & 1;
81
82         i2c_init();
83
84         init_serial(38400);
85         sei();
86
87         printf("TV i2c hack\n");
88
89         for(;;) {
90                 i2c_check_async();
91
92                 cli();
93                 pend = pending;
94                 pending = 0;
95                 sei();
96                 if(pend & 1) {
97                         printf("IR: %04x (%s)\n", (unsigned int)ir_input & 0x7fff, ir_input & 0x8000 ? "err" : "ok");
98                 }
99                 if(pend & 2 && enable != prev_enable) {
100                         printf("enable: %d\n", enable);
101                         prev_enable = enable;
102                 }
103
104                 if(have_input()) {
105                         int c = getchar();
106                         putchar(c);
107
108                         if(c == '\r' || c == '\n') {
109                                 putchar('\n');
110                                 input[inp_cidx] = 0;
111                                 proc_cmd(input);
112                                 inp_cidx = 0;
113                         } else if(inp_cidx < sizeof input - 1) {
114                                 input[inp_cidx++] = c;
115                         }
116                 }
117
118                 /* read from queue and send over the serial port */
119                 /*
120                 uint16_t val;
121                 val = 0xffff;
122                 cli();
123                 if(evq_wr != evq_rd) {
124                         val = evq[evq_rd];
125                         evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1);
126                 }
127                 sei();
128
129                 if(val != 0xffff) {
130                         unsigned char ev = val >> 8;
131                         printf("%02x: %02x\n", ev, val & 0xff);
132                 }
133                 */
134         }
135         return 0;
136 }
137
138 static void proc_cmd(char *input)
139 {
140         char *endp;
141
142         if(strcmp(input, "dbg") == 0) {
143                 printf("OK dbg\n");
144                 dbgmode = 1;
145
146         } else if(strcmp(input, "nodbg") == 0) {
147                 printf("OK nodbg\n");
148                 dbgmode = 0;
149
150         } else if(strcmp(input, "av") == 0) {
151                 printf("OK AV\n");
152
153                 /* AV switch (22): 0 0 SVO CMB1 CMB0 INA INB 0 */
154                 data[0] = 0x22;
155                 i2c_write(0x8a, 0x22, data, 1);
156                 i2c_async(i2c_hold);
157
158         } else if(strcmp(input, "rgb") == 0) {
159                 printf("OK RGB\n");
160
161                 /* Control 0 (2a): 0 IE2 RBL AKB  CL3 CL2 CL1 CL0
162                  * Control 1 (2b): 0  0   0   0    0   0  YUV HBL
163                  */
164                 data[0] = 0x70;
165                 data[1] = 0;
166                 i2c_write(0x8a, 0x2a, data, 1);
167                 i2c_wait();
168                 i2c_write(0x8a, 0x2b, data + 1, 1);
169
170         } else if(memcmp(input, "vol ", 4) == 0) {
171                 int vol = atoi(input + 4);
172                 if(vol < 1 || vol > 63) {
173                         printf("ERR vol (%s)\n", input + 4);
174                 } else {
175                         data[0] = vol;
176                         i2c_write(0x8a, 0x1f, data, 1);
177                         printf("OK volume: %d\n", vol);
178                 }
179
180         } else if(memcmp(input, "sat", 3) == 0) {
181                 printf("OK sat\n");
182                 data[0] = atoi(input + 3);
183                 if(data[0] <= 0 || data[0] > 0x3f) {
184                         data[0] = 0x1f;
185                 }
186                 i2c_write(0x8a, 0x1c, data, 1);
187                 i2c_async(i2c_hold);    /* hold I2C when done */
188
189         } else if(strcmp(input, "rel") == 0) {
190                 printf("OK release\n");
191                 i2c_release();
192
193         } else if(strcmp(input, "status") == 0) {
194                 i2c_read(0x8a, 0, data, 3);
195                 i2c_async(printstat);
196
197         } else if(memcmp(input, "rd ", 3) == 0) {
198                 dump_addr = strtol(input + 3, &endp, 16);
199
200                 if(endp > input + 3 && dump_addr >= 0 && dump_addr < 2048) {
201                         dump_count = 1;
202                         i2c_read(0xa0 | ((dump_addr >> 7) & 0xe), dump_addr & 0xff, data, 1);
203                         i2c_async(printdump);
204                 } else {
205                         printf("ERR address: %s\n", input + 3);
206                 }
207
208         } else if(memcmp(input, "dump ", 5) == 0) {
209                 dump_addr = strtol(input + 5, &endp, 16);
210
211                 if(endp > input + 5 && dump_addr >= 0 && dump_addr < 2048) {
212                         dump_count = 1;
213                         dump_addr &= 0xff0;
214                         i2c_read(0xa0 | ((dump_addr >> 7) & 0xe), dump_addr & 0xff, data, 16);
215                         i2c_async(printdump);
216                 } else {
217                         printf("ERR address: %s\n", input + 5);
218                 }
219
220         } else if(strcmp(input, "abort") == 0) {
221                 i2c_abort();
222                 printf("OK\n");
223
224         } else {
225                 printf("ERR command (%s)\n", input);
226         }
227 }
228
229 static void printstat(void)
230 {
231         printf("OK status: %02x %02x %02x\n", (unsigned int)data[0],
232                         (unsigned int)data[1], (unsigned int)data[2]);
233 }
234
235 static void printdump(void)
236 {
237         int i;
238         while(dump_count > 0) {
239                 printf("OK %03x:", dump_addr);
240                 for(i=0; i<16; i++) {
241                         if(i == 8) putchar(' ');
242
243                         if(--dump_count >= 0) {
244                                 printf(" %02x", data[i]);
245                         } else {
246                                 printf("   ");
247                         }
248                         dump_addr += 16;
249                 }
250                 putchar('\n');
251         }
252 }
253
254 static unsigned char nsamples;
255 static uint32_t samples;
256
257 ISR(INT0_vect)
258 {
259         /* ignore interrupts while a previous input is pending */
260         if(pending & 1) return;
261
262         /* IR going low */
263         ir_input = 0;
264         nsamples = 1;   /* we're starting in the middle of the first bit which should be 01 (=1) */
265         samples = 0;
266         EIMSK &= 0xfe;  /* disable further interrupts while decoding IR code */
267
268         TCNT0 = 96;     /* reset the counter to half the range so it'll trigger in the middle of the current pulse */
269         TIFR0 |= 1 << OCF0A;    /* clear pending interrupts */
270         TIMSK0 = 1 << OCIE0A;   /* enable output compare interrupt */
271
272         //PORTB ^= 1;
273 }
274
275 ISR(TIMER0_COMPA_vect)
276 {
277         static unsigned char err;
278
279         PORTB ^= 1;
280
281         samples = (samples << 1) | (~(PIND >> PD_IR) & 1);
282         if((++nsamples & 1) == 0) {
283                 if((samples & 3) == 0 || (samples & 3) == 3) {
284                         /* 00 or 11 are invalid sequences, we lost sync */
285                         err = 1;
286                 }
287
288                 /* 01->1, 10->0 */
289                 ir_input = (ir_input << 1) | (samples & 1);
290         }
291
292         if(nsamples >= 28) {
293                 if(err) {
294                         ir_input |= 0x8000;
295                 }
296                 pending |= 1;
297                 err = 0;
298                 TIMSK0 &= ~(1 << OCIE0A);       /* disable the sampling interrupt */
299                 EIMSK |= 1;             /* re-enable the edge-detect interrupt for the next input */
300         }
301 }
302
303 ISR(INT1_vect)
304 {
305         enable = (~PIND >> PD_ENSW) & 1;
306         pending |= 2;
307 }