Commits

Peter Ward committed b76a6a2

Stuff.

Comments (0)

Files changed (15)

examples/Makefile

 OBJCOPY = avr-objcopy
 AVRDUDE = avrdude
 
-CFLAGS_STD = -std=c99 -Wall -Wextra
-CFLAGS_OPT = -Os -mcall-prologues
+CFLAGS_STD = -std=c99 -Wall -Wextra -Winline
+CFLAGS_OPT_PARAMS = -fno-jump-tables
+CFLAGS_OPT_PARAMS := $(CFLAGS_OPT_PARAMS) --param large-function-insns=5000
+CFLAGS_OPT_PARAMS := $(CFLAGS_OPT_PARAMS) --param inline-unit-growth=100
+CFLAGS_OPT_PARAMS := $(CFLAGS_OPT_PARAMS) --param max-inline-recursive-depth=16
+CFLAGS_OPT = -O3 -mcall-prologues -fipa-cp-clone $(CFLAGS_OPT_PARAMS)
 CFLAGS_MACH = -mmcu=$(MCU) -DF_CPU=$(F_CPU)
 CFLAGS = $(CFLAGS_STD) $(CFLAGS_OPT) $(CFLAGS_MACH) -I../include
-LDFLAGS = -Wl,--gc-sections -Wl,-u,vfscanf -lscanf_min -lm
+#LDFLAGS = -Wl,--gc-sections -Wl,-u,vfscanf -lscanf_min -lm
+LDFLAGS = -Wl,--gc-sections
 
 AVRDUDE_FLAGS = -V -F -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) -b $(UPLOAD_RATE)
 
 %.o: %.c
 	$(CC) $(CFLAGS) -c -o $@ $<
 
-%.elf: $(OBJECTS)
+#$(OBJECTS)
+%.elf: %.o
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
 
 %.hex: %.elf
 	$(OBJCOPY) -O ihex -R .eeprom $< $@
+	avr-size $< $@
 
 upload: $(TARGET)
 	stty -F $(AVRDUDE_PORT) hupcl ; sleep 0.1 ; stty -F $(AVRDUDE_PORT) hupcl

examples/main.c

-#include <arduino/ed1.h>
-
-void shift_register_pwm(shift_register_t sr, int *levels) {
-    for (int i = 0; i < 100; i++) {
-        int value = 0;
-        for (int j = 0; j < 8; j++) {
-            if (i < levels[j])
-                value |= 1 << j;
-        }
-        shift_register_set(sr, LSBFIRST, value);
-    }
-    shift_register_set(sr, LSBFIRST, 0);
-}
-
-int main(void) {
-    // initialise the board
-    ed1_init();
-
-    // initialise the shift register
-    shift_register_set_mode(ed1_ledsr, OUTPUT);
-
-    int levels[8];
-
-    for (;;) {
-        // read a value from the potentiometer
-        int n = (long) analog_pin_read(ed1_pot) * 100 / 1023;
-
-        for (int i = 0; i < 8; i++)
-            levels[i] = n;
-
-        // fade the leds on the shift register
-        shift_register_pwm(ed1_ledsr, levels);
-    }
-}
+#include <arduino/ed1.h>
+
+void shift_register_pwm(shift_register_t sr, int *levels) {
+    for (int i = 0; i < 100; i++) {
+        int value = 0;
+        for (int j = 0; j < 8; j++) {
+            if (i < levels[j])
+                value |= 1 << j;
+        }
+        shift_register_set(sr, LSBFIRST, value);
+    }
+    shift_register_set(sr, LSBFIRST, 0);
+}
+
+int main(void) {
+    // initialise the board
+    ed1_init();
+
+    // initialise the shift register
+    shift_register_set_mode(ed1_ledsr, OUTPUT);
+
+    int levels[8];
+
+    for (;;) {
+        // read a value from the potentiometer
+        int n = (long) analog_pin_read(ed1_pot) * 100 / 1023;
+
+        for (int i = 0; i < 8; i++)
+            levels[i] = n;
+
+        // fade the leds on the shift register
+        shift_register_pwm(ed1_ledsr, levels);
+    }
+}

examples/pwm.h

-void shift_register_pwm(shift_register_t sr, int *levels) {
-    for (int i = 0; i < 100; i++) {
-        int value = 0;
-        for (int j = 0; j < 8; j++) {
-            if (i < levels[j])
-                value |= 1 << j;
-        }
-        shift_register_set(sr, LSBFIRST, value);
-    }
-    shift_register_set(sr, LSBFIRST, 0);
-}
-

examples/read_serial.py

-from serial import Serial
-s = Serial('/dev/ttyUSB0')
-while True:
-    print repr(s.read())

examples/write_serial.py

-from serial import Serial
-s = Serial('/dev/ttyUSB0')
-

include/arduino/accel.h

