From 606ea2b10faccd94b2d9724055c0f6020b9c2fc6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 8 May 2019 14:27:18 +1000 Subject: [PATCH] extmod/machine_i2c: Change C-level API to allow split I2C transactions. API is: int transfer( mp_obj_base_t *obj, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags ) --- extmod/machine_i2c.c | 152 +++++++++++++++++++++++++------------------ extmod/machine_i2c.h | 17 +++-- 2 files changed, 102 insertions(+), 67 deletions(-) diff --git a/extmod/machine_i2c.c b/extmod/machine_i2c.c index c1a93ab04..0202fc2bb 100644 --- a/extmod/machine_i2c.c +++ b/extmod/machine_i2c.c @@ -180,9 +180,9 @@ STATIC int mp_hal_i2c_read_byte(machine_i2c_obj_t *self, uint8_t *val, int nack) } // return value: -// >=0 - number of acks received +// >=0 - success; for read it's 0, for write it's number of acks received // <0 - error, with errno being the negative of the return value -int mp_machine_soft_i2c_writeto(mp_obj_base_t *self_in, uint16_t addr, const uint8_t *src, size_t len, bool stop) { +int mp_machine_soft_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) { machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in; // start the I2C transaction @@ -192,7 +192,7 @@ int mp_machine_soft_i2c_writeto(mp_obj_base_t *self_in, uint16_t addr, const uin } // write the slave address - ret = mp_hal_i2c_write_byte(self, addr << 1); + ret = mp_hal_i2c_write_byte(self, (addr << 1) | (flags & MP_MACHINE_I2C_FLAG_READ)); if (ret < 0) { return ret; } else if (ret != 0) { @@ -201,69 +201,102 @@ int mp_machine_soft_i2c_writeto(mp_obj_base_t *self_in, uint16_t addr, const uin return -MP_ENODEV; } - // write the buffer to the I2C memory - int num_acks = 0; - while (len--) { - ret = mp_hal_i2c_write_byte(self, *src++); - if (ret < 0) { - return ret; - } else if (ret != 0) { - // nack received, stop sending - break; + int transfer_ret = 0; + for (; n--; ++bufs) { + size_t len = bufs->len; + uint8_t *buf = bufs->buf; + if (flags & MP_MACHINE_I2C_FLAG_READ) { + // read bytes from the slave into the given buffer(s) + while (len--) { + ret = mp_hal_i2c_read_byte(self, buf++, (n | len) == 0); + if (ret != 0) { + return ret; + } + } + } else { + // write bytes from the given buffer(s) to the slave + while (len--) { + ret = mp_hal_i2c_write_byte(self, *buf++); + if (ret < 0) { + return ret; + } else if (ret != 0) { + // nack received, stop sending + n = 0; + break; + } + ++transfer_ret; // count the number of acks + } } - ++num_acks; } // finish the I2C transaction - if (stop) { + if (flags & MP_MACHINE_I2C_FLAG_STOP) { ret = mp_hal_i2c_stop(self); if (ret != 0) { return ret; } } - return num_acks; + return transfer_ret; } -// return value: -// 0 - success -// <0 - error, with errno being the negative of the return value -int mp_machine_soft_i2c_readfrom(mp_obj_base_t *self_in, uint16_t addr, uint8_t *dest, size_t len, bool stop) { - machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in; +/******************************************************************************/ +// Generic helper functions - // start the I2C transaction - int ret = mp_hal_i2c_start(self); - if (ret != 0) { - return ret; - } - - // write the slave address - ret = mp_hal_i2c_write_byte(self, (addr << 1) | 1); - if (ret < 0) { - return ret; - } else if (ret != 0) { - // nack received, release the bus cleanly - mp_hal_i2c_stop(self); - return -MP_ENODEV; - } - - // read the bytes from the slave - while (len--) { - ret = mp_hal_i2c_read_byte(self, dest++, len == 0); - if (ret != 0) { - return ret; +// For use by ports that require a single buffer of data for a read/write transfer +int mp_machine_i2c_transfer_adaptor(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags) { + size_t len; + uint8_t *buf; + if (n == 1) { + // Use given single buffer + len = bufs[0].len; + buf = bufs[0].buf; + } else { + // Combine buffers into a single one + len = 0; + for (size_t i = 0; i < n; ++i) { + len += bufs[i].len; + } + buf = m_new(uint8_t, len); + if (!(flags & MP_MACHINE_I2C_FLAG_READ)) { + len = 0; + for (size_t i = 0; i < n; ++i) { + memcpy(buf + len, bufs[i].buf, bufs[i].len); + len += bufs[i].len; + } } } - // finish the I2C transaction - if (stop) { - ret = mp_hal_i2c_stop(self); - if (ret != 0) { - return ret; + mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; + int ret = i2c_p->transfer_single(self, addr, len, buf, flags); + + if (n > 1) { + if (flags & MP_MACHINE_I2C_FLAG_READ) { + // Copy data from single buffer to individual ones + len = 0; + for (size_t i = 0; i < n; ++i) { + memcpy(bufs[i].buf, buf + len, bufs[i].len); + len += bufs[i].len; + } } + m_del(uint8_t, buf, len); } - return 0; // success + return ret; +} + +STATIC int mp_machine_i2c_readfrom(mp_obj_base_t *self, uint16_t addr, uint8_t *dest, size_t len, bool stop) { + mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; + mp_machine_i2c_buf_t buf = {.len = len, .buf = dest}; + unsigned int flags = MP_MACHINE_I2C_FLAG_READ | (stop ? MP_MACHINE_I2C_FLAG_STOP : 0); + return i2c_p->transfer(self, addr, 1, &buf, flags); +} + +STATIC int mp_machine_i2c_writeto(mp_obj_base_t *self, uint16_t addr, const uint8_t *src, size_t len, bool stop) { + mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; + mp_machine_i2c_buf_t buf = {.len = len, .buf = (uint8_t*)src}; + unsigned int flags = stop ? MP_MACHINE_I2C_FLAG_STOP : 0; + return i2c_p->transfer(self, addr, 1, &buf, flags); } /******************************************************************************/ @@ -318,11 +351,10 @@ MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_init_obj, 1, machine_i2c_obj_init); STATIC mp_obj_t machine_i2c_scan(mp_obj_t self_in) { mp_obj_base_t *self = MP_OBJ_TO_PTR(self_in); - mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; mp_obj_t list = mp_obj_new_list(0, NULL); // 7-bit addresses 0b0000xxx and 0b1111xxx are reserved for (int addr = 0x08; addr < 0x78; ++addr) { - int ret = i2c_p->writeto(self, addr, NULL, 0, true); + int ret = mp_machine_i2c_writeto(self, addr, NULL, 0, true); if (ret == 0) { mp_obj_list_append(list, MP_OBJ_NEW_SMALL_INT(addr)); } @@ -407,12 +439,11 @@ MP_DEFINE_CONST_FUN_OBJ_2(machine_i2c_write_obj, machine_i2c_write); STATIC mp_obj_t machine_i2c_readfrom(size_t n_args, const mp_obj_t *args) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); - mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; mp_int_t addr = mp_obj_get_int(args[1]); vstr_t vstr; vstr_init_len(&vstr, mp_obj_get_int(args[2])); bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]); - int ret = i2c_p->readfrom(self, addr, (uint8_t*)vstr.buf, vstr.len, stop); + int ret = mp_machine_i2c_readfrom(self, addr, (uint8_t*)vstr.buf, vstr.len, stop); if (ret < 0) { mp_raise_OSError(-ret); } @@ -422,12 +453,11 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readfrom_obj, 3, 4, machine_i2c_ STATIC mp_obj_t machine_i2c_readfrom_into(size_t n_args, const mp_obj_t *args) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); - mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; mp_int_t addr = mp_obj_get_int(args[1]); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]); - int ret = i2c_p->readfrom(self, addr, bufinfo.buf, bufinfo.len, stop); + int ret = mp_machine_i2c_readfrom(self, addr, bufinfo.buf, bufinfo.len, stop); if (ret < 0) { mp_raise_OSError(-ret); } @@ -437,12 +467,11 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readfrom_into_obj, 3, 4, machine STATIC mp_obj_t machine_i2c_writeto(size_t n_args, const mp_obj_t *args) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); - mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; mp_int_t addr = mp_obj_get_int(args[1]); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); bool stop = (n_args == 3) ? true : mp_obj_is_true(args[3]); - int ret = i2c_p->writeto(self, addr, bufinfo.buf, bufinfo.len, stop); + int ret = mp_machine_i2c_writeto(self, addr, bufinfo.buf, bufinfo.len, stop); if (ret < 0) { mp_raise_OSError(-ret); } @@ -453,19 +482,18 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_writeto_obj, 3, 4, machin STATIC int read_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t addrsize, uint8_t *buf, size_t len) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in); - mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; uint8_t memaddr_buf[4]; size_t memaddr_len = 0; for (int16_t i = addrsize - 8; i >= 0; i -= 8) { memaddr_buf[memaddr_len++] = memaddr >> i; } - int ret = i2c_p->writeto(self, addr, memaddr_buf, memaddr_len, false); + int ret = mp_machine_i2c_writeto(self, addr, memaddr_buf, memaddr_len, false); if (ret != memaddr_len) { // must generate STOP - i2c_p->writeto(self, addr, NULL, 0, true); + mp_machine_i2c_writeto(self, addr, NULL, 0, true); return ret; } - return i2c_p->readfrom(self, addr, buf, len, true); + return mp_machine_i2c_readfrom(self, addr, buf, len, true); } #define MAX_MEMADDR_SIZE (4) @@ -473,7 +501,6 @@ STATIC int read_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t a STATIC int write_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t addrsize, const uint8_t *buf, size_t len) { mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in); - mp_machine_i2c_p_t *i2c_p = (mp_machine_i2c_p_t*)self->type->protocol; // need some memory to create the buffer to send; try to use stack if possible uint8_t buf2_stack[MAX_MEMADDR_SIZE + BUF_STACK_SIZE]; @@ -493,7 +520,7 @@ STATIC int write_mem(mp_obj_t self_in, uint16_t addr, uint32_t memaddr, uint8_t } memcpy(buf2 + memaddr_len, buf, len); - int ret = i2c_p->writeto(self, addr, buf2, memaddr_len + len, true); + int ret = mp_machine_i2c_writeto(self, addr, buf2, memaddr_len + len, true); if (buf2_alloc != 0) { m_del(uint8_t, buf2, buf2_alloc); } @@ -625,8 +652,7 @@ STATIC const mp_machine_i2c_p_t mp_machine_soft_i2c_p = { .stop = (int(*)(mp_obj_base_t*))mp_hal_i2c_stop, .read = mp_machine_soft_i2c_read, .write = mp_machine_soft_i2c_write, - .readfrom = mp_machine_soft_i2c_readfrom, - .writeto = mp_machine_soft_i2c_writeto, + .transfer = mp_machine_soft_i2c_transfer, }; const mp_obj_type_t machine_i2c_type = { diff --git a/extmod/machine_i2c.h b/extmod/machine_i2c.h index f5af6656f..f951c1f21 100644 --- a/extmod/machine_i2c.h +++ b/extmod/machine_i2c.h @@ -28,15 +28,24 @@ #include "py/obj.h" +#define MP_MACHINE_I2C_FLAG_READ (0x01) // if not set then it's a write +#define MP_MACHINE_I2C_FLAG_STOP (0x02) + +typedef struct _mp_machine_i2c_buf_t { + size_t len; + uint8_t *buf; +} mp_machine_i2c_buf_t; + // I2C protocol // the first 4 methods can be NULL, meaning operation is not supported +// transfer_single only needs to be set if transfer=mp_machine_i2c_transfer_adaptor typedef struct _mp_machine_i2c_p_t { int (*start)(mp_obj_base_t *obj); int (*stop)(mp_obj_base_t *obj); int (*read)(mp_obj_base_t *obj, uint8_t *dest, size_t len, bool nack); int (*write)(mp_obj_base_t *obj, const uint8_t *src, size_t len); - int (*readfrom)(mp_obj_base_t *obj, uint16_t addr, uint8_t *dest, size_t len, bool stop); - int (*writeto)(mp_obj_base_t *obj, uint16_t addr, const uint8_t *src, size_t len, bool stop); + int (*transfer)(mp_obj_base_t *obj, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); + int (*transfer_single)(mp_obj_base_t *obj, uint16_t addr, size_t len, uint8_t *buf, unsigned int flags); } mp_machine_i2c_p_t; typedef struct _mp_machine_soft_i2c_obj_t { @@ -50,7 +59,7 @@ typedef struct _mp_machine_soft_i2c_obj_t { extern const mp_obj_type_t machine_i2c_type; extern const mp_obj_dict_t mp_machine_soft_i2c_locals_dict; -int mp_machine_soft_i2c_readfrom(mp_obj_base_t *self_in, uint16_t addr, uint8_t *dest, size_t len, bool stop); -int mp_machine_soft_i2c_writeto(mp_obj_base_t *self_in, uint16_t addr, const uint8_t *src, size_t len, bool stop); +int mp_machine_i2c_transfer_adaptor(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); +int mp_machine_soft_i2c_transfer(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); #endif // MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H