Skip to content

Commit ffebb5a

Browse files
authored
Merge pull request #8094 from furbrain/fix_i2c_hangs
Fix for #8093
2 parents a84551a + a753490 commit ffebb5a

File tree

2 files changed

+57
-22
lines changed

2 files changed

+57
-22
lines changed

ports/nrf/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ SRC_NRFX = $(addprefix nrfx/,\
114114
drivers/src/nrfx_spim.c \
115115
drivers/src/nrfx_timer.c \
116116
drivers/src/nrfx_twim.c \
117+
drivers/src/nrfx_twi_twim.c \
117118
drivers/src/nrfx_uarte.c \
118119
drivers/src/nrfx_gpiote.c \
119120
drivers/src/nrfx_rtc.c \

ports/nrf/common-hal/busio/I2C.c

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -79,20 +79,59 @@ void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) {
7979
}
8080
}
8181

82+
static bool _bus_is_sane(uint32_t scl_pin, uint32_t sda_pin) {
83+
#if CIRCUITPY_REQUIRE_I2C_PULLUPS
84+
nrf_gpio_cfg_input(scl_pin, NRF_GPIO_PIN_PULLDOWN);
85+
nrf_gpio_cfg_input(sda_pin, NRF_GPIO_PIN_PULLDOWN);
86+
87+
common_hal_mcu_delay_us(10);
88+
89+
nrf_gpio_cfg_input(scl_pin, NRF_GPIO_PIN_NOPULL);
90+
nrf_gpio_cfg_input(sda_pin, NRF_GPIO_PIN_NOPULL);
91+
92+
// We must pull up within 3us to achieve 400khz.
93+
common_hal_mcu_delay_us(3);
94+
if (!nrf_gpio_pin_read(sda_pin) || !nrf_gpio_pin_read(scl_pin)) {
95+
return false;
96+
} else {
97+
return true;
98+
}
99+
#else
100+
return true;
101+
#endif
102+
}
103+
104+
static nrfx_err_t _safe_twim_enable(busio_i2c_obj_t *self) {
105+
// check to see if bus is in sensible state before enabling twim
106+
nrfx_err_t recover_result;
107+
108+
if (!_bus_is_sane(self->scl_pin_number, self->sda_pin_number)) {
109+
// bus not in a sane state - try to recover
110+
recover_result = nrfx_twim_bus_recover(self->scl_pin_number, self->sda_pin_number);
111+
if (NRFX_SUCCESS != recover_result) {
112+
// return error message if unable to recover the bus
113+
return recover_result;
114+
}
115+
}
116+
117+
nrfx_twim_enable(&self->twim_peripheral->twim);
118+
return NRFX_SUCCESS;
119+
}
120+
82121
static uint8_t twi_error_to_mp(const nrfx_err_t err) {
83122
switch (err) {
84123
case NRFX_ERROR_DRV_TWI_ERR_ANACK:
85124
return MP_ENODEV;
86125
case NRFX_ERROR_BUSY:
87126
return MP_EBUSY;
127+
case NRFX_SUCCESS:
128+
return 0;
88129
case NRFX_ERROR_DRV_TWI_ERR_DNACK:
89130
case NRFX_ERROR_INVALID_ADDR:
90-
return MP_EIO;
131+
case NRFX_ERROR_INTERNAL:
91132
default:
92-
break;
133+
return MP_EIO;
93134
}
94-
95-
return 0;
96135
}
97136

98137
void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda, uint32_t frequency, uint32_t timeout) {
@@ -114,25 +153,12 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, const mcu_pin_obj_t *
114153
mp_raise_ValueError(translate("All I2C peripherals are in use"));
115154
}
116155

117-
#if CIRCUITPY_REQUIRE_I2C_PULLUPS
118-
// Test that the pins are in a high state. (Hopefully indicating they are pulled up.)
119-
nrf_gpio_cfg_input(scl->number, NRF_GPIO_PIN_PULLDOWN);
120-
nrf_gpio_cfg_input(sda->number, NRF_GPIO_PIN_PULLDOWN);
121-
122-
common_hal_mcu_delay_us(10);
123-
124-
nrf_gpio_cfg_input(scl->number, NRF_GPIO_PIN_NOPULL);
125-
nrf_gpio_cfg_input(sda->number, NRF_GPIO_PIN_NOPULL);
126-
127-
// We must pull up within 3us to achieve 400khz.
128-
common_hal_mcu_delay_us(3);
129-
130-
if (!nrf_gpio_pin_read(sda->number) || !nrf_gpio_pin_read(scl->number)) {
156+
// check bus is in a sane state
157+
if (!_bus_is_sane(scl->number,sda->number)) {
131158
reset_pin_number(sda->number);
132159
reset_pin_number(scl->number);
133160
mp_raise_RuntimeError(translate("No pull up found on SDA or SCL; check your wiring"));
134161
}
135-
#endif
136162

137163
nrfx_twim_config_t config = NRFX_TWIM_DEFAULT_CONFIG(scl->number, sda->number);
138164

@@ -188,7 +214,9 @@ bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) {
188214
NRF_TWIM_Type *reg = self->twim_peripheral->twim.p_twim;
189215
bool found = true;
190216

191-
nrfx_twim_enable(&self->twim_peripheral->twim);
217+
if (NRFX_SUCCESS != _safe_twim_enable(self)) {
218+
return false;
219+
}
192220

193221
nrf_twim_address_set(reg, addr);
194222
nrf_twim_tx_buffer_set(reg, NULL, 0);
@@ -246,7 +274,10 @@ STATIC uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr,
246274

247275
nrfx_err_t err = NRFX_SUCCESS;
248276

249-
nrfx_twim_enable(&self->twim_peripheral->twim);
277+
err = _safe_twim_enable(self);
278+
if (NRFX_SUCCESS != err) {
279+
return twi_error_to_mp(err);
280+
}
250281

251282
// break into MAX_XFER_LEN transaction
252283
while (len) {
@@ -278,7 +309,10 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t
278309

279310
nrfx_err_t err = NRFX_SUCCESS;
280311

281-
nrfx_twim_enable(&self->twim_peripheral->twim);
312+
err = _safe_twim_enable(self);
313+
if (NRFX_SUCCESS != err) {
314+
return twi_error_to_mp(err);
315+
}
282316

283317
// break into MAX_XFER_LEN transaction
284318
while (len) {

0 commit comments

Comments
 (0)