5 #include <avr/interrupt.h>
6 #include <util/delay.h>
31 STAT_COLOR_PAL = 0x10,
32 STAT_COLOR_NTSC = 0x20,
33 STAT_COLOR_SECAM = 0x30,
38 #define STAT_COLOR_MASK 0x30
49 /* serial input buffer */
50 static char input[64];
51 static unsigned char inp_cidx;
53 static unsigned char data[16];
55 static uint16_t dump_addr;
56 static int dump_count;
59 static volatile unsigned char pending;
60 static volatile unsigned char enable, prev_enable;
61 static unsigned char stat, prev_stat;
62 static volatile uint16_t ir_input;
64 static unsigned char volume, mute;
65 static unsigned char grab_timeout;
67 static void updhold(void);
68 static unsigned char read_status(void);
69 static void proc_cmd(char *input);
70 static void printstat(void);
71 static void printdump(void);
77 uint16_t timer, prev_time;
79 /* tri-state everything and disable pullups */
80 DDRB = 1; /* B0: activity LED */
81 DDRC = 0; /* I2C pins as inputs */
87 /* setup external interrupts */
88 EICRA = 0x6; /* INT0 falling edge, INT1 both */
89 EIMSK = 3; /* enable INT0 and INT1 */
91 /* we'll use the timer 0 compare interrupt for sampling the IR input pulses
92 * we need to sample every 889us or 1124.86 Hz
93 * interrupt frequency = F_CPU / (prescaler * (1 - OCRnx))
94 * 14745600 / (64.0 * (1.0 + 204)) -> 1123.9 Hz
96 TCCR0A = 2; /* clear on compare match */
97 TCCR0B = 3; /* clock select: ioclock / 64 <- prescaler */
98 OCR0A = 192; /* count top (tweaked until it works) */
100 /* setup timer 1 as a freerunning 16bit counter */
102 TCCR1B = 5; /* prescaler: clk / 1024: 14.4khz */
104 timer = prev_time = 0;
106 /* read initial enable switch state */
107 enable = (~PIND >> PD_ENSW) & 1;
111 if(enable) grab_timeout = 10;
118 printf("TV i2c hack\n");
128 if((pend & PEND_IR) && !(ir_input & 0x8000)) {
130 printf("IR addr: %02x cmd: %02x (%04x)\n", (ir_input >> 6) & 0x1f, ir_input & 0x3f, ir_input);
133 if(((ir_input >> 6) & 0x1f) == 0) { /* TV remote sends IR address 0 */
134 switch(ir_input & 0x3f) {
166 if((pend & PEND_EN) && enable != prev_enable) {
167 printf("enable: %d\n", enable);
168 prev_enable = enable;
170 if(!enable && i2c_isheld()) {
180 if(c == '\r' || c == '\n') {
185 } else if(inp_cidx < sizeof input - 1) {
186 input[inp_cidx++] = c;
190 if(timer - prev_time >= 14400) { /* 1 sec */
194 stat = read_status();
196 if(stat != prev_stat) {
197 printf("[%s|%s|%dHz|", stat & STAT_ON ? " on" : "off",
198 stat & STAT_SYNC ? "lock" : " ", stat & STAT_60HZ ? 60 : 50);
200 switch(stat & STAT_COLOR_MASK) {
204 case STAT_COLOR_NTSC:
207 case STAT_COLOR_SECAM:
214 printf(" [%s|%s]\n", stat & STAT_EN ? "en" : " ",
215 stat & STAT_BUSHOLD ? "hold" : " ");
217 /*printf(" %02x %02x %02x\n", data[0], data[1], data[2]);*/
221 static unsigned char rel_timeout;
223 if(!(stat & STAT_ON) || (stat & STAT_COLOR_MASK)) {
224 /* if we turn the TV off, or if we don't have RGB input
225 * release the i2c bus (after a few seconds to make sure it's not a glitch)
227 if(++rel_timeout > 3) {
235 /* handle any pending volume changes */
237 if(pend & PEND_VOL) {
244 /* we're not currently holding the bus */
245 if((stat & STAT_ON) && !(stat & STAT_COLOR_MASK)) {
246 /* if the TV is on and we're in RGB mode, grab the bus
247 * (after a few seconds to allow the tv to initialize)
249 if(grab_timeout > 0) {
263 static void updhold(void)
265 data[0] = mute ? 0 : volume;
268 i2c_write(JADDR, JSUB_VOLUME, data, 1);
271 i2c_write(JADDR, JSUB_SATURATION, data + 1, 1);
276 static unsigned char read_status(void)
278 unsigned char s, col;
280 s = i2c_isheld() ? STAT_BUSHOLD : 0;
281 if(enable) s |= STAT_EN;
283 i2c_read(JADDR, JSUB_STAT0, data, 3);
284 if(s & STAT_BUSHOLD) {
291 if(data[2] & 0x80) s |= STAT_ON;
292 if(data[0] & 0x10) s |= STAT_SYNC;
293 if(data[1] & 0x20) s |= STAT_60HZ;
294 col = data[0] & 0x0f;
298 s |= STAT_COLOR_SECAM;
300 s |= STAT_COLOR_NTSC;
309 static void proc_cmd(char *input)
313 if(strcmp(input, "dbg") == 0) {
317 } else if(strcmp(input, "nodbg") == 0) {
318 printf("OK nodbg\n");
321 } else if(strcmp(input, "av") == 0) {
324 /* AV switch (22): 0 0 SVO CMB1 CMB0 INA INB 0 */
326 i2c_write(JADDR, JSUB_AVSWITCH, data, 1);
329 } else if(strcmp(input, "rgb") == 0) {
332 /* Control 0 (2a): 0 IE2 RBL AKB CL3 CL2 CL1 CL0
333 * Control 1 (2b): 0 0 0 0 0 0 YUV HBL
337 i2c_write(JADDR, JSUB_CTRL0, data, 1);
339 i2c_write(JADDR, JSUB_CTRL1, data + 1, 1);
341 } else if(memcmp(input, "vol ", 4) == 0) {
342 int vol = atoi(input + 4);
343 if(vol < 1 || vol > 63) {
344 printf("ERR vol (%s)\n", input + 4);
347 i2c_write(JADDR, JSUB_VOLUME, data, 1);
348 printf("OK volume: %d\n", vol);
351 } else if(memcmp(input, "sat", 3) == 0) {
353 data[0] = atoi(input + 3);
354 if(data[0] <= 0 || data[0] > 0x3f) {
357 i2c_write(JADDR, JSUB_SATURATION, data, 1);
358 i2c_async(i2c_hold); /* hold I2C when done */
360 } else if(strcmp(input, "rel") == 0) {
361 printf("OK release\n");
364 } else if(strcmp(input, "status") == 0) {
365 i2c_read(JADDR, JSUB_STAT0, data, 3);
366 i2c_async(printstat);
368 } else if(memcmp(input, "rd ", 3) == 0) {
369 dump_addr = strtol(input + 3, &endp, 16);
371 if(endp > input + 3 && dump_addr >= 0 && dump_addr < 2048) {
373 i2c_read(0xa0 | ((dump_addr >> 7) & 0xe), dump_addr & 0xff, data, 1);
374 i2c_async(printdump);
376 printf("ERR address: %s\n", input + 3);
379 } else if(memcmp(input, "dump ", 5) == 0) {
380 dump_addr = strtol(input + 5, &endp, 16);
382 if(endp > input + 5 && dump_addr >= 0 && dump_addr < 2048) {
385 i2c_read(0xa0 | ((dump_addr >> 7) & 0xe), dump_addr & 0xff, data, 16);
386 i2c_async(printdump);
388 printf("ERR address: %s\n", input + 5);
391 } else if(strcmp(input, "abort") == 0) {
396 printf("ERR command (%s)\n", input);
400 static void printstat(void)
402 printf("OK status: %02x %02x %02x\n", (unsigned int)data[0],
403 (unsigned int)data[1], (unsigned int)data[2]);
406 static void printdump(void)
409 while(dump_count > 0) {
410 printf("OK %03x:", dump_addr);
411 for(i=0; i<16; i++) {
412 if(i == 8) putchar(' ');
414 if(--dump_count >= 0) {
415 printf(" %02x", data[i]);
425 static unsigned char nsamples;
426 static uint32_t samples;
430 /* ignore interrupts while a previous input is pending */
431 if(pending & 1) return;
435 nsamples = 1; /* we're starting in the middle of the first bit which should be 01 (=1) */
437 EIMSK &= 0xfe; /* disable further interrupts while decoding IR code */
439 TCNT0 = 96; /* reset the counter to half the range so it'll trigger in the middle of the current pulse */
440 TIFR0 |= 1 << OCF0A; /* clear pending interrupts */
441 TIMSK0 = 1 << OCIE0A; /* enable output compare interrupt */
444 ISR(TIMER0_COMPA_vect)
446 static unsigned char err;
448 samples = (samples << 1) | (~(PIND >> PD_IR) & 1);
449 if((++nsamples & 1) == 0) {
450 if((samples & 3) == 0 || (samples & 3) == 3) {
451 /* 00 or 11 are invalid sequences, we lost sync */
456 ir_input = (ir_input << 1) | (samples & 1);
460 if(err || !(ir_input & 0x2000)) {
465 TIMSK0 &= ~(1 << OCIE0A); /* disable the sampling interrupt */
466 EIMSK |= 1; /* re-enable the edge-detect interrupt for the next input */
472 enable = (~PIND >> PD_ENSW) & 1;