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