Skip to content

Commit 6228ff5

Browse files
committed
nrf: Add I2C timeout parameter and fix disable on error.
Accept the timeout kwarg (default 50ms) for compatibility with rp2 and alif ports. Also fix TWI peripheral not being disabled on the transfer error path. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
1 parent e177ea6 commit 6228ff5

1 file changed

Lines changed: 50 additions & 6 deletions

File tree

  • ports/nrf/modules/machine

ports/nrf/modules/machine/i2c.c

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,20 @@
5555

5656
#define nrfx_twi_xfer_desc_t nrfx_twim_xfer_desc_t
5757

58+
#define nrfx_twi_evt_handler_t nrfx_twim_evt_handler_t
59+
#define nrfx_twi_evt_t nrfx_twim_evt_t
60+
#define nrfx_twi_evt_type_t nrfx_twim_evt_type_t
61+
5862
#define NRFX_TWI_XFER_DESC_RX NRFX_TWIM_XFER_DESC_RX
5963
#define NRFX_TWI_XFER_DESC_TX NRFX_TWIM_XFER_DESC_TX
6064

6165
#define NRFX_TWI_INSTANCE NRFX_TWIM_INSTANCE
6266

67+
#define NRFX_TWI_EVT_DONE NRFX_TWIM_EVT_DONE
68+
#define NRFX_TWI_EVT_ADDRESS_NACK NRFX_TWIM_EVT_ADDRESS_NACK
69+
#define NRFX_TWI_EVT_DATA_NACK NRFX_TWIM_EVT_DATA_NACK
70+
#define NRFX_TWI_EVT_BUS_ERROR NRFX_TWIM_EVT_BUS_ERROR
71+
6372
#define NRF_TWI_FREQ_100K NRF_TWIM_FREQ_100K
6473
#define NRF_TWI_FREQ_250K NRF_TWIM_FREQ_250K
6574
#define NRF_TWI_FREQ_400K NRF_TWIM_FREQ_400K
@@ -69,16 +78,25 @@
6978
typedef struct _machine_hard_i2c_obj_t {
7079
mp_obj_base_t base;
7180
nrfx_twi_t p_twi; // Driver instance
81+
uint32_t timeout;
82+
volatile bool xfer_done;
83+
volatile nrfx_twi_evt_type_t xfer_evt;
7284
} machine_hard_i2c_obj_t;
7385

74-
static const machine_hard_i2c_obj_t machine_hard_i2c_obj[] = {
86+
static machine_hard_i2c_obj_t machine_hard_i2c_obj[] = {
7587
{{&machine_i2c_type}, .p_twi = NRFX_TWI_INSTANCE(0)},
7688
{{&machine_i2c_type}, .p_twi = NRFX_TWI_INSTANCE(1)},
7789
};
7890

7991
void i2c_init0(void) {
8092
}
8193

94+
static void twi_event_handler(nrfx_twi_evt_t const *p_event, void *p_context) {
95+
machine_hard_i2c_obj_t *self = (machine_hard_i2c_obj_t *)p_context;
96+
self->xfer_evt = p_event->type;
97+
self->xfer_done = true;
98+
}
99+
82100
static int i2c_find(mp_obj_t id) {
83101
// given an integer id
84102
int i2c_id = mp_obj_get_int(id);
@@ -90,7 +108,7 @@ static int i2c_find(mp_obj_t id) {
90108

91109
static void machine_hard_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
92110
machine_hard_i2c_obj_t *self = self_in;
93-
mp_printf(print, "I2C(%u)", self->p_twi.drv_inst_idx);
111+
mp_printf(print, "I2C(%u, timeout=%u)", self->p_twi.drv_inst_idx, self->timeout);
94112
}
95113

96114
/******************************************************************************/
@@ -99,12 +117,13 @@ static void machine_hard_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp
99117
mp_obj_t machine_hard_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
100118
MP_MACHINE_I2C_CHECK_FOR_LEGACY_SOFTI2C_CONSTRUCTION(n_args, n_kw, all_args);
101119

102-
enum { ARG_id, ARG_scl, ARG_sda, ARG_freq };
120+
enum { ARG_id, ARG_scl, ARG_sda, ARG_freq, ARG_timeout };
103121
static const mp_arg_t allowed_args[] = {
104122
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ },
105123
{ MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ },
106124
{ MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ },
107125
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
126+
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 50000} },
108127
};
109128

110129
// parse args
@@ -113,7 +132,9 @@ mp_obj_t machine_hard_i2c_make_new(const mp_obj_type_t *type, size_t n_args, siz
113132

114133
// get static peripheral object
115134
int i2c_id = i2c_find(args[ARG_id].u_obj);
116-
const machine_hard_i2c_obj_t *self = &machine_hard_i2c_obj[i2c_id];
135+
machine_hard_i2c_obj_t *self = &machine_hard_i2c_obj[i2c_id];
136+
137+
self->timeout = args[ARG_timeout].u_int;
117138

118139
nrfx_twi_config_t config;
119140
#if NRFX_TWI_ENABLED
@@ -139,8 +160,8 @@ mp_obj_t machine_hard_i2c_make_new(const mp_obj_type_t *type, size_t n_args, siz
139160
// First reset the TWI
140161
nrfx_twi_uninit(&self->p_twi);
141162

142-
// Set context to this object.
143-
nrfx_twi_init(&self->p_twi, &config, NULL, (void *)self);
163+
// Set context to this object, use non-blocking mode with event handler.
164+
nrfx_twi_init(&self->p_twi, &config, twi_event_handler, (void *)self);
144165

145166
return MP_OBJ_FROM_PTR(self);
146167
}
@@ -150,6 +171,9 @@ int machine_hard_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, size
150171

151172
nrfx_twi_enable(&self->p_twi);
152173

174+
self->xfer_done = false;
175+
self->xfer_evt = NRFX_TWI_EVT_DONE;
176+
153177
nrfx_err_t err_code;
154178
int transfer_ret = 0;
155179
if (flags & MP_MACHINE_I2C_FLAG_READ) {
@@ -162,6 +186,7 @@ int machine_hard_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, size
162186
}
163187

164188
if (err_code != NRFX_SUCCESS) {
189+
nrfx_twi_disable(&self->p_twi);
165190
if (err_code == NRFX_ERROR_DRV_TWI_ERR_ANACK) {
166191
return -MP_ENODEV;
167192
} else if (err_code == NRFX_ERROR_DRV_TWI_ERR_DNACK) {
@@ -170,8 +195,27 @@ int machine_hard_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, size
170195
return -MP_ETIMEDOUT;
171196
}
172197

198+
// Poll for transfer completion with timeout.
199+
mp_uint_t start = mp_hal_ticks_us();
200+
while (!self->xfer_done) {
201+
if (self->timeout > 0 && (mp_hal_ticks_us() - start) >= self->timeout) {
202+
nrfx_twi_disable(&self->p_twi);
203+
nrfx_twi_enable(&self->p_twi);
204+
return -MP_ETIMEDOUT;
205+
}
206+
MICROPY_EVENT_POLL_HOOK;
207+
}
208+
173209
nrfx_twi_disable(&self->p_twi);
174210

211+
if (self->xfer_evt == NRFX_TWI_EVT_ADDRESS_NACK) {
212+
return -MP_ENODEV;
213+
} else if (self->xfer_evt == NRFX_TWI_EVT_DATA_NACK) {
214+
return -MP_EIO;
215+
} else if (self->xfer_evt != NRFX_TWI_EVT_DONE) {
216+
return -MP_EIO;
217+
}
218+
175219
return transfer_ret;
176220
}
177221

0 commit comments

Comments
 (0)