Skip to content

Commit bf3eb9d

Browse files
DavidEGraysondpgeorge
authored andcommitted
rp2/machine_i2c: Add timeout parameter for machine.I2C().
This commit adds support for the `timeout` keyword argument to machine.I2C on the rp2 port, following how it's done on other ports. The main motivation here is avoid the interpreter crashing due to infinite loops when SDA is stuck low, which is quite common if the board gets reset while reading from an I2C device. A default timeout of 50ms is chosen because it's consistent with: - Commit a707fe5 which used a timeout of 50,000us for zero-length writes on the rp2 port. - The machine.SoftI2C class which uses 50,000us as the default timeout. - The stm32 port's hardware I2C, which uses 50,000us for I2C_POLL_DEFAULT_TIMEOUT_US. This commit also fixes the default timeout on the esp32 port to be consistent with the above, and updates the documentation for machine.I2C to document this keyword argument.
1 parent b525f1c commit bf3eb9d

File tree

3 files changed

+14
-8
lines changed

3 files changed

+14
-8
lines changed

docs/library/machine.I2C.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Example usage::
5252
Constructors
5353
------------
5454

55-
.. class:: I2C(id, *, scl, sda, freq=400000)
55+
.. class:: I2C(id, *, scl, sda, freq=400000, timeout=50000)
5656

5757
Construct and return a new I2C object using the following parameters:
5858

@@ -62,6 +62,8 @@ Constructors
6262
- *sda* should be a pin object specifying the pin to use for SDA.
6363
- *freq* should be an integer which sets the maximum frequency
6464
for SCL.
65+
- *timeout* is the maximum time in microseconds to allow for I2C
66+
transactions. This parameter is not allowed on some ports.
6567

6668
Note that some ports/boards will have default values of *scl* and *sda*
6769
that can be changed in this constructor. Others will have fixed values

ports/esp32/machine_i2c.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
#error "unsupported I2C for ESP32 SoC variant"
6363
#endif
6464

65-
#define I2C_DEFAULT_TIMEOUT_US (10000) // 10ms
65+
#define I2C_DEFAULT_TIMEOUT_US (50000) // 50ms
6666

6767
typedef struct _machine_hw_i2c_obj_t {
6868
mp_obj_base_t base;

ports/rp2/machine_i2c.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "hardware/i2c.h"
3434

3535
#define DEFAULT_I2C_FREQ (400000)
36+
#define DEFAULT_I2C_TIMEOUT (50000)
3637

3738
#ifndef MICROPY_HW_I2C0_SCL
3839
#if PICO_DEFAULT_I2C == 0
@@ -65,6 +66,7 @@ typedef struct _machine_i2c_obj_t {
6566
uint8_t scl;
6667
uint8_t sda;
6768
uint32_t freq;
69+
uint32_t timeout;
6870
} machine_i2c_obj_t;
6971

7072
STATIC machine_i2c_obj_t machine_i2c_obj[] = {
@@ -74,17 +76,18 @@ STATIC machine_i2c_obj_t machine_i2c_obj[] = {
7476

7577
STATIC void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
7678
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
77-
mp_printf(print, "I2C(%u, freq=%u, scl=%u, sda=%u)",
78-
self->i2c_id, self->freq, self->scl, self->sda);
79+
mp_printf(print, "I2C(%u, freq=%u, scl=%u, sda=%u, timeout=%u)",
80+
self->i2c_id, self->freq, self->scl, self->sda, self->timeout);
7981
}
8082

8183
mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
82-
enum { ARG_id, ARG_freq, ARG_scl, ARG_sda };
84+
enum { ARG_id, ARG_freq, ARG_scl, ARG_sda, ARG_timeout };
8385
static const mp_arg_t allowed_args[] = {
8486
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ },
8587
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = DEFAULT_I2C_FREQ} },
8688
{ MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
8789
{ MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
90+
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_I2C_TIMEOUT} },
8891
};
8992

9093
// Parse args.
@@ -121,6 +124,7 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
121124
self->freq = args[ARG_freq].u_int;
122125
i2c_init(self->i2c_inst, self->freq);
123126
self->freq = i2c_set_baudrate(self->i2c_inst, self->freq);
127+
self->timeout = args[ARG_timeout].u_int;
124128
gpio_set_function(self->scl, GPIO_FUNC_I2C);
125129
gpio_set_function(self->sda, GPIO_FUNC_I2C);
126130
gpio_set_pulls(self->scl, true, 0);
@@ -135,14 +139,14 @@ STATIC int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si
135139
int ret;
136140
bool nostop = !(flags & MP_MACHINE_I2C_FLAG_STOP);
137141
if (flags & MP_MACHINE_I2C_FLAG_READ) {
138-
ret = i2c_read_blocking(self->i2c_inst, addr, buf, len, nostop);
142+
ret = i2c_read_timeout_us(self->i2c_inst, addr, buf, len, nostop, self->timeout);
139143
} else {
140144
if (len == 0) {
141145
// Workaround issue with hardware I2C not accepting zero-length writes.
142146
mp_machine_soft_i2c_obj_t soft_i2c = {
143147
.base = { &mp_machine_soft_i2c_type },
144148
.us_delay = 500000 / self->freq + 1,
145-
.us_timeout = 50000,
149+
.us_timeout = self->timeout,
146150
.scl = self->scl,
147151
.sda = self->sda,
148152
};
@@ -157,7 +161,7 @@ STATIC int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si
157161
gpio_set_function(self->sda, GPIO_FUNC_I2C);
158162
return ret;
159163
} else {
160-
ret = i2c_write_blocking(self->i2c_inst, addr, buf, len, nostop);
164+
ret = i2c_write_timeout_us(self->i2c_inst, addr, buf, len, nostop, self->timeout);
161165
}
162166
}
163167
if (ret < 0) {

0 commit comments

Comments
 (0)