From cbc6821c4e1e10579e12fe0bc92ff14424140dd0 Mon Sep 17 00:00:00 2001 From: rshartog Date: Fri, 19 Feb 2016 22:57:44 -0500 Subject: [PATCH 1/4] Fully Interrupt Driven usiTwiSlave Library These changes make the usiTwiSlave library fully interrupt driven. The fundamental change is to explicitly determine when the I2C master is done sending data [ in a Wire.beginTransmission() / Wire.write() / Wire.endTransmission() set ] within the Interrupt Service Routine (ISR) and then calling the USI_RECEIVE_CALLBACK() function directly. The TinyWireS_Stress_Master and TinyWires_Stress_Slave examples have been modified. The most interesting change is that the slave program now sleeps the Tiny85 CPU which was not possible with the previous version of the usiTwiSlave library. The TinyWireS_stop_check() call is no longer necessary in the main loop because the receive callback routine is called directly from the ISR. The TinyWireS_stop_check() function is still in the TinyWireS library so that prior code that uses the library will still compile and function, however, the TinyWireS_stop_check() function is now empty. Similarly, the tws_delay() is no longer necessary because its main purpose was to call TinyWireS_stop_check() during a delay operation. Regular delay() calls can be used in the main loop. The tws_delay() function is still in the TinyWireS library so that prior code that uses the library will still compile and function. It is intact, but its call the TinyWireS_stop_check calls an empty function. The library changes and the TinyWireS_Stress_Master/_Slave programs have been tested on Uno R3, Mega 2560, Leonardo, and MonkeyBoard ESP8266 E12 boards. --- TinyWireS/TinyWireS.cpp | 24 +- .../TinyWireS_Stress_Master.ino | 60 ++++- .../TinyWireS_Stress_Slave.ino | 57 ++++- TinyWireS/usiTwiSlave.c | 212 ++++++++++++------ TinyWireS/usiTwiSlave.h | 3 +- 5 files changed, 254 insertions(+), 102 deletions(-) diff --git a/TinyWireS/TinyWireS.cpp b/TinyWireS/TinyWireS.cpp index 88fbcdb..783e8e7 100644 --- a/TinyWireS/TinyWireS.cpp +++ b/TinyWireS/TinyWireS.cpp @@ -59,28 +59,18 @@ 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() { - if (!usi_onReceiverPtr) - { - // no onReceive callback, nothing to do... - return; - } - if (!(USISR & ( 1 << USIPF ))) - { - // Stop not detected - return; - } - uint8_t amount = usiTwiAmountDataInReceiveBuffer(); - if (amount == 0) - { - // no data in buffer - return; - } - usi_onReceiverPtr(amount); + // 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(); diff --git a/TinyWireS/examples/TinyWireS_Stress_Master/TinyWireS_Stress_Master.ino b/TinyWireS/examples/TinyWireS_Stress_Master/TinyWireS_Stress_Master.ino index 75a5d29..d50adb7 100644 --- a/TinyWireS/examples/TinyWireS_Stress_Master/TinyWireS_Stress_Master.ino +++ b/TinyWireS/examples/TinyWireS_Stress_Master/TinyWireS_Stress_Master.ino @@ -29,7 +29,22 @@ #define I2C_SLAVE_ADDR 0x26 // i2c slave address (38, 0x26) -#define SLAVE_RESET_PIN 2 +#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 @@ -39,10 +54,13 @@ char c_buf[64]; // for creating messages void setup() { // set pin modes - pinMode(SLAVE_RESET_PIN,OUTPUT); + 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(115200); + Serial.begin(9600); // print some useful pinnout info for the Arduino //Serial.println(String("SCL:")+String(SCL)+String(", SDA:")+String(SDA)); @@ -50,11 +68,18 @@ void setup() // 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); @@ -74,11 +99,11 @@ void loop() count++; // compute random number of bytes for this pass - rand_byte_count = random(12) + 1; + 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 ( tmphead == txTail );". + // 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 @@ -87,8 +112,12 @@ void loop() Wire.write(out_rand[i] = random(256)); Wire.endTransmission(); - //delay (10); // optional delay if required by slave (like sample ADC) - + // 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++) @@ -101,9 +130,22 @@ void loop() // 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++; + 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 + // 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); diff --git a/TinyWireS/examples/TinyWireS_Stress_Slave/TinyWireS_Stress_Slave.ino b/TinyWireS/examples/TinyWireS_Stress_Slave/TinyWireS_Stress_Slave.ino index be9f49f..c7fbe41 100644 --- a/TinyWireS/examples/TinyWireS_Stress_Slave/TinyWireS_Stress_Slave.ino +++ b/TinyWireS/examples/TinyWireS_Stress_Slave/TinyWireS_Stress_Slave.ino @@ -28,30 +28,42 @@ // Please see credits and usage for usiTwiSlave and TinyWireS in the .h files of // those libraries. +#include +#include #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; @@ -61,20 +73,20 @@ void requestEvent() TinyWireS.send(master_data[i]); // corrupt the byte values in the data buffer - // so that subsequent call won't match + // 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 dont' make it zero + // 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 set by that call. + // 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*. + // 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. @@ -86,6 +98,8 @@ void requestEvent() 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 @@ -94,11 +108,36 @@ void setup() // 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() { - // This needs to be here - TinyWireS_stop_check(); - // otherwise empty 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/TinyWireS/usiTwiSlave.c b/TinyWireS/usiTwiSlave.c index e6bd595..5416ac3 100644 --- a/TinyWireS/usiTwiSlave.c +++ b/TinyWireS/usiTwiSlave.c @@ -30,8 +30,9 @@ Change Activity: 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 2015 Minor change to allow mutli-byte requestFrom() from master. - 10 Feb 2015 Simplied RX/TX buffer code and allowed use of full buffer. + 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. ********************************************************************************/ @@ -153,7 +154,10 @@ Change Activity: # 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 ) /******************************************************************************** @@ -163,7 +167,7 @@ Change Activity: #define SET_USI_TO_SEND_ACK( ) \ { \ - /* prepare ACK */ \ + /* prepare ACK, ack is a zero */ \ USIDR = 0; \ /* set SDA as output */ \ DDR_USI |= ( 1 << PORT_USI_SDA ); \ @@ -245,18 +249,8 @@ Change Activity: } \ } -#define ONSTOP_USI_RECEIVE_CALLBACK() \ -{ \ - if (USISR & ( 1 << USIPF )) \ - { \ - USI_RECEIVE_CALLBACK(); \ - } \ -} - - #define USI_REQUEST_CALLBACK() \ { \ - USI_RECEIVE_CALLBACK(); \ if(usi_onRequestPtr) usi_onRequestPtr(); \ } @@ -285,6 +279,8 @@ typedef enum ********************************************************************************/ static uint8_t slaveAddress; +static uint8_t sleep_enable_bit; +static uint8_t in_transaction; static volatile overflowState_t overflowState; @@ -306,15 +302,9 @@ static volatile uint8_t txCount; ********************************************************************************/ - - // flushes the TWI buffers -static -void -flushTwiBuffers( - void -) +static void flushTwiBuffers( void ) { rxTail = 0; rxHead = 0; @@ -332,16 +322,11 @@ flushTwiBuffers( ********************************************************************************/ - - // initialise USI for TWI slave mode -void -usiTwiSlaveInit( - uint8_t ownAddress -) +void usiTwiSlaveInit( uint8_t ownAddress ) { - + // initialize the TX and RX buffers to empty flushTwiBuffers( ); slaveAddress = ownAddress; @@ -380,6 +365,10 @@ usiTwiSlaveInit( 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 @@ -394,14 +383,9 @@ bool usiTwiDataInTransmitBuffer(void) // put data in the transmission buffer, wait if buffer is full -void -usiTwiTransmitByte( - uint8_t data -) +void usiTwiTransmitByte( uint8_t data ) { - uint8_t tmphead; - // wait for free space in buffer while ( txCount == TWI_TX_BUFFER_SIZE) ; @@ -413,15 +397,9 @@ usiTwiTransmitByte( } // end usiTwiTransmitByte - - - // return a byte from the receive buffer, wait if buffer is empty -uint8_t -usiTwiReceiveByte( - void -) +uint8_t usiTwiReceiveByte( void ) { uint8_t rtn_byte; @@ -439,14 +417,11 @@ usiTwiReceiveByte( } // end usiTwiReceiveByte - uint8_t usiTwiAmountDataInReceiveBuffer(void) { return rxCount; } - - /******************************************************************************** @@ -456,15 +431,34 @@ uint8_t usiTwiAmountDataInReceiveBuffer(void) 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 ); - /* - // This triggers on second write, but claims to the callback there is only *one* byte in buffer - ONSTOP_USI_RECEIVE_CALLBACK(); - */ - /* - // This triggers on second write, but claims to the callback there is only *one* byte in buffer - USI_RECEIVE_CALLBACK(); - */ + // 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; @@ -472,20 +466,19 @@ ISR( USI_START_VECTOR ) // 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 his high - ( PIN_USI & ( 1 << PIN_USI_SCL ) ) && - // and SDA is low - !( ( PIN_USI & ( 1 << PIN_USI_SDA ) ) ) - ); + // while SCL is high and SDA is low + while ( ( usi_pins = PIN_USI & USI_PINS_SCL_SDA ) == USI_PINS_SCL ); - if ( !( PIN_USI & ( 1 << PIN_USI_SDA ) ) ) + // if SDA line was low at SCL edge, then start condition occurred + if ( !( usi_pins & USI_PINS_SDA ) ) { // a Stop Condition did not occur @@ -502,6 +495,9 @@ ISR( USI_START_VECTOR ) ( 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 @@ -521,6 +517,11 @@ ISR( USI_START_VECTOR ) // 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 = @@ -531,6 +532,11 @@ ISR( USI_START_VECTOR ) // 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 ) @@ -548,6 +554,36 @@ 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 ) { @@ -559,29 +595,36 @@ ISR( USI_OVERFLOW_VECTOR ) { if ( USIDR & 0x01 ) { + // load up the tx buffer with the data to send to the master USI_REQUEST_CALLBACK(); + 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 write data mode: check reply and goto USI_SLAVE_SEND_DATA if OK, + // 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: if ( USIDR ) { // if NACK, the master does not want more data SET_USI_TO_TWI_START_CONDITION_MODE( ); - return; + finished = 1; + break; } // from here we just drop straight into USI_SLAVE_SEND_DATA if the // master sent an ACK @@ -595,16 +638,16 @@ ISR( USI_OVERFLOW_VECTOR ) 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( ); - return; } // end if - overflowState = USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA; - SET_USI_TO_SEND_DATA( ); break; // set USI to sample reply from master @@ -614,11 +657,35 @@ ISR( USI_OVERFLOW_VECTOR ) SET_USI_TO_READ_ACK( ); break; - // Master read data mode: set USI to sample data from master, next + // 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 @@ -642,4 +709,19 @@ ISR( USI_OVERFLOW_VECTOR ) } // 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/TinyWireS/usiTwiSlave.h b/TinyWireS/usiTwiSlave.h index 8ffdda2..88b9445 100644 --- a/TinyWireS/usiTwiSlave.h +++ b/TinyWireS/usiTwiSlave.h @@ -43,6 +43,7 @@ Change Activity: ********************************************************************************/ #include +#include @@ -55,8 +56,6 @@ Change Activity: void usiTwiSlaveInit( uint8_t ); void usiTwiTransmitByte( uint8_t ); uint8_t usiTwiReceiveByte( void ); -bool usiTwiDataInReceiveBuffer( void ); -void (*_onTwiDataRequest)(void); bool usiTwiDataInTransmitBuffer(void); uint8_t usiTwiAmountDataInReceiveBuffer(void); // on_XXX handler pointers From c1c0c03b72b7457dd7fe0d6d772aebacec799441 Mon Sep 17 00:00:00 2001 From: Jasper Horn Date: Sat, 23 Dec 2017 14:22:37 +0100 Subject: [PATCH 2/4] Fix repeated start condition Execute receive callback on repeated start in addition to doing so on a stop. --- TinyWireS/usiTwiSlave.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/TinyWireS/usiTwiSlave.c b/TinyWireS/usiTwiSlave.c index 5416ac3..b25f42c 100644 --- a/TinyWireS/usiTwiSlave.c +++ b/TinyWireS/usiTwiSlave.c @@ -480,8 +480,13 @@ ISR( USI_START_VECTOR ) // 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 From fd520f7210a8e2dbbd114f1b4ec93d333cc4d2eb Mon Sep 17 00:00:00 2001 From: Jasper Horn Date: Sat, 23 Dec 2017 14:29:39 +0100 Subject: [PATCH 3/4] Add entry to changelog --- TinyWireS/usiTwiSlave.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TinyWireS/usiTwiSlave.c b/TinyWireS/usiTwiSlave.c index 90a7d34..19365cc 100644 --- a/TinyWireS/usiTwiSlave.c +++ b/TinyWireS/usiTwiSlave.c @@ -34,6 +34,8 @@ Change Activity: 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) ********************************************************************************/ From f0eb04fb699615d83fac3ead610cade7608d9b9e Mon Sep 17 00:00:00 2001 From: Jasper Horn Date: Sat, 23 Dec 2017 14:53:31 +0100 Subject: [PATCH 4/4] Execute request call for each byte requested This reverses the change done in a503849. That change changed the API (even if it wasn't in a way visible through the methods) and failed to match the way the library was intended to be used. This once again makes it possible to send bodies larger than the buffer. --- TinyWireS/usiTwiSlave.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TinyWireS/usiTwiSlave.c b/TinyWireS/usiTwiSlave.c index ecb0365..a11bfba 100644 --- a/TinyWireS/usiTwiSlave.c +++ b/TinyWireS/usiTwiSlave.c @@ -573,7 +573,6 @@ ISR( USI_OVERFLOW_VECTOR ) { if ( USIDR & 0x01 ) { - USI_REQUEST_CALLBACK(); overflowState = USI_SLAVE_SEND_DATA; } else @@ -591,6 +590,9 @@ ISR( USI_OVERFLOW_VECTOR ) // Master write data mode: 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