+#ifndef __ACCEL_H
+#define __ACCEL_H
+
+#include "twi.h"
+
+#define ACCEL_MEASURE_2G_MODE 0x05
+#define ACCEL_MEASURE_4G_MODE 0x09
+#define ACCEL_MEASURE_8G_MODE 0x01
+
+#define ACCEL_PULSE_MODE 0x03
+#define ACCEL_LEVEL_MODE 0x02
+
+typedef struct {
+    twi_t twi;
+    uint8_t part_addr;
+} accel_t;
+
+#define accel_create(t, a) \
+    (accel_t) { t, a }
+
+#define ACCEL_MODE_REG 0x16
+
+typedef enum {
+    ACCEL_AXIS_X,
+    ACCEL_AXIS_Y,
+    ACCEL_AXIS_Z
+} accel_axis_t;
+
+static inline
+void _accel_send(accel_t a, uint8_t reg, uint8_t val) {
+    uint8_t values[2] = {reg, val};
+    twi_write(a.twi, a.part_addr, values, 2);
+    delay_ms(10);
+}
+
+static inline
+uint8_t _accel_recv(accel_t a, uint8_t reg) {
+    uint8_t rv;
+    twi_write(a.twi, a.part_addr, &reg, 1);
+    twi_read(a.twi, a.part_addr, &rv, 1);
+    return rv;
+}
+
+static inline
+bool accel_init(accel_t a, uint8_t mode) {
+    // other modes are untested
+    if (mode != ACCEL_MEASURE_2G_MODE)
+        return false;
+
+    _accel_send(a, ACCEL_MODE_REG, mode);
+
+    // read to confirm part is responding
+    uint8_t check_mode = _accel_recv(a, ACCEL_MODE_REG);
+    if (check_mode != mode)
+        return false;
+
+    return true;
+}
+
+static inline
+int8_t accel_read(accel_t a, accel_axis_t dir) {
+    uint8_t reg;
+    switch (dir) {
+        case ACCEL_AXIS_X:
+            reg = 0x06;
+            break;
+        case ACCEL_AXIS_Y:
+            reg = 0x07;
+            break;
+        case ACCEL_AXIS_Z:
+            reg = 0x08;
+            break;
+        default:
+            // TODO: is this sane?
+            return 127; // ERROR
+    }
+    return _accel_recv(a, dir);
+}
+
+static inline
+bool accel_set_offsets(accel_t a, accel_axis_t dir, int val) {
+    uint8_t reg;
+    switch (dir) {
+        case ACCEL_AXIS_X:
+            reg = 0x10;
+            break;
+        case ACCEL_AXIS_Y:
+            reg = 0x12;
+            break;
+        case ACCEL_AXIS_Z:
+            reg = 0x14;
+            break;
+        default:
+            return false;
+    }
+
+    _accel_send(a, reg, val & 0x00ff);
+    _accel_send(a, reg + 1, val >> 8);
+
+    return true;
+}
+
+#endif

include/arduino/analog_pin.h

 
 const uint8_t analog_pin_reference = 1;
 
-//static inline
+static inline
 int analog_pin_read(analog_pin_t pin) {
     uint8_t low, high;
 

include/arduino/ed1.h

 #include <avr/io.h>
 #include <avr/interrupt.h>
 
+// DIGITAL PINS
 #include "pin.h"
-#include "analog_pin.h"
+
+#define ed1_led pin_create_simple(B, 5)
+#define ed1_piezo pin_create_simple(B, 1)
+
+#define ed1_button_left pin_create_simple(C, 0)
+#define ed1_button_right pin_create_simple(C, 1)
+
+#define ed1_ledsr_clock pin_create_simple(B, 2)
+#define ed1_ledsr_latch pin_create_simple(B, 3)
+#define ed1_ledsr_data pin_create_simple(B, 4)
+
+// SHIFT REGISTER
+
 #include "shift_register.h"
-#include "serial.h"
-
-#define ed1_led pin_create(&PORTB, &DDRB, 5)
-
-#define ed1_piezo pin_create(&PORTB, &DDRB, 1)
-
-#define ed1_ledsr_clock pin_create(&PORTB, &DDRB, 2)
-#define ed1_ledsr_latch pin_create(&PORTB, &DDRB, 3)
-#define ed1_ledsr_data pin_create(&PORTB, &DDRB, 4)
 
 #define ed1_ledsr shift_register_create( \
     ed1_ledsr_latch, \
     ed1_ledsr_data \
 )
 
+// ANALOG PINS
+
+#include "analog_pin.h"
+
 #define ed1_pot analog_pin_create(3)
 #define ed1_light analog_pin_create(6)
 #define ed1_temp analog_pin_create(7)
 
-unsigned char ed1_serial_buffer[128];
-ring_buffer_t ed1_serial_rb;
+// LCD
+
+#include "lcd.h"
+
+// rs, rw, en, d4, d5, d6, d7
+#define ed1_lcd lcd_create(\
+    pin_create_simple(D, 6), \
+    pin_create_simple(D, 7), \
+    pin_create_simple(B, 0), \
+    pin_create_simple(D, 2), \
+    pin_create_simple(D, 3), \
+    pin_create_simple(D, 4), \
+    pin_create_simple(D, 5), \
+    NULL_PIN, NULL_PIN, NULL_PIN, NULL_PIN \
+)
+
+// SERIAL
+
+#ifdef ED1_USE_SERIAL
+#include "serial.h"
+
+#ifndef ED1_SERIAL_BUFFER
+#define ED1_SERIAL_BUFFER 128
+#endif
+unsigned char _ed1_serial_buffer[ED1_SERIAL_BUFFER];
+ring_buffer_t _ed1_serial_rb;
 #define ed1_serial serial_create_simple(&ed1_serial_rb, 0)
 
 SERIAL_INTERRUPT_RX(ed1_serial, USART_RX_vect);
 
