Skip to content

Commit fd4f47d

Browse files
author
Cruz Monrreal
authored
Merge pull request #8109 from deepikabhavnani/i2c_recovery
Add reset recovery for I2C bus
2 parents b7cf1ab + 0ef5d92 commit fd4f47d

File tree

4 files changed

+76
-38
lines changed

4 files changed

+76
-38
lines changed

components/802.15.4_RF/atmel-rf-driver/source/at24mac.cpp

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,9 @@
2929
#define EUI64_LEN 8
3030
#define EUI48_LEN 6
3131

32-
AT24Mac::I2CReset::I2CReset(PinName sda, PinName scl)
33-
{
34-
mbed::DigitalInOut pin_sda(sda, PIN_OUTPUT, PullUp, 1);
35-
mbed::DigitalInOut pin_scl(scl, PIN_OUTPUT, PullUp, 0);
36-
//generate 9 clocks for worst-case scenario
37-
for (int i = 0; i < 10; ++i) {
38-
pin_scl = 1;
39-
wait_us(5);
40-
pin_scl = 0;
41-
wait_us(5);
42-
}
43-
//generate a STOP condition
44-
pin_sda = 0;
45-
wait_us(5);
46-
pin_scl = 1;
47-
wait_us(5);
48-
pin_sda = 1;
49-
wait_us(5);
50-
}
32+
using namespace mbed;
5133

52-
/*I2C needs to be reset before constructing the I2C object (in case I2C is stuck)
53-
because they use the same pins, therefore i2c_reset has to be before _i2c
54-
in the initializer list*/
55-
AT24Mac::AT24Mac(PinName sda, PinName scl) : i2c_reset(sda, scl), _i2c(sda, scl)
34+
AT24Mac::AT24Mac(PinName sda, PinName scl) : _i2c(sda , scl)
5635
{
5736
// Do nothing
5837
}

components/802.15.4_RF/atmel-rf-driver/source/at24mac.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,6 @@ class AT24Mac {
5959
int read_eui48(void *buf);
6060

6161
private:
62-
/*
63-
* Dummy class to allow us to reset I2C before the I2C constructor is called in
64-
* the initializer list of AT24Mac's constructor
65-
*/
66-
class I2CReset {
67-
public:
68-
I2CReset(PinName sda, PinName scl);
69-
};
70-
I2CReset i2c_reset;
7162
mbed::I2C _i2c;
7263
};
7364

drivers/I2C.cpp

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
#include "drivers/I2C.h"
18+
#include "drivers/DigitalInOut.h"
19+
#include "platform/mbed_wait_api.h"
1720

1821
#if DEVICE_I2C
1922

@@ -32,13 +35,15 @@ I2C::I2C(PinName sda, PinName scl) :
3235
#endif
3336
_i2c(), _hz(100000)
3437
{
35-
// No lock needed in the constructor
36-
38+
lock();
3739
// The init function also set the frequency to 100000
38-
i2c_init(&_i2c, sda, scl);
39-
40+
_sda = sda;
41+
_scl = scl;
42+
recover(sda, scl);
43+
i2c_init(&_i2c, _sda, _scl);
4044
// Used to avoid unnecessary frequency updates
4145
_owner = this;
46+
unlock();
4247
}
4348

4449
void I2C::frequency(int hz)
@@ -135,6 +140,52 @@ void I2C::unlock()
135140
_mutex->unlock();
136141
}
137142

143+
int I2C::recover(PinName sda, PinName scl)
144+
{
145+
DigitalInOut pin_sda(sda, PIN_INPUT, PullNone, 1);
146+
DigitalInOut pin_scl(scl, PIN_INPUT, PullNone, 1);
147+
148+
// Return as SCL is low and no access to become master.
149+
if (pin_scl == 0) {
150+
return I2C_ERROR_BUS_BUSY;
151+
}
152+
153+
// Return successfully as SDA and SCL is high
154+
if (pin_sda == 1) {
155+
return 0;
156+
}
157+
158+
// Send clock pulses, for device to recover 9
159+
pin_scl.mode(PullNone);
160+
pin_scl.output();
161+
for (int count = 0; count < 10; count++) {
162+
pin_scl.mode(PullNone);
163+
pin_scl = 0;
164+
wait_us(5);
165+
pin_scl.mode(PullUp);
166+
pin_scl = 1;
167+
wait_us(5);
168+
}
169+
170+
// Send Stop
171+
pin_sda.output();
172+
pin_sda = 0;
173+
wait_us(5);
174+
pin_scl = 1;
175+
wait_us(5);
176+
pin_sda = 1;
177+
wait_us(5);
178+
179+
pin_sda.input();
180+
pin_scl.input();
181+
if ((pin_scl == 0) || (pin_sda == 0)) {
182+
// Return as SCL is low and no access to become master.
183+
return I2C_ERROR_BUS_BUSY;
184+
}
185+
186+
return 0;
187+
}
188+
138189
#if DEVICE_I2C_ASYNCH
139190

140191
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)
169220
if (_callback && event) {
170221
_callback.call(event);
171222
}
223+
172224
if (event) {
173225
unlock_deep_sleep();
174226
}
175-
176227
}
177228

178229
void I2C::lock_deep_sleep()

drivers/I2C.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define MBED_I2C_H
1818

1919
#include "platform/platform.h"
20+
#include "hal/gpio_api.h"
2021

2122
#if defined (DEVICE_I2C) || defined(DOXYGEN_ONLY)
2223

@@ -197,8 +198,24 @@ class I2C : private NonCopyable<I2C> {
197198

198199
i2c_t _i2c;
199200
static I2C *_owner;
200-
int _hz;
201+
int _hz;
201202
static SingletonPtr<PlatformMutex> _mutex;
203+
PinName _sda;
204+
PinName _scl;
205+
206+
private:
207+
/** Recover I2C bus, when stuck with SDA low
208+
* @note : Initialization of I2C bus is required after this API.
209+
*
210+
* @param sda I2C data line pin
211+
* @param scl I2C clock line pin
212+
*
213+
* @returns:
214+
* '0' - Successfully recovered
215+
* 'I2C_ERROR_BUS_BUSY' - In case of failure
216+
*
217+
*/
218+
int recover(PinName sda, PinName scl);
202219
};
203220

204221
} // namespace mbed

0 commit comments

Comments
 (0)