--- /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"
+
+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;
+ */
+ }
+}
--- /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);
+ }
+}