-static FILE mystdio;
+#ifdef ED1_USE_SERIAL_IO
+static FILE ed1_serial_stdio;
+#endif
+#endif
+
+// TWI
+#ifdef ED1_USE_TWI
+#include "twi.h"
+
+#ifndef ED1_TWI_BUFFER
+#define ED1_TWI_BUFFER 128
+#endif
+
+unsigned char _ed1_twi_rx_buffer[ED1_TWI_BUFFER];
+ring_buffer_t _ed1_twi_rx;
+
+unsigned char _ed1_twi_tx_buffer[ED1_TWI_BUFFER];
+ring_buffer_t _ed1_twi_tx;
+
+twi_ram_t _ed1_twi_state;
+
+#define ed1_twi twi_create( \
+    &_ed1_twi_state, \
+    &_ed1_twi_rx, \
+    &_ed1_twi_tx, \
+    pin_create_simple(C, 4), \
+    pin_create_simple(C, 5), \
+    &TWAR, &TWBR, &TWCR, &TWDR, &TWSR, \
+    TWEA, TWEN, TWIE, TWINT, TWPS0, TWPS1, TWSTA, TWSTO \
+)
+
+TWI_INTERRUPT(ed1_twi, TWI_vect)
+
+#endif
+
+// ACCELEROMETER
+#ifdef ED1_USE_ACCEL
+#include "accel.h"
+
+#define ed1_accel accel_create(ed1_twi, 0x1D)
+
+#endif
+
+// BOARD INITIALISATION CODE
 
 static inline
 void ed1_init()
         (1 << ADPS0)
     ;
 
-    ring_buffer_init(&ed1_serial_rb, ed1_serial_buffer, 128);
+#ifdef ED1_USE_SERIAL
+    ring_buffer_init(&_ed1_serial_rb, _ed1_serial_buffer, ED1_SERIAL_BUFFER);
 
+#ifdef ED1_USE_SERIAL_IO
     mystdio = serial_make_file(&ed1_serial);
     stdin = &mystdio;
     stdout = &mystdio;
+#endif
+#endif
+
+#ifdef ED1_USE_TWI
+    ring_buffer_init(&_ed1_twi_rx, _ed1_twi_rx_buffer, ED1_TWI_BUFFER);
+    ring_buffer_init(&_ed1_twi_tx, _ed1_twi_tx_buffer, ED1_TWI_BUFFER);
+#endif
 }
 
 #endif

include/arduino/lcd.h

