aboutsummaryrefslogtreecommitdiff
path: root/digital-driver-board/firmware/TinyWireS
diff options
context:
space:
mode:
Diffstat (limited to 'digital-driver-board/firmware/TinyWireS')
-rw-r--r--digital-driver-board/firmware/TinyWireS/TinyWireS.cpp91
-rw-r--r--digital-driver-board/firmware/TinyWireS/TinyWireS.h64
-rw-r--r--digital-driver-board/firmware/TinyWireS/avr_usi_i2c_master.pdfbin0 -> 128819 bytes
-rw-r--r--digital-driver-board/firmware/TinyWireS/avr_usi_i2c_slave.pdfbin0 -> 81217 bytes
-rw-r--r--digital-driver-board/firmware/TinyWireS/examples/TinyWireS_Stress_Master/TinyWireS_Stress_Master.ino184
-rw-r--r--digital-driver-board/firmware/TinyWireS/examples/TinyWireS_Stress_Slave/TinyWireS_Stress_Slave.ino143
-rw-r--r--digital-driver-board/firmware/TinyWireS/examples/attiny85_i2c_analog/attiny85_i2c_analog.ino210
-rw-r--r--digital-driver-board/firmware/TinyWireS/examples/attiny85_i2c_slave/attiny85_i2c_slave.ino152
-rw-r--r--digital-driver-board/firmware/TinyWireS/examples/attiny85_i2c_slave_task/attiny85_i2c_slave_task.ino246
-rw-r--r--digital-driver-board/firmware/TinyWireS/keywords.txt27
-rw-r--r--digital-driver-board/firmware/TinyWireS/usiTwiSlave.c748
-rw-r--r--digital-driver-board/firmware/TinyWireS/usiTwiSlave.h96
12 files changed, 1961 insertions, 0 deletions
diff --git a/digital-driver-board/firmware/TinyWireS/TinyWireS.cpp b/digital-driver-board/firmware/TinyWireS/TinyWireS.cpp
new file mode 100644
index 0000000..783e8e7
--- /dev/null
+++ b/digital-driver-board/firmware/TinyWireS/TinyWireS.cpp
@@ -0,0 +1,91 @@
+/*
+ TinyWireS.cpp - a wrapper class for Don Blake's usiTwiSlave routines.
+ Provides TWI/I2C Slave functionality on ATtiny processers in Arduino environment.
+ 1/23/2011 BroHogan - brohoganx10 at gmail dot com
+
+ **** See TinyWireS.h for Credits and Usage information ****
+
+ This library is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 2.1 of the License, or any later version.
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ PARTICULAR PURPOSE. See the GNU General Public License for more details.
+*/
+
+extern "C" {
+ #include <inttypes.h>
+ #include "usiTwiSlave.h"
+ #include <avr/interrupt.h>
+ }
+
+#include "TinyWireS.h"
+#include "Arduino.h"
+
+// Constructors ////////////////////////////////////////////////////////////////
+
+USI_TWI_S::USI_TWI_S(){
+}
+
+
+// Public Methods //////////////////////////////////////////////////////////////
+
+void USI_TWI_S::begin(uint8_t slaveAddr){ // initialize I2C lib
+ usiTwiSlaveInit(slaveAddr);
+}
+
+void USI_TWI_S::send(uint8_t data){ // send it back to master
+ usiTwiTransmitByte(data);
+}
+
+uint8_t USI_TWI_S::available(){ // the bytes available that haven't been read yet
+ return usiTwiAmountDataInReceiveBuffer();
+ //return usiTwiDataInReceiveBuffer(); // This is wrong as far as the Wire API is concerned since it returns boolean and not amount
+}
+
+uint8_t USI_TWI_S::receive(){ // returns the bytes received one at a time
+ return usiTwiReceiveByte();
+}
+
+// sets function called on slave write
+void USI_TWI_S::onReceive( void (*function)(uint8_t) )
+{
+ usi_onReceiverPtr = function;
+}
+
+// sets function called on slave read
+void USI_TWI_S::onRequest( void (*function)(void) )
+{
+ usi_onRequestPtr = function;
+}
+
+// This routine is no longer used now that the usiTwiSlave is completely
+// interrupt driven. The function is maintained here so that programs
+// written for the pre-interrupt driven version will still compile and function.
+void TinyWireS_stop_check()
+{
+ // empty functions
+}
+
+// Implement a delay loop that checks for the stop bit (basically direct copy of the stock arduino implementation from wiring.c)
+// I don't think this function is necessary now that the
+// usiTwiSlave is completely interrupt driven. I'm not sure, and the function is
+// behaviorally harmless because TinyWireS_stop_check() is empty, so I'm leaving it alone.
+void tws_delay(unsigned long ms)
+{
+ uint16_t start = (uint16_t)micros();
+ while (ms > 0)
+ {
+ TinyWireS_stop_check();
+ if (((uint16_t)micros() - start) >= 1000)
+ {
+ ms--;
+ start += 1000;
+ }
+ }
+}
+
+// Preinstantiate Objects //////////////////////////////////////////////////////
+
+USI_TWI_S TinyWireS = USI_TWI_S();
+
diff --git a/digital-driver-board/firmware/TinyWireS/TinyWireS.h b/digital-driver-board/firmware/TinyWireS/TinyWireS.h
new file mode 100644
index 0000000..ccef042
--- /dev/null
+++ b/digital-driver-board/firmware/TinyWireS/TinyWireS.h
@@ -0,0 +1,64 @@
+/*
+ TinyWireS.h - a wrapper class for Don Blake's usiTwiSlave routines.
+ Provides TWI/I2C Slave functionality on ATtiny processers in Arduino environment.
+ 1/23/2011 BroHogan - brohoganx10 at gmail dot com
+
+ Major credit and thanks to Don Blake for his usiTwiSlave code which makes this possible
+ http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=51467&start=all&postdays=0&postorder=asc
+ (Changed #define USI_START_COND_INT USISIF (was USICIF) in usiTwiSlave.h)
+
+ NOTE! - It's very important to use pullups on the SDA & SCL lines! More so than with the Wire lib.
+ Current Rx & Tx buffers set at 32 bytes - see usiTwiSlave.h
+
+ USAGE is modeled after the standard Wire library . . .
+ Put in setup():
+ TinyWireS.begin(I2C_SLAVE_ADDR); // initialize I2C lib & setup slave's address (7 bit - same as Wire)
+
+ To Receive:
+ someByte = TinyWireS.available(){ // returns the number of bytes in the received buffer
+ someByte = TinyWireS.receive(){ // returns the next byte in the received buffer
+
+ To Send:
+ TinyWireS.send(uint8_t data){ // sends a requested byte to master
+
+ TODO: (by others!)
+ - onReceive and onRequest handlers are not implimented.
+ - merge this class with TinyWireM for master & slave support in one library
+
+ This library is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 2.1 of the License, or any later version.
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ PARTICULAR PURPOSE. See the GNU General Public License for more details.
+*/
+
+#ifndef TinyWireS_h
+#define TinyWireS_h
+
+#include <inttypes.h>
+
+
+class USI_TWI_S
+{
+ private:
+ //static uint8_t USI_BytesAvail;
+
+ public:
+ USI_TWI_S();
+ void begin(uint8_t I2C_SLAVE_ADDR);
+ void send(uint8_t data);
+ uint8_t available();
+ uint8_t receive();
+ void onReceive( void (*)(uint8_t) );
+ void onRequest( void (*)(void) );
+};
+
+void TinyWireS_stop_check();
+// Implement a delay loop that checks for the stop bit (basically direct copy of the stock arduino implementation from wiring.c)
+void tws_delay(unsigned long);
+
+extern USI_TWI_S TinyWireS;
+
+#endif
+
diff --git a/digital-driver-board/firmware/TinyWireS/avr_usi_i2c_master.pdf b/digital-driver-board/firmware/TinyWireS/avr_usi_i2c_master.pdf
new file mode 100644
index 0000000..dc8f460
--- /dev/null
+++ b/digital-driver-board/firmware/TinyWireS/avr_usi_i2c_master.pdf
Binary files differ
diff --git a/digital-driver-board/firmware/TinyWireS/avr_usi_i2c_slave.pdf b/digital-driver-board/firmware/TinyWireS/avr_usi_i2c_slave.pdf
new file mode 100644
index 0000000..e4704c7
--- /dev/null
+++ b/digital-driver-board/firmware/TinyWireS/avr_usi_i2c_slave.pdf
Binary files differ
diff --git a/digital-driver-board/firmware/TinyWireS/examples/TinyWireS_Stress_Master/TinyWireS_Stress_Master.ino b/digital-driver-board/firmware/TinyWireS/examples/TinyWireS_Stress_Master/TinyWireS_Stress_Master.ino
new file mode 100644
index 0000000..d50adb7
--- /dev/null
+++ b/digital-driver-board/firmware/TinyWireS/examples/TinyWireS_Stress_Master/TinyWireS_Stress_Master.ino
@@ -0,0 +1,184 @@
+// ---------------------------------
+// Stress test program/example for TinyWireS I2C library.
+// Run this master program on the Arduino Uno R3.
+// Run the other slave program on the Attiny.
+// ---------------------------------
+// Written by Scott Hartog, 2/6/2016
+// This is the I2C master program which runs on on a regular Arduino
+// (not a AtTiny). This program uses the regular Wire library from the Arduino IDE.
+//
+// It performs these steps in a loop:
+// 1. picks a random number of bytes between 1 and 12
+// 2. sends that many bytes of random data to the AtTiny slave within
+// a single Wire.beginTransmission() / Wire.write() / Wire.endTransmission() set
+// 3. reads that same number of bytes back with a single Wire.requestFrom() call
+// 4. compares the received data to the originally transmitted data
+// 5. displays the number of requests, number of requests with mismatches,
+// and enough of the data so that the operator can tell it's working.
+//
+#include <Wire.h>
+
+// BREADBOARD SETUP:
+// Arduino Uno R3 (D18/SDA) = I2C SDA
+// connect to SDA on slave with external pull-up (~4.7K)
+// Arduino Uno R3 (D19/SCL) = I2C SCL
+// connect to SCL on slave with external pull-up (~4.7K)
+// Arduino Uno R3 (D2)
+// connect to !RST on slave
+// Can alternatively connect !RST on slave to the Ardiuno "!RESET" pin
+
+#define I2C_SLAVE_ADDR 0x26 // i2c slave address (38, 0x26)
+
+#if defined(ESP8266)
+ // pins that work for Monkey Board ESP8266 12-E
+ // SCL=5, SDA=4
+ #define SLAVE_RESET_PIN 2
+ #define ALL_OK_LED_PIN 16
+ #define OK_LED_PIN 14
+ #define ERROR_LED_PIN 13
+#else
+ // pins that work for Micro Pro, Uno, Mega 2560
+ // reference documentation for SCL and SDA pin locations
+ // Uno SDA=D18, SCL=D19
+ #define SLAVE_RESET_PIN 6
+ #define ALL_OK_LED_PIN 9
+ #define OK_LED_PIN 7
+ #define ERROR_LED_PIN 8
+#endif
+
+uint16_t count = 0; // total number of passes so far
+uint16_t error_count = 0; // total errors encountered so far
+
+char c_buf[64]; // for creating messages
+
+void setup()
+{
+ // set pin modes
+ pinMode(SLAVE_RESET_PIN,OUTPUT); // active low reset to slave device
+ pinMode(OK_LED_PIN,OUTPUT); // indicates last transaction matched
+ pinMode(ALL_OK_LED_PIN,OUTPUT); // indicates all transactions so far have matched
+ pinMode(ERROR_LED_PIN,OUTPUT); // indicates last transaction mismatched
+
+ // init the serial port
+ Serial.begin(9600);
+
+ // print some useful pinnout info for the Arduino
+ //Serial.println(String("SCL:")+String(SCL)+String(", SDA:")+String(SDA));
+ //Serial.println(String("MOSI:")+String(MOSI)+String(", SCK:")+String(SCK));
+
+ // init the Wire object (for I2C)
+ Wire.begin();
+
+ // init the i2c clock
+ // default is 100kHz if not changed
+ // Wire.setClock(400000L); // 400kHz
+
+ // reset the slave
+ digitalWrite(SLAVE_RESET_PIN, LOW);
+ delay(10);
+ digitalWrite(SLAVE_RESET_PIN, HIGH);
+
+ // set the all okay pin high
+ digitalWrite(ALL_OK_LED_PIN, HIGH);
+
+ // wait for slave to finish any init sequence
+ delay(2000);
+}
+
+void loop()
+{
+ uint8_t i;
+ uint8_t req_rtn; // num bytes returned by requestFrom() call
+ uint8_t rand_byte_count;
+ uint8_t out_rand[16]; // data written from master
+ uint8_t in_rand[16]; // data read back from slave
+
+ bool mismatch;
+
+ // count total number of request
+ count++;
+
+ // compute random number of bytes for this pass
+ rand_byte_count = random(16) + 1;
+
+ // force the first three requests to be small so that the tx buffer doesn't overflow
+ // instantly and the user can see at least one successful transaction and some
+ // mismtaches before the usiTwiSlave.c library hangs on the line "while ( !txCount );".
+ if (count <= 3) rand_byte_count = 2;
+
+ // generate, save, and send N random byte values
+ Wire.beginTransmission(I2C_SLAVE_ADDR);
+ for (i = 0; i < rand_byte_count; i++)
+ Wire.write(out_rand[i] = random(256));
+ Wire.endTransmission();
+
+ // delay 20 milliseconds to accomodate slave onReceive() callback
+ // function. The actual time that slave takes is application dependent, but
+ // just storing the master's transmitted data does not take
+ // anywhere near 20ms.
+ delay(20);
+
+ // read N bytes from slave
+ req_rtn = Wire.requestFrom(I2C_SLAVE_ADDR, (int)rand_byte_count); // Request N bytes from slave
+ for (i = 0; i < req_rtn; i++)
+ in_rand[i] = Wire.read();
+
+ // compare in/out data values
+ mismatch = false;
+ for (i = 0; i < rand_byte_count; i++)
+ mismatch = mismatch || (out_rand[i] != in_rand[i]);
+
+ // increment the error counter if the number of byte variables don't match or
+ // if the data itself doesn't match
+ if (mismatch || (rand_byte_count != req_rtn))
+ {
+ error_count++;
+ digitalWrite(ERROR_LED_PIN, HIGH);
+ digitalWrite(OK_LED_PIN, LOW);
+ // If there's ever an error, reset the ALL_OK_LED
+ // and it is not set again until the master resets.
+ digitalWrite(ALL_OK_LED_PIN, LOW);
+ }
+ else
+ {
+ digitalWrite(ERROR_LED_PIN, LOW);
+ digitalWrite(OK_LED_PIN, HIGH);
+ }
+
+ // The rest of the program just displays the results to the serial port
+
+ // display total requests so far and error count so far
+ snprintf(c_buf, sizeof(c_buf), "req: %3d,err: %3d", count, error_count);
+ Serial.println(c_buf);
+
+ // display the random byte count, the number of bytes read back, and "MATCH"/"MISMATCH"
+ snprintf(c_buf, sizeof(c_buf), "size: %2d/%2d,%s", rand_byte_count, req_rtn, rand_byte_count != req_rtn?"MISMATCH <<--- !!!":"MATCH");
+ Serial.println(c_buf);
+
+ // display whether the data compare matched or mismatched
+ snprintf(c_buf, sizeof(c_buf), "data: %s", mismatch?"MISMATCH <<--- !!!":"MATCH");
+ Serial.println(c_buf);
+
+ // send up to three tx/rx bytes so that random data can be
+ // visually verified
+ if (rand_byte_count >= 1)
+ {
+ snprintf(c_buf, sizeof(c_buf), "rand[0]: %02x/%02x", out_rand[0], in_rand[0]);
+ Serial.println(c_buf);
+ }
+
+ if (rand_byte_count >= 2)
+ {
+ snprintf(c_buf, sizeof(c_buf), "rand[1]: %02x/%02x", out_rand[1], in_rand[1]);
+ Serial.println(c_buf);
+ }
+
+ if (rand_byte_count >= 3)
+ {
+ snprintf(c_buf, sizeof(c_buf), "rand[2]: %02x/%02x", out_rand[2], in_rand[2]);
+ Serial.println(c_buf);
+ }
+
+ // delay 1 second so user can watch results
+ delay(1000);
+}
diff --git a/digital-driver-board/firmware/TinyWireS/examples/TinyWireS_Stress_Slave/TinyWireS_Stress_Slave.ino b/digital-driver-board/firmware/TinyWireS/examples/TinyWireS_Stress_Slave/TinyWireS_Stress_Slave.ino
new file mode 100644
index 0000000..c7fbe41
--- /dev/null
+++ b/digital-driver-board/firmware/TinyWireS/examples/TinyWireS_Stress_Slave/TinyWireS_Stress_Slave.ino
@@ -0,0 +1,143 @@
+// ---------------------------------
+// Stress test program/example for TinyWireS I2C library.
+// Run this slave program on the Attiny.
+// Run the other master program on the Arduino Uno R3.
+// ---------------------------------
+// // Written by Scott Hartog, 2/6/2016, to stress test the TinyWireS library.
+// https://github.com/rambo/TinyWire
+//
+// This project uses the Tiny85 as an I2C slave.
+//
+// The slave program using TinyWireS, running on a Attiny85, receives
+// N bytes of random data in a single receiveEvent() callback and
+// stores that data in a global buffer. It then responds the first requestEvent()
+// callback with that same data. The requestEvent() callback overwrites the data
+// buffer with zeros after responding so it will only respond correctly to the
+// first requestEvent() callback after each receiveEvent() callback. Subsequent
+// requestEvent() will respond with 0xff for all data bytes.
+//
+//
+// SETUP:
+// AtTiny Pin 5 (PB0/SDA) = I2C SDA
+// connect to SDA on master with external pull-up (~4.7K)
+// AtTiny Pin 7 (PB0/SCL) = I2C SCL
+// connect to SCL on master with external pull-up (~4.7K)
+// AtTiny Pin 1 (PB5/!RST)
+// connect to reset on master (or just pull-up)
+//
+// Please see credits and usage for usiTwiSlave and TinyWireS in the .h files of
+// those libraries.
+
+#include <avr/sleep.h>
+#include <avr/wdt.h>
+#include "TinyWireS.h" // wrapper class for I2C slave routines
+
+#define I2C_SLAVE_ADDR 0x26 // i2c slave address (38, 0x26)
+
+// turns on code that makes the Tiny85 sleep between transactions
+// This is optional. The Tiny85 current drops from
+// about 2mA to about 20uA when the CPU is put into
+// PowerDown sleep mode.
+#define USE_CPU_SLEEP
+
+// global buffer to store data sent from the master.
+uint8_t master_data[16];
+// global variable to number of bytes sent from the master.
+uint8_t master_bytes;
+
+// Gets called when the ATtiny receives an i2c write slave request
+// This routine runs from the usiTwiSlave interrupt service routine (ISR)
+// so interrupts are disabled while it runs.
+void receiveEvent(uint8_t num_bytes)
+{
+ uint8_t i;
+
+ // save the number of bytes sent from the master
+ master_bytes = num_bytes;
+
+ // store the data from the master into the data buffer
+ for (i = 0; i < master_bytes; i++)
+ master_data[i] = TinyWireS.receive();
+
+}
+
+// Gets called when the ATtiny receives an i2c read slave request
+// This routine runs from the usiTwiSlave interrupt service routine (ISR)
+// so interrupts are disabled while it runs.
+void requestEvent()
+{
+ uint8_t i;
+
+ // send the data buffer back to the master
+ for (i = 0; i < master_bytes; i++)
+ TinyWireS.send(master_data[i]);
+
+ // corrupt the byte values in the data buffer
+ // so that any subsequent call won't match
+ for (i = 0; i < master_bytes; i++)
+ master_data[i] += 0x5a;
+
+ // corrupt length of the request, but don't make it zero
+
+ // if the usiTwiSlave.c is working fine, then this number is completely irrelevant
+ // because the requestEvent() callback will not be called again until
+ // after the next receiveEvent() callback, so the master_data and
+ // master_bytes variables will be overwritten by that call.
+
+ // If the usiTwiSlave.c has the issue of calling the requestFrom() callback
+ // for each byte sent, the buffer will accumulate by this amount *for each byte
+ // in the original request*. (This problem is fixed in the recent version.)
+ //
+ // Making it zero will obscure the 1-byte send issue in the usiTwiSlave.c
+ // that is being tested.
+ // Making it small will allow a few requests to succeed before the tx buffer
+ // overflows and the usiTwiSlave.c hangs on the "while ( tmphead == txTail );"
+ // line
+ master_bytes = 2;
+}
+
+void setup()
+{
+ //pinMode(1,OUTPUT); // This pin can be used for rudimentary debug
+
+ // initialize the TinyWireS and usiTwiSlave libraries
+ TinyWireS.begin(I2C_SLAVE_ADDR); // init I2C Slave mode
+
+ // register the onReceive() callback function
+ TinyWireS.onReceive(receiveEvent);
+
+ // register the onRequest() callback function
+ TinyWireS.onRequest(requestEvent);
+
+ // disable the watchdog timer so that it doesn't
+ // cause power-up, code is from datasheet
+ // Clear WDRF in MCUSR – MCU Status Register
+ // MCUSR provides information on which reset source caused an MCU Reset.
+ MCUSR = 0x00;
+ // WDTCR - Watchdog Timer Control Register
+ // Write logical one to WDCE and WDE (must be done before disabling)
+ WDTCR |= ( _BV(WDCE) | _BV(WDE) );
+ // Turn off WDT
+ WDTCR = 0x00;
+
+#ifdef USE_CPU_SLEEP
+ // enable power down sleep mode
+ set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode
+ sleep_enable();
+#endif
+
+ sei(); // enable interrupts
+
+}
+
+void loop()
+{
+
+#ifdef USE_CPU_SLEEP
+ // optionally put the CPU to sleep. It will be woken by a USI interrupt
+ // when it sees a "start condition" on the I2C bus. Everything interesting
+ // happens in the usiTwiSlave ISR.
+ sleep_cpu();
+#endif
+
+}
diff --git a/digital-driver-board/firmware/TinyWireS/examples/attiny85_i2c_analog/attiny85_i2c_analog.ino b/digital-driver-board/firmware/TinyWireS/examples/attiny85_i2c_analog/attiny85_i2c_analog.ino
new file mode 100644
index 0000000..7190c93
--- /dev/null
+++ b/digital-driver-board/firmware/TinyWireS/examples/attiny85_i2c_analog/attiny85_i2c_analog.ino
@@ -0,0 +1,210 @@
+/**
+ * Example sketch for writing to and reading from a slave in transactional manner
+ *
+ * NOTE: You must not use delay() or I2C communications will fail, use tws_delay() instead (or preferably some smarter timing system)
+ *
+ * On write the first byte received is considered the register addres to modify/read
+ * On each byte sent or read the register address is incremented (and it will loop back to 0)
+ *
+ * You can try this with the Arduino I2C REPL sketch at https://github.com/rambo/I2C/blob/master/examples/i2crepl/i2crepl.ino
+ * If you have bus-pirate remember that the older revisions do not like the slave streching the clock, this leads to all sorts of weird behaviour
+ * Examples use bus-pirate semantics (like the REPL)
+ *
+ * The basic idea is:
+ * 1. Choose your ADC channel (0-X), use "byte ch = 1;" for example.
+ * 2. Combine the channel and conversion start flag to single calue: byte start_on_ch = (ch | _BV(7)); // This is 0x81
+ * 3. Write start_on_ch to the first register on the attiny [ 8 0 81 ]
+ * 4. Come back later and check the first register [ 8 0 [ r ], if the value is same as ch then the conversion is complete, you can now read the value
+ * 5. read the value [ 8 2 [ r r ] (first one is low, second high byte)
+ *
+ * You need to have at least 8MHz clock on the ATTiny for this to work (and in fact I have so far tested it only on ATTiny85 @8MHz using internal oscillator)
+ * Remember to "Burn bootloader" to make sure your chip is in correct mode
+ */
+
+
+/**
+ * Pin notes by Suovula, see also http://hlt.media.mit.edu/?p=1229
+ *
+ * DIP and SOIC have same pinout, however the SOIC chips are much cheaper, especially if you buy more than 5 at a time
+ * For nice breakout boards see https://github.com/rambo/attiny_boards
+ *
+ * Basically the arduino pin numbers map directly to the PORTB bit numbers.
+ *
+// I2C
+arduino pin 0 = not(OC1A) = PORTB <- _BV(0) = SOIC pin 5 (I2C SDA, PWM)
+arduino pin 2 = = PORTB <- _BV(2) = SOIC pin 7 (I2C SCL, Analog 1)
+// Timer1 -> PWM
+arduino pin 1 = OC1A = PORTB <- _BV(1) = SOIC pin 6 (PWM)
+arduino pin 3 = not(OC1B) = PORTB <- _BV(3) = SOIC pin 2 (Analog 3)
+arduino pin 4 = OC1B = PORTB <- _BV(4) = SOIC pin 3 (Analog 2)
+ */
+#define I2C_SLAVE_ADDRESS 0x4 // the 7-bit address (remember to change this when adapting this example)
+// Get this from https://github.com/rambo/TinyWire
+#include <TinyWireS.h>
+// The default buffer size, though we cannot actually affect it by defining it in the sketch
+#ifndef TWI_RX_BUFFER_SIZE
+#define TWI_RX_BUFFER_SIZE ( 16 )
+#endif
+// For the ADC_xxx helpers
+#include <core_adc.h>
+
+// The "registers" we expose to I2C
+volatile uint8_t i2c_regs[] =
+{
+ 0x0, // Status register, writing (1<<7 & channel) will start a conversion on that channel, the flag will be set low when conversion is done.
+ 0x1, // Averaging count, make this many conversions in row and average the result (well, actually it's a rolling average since we do not want to have the possibility of integer overflows)
+ 0x0, // low byte
+ 0x0, // high byte
+};
+const byte reg_size = sizeof(i2c_regs);
+// Tracks the current register pointer position
+volatile byte reg_position;
+// Tracks wheter to start a conversion cycle
+volatile boolean start_conversion;
+// Counter to track where we are averaging
+byte avg_count;
+// Some temp value holders
+int avg_temp1;
+int avg_temp2;
+
+/**
+ * This is called for each read request we receive, never put more than one byte of data (with TinyWireS.send) to the
+ * send-buffer when using this callback
+ */
+void requestEvent()
+{
+ TinyWireS.send(i2c_regs[reg_position]);
+ // Increment the reg position on each read, and loop back to zero
+ reg_position++;
+ if (reg_position >= reg_size)
+ {
+ reg_position = 0;
+ }
+}
+
+/**
+ * The I2C data received -handler
+ *
+ * This needs to complete before the next incoming transaction (start, data, restart/stop) on the bus does
+ * so be quick, set flags for long running tasks to be called from the mainloop instead of running them directly,
+ */
+void receiveEvent(uint8_t howMany)
+{
+ if (howMany < 1)
+ {
+ // Sanity-check
+ return;
+ }
+ if (howMany > TWI_RX_BUFFER_SIZE)
+ {
+ // Also insane number
+ return;
+ }
+
+ reg_position = TinyWireS.receive();
+ howMany--;
+ if (!howMany)
+ {
+ // This write was only to set the buffer for next read
+ return;
+ }
+ while(howMany--)
+ {
+ i2c_regs[reg_position] = TinyWireS.receive();
+ if ( reg_position == 0 // If it was the first register
+ && bitRead(i2c_regs[0], 7) // And the highest bit is set
+ && !ADC_ConversionInProgress() // and we do not actually have a conversion running already
+ )
+ {
+ start_conversion = true;
+ }
+ reg_position++;
+ if (reg_position >= reg_size)
+ {
+ reg_position = 0;
+ }
+ }
+}
+
+
+void setup()
+{
+ // TODO: Tri-state this and wait for input voltage to stabilize
+ pinMode(3, OUTPUT); // OC1B-, Arduino pin 3, ADC
+ digitalWrite(3, LOW); // Note that this makes the led turn on, it's wire this way to allow for the voltage sensing above.
+
+ pinMode(1, OUTPUT); // OC1A, also The only HW-PWM -pin supported by the tiny core analogWrite
+
+ /**
+ * Reminder: taking care of pull-ups is the masters job
+ */
+
+ TinyWireS.begin(I2C_SLAVE_ADDRESS);
+ TinyWireS.onReceive(receiveEvent);
+ TinyWireS.onRequest(requestEvent);
+
+
+ // Whatever other setup routines ?
+
+ digitalWrite(3, HIGH);
+}
+
+void loop()
+{
+ /**
+ * This is the only way we can detect stop condition (http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=984716&sid=82e9dc7299a8243b86cf7969dd41b5b5#984716)
+ * it needs to be called in a very tight loop in order not to miss any (REMINDER: Do *not* use delay() anywhere, use tws_delay() instead).
+ * It will call the function registered via TinyWireS.onReceive(); if there is data in the buffer on stop.
+ */
+ TinyWireS_stop_check();
+
+ // Thus stuff is basically copied from wiring_analog.c
+ if (start_conversion)
+ {
+ //Avoid doubled starts
+ start_conversion = false;
+ byte adcpin = (i2c_regs[0] & 0x7f); // Set the channel from the control reg, dropping the highest bit.
+#if defined( CORE_ANALOG_FIRST )
+ if ( adcpin >= CORE_ANALOG_FIRST ) adcpin -= CORE_ANALOG_FIRST; // allow for channel or pin numbers
+#endif
+ // NOTE: These handy helpers (ADC_xxx) are only present in the tiny-core, for other cores you need to check their wiring_analog.c source.
+ ADC_SetInputChannel( (adc_ic_t)adcpin ); // we need to typecast
+ ADC_StartConversion();
+ // Reset these variables
+ avg_count = 0;
+ avg_temp2 = 0;
+ }
+
+ if ( bitRead(i2c_regs[0], 7) // We have conversion flag up
+ && !ADC_ConversionInProgress()) // But the conversion is complete
+ {
+ // So handle it
+ avg_temp1 = ADC_GetDataRegister();
+ // Rolling average
+ if (avg_count)
+ {
+ avg_temp2 = (avg_temp2+avg_temp1)/2;
+ }
+ else
+ {
+ avg_temp2 = avg_temp1;
+ }
+ avg_count++;
+ if (avg_count >= i2c_regs[1])
+ {
+ // All done, set the bytes to registers
+ cli();
+ i2c_regs[2] = lowByte(avg_temp2);
+ i2c_regs[3] = highByte(avg_temp2);
+ sei();
+ // And clear the conversion flag so the master knows we're ready
+ bitClear(i2c_regs[0], 7);
+ }
+ else
+ {
+ // Re-trigger conversion
+ ADC_StartConversion();
+ }
+ }
+
+}
diff --git a/digital-driver-board/firmware/TinyWireS/examples/attiny85_i2c_slave/attiny85_i2c_slave.ino b/digital-driver-board/firmware/TinyWireS/examples/attiny85_i2c_slave/attiny85_i2c_slave.ino
new file mode 100644
index 0000000..df2532f
--- /dev/null
+++ b/digital-driver-board/firmware/TinyWireS/examples/attiny85_i2c_slave/attiny85_i2c_slave.ino
@@ -0,0 +1,152 @@
+/**
+ * Example sketch for writing to and reading from a slave in transactional manner
+ *
+ * NOTE: You must not use delay() or I2C communications will fail, use tws_delay() instead (or preferably some smarter timing system)
+ *
+ * On write the first byte received is considered the register addres to modify/read
+ * On each byte sent or read the register address is incremented (and it will loop back to 0)
+ *
+ * You can try this with the Arduino I2C REPL sketch at https://github.com/rambo/I2C/blob/master/examples/i2crepl/i2crepl.ino
+ * If you have bus-pirate remember that the older revisions do not like the slave streching the clock, this leads to all sorts of weird behaviour
+ *
+ * To read third value (register number 2 since counting starts at 0) send "[ 8 2 [ 9 r ]", value read should be 0xBE
+ * If you then send "[ 9 r r r ]" you should get 0xEF 0xDE 0xAD as response (demonstrating the register counter looping back to zero)
+ *
+ * You need to have at least 8MHz clock on the ATTiny for this to work (and in fact I have so far tested it only on ATTiny85 @8MHz using internal oscillator)
+ * Remember to "Burn bootloader" to make sure your chip is in correct mode
+ */
+
+
+/**
+ * Pin notes by Suovula, see also http://hlt.media.mit.edu/?p=1229
+ *
+ * DIP and SOIC have same pinout, however the SOIC chips are much cheaper, especially if you buy more than 5 at a time
+ * For nice breakout boards see https://github.com/rambo/attiny_boards
+ *
+ * Basically the arduino pin numbers map directly to the PORTB bit numbers.
+ *
+// I2C
+arduino pin 0 = not(OC1A) = PORTB <- _BV(0) = SOIC pin 5 (I2C SDA, PWM)
+arduino pin 2 = = PORTB <- _BV(2) = SOIC pin 7 (I2C SCL, Analog 1)
+// Timer1 -> PWM
+arduino pin 1 = OC1A = PORTB <- _BV(1) = SOIC pin 6 (PWM)
+arduino pin 3 = not(OC1B) = PORTB <- _BV(3) = SOIC pin 2 (Analog 3)
+arduino pin 4 = OC1B = PORTB <- _BV(4) = SOIC pin 3 (Analog 2)
+ */
+#define I2C_SLAVE_ADDRESS 0x4 // the 7-bit address (remember to change this when adapting this example)
+// Get this from https://github.com/rambo/TinyWire
+#include <TinyWireS.h>
+// The default buffer size, Can't recall the scope of defines right now
+#ifndef TWI_RX_BUFFER_SIZE
+#define TWI_RX_BUFFER_SIZE ( 16 )
+#endif
+
+
+volatile uint8_t i2c_regs[] =
+{
+ 0xDE,
+ 0xAD,
+ 0xBE,
+ 0xEF,
+};
+// Tracks the current register pointer position
+volatile byte reg_position;
+const byte reg_size = sizeof(i2c_regs);
+
+/**
+ * This is called for each read request we receive, never put more than one byte of data (with TinyWireS.send) to the
+ * send-buffer when using this callback
+ */
+void requestEvent()
+{
+ TinyWireS.send(i2c_regs[reg_position]);
+ // Increment the reg position on each read, and loop back to zero
+ reg_position++;
+ if (reg_position >= reg_size)
+ {
+ reg_position = 0;
+ }
+}
+
+// TODO: Either update this to use something smarter for timing or remove it alltogether
+void blinkn(uint8_t blinks)
+{
+ digitalWrite(3, HIGH);
+ while(blinks--)
+ {
+ digitalWrite(3, LOW);
+ tws_delay(50);
+ digitalWrite(3, HIGH);
+ tws_delay(100);
+ }
+}
+
+/**
+ * The I2C data received -handler
+ *
+ * This needs to complete before the next incoming transaction (start, data, restart/stop) on the bus does
+ * so be quick, set flags for long running tasks to be called from the mainloop instead of running them directly,
+ */
+void receiveEvent(uint8_t howMany)
+{
+ if (howMany < 1)
+ {
+ // Sanity-check
+ return;
+ }
+ if (howMany > TWI_RX_BUFFER_SIZE)
+ {
+ // Also insane number
+ return;
+ }
+
+ reg_position = TinyWireS.receive();
+ howMany--;
+ if (!howMany)
+ {
+ // This write was only to set the buffer for next read
+ return;
+ }
+ while(howMany--)
+ {
+ i2c_regs[reg_position] = TinyWireS.receive();
+ reg_position++;
+ if (reg_position >= reg_size)
+ {
+ reg_position = 0;
+ }
+ }
+}
+
+
+void setup()
+{
+ // TODO: Tri-state this and wait for input voltage to stabilize
+ pinMode(3, OUTPUT); // OC1B-, Arduino pin 3, ADC
+ digitalWrite(3, LOW); // Note that this makes the led turn on, it's wire this way to allow for the voltage sensing above.
+
+ pinMode(1, OUTPUT); // OC1A, also The only HW-PWM -pin supported by the tiny core analogWrite
+
+ /**
+ * Reminder: taking care of pull-ups is the masters job
+ */
+
+ TinyWireS.begin(I2C_SLAVE_ADDRESS);
+ TinyWireS.onReceive(receiveEvent);
+ TinyWireS.onRequest(requestEvent);
+
+
+ // Whatever other setup routines ?
+
+ digitalWrite(3, HIGH);
+}
+
+void loop()
+{
+ /**
+ * This is the only way we can detect stop condition (http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=984716&sid=82e9dc7299a8243b86cf7969dd41b5b5#984716)
+ * it needs to be called in a very tight loop in order not to miss any (REMINDER: Do *not* use delay() anywhere, use tws_delay() instead).
+ * It will call the function registered via TinyWireS.onReceive(); if there is data in the buffer on stop.
+ */
+ TinyWireS_stop_check();
+}
diff --git a/digital-driver-board/firmware/TinyWireS/examples/attiny85_i2c_slave_task/attiny85_i2c_slave_task.ino b/digital-driver-board/firmware/TinyWireS/examples/attiny85_i2c_slave_task/attiny85_i2c_slave_task.ino
new file mode 100644
index 0000000..4b3f269
--- /dev/null
+++ b/digital-driver-board/firmware/TinyWireS/examples/attiny85_i2c_slave_task/attiny85_i2c_slave_task.ino
@@ -0,0 +1,246 @@
+/**
+ * Example sketch for writing to and reading from a slave in transactional manner, it will also blink a led attached to pin 3 (which is the SOIC pin 2)
+ * (provided you're using one of my ATTiny85 boards from https://github.com/rambo/attiny_boards with the led soldered)
+ *
+ * NOTE: You must not use delay() or I2C communications will fail, use tws_delay() instead (or preferably some smarter timing system, like the Task library used in this example)
+ *
+ * On write the first byte received is considered the register addres to modify/read
+ * On each byte sent or read the register address is incremented (and it will loop back to 0)
+ *
+ * You can try this with the Arduino I2C REPL sketch at https://github.com/rambo/I2C/blob/master/examples/i2crepl/i2crepl.ino
+ * If you have bus-pirate remember that the older revisions do not like the slave streching the clock, this leads to all sorts of weird behaviour
+ *
+ * By default this blinks the SOS morse pattern and then has long on/off time to indicate end of pattern, send [ 8 0 32 ] (using the REPL/bus-pirate
+ * semantics) to make the delay per bit smaller (and thus blinking faster). The pattern lenght is calculated from the register size, it would be fairly
+ * trivial to make it yet another variable changeable via I2C.
+ *
+ * You need to have at least 8MHz clock on the ATTiny for this to work (and in fact I have so far tested it only on ATTiny85 @8MHz using internal oscillator)
+ * Remember to "Burn bootloader" to make sure your chip is in correct mode
+ */
+
+
+/**
+ * Pin notes by Suovula, see also http://hlt.media.mit.edu/?p=1229
+ *
+ * DIP and SOIC have same pinout, however the SOIC chips are much cheaper, especially if you buy more than 5 at a time
+ * For nice breakout boards see https://github.com/rambo/attiny_boards
+ *
+ * Basically the arduino pin numbers map directly to the PORTB bit numbers.
+ *
+// I2C
+arduino pin 0 = not(OC1A) = PORTB <- _BV(0) = SOIC pin 5 (I2C SDA, PWM)
+arduino pin 2 = = PORTB <- _BV(2) = SOIC pin 7 (I2C SCL, Analog 1)
+// Timer1 -> PWM
+arduino pin 1 = OC1A = PORTB <- _BV(1) = SOIC pin 6 (PWM)
+arduino pin 3 = not(OC1B) = PORTB <- _BV(3) = SOIC pin 2 (Analog 3)
+arduino pin 4 = OC1B = PORTB <- _BV(4) = SOIC pin 3 (Analog 2)
+ */
+#define I2C_SLAVE_ADDRESS 0x4 // the 7-bit address (remember to change this when adapting this example)
+// Get this from https://github.com/rambo/TinyWire
+#include <TinyWireS.h>
+// The default buffer size, Can't recall the scope of defines right now
+#ifndef TWI_RX_BUFFER_SIZE
+#define TWI_RX_BUFFER_SIZE ( 16 )
+#endif
+// Get this library from http://bleaklow.com/files/2010/Task.tar.gz
+// and read http://bleaklow.com/2010/07/20/a_very_simple_arduino_task_manager.html for background and instructions
+#include <Task.h>
+#include <TaskScheduler.h>
+
+// The led is connected so that the tiny sinks current
+#define LED_ON LOW
+#define LED_OFF HIGH
+
+// The I2C registers
+volatile uint8_t i2c_regs[] =
+{
+ 150, // Delay between each position (ms, remeber that this isa byte so 255 is max)
+ B10101000, // SOS pattern
+ B01110111,
+ B01110001,
+ B01010000,
+ B00000000,
+ B11111111, // Long on and off to mark end of pattern
+ B00000000,
+};
+// Tracks the current register pointer position
+volatile byte reg_position;
+const byte reg_size = sizeof(i2c_regs);
+
+
+/**
+ * BEGIN: PatternBlinker task based on the Task library Blinker example
+ */
+// Timed task to blink a LED.
+const byte pattern_lenght = (sizeof(i2c_regs)-1) * 8; // bits (first is the speed, rest is the pattern)
+class PatternBlinker : public TimedTask
+{
+public:
+ // Create a new blinker for the specified pin and rate.
+ PatternBlinker(uint8_t _pin);
+ virtual void run(uint32_t now);
+private:
+ uint8_t pin; // LED pin.
+ uint8_t pattern_position; // Used to calcuate the register and bit offset
+};
+
+PatternBlinker::PatternBlinker(uint8_t _pin)
+: TimedTask(millis()),
+ pin(_pin)
+{
+ pinMode(pin, OUTPUT); // Set pin for output.
+}
+
+void PatternBlinker::run(uint32_t now)
+{
+ // Start by setting the next runtime
+ incRunTime(i2c_regs[0]);
+
+ // Written out for clear code, the complier might optimize it to something more efficient even without it being unrolled into one line
+ byte reg = i2c_regs[1+(pattern_position/8)]; // Get the register where the bit pattern position is stored
+ byte shift_amount = 7 - (pattern_position % 7); // To have "natural" left-to-right pattern flow.
+ bool state = (reg >> shift_amount) & 0x1;
+ if (state) {
+ digitalWrite(pin, LED_ON);
+ } else {
+ digitalWrite(pin, LED_OFF);
+ }
+ // Calculate the next pattern position
+ pattern_position = (pattern_position+1) % pattern_lenght;
+}
+/**
+ * END: PatternBlinker task copied from the Task library example
+ */
+/**
+ * BEGIN: I2C Stop flag checker
+ *
+ * This task needs to run almost all the time due to the USI I2C implementation limitations
+ *
+ * So I2CStopCheck_YIELD_TICKS below is used to specify how often the task is run, not it's every 4 ticks
+ */
+#define I2CStopCheck_YIELD_TICKS 4
+class I2CStopCheck : public Task
+{
+public:
+ I2CStopCheck();
+ virtual void run(uint32_t now);
+ virtual bool canRun(uint32_t now);
+private:
+ uint8_t yield_counter; // Incremented on each canRun call, used to yield to other tasks.
+};
+
+I2CStopCheck::I2CStopCheck()
+: Task()
+{
+}
+
+// We can't just return true since then no other task could ever run (since we have the priority)
+bool I2CStopCheck::canRun(uint32_t now)
+{
+ yield_counter++;
+ bool ret = false;
+ if (yield_counter == I2CStopCheck_YIELD_TICKS)
+ {
+ ret = true;
+ yield_counter = 0;
+ }
+ return ret;
+}
+
+void I2CStopCheck::run(uint32_t now)
+{
+ TinyWireS_stop_check();
+}
+/**
+ * END: I2C Stop flag checker
+ */
+
+// Create the tasks.
+PatternBlinker blinker(3);
+I2CStopCheck checker;
+
+// Tasks are in priority order, only one task is run per tick
+Task *tasks[] = { &checker, &blinker, };
+TaskScheduler sched(tasks, NUM_TASKS(tasks));
+
+
+/**
+ * This is called for each read request we receive, never put more than one byte of data (with TinyWireS.send) to the
+ * send-buffer when using this callback
+ */
+void requestEvent()
+{
+ TinyWireS.send(i2c_regs[reg_position]);
+ // Increment the reg position on each read, and loop back to zero
+ reg_position++;
+ if (reg_position >= reg_size)
+ {
+ reg_position = 0;
+ }
+}
+
+/**
+ * The I2C data received -handler
+ *
+ * This needs to complete before the next incoming transaction (start, data, restart/stop) on the bus does
+ * so be quick, set flags for long running tasks to be called from the mainloop instead of running them directly,
+ */
+void receiveEvent(uint8_t howMany)
+{
+ if (howMany < 1)
+ {
+ // Sanity-check
+ return;
+ }
+ if (howMany > TWI_RX_BUFFER_SIZE)
+ {
+ // Also insane number
+ return;
+ }
+
+ reg_position = TinyWireS.receive();
+ howMany--;
+ if (!howMany)
+ {
+ // This write was only to set the buffer for next read
+ return;
+ }
+ while(howMany--)
+ {
+ i2c_regs[reg_position] = TinyWireS.receive();
+ reg_position++;
+ if (reg_position >= reg_size)
+ {
+ reg_position = 0;
+ }
+ }
+}
+
+
+void setup()
+{
+ // TODO: Tri-state this and wait for input voltage to stabilize
+ pinMode(3, OUTPUT); // OC1B-, Arduino pin 3, ADC
+ digitalWrite(3, LED_ON); // Note that this makes the led turn on, it's wire this way to allow for the voltage sensing above.
+
+ pinMode(1, OUTPUT); // OC1A, also The only HW-PWM -pin supported by the tiny core analogWrite
+
+ /**
+ * Reminder: taking care of pull-ups is the masters job
+ */
+
+ TinyWireS.begin(I2C_SLAVE_ADDRESS);
+ TinyWireS.onReceive(receiveEvent);
+ TinyWireS.onRequest(requestEvent);
+
+
+ // Whatever other setup routines ?
+
+ digitalWrite(3, LED_OFF);
+}
+
+void loop()
+{
+ // Run the scheduler - never returns.
+ sched.run();
+}
diff --git a/digital-driver-board/firmware/TinyWireS/keywords.txt b/digital-driver-board/firmware/TinyWireS/keywords.txt
new file mode 100644
index 0000000..ccc0b46
--- /dev/null
+++ b/digital-driver-board/firmware/TinyWireS/keywords.txt
@@ -0,0 +1,27 @@
+#######################################
+# Syntax Coloring Map For TinyWireS
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+begin KEYWORD2
+send KEYWORD2
+available KEYWORD2
+receive KEYWORD2
+
+#######################################
+# Instances (KEYWORD2)
+#######################################
+
+TinyWireS KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
diff --git a/digital-driver-board/firmware/TinyWireS/usiTwiSlave.c b/digital-driver-board/firmware/TinyWireS/usiTwiSlave.c
new file mode 100644
index 0000000..a263184
--- /dev/null
+++ b/digital-driver-board/firmware/TinyWireS/usiTwiSlave.c
@@ -0,0 +1,748 @@
+/********************************************************************************
+
+USI TWI Slave driver.
+
+Created by Donald R. Blake. donblake at worldnet.att.net
+Adapted by Jochen Toppe, jochen.toppe at jtoee.com
+
+---------------------------------------------------------------------------------
+
+Created from Atmel source files for Application Note AVR312: Using the USI Module
+as an I2C slave.
+
+This program is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+---------------------------------------------------------------------------------
+
+Change Activity:
+
+ Date Description
+ ------ -------------
+ 16 Mar 2007 Created.
+ 27 Mar 2007 Added support for ATtiny261, 461 and 861.
+ 26 Apr 2007 Fixed ACK of slave address on a read.
+ 04 Jul 2007 Fixed USISIF in ATtiny45 def
+ 12 Dev 2009 Added callback functions for data requests
+ 06 Feb 2016 Minor change to allow mutli-byte requestFrom() from master.
+ 10 Feb 2016 Simplied RX/TX buffer code and allowed use of full buffer.
+ 13 Feb 2016 Made USI_RECEIVE_CALLBACK() callback fully interrupt-driven
+ 12 Dec 2016 Added support for ATtiny167
+ 23 Dec 2017 Fixed repeated restart (which broke when making receive callback
+ interrupt-driven)
+
+********************************************************************************/
+
+
+/********************************************************************************
+ includes
+********************************************************************************/
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+#include "usiTwiSlave.h"
+//#include "../common/util.h"
+
+
+/********************************************************************************
+ device dependent defines
+********************************************************************************/
+
+#if defined( __AVR_ATtiny167__ )
+# define DDR_USI DDRB
+# define PORT_USI PORTB
+# define PIN_USI PINB
+# define PORT_USI_SDA PB0
+# define PORT_USI_SCL PB2
+# define PIN_USI_SDA PINB0
+# define PIN_USI_SCL PINB2
+# define USI_START_COND_INT USISIF
+# define USI_START_VECTOR USI_START_vect
+# define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
+#endif
+
+#if defined( __AVR_ATtiny2313__ )
+# define DDR_USI DDRB
+# define PORT_USI PORTB
+# define PIN_USI PINB
+# define PORT_USI_SDA PB5
+# define PORT_USI_SCL PB7
+# define PIN_USI_SDA PINB5
+# define PIN_USI_SCL PINB7
+# define USI_START_COND_INT USISIF
+# define USI_START_VECTOR USI_START_vect
+# define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
+#endif
+
+#if defined(__AVR_ATtiny84__) | \
+ defined(__AVR_ATtiny44__)
+# define DDR_USI DDRA
+# define PORT_USI PORTA
+# define PIN_USI PINA
+# define PORT_USI_SDA PORTA6
+# define PORT_USI_SCL PORTA4
+# define PIN_USI_SDA PINA6
+# define PIN_USI_SCL PINA4
+# define USI_START_COND_INT USISIF
+# define USI_START_VECTOR USI_START_vect
+# define USI_OVERFLOW_VECTOR USI_OVF_vect
+#endif
+
+#if defined( __AVR_ATtiny25__ ) | \
+ defined( __AVR_ATtiny45__ ) | \
+ defined( __AVR_ATtiny85__ )
+# define DDR_USI DDRB
+# define PORT_USI PORTB
+# define PIN_USI PINB
+# define PORT_USI_SDA PB0
+# define PORT_USI_SCL PB2
+# define PIN_USI_SDA PINB0
+# define PIN_USI_SCL PINB2
+# define USI_START_COND_INT USISIF
+# define USI_START_VECTOR USI_START_vect
+# define USI_OVERFLOW_VECTOR USI_OVF_vect
+#endif
+
+#if defined( __AVR_ATtiny26__ )
+# define DDR_USI DDRB
+# define PORT_USI PORTB
+# define PIN_USI PINB
+# define PORT_USI_SDA PB0
+# define PORT_USI_SCL PB2
+# define PIN_USI_SDA PINB0
+# define PIN_USI_SCL PINB2
+# define USI_START_COND_INT USISIF
+# define USI_START_VECTOR USI_STRT_vect
+# define USI_OVERFLOW_VECTOR USI_OVF_vect
+#endif
+
+#if defined( __AVR_ATtiny261__ ) | \
+ defined( __AVR_ATtiny461__ ) | \
+ defined( __AVR_ATtiny861__ )
+# define DDR_USI DDRB
+# define PORT_USI PORTB
+# define PIN_USI PINB
+# define PORT_USI_SDA PB0
+# define PORT_USI_SCL PB2
+# define PIN_USI_SDA PINB0
+# define PIN_USI_SCL PINB2
+# define USI_START_COND_INT USISIF
+# define USI_START_VECTOR USI_START_vect
+# define USI_OVERFLOW_VECTOR USI_OVF_vect
+#endif
+
+#if defined( __AVR_ATmega165__ ) | \
+ defined( __AVR_ATmega325__ ) | \
+ defined( __AVR_ATmega3250__ ) | \
+ defined( __AVR_ATmega645__ ) | \
+ defined( __AVR_ATmega6450__ ) | \
+ defined( __AVR_ATmega329__ ) | \
+ defined( __AVR_ATmega3290__ )
+# define DDR_USI DDRE
+# define PORT_USI PORTE
+# define PIN_USI PINE
+# define PORT_USI_SDA PE5
+# define PORT_USI_SCL PE4
+# define PIN_USI_SDA PINE5
+# define PIN_USI_SCL PINE4
+# define USI_START_COND_INT USISIF
+# define USI_START_VECTOR USI_START_vect
+# define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
+#endif
+
+#if defined( __AVR_ATmega169__ )
+# define DDR_USI DDRE
+# define PORT_USI PORTE
+# define PIN_USI PINE
+# define PORT_USI_SDA PE5
+# define PORT_USI_SCL PE4
+# define PIN_USI_SDA PINE5
+# define PIN_USI_SCL PINE4
+# define USI_START_COND_INT USISIF
+# define USI_START_VECTOR USI_START_vect
+# define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
+#endif
+
+// These macros make the stop condition detection code more readable.
+#define USI_PINS_SCL_SDA ( ( 1 << PIN_USI_SDA ) | ( 1 << PIN_USI_SCL ) )
+#define USI_PINS_SDA ( 1 << PIN_USI_SDA )
+#define USI_PINS_SCL ( 1 << PIN_USI_SCL )
+
+/********************************************************************************
+
+ functions implemented as macros
+
+********************************************************************************/
+
+#define SET_USI_TO_SEND_ACK( ) \
+{ \
+ /* prepare ACK, ack is a zero */ \
+ USIDR = 0; \
+ /* set SDA as output */ \
+ DDR_USI |= ( 1 << PORT_USI_SDA ); \
+ /* clear all interrupt flags, except Start Cond */ \
+ USISR = \
+ ( 0 << USI_START_COND_INT ) | \
+ ( 1 << USIOIF ) | ( 1 << USIPF ) | \
+ ( 1 << USIDC )| \
+ /* set USI counter to shift 1 bit */ \
+ ( 0x0E << USICNT0 ); \
+}
+
+#define SET_USI_TO_READ_ACK( ) \
+{ \
+ /* set SDA as input */ \
+ DDR_USI &= ~( 1 << PORT_USI_SDA ); \
+ /* prepare ACK */ \
+ USIDR = 0; \
+ /* clear all interrupt flags, except Start Cond */ \
+ USISR = \
+ ( 0 << USI_START_COND_INT ) | \
+ ( 1 << USIOIF ) | \
+ ( 1 << USIPF ) | \
+ ( 1 << USIDC ) | \
+ /* set USI counter to shift 1 bit */ \
+ ( 0x0E << USICNT0 ); \
+}
+
+#define SET_USI_TO_TWI_START_CONDITION_MODE( ) \
+{ \
+ USICR = \
+ /* enable Start Condition Interrupt, disable Overflow Interrupt */ \
+ ( 1 << USISIE ) | ( 0 << USIOIE ) | \
+ /* set USI in Two-wire mode, no USI Counter overflow hold */ \
+ ( 1 << USIWM1 ) | ( 0 << USIWM0 ) | \
+ /* Shift Register Clock Source = External, positive edge */ \
+ /* 4-Bit Counter Source = external, both edges */ \
+ ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) | \
+ /* no toggle clock-port pin */ \
+ ( 0 << USITC ); \
+ USISR = \
+ /* clear all interrupt flags, except Start Cond */ \
+ ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | \
+ ( 1 << USIDC ) | ( 0x0 << USICNT0 ); \
+}
+
+#define SET_USI_TO_SEND_DATA( ) \
+{ \
+ /* set SDA as output */ \
+ DDR_USI |= ( 1 << PORT_USI_SDA ); \
+ /* clear all interrupt flags, except Start Cond */ \
+ USISR = \
+ ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | \
+ ( 1 << USIDC) | \
+ /* set USI to shift out 8 bits */ \
+ ( 0x0 << USICNT0 ); \
+}
+
+#define SET_USI_TO_READ_DATA( ) \
+{ \
+ /* set SDA as input */ \
+ DDR_USI &= ~( 1 << PORT_USI_SDA ); \
+ /* clear all interrupt flags, except Start Cond */ \
+ USISR = \
+ ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | \
+ ( 1 << USIPF ) | ( 1 << USIDC ) | \
+ /* set USI to shift out 8 bits */ \
+ ( 0x0 << USICNT0 ); \
+}
+
+#define USI_RECEIVE_CALLBACK() \
+{ \
+ if (usi_onReceiverPtr) \
+ { \
+ if (usiTwiAmountDataInReceiveBuffer()) \
+ { \
+ usi_onReceiverPtr(usiTwiAmountDataInReceiveBuffer()); \
+ } \
+ } \
+}
+
+#define USI_REQUEST_CALLBACK() \
+{ \
+ if(usi_onRequestPtr) usi_onRequestPtr(); \
+}
+
+/********************************************************************************
+
+ typedef's
+
+********************************************************************************/
+
+typedef enum
+{
+ USI_SLAVE_CHECK_ADDRESS = 0x00,
+ USI_SLAVE_SEND_DATA = 0x01,
+ USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA = 0x02,
+ USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA = 0x03,
+ USI_SLAVE_REQUEST_DATA = 0x04,
+ USI_SLAVE_GET_DATA_AND_SEND_ACK = 0x05
+} overflowState_t;
+
+
+
+/********************************************************************************
+
+ local variables
+
+********************************************************************************/
+
+static uint8_t slaveAddress;
+static uint8_t sleep_enable_bit;
+static uint8_t in_transaction;
+static volatile overflowState_t overflowState;
+
+
+static uint8_t rxBuf[ TWI_RX_BUFFER_SIZE ];
+static volatile uint8_t rxHead;
+static volatile uint8_t rxTail;
+static volatile uint8_t rxCount;
+
+static uint8_t txBuf[ TWI_TX_BUFFER_SIZE ];
+static volatile uint8_t txHead;
+static volatile uint8_t txTail;
+static volatile uint8_t txCount;
+
+
+
+/********************************************************************************
+
+ local functions
+
+********************************************************************************/
+
+// flushes the TWI buffers
+
+static void flushTwiBuffers( void )
+{
+ rxTail = 0;
+ rxHead = 0;
+ rxCount = 0;
+ txTail = 0;
+ txHead = 0;
+ txCount = 0;
+} // end flushTwiBuffers
+
+
+
+/********************************************************************************
+
+ public functions
+
+********************************************************************************/
+
+// initialise USI for TWI slave mode
+
+void usiTwiSlaveInit( uint8_t ownAddress )
+{
+ // initialize the TX and RX buffers to empty
+ flushTwiBuffers( );
+
+ slaveAddress = ownAddress;
+
+ // In Two Wire mode (USIWM1, USIWM0 = 1X), the slave USI will pull SCL
+ // low when a start condition is detected or a counter overflow (only
+ // for USIWM1, USIWM0 = 11). This inserts a wait state. SCL is released
+ // by the ISRs (USI_START_vect and USI_OVERFLOW_vect).
+
+ // Set SCL and SDA as output
+ DDR_USI |= ( 1 << PORT_USI_SCL ) | ( 1 << PORT_USI_SDA );
+
+ // set SCL high
+ PORT_USI |= ( 1 << PORT_USI_SCL );
+
+ // set SDA high
+ PORT_USI |= ( 1 << PORT_USI_SDA );
+
+ // Set SDA as input
+ DDR_USI &= ~( 1 << PORT_USI_SDA );
+
+ USICR =
+ // enable Start Condition Interrupt
+ ( 1 << USISIE ) |
+ // disable Overflow Interrupt
+ ( 0 << USIOIE ) |
+ // set USI in Two-wire mode, no USI Counter overflow hold
+ ( 1 << USIWM1 ) | ( 0 << USIWM0 ) |
+ // Shift Register Clock Source = external, positive edge
+ // 4-Bit Counter Source = external, both edges
+ ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) |
+ // no toggle clock-port pin
+ ( 0 << USITC );
+
+ // clear all interrupt flags and reset overflow counter
+
+ USISR = ( 1 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | ( 1 << USIDC );
+
+ // The 'in_transaction' variable remembers if the usiTwiSlave driver is in the middle of
+ // an i2c transaction. Initialize it to zero
+ in_transaction = 0;
+
+} // end usiTwiSlaveInit
+
+
+bool usiTwiDataInTransmitBuffer(void)
+{
+
+ // return 0 (false) if the receive buffer is empty
+ return txCount;
+
+} // end usiTwiDataInTransmitBuffer
+
+
+// put data in the transmission buffer, wait if buffer is full
+
+void usiTwiTransmitByte( uint8_t data )
+{
+
+ // wait for free space in buffer
+ while ( txCount == TWI_TX_BUFFER_SIZE) ;
+
+ // store data in buffer
+ txBuf[ txHead ] = data;
+ txHead = ( txHead + 1 ) & TWI_TX_BUFFER_MASK;
+ txCount++;
+
+} // end usiTwiTransmitByte
+
+
+// return a byte from the receive buffer, wait if buffer is empty
+
+uint8_t usiTwiReceiveByte( void )
+{
+ uint8_t rtn_byte;
+
+ // wait for Rx data
+ while ( !rxCount );
+
+ rtn_byte = rxBuf [ rxTail ];
+ // calculate buffer index
+ rxTail = ( rxTail + 1 ) & TWI_RX_BUFFER_MASK;
+ rxCount--;
+
+ // return data from the buffer.
+ return rtn_byte;
+
+} // end usiTwiReceiveByte
+
+
+uint8_t usiTwiAmountDataInReceiveBuffer(void)
+{
+ return rxCount;
+}
+
+
+/********************************************************************************
+
+ USI Start Condition ISR
+
+********************************************************************************/
+
+ISR( USI_START_VECTOR )
+{
+ uint8_t usi_pins;
+ // http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__interrupts.html
+
+ // Notes about ISR. The compiler in the Arduino IDE handles some of the
+ // basic ISR plumbing (unless the "ISR_NAKED" attribute is applied).
+ // * The AVR processor resets the SREG.I bit when jumping into an ISR
+ // * The compiler automatically adds code to save SREG
+ // * < user's ISR code goes here >
+ // * The compiler automatically adds code to restore SREG
+ // * The compiler automatically uses the RETI instruction to return from the ISR.
+ // The RETI instruction enables interrupts after the return from ISR.
+ // The compiler behavior can be altered with attributes into the ISR declaration;
+ // however, the description above is the default.
+
+ // cli() call is not necessary. Processor disables interrupts when
+ // jumping to an ISR
+
+ // no need to save the SREG. The compiler does this automatically when using the
+ // ISR construct without modifying attributes.
+
+ if ( !in_transaction )
+ {
+ // remeber the sleep enable bit when entering the ISR
+ sleep_enable_bit = MCUCR & ( 1 << SE );
+
+ // clear the sleep enable bit to prevent the CPU from entering sleep mode while executing this ISR.
+ MCUCR &= ~( 1 << SE );
+ }
+
+ // set default starting conditions for new TWI package
+ overflowState = USI_SLAVE_CHECK_ADDRESS;
+
+ // set SDA as input
+ DDR_USI &= ~( 1 << PORT_USI_SDA );
+
+ // the start condition is that the master pulls SDA low.
+
+ // wait for SCL to go low to ensure the Start Condition has completed (the
+ // start detector will hold SCL low ) - if a Stop Condition arises then leave
+ // the interrupt to prevent waiting forever - don't use USISR to test for Stop
+ // Condition as in Application Note AVR312 because the Stop Condition Flag is
+ // going to be set from the last TWI sequence
+
+ // while SCL is high and SDA is low
+ while ( ( usi_pins = PIN_USI & USI_PINS_SCL_SDA ) == USI_PINS_SCL );
+
+ // if SDA line was low at SCL edge, then start condition occurred
+ if ( !( usi_pins & USI_PINS_SDA ) )
+ {
+ // a Stop Condition did not occur
+
+ // Execute callback if this is a repeated start
+ if (in_transaction)
+ {
+ USI_RECEIVE_CALLBACK();
+ }
+
+ USICR =
+ // keep Start Condition Interrupt enabled to detect RESTART
+ ( 1 << USISIE ) |
+ // enable Overflow Interrupt
+ ( 1 << USIOIE ) |
+ // set USI in Two-wire mode, hold SCL low on USI Counter overflow
+ ( 1 << USIWM1 ) | ( 1 << USIWM0 ) |
+ // Shift Register Clock Source = External, positive edge
+ // 4-Bit Counter Source = external, both edges
+ ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) |
+ // no toggle clock-port pin
+ ( 0 << USITC );
+
+ //remember that the USI is in a valid i2c transaction
+ in_transaction = 1;
+
+ }
+ else
+ {
+ // a Stop Condition did occur
+
+ USICR =
+ // enable Start Condition Interrupt
+ ( 1 << USISIE ) |
+ // disable Overflow Interrupt
+ ( 0 << USIOIE ) |
+ // set USI in Two-wire mode, no USI Counter overflow hold
+ ( 1 << USIWM1 ) | ( 0 << USIWM0 ) |
+ // Shift Register Clock Source = external, positive edge
+ // 4-Bit Counter Source = external, both edges
+ ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) |
+ // no toggle clock-port pin
+ ( 0 << USITC );
+
+ //no longer in valid i2c transaction
+ in_transaction = 0;
+ // restore the sleep enable bit
+ MCUCR |= sleep_enable_bit;
+
+ } // end if
+
+ USISR =
+ // clear interrupt flags - resetting the Start Condition Flag will
+ // release SCL
+ ( 1 << USI_START_COND_INT ) | ( 1 << USIOIF ) |
+ ( 1 << USIPF ) |( 1 << USIDC ) |
+ // set USI to sample 8 bits (count 16 external SCL pin toggles)
+ ( 0x0 << USICNT0);
+
+ // no need to restore the SREG. The compiler does this automatically when using the
+ // ISR construct without modifying attributes.
+
+ // The compiler automatically uses an RETI instruction to return when using the
+ // ISR construct without modifying attributes.
+
+} // end ISR( USI_START_VECTOR )
+
+
+
+/********************************************************************************
+
+ USI Overflow ISR
+
+Handles all the communication.
+
+Only disabled when waiting for a new Start Condition.
+
+********************************************************************************/
+
+ISR( USI_OVERFLOW_VECTOR )
+{
+ uint8_t finished;
+ uint8_t usi_pins;
+
+ // http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__interrupts.html
+
+ // Notes about ISR. The compiler in the Arduino IDE handles some of the
+ // basic ISR plumbing.
+ // * The AVR processor resets the SREG.I bit when jumping into an ISR
+ // * The compiler automatically adds code to save the SREG
+ // * < user's ISR code goes here >
+ // * The compiler automatically adds code to restore the SREG
+ // * The compiler automatically uses the RETI instruction to return from the ISR.
+ // The RETI insturction enables interrupts after the return from ISR.
+ // The compiler behavior can be altered with attributes into the ISR declaration;
+ // however, the description above is the default.
+
+ // cli() call is not necessary. Processor disables interrupts when
+ // jumping to an ISR
+
+ // no need to save the SREG. The compiler does this automatically when using the
+ // ISR construct without modifying attributes.
+
+ // The ISR is only ever entered because the ISR(USI_START_VECTOR) interrupt
+ // routine ran first. That routine saved the sleep mode and disabled sleep.
+
+ // Most of the time this routine exits, it has setup the USI to shift in/out bits
+ // and is expected to re-entered because of the USI overflow interrupt. Track whether or
+ // not the transaction is completely finished.
+ finished = 0;
+
+
+ switch ( overflowState )
+ {
+
+ // Address mode: check address and send ACK (and next USI_SLAVE_SEND_DATA) if OK,
+ // else reset USI
+ case USI_SLAVE_CHECK_ADDRESS:
+ if ( ( USIDR == 0 ) || ( ( USIDR >> 1 ) == slaveAddress) )
+ {
+ if ( USIDR & 0x01 )
+ {
+ overflowState = USI_SLAVE_SEND_DATA;
+ }
+ else
+ {
+ overflowState = USI_SLAVE_REQUEST_DATA;
+ } // end if
+
+ // ack the start frame
+ // sets up the USI to pull SDA low and clock one bit (two edges)
+ SET_USI_TO_SEND_ACK( );
+ }
+ else
+ {
+ SET_USI_TO_TWI_START_CONDITION_MODE( );
+ finished = 1;
+ }
+ break;
+
+ // master-read / slave-send: check reply and goto USI_SLAVE_SEND_DATA if OK,
+ // else reset USI
+ case USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA:
+ // Execute request callback for each byte requested, as this is the intended
+ // behavior of this library
+ USI_REQUEST_CALLBACK();
+ if ( USIDR )
+ {
+ // if NACK, the master does not want more data
+ SET_USI_TO_TWI_START_CONDITION_MODE( );
+ finished = 1;
+ break;
+ }
+ // from here we just drop straight into USI_SLAVE_SEND_DATA if the
+ // master sent an ACK
+
+ // copy data from buffer to USIDR and set USI to shift byte
+ // next USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA
+ case USI_SLAVE_SEND_DATA:
+ // Get data from Buffer
+ if ( txCount )
+ {
+ USIDR = txBuf[ txTail ];
+ txTail = ( txTail + 1 ) & TWI_TX_BUFFER_MASK;
+ txCount--;
+
+ overflowState = USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA;
+ SET_USI_TO_SEND_DATA( );
+ }
+ else
+ {
+ // the buffer is empty
+ SET_USI_TO_READ_ACK( ); // This might be neccessary sometimes see http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=805227#805227
+ SET_USI_TO_TWI_START_CONDITION_MODE( );
+ } // end if
+ break;
+
+ // set USI to sample reply from master
+ // next USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA
+ case USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA:
+ overflowState = USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA;
+ SET_USI_TO_READ_ACK( );
+ break;
+
+ // master-send / slave-receive: set USI to sample data from master, next
+ // USI_SLAVE_GET_DATA_AND_SEND_ACK
+ case USI_SLAVE_REQUEST_DATA:
+ overflowState = USI_SLAVE_GET_DATA_AND_SEND_ACK;
+ SET_USI_TO_READ_DATA( );
+
+ // with the SET_USI_TO_READ_DATA() macro call above, the USI has
+ // been setup to catch the next byte if the master sends one.
+ // while that's going on, look for a stop condition here which
+ // is when the SDA line goes high after the SCL line;
+
+ // wait until SCL goes high
+ while ( ! ( ( usi_pins = PIN_USI & USI_PINS_SCL_SDA ) & USI_PINS_SCL ) );
+
+ // if SDA line was high at SCL edge, then not a stop condition
+ if ( usi_pins & USI_PINS_SDA )
+ break;
+
+ // wait until SCL goes low or SDA goes high
+ while ( ( usi_pins = PIN_USI & USI_PINS_SCL_SDA ) == USI_PINS_SCL );
+
+ // if both SCL and SDA are high, then stop condition occurred
+ if ( usi_pins == USI_PINS_SCL_SDA )
+ {
+ USI_RECEIVE_CALLBACK();
+ SET_USI_TO_TWI_START_CONDITION_MODE( );
+ finished = 1;
+ }
+
+ break;
+
+ // copy data from USIDR and send ACK
+ // next USI_SLAVE_REQUEST_DATA
+ case USI_SLAVE_GET_DATA_AND_SEND_ACK:
+ // put data into buffer
+ // check buffer size
+ if ( rxCount < TWI_RX_BUFFER_SIZE )
+ {
+ rxBuf[ rxHead ] = USIDR;
+ rxHead = ( rxHead + 1 ) & TWI_RX_BUFFER_MASK;
+ rxCount++;
+ } else {
+ // overrun
+ // drop data
+ }
+ // next USI_SLAVE_REQUEST_DATA
+ overflowState = USI_SLAVE_REQUEST_DATA;
+ SET_USI_TO_SEND_ACK( );
+ break;
+
+ } // end switch
+
+ if (finished)
+ {
+ //no longer in valid i2c transaction
+ in_transaction = 0;
+ // restore the sleep enable bit
+ // note that this allows sleep -- it does not cause sleep
+ MCUCR |= sleep_enable_bit;
+ }
+
+ // no need to restore the SREG. The compiler does this automatically when using the
+ // ISR construct without modifying attributes.
+
+ // The compiler automatically uses an RETI instruction to return when using the
+ // ISR construct without modifying attributes.
+
+} // end ISR( USI_OVERFLOW_VECTOR )
diff --git a/digital-driver-board/firmware/TinyWireS/usiTwiSlave.h b/digital-driver-board/firmware/TinyWireS/usiTwiSlave.h
new file mode 100644
index 0000000..88b9445
--- /dev/null
+++ b/digital-driver-board/firmware/TinyWireS/usiTwiSlave.h
@@ -0,0 +1,96 @@
+/********************************************************************************
+
+Header file for the USI TWI Slave driver.
+
+Created by Donald R. Blake
+donblake at worldnet.att.net
+
+---------------------------------------------------------------------------------
+
+Created from Atmel source files for Application Note AVR312: Using the USI Module
+as an I2C slave.
+
+This program is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+---------------------------------------------------------------------------------
+
+Change Activity:
+
+ Date Description
+ ------ -------------
+ 15 Mar 2007 Created.
+
+********************************************************************************/
+
+
+
+#ifndef _USI_TWI_SLAVE_H_
+#define _USI_TWI_SLAVE_H_
+
+
+
+/********************************************************************************
+
+ includes
+
+********************************************************************************/
+
+#include <stdbool.h>
+#include <avr/sleep.h>
+
+
+
+/********************************************************************************
+
+ prototypes
+
+********************************************************************************/
+
+void usiTwiSlaveInit( uint8_t );
+void usiTwiTransmitByte( uint8_t );
+uint8_t usiTwiReceiveByte( void );
+bool usiTwiDataInTransmitBuffer(void);
+uint8_t usiTwiAmountDataInReceiveBuffer(void);
+// on_XXX handler pointers
+void (*usi_onRequestPtr)(void);
+void (*usi_onReceiverPtr)(uint8_t);
+
+
+/********************************************************************************
+
+ driver buffer definitions
+
+********************************************************************************/
+
+// permitted RX buffer sizes: 1, 2, 4, 8, 16, 32, 64, 128 or 256
+
+#ifndef TWI_RX_BUFFER_SIZE
+#define TWI_RX_BUFFER_SIZE ( 16 )
+#endif
+#define TWI_RX_BUFFER_MASK ( TWI_RX_BUFFER_SIZE - 1 )
+
+#if ( TWI_RX_BUFFER_SIZE & TWI_RX_BUFFER_MASK )
+# error TWI RX buffer size is not a power of 2
+#endif
+
+// permitted TX buffer sizes: 1, 2, 4, 8, 16, 32, 64, 128 or 256
+
+#ifndef TWI_TX_BUFFER_SIZE
+#define TWI_TX_BUFFER_SIZE ( 16 )
+#endif
+#define TWI_TX_BUFFER_MASK ( TWI_TX_BUFFER_SIZE - 1 )
+
+#if ( TWI_TX_BUFFER_SIZE & TWI_TX_BUFFER_MASK )
+# error TWI TX buffer size is not a power of 2
+#endif
+
+
+
+#endif // ifndef _USI_TWI_SLAVE_H_