From 3efe37d45bdac5a1255f3f1aa39912f3af31bfb6 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Tue, 2 Mar 2021 03:11:21 +0200 Subject: [PATCH] I can send commands, but the tv overrides them almost immediately --- .gitignore | 7 ++ Makefile | 43 +++++++++ main.c | 316 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ serial.c | 103 ++++++++++++++++++++ serial.h | 7 ++ 5 files changed, 476 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 main.c create mode 100644 serial.c create mode 100644 serial.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3016d77 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.o +*.d +*.swp +test +*.eep +*.hex +*.map diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..86277c5 --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +src = $(wildcard *.c) +obj = $(src:.c=.o) +bin = test +hex = $(bin).hex +eep = $(bin).eep + +mcu_gcc = atmega168 +mcu_dude = m168 + +CC = avr-gcc +OBJCOPY = avr-objcopy + +warn = -pedantic -Wall + +CFLAGS = -Os $(warn) -mmcu=$(mcu_gcc) -DF_CPU=3686400 +LDFLAGS = -Wl,-Map,$(bin).map -mmcu=$(mcu_gcc) -lprintf_min + +.PHONY: all +all: $(hex) $(eep) + +$(bin): $(obj) + $(CC) -o $@ $(obj) $(LDFLAGS) + +$(hex): $(bin) + $(OBJCOPY) -j .text -j .data -O ihex -R .eeprom $< $@ + +$(bin).bin: $(bin) + $(OBJCOPY) -j .text -j .data -O binary -R .eeprom $< $@ + +$(eep): $(bin) + $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@ + +.PHONY: fuses +fuses: + avrdude -c usbtiny -p $(mcu_dude) -U lfuse:w:0xe7:m -U hfuse:w:0xdf:m -U efuse:w:0xf9:m + +.PHONY: program +program: $(hex) + avrdude -c usbtiny -p $(mcu_dude) -e -U flash:w:$(hex) + +.PHONY: clean +clean: + rm -f $(bin) $(obj) $(hex) $(eep) $(bin).map diff --git a/main.c b/main.c new file mode 100644 index 0000000..8e3a5c9 --- /dev/null +++ b/main.c @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include +#include +#include "serial.h" + +enum { + I2C_IDLE, + I2C_MASTER_SEND, + I2C_MASTER_RECV +}; + +#define TWINT_BIT (1 << TWINT) +#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) + +unsigned char i2c_addr, i2c_subaddr, 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) \ + do { \ + evq[evq_wr] = x; \ + 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 void proc_cmd(char *input); +void i2c_write(unsigned char addr, unsigned char subaddr, int ndata, unsigned char *data); + +int main(void) +{ + /* tri-state everything and disable pullups */ + DDRB = 1; /* B0: activity LED */ + DDRC = 0; + DDRD = 0; + PORTB = 0; + PORTC = 0; + PORTD = 0; + + TWBR = 10; /* 10 with 1x prescaler should make it about 100khz */ + TWSR = 0; /* prescaler 1x */ + TWAR = 0xff; + TWCR = (1 << TWEN) | (1 << TWIE); /* enable i2c, don't ack, enable intr */ + TWAR = 0xff; + TWAMR = 0;//0xff; /* match all addresses to monitor the bus */ + + i2c_mode = I2C_IDLE; + + init_serial(38400); + sei(); + + printf("Starting i2c hack\n"); + + for(;;) { + uint16_t val = 0xffff; + + 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 */ + cli(); + if(evq_wr != evq_rd) { + val = evq[evq_rd]; + evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1); + } + sei(); + + if(val != 0xffff) { + printf("s(%u): %x\n", (unsigned int)(val >> 8), (unsigned int)(val & 0xff)); + } + } + return 0; +} + +static void proc_cmd(char *input) +{ + unsigned char data[8]; + + if(strcmp(input, "rgb") == 0) { + printf("OK sending RGB switch command\n"); + + /* i2c addr 8b: jungle + * subaddr 2b: Control 1 reg + * bit 1: YUV + */ + data[0] = 2; + i2c_write(0x8a, 0x2b, 1, data); + + } else if(memcmp(input, "vol ", 4) == 0) { + int vol = atoi(input + 4); + if(vol < 1 || vol > 63) { + printf("ERR invalid vol (%s)\n", input + 4); + } else { + data[0] = vol; + i2c_write(0x8a, 0x1f, 1, data); + printf("OK volume: %d\n", vol); + } + + } else if(strcmp(input, "zoom") == 0) { + data[0] = 0x3f; + i2c_write(0x8a, 0x10, 1, data); + printf("OK zoom in\n"); + + } else if(strcmp(input, "unzoom") == 0) { + data[0] = 0x20; + i2c_write(0x8a, 0x10, 1, data); + printf("OK unzoom\n"); + + } else if(strcmp(input, "sat") == 0) { + data[0] = 0x3f; + i2c_write(0x8a, 0x1c, 1, data); + printf("OK saturate\n"); + + } else if(strcmp(input, "abort") == 0) { + if(i2c_mode != I2C_IDLE) { + i2c_mode = I2C_IDLE; + I2C_STOP(); + printf("OK aborting i2c op\n"); + } else { + printf("ERR i2c is idle\n"); + } + + } else { + printf("ERR invalid command (%s)\n", input); + } +} + +void i2c_write(unsigned char addr, unsigned char subaddr, int ndata, unsigned char *data) +{ + printf("i2c: write %x (sub: %x)\n", addr, subaddr); + i2c_addr = addr & 0xfe; + i2c_subaddr = subaddr; + i2c_mode = I2C_MASTER_SEND; + i2c_ndata = ndata; + i2c_data = data; + 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 0x10: + /* repeated start, same as start, but also increment i2c_seq */ + i2c_seq++; + case 0x8: + /* start initiated, write the slave address */ + //printf("DBG i2c SLA: %x\n", (unsigned int)i2c_addr); + TWDR = i2c_addr; + I2C_WRITE(); + break; + + case 0x18: + /* slave addr sent and ACKed, send subaddr or data */ + if(i2c_seq == 0) { + /* this is the first packet, send subaddr */ + i2c_seq++; + LOGNUM(0x100 | i2c_subaddr); + TWDR = i2c_subaddr; + I2C_WRITE(); + } else { + if(i2c_ndata--) { + LOGNUM(0x200 | *i2c_data); + TWDR = *i2c_data++; + I2C_WRITE(); + } else { + /* done sending, send stop */ + i2c_mode = I2C_IDLE; + I2C_STOP(); + } + } + break; + + case 0x20: + /* slave addr sent but not ACKed, abort */ + i2c_mode = I2C_IDLE; + I2C_STOP(); + printf("i2c: NACK after SLA+W\n"); + break; + + case 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--) { + LOGNUM(0x200 | *i2c_data); + TWDR = *i2c_data++; + I2C_WRITE(); + } else { + i2c_mode = I2C_IDLE; + I2C_STOP(); + } +// } + break; + + case 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 data packets pending\n", i2c_ndata); + } + } + i2c_mode = I2C_IDLE; + I2C_STOP(); + break; + + case 0x38: + /* arbitration lost */ + printf("i2c: arbitration lost\n"); + I2C_START(); + break; + + case 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) +{ + printf("i2c: recv unimplemented\n"); +} + +ISR(TWI_vect) +{ + uint16_t ev; + + switch(i2c_mode) { + case I2C_MASTER_SEND: + i2c_handle_send(); + break; + + case I2C_MASTER_RECV: + i2c_handle_recv(); + break; + + default: + /* log traffic on the bus */ + ev = (uint16_t)(TWSR & 0xf8) << 8; + switch(TWSR & 0xf8) { + case 0x80: /* own SLA+W received, ack */ + case 0x88: /* own SLA+W recv, no-ack */ + case 0x90: /* general recv, ack */ + case 0x98: /* general recv, no-ack */ + ev |= TWDR; + break; + + default: + break; + } + /* append data to input queue */ + /* + TWCR |= 1 << TWINT; + + evq[evq_wr] = ev; + evq_wr = (evq_wr + 1) & (EVQ_SIZE - 1); + if(evq_wr == evq_rd) { + evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1); + } + + PORTB = 1; + */ + } +} diff --git a/serial.c b/serial.c new file mode 100644 index 0000000..3ca641a --- /dev/null +++ b/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/serial.h b/serial.h new file mode 100644 index 0000000..615626e --- /dev/null +++ b/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