+#ifndef __LCD_H
+#define __LCD_H
+
+// commands
+#define LCD_CLEARDISPLAY 0x01
+#define LCD_RETURNHOME 0x02
+#define LCD_ENTRYMODESET 0x04
+#define LCD_DISPLAYCONTROL 0x08
+#define LCD_CURSORSHIFT 0x10
+#define LCD_FUNCTIONSET 0x20
+#define LCD_SETCGRAMADDR 0x40
+#define LCD_SETDDRAMADDR 0x80
+
+// flags for display entry mode
+#define LCD_ENTRYRIGHT 0x00
+#define LCD_ENTRYLEFT 0x02
+#define LCD_ENTRYSHIFTINCREMENT 0x01
+#define LCD_ENTRYSHIFTDECREMENT 0x00
+
+// flags for display on/off control
+#define LCD_DISPLAYON 0x04
+#define LCD_DISPLAYOFF 0x00
+#define LCD_CURSORON 0x02
+#define LCD_CURSOROFF 0x00
+#define LCD_BLINKON 0x01
+#define LCD_BLINKOFF 0x00
+
+// flags for display/cursor shift
+#define LCD_DISPLAYMOVE 0x08
+#define LCD_CURSORMOVE 0x00
+#define LCD_MOVERIGHT 0x04
+#define LCD_MOVELEFT 0x00
+
+// flags for function set
+#define LCD_8BITMODE_BIT 4
+#define LCD_LINES_BIT 3
+#define LCD_HEIGHT_BIT 2
+
+typedef struct {
+    pin_t rs;
+    pin_t rw;
+    pin_t en;
+
+    pin_t d0;
+    pin_t d1;
+    pin_t d2;
+    pin_t d3;
+
+    // these four will be NULL_PIN
+    // in four-bit mode
+    pin_t d4;
+    pin_t d5;
+    pin_t d6;
+    pin_t d7;
+} lcd_t;
+
+#define lcd_create(rs, rw, en, d0, d1, d2, d3, d4, d5, d6, d7) \
+    (lcd_t) { rs, rw, en, d0, d1, d2, d3, d4, d5, d6, d7 }
+
+#if 1
+static inline
+bool lcd_is_four_bit_mode(lcd_t l) {
+    /*
+     * to try and avoid breaking things,
+     * we'll define it as any of the four
+     * being NULL_PIN, instead of all.
+     */
+    return
+        IS_NULL_PIN(l.d4) ||
+        IS_NULL_PIN(l.d5) ||
+        IS_NULL_PIN(l.d6) ||
+        IS_NULL_PIN(l.d7)
+    ;
+}
+#else
+#define lcd_is_four_bit_mode(l) \
+    (IS_NULL_PIN(l.d4) || IS_NULL_PIN(l.d5) || IS_NULL_PIN(l.d6) || IS_NULL_PIN(l.d7))
+#endif
+
+static inline
+void _lcd_pulse_enable(lcd_t l) {
+    pin_write(l.en, LOW);
+    delay_us(1);
+    pin_write(l.en, HIGH);
+    delay_us(1);
+    pin_write(l.en, LOW);
+    delay_us(100);
+}
+
+static inline
+void _lcd_send_4bit(lcd_t l, uint8_t value) {
+    pin_write(l.d0, value & 1);
+    pin_write(l.d1, value & 2);
+    pin_write(l.d2, value & 4);
+    pin_write(l.d3, value & 8);
+    _lcd_pulse_enable(l);
+}
+
+static inline
+void _lcd_send_8bit(lcd_t l, uint8_t value) {
+    pin_write(l.d0, value & 1);
+    pin_write(l.d1, value & 2);
+    pin_write(l.d2, value & 4);
+    pin_write(l.d3, value & 8);
+    pin_write(l.d4, value & 16);
+    pin_write(l.d5, value & 32);
+    pin_write(l.d6, value & 64);
+    pin_write(l.d7, value & 128);
+    _lcd_pulse_enable(l);
+}
+
+static inline
+void _lcd_send(lcd_t l, uint8_t value, bool is_command) {
+    if (is_command) {
+        pin_write(l.rs, LOW);
+    } else {
+        pin_write(l.rs, HIGH);
+    }
+
+    if (!IS_NULL_PIN(l.rw)) {
+        pin_write(l.rw, LOW);
+    }
+
+    if (lcd_is_four_bit_mode(l)) {
+        _lcd_send_4bit(l, value >> 4);
+        _lcd_send_4bit(l, value);
+    } else {
+        _lcd_send_8bit(l, value);
+    }
+
+    if (!IS_NULL_PIN(l.rw)) {
+        pin_write(l.rw, HIGH);
+    }
+}
+
+static inline
+void lcd_clear(lcd_t l) {
+    _lcd_send(l, LCD_CLEARDISPLAY, true);
+    delay_us(2000);
+}
+
+static inline
+void lcd_write(lcd_t l, char c) {
+    _lcd_send(l, c, false);
+}
+
+static inline
+void lcd_print(lcd_t l, const char *s) {
+    char c;
+    while ((c = *s++) != '\0') {
+        lcd_write(l, c);
+    }
+}
+
+static inline
+void lcd_init(lcd_t l, int lines, bool tall_font) {
+    // we can only use the tall font with one line,
+    // hence we force it here.
+    if (tall_font)
+        lines = 1;
+
+    // setup the pin modes
+    pin_mode(l.rs, OUTPUT);
+
+    if (!IS_NULL_PIN(l.rw))
+        pin_mode(l.rw, OUTPUT);
+
+    pin_mode(l.en, OUTPUT);
+
+    pin_mode(l.d0, OUTPUT);
+    pin_mode(l.d1, OUTPUT);
+    pin_mode(l.d2, OUTPUT);
+    pin_mode(l.d3, OUTPUT);
+
+    bool is_4bit = lcd_is_four_bit_mode(l);
+
+    if (!is_4bit) {
+        pin_mode(l.d4, OUTPUT);
+        pin_mode(l.d5, OUTPUT);
+        pin_mode(l.d6, OUTPUT);
+        pin_mode(l.d7, OUTPUT);
+    }
+
+    delay_ms(50);
+
+    // send the initialisation commands
+    pin_write(l.rs, LOW);
+    pin_write(l.en, LOW);
+    if (!IS_NULL_PIN(l.rw))
+        pin_write(l.rw, LOW);
+
+    int fnset_flags = 
+        ((!!(lines > 1)) << LCD_LINES_BIT) |
+        ((!is_4bit) << LCD_8BITMODE_BIT) |
+        ((!!tall_font) << LCD_HEIGHT_BIT)
+    ;
+
+    if (is_4bit) {
+        _lcd_send_4bit(l, 0x03);
+        delay_us(4500);
+
+        _lcd_send_4bit(l, 0x03);
+        delay_us(4500);
+
+        _lcd_send_4bit(l, 0x03);
+        delay_us(150);
+
+        _lcd_send_4bit(l, 0x02);
+
+    } else {
+        _lcd_send(l, LCD_FUNCTIONSET | fnset_flags, true);
+        delay_us(4500);
+
+        _lcd_send(l, LCD_FUNCTIONSET | fnset_flags, true);
+        delay_us(150);
+
+        _lcd_send(l, LCD_FUNCTIONSET | fnset_flags, true);
+    }
+
+    _lcd_send(l, LCD_FUNCTIONSET | fnset_flags, true);
+
+    // turn the display on
+    int disp_flags = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
+    _lcd_send(l, LCD_DISPLAYCONTROL | disp_flags, true);
+
+    // and clear the contents
+    lcd_clear(l);
+
+    int em_flags = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
+    _lcd_send(l, LCD_ENTRYMODESET | em_flags, true);
+}
+
+#endif

