From 3fb87a584deec38481cfbf2317167de8ee3e73ab Mon Sep 17 00:00:00 2001 From: edbaafi Date: Thu, 10 Nov 2011 22:33:51 -0500 Subject: [PATCH 1/8] Update libraries/Wire/Wire.h --- libraries/Wire/Wire.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 23d466c1989..c0fc1bc5f96 100755 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -40,8 +40,9 @@ class TwoWire : public Stream static uint8_t transmitting; static void (*user_onRequest)(void); static void (*user_onReceive)(int); + static void (*user_onGcallReceive)(int); static void onRequestService(void); - static void onReceiveService(uint8_t*, int); + static void onReceiveService(uint8_t*, int,uint8_t); public: TwoWire(); void begin(); @@ -59,6 +60,7 @@ class TwoWire : public Stream virtual int peek(void); virtual void flush(void); void onReceive( void (*)(int) ); + void onReceive( void (*)(int), void (*)(int) ); void onRequest( void (*)(void) ); using Print::write; From 51a4b2935b949cbd43a11e2a2a3dc959ae59ce5d Mon Sep 17 00:00:00 2001 From: edbaafi Date: Thu, 10 Nov 2011 22:52:06 -0500 Subject: [PATCH 2/8] Added TWI general call (gcall) callback. Overloaded onReceive with second function pointer for gcall callback. --- libraries/Wire/Wire.cpp | 58 ++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index d83f4789e24..077402179dc 100755 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -40,6 +40,7 @@ uint8_t TwoWire::txBufferLength = 0; uint8_t TwoWire::transmitting = 0; void (*TwoWire::user_onRequest)(void); void (*TwoWire::user_onReceive)(int); +void (*TwoWire::user_onGcallReceive)(int); // Constructors //////////////////////////////////////////////////////////////// @@ -208,12 +209,22 @@ void TwoWire::flush(void) } // behind the scenes function that is called when data is received -void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes) +void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes, uint8_t isGeneralCall) { - // don't bother if user hasn't registered a callback - if(!user_onReceive){ - return; - } + //check if general call or normal receive + if(isGeneralCall){ + // don't bother if user hasn't registered a general call callback + if(!user_onGcallReceive){ + return; + } + } + else{ + // don't bother if user hasn't registered a callback + if(!user_onReceive){ + return; + } + } + // don't bother if rx buffer is in use by a master requestFrom() op // i know this drops data, but it allows for slight stupidity // meaning, they may not have read all the master requestFrom() data yet @@ -228,8 +239,18 @@ void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes) // set rx iterator vars rxBufferIndex = 0; rxBufferLength = numBytes; - // alert user program - user_onReceive(numBytes); + + if(isGeneralCall){ + // alert user's general call receive callback + user_onGcallReceive(numBytes); + } + else{ + // alert user's receive callback + user_onReceive(numBytes); + } + + + } // behind the scenes function that is called when data is requested @@ -247,16 +268,27 @@ void TwoWire::onRequestService(void) user_onRequest(); } -// sets function called on slave write -void TwoWire::onReceive( void (*function)(int) ) +// sets function called when slave receives data from master at slave's address +void TwoWire::onReceive( void (*recFunction)(int) ) +{ + user_onReceive = recFunction; +} + +// sets function called when slave receives data from master at slaves address +// and another function called when slave receives data from a general call +void TwoWire::onReceive( void (*recFunction)(int), void (*gcallRecFunction)(int) ) { - user_onReceive = function; + user_onReceive = recFunction; + user_onGcallReceive = gcallRecFunction; + + //enable general call addressing + twi_enableGenCall(); } -// sets function called on slave read -void TwoWire::onRequest( void (*function)(void) ) +// sets function called when master requests data from slave +void TwoWire::onRequest( void (*reqFunction)(void) ) { - user_onRequest = function; + user_onRequest = reqFunction; } // Preinstantiate Objects ////////////////////////////////////////////////////// From 28d531e063d83abed0596a8427c5846c51d466e6 Mon Sep 17 00:00:00 2001 From: edbaafi Date: Thu, 10 Nov 2011 23:04:08 -0500 Subject: [PATCH 3/8] Added twi_enableGenCall. Added flag for gcall receive and pass to onSlaveReceive. --- libraries/Wire/utility/twi.c | 43 +++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/libraries/Wire/utility/twi.c b/libraries/Wire/utility/twi.c index d80114b6e77..38280fec833 100644 --- a/libraries/Wire/utility/twi.c +++ b/libraries/Wire/utility/twi.c @@ -40,7 +40,7 @@ static volatile uint8_t twi_state; static uint8_t twi_slarw; static void (*twi_onSlaveTransmit)(void); -static void (*twi_onSlaveReceive)(uint8_t*, int); +static void (*twi_onSlaveReceive)(uint8_t*, int, uint8_t); static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH]; static volatile uint8_t twi_masterBufferIndex; @@ -54,6 +54,8 @@ static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH]; static volatile uint8_t twi_rxBufferIndex; static volatile uint8_t twi_error; +static volatile uint8_t twi_gcall_data; + /* * Function twi_init @@ -82,20 +84,36 @@ void twi_init(void) // enable twi module, acks, and twi interrupt TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA); + } /* - * Function twi_slaveInit - * Desc sets slave address and enables interrupt - * Input none + * Function twi_setAddress + * Desc sets slave address + * Input address: 7bit i2c device address * Output none */ void twi_setAddress(uint8_t address) { - // set twi slave address (skip over TWGCE bit) - TWAR = address << 1; + // set twi slave address and leave TWGCE bit (LSB) + TWAR = (address << TWA0) | ( TWAR & (1 << TWGCE) ) ; } + +/* + * Function twi_enableGenCall + * Desc enables general call address for slave + * Input none + * Output none + */ +void twi_enableGenCall(void) +{ + + // set TWGCE bit (LSB) and leave rest of address + TWAR |= 1 << TWGCE; +} + + /* * Function twi_readFrom * Desc attempts to become twi bus master and read a @@ -255,7 +273,7 @@ uint8_t twi_transmit(const uint8_t* data, uint8_t length) * Input function: callback function to use * Output none */ -void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) ) +void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int, uint8_t) ) { twi_onSlaveReceive = function; } @@ -388,10 +406,15 @@ SIGNAL(TWI_vect) twi_state = TWI_SRX; // indicate that rx buffer can be overwritten and ack twi_rxBufferIndex = 0; - twi_reply(1); + + //indicate that we have not received any gcall data + twi_gcall_data = 0; + + twi_reply(1); break; - case TW_SR_DATA_ACK: // data received, returned ack case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack + twi_gcall_data = 1; //indicate that we received some gcall data - need to send flag to receiver + case TW_SR_DATA_ACK: // data received, returned ack // if there is still room in the rx buffer if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ // put byte in buffer and ack @@ -410,7 +433,7 @@ SIGNAL(TWI_vect) // sends ack and stops interface for clock stretching twi_stop(); // callback to user defined callback - twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex); + twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex,twi_gcall_data); // since we submit rx buffer to "wire" library, we can reset it twi_rxBufferIndex = 0; // ack future responses and leave slave receiver state From 26ef10db1f5194f66cf2e276adbec246a5762b4c Mon Sep 17 00:00:00 2001 From: edbaafi Date: Thu, 10 Nov 2011 23:05:12 -0500 Subject: [PATCH 4/8] Added twi_enableGenCall. Added flag for gcall receive to pass to caller through onSlaveReceive. --- libraries/Wire/utility/twi.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/Wire/utility/twi.h b/libraries/Wire/utility/twi.h index 831b9282ab4..125df256046 100755 --- a/libraries/Wire/utility/twi.h +++ b/libraries/Wire/utility/twi.h @@ -40,10 +40,11 @@ void twi_init(void); void twi_setAddress(uint8_t); + void twi_enableGenCall(void); uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t); uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t); uint8_t twi_transmit(const uint8_t*, uint8_t); - void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) ); + void twi_attachSlaveRxEvent( void (*)(uint8_t*, int, uint8_t) ); void twi_attachSlaveTxEvent( void (*)(void) ); void twi_reply(uint8_t); void twi_stop(void); From 42d38887984eb2669b2674b29bc1c6be74e0ec9c Mon Sep 17 00:00:00 2001 From: Modkit Date: Fri, 11 Nov 2011 01:58:58 -0500 Subject: [PATCH 5/8] Added TWI General Call Examples --- .../general_call_master.ino | 69 +++++++++++++++ .../general_call_slave/general_call_slave.ino | 84 +++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 libraries/Wire/examples/general_call_master/general_call_master.ino create mode 100644 libraries/Wire/examples/general_call_slave/general_call_slave.ino diff --git a/libraries/Wire/examples/general_call_master/general_call_master.ino b/libraries/Wire/examples/general_call_master/general_call_master.ino new file mode 100644 index 00000000000..4f28446e21f --- /dev/null +++ b/libraries/Wire/examples/general_call_master/general_call_master.ino @@ -0,0 +1,69 @@ +/* + * Author: Ed Baafi + * Date: 11/11/11 + * + * This example demonstrates the TWI (I2C) "General Call" capabilities. + * + * General call is like a broadcast that can be received by many listeners at the same same. This + * is useful when you don't know the address of the device you want to talk to or it can be used as + * a sort of interrupt over the TWI (I2C). For more info on General Call, please see the I2C specs. + * + * The Wire library now supports General Call by adding a second receive callback to Wire.onReceive() + * This second callback will be called whenever a message is received over the general call address. + * + * This code is for the master device. Instructions are in comments for the slave device code. + * + */ + +#include + +//button to send a general call message (connect button between pin10 and ground) +#define GENERAL_CALL_ADDR_BUTTON 10 +//button to send a normal i2c message (connect button between pin 9 and ground) +#define SLAVE_ADDR_BUTTON 9 + +#define SLAVE_ADDRESS 0x9 +#define GENERAL_CALL_ADDR 0x0 + +//store state of slave message button +boolean slave_btn_state = HIGH; +//store state of general call message button +boolean general_call_btn_state = HIGH; + +void setup() { + Serial.begin(9600); + + //setup general call button as input with internal pullup + pinMode(GENERAL_CALL_ADDR_BUTTON, INPUT); + digitalWrite(GENERAL_CALL_ADDR_BUTTON, HIGH); + + //setup normal i2c send button as input with internal pullup + pinMode(SLAVE_ADDR_BUTTON, INPUT); + digitalWrite(SLAVE_ADDR_BUTTON, HIGH); + + //setup TWI as master + Wire.begin(); + +} + +void loop() { + //if general call button state changes, send new state over message to general call address + if (digitalRead(GENERAL_CALL_ADDR_BUTTON) != general_call_btn_state){ + general_call_btn_state = !general_call_btn_state; + Wire.beginTransmission(GENERAL_CALL_ADDR); + Wire.write(general_call_btn_state); + Wire.endTransmission(); + delay(100); + } + + //if slave address send button changes state, send new state over message to slave address + if (digitalRead(SLAVE_ADDR_BUTTON) != slave_btn_state){ + slave_btn_state = !slave_btn_state; + Wire.beginTransmission(SLAVE_ADDRESS); + Wire.write(slave_btn_state); + Wire.endTransmission(); + //we need a small delay or we'll crash the Wire library - TODO - fix it + delay(100); + } + +} diff --git a/libraries/Wire/examples/general_call_slave/general_call_slave.ino b/libraries/Wire/examples/general_call_slave/general_call_slave.ino new file mode 100644 index 00000000000..7482825aab5 --- /dev/null +++ b/libraries/Wire/examples/general_call_slave/general_call_slave.ino @@ -0,0 +1,84 @@ +/* + * Author: Ed Baafi + * Date: 11/11/11 + * + * This example demonstrates the TWI (I2C) "General Call" capabilities. + * + * General call is like a broadcast that can be received by many listeners at the same same. This + * is useful when you don't know the address of the device you want to talk to or it can be used as + * a sort of interrupt over the TWI (I2C). For more info on General Call, please see the I2C specs. + * + * The Wire library now supports General Call by adding a second receive callback to Wire.onReceive() + * This second callback will be called whenever a message is received over the general call address. + * + * 1) This code is for the slave device. Connect the slave device (through usb) to your PC and open + * up the serial monitor (set it to 9600 bps). You can optionally connect two LEDs to pins 12 and 11 + * + * 2) On the master device, connect a button between pin10 and ground and another between pin9 + * and ground. The pin10 button will send its state to the general call address (can be picked up by + * any attached slave device) and the pin9 button will sent through to a specific slave address. + * + * 3) Connect the SCL and SDA lines from the slave to the master. For UNO boards SDA is on analog pin + * 4 and SCL is on analog pin 5. + * + * 4) Pressing one button will send a message to general call and the other will send a message through + * the slave's address. Want to go further, add a second slave device and/or use the LEDs to see which + * address is being sent to. + */ + +#include + +//led to tie to the master's general call i2c button +#define GENERAL_CALL_RECV_LED 12 +//led to tie to the master's normal i2c button +#define RECV_LED 11 + +#define MY_ADDRESS 0x9 + +void setup() { + Serial.begin(9600); + + //set general call led pin as output and set low (turn off) + pinMode(GENERAL_CALL_RECV_LED, OUTPUT); + digitalWrite(GENERAL_CALL_RECV_LED, LOW); + + //set slave receive led pin as output and set low (turn off) + pinMode(RECV_LED, OUTPUT); + digitalWrite(RECV_LED, LOW); + + //setup TWI as slave and set address + Wire.begin(MY_ADDRESS); + + //setup first callback to fire when data is received on MY_ADDRESS + //and the second when a data is received on the general call address (0x0) + //if you omit the second callback, general call wil not be enabled in the Wire library + Wire.onReceive(receiveCallback,generalCallreceiveCallback); + +} + +void loop() { + //do nothing - wait for callbacks to fire +} + +void receiveCallback(int howMany){ + //print data sent to MY_ADDRESS to the serial monitor + Serial.print("\nRECIEIVED BYTE FROM SLAVE ADDRESS: "); + while (Wire.available() > 0 && howMany > 0){ + boolean b = Wire.read(); + Serial.print(b, DEC); + digitalWrite(RECV_LED, !b); + howMany--; + } +} + +void generalCallreceiveCallback(int howMany){ + //print data sent to the general call address to the serial monitor + Serial.print("\nRECEIVED BYTE FROM GENERAL CALL ADDRESS: "); + while (Wire.available() > 0 && howMany > 0){ + boolean b = Wire.read(); + Serial.print(b, DEC); + digitalWrite(GENERAL_CALL_RECV_LED, !b); + howMany--; + } +} + From f79e814fa17bf75fb6a8bade43612273b5569e56 Mon Sep 17 00:00:00 2001 From: Modkit Date: Fri, 11 Nov 2011 13:14:02 -0500 Subject: [PATCH 6/8] Make SoftwareSerial (formerly NewSoftwareSerial) allow rx and tx to share a single pin for half-duplex communication --- libraries/SoftwareSerial/SoftwareSerial.cpp | 25 +++++++++++++++++++-- libraries/SoftwareSerial/SoftwareSerial.h | 3 ++- 2 files changed, 25 insertions(+), 3 deletions(-) mode change 100755 => 100644 libraries/SoftwareSerial/SoftwareSerial.cpp mode change 100755 => 100644 libraries/SoftwareSerial/SoftwareSerial.h diff --git a/libraries/SoftwareSerial/SoftwareSerial.cpp b/libraries/SoftwareSerial/SoftwareSerial.cpp old mode 100755 new mode 100644 index c15bdda0d5a..f8abdcfa78d --- a/libraries/SoftwareSerial/SoftwareSerial.cpp +++ b/libraries/SoftwareSerial/SoftwareSerial.cpp @@ -337,6 +337,12 @@ SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inv _buffer_overflow(false), _inverse_logic(inverse_logic) { + //set flag if rx and tx shares pin + if (transmitPin == receivePin) + _sharedRxTx = 1; + else + _sharedRxTx = 0; + setTX(transmitPin); setRX(receivePin); } @@ -351,8 +357,11 @@ SoftwareSerial::~SoftwareSerial() void SoftwareSerial::setTX(uint8_t tx) { - pinMode(tx, OUTPUT); - digitalWrite(tx, HIGH); + //if rx and tx shares a pin, only set pin to output when writing + if(!_sharedRxTx){ + pinMode(tx, OUTPUT); + digitalWrite(tx, HIGH); + } _transmitBitMask = digitalPinToBitMask(tx); uint8_t port = digitalPinToPort(tx); _transmitPortRegister = portOutputRegister(port); @@ -450,6 +459,12 @@ size_t SoftwareSerial::write(uint8_t b) uint8_t oldSREG = SREG; cli(); // turn off interrupts for a clean txmit + //if rx and tx shares a pin, set pin to output when writing + if(_sharedRxTx){ + //_receivePin same as transmit pin + pinMode(_receivePin, OUTPUT); + digitalWrite(_receivePin, HIGH); + } // Write the start bit tx_pin_write(_inverse_logic ? HIGH : LOW); tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT); @@ -483,6 +498,12 @@ size_t SoftwareSerial::write(uint8_t b) tx_pin_write(HIGH); // restore pin to natural state } + if(_sharedRxTx){ + + pinMode(_receivePin, INPUT); + if (!_inverse_logic) + digitalWrite(_receivePin, HIGH); // pullup for normal logic! + } SREG = oldSREG; // turn interrupts back on tunedDelay(_tx_delay); diff --git a/libraries/SoftwareSerial/SoftwareSerial.h b/libraries/SoftwareSerial/SoftwareSerial.h old mode 100755 new mode 100644 index a6a60b5560f..0e0bb6a15cb --- a/libraries/SoftwareSerial/SoftwareSerial.h +++ b/libraries/SoftwareSerial/SoftwareSerial.h @@ -47,7 +47,8 @@ The latest version of this library can always be found at class SoftwareSerial : public Stream { private: - // per object data + // per object data + uint8_t _sharedRxTx; uint8_t _receivePin; uint8_t _receiveBitMask; volatile uint8_t *_receivePortRegister; From 129f4d13bde578dbd0d897238f299674e6437d40 Mon Sep 17 00:00:00 2001 From: Modkit Date: Fri, 11 Nov 2011 13:53:56 -0500 Subject: [PATCH 7/8] Added comments about inverse logic issues and corrected rx pullup state --- libraries/SoftwareSerial/SoftwareSerial.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/libraries/SoftwareSerial/SoftwareSerial.cpp b/libraries/SoftwareSerial/SoftwareSerial.cpp index f8abdcfa78d..09371b91922 100644 --- a/libraries/SoftwareSerial/SoftwareSerial.cpp +++ b/libraries/SoftwareSerial/SoftwareSerial.cpp @@ -360,6 +360,9 @@ void SoftwareSerial::setTX(uint8_t tx) //if rx and tx shares a pin, only set pin to output when writing if(!_sharedRxTx){ pinMode(tx, OUTPUT); + + //@edbaafi - if inverse logic is allowed, why would line rest high + //regardless if _invers_logic is set? Was inverse logic added for rx-only? digitalWrite(tx, HIGH); } _transmitBitMask = digitalPinToBitMask(tx); @@ -498,11 +501,25 @@ size_t SoftwareSerial::write(uint8_t b) tx_pin_write(HIGH); // restore pin to natural state } + + //if rx and tx shares a pin, set back to input when done writing if(_sharedRxTx){ pinMode(_receivePin, INPUT); - if (!_inverse_logic) + if (!_inverse_logic){ + //@edbaafi - PORTx is already set to high for this pin + ////but given questions about rest states when inverse logic, I'll leave this for clarity digitalWrite(_receivePin, HIGH); // pullup for normal logic! + } + else{ + //disable pullup + + //@edbaafi - don't think this is happening by default by setting pinMode alone + ////Haven't followed pinMode->portModeRegister->even_more_indirection but it appears to + ////simply set DDRB without initializing PORTx and previous (rest) state is HIGH + ////(even when inverse logic - see questions above) + digitalWrite(_receivePin,LOW); + } } SREG = oldSREG; // turn interrupts back on From 423308f8791f0a553ab5682f244f345b8e705bab Mon Sep 17 00:00:00 2001 From: Modkit Date: Thu, 19 Jul 2012 13:20:40 -0400 Subject: [PATCH 8/8] added convenience constructor to set rx/tx to same pin --- libraries/SoftwareSerial/SoftwareSerial.cpp | 21 +++++++++++++++++++++ libraries/SoftwareSerial/SoftwareSerial.h | 1 + 2 files changed, 22 insertions(+) diff --git a/libraries/SoftwareSerial/SoftwareSerial.cpp b/libraries/SoftwareSerial/SoftwareSerial.cpp index 09371b91922..aea351cbc6c 100644 --- a/libraries/SoftwareSerial/SoftwareSerial.cpp +++ b/libraries/SoftwareSerial/SoftwareSerial.cpp @@ -347,6 +347,27 @@ SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inv setRX(receivePin); } +// +// Convenience Constructor +// (only pass one pin when rx/tx are the same) +// +SoftwareSerial::SoftwareSerial(uint8_t receiveTransmitPin, bool inverse_logic /* = false */) : +_rx_delay_centering(0), +_rx_delay_intrabit(0), +_rx_delay_stopbit(0), +_tx_delay(0), +_buffer_overflow(false), +_inverse_logic(inverse_logic) +{ + //set flag as rx and tx shares pin + + _sharedRxTx = 1; + + + setTX(receiveTransmitPin); + setRX(receiveTransmitPin); +} + // // Destructor // diff --git a/libraries/SoftwareSerial/SoftwareSerial.h b/libraries/SoftwareSerial/SoftwareSerial.h index 0e0bb6a15cb..43f57dbbb41 100644 --- a/libraries/SoftwareSerial/SoftwareSerial.h +++ b/libraries/SoftwareSerial/SoftwareSerial.h @@ -82,6 +82,7 @@ class SoftwareSerial : public Stream public: // public methods SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false); + SoftwareSerial(uint8_t receiveTransmitPin, bool inverse_logic = false); ~SoftwareSerial(); void begin(long speed); bool listen();