*.o
*.d
*.swp
-test
+tv_i2c_hack
*.eep
*.hex
*.map
-src = $(wildcard *.c)
+src = $(wildcard src/*.c)
obj = $(src:.c=.o)
-bin = test
+bin = tv_i2c_hack
hex = $(bin).hex
eep = $(bin).eep
warn = -pedantic -Wall
-CFLAGS = -Os $(warn) -mmcu=$(mcu_gcc) -DF_CPU=20000000
+CFLAGS = -Os $(warn) -mmcu=$(mcu_gcc) -DF_CPU=14745600
LDFLAGS = -Wl,-Map,$(bin).map -mmcu=$(mcu_gcc) -lprintf_min
.PHONY: all
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <avr/io.h>
-#include <avr/interrupt.h>
-#include <util/delay.h>
-#include "serial.h"
-
-#undef USE_PCINT
-
-enum {
- I2C_IDLE,
- I2C_MASTER_WRITE,
- I2C_MASTER_READ
-};
-
-enum {
- ST_INVALID = 0,
- ST_UNKNOWN = 0xf8,
- ST_START = 0x08,
- ST_REP_START = 0x10,
- ST_SLA_W_ACK = 0x18,
- ST_SLA_W_NACK = 0x20,
- ST_WR_ACK = 0x28,
- ST_WR_NACK = 0x30,
- ST_ARBLOST = 0x38,
- ST_SLA_R_ACK = 0x40,
- ST_SLA_R_NACK = 0x48,
- ST_RD_ACK = 0x50,
- ST_RD_NACK = 0x58,
- ST_MATCH_W = 0x60,
- ST_ARBLOST_MATCH = 0x68,
- ST_GENMATCH = 0x70,
- ST_ARBLOST_GENMATCH = 0x78,
- ST_SLAVE_RD_ACK = 0x80,
- ST_SLAVE_RD_NACK = 0x88,
- ST_SLAVE_GENRD_ACK = 0x90,
- ST_SLAVE_GENRD_NACK = 0x98,
- ST_SLAVE_STARTSTOP = 0xa0,
- ST_SLAVE_SLA_WR_ACK = 0xa8,
- ST_SLAVE_ARBLOST_WR_ACK = 0xb0,
- ST_SLAVE_WR_ACK = 0xb8,
- ST_SLAVE_WR_NACK = 0xc0,
- ST_SLAVE_LAST_WR_ACK = 0xc8
-};
-
-enum {
- EV_DEBUG,
- EV_START,
- EV_STOP,
- EV_SLA_W,
- EV_SLA_R,
- EV_DATA
-};
-
-static const char *evname[] = {
- "DBG",
- "STA",
- "STO",
- "A+W",
- "A+R",
- "DAT"
-};
-
-#define TWINT_BIT (1 << TWINT)
-#define TWEA_BIT (1 << TWEA)
-#define TWSTA_BIT (1 << TWSTA)
-#define TWSTO_BIT (1 << TWSTO)
-#define TWEN_BIT (1 << TWEN)
-#define TWIE_BIT (1 << TWIE)
-
-#define I2C_START() (TWCR = TWINT_BIT | TWSTA_BIT | TWEN_BIT | TWIE_BIT)
-#define I2C_STOP() (TWCR = TWINT_BIT | TWSTO_BIT | TWEN_BIT | TWIE_BIT)
-#define I2C_WRITE() (TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT)
-#define I2C_READ_ACK() (TWCR = TWINT_BIT | TWEA_BIT | TWEN_BIT | TWIE_BIT)
-#define I2C_READ_NACK() (TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT)
-
-unsigned char i2c_addr, i2c_subaddr;
-volatile unsigned char i2c_mode;
-int i2c_seq, i2c_ndata;
-unsigned char *i2c_data;
-int i2c_retry_delay;
-
-#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 unsigned char data[16];
-
-static void (*async_func)(void);
-
-static uint16_t dump_addr;
-static int dump_count;
-static int dbgmode;
-static int snooping;
-
-static void proc_cmd(char *input);
-static void printstat(void);
-static void printdump(void);
-
-void i2c_write(unsigned char addr, unsigned char subaddr, unsigned char *data, int ndata);
-void i2c_read(unsigned char addr, unsigned char subaddr, unsigned char *buf, int size);
-void i2c_wait(void);
-void i2c_async(void (*donecb)(void));
-void i2c_check_async(void);
-void i2c_hold(void);
-void i2c_release(void);
-void i2c_snoop(int onoff);
-#ifndef USE_PCINT
-void snoop(void);
-#endif
-
-int main(void)
-{
- uint16_t val;
-
- /* tri-state everything and disable pullups */
- DDRB = 1; /* B0: activity LED */
- DDRC = 0; /* I2C pins as inputs */
- DDRD = 0;
- PORTB = 0;
- PORTC = 0;
- PORTD = 0;
-
- TWBR = 10; /* 10 with 1x prescaler should make it about 100khz */
- TWSR = 0; /* prescaler 1x */
- TWAR = 0;
- TWAMR = 0;
- TWCR = 0; /* I2C disabled by default */
-
- i2c_mode = I2C_IDLE;
- i2c_snoop(0); /* disable snooping by default */
-
- init_serial(38400);
- sei();
-
- printf("Starting i2c hack\n");
-
- for(;;) {
-#ifndef USE_PCINT
- if(snooping) {
- snoop();
- if(have_input()) {
- i2c_snoop(0);
- }
- continue;
- }
-#endif
-
- i2c_check_async();
-
- if(have_input()) {
- int c = getchar();
- putchar(c);
-
- if(c == '\r' || c == '\n') {
- putchar('\n');
- input[inp_cidx] = 0;
- proc_cmd(input);
- inp_cidx = 0;
- } else if(inp_cidx < sizeof input - 1) {
- input[inp_cidx++] = c;
- }
- }
-
- /* read from queue and send over the serial port */
- val = 0xffff;
- cli();
- if(evq_wr != evq_rd) {
- val = evq[evq_rd];
- evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1);
- }
- sei();
-
- if(val != 0xffff) {
- unsigned char ev = val >> 8;
- if(ev > EV_DATA) {
- printf("%02x: %02x\n", ev, val & 0xff);
- } else {
- printf("%s: %02x\n", evname[ev], val & 0xff);
- }
- }
- }
- return 0;
-}
-
-static void proc_cmd(char *input)
-{
- char *endp;
-
- if(strcmp(input, "dbg") == 0) {
- printf("OK dbg\n");
- dbgmode = 1;
-
- } else if(strcmp(input, "nodbg") == 0) {
- printf("OK nodbg\n");
- dbgmode = 0;
-
- } else if(strcmp(input, "start") == 0) {
- printf("OK snooping\n");
- i2c_snoop(1);
-
- } else if(strcmp(input, "stop") == 0) {
- i2c_snoop(0);
- printf("OK stopped snooping\n");
-
- } else if(strcmp(input, "av") == 0) {
- printf("OK AV\n");
-
- /* AV switch (22): 0 0 SVO CMB1 CMB0 INA INB 0 */
- data[0] = 0x22;
- i2c_write(0x8a, 0x22, data, 1);
- i2c_async(i2c_hold);
-
- } else if(strcmp(input, "rgb") == 0) {
- printf("OK RGB\n");
-
- /* Control 0 (2a): 0 IE2 RBL AKB CL3 CL2 CL1 CL0
- * Control 1 (2b): 0 0 0 0 0 0 YUV HBL
- */
- data[0] = 0x70;
- data[1] = 0;
- i2c_write(0x8a, 0x2a, data, 1);
- while(i2c_mode);
- i2c_write(0x8a, 0x2b, data + 1, 1);
-
- } else if(memcmp(input, "vol ", 4) == 0) {
- int vol = atoi(input + 4);
- if(vol < 1 || vol > 63) {
- printf("ERR vol (%s)\n", input + 4);
- } else {
- data[0] = vol;
- i2c_write(0x8a, 0x1f, data, 1);
- printf("OK volume: %d\n", vol);
- }
-
- } else if(memcmp(input, "sat", 3) == 0) {
- printf("OK sat\n");
- data[0] = atoi(input + 3);
- if(data[0] <= 0 || data[0] > 0x3f) {
- data[0] = 0x1f;
- }
- i2c_write(0x8a, 0x1c, data, 1);
- i2c_async(i2c_hold); /* hold I2C when done */
-
- } else if(strcmp(input, "rel") == 0) {
- printf("OK release\n");
- i2c_release();
-
- } else if(strcmp(input, "status") == 0) {
- i2c_read(0x8a, 0, data, 3);
- i2c_async(printstat);
-
- } else if(memcmp(input, "rd ", 3) == 0) {
- dump_addr = strtol(input + 3, &endp, 16);
-
- if(endp > input + 3 && dump_addr >= 0 && dump_addr < 2048) {
- dump_count = 1;
- i2c_read(0xa0 | ((dump_addr >> 7) & 0xe), dump_addr & 0xff, data, 1);
- i2c_async(printdump);
- } else {
- printf("ERR address: %s\n", input + 3);
- }
-
- } else if(memcmp(input, "dump ", 5) == 0) {
- dump_addr = strtol(input + 5, &endp, 16);
-
- if(endp > input + 5 && dump_addr >= 0 && dump_addr < 2048) {
- dump_count = 1;
- dump_addr &= 0xff0;
- i2c_read(0xa0 | ((dump_addr >> 7) & 0xe), dump_addr & 0xff, data, 16);
- i2c_async(printdump);
- } else {
- printf("ERR address: %s\n", input + 5);
- }
-
- } else if(strcmp(input, "abort") == 0) {
- if(i2c_mode != I2C_IDLE) {
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- printf("OK\n");
- } else {
- printf("ERR i2c is idle\n");
- }
-
- } else {
- printf("ERR command (%s)\n", input);
- }
-}
-
-static void printstat(void)
-{
- printf("OK status: %02x %02x %02x\n", (unsigned int)data[0],
- (unsigned int)data[1], (unsigned int)data[2]);
-}
-
-static void printdump(void)
-{
- int i;
- while(dump_count > 0) {
- printf("OK %03x:", dump_addr);
- for(i=0; i<16; i++) {
- if(i == 8) putchar(' ');
-
- if(--dump_count >= 0) {
- printf(" %02x", data[i]);
- } else {
- printf(" ");
- }
- dump_addr += 16;
- }
- putchar('\n');
- }
-}
-
-void i2c_write(unsigned char addr, unsigned char subaddr, unsigned char *data, int ndata)
-{
- i2c_addr = addr & 0xfe;
- i2c_subaddr = subaddr;
- i2c_mode = I2C_MASTER_WRITE;
- i2c_ndata = ndata;
- i2c_data = data;
- i2c_seq = 0;
- I2C_START();
-}
-
-void i2c_read(unsigned char addr, unsigned char subaddr, unsigned char *buf, int size)
-{
- i2c_addr = addr & 0xfe;
- i2c_subaddr = subaddr;
- i2c_mode = I2C_MASTER_READ;
- i2c_ndata = size;
- i2c_data = buf;
- i2c_seq = 0;
- I2C_START();
-}
-
-void i2c_handle_send(void)
-{
- unsigned char state = TWSR & 0xf8;
-
- //printf("DBG i2c state (%x)\n", (unsigned int)state);
- LOGNUM(state);
-
- switch(state) {
- case ST_REP_START: /* 0x10 */
- /* repeated start, same as start, but also increment i2c_seq */
- i2c_seq++;
- case ST_START: /* 0x8 */
- /* start initiated, write the slave address */
- //printf("DBG i2c SLA: %x\n", (unsigned int)i2c_addr);
- TWDR = i2c_addr;
- I2C_WRITE();
- break;
-
- case ST_SLA_W_ACK: /* 0x18 */
- /* slave addr sent and ACKed, send subaddr or data */
- if(i2c_seq == 0) {
- /* this is the first packet, send subaddr */
- i2c_seq++;
- TWDR = i2c_subaddr;
- I2C_WRITE();
- } else {
- if(i2c_ndata--) {
- TWDR = *i2c_data++;
- I2C_WRITE();
- } else {
- /* done sending, send stop */
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- }
- }
- break;
-
- case ST_SLA_W_NACK: /* 0x20 */
- /* slave addr sent but not ACKed, abort */
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- printf("i2c: NACK after SLA+W\n");
- break;
-
- case ST_WR_ACK: /* 0x28 */
- /* data (or subaddr) sent and ACKed, send more data (or restart) if available */
-#if 0
- if(i2c_seq == 0) {
- /* subaddr was sent, send repeated start */
- I2C_START();
- } else {
-#endif
- /* data was sent, send more data or stop */
- if(i2c_ndata--) {
- TWDR = *i2c_data++;
- I2C_WRITE();
- } else {
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- }
-// }
- break;
-
- case ST_WR_NACK: /* 0x30 */
- /* data (or subaddr) sent but not ACKed */
- if(i2c_seq == 0) {
- /* NACK after subaddr, abort */
- printf("i2c: NACK after subaddr\n");
- } else {
- /* NACK after data */
- if(i2c_ndata) {
- printf("i2c: NACK with %d pending\n", i2c_ndata);
- }
- }
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- break;
-
- case ST_ARBLOST: /* 0x38 */
- /* arbitration lost */
- printf("i2c: arb lost\n");
- I2C_START();
- break;
-
- case ST_INVALID: /* 0 */
- printf("i2c: invalid start/stop\n");
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- break;
-
- default:
- printf("i2c: unexpected state (W): 0x%x\n", state);
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- }
-}
-
-void i2c_handle_recv(void)
-{
- unsigned char state = TWSR & 0xf8;
-
- LOGNUM(state);
-
- switch(state) {
- case ST_START: /* 0x8 */
- TWDR = i2c_addr; /* start a *write* (we need to send the subaddress before reading) */
- I2C_WRITE();
- break;
-
- case ST_REP_START: /* 0x10 */
- /* repeated start, now we can issue the actual read */
- TWDR = i2c_addr | 1;
- I2C_WRITE();
- break;
-
- case ST_SLA_W_ACK: /* 0x18 */
- /* SLA+W means we just started the write part, need to send the subaddress */
- TWDR = i2c_subaddr;
- I2C_WRITE();
- break;
-
- case ST_SLA_W_NACK:
- /* slave addr sent but not ACKed, abort */
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- printf("i2c: NACK after SLA+W (R)\n");
- break;
-
- case ST_WR_ACK: /* 0x28 */
- /* the subaddress write was ACKed, rep-start to issue the read */
- I2C_START();
- break;
-
- case ST_WR_NACK:
- /* the subaddress write was not ACKed, abort */
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- printf("i2c: NACK after subaddr (R)\n");
- break;
-
- case ST_SLA_R_ACK: /* 0x40 */
- /* SLA+R was ACKed send ACK to start receiving */
- I2C_READ_ACK();
- break;
-
- case ST_RD_ACK: /* 0x50 */
- /* ... or last read was ACKed, again read next and ACK for more */
- *i2c_data++ = TWDR;
- if(--i2c_ndata > 0) {
- I2C_READ_ACK();
- } else {
- I2C_READ_NACK();
- }
- break;
-
- case ST_SLA_R_NACK: /* 0x48 */
- /* SLA+R was sent but ACK was not received, abort */
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- printf("i2c: NACK after SLA+R\n");
- break;
-
- case ST_RD_NACK: /* 0x58 */
- /* read without ACK, we get this after we send a NACK, or this is the last byte (?) */
- if(i2c_ndata > 0) {
- *i2c_data++ = TWDR;
- i2c_ndata--;
- }
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- break;
-
- case ST_ARBLOST: /* 0x38 */
- /* arbitration lost */
- printf("i2c: arb lost\n");
- I2C_START();
- break;
-
- case ST_INVALID: /* 0 */
- printf("i2c: invalid start/stop\n");
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- break;
-
- default:
- printf("i2c: unexpected state (R): 0x%x\n", state);
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- }
-}
-
-ISR(TWI_vect)
-{
- switch(i2c_mode) {
- case I2C_MASTER_WRITE:
- i2c_handle_send();
- break;
-
- case I2C_MASTER_READ:
- i2c_handle_recv();
- break;
- }
-}
-
-void i2c_wait(void)
-{
- while(i2c_mode);
-}
-
-void i2c_async(void (*donecb)(void))
-{
- async_func = donecb;
-}
-
-void i2c_check_async(void)
-{
- if(async_func && i2c_mode == I2C_IDLE) {
- async_func();
- async_func = 0;
- }
-}
-
-void i2c_hold(void)
-{
- TWCR = 0; /* make sure the AVR i2c hardware is disabled */
- DDRC = 0x20; /* ... and drive SCL pin low */
- PORTC = 0;
-}
-
-void i2c_release(void)
-{
- DDRC = 0;
-}
-
-#define PC_SCL 5
-#define PC_SDA 4
-#define PC_SCL_BIT (1 << PC_SCL)
-#define PC_SDA_BIT (1 << PC_SDA)
-
-static unsigned char pcprev, state;
-static unsigned char value;
-static int nbits;
-
-/* use PCINT12,13 to snoop i2c traffic */
-void i2c_snoop(int onoff)
-{
- snooping = onoff;
-
- if(!onoff) {
-#ifdef USE_PCINT
- /* to stop snooping just disable the interrupt */
- cli();
- PCMSK1 = 0;
- PCICR &= ~(1 << PCIE1);
- sei();
-#endif
- return;
- }
-
- TWCR = 0; /* make sure the i2c hw is disabled */
- DDRC = 0;
- PORTC = 0;
-
-#ifdef USE_PCINT
- cli();
- PCICR |= 1 << PCIE1; /* enable the pin-change interrupt */
- PCMSK1 = (1 << PCINT12) | (1 << PCINT13);
-#endif
-
- pcprev = PINC;
- state = 0;
- value = 0;
- nbits = 0;
-#ifdef USE_PCINT
- sei();
-#endif
-}
-
-#ifdef USE_PCINT
-ISR(PCINT1_vect)
-#else
-void snoop(void)
-#endif
-{
- unsigned char pinc, delta;
-
- pinc = PINC;
- delta = pinc ^ pcprev;
- pcprev = pinc;
-
- if(delta & PC_SDA_BIT) {
- if(pinc & PC_SCL_BIT) {
- /* clock is high, so this is either a start or a stop */
- if(pinc & PC_SDA_BIT) {
- EVLOG(EV_STOP, 0);
- state = 0;
- } else {
- EVLOG(EV_START, 0);
- state = EV_START;
- value = 0;
- nbits = 0;
- }
- return;
- }
- }
-
- if(!state) {
- uint16_t val, ev;
- if(evq_wr != evq_rd) {
- val = evq[evq_rd];
- evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1);
- ev = val >> 8;
- if(ev <= EV_STOP) {
- printf("%s\n", evname[ev]);
- } else {
- printf("%s: %02x\n", evname[ev], val & 0xff);
- }
- }
- return;
- }
-
- if(delta & PC_SCL_BIT) {
- if(pinc & PC_SCL_BIT) {
- /* clock is going high, shift SDA */
- value = (value << 1) | ((pinc >> PC_SDA) & 1);
- if(++nbits >= 8) {
- switch(state) {
- case EV_START:
- state = (value & 1) ? EV_SLA_R : EV_SLA_W;
- EVLOG(state, value & 0xfe);
- break;
- case EV_SLA_W:
- case EV_SLA_R:
- state = EV_DATA;
- EVLOG(state, value);
- case EV_DATA:
- //EVLOG(state, value);
- default:
- break;
- }
- nbits = 0;
- value = 0;
- }
- }
- }
-
- PORTB ^= 1;
-}
+++ /dev/null
-#ifndef F_CPU
-#ifdef XTAL
-#define F_CPU XTAL
-#else
-#warning "compiled for 1mhz internal rc osc. serial comms won't work"
-#define F_CPU 1000000
-#endif
-#endif
-
-#include <stdio.h>
-#include <avr/io.h>
-#include <avr/interrupt.h>
-#include <util/delay.h>
-#include <avr/power.h>
-
-static int uart_send_char(char c, FILE *fp);
-static int uart_get_char(FILE *fp);
-
-#define BUF_SZ 16
-#define BUF_IDX_MASK (BUF_SZ - 1)
-#define NEXT_IDX(x) (((x) + 1) & BUF_IDX_MASK)
-static char outbuf[BUF_SZ];
-static volatile unsigned char out_rd, out_wr;
-static char inbuf[BUF_SZ];
-static volatile unsigned char in_rd, in_wr;
-
-static FILE std_stream = FDEV_SETUP_STREAM(uart_send_char, uart_get_char, _FDEV_SETUP_RW);
-
-
-
-void init_serial(long baud)
-{
- unsigned int ubrr_val = F_CPU / 16 / baud - 1;
-
- power_usart0_enable();
-
- /* set baud generator timer reset value */
- UBRR0H = (unsigned char)(ubrr_val >> 8);
- UBRR0L = (unsigned char)ubrr_val;
-
- /* enable rx/tx and recv interrupt */
- UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);
- /* set frame format: 8n1 */
- UCSR0C = 3 << UCSZ00;
-
- stdin = stdout = stderr = &std_stream;
-}
-
-int have_input(void)
-{
- return in_wr != in_rd;
-}
-
-static int uart_send_char(char c, FILE *fp)
-{
- /*int next;*/
- if(c == '\n') uart_send_char('\r', fp);
-
- while((UCSR0A & (1 << UDRE0)) == 0);
- UDR0 = (unsigned char)c;
-#if 0
- next = NEXT_IDX(out_wr);
- while(next == out_rd);
-
- outbuf[out_wr] = c;
- out_wr = next;
-
- /* enable the Tx data register empty interrupt */
- UCSR0B |= 1 << UDRIE0;
-#endif
- return 0;
-}
-
-static int uart_get_char(FILE *fp)
-{
- char c;
-
- while(in_rd == in_wr);
-
- c = inbuf[in_rd];
- in_rd = NEXT_IDX(in_rd);
- return c;
-}
-
-ISR(USART_RX_vect)
-{
- char c = UDR0;
-
- inbuf[in_wr] = c;
- in_wr = NEXT_IDX(in_wr);
-}
-
-/* USART Tx data register empty (can send more data) */
-ISR(USART_UDRE_vect)
-{
- if(out_rd != out_wr) {
- UDR0 = outbuf[out_rd];
- out_rd = NEXT_IDX(out_rd);
- } else {
- /* no more data to send for now, disable the interrupt */
- UCSR0B &= ~(1 << UDRIE0);
- }
-}
+++ /dev/null
-#ifndef SERIAL_H_
-#define SERIAL_H_
-
-void init_serial(long baud);
-int have_input(void);
-
-#endif /* SERIAL_H_ */
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include "i2c.h"
+
+enum {
+ I2C_IDLE,
+ I2C_MASTER_WRITE,
+ I2C_MASTER_READ
+};
+
+enum {
+ ST_INVALID = 0,
+ ST_UNKNOWN = 0xf8,
+ ST_START = 0x08,
+ ST_REP_START = 0x10,
+ ST_SLA_W_ACK = 0x18,
+ ST_SLA_W_NACK = 0x20,
+ ST_WR_ACK = 0x28,
+ ST_WR_NACK = 0x30,
+ ST_ARBLOST = 0x38,
+ ST_SLA_R_ACK = 0x40,
+ ST_SLA_R_NACK = 0x48,
+ ST_RD_ACK = 0x50,
+ ST_RD_NACK = 0x58,
+ ST_MATCH_W = 0x60,
+ ST_ARBLOST_MATCH = 0x68,
+ ST_GENMATCH = 0x70,
+ ST_ARBLOST_GENMATCH = 0x78,
+ ST_SLAVE_RD_ACK = 0x80,
+ ST_SLAVE_RD_NACK = 0x88,
+ ST_SLAVE_GENRD_ACK = 0x90,
+ ST_SLAVE_GENRD_NACK = 0x98,
+ ST_SLAVE_STARTSTOP = 0xa0,
+ ST_SLAVE_SLA_WR_ACK = 0xa8,
+ ST_SLAVE_ARBLOST_WR_ACK = 0xb0,
+ ST_SLAVE_WR_ACK = 0xb8,
+ ST_SLAVE_WR_NACK = 0xc0,
+ ST_SLAVE_LAST_WR_ACK = 0xc8
+};
+
+enum {
+ EV_DEBUG,
+ EV_START,
+ EV_STOP,
+ EV_SLA_W,
+ EV_SLA_R,
+ EV_DATA
+};
+
+#define TWINT_BIT (1 << TWINT)
+#define TWEA_BIT (1 << TWEA)
+#define TWSTA_BIT (1 << TWSTA)
+#define TWSTO_BIT (1 << TWSTO)
+#define TWEN_BIT (1 << TWEN)
+#define TWIE_BIT (1 << TWIE)
+
+#define I2C_START() (TWCR = TWINT_BIT | TWSTA_BIT | TWEN_BIT | TWIE_BIT)
+#define I2C_STOP() (TWCR = TWINT_BIT | TWSTO_BIT | TWEN_BIT | TWIE_BIT)
+#define I2C_WRITE() (TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT)
+#define I2C_READ_ACK() (TWCR = TWINT_BIT | TWEA_BIT | TWEN_BIT | TWIE_BIT)
+#define I2C_READ_NACK() (TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT)
+
+static unsigned char i2c_addr, i2c_subaddr;
+static volatile unsigned char i2c_mode;
+static int i2c_seq, i2c_ndata;
+static unsigned char *i2c_data;
+
+static void (*async_func)(void);
+
+void i2c_init(void)
+{
+ TWBR = 10; /* 10 with 1x prescaler should make it about 100khz */
+ TWSR = 0; /* prescaler 1x */
+ TWAR = 0;
+ TWAMR = 0;
+ TWCR = 0; /* I2C disabled by default */
+
+ i2c_mode = I2C_IDLE;
+}
+
+
+void i2c_write(unsigned char addr, unsigned char subaddr, unsigned char *data, int ndata)
+{
+ i2c_addr = addr & 0xfe;
+ i2c_subaddr = subaddr;
+ i2c_mode = I2C_MASTER_WRITE;
+ i2c_ndata = ndata;
+ i2c_data = data;
+ i2c_seq = 0;
+ I2C_START();
+}
+
+void i2c_read(unsigned char addr, unsigned char subaddr, unsigned char *buf, int size)
+{
+ i2c_addr = addr & 0xfe;
+ i2c_subaddr = subaddr;
+ i2c_mode = I2C_MASTER_READ;
+ i2c_ndata = size;
+ i2c_data = buf;
+ i2c_seq = 0;
+ I2C_START();
+}
+
+void i2c_handle_send(void)
+{
+ unsigned char state = TWSR & 0xf8;
+
+ /*LOGNUM(state);*/
+
+ switch(state) {
+ case ST_REP_START: /* 0x10 */
+ /* repeated start, same as start, but also increment i2c_seq */
+ i2c_seq++;
+ case ST_START: /* 0x8 */
+ /* start initiated, write the slave address */
+ //printf("DBG i2c SLA: %x\n", (unsigned int)i2c_addr);
+ TWDR = i2c_addr;
+ I2C_WRITE();
+ break;
+
+ case ST_SLA_W_ACK: /* 0x18 */
+ /* slave addr sent and ACKed, send subaddr or data */
+ if(i2c_seq == 0) {
+ /* this is the first packet, send subaddr */
+ i2c_seq++;
+ TWDR = i2c_subaddr;
+ I2C_WRITE();
+ } else {
+ if(i2c_ndata--) {
+ TWDR = *i2c_data++;
+ I2C_WRITE();
+ } else {
+ /* done sending, send stop */
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ }
+ }
+ break;
+
+ case ST_SLA_W_NACK: /* 0x20 */
+ /* slave addr sent but not ACKed, abort */
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ printf("i2c: NACK after SLA+W\n");
+ break;
+
+ case ST_WR_ACK: /* 0x28 */
+ /* data (or subaddr) sent and ACKed, send more data (or restart) if available */
+#if 0
+ if(i2c_seq == 0) {
+ /* subaddr was sent, send repeated start */
+ I2C_START();
+ } else {
+#endif
+ /* data was sent, send more data or stop */
+ if(i2c_ndata--) {
+ TWDR = *i2c_data++;
+ I2C_WRITE();
+ } else {
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ }
+// }
+ break;
+
+ case ST_WR_NACK: /* 0x30 */
+ /* data (or subaddr) sent but not ACKed */
+ if(i2c_seq == 0) {
+ /* NACK after subaddr, abort */
+ printf("i2c: NACK after subaddr\n");
+ } else {
+ /* NACK after data */
+ if(i2c_ndata) {
+ printf("i2c: NACK with %d pending\n", i2c_ndata);
+ }
+ }
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ break;
+
+ case ST_ARBLOST: /* 0x38 */
+ /* arbitration lost */
+ printf("i2c: arb lost\n");
+ I2C_START();
+ break;
+
+ case ST_INVALID: /* 0 */
+ printf("i2c: invalid start/stop\n");
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ break;
+
+ default:
+ printf("i2c: unexpected state (W): 0x%x\n", state);
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ }
+}
+
+void i2c_handle_recv(void)
+{
+ unsigned char state = TWSR & 0xf8;
+
+ /*LOGNUM(state);*/
+
+ switch(state) {
+ case ST_START: /* 0x8 */
+ TWDR = i2c_addr; /* start a *write* (we need to send the subaddress before reading) */
+ I2C_WRITE();
+ break;
+
+ case ST_REP_START: /* 0x10 */
+ /* repeated start, now we can issue the actual read */
+ TWDR = i2c_addr | 1;
+ I2C_WRITE();
+ break;
+
+ case ST_SLA_W_ACK: /* 0x18 */
+ /* SLA+W means we just started the write part, need to send the subaddress */
+ TWDR = i2c_subaddr;
+ I2C_WRITE();
+ break;
+
+ case ST_SLA_W_NACK:
+ /* slave addr sent but not ACKed, abort */
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ printf("i2c: NACK after SLA+W (R)\n");
+ break;
+
+ case ST_WR_ACK: /* 0x28 */
+ /* the subaddress write was ACKed, rep-start to issue the read */
+ I2C_START();
+ break;
+
+ case ST_WR_NACK:
+ /* the subaddress write was not ACKed, abort */
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ printf("i2c: NACK after subaddr (R)\n");
+ break;
+
+ case ST_SLA_R_ACK: /* 0x40 */
+ /* SLA+R was ACKed send ACK to start receiving */
+ I2C_READ_ACK();
+ break;
+
+ case ST_RD_ACK: /* 0x50 */
+ /* ... or last read was ACKed, again read next and ACK for more */
+ *i2c_data++ = TWDR;
+ if(--i2c_ndata > 0) {
+ I2C_READ_ACK();
+ } else {
+ I2C_READ_NACK();
+ }
+ break;
+
+ case ST_SLA_R_NACK: /* 0x48 */
+ /* SLA+R was sent but ACK was not received, abort */
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ printf("i2c: NACK after SLA+R\n");
+ break;
+
+ case ST_RD_NACK: /* 0x58 */
+ /* read without ACK, we get this after we send a NACK, or this is the last byte (?) */
+ if(i2c_ndata > 0) {
+ *i2c_data++ = TWDR;
+ i2c_ndata--;
+ }
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ break;
+
+ case ST_ARBLOST: /* 0x38 */
+ /* arbitration lost */
+ printf("i2c: arb lost\n");
+ I2C_START();
+ break;
+
+ case ST_INVALID: /* 0 */
+ printf("i2c: invalid start/stop\n");
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ break;
+
+ default:
+ printf("i2c: unexpected state (R): 0x%x\n", state);
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ }
+}
+
+ISR(TWI_vect)
+{
+ switch(i2c_mode) {
+ case I2C_MASTER_WRITE:
+ i2c_handle_send();
+ break;
+
+ case I2C_MASTER_READ:
+ i2c_handle_recv();
+ break;
+ }
+}
+
+void i2c_wait(void)
+{
+ while(i2c_mode);
+}
+
+void i2c_async(void (*donecb)(void))
+{
+ async_func = donecb;
+}
+
+void i2c_check_async(void)
+{
+ if(async_func && i2c_mode == I2C_IDLE) {
+ async_func();
+ async_func = 0;
+ }
+}
+
+void i2c_hold(void)
+{
+ TWCR = 0; /* make sure the AVR i2c hardware is disabled */
+ DDRC = 0x20; /* ... and drive SCL pin low */
+ PORTC = 0;
+}
+
+void i2c_release(void)
+{
+ DDRC = 0;
+}
+
+void i2c_abort(void)
+{
+ if(i2c_mode != I2C_IDLE) {
+ i2c_mode = I2C_IDLE;
+ I2C_STOP();
+ }
+}
--- /dev/null
+#ifndef I2C_H_
+#define I2C_H_
+
+void i2c_init(void);
+void i2c_write(unsigned char addr, unsigned char subaddr, unsigned char *data, int ndata);
+void i2c_read(unsigned char addr, unsigned char subaddr, unsigned char *buf, int size);
+void i2c_wait(void);
+void i2c_async(void (*donecb)(void));
+void i2c_check_async(void);
+void i2c_hold(void);
+void i2c_release(void);
+void i2c_abort(void);
+
+
+#endif /* I2C_H_ */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+#include "serial.h"
+#include "i2c.h"
+
+/* TODO
+ * IR decoding, connected to PCINT0
+ * enable/disable with switch on PCINT1
+ * some of the buttons multiplexed on ADC0
+ */
+
+#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 unsigned char data[16];
+
+static uint16_t dump_addr;
+static int dump_count;
+static int dbgmode;
+
+static void proc_cmd(char *input);
+static void printstat(void);
+static void printdump(void);
+
+
+int main(void)
+{
+ uint16_t val;
+
+ /* tri-state everything and disable pullups */
+ DDRB = 1; /* B0: activity LED */
+ DDRC = 0; /* I2C pins as inputs */
+ DDRD = 0;
+ PORTB = 0;
+ PORTC = 0;
+ PORTD = 0;
+
+ i2c_init();
+
+ init_serial(38400);
+ sei();
+
+ printf("TV i2c hack\n");
+
+ for(;;) {
+ i2c_check_async();
+
+ if(have_input()) {
+ int c = getchar();
+ putchar(c);
+
+ if(c == '\r' || c == '\n') {
+ putchar('\n');
+ input[inp_cidx] = 0;
+ proc_cmd(input);
+ inp_cidx = 0;
+ } else if(inp_cidx < sizeof input - 1) {
+ input[inp_cidx++] = c;
+ }
+ }
+
+ /* read from queue and send over the serial port */
+ val = 0xffff;
+ cli();
+ if(evq_wr != evq_rd) {
+ val = evq[evq_rd];
+ evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1);
+ }
+ sei();
+
+ if(val != 0xffff) {
+ unsigned char ev = val >> 8;
+ printf("%02x: %02x\n", ev, val & 0xff);
+ }
+ }
+ return 0;
+}
+
+static void proc_cmd(char *input)
+{
+ char *endp;
+
+ if(strcmp(input, "dbg") == 0) {
+ printf("OK dbg\n");
+ dbgmode = 1;
+
+ } else if(strcmp(input, "nodbg") == 0) {
+ printf("OK nodbg\n");
+ dbgmode = 0;
+
+ } else if(strcmp(input, "av") == 0) {
+ printf("OK AV\n");
+
+ /* AV switch (22): 0 0 SVO CMB1 CMB0 INA INB 0 */
+ data[0] = 0x22;
+ i2c_write(0x8a, 0x22, data, 1);
+ i2c_async(i2c_hold);
+
+ } else if(strcmp(input, "rgb") == 0) {
+ printf("OK RGB\n");
+
+ /* Control 0 (2a): 0 IE2 RBL AKB CL3 CL2 CL1 CL0
+ * Control 1 (2b): 0 0 0 0 0 0 YUV HBL
+ */
+ data[0] = 0x70;
+ data[1] = 0;
+ i2c_write(0x8a, 0x2a, data, 1);
+ i2c_wait();
+ i2c_write(0x8a, 0x2b, data + 1, 1);
+
+ } else if(memcmp(input, "vol ", 4) == 0) {
+ int vol = atoi(input + 4);
+ if(vol < 1 || vol > 63) {
+ printf("ERR vol (%s)\n", input + 4);
+ } else {
+ data[0] = vol;
+ i2c_write(0x8a, 0x1f, data, 1);
+ printf("OK volume: %d\n", vol);
+ }
+
+ } else if(memcmp(input, "sat", 3) == 0) {
+ printf("OK sat\n");
+ data[0] = atoi(input + 3);
+ if(data[0] <= 0 || data[0] > 0x3f) {
+ data[0] = 0x1f;
+ }
+ i2c_write(0x8a, 0x1c, data, 1);
+ i2c_async(i2c_hold); /* hold I2C when done */
+
+ } else if(strcmp(input, "rel") == 0) {
+ printf("OK release\n");
+ i2c_release();
+
+ } else if(strcmp(input, "status") == 0) {
+ i2c_read(0x8a, 0, data, 3);
+ i2c_async(printstat);
+
+ } else if(memcmp(input, "rd ", 3) == 0) {
+ dump_addr = strtol(input + 3, &endp, 16);
+
+ if(endp > input + 3 && dump_addr >= 0 && dump_addr < 2048) {
+ dump_count = 1;
+ i2c_read(0xa0 | ((dump_addr >> 7) & 0xe), dump_addr & 0xff, data, 1);
+ i2c_async(printdump);
+ } else {
+ printf("ERR address: %s\n", input + 3);
+ }
+
+ } else if(memcmp(input, "dump ", 5) == 0) {
+ dump_addr = strtol(input + 5, &endp, 16);
+
+ if(endp > input + 5 && dump_addr >= 0 && dump_addr < 2048) {
+ dump_count = 1;
+ dump_addr &= 0xff0;
+ i2c_read(0xa0 | ((dump_addr >> 7) & 0xe), dump_addr & 0xff, data, 16);
+ i2c_async(printdump);
+ } else {
+ printf("ERR address: %s\n", input + 5);
+ }
+
+ } else if(strcmp(input, "abort") == 0) {
+ i2c_abort();
+ printf("OK\n");
+
+ } else {
+ printf("ERR command (%s)\n", input);
+ }
+}
+
+static void printstat(void)
+{
+ printf("OK status: %02x %02x %02x\n", (unsigned int)data[0],
+ (unsigned int)data[1], (unsigned int)data[2]);
+}
+
+static void printdump(void)
+{
+ int i;
+ while(dump_count > 0) {
+ printf("OK %03x:", dump_addr);
+ for(i=0; i<16; i++) {
+ if(i == 8) putchar(' ');
+
+ if(--dump_count >= 0) {
+ printf(" %02x", data[i]);
+ } else {
+ printf(" ");
+ }
+ dump_addr += 16;
+ }
+ putchar('\n');
+ }
+}
--- /dev/null
+#ifndef F_CPU
+#ifdef XTAL
+#define F_CPU XTAL
+#else
+#warning "compiled for 1mhz internal rc osc. serial comms won't work"
+#define F_CPU 1000000
+#endif
+#endif
+
+#include <stdio.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+#include <avr/power.h>
+
+static int uart_send_char(char c, FILE *fp);
+static int uart_get_char(FILE *fp);
+
+#define BUF_SZ 16
+#define BUF_IDX_MASK (BUF_SZ - 1)
+#define NEXT_IDX(x) (((x) + 1) & BUF_IDX_MASK)
+static char outbuf[BUF_SZ];
+static volatile unsigned char out_rd, out_wr;
+static char inbuf[BUF_SZ];
+static volatile unsigned char in_rd, in_wr;
+
+static FILE std_stream = FDEV_SETUP_STREAM(uart_send_char, uart_get_char, _FDEV_SETUP_RW);
+
+
+
+void init_serial(long baud)
+{
+ unsigned int ubrr_val = F_CPU / 16 / baud - 1;
+
+ power_usart0_enable();
+
+ /* set baud generator timer reset value */
+ UBRR0H = (unsigned char)(ubrr_val >> 8);
+ UBRR0L = (unsigned char)ubrr_val;
+
+ /* enable rx/tx and recv interrupt */
+ UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);
+ /* set frame format: 8n1 */
+ UCSR0C = 3 << UCSZ00;
+
+ stdin = stdout = stderr = &std_stream;
+}
+
+int have_input(void)
+{
+ return in_wr != in_rd;
+}
+
+static int uart_send_char(char c, FILE *fp)
+{
+ /*int next;*/
+ if(c == '\n') uart_send_char('\r', fp);
+
+ while((UCSR0A & (1 << UDRE0)) == 0);
+ UDR0 = (unsigned char)c;
+#if 0
+ next = NEXT_IDX(out_wr);
+ while(next == out_rd);
+
+ outbuf[out_wr] = c;
+ out_wr = next;
+
+ /* enable the Tx data register empty interrupt */
+ UCSR0B |= 1 << UDRIE0;
+#endif
+ return 0;
+}
+
+static int uart_get_char(FILE *fp)
+{
+ char c;
+
+ while(in_rd == in_wr);
+
+ c = inbuf[in_rd];
+ in_rd = NEXT_IDX(in_rd);
+ return c;
+}
+
+ISR(USART_RX_vect)
+{
+ char c = UDR0;
+
+ inbuf[in_wr] = c;
+ in_wr = NEXT_IDX(in_wr);
+}
+
+/* USART Tx data register empty (can send more data) */
+ISR(USART_UDRE_vect)
+{
+ if(out_rd != out_wr) {
+ UDR0 = outbuf[out_rd];
+ out_rd = NEXT_IDX(out_rd);
+ } else {
+ /* no more data to send for now, disable the interrupt */
+ UCSR0B &= ~(1 << UDRIE0);
+ }
+}
--- /dev/null
+#ifndef SERIAL_H_
+#define SERIAL_H_
+
+void init_serial(long baud);
+int have_input(void);
+
+#endif /* SERIAL_H_ */