include/arduino/pin.h

 #ifndef __PIN_H
 #define __PIN_H
 
+#include <stddef.h>
+
 #include "utils.h"
 
-typedef struct {
-    volatile uint8_t *port;
-    volatile uint8_t *dir;
-    uint8_t pin;
-} pin_t;
-
 #define HIGH true
 #define LOW false
 
 #define INPUT false
 #define OUTPUT true
 
-#define pin_set(p, state) \
-    bit_set_cond(*(p.port), p.pin, state == HIGH)
+typedef struct {
+    volatile uint8_t *pin;
+    volatile uint8_t *port;
+    volatile uint8_t *dir;
+    uint8_t bit;
+} pin_t;
 
-#define pin_set_mode(p, mode) \
-    bit_set_cond(*(p.dir), p.pin, mode == OUTPUT)
+#define pin_create(pin, port, dir, bit) \
+    ((pin_t) {pin, port, dir, bit})
 
-#define pin_create(port, dir, pin) \
-    ((pin_t) {port, dir, pin})
+#define pin_create_simple(l, n) \
+    pin_create(&PIN##l, &PORT##l, &DDR##l, n)
+
+#define NULL_PIN (pin_t) {NULL, NULL, NULL, 0}
+#define IS_NULL_PIN(p) pin_equals(p, NULL_PIN)
+
+static inline
+bool pin_equals(pin_t a, pin_t b) {
+    return
+        (a.pin == b.pin) &&
+        (a.port == b.port) &&
+        (a.dir == b.dir) &&
+        (a.pin == b.pin);
+}
+
+static inline
+bool pin_read(pin_t p) {
+    return bit_get(*(p.pin), p.bit) ? HIGH : LOW;
+}
+
+static inline
+void pin_write(pin_t p, bool state) {
+    bit_set_cond(*(p.port), p.bit, state != LOW);
+}
+
+static inline
+void pin_mode(pin_t p, bool mode) {
+    bit_set_cond(*(p.dir), p.bit, mode == OUTPUT);
+}
 
 #endif

include/arduino/ring_buffer.h

  */
 
 typedef struct {
-    int buffer_size;
-    int head;
-    int tail;
+    uint8_t buffer_size;
+    uint8_t head;
+    uint8_t tail;
     unsigned char *buffer;
 } ring_buffer_t;
 
     unsigned char c
 ) {
     int next_tail = (rb->tail + 1) % rb->buffer_size;
+
     if (next_tail == rb->head)
         return false;
 
 }
 
 static inline
