diff options
Diffstat (limited to 'extmod/machine_i2c.c')
| -rw-r--r-- | extmod/machine_i2c.c | 154 | 
1 files changed, 90 insertions, 64 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; - -    // 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; +/******************************************************************************/ +// Generic helper functions + +// 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 = {  | 
