diff options
Diffstat (limited to 'digital-driver-board/firmware/TinyWireS')
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 Binary files differnew file mode 100644 index 0000000..dc8f460 --- /dev/null +++ b/digital-driver-board/firmware/TinyWireS/avr_usi_i2c_master.pdf diff --git a/digital-driver-board/firmware/TinyWireS/avr_usi_i2c_slave.pdf b/digital-driver-board/firmware/TinyWireS/avr_usi_i2c_slave.pdf Binary files differnew file mode 100644 index 0000000..e4704c7 --- /dev/null +++ b/digital-driver-board/firmware/TinyWireS/avr_usi_i2c_slave.pdf 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_  | 