-unsigned int ring_buffer_size(
+uint8_t ring_buffer_size(
     ring_buffer_t *rb
 ) {
-    int diff = rb->tail - rb->head;
-    if (diff < 0)
-        diff += rb->buffer_size;
-    return diff;
+    return (rb->buffer_size + rb->tail - rb->head) % (rb->buffer_size);
 }
 
 static inline

include/arduino/shift_register.h

 #define LSBFIRST false
 
 static inline
-void shift_register_set_mode(
+void shift_register_mode(
     shift_register_t sr,
     bool mode
 ) {
-    pin_set_mode(sr.latch, mode);
-    pin_set_mode(sr.clock, mode);
-    pin_set_mode(sr.data, mode);
+    pin_mode(sr.latch, mode);
+    pin_mode(sr.clock, mode);
+    pin_mode(sr.data, mode);
 }
 
 static inline
-void shift_register_set(
+void shift_register_write(
     shift_register_t sr,
     bool mode,
     uint8_t value
 ) {
-    pin_set(sr.latch, LOW);
+    pin_write(sr.latch, LOW);
     for (int i = 0; i < 8; i++) {
         if (mode == MSBFIRST)
-            pin_set(sr.data, !!(value & (1 << i)));
+            pin_write(sr.data, !!(value & (1 << i)));
         else
-            pin_set(sr.data, !!(value & (1 << (7 - i))));
+            pin_write(sr.data, !!(value & (1 << (7 - i))));
 
-        pin_set(sr.clock, HIGH);
-        pin_set(sr.clock, LOW);
+        pin_write(sr.clock, HIGH);
+        pin_write(sr.clock, LOW);
     }
-    pin_set(sr.latch, HIGH);
+    pin_write(sr.latch, HIGH);
 }
 
 #define shift_register_create(latch, clock, data) \

include/arduino/twi.h

+#ifndef __TWI_H
+#define __TWI_H
+
+#include <compat/twi.h>
+
+#include "ring_buffer.h"
+
+#ifndef TWI_FREQ
+#define TWI_FREQ 100000L
+#endif
+
+#define TWI_INTERRUPT(t, i) \
+    SIGNAL(i) { twi_interrupt(t); }
+
+typedef enum {
+    TWI_STATE_READY,
+    TWI_STATE_MRX,
+    TWI_STATE_MTX,
+    TWI_STATE_SRX,
+    TWI_STATE_STX
+} twi_state_t;
+
+typedef struct {
+//    volatile uint8_t state;
+    volatile twi_state_t state;
+    volatile uint8_t error;
+
+    uint8_t slarw;
+
+    volatile uint8_t *master_buffer;
+    volatile int master_buffer_left;
+} twi_ram_t;
+
+typedef struct {
+    twi_ram_t *ram;
+
+    ring_buffer_t *rx_buffer;
+    ring_buffer_t *tx_buffer;
+
+    pin_t sda;
+    pin_t scl;
+    volatile uint8_t *twar;
+    volatile uint8_t *twbr;
+    volatile uint8_t *twcr;
+    volatile uint8_t *twdr;
+    volatile uint8_t *twsr;
+    uint8_t twea;
+    uint8_t twen;
+    uint8_t twie;
+    uint8_t twint;
+    uint8_t twps0;
+    uint8_t twps1;
+    uint8_t twsta;
+    uint8_t twsto;
+} twi_t;
+
+#define twi_create( \
+    ram, rx, tw, sda, scl, \
+    twar, twbr, twcr, twdr, twsr, \
+    twea, twen, twie, twint, twps0, twps1, twsta, twsto \
+) \
+    (twi_t) {\
+        ram, rx, tw, sda, scl, \
+        twar, twbr, twcr, twdr, twsr, \
+        twea, twen, twie, twint, twps0, twps1, twsta, twsto \
+    }
+
+/* 
+ * readys twi pins and sets twi bitrate
+ */
+static inline
+void twi_init(twi_t t) {
+    t.ram->master_buffer_left = 0;
+    t.ram->master_buffer = NULL;
+
+    // initialize state
+    t.ram->state = TWI_STATE_READY;
+
+    // activate internal pull-ups for twi
+    pin_write(t.sda, HIGH);
+    pin_write(t.scl, HIGH);
+
+    // initialize twi prescaler and bit rate
+    bit_unset(*(t.twsr), t.twps0);
+    bit_unset(*(t.twsr), t.twps1);
+    *(t.twbr) = ((F_CPU / TWI_FREQ) - 16) / 2;
+
+    /* twi bit rate formula from atmega128 manual pg 204
+       SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
+note: TWBR should be 10 or higher for master mode
+It is 72 for a 16mhz Wiring board with 100kHz TWI */
+
+    // enable twi module, acks, and twi interrupt
+    *(t.twcr) = (
+        (1 << t.twen) |
+        (1 << t.twie) |
+        (1 << t.twea)
+    );
+}
+
+static inline
+void twi_set_address(twi_t t, uint8_t address)
+{
+    // set twi slave address (skip over TWGCE bit)
+    *(t.twar) = address << 1;
+}
+
+/* 
+ * attempt to become twi bus master, and read a series of bytes from a device on
+ * the bus.
+ *
+ * @param address: 7 bit i2c device address
+ * @param data: pointer to byte array to read into
+ * @param length: number of bytes to read
+ * @returns: number of bytes read
+ */
+static inline
+int twi_read(
+    twi_t t,
+    uint8_t address,
+    uint8_t* data,
+    int length
+) {
+    // block until twi is ready
+    while (t.ram->state != TWI_STATE_READY);
+
+    // become master receiver
+    t.ram->state = TWI_STATE_MRX;
+    // reset error state (0xFF.. no error occured)
+    t.ram->error = 0xff;
+
+    // initialize buffer iteration vars
+    t.ram->master_buffer = data;
+
+    // This is not intuitive, read on...
+    // On receive, the previously configured ACK/NACK setting is transmitted in
+    // response to the received byte before the interrupt is signalled. 
+    // Therefor we must actually set NACK when the _next_ to last byte is
+    // received, causing that NACK to be sent in response to receiving the last
+    // expected byte of data.
+    t.ram->master_buffer_left = length - 1;
+
+    // build sla+w, slave device address + w bit
+    t.ram->slarw = TW_READ;
+    t.ram->slarw |= address << 1;
+
+    // send start condition
+    *(t.twcr) = (
+        (1 << t.twen) |
+        (1 << t.twie) |
+        (1 << t.twea) |
+        (1 << t.twint) |
+        (1 << t.twsta)
+    );
+
+    // block until read operation completes
+    while (t.ram->state == TWI_STATE_MRX);
+
+    int left_over = t.ram->master_buffer_left;
+
+    t.ram->master_buffer_left = 0;
+    t.ram->master_buffer = NULL;
+
+    return length - left_over;
+}
+
+/* 
+ * attempts to become twi bus master and write a series of bytes to a device on
+ * the bus
+ * @param address: 7bit i2c device address
+ * @param data: pointer to byte array
+ * @param length: number of bytes in array
+ * @returns 0 .. success
+ *          1 .. address send, NACK received
+ *          2 .. data send, NACK received
+ *          3 .. other twi error (lost bus arbitration, bus error, ..)
+ */
+static inline
+uint8_t twi_write(
+    twi_t t,
+    uint8_t address,
+    uint8_t* data,
+    uint8_t length
+) {
+    // wait until twi is ready
+    while (t.ram->state != TWI_STATE_READY);
+
+    // become master transmitter
+    t.ram->state = TWI_STATE_MTX;
+
+    // reset error state (0xFF.. no error occured)
+    t.ram->error = 0xFF;
+
+    // initialize buffer iteration vars
+    t.ram->master_buffer = data;
+    t.ram->master_buffer_left = length;
+
+    // build sla+w, slave device address + w bit
+    t.ram->slarw = TW_WRITE;
+    t.ram->slarw |= address << 1;
+
+    // send start condition
+    *(t.twcr) = (
+        (1 << t.twen) |
+        (1 << t.twie) |
+        (1 << t.twea) |
+        (1 << t.twint) |
+        (1 << t.twsta)
+    );
+
+    // wait for write operation to complete
+    while (t.ram->state == TWI_STATE_MTX);
+
+    switch (t.ram->error) {
+        case 0xff: // success
+            return 0;
+        case TW_MT_SLA_NACK:
+            return 1; // error: address send, nack received
+        case TW_MT_DATA_NACK:
+            return 2; // error: data send, nack receieved
+    }
+    return 4;
+}
+
+#if 0
+/* 
+ * Function twi_transmit
+ * Desc     fills slave tx buffer with data
+ *          must be called in slave tx event callback
+ * Input    data: pointer to byte array
+ *          length: number of bytes in array
+ * Output   1 length too long for buffer
+ *          2 not slave transmitter
+ *          0 ok
+ */
+uint8_t twi_transmit(uint8_t* data, uint8_t length)
+{
+    uint8_t i;
+
+    // ensure data will fit into buffer
+    if(TWI_BUFFER_LENGTH < length){
+        return 1;
+    }
+
+    // ensure we are currently a slave transmitter
+    if(TWI_STATE_STX != twi_state){
+        return 2;
+    }
+
+    // set length and copy data into tx buffer
+    twi_txBufferLength = length;
+    for(i = 0; i < length; ++i){
+        twi_txBuffer[i] = data[i];
+    }
+
+    return 0;
+}
+
+/* 
+ * Function twi_attachSlaveRxEvent
+ * Desc     sets function called before a slave read operation
+ * Input    function: callback function to use
+ * Output   none
+ */
+void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) )
+{
+    twi_onSlaveReceive = function;
+}
+
+/* 
+ * Function twi_attachSlaveTxEvent
+ * Desc     sets function called before a slave write operation
+ * Input    function: callback function to use
+ * Output   none
+ */
+void twi_attachSlaveTxEvent( void (*function)(void) )
+{
+    twi_onSlaveTransmit = function;
+}
+#endif
+
+// INTERNAL FUNCTIONS
+
+/* 
+ * Function twi_reply
+ * Desc     sends byte or readys receive line
+ * Input    ack: byte indicating to ack or to nack
+ * Output   none
+ */
+static inline
+void _twi_reply(uint8_t ack)
+{
+    // transmit master read ready signal, with or without ack
+    if(ack){
+        TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
+    }else{
+        TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
+    }
+}
+
+/* 
+ * Function twi_stop
+ * Desc     relinquishes bus master status
+ * Input    none
+ * Output   none
+ */
+static inline
+void _twi_stop(twi_t t)
+{
+    // send stop condition
+    *(t.twcr) = (
+        (1 << t.twen) |
+        (1 << t.twie) |
+        (1 << t.twea) |
+        (1 << t.twint) |
+        (1 << t.twsto)
+    );
+
+    // wait for stop condition to be executed on bus
+    // TWINT is not set after a stop condition!
+    while (*(t.twcr) & (1 << t.twsto));
+
+    // update twi state
+    t.ram->state = TWI_STATE_READY;
+}
+
+/* 
+ * Function twi_releaseBus
+ * Desc     releases bus control
+ * Input    none
+ * Output   none
+ */
+static inline
+void _twi_release_bus(twi_t t)
+{
+    // release bus
+    *(t.twcr) = (
+        (1 << t.twen) |
+        (1 << t.twie) |
+        (1 << t.twea) |
+        (1 << t.twint)
+    );
+
+    // update twi state
+    t.ram->state = TWI_STATE_READY;
+}
+
+static inline
+void twi_interrupt(twi_t t) {
+    bool success;
+    int val;
+    switch (TW_STATUS) {
+        // All Master
+        case TW_START:     // sent start condition
+        case TW_REP_START: // sent repeated start condition
+            // copy device address and r/w bit to output register and ack
+            *(t.twdr) = t.ram->slarw;
+            _twi_reply(1);
+            break;
+
+        // Master Transmitter
+        case TW_MT_SLA_ACK:  // slave receiver acked address
+        case TW_MT_DATA_ACK: // slave receiver acked data
+            // if there is data to send, send it, otherwise stop 
+            if (t.ram->master_buffer_left) {
+                // copy data to output register and ack
+                *(t.twdr) = *(t.ram->master_buffer++);
+                t.ram->master_buffer_left--;
+                _twi_reply(1);
+            } else {
+                _twi_stop(t);
+            }
+            break;
+        case TW_MT_SLA_NACK:  // address sent, nack received
+            t.ram->error = TW_MT_SLA_NACK;
+            _twi_stop(t);
+            break;
+        case TW_MT_DATA_NACK: // data sent, nack received
+            t.ram->error = TW_MT_DATA_NACK;
+            _twi_stop(t);
+            break;
+        case TW_MT_ARB_LOST: // lost bus arbitration
+            t.ram->error = TW_MT_ARB_LOST;
+            _twi_release_bus(t);
+            break;
+
+        // Master Receiver
+        case TW_MR_DATA_ACK: // data received, ack sent
+            // put byte into buffer
+            *(t.ram->master_buffer++) = *(t.twdr);
+            t.ram->master_buffer_left--;
+        case TW_MR_SLA_ACK:  // address sent, ack received
+            // ack if more bytes are expected, otherwise nack
+            _twi_reply(t.ram->master_buffer_left > 0);
+            break;
+        case TW_MR_DATA_NACK: // data received, nack sent
+            // put final byte into buffer
+            *(t.ram->master_buffer++) = *(t.twdr);
+            t.ram->master_buffer_left--;
+        case TW_MR_SLA_NACK: // address sent, nack received
+            _twi_stop(t);
+            break;
+            // TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case
+
+        // Slave Receiver
+        case TW_SR_SLA_ACK:   // addressed, returned ack
+        case TW_SR_GCALL_ACK: // addressed generally, returned ack
+        case TW_SR_ARB_LOST_SLA_ACK:   // lost arbitration, returned ack
+        case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack
+            // enter slave receiver mode
+            t.ram->state = TWI_STATE_SRX;
+            // indicate that rx buffer can be overwritten and ack
+            ring_buffer_clear(t.rx_buffer);
+            _twi_reply(1);
+            break;
+        case TW_SR_DATA_ACK:       // data received, returned ack
+        case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack
+            // if there is still room in the rx buffer
+            success = ring_buffer_append(t.rx_buffer, *(t.twdr));
+            _twi_reply(success);
+            break;
+        case TW_SR_STOP: // stop or repeated start condition received
+            // sends ack and stops interface for clock stretching
+            _twi_stop(t);
+
+            // TODO: provide callback for slave receive
+            // t.ram->on_slave_receive(t.rx_buffer)
+
+            // force the callback to read it into another buffer
+            ring_buffer_clear(t.rx_buffer);
+
+            // ack future responses and leave slave receiver state
+            _twi_release_bus(t);
+            break;
+
+        case TW_SR_DATA_NACK:       // data received, returned nack
+        case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack
+            // nack back at master
+            _twi_reply(0);
+            break;
+
+        // Slave Transmitter
+        case TW_ST_SLA_ACK:          // addressed, returned ack
+        case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack
+            // enter slave transmitter mode
+            t.ram->state = TWI_STATE_STX;
+            // clear the tx buffer
+            ring_buffer_clear(t.tx_buffer);
+            // TODO: allow user to fill tx buffer with data
+            // t.ram->on_slave_trasmit(t.tx_buffer)
+        case TW_ST_DATA_ACK: // byte sent, ack returned
+            // copy data to output register
+            val = ring_buffer_pop(t.tx_buffer);
+            if (val != -1) {
+                *(t.twdr) = val;
+                _twi_reply(1);
+            } else {
+                _twi_reply(0);
+            }
+            break;
+        case TW_ST_DATA_NACK: // received nack, we are done 
+        case TW_ST_LAST_DATA: // received ack, but we are done already!
+            // ack future responses
+            _twi_reply(1);
+            // leave slave receiver state
+            t.ram->state = TWI_STATE_READY;
+            break;
+
+            // All
+        case TW_NO_INFO:   // no state information
+            break;
+        case TW_BUS_ERROR: // bus error, illegal stop/start
+            t.ram->error = TW_BUS_ERROR;
+            _twi_stop(t);
+            break;
+    }
+}
+
+#endif

