diff --git a/cores/arduino/USBAPI.h b/cores/arduino/USBAPI.h index 479ced9c8..701a14f78 100644 --- a/cores/arduino/USBAPI.h +++ b/cores/arduino/USBAPI.h @@ -65,6 +65,8 @@ class USBDevice_ void detach(); // Serial port goes down too... void poll(); bool wakeupHost(); // returns false, when wakeup cannot be processed + + bool isSuspended(); }; extern USBDevice_ USBDevice; diff --git a/cores/arduino/USBCore.cpp b/cores/arduino/USBCore.cpp index 81f689d24..c0a4c7cc0 100644 --- a/cores/arduino/USBCore.cpp +++ b/cores/arduino/USBCore.cpp @@ -855,4 +855,10 @@ bool USBDevice_::wakeupHost() return false; } +bool USBDevice_::isSuspended() +{ + return (_usbSuspendState & (1 << SUSPI)); +} + + #endif /* if defined(USBCON) */ diff --git a/cores/arduino/WInterrupts.c b/cores/arduino/WInterrupts.c index cef1106e0..ac72dda44 100644 --- a/cores/arduino/WInterrupts.c +++ b/cores/arduino/WInterrupts.c @@ -65,7 +65,6 @@ static volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS] = { nothing, #endif }; -// volatile static voidFuncPtr twiIntFunc; void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) { if(interruptNum < EXTERNAL_NUM_INTERRUPTS) { @@ -274,11 +273,6 @@ void detachInterrupt(uint8_t interruptNum) { } } -/* -void attachInterruptTwi(void (*userFunc)(void) ) { - twiIntFunc = userFunc; -} -*/ #define IMPLEMENT_ISR(vect, interrupt) \ ISR(vect) { \ @@ -314,11 +308,3 @@ IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_2) #endif #endif - -/* -ISR(TWI_vect) { - if(twiIntFunc) - twiIntFunc(); -} -*/ - diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 58916ce93..a13481acc 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -15,7 +15,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - + Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts Modified 2017 by Chuck Todd (ctodd@cableone.net) to correct Unconfigured Slave Mode reboot */ @@ -86,24 +86,29 @@ void TwoWire::setClock(uint32_t clock) twi_setFrequency(clock); } +void TwoWire::setTimeoutInMillis(uint8_t timeout) +{ + twi_setTimeoutInMillis(timeout); +} + uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop) { if (isize > 0) { - // send internal address; this mode allows sending a repeated start to access - // some devices' internal registers. This function is executed by the hardware - // TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers) + // send internal address; this mode allows sending a repeated start to access + // some devices' internal registers. This function is executed by the hardware + // TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers) - beginTransmission(address); + beginTransmission(address); - // the maximum size of internal address is 3 bytes - if (isize > 3){ - isize = 3; - } + // the maximum size of internal address is 3 bytes + if (isize > 3){ + isize = 3; + } - // write internal register address - most significant byte first - while (isize-- > 0) - write((uint8_t)(iaddress >> (isize*8))); - endTransmission(false); + // write internal register address - most significant byte first + while (isize-- > 0) + write((uint8_t)(iaddress >> (isize*8))); + endTransmission(false); } // clamp to buffer length @@ -158,8 +163,8 @@ void TwoWire::beginTransmission(int address) // Originally, 'endTransmission' was an f(void) function. // It has been modified to take one parameter indicating // whether or not a STOP should be performed on the bus. -// Calling endTransmission(false) allows a sketch to -// perform a repeated start. +// Calling endTransmission(false) allows a sketch to +// perform a repeated start. // // WARNING: Nothing in the library keeps track of whether // the bus tenure has been properly ended with a STOP. It @@ -202,7 +207,7 @@ size_t TwoWire::write(uint8_t data) // put byte in tx buffer txBuffer[txBufferIndex] = data; ++txBufferIndex; - // update amount in buffer + // update amount in buffer txBufferLength = txBufferIndex; }else{ // in slave send mode @@ -244,7 +249,7 @@ int TwoWire::available(void) int TwoWire::read(void) { int value = -1; - + // get each successive byte on each call if(rxBufferIndex < rxBufferLength){ value = rxBuffer[rxBufferIndex]; @@ -260,7 +265,7 @@ int TwoWire::read(void) int TwoWire::peek(void) { int value = -1; - + if(rxBufferIndex < rxBufferLength){ value = rxBuffer[rxBufferIndex]; } @@ -289,7 +294,7 @@ void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes) // copy twi rx buffer into local read buffer // this enables new reads to happen in parallel for(uint8_t i = 0; i < numBytes; ++i){ - rxBuffer[i] = inBytes[i]; + rxBuffer[i] = inBytes[i]; } // set rx iterator vars rxBufferIndex = 0; diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 702f37d64..b3ce61a3a 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -54,6 +54,7 @@ class TwoWire : public Stream void begin(int); void end(); void setClock(uint32_t); + void setTimeoutInMillis(uint8_t); void beginTransmission(uint8_t); void beginTransmission(int); uint8_t endTransmission(void); diff --git a/libraries/Wire/src/utility/twi.c b/libraries/Wire/src/utility/twi.c index 171af7303..8a43e58f4 100644 --- a/libraries/Wire/src/utility/twi.c +++ b/libraries/Wire/src/utility/twi.c @@ -25,7 +25,7 @@ #include #include #include -#include "Arduino.h" // for digitalWrite +#include "Arduino.h" // for digitalWrite and millis #ifndef cbi #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) @@ -38,10 +38,38 @@ #include "pins_arduino.h" #include "twi.h" +#define BUSYWAIT_WITH_TIMEOUT_UNTIL(exit_condition, timedout_label) \ + do { \ + uint32_t startMillis = millis(); \ + while (!(exit_condition)) { \ + if ((twi_timeout_ms > 0) && (millis() - startMillis > twi_timeout_ms)) { \ + goto timedout_label; \ + } \ + } \ + } while (0) + +// Same as BUSYWAIT_WITH_TIMEOUT_UNTIL but intended to be used in code +// executed in interrupt service routines where millis() cannot be used. +// https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/: +// "millis() relies on interrupts to count, so it will never increment inside an ISR." +// TODO: Document calculations behind the 25000 upper counter limit. +// TODO: Make the upper counter limit reflect the twi_timeout_ms value. +#define IRS_BUSYWAIT_WITH_TIMEOUT_UNTIL(exit_condition, timedout_label) \ + do { \ + uint32_t counter = 0; \ + while (!(exit_condition)) { \ + counter++; \ + if ((twi_timeout_ms > 0) && (counter >= 25000)) { \ + goto timedout_label; \ + } \ + } \ + } while (0) + static volatile uint8_t twi_state; static volatile uint8_t twi_slarw; static volatile uint8_t twi_sendStop; // should the transaction end with a stop static volatile uint8_t twi_inRepStart; // in the middle of a repeated start +static volatile uint8_t twi_timeout_ms = 0; static void (*twi_onSlaveTransmit)(void); static void (*twi_onSlaveReceive)(uint8_t*, int); @@ -59,7 +87,7 @@ static volatile uint8_t twi_rxBufferIndex; static volatile uint8_t twi_error; -/* +/* * Function twi_init * Desc readys twi pins and sets twi bitrate * Input none @@ -71,7 +99,7 @@ void twi_init(void) twi_state = TWI_READY; twi_sendStop = true; // default value twi_inRepStart = false; - + // activate internal pullups for twi. digitalWrite(SDA, 1); digitalWrite(SCL, 1); @@ -90,7 +118,7 @@ void twi_init(void) TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA); } -/* +/* * Function twi_disable * Desc disables twi pins * Input none @@ -106,7 +134,7 @@ void twi_disable(void) digitalWrite(SCL, 0); } -/* +/* * Function twi_slaveInit * Desc sets slave address and enables interrupt * Input none @@ -118,7 +146,7 @@ void twi_setAddress(uint8_t address) TWAR = address << 1; } -/* +/* * Function twi_setClock * Desc sets twi bit rate * Input Clock Frequency @@ -127,14 +155,14 @@ void twi_setAddress(uint8_t address) void twi_setFrequency(uint32_t frequency) { TWBR = ((F_CPU / frequency) - 16) / 2; - + /* twi bit rate formula from atmega128 manual pg 204 SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR)) note: TWBR should be 10 or higher for master mode It is 72 for a 16mhz Wiring board with 100kHz TWI */ } -/* +/* * Function twi_readFrom * Desc attempts to become twi bus master and read a * series of bytes from a device on the bus @@ -154,9 +182,8 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen } // wait until twi is ready, become master receiver - while(TWI_READY != twi_state){ - continue; - } + BUSYWAIT_WITH_TIMEOUT_UNTIL(twi_state == TWI_READY, waiting_timedout); + twi_state = TWI_MRX; twi_sendStop = sendStop; // reset error state (0xFF.. no error occured) @@ -166,7 +193,7 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen twi_masterBufferIndex = 0; twi_masterBufferLength = length-1; // This is not intuitive, read on... // On receive, the previously configured ACK/NACK setting is transmitted in - // response to the received byte before the interrupt is signalled. + // response to the received byte before the interrupt is signalled. // Therefor we must actually set NACK when the _next_ to last byte is // received, causing that NACK to be sent in response to receiving the last // expected byte of data. @@ -180,7 +207,7 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen // (@@@ we hope), and the TWI statemachine is just waiting for the address byte. // We need to remove ourselves from the repeated start state before we enable interrupts, // since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning - // up. Also, don't enable the START interrupt. There may be one pending from the + // up. Also, don't enable the START interrupt. There may be one pending from the // repeated start that we sent ourselves, and that would really confuse things. twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR do { @@ -193,9 +220,7 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); // wait for read operation to complete - while(TWI_MRX == twi_state){ - continue; - } + BUSYWAIT_WITH_TIMEOUT_UNTIL(twi_state != TWI_MRX, waiting_timedout); if (twi_masterBufferIndex < length) length = twi_masterBufferIndex; @@ -204,11 +229,16 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen for(i = 0; i < length; ++i){ data[i] = twi_masterBuffer[i]; } - + return length; + +waiting_timedout: + twi_disable(); + twi_init(); + return 0; } -/* +/* * Function twi_writeTo * Desc attempts to become twi bus master and write a * series of bytes to a device on the bus @@ -233,9 +263,8 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait } // wait until twi is ready, become master transmitter - while(TWI_READY != twi_state){ - continue; - } + BUSYWAIT_WITH_TIMEOUT_UNTIL(twi_state == TWI_READY, waiting_timedout); + twi_state = TWI_MTX; twi_sendStop = sendStop; // reset error state (0xFF.. no error occured) @@ -244,16 +273,16 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait // initialize buffer iteration vars twi_masterBufferIndex = 0; twi_masterBufferLength = length; - + // copy data to twi buffer for(i = 0; i < length; ++i){ twi_masterBuffer[i] = data[i]; } - + // build sla+w, slave device address + w bit twi_slarw = TW_WRITE; twi_slarw |= address << 1; - + // if we're in a repeated start, then we've already sent the START // in the ISR. Don't do it again. // @@ -262,11 +291,11 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait // (@@@ we hope), and the TWI statemachine is just waiting for the address byte. // We need to remove ourselves from the repeated start state before we enable interrupts, // since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning - // up. Also, don't enable the START interrupt. There may be one pending from the + // up. Also, don't enable the START interrupt. There may be one pending from the // repeated start that we sent outselves, and that would really confuse things. twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR do { - TWDR = twi_slarw; + TWDR = twi_slarw; } while(TWCR & _BV(TWWC)); TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START } @@ -275,10 +304,10 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // enable INTs // wait for write operation to complete - while(wait && (TWI_MTX == twi_state)){ - continue; + if (wait) { + BUSYWAIT_WITH_TIMEOUT_UNTIL(twi_state != TWI_MTX, waiting_timedout); } - + if (twi_error == 0xFF) return 0; // success else if (twi_error == TW_MT_SLA_NACK) @@ -287,9 +316,14 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait return 3; // error: data send, nack received else return 4; // other twi error + +waiting_timedout: + twi_disable(); + twi_init(); + return 4; } -/* +/* * Function twi_transmit * Desc fills slave tx buffer with data * must be called in slave tx event callback @@ -307,22 +341,22 @@ uint8_t twi_transmit(const uint8_t* data, uint8_t length) if(TWI_BUFFER_LENGTH < (twi_txBufferLength+length)){ return 1; } - + // ensure we are currently a slave transmitter if(TWI_STX != twi_state){ return 2; } - + // set length and copy data into tx buffer for(i = 0; i < length; ++i){ twi_txBuffer[twi_txBufferLength+i] = data[i]; } twi_txBufferLength += length; - + return 0; } -/* +/* * Function twi_attachSlaveRxEvent * Desc sets function called before a slave read operation * Input function: callback function to use @@ -333,7 +367,7 @@ void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) ) twi_onSlaveReceive = function; } -/* +/* * Function twi_attachSlaveTxEvent * Desc sets function called before a slave write operation * Input function: callback function to use @@ -344,7 +378,7 @@ void twi_attachSlaveTxEvent( void (*function)(void) ) twi_onSlaveTransmit = function; } -/* +/* * Function twi_reply * Desc sends byte or readys receive line * Input ack: byte indicating to ack or to nack @@ -356,11 +390,11 @@ void twi_reply(uint8_t ack) if(ack){ TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); }else{ - TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); } } -/* +/* * Function twi_stop * Desc relinquishes bus master status * Input none @@ -373,15 +407,17 @@ void twi_stop(void) // wait for stop condition to be exectued on bus // TWINT is not set after a stop condition! - while(TWCR & _BV(TWSTO)){ - continue; - } + IRS_BUSYWAIT_WITH_TIMEOUT_UNTIL((TWCR & _BV(TWSTO)) == 0, waiting_timedout); // update twi state twi_state = TWI_READY; + +waiting_timedout: + twi_disable(); + twi_init(); } -/* +/* * Function twi_releaseBus * Desc releases bus control * Input none @@ -396,6 +432,11 @@ void twi_releaseBus(void) twi_state = TWI_READY; } +void twi_setTimeoutInMillis(uint8_t timeout) +{ + twi_timeout_ms = timeout; +} + ISR(TWI_vect) { switch(TW_STATUS){ @@ -410,22 +451,22 @@ ISR(TWI_vect) // Master Transmitter case TW_MT_SLA_ACK: // slave receiver acked address case TW_MT_DATA_ACK: // slave receiver acked data - // if there is data to send, send it, otherwise stop + // if there is data to send, send it, otherwise stop if(twi_masterBufferIndex < twi_masterBufferLength){ // copy data to output register and ack TWDR = twi_masterBuffer[twi_masterBufferIndex++]; twi_reply(1); }else{ - if (twi_sendStop) + if (twi_sendStop) twi_stop(); - else { - twi_inRepStart = true; // we're gonna send the START - // don't enable the interrupt. We'll generate the start, but we - // avoid handling the interrupt until we're in the next transaction, - // at the point where we would normally issue the start. - TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ; - twi_state = TWI_READY; - } + else { + twi_inRepStart = true; // we're gonna send the START + // don't enable the interrupt. We'll generate the start, but we + // avoid handling the interrupt until we're in the next transaction, + // at the point where we would normally issue the start. + TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ; + twi_state = TWI_READY; + } } break; case TW_MT_SLA_NACK: // address sent, nack received @@ -456,17 +497,17 @@ ISR(TWI_vect) case TW_MR_DATA_NACK: // data received, nack sent // put final byte into buffer twi_masterBuffer[twi_masterBufferIndex++] = TWDR; - if (twi_sendStop) + if (twi_sendStop) twi_stop(); - else { - twi_inRepStart = true; // we're gonna send the START - // don't enable the interrupt. We'll generate the start, but we - // avoid handling the interrupt until we're in the next transaction, - // at the point where we would normally issue the start. - TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ; - twi_state = TWI_READY; - } - break; + else { + twi_inRepStart = true; // we're gonna send the START + // don't enable the interrupt. We'll generate the start, but we + // avoid handling the interrupt until we're in the next transaction, + // at the point where we would normally issue the start. + TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ; + twi_state = TWI_READY; + } + break; case TW_MR_SLA_NACK: // address sent, nack received twi_stop(); break; @@ -512,7 +553,7 @@ ISR(TWI_vect) // nack back at master twi_reply(0); break; - + // Slave Transmitter case TW_ST_SLA_ACK: // addressed, returned ack case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack @@ -541,7 +582,7 @@ ISR(TWI_vect) twi_reply(0); } break; - case TW_ST_DATA_NACK: // received nack, we are done + case TW_ST_DATA_NACK: // received nack, we are done case TW_ST_LAST_DATA: // received ack, but we are done already! // ack future responses twi_reply(1); diff --git a/libraries/Wire/src/utility/twi.h b/libraries/Wire/src/utility/twi.h index d27325ea7..d52b5d8a8 100644 --- a/libraries/Wire/src/utility/twi.h +++ b/libraries/Wire/src/utility/twi.h @@ -50,6 +50,7 @@ void twi_reply(uint8_t); void twi_stop(void); void twi_releaseBus(void); + void twi_setTimeoutInMillis(uint8_t); #endif diff --git a/programmers.txt b/programmers.txt index d0d2cc175..69ddf692c 100644 --- a/programmers.txt +++ b/programmers.txt @@ -43,13 +43,22 @@ parallel.program.extra_params=-F arduinoasisp.name=Arduino as ISP arduinoasisp.communication=serial -arduinoasisp.protocol=arduino +arduinoasisp.protocol=stk500v1 arduinoasisp.speed=19200 -arduinoasisp.program.protocol=arduino +arduinoasisp.program.protocol=stk500v1 arduinoasisp.program.speed=19200 arduinoasisp.program.tool=avrdude arduinoasisp.program.extra_params=-P{serial.port} -b{program.speed} +arduinoasispatmega32u4.name=Arduino as ISP (ATmega32U4) +arduinoasispatmega32u4.communication=serial +arduinoasispatmega32u4.protocol=arduino +arduinoasispatmega32u4.speed=19200 +arduinoasispatmega32u4.program.protocol=arduino +arduinoasispatmega32u4.program.speed=19200 +arduinoasispatmega32u4.program.tool=avrdude +arduinoasispatmega32u4.program.extra_params=-P{serial.port} -b{program.speed} + usbGemma.name=Arduino Gemma usbGemma.protocol=arduinogemma usbGemma.program.tool=avrdude