I can send commands, but the tv overrides them almost immediately
authorJohn Tsiombikas <nuclear@member.fsf.org>
Tue, 2 Mar 2021 01:11:21 +0000 (03:11 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Tue, 2 Mar 2021 01:11:21 +0000 (03:11 +0200)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
main.c [new file with mode: 0644]
serial.c [new file with mode: 0644]
serial.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..3016d77
--- /dev/null
@@ -0,0 +1,7 @@
+*.o
+*.d
+*.swp
+test
+*.eep
+*.hex
+*.map
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
index 0000000..8e3a5c9
--- /dev/null
+++ b/main.c
@@ -0,0 +1,316 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+#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 (file)
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 <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);
+       }
+}
diff --git a/serial.h b/serial.h
new file mode 100644 (file)
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_ */