include/arduino/utils.h

 #define true 1
 typedef unsigned char bool;
 
+#define bit_get(byte, bit) (byte) & (1<<(bit))
 #define bit_set(byte, bit) (byte) |= (1<<(bit))
 #define bit_unset(byte, bit) (byte) &= ~(1<<(bit))
 
 #include <util/delay.h>
 
 #define delay_ms _delay_ms
-#define delay_us _delay_us
+//#define delay_us _delay_us
+
+/* Delay for the given number of microseconds.  Assumes a 8 or 16 MHz clock. */
+static inline
+void delay_us(unsigned int us)
+{
+#if F_CPU >= 16000000L
+	// for the 16 MHz clock on most Arduino boards
+
+	// for a one-microsecond delay, simply return.  the overhead
+	// of the function call yields a delay of approximately 1 1/8 us.
+	if (--us == 0)
+		return;
+
+	// the following loop takes a quarter of a microsecond (4 cycles)
+	// per iteration, so execute it four times for each microsecond of
+	// delay requested.
+	us <<= 2;
+
+	// account for the time taken in the preceeding commands.
+	us -= 2;
+#else
+	// for the 8 MHz internal clock on the ATmega168
+
+	// for a one- or two-microsecond delay, simply return.  the overhead of
+	// the function calls takes more than two microseconds.  can't just
+	// subtract two, since us is unsigned; we'd overflow.
+	if (--us == 0)
+		return;
+	if (--us == 0)
+		return;
+
+	// the following loop takes half of a microsecond (4 cycles)
+	// per iteration, so execute it twice for each microsecond of
+	// delay requested.
+	us <<= 1;
+    
+	// partially compensate for the time taken by the preceeding commands.
+	// we can't subtract any more than this or we'd overflow w/ small delays.
+	us--;
+#endif
+
+	// busy wait
+	__asm__ __volatile__ (
+		"1: sbiw %0,1" "\n\t" // 2 cycles
+		"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
+	);
+}
 
 #endif