#include "serial.h"
#include "i2c.h"
+enum {
+ IR_CMD_ONOFF = 0x0c,
+ IR_CMD_MUTE = 0x0d,
+ IR_CMD_VOLUP = 0x10,
+ IR_CMD_VOLDN = 0x11,
+ IR_CMD_PROGUP = 0x20,
+ IR_CMD_PROGDN = 0x21,
+ IR_CMD_OK = 0x25,
+ IR_CMD_YELLOW = 0x32,
+ IR_CMD_BLUE = 0x34,
+ IR_CMD_GREEN = 0x36,
+ IR_CMD_RED = 0x37,
+ IR_CMD_TVAV = 0x38,
+ IR_CMD_MENU = 0x3b
+};
+
+enum {
+ STAT_ON = 1,
+ STAT_SYNC = 2,
+ STAT_60HZ = 4,
+ STAT_COLOR_PAL = 0x10,
+ STAT_COLOR_NTSC = 0x20,
+ STAT_COLOR_SECAM = 0x30,
+
+ STAT_EN = 0x40,
+ STAT_BUSHOLD = 0x80
+};
+#define STAT_COLOR_MASK 0x30
+
+enum {
+ PEND_IR = 1,
+ PEND_EN = 2,
+ PEND_VOL = 4
+};
+
/* TODO
* IR decoding, connected to PD2
* enable/disable with switch on PCINT1
#define PD_IR 2
#define PD_ENSW 3
-#define EVQ_SIZE 64
-uint16_t evq[EVQ_SIZE];
-volatile int evq_wr, evq_rd;
-
-#define LOGNUM(x) EVLOG(EV_DEBUG, (x))
-
-#define EVLOG(ev, data) \
- do { \
- if(ev != EV_DEBUG || dbgmode) { \
- evq[evq_wr] = ((uint16_t)(ev) << 8) | (data); \
- evq_wr = (evq_wr + 1) & (EVQ_SIZE - 1); \
- if(evq_wr == evq_rd) { \
- evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1); \
- } \
- } \
- } while(0)
-
/* serial input buffer */
static char input[64];
static unsigned char inp_cidx;
static volatile unsigned char pending;
static volatile unsigned char enable, prev_enable;
+static unsigned char stat, prev_stat;
static volatile uint16_t ir_input;
+static unsigned char volume, mute;
+static unsigned char grab_timeout;
+
+static void updhold(void);
+static unsigned char read_status(void);
static void proc_cmd(char *input);
static void printstat(void);
static void printdump(void);
int main(void)
{
unsigned char pend;
+ uint16_t timer, prev_time;
/* tri-state everything and disable pullups */
DDRB = 1; /* B0: activity LED */
*/
TCCR0A = 2; /* clear on compare match */
TCCR0B = 3; /* clock select: ioclock / 64 <- prescaler */
- OCR0A = 192; /* count top */
+ OCR0A = 192; /* count top (tweaked until it works) */
+
+ /* setup timer 1 as a freerunning 16bit counter */
+ TCCR1A = 0;
+ TCCR1B = 5; /* prescaler: clk / 1024: 14.4khz */
+
+ timer = prev_time = 0;
/* read initial enable switch state */
enable = (~PIND >> PD_ENSW) & 1;
+ volume = 31;
+ mute = 0;
+
+ if(enable) grab_timeout = 10;
i2c_init();
printf("TV i2c hack\n");
for(;;) {
+ timer = TCNT1;
i2c_check_async();
cli();
pend = pending;
pending = 0;
sei();
- if(pend & 1) {
- printf("IR: %04x (%s)\n", (unsigned int)ir_input & 0x7fff, ir_input & 0x8000 ? "err" : "ok");
+ if((pend & PEND_IR) && !(ir_input & 0x8000)) {
+ printf("IR addr: %02x cmd: %02x (%04x)\n", (ir_input >> 6) & 0x1f, ir_input & 0x3f, ir_input);
+
+ if(((ir_input >> 6) & 0x1f) == 0) { /* TV remote sends IR address 0 */
+ switch(ir_input & 0x3f) {
+ case IR_CMD_GREEN:
+ enable = 1;
+ pend |= PEND_EN;
+ break;
+
+ case IR_CMD_RED:
+ enable = 0;
+ pend |= PEND_EN;
+ break;
+
+ case IR_CMD_VOLUP:
+ if(volume < 0x3f) {
+ volume++;
+ pend |= PEND_VOL;
+ }
+ break;
+
+ case IR_CMD_VOLDN:
+ if(volume > 0) {
+ volume--;
+ pend |= PEND_VOL;
+ }
+ break;
+
+ case IR_CMD_MUTE:
+ mute ^= 1;
+ pend |= PEND_VOL;
+ break;
+ }
+ }
}
- if(pend & 2 && enable != prev_enable) {
+ if((pend & PEND_EN) && enable != prev_enable) {
printf("enable: %d\n", enable);
prev_enable = enable;
+
+ if(!enable && i2c_isheld()) {
+ i2c_release();
+ printf("rel1\n");
+ }
}
if(have_input()) {
}
}
- /* read from queue and send over the serial port */
- /*
- uint16_t val;
- val = 0xffff;
- cli();
- if(evq_wr != evq_rd) {
- val = evq[evq_rd];
- evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1);
+ if(timer - prev_time >= 14400) { /* 1 sec */
+ prev_time = timer;
+
+ prev_stat = stat;
+ stat = read_status();
+
+ if(stat != prev_stat) {
+ printf("[%s|%s|%dHz|", stat & STAT_ON ? " on" : "off",
+ stat & STAT_SYNC ? "lock" : " ", stat & STAT_60HZ ? 60 : 50);
+
+ switch(stat & STAT_COLOR_MASK) {
+ case STAT_COLOR_PAL:
+ printf(" PAL ]");
+ break;
+ case STAT_COLOR_NTSC:
+ printf(" NTSC]");
+ break;
+ case STAT_COLOR_SECAM:
+ printf("SECAM]");
+ break;
+ default:
+ printf(" ]");
+ }
+
+ printf(" [%s|%s]\n", stat & STAT_EN ? "en" : " ",
+ stat & STAT_BUSHOLD ? "hold" : " ");
+ }
+ /*printf(" %02x %02x %02x\n", data[0], data[1], data[2]);*/
+
+
+ if(enable) {
+ static unsigned char rel_timeout;
+ if(i2c_isheld()) {
+ if(!(stat & STAT_ON) || (stat & STAT_COLOR_MASK)) {
+ /* if we turn the TV off, or if we don't have RGB input
+ * release the i2c bus (after a few seconds to make sure it's not a glitch)
+ */
+ if(++rel_timeout > 3) {
+ i2c_release();
+ printf("rel2\n");
+ rel_timeout = 0;
+ grab_timeout = 10;
+ }
+ } else {
+ rel_timeout = 0;
+ /* handle any pending volume changes */
+ /*
+ if(pend & PEND_VOL) {
+ updhold();
+ }
+ */
+ }
+
+ } else {
+ /* we're not currently holding the bus */
+ if((stat & STAT_ON) && !(stat & STAT_COLOR_MASK)) {
+ /* if the TV is on and we're in RGB mode, grab the bus
+ * (after a few seconds to allow the tv to initialize)
+ */
+ if(grab_timeout > 0) {
+ grab_timeout--;
+ } else {
+ updhold();
+ grab_timeout = 0;
+ }
+ }
+ }
+ }
}
- sei();
+ }
+ return 0;
+}
- if(val != 0xffff) {
- unsigned char ev = val >> 8;
- printf("%02x: %02x\n", ev, val & 0xff);
+static void updhold(void)
+{
+ data[0] = mute ? 0 : volume;
+ data[1] = 0x1f;
+ i2c_write(0x8a, 0x1f, data, 1);
+ i2c_wait();
+ i2c_write(0x8a, 0x1c, data + 1, 1);
+ i2c_wait();
+ i2c_hold();
+}
+
+static unsigned char read_status(void)
+{
+ unsigned char s, col;
+
+ s = i2c_isheld() ? STAT_BUSHOLD : 0;
+ if(enable) s |= STAT_EN;
+
+ i2c_read(0x8a, 0, data, 3);
+ if(s & STAT_BUSHOLD) {
+ i2c_wait();
+ i2c_hold();
+ } else {
+ i2c_wait();
+ }
+
+ if(data[2] & 0x80) s |= STAT_ON;
+ if(data[0] & 0x10) s |= STAT_SYNC;
+ if(data[1] & 0x20) s |= STAT_60HZ;
+ col = data[0] & 0x0f;
+
+ if(col != 0) {
+ if(col == 10) {
+ s |= STAT_COLOR_SECAM;
+ } else if(col & 1) {
+ s |= STAT_COLOR_NTSC;
+ } else {
+ s |= STAT_COLOR_PAL;
}
- */
}
- return 0;
+
+ return s;
}
static void proc_cmd(char *input)
if(pending & 1) return;
/* IR going low */
- ir_input = 0;
+ ir_input = 0x8000;
nsamples = 1; /* we're starting in the middle of the first bit which should be 01 (=1) */
samples = 0;
EIMSK &= 0xfe; /* disable further interrupts while decoding IR code */
TCNT0 = 96; /* reset the counter to half the range so it'll trigger in the middle of the current pulse */
TIFR0 |= 1 << OCF0A; /* clear pending interrupts */
TIMSK0 = 1 << OCIE0A; /* enable output compare interrupt */
-
- //PORTB ^= 1;
}
ISR(TIMER0_COMPA_vect)
{
static unsigned char err;
- PORTB ^= 1;
-
samples = (samples << 1) | (~(PIND >> PD_IR) & 1);
if((++nsamples & 1) == 0) {
if((samples & 3) == 0 || (samples & 3) == 3) {
}
if(nsamples >= 28) {
- if(err) {
+ if(err || !(ir_input & 0x2000)) {
ir_input |= 0x8000;
}
pending |= 1;