diff --git a/components/802.15.4_RF/atmel-rf-driver/source/at24mac.cpp b/components/802.15.4_RF/atmel-rf-driver/source/at24mac.cpp index 0f45f5f158..5d34bd84ca 100644 --- a/components/802.15.4_RF/atmel-rf-driver/source/at24mac.cpp +++ b/components/802.15.4_RF/atmel-rf-driver/source/at24mac.cpp @@ -29,30 +29,9 @@ #define EUI64_LEN 8 #define EUI48_LEN 6 -AT24Mac::I2CReset::I2CReset(PinName sda, PinName scl) -{ - mbed::DigitalInOut pin_sda(sda, PIN_OUTPUT, PullUp, 1); - mbed::DigitalInOut pin_scl(scl, PIN_OUTPUT, PullUp, 0); - //generate 9 clocks for worst-case scenario - for (int i = 0; i < 10; ++i) { - pin_scl = 1; - wait_us(5); - pin_scl = 0; - wait_us(5); - } - //generate a STOP condition - pin_sda = 0; - wait_us(5); - pin_scl = 1; - wait_us(5); - pin_sda = 1; - wait_us(5); -} +using namespace mbed; -/*I2C needs to be reset before constructing the I2C object (in case I2C is stuck) - because they use the same pins, therefore i2c_reset has to be before _i2c - in the initializer list*/ -AT24Mac::AT24Mac(PinName sda, PinName scl) : i2c_reset(sda, scl), _i2c(sda, scl) +AT24Mac::AT24Mac(PinName sda, PinName scl) : _i2c(sda , scl) { // Do nothing } diff --git a/components/802.15.4_RF/atmel-rf-driver/source/at24mac.h b/components/802.15.4_RF/atmel-rf-driver/source/at24mac.h index 2a2c79f549..5cfd8c2a26 100644 --- a/components/802.15.4_RF/atmel-rf-driver/source/at24mac.h +++ b/components/802.15.4_RF/atmel-rf-driver/source/at24mac.h @@ -59,15 +59,6 @@ public: int read_eui48(void *buf); private: - /* - * Dummy class to allow us to reset I2C before the I2C constructor is called in - * the initializer list of AT24Mac's constructor - */ - class I2CReset { - public: - I2CReset(PinName sda, PinName scl); - }; - I2CReset i2c_reset; mbed::I2C _i2c; }; diff --git a/drivers/I2C.cpp b/drivers/I2C.cpp index 08d8d2320f..2cdcc49d8c 100644 --- a/drivers/I2C.cpp +++ b/drivers/I2C.cpp @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "drivers/I2C.h" +#include "drivers/DigitalInOut.h" +#include "platform/mbed_wait_api.h" #if DEVICE_I2C @@ -32,13 +35,15 @@ I2C::I2C(PinName sda, PinName scl) : #endif _i2c(), _hz(100000) { - // No lock needed in the constructor - + lock(); // The init function also set the frequency to 100000 - i2c_init(&_i2c, sda, scl); - + _sda = sda; + _scl = scl; + recover(sda, scl); + i2c_init(&_i2c, _sda, _scl); // Used to avoid unnecessary frequency updates _owner = this; + unlock(); } void I2C::frequency(int hz) @@ -135,6 +140,52 @@ void I2C::unlock() _mutex->unlock(); } +int I2C::recover(PinName sda, PinName scl) +{ + DigitalInOut pin_sda(sda, PIN_INPUT, PullNone, 1); + DigitalInOut pin_scl(scl, PIN_INPUT, PullNone, 1); + + // Return as SCL is low and no access to become master. + if (pin_scl == 0) { + return I2C_ERROR_BUS_BUSY; + } + + // Return successfully as SDA and SCL is high + if (pin_sda == 1) { + return 0; + } + + // Send clock pulses, for device to recover 9 + pin_scl.mode(PullNone); + pin_scl.output(); + for (int count = 0; count < 10; count++) { + pin_scl.mode(PullNone); + pin_scl = 0; + wait_us(5); + pin_scl.mode(PullUp); + pin_scl = 1; + wait_us(5); + } + + // Send Stop + pin_sda.output(); + pin_sda = 0; + wait_us(5); + pin_scl = 1; + wait_us(5); + pin_sda = 1; + wait_us(5); + + pin_sda.input(); + pin_scl.input(); + if ((pin_scl == 0) || (pin_sda == 0)) { + // Return as SCL is low and no access to become master. + return I2C_ERROR_BUS_BUSY; + } + + return 0; +} + #if DEVICE_I2C_ASYNCH int I2C::transfer(int address, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, const event_callback_t &callback, int event, bool repeated) @@ -169,10 +220,10 @@ void I2C::irq_handler_asynch(void) if (_callback && event) { _callback.call(event); } + if (event) { unlock_deep_sleep(); } - } void I2C::lock_deep_sleep() diff --git a/drivers/I2C.h b/drivers/I2C.h index 13f43186d6..c031d65b59 100644 --- a/drivers/I2C.h +++ b/drivers/I2C.h @@ -17,6 +17,7 @@ #define MBED_I2C_H #include "platform/platform.h" +#include "hal/gpio_api.h" #if defined (DEVICE_I2C) || defined(DOXYGEN_ONLY) @@ -197,8 +198,24 @@ protected: i2c_t _i2c; static I2C *_owner; - int _hz; + int _hz; static SingletonPtr _mutex; + PinName _sda; + PinName _scl; + +private: + /** Recover I2C bus, when stuck with SDA low + * @note : Initialization of I2C bus is required after this API. + * + * @param sda I2C data line pin + * @param scl I2C clock line pin + * + * @returns: + * '0' - Successfully recovered + * 'I2C_ERROR_BUS_BUSY' - In case of failure + * + */ + int recover(PinName sda, PinName scl); }; } // namespace mbed