Merge pull request #8109 from deepikabhavnani/i2c_recovery

Add reset recovery for I2C bus
pull/7948/head
Cruz Monrreal 2018-10-08 10:27:04 -05:00 committed by GitHub
commit fd4f47d18f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 38 deletions

View File

@ -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
}

View File

@ -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;
};

View File

@ -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()

View File

@ -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<PlatformMutex> _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