mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #8109 from deepikabhavnani/i2c_recovery
Add reset recovery for I2C buspull/7948/head
commit
fd4f47d18f
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue