From 3b128b470c001555ba1c69ba8b0c96994182f8a1 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Mon, 8 Mar 2021 11:14:57 +0200 Subject: [PATCH] code cleanup, dropped the sniffer --- .gitignore | 2 +- Makefile | 6 +- main.c | 702 ---------------------------------------------------------- serial.c | 103 --------- serial.h | 7 - src/i2c.c | 344 ++++++++++++++++++++++++++++ src/i2c.h | 15 ++ src/main.c | 215 ++++++++++++++++++ src/serial.c | 103 +++++++++ src/serial.h | 7 + 10 files changed, 688 insertions(+), 816 deletions(-) delete mode 100644 main.c delete mode 100644 serial.c delete mode 100644 serial.h create mode 100644 src/i2c.c create mode 100644 src/i2c.h create mode 100644 src/main.c create mode 100644 src/serial.c create mode 100644 src/serial.h diff --git a/.gitignore b/.gitignore index 3016d77..b890f1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ *.o *.d *.swp -test +tv_i2c_hack *.eep *.hex *.map diff --git a/Makefile b/Makefile index 9d5bcf1..05efc87 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -src = $(wildcard *.c) +src = $(wildcard src/*.c) obj = $(src:.c=.o) -bin = test +bin = tv_i2c_hack hex = $(bin).hex eep = $(bin).eep @@ -12,7 +12,7 @@ OBJCOPY = avr-objcopy 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 diff --git a/main.c b/main.c deleted file mode 100644 index fa0be2f..0000000 --- a/main.c +++ /dev/null @@ -1,702 +0,0 @@ -#include -#include -#include -#include -#include -#include -#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; -} diff --git a/serial.c b/serial.c deleted file mode 100644 index 3ca641a..0000000 --- a/serial.c +++ /dev/null @@ -1,103 +0,0 @@ -#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 -#include -#include -#include -#include - -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); - } -} diff --git a/serial.h b/serial.h deleted file mode 100644 index 615626e..0000000 --- a/serial.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef SERIAL_H_ -#define SERIAL_H_ - -void init_serial(long baud); -int have_input(void); - -#endif /* SERIAL_H_ */ diff --git a/src/i2c.c b/src/i2c.c new file mode 100644 index 0000000..c7af41d --- /dev/null +++ b/src/i2c.c @@ -0,0 +1,344 @@ +#include +#include +#include +#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(); + } +} diff --git a/src/i2c.h b/src/i2c.h new file mode 100644 index 0000000..e26e43d --- /dev/null +++ b/src/i2c.h @@ -0,0 +1,15 @@ +#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_ */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..2584292 --- /dev/null +++ b/src/main.c @@ -0,0 +1,215 @@ +#include +#include +#include +#include +#include +#include +#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'); + } +} diff --git a/src/serial.c b/src/serial.c new file mode 100644 index 0000000..3ca641a --- /dev/null +++ b/src/serial.c @@ -0,0 +1,103 @@ +#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 +#include +#include +#include +#include + +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); + } +} diff --git a/src/serial.h b/src/serial.h new file mode 100644 index 0000000..615626e --- /dev/null +++ b/src/serial.h @@ -0,0 +1,7 @@ +#ifndef SERIAL_H_ +#define SERIAL_H_ + +void init_serial(long baud); +int have_input(void); + +#endif /* SERIAL_H_ */ -- 1.7.10.4