diff options
Diffstat (limited to 'extmod')
-rw-r--r-- | extmod/machine_i2c.c | 440 | ||||
-rw-r--r-- | extmod/machine_i2c.h | 23 | ||||
-rw-r--r-- | extmod/machine_mem.c | 2 | ||||
-rw-r--r-- | extmod/machine_spi.c | 204 | ||||
-rw-r--r-- | extmod/machine_spi.h | 7 | ||||
-rw-r--r-- | extmod/modbtree.c | 7 | ||||
-rw-r--r-- | extmod/modframebuf.c | 453 | ||||
-rw-r--r-- | extmod/modlwip.c | 34 | ||||
-rw-r--r-- | extmod/modurandom.c | 2 | ||||
-rw-r--r-- | extmod/moduselect.c | 312 | ||||
-rw-r--r-- | extmod/modussl_axtls.c | 1 | ||||
-rw-r--r-- | extmod/modussl_mbedtls.c | 1 | ||||
-rw-r--r-- | extmod/modutimeq.c | 212 | ||||
-rw-r--r-- | extmod/moduzlib.c | 1 | ||||
-rw-r--r-- | extmod/vfs_fat.c | 6 | ||||
-rw-r--r-- | extmod/vfs_fat_file.c | 20 | ||||
-rw-r--r-- | extmod/vfs_fat_lexer.c | 83 | ||||
-rw-r--r-- | extmod/vfs_fat_reader.c | 88 |
18 files changed, 1579 insertions, 317 deletions
diff --git a/extmod/machine_i2c.c b/extmod/machine_i2c.c index e201b23990..c0a51a6e7f 100644 --- a/extmod/machine_i2c.c +++ b/extmod/machine_i2c.c @@ -28,21 +28,14 @@ #include <stdint.h> #include <string.h> +#include "py/mperrno.h" #include "py/mphal.h" #include "py/runtime.h" #include "extmod/machine_i2c.h" #if MICROPY_PY_MACHINE_I2C -// Clock stretching limit, so that we don't get stuck. -#define I2C_STRETCH_LIMIT 255 - -typedef struct _machine_i2c_obj_t { - mp_obj_base_t base; - uint32_t us_delay; - mp_hal_pin_obj_t scl; - mp_hal_pin_obj_t sda; -} machine_i2c_obj_t; +typedef mp_machine_soft_i2c_obj_t machine_i2c_obj_t; STATIC void mp_hal_i2c_delay(machine_i2c_obj_t *self) { // We need to use an accurate delay to get acceptable I2C @@ -54,13 +47,19 @@ STATIC void mp_hal_i2c_scl_low(machine_i2c_obj_t *self) { mp_hal_pin_od_low(self->scl); } -STATIC void mp_hal_i2c_scl_release(machine_i2c_obj_t *self) { +STATIC int mp_hal_i2c_scl_release(machine_i2c_obj_t *self) { + uint32_t count = self->us_timeout; + mp_hal_pin_od_high(self->scl); mp_hal_i2c_delay(self); // For clock stretching, wait for the SCL pin to be released, with timeout. - for (int count = I2C_STRETCH_LIMIT; mp_hal_pin_read(self->scl) == 0 && count; --count) { + for (; mp_hal_pin_read(self->scl) == 0 && count; --count) { mp_hal_delay_us_fast(1); } + if (count == 0) { + return -MP_ETIMEDOUT; + } + return 0; // success } STATIC void mp_hal_i2c_sda_low(machine_i2c_obj_t *self) { @@ -75,21 +74,26 @@ STATIC int mp_hal_i2c_sda_read(machine_i2c_obj_t *self) { return mp_hal_pin_read(self->sda); } -STATIC void mp_hal_i2c_start(machine_i2c_obj_t *self) { +STATIC int mp_hal_i2c_start(machine_i2c_obj_t *self) { mp_hal_i2c_sda_release(self); mp_hal_i2c_delay(self); - mp_hal_i2c_scl_release(self); + int ret = mp_hal_i2c_scl_release(self); + if (ret != 0) { + return ret; + } mp_hal_i2c_sda_low(self); mp_hal_i2c_delay(self); + return 0; // success } -STATIC void mp_hal_i2c_stop(machine_i2c_obj_t *self) { +STATIC int mp_hal_i2c_stop(machine_i2c_obj_t *self) { mp_hal_i2c_delay(self); mp_hal_i2c_sda_low(self); mp_hal_i2c_delay(self); - mp_hal_i2c_scl_release(self); + int ret = mp_hal_i2c_scl_release(self); mp_hal_i2c_sda_release(self); mp_hal_i2c_delay(self); + return ret; } STATIC void mp_hal_i2c_init(machine_i2c_obj_t *self, uint32_t freq) { @@ -99,9 +103,13 @@ STATIC void mp_hal_i2c_init(machine_i2c_obj_t *self, uint32_t freq) { } mp_hal_pin_open_drain(self->scl); mp_hal_pin_open_drain(self->sda); - mp_hal_i2c_stop(self); + mp_hal_i2c_stop(self); // ignore error } +// return value: +// 0 - byte written and ack received +// 1 - byte written and nack received +// <0 - error, with errno being the negative of the return value STATIC int mp_hal_i2c_write_byte(machine_i2c_obj_t *self, uint8_t val) { mp_hal_i2c_delay(self); mp_hal_i2c_scl_low(self); @@ -113,21 +121,31 @@ STATIC int mp_hal_i2c_write_byte(machine_i2c_obj_t *self, uint8_t val) { mp_hal_i2c_sda_low(self); } mp_hal_i2c_delay(self); - mp_hal_i2c_scl_release(self); + int ret = mp_hal_i2c_scl_release(self); + if (ret != 0) { + mp_hal_i2c_sda_release(self); + return ret; + } mp_hal_i2c_scl_low(self); } mp_hal_i2c_sda_release(self); mp_hal_i2c_delay(self); - mp_hal_i2c_scl_release(self); + int ret = mp_hal_i2c_scl_release(self); + if (ret != 0) { + return ret; + } - int ret = mp_hal_i2c_sda_read(self); + int ack = mp_hal_i2c_sda_read(self); mp_hal_i2c_delay(self); mp_hal_i2c_scl_low(self); - return !ret; + return ack; } +// return value: +// 0 - success +// <0 - error, with errno being the negative of the return value STATIC int mp_hal_i2c_read_byte(machine_i2c_obj_t *self, uint8_t *val, int nack) { mp_hal_i2c_delay(self); mp_hal_i2c_scl_low(self); @@ -135,7 +153,10 @@ STATIC int mp_hal_i2c_read_byte(machine_i2c_obj_t *self, uint8_t *val, int nack) uint8_t data = 0; for (int i = 7; i >= 0; i--) { - mp_hal_i2c_scl_release(self); + int ret = mp_hal_i2c_scl_release(self); + if (ret != 0) { + return ret; + } data = (data << 1) | mp_hal_i2c_sda_read(self); mp_hal_i2c_scl_low(self); mp_hal_i2c_delay(self); @@ -147,111 +168,140 @@ STATIC int mp_hal_i2c_read_byte(machine_i2c_obj_t *self, uint8_t *val, int nack) mp_hal_i2c_sda_low(self); } mp_hal_i2c_delay(self); - mp_hal_i2c_scl_release(self); + int ret = mp_hal_i2c_scl_release(self); + if (ret != 0) { + mp_hal_i2c_sda_release(self); + return ret; + } mp_hal_i2c_scl_low(self); mp_hal_i2c_sda_release(self); - return 1; // success + return 0; // success } -// addr is the device address, memaddr is a memory address sent big-endian -STATIC int mp_hal_i2c_write_addresses(machine_i2c_obj_t *self, uint8_t addr, - uint32_t memaddr, uint8_t addrsize) { - if (!mp_hal_i2c_write_byte(self, addr << 1)) { - return 0; // error - } - for (int16_t i = addrsize - 8; i >= 0; i -= 8) { - if (!mp_hal_i2c_write_byte(self, memaddr >> i)) { - return 0; // error - } - } - return 1; // success -} +// return value: +// >=0 - 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) { + machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in; -STATIC void mp_hal_i2c_write_mem(machine_i2c_obj_t *self, uint8_t addr, - uint32_t memaddr, uint8_t addrsize, const uint8_t *src, size_t len) { // start the I2C transaction - mp_hal_i2c_start(self); + int ret = mp_hal_i2c_start(self); + if (ret != 0) { + return ret; + } - // write the slave address and the memory address within the slave - if (!mp_hal_i2c_write_addresses(self, addr, memaddr, addrsize)) { - goto er; + // write the slave address + ret = mp_hal_i2c_write_byte(self, addr << 1); + if (ret < 0) { + return ret; + } else if (ret != 0) { + // nack received, release the bus cleanly + mp_hal_i2c_stop(self); + return -MP_ENODEV; } // write the buffer to the I2C memory + int num_acks = 0; while (len--) { - if (!mp_hal_i2c_write_byte(self, *src++)) { - goto er; + ret = mp_hal_i2c_write_byte(self, *src++); + if (ret < 0) { + return ret; + } else if (ret != 0) { + // nack received, stop sending + break; } + ++num_acks; } // finish the I2C transaction - mp_hal_i2c_stop(self); - return; + if (stop) { + ret = mp_hal_i2c_stop(self); + if (ret != 0) { + return ret; + } + } -er: - mp_hal_i2c_stop(self); - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error")); + return num_acks; } -STATIC void mp_hal_i2c_read_mem(machine_i2c_obj_t *self, uint8_t addr, - uint32_t memaddr, uint8_t addrsize, uint8_t *dest, size_t len) { - // start the I2C transaction - mp_hal_i2c_start(self); - - if (addrsize) { - // write the slave address and the memory address within the slave - if (!mp_hal_i2c_write_addresses(self, addr, memaddr, addrsize)) { - goto er; - } +// 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; - // i2c_read will do a repeated start, and then read the I2C memory - mp_hal_i2c_start(self); + // start the I2C transaction + int ret = mp_hal_i2c_start(self); + if (ret != 0) { + return ret; } - if (!mp_hal_i2c_write_byte(self, (addr << 1) | 1)) { - goto er; + // 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--) { - if (!mp_hal_i2c_read_byte(self, dest++, len == 0)) { - goto er; + ret = mp_hal_i2c_read_byte(self, dest++, len == 0); + if (ret != 0) { + return ret; } } - mp_hal_i2c_stop(self); - return; - -er: - mp_hal_i2c_stop(self); - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error")); -} -STATIC void mp_hal_i2c_write(machine_i2c_obj_t *self, uint8_t addr, const uint8_t *src, size_t len) { - mp_hal_i2c_write_mem(self, addr, 0, 0, src, len); -} + // finish the I2C transaction + if (stop) { + ret = mp_hal_i2c_stop(self); + if (ret != 0) { + return ret; + } + } -STATIC void mp_hal_i2c_read(machine_i2c_obj_t *self, uint8_t addr, uint8_t *dest, size_t len) { - mp_hal_i2c_read_mem(self, addr, 0, 0, dest, len); + return 0; // success } /******************************************************************************/ // MicroPython bindings for I2C STATIC void machine_i2c_obj_init_helper(machine_i2c_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_scl, ARG_sda, ARG_freq }; + enum { ARG_scl, ARG_sda, ARG_freq, ARG_timeout }; static const mp_arg_t allowed_args[] = { { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 255} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); self->scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj); self->sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj); + self->us_timeout = args[ARG_timeout].u_int; mp_hal_i2c_init(self, args[ARG_freq].u_int); } STATIC 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 *args) { - mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, true); + // check the id argument, if given + if (n_args > 0) { + if (args[0] != MP_OBJ_NEW_SMALL_INT(-1)) { + #if defined(MICROPY_PY_MACHINE_I2C_MAKE_NEW) + // dispatch to port-specific constructor + extern mp_obj_t MICROPY_PY_MACHINE_I2C_MAKE_NEW(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); + return MICROPY_PY_MACHINE_I2C_MAKE_NEW(type, n_args, n_kw, args); + #else + mp_raise_ValueError("invalid I2C peripheral"); + #endif + } + --n_args; + ++args; + } + + // create new soft I2C object machine_i2c_obj_t *self = m_new_obj(machine_i2c_obj_t); self->base.type = &machine_i2c_type; mp_map_t kw_args; @@ -267,99 +317,188 @@ STATIC mp_obj_t machine_i2c_obj_init(size_t n_args, const mp_obj_t *args, mp_map 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) { - machine_i2c_obj_t *self = MP_OBJ_TO_PTR(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) { - mp_hal_i2c_start(self); - int ack = mp_hal_i2c_write_byte(self, (addr << 1)); - if (ack) { + int ret = i2c_p->writeto(self, addr, NULL, 0, true); + if (ret == 0) { mp_obj_list_append(list, MP_OBJ_NEW_SMALL_INT(addr)); } - mp_hal_i2c_stop(self); } return list; } MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_scan_obj, machine_i2c_scan); STATIC mp_obj_t machine_i2c_start(mp_obj_t self_in) { - machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_hal_i2c_start(self); + 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; + if (i2c_p->start == NULL) { + mp_raise_msg(&mp_type_OSError, "I2C operation not supported"); + } + int ret = i2c_p->start(self); + if (ret != 0) { + mp_raise_OSError(-ret); + } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_start_obj, machine_i2c_start); STATIC mp_obj_t machine_i2c_stop(mp_obj_t self_in) { - machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_hal_i2c_stop(self); + 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; + if (i2c_p->stop == NULL) { + mp_raise_msg(&mp_type_OSError, "I2C operation not supported"); + } + int ret = i2c_p->stop(self); + if (ret != 0) { + mp_raise_OSError(-ret); + } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_stop_obj, machine_i2c_stop); -STATIC mp_obj_t machine_i2c_readinto(mp_obj_t self_in, mp_obj_t buf_in) { - machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); +STATIC mp_obj_t machine_i2c_readinto(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; + if (i2c_p->read == NULL) { + mp_raise_msg(&mp_type_OSError, "I2C operation not supported"); + } // get the buffer to read into mp_buffer_info_t bufinfo; - mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE); + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); + + // work out if we want to send a nack at the end + bool nack = (n_args == 2) ? true : mp_obj_is_true(args[2]); // do the read - uint8_t *dest = bufinfo.buf; - while (bufinfo.len--) { - if (!mp_hal_i2c_read_byte(self, dest++, bufinfo.len == 0)) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error")); - } + int ret = i2c_p->read(self, bufinfo.buf, bufinfo.len, nack); + if (ret != 0) { + mp_raise_OSError(-ret); } return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_2(machine_i2c_readinto_obj, machine_i2c_readinto); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readinto_obj, 2, 3, machine_i2c_readinto); STATIC mp_obj_t machine_i2c_write(mp_obj_t self_in, mp_obj_t buf_in) { - machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); + 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; + if (i2c_p->write == NULL) { + mp_raise_msg(&mp_type_OSError, "I2C operation not supported"); + } // get the buffer to write from mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); // do the write - uint8_t *src = bufinfo.buf; - while (bufinfo.len--) { - if (!mp_hal_i2c_write_byte(self, *src++)) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error")); - } + int ret = i2c_p->write(self, bufinfo.buf, bufinfo.len); + if (ret < 0) { + mp_raise_OSError(-ret); } - return mp_const_none; + // return number of acks received + return MP_OBJ_NEW_SMALL_INT(ret); } MP_DEFINE_CONST_FUN_OBJ_2(machine_i2c_write_obj, machine_i2c_write); -STATIC mp_obj_t machine_i2c_readfrom(mp_obj_t self_in, mp_obj_t addr_in, mp_obj_t nbytes_in) { - machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); +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(nbytes_in)); - mp_hal_i2c_read(self, mp_obj_get_int(addr_in), (uint8_t*)vstr.buf, vstr.len); + 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); + if (ret < 0) { + mp_raise_OSError(-ret); + } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } -MP_DEFINE_CONST_FUN_OBJ_3(machine_i2c_readfrom_obj, machine_i2c_readfrom); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readfrom_obj, 3, 4, machine_i2c_readfrom); -STATIC mp_obj_t machine_i2c_readfrom_into(mp_obj_t self_in, mp_obj_t addr_in, mp_obj_t buf_in) { - machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); +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(buf_in, &bufinfo, MP_BUFFER_WRITE); - mp_hal_i2c_read(self, mp_obj_get_int(addr_in), (uint8_t*)bufinfo.buf, bufinfo.len); + 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); + if (ret < 0) { + mp_raise_OSError(-ret); + } return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_3(machine_i2c_readfrom_into_obj, machine_i2c_readfrom_into); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_readfrom_into_obj, 3, 4, machine_i2c_readfrom_into); -STATIC mp_obj_t machine_i2c_writeto(mp_obj_t self_in, mp_obj_t addr_in, mp_obj_t buf_in) { - machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); +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(buf_in, &bufinfo, MP_BUFFER_READ); - mp_hal_i2c_write(self, mp_obj_get_int(addr_in), bufinfo.buf, bufinfo.len); - return mp_const_none; + 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); + if (ret < 0) { + mp_raise_OSError(-ret); + } + // return number of acks received + return MP_OBJ_NEW_SMALL_INT(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_i2c_writeto_obj, 3, 4, machine_i2c_writeto); + +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); + if (ret != memaddr_len) { + // must generate STOP + i2c_p->writeto(self, addr, NULL, 0, true); + return ret; + } + return i2c_p->readfrom(self, addr, buf, len, true); +} + +#define MAX_MEMADDR_SIZE (4) +#define BUF_STACK_SIZE (12) + +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]; + uint8_t *buf2; + size_t buf2_alloc = 0; + if (len <= BUF_STACK_SIZE) { + buf2 = buf2_stack; + } else { + buf2_alloc = MAX_MEMADDR_SIZE + len; + buf2 = m_new(uint8_t, buf2_alloc); + } + + // create the buffer to send + size_t memaddr_len = 0; + for (int16_t i = addrsize - 8; i >= 0; i -= 8) { + buf2[memaddr_len++] = memaddr >> i; + } + memcpy(buf2 + memaddr_len, buf, len); + + int ret = i2c_p->writeto(self, addr, buf2, memaddr_len + len, true); + if (buf2_alloc != 0) { + m_del(uint8_t, buf2, buf2_alloc); + } + return ret; } -STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_i2c_writeto_obj, machine_i2c_writeto); STATIC const mp_arg_t machine_i2c_mem_allowed_args[] = { { MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, @@ -370,7 +509,6 @@ STATIC const mp_arg_t machine_i2c_mem_allowed_args[] = { STATIC mp_obj_t machine_i2c_readfrom_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_addr, ARG_memaddr, ARG_n, ARG_addrsize }; - machine_i2c_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args); @@ -380,8 +518,12 @@ STATIC mp_obj_t machine_i2c_readfrom_mem(size_t n_args, const mp_obj_t *pos_args vstr_init_len(&vstr, mp_obj_get_int(args[ARG_n].u_obj)); // do the transfer - mp_hal_i2c_read_mem(self, args[ARG_addr].u_int, args[ARG_memaddr].u_int, + int ret = read_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int, args[ARG_addrsize].u_int, (uint8_t*)vstr.buf, vstr.len); + if (ret < 0) { + mp_raise_OSError(-ret); + } + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_obj, 1, machine_i2c_readfrom_mem); @@ -389,7 +531,6 @@ MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_obj, 1, machine_i2c_readfrom STATIC mp_obj_t machine_i2c_readfrom_mem_into(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_addr, ARG_memaddr, ARG_buf, ARG_addrsize }; - machine_i2c_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args); @@ -399,15 +540,17 @@ STATIC mp_obj_t machine_i2c_readfrom_mem_into(size_t n_args, const mp_obj_t *pos mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_WRITE); // do the transfer - mp_hal_i2c_read_mem(self, args[ARG_addr].u_int, args[ARG_memaddr].u_int, + int ret = read_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int, args[ARG_addrsize].u_int, bufinfo.buf, bufinfo.len); + if (ret < 0) { + mp_raise_OSError(-ret); + } return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_into_obj, 1, machine_i2c_readfrom_mem_into); STATIC mp_obj_t machine_i2c_writeto_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_addr, ARG_memaddr, ARG_buf, ARG_addrsize }; - machine_i2c_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); mp_arg_val_t args[MP_ARRAY_SIZE(machine_i2c_mem_allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(machine_i2c_mem_allowed_args), machine_i2c_mem_allowed_args, args); @@ -417,8 +560,12 @@ STATIC mp_obj_t machine_i2c_writeto_mem(size_t n_args, const mp_obj_t *pos_args, mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_READ); // do the transfer - mp_hal_i2c_write_mem(self, args[ARG_addr].u_int, args[ARG_memaddr].u_int, + int ret = write_mem(pos_args[0], args[ARG_addr].u_int, args[ARG_memaddr].u_int, args[ARG_addrsize].u_int, bufinfo.buf, bufinfo.len); + if (ret < 0) { + mp_raise_OSError(-ret); + } + return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_writeto_mem_obj, 1, machine_i2c_writeto_mem); @@ -444,13 +591,50 @@ STATIC const mp_rom_map_elem_t machine_i2c_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_writeto_mem), MP_ROM_PTR(&machine_i2c_writeto_mem_obj) }, }; -STATIC MP_DEFINE_CONST_DICT(machine_i2c_locals_dict, machine_i2c_locals_dict_table); +MP_DEFINE_CONST_DICT(mp_machine_soft_i2c_locals_dict, machine_i2c_locals_dict_table); + +int mp_machine_soft_i2c_read(mp_obj_base_t *self_in, uint8_t *dest, size_t len, bool nack) { + machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in; + while (len--) { + int ret = mp_hal_i2c_read_byte(self, dest++, nack && (len == 0)); + if (ret != 0) { + return ret; + } + } + return 0; // success +} + +int mp_machine_soft_i2c_write(mp_obj_base_t *self_in, const uint8_t *src, size_t len) { + machine_i2c_obj_t *self = (machine_i2c_obj_t*)self_in; + int num_acks = 0; + while (len--) { + int ret = mp_hal_i2c_write_byte(self, *src++); + if (ret < 0) { + return ret; + } else if (ret != 0) { + // nack received, stop sending + break; + } + ++num_acks; + } + return num_acks; +} + +STATIC const mp_machine_i2c_p_t mp_machine_soft_i2c_p = { + .start = (int(*)(mp_obj_base_t*))mp_hal_i2c_start, + .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, +}; const mp_obj_type_t machine_i2c_type = { { &mp_type_type }, .name = MP_QSTR_I2C, .make_new = machine_i2c_make_new, - .locals_dict = (mp_obj_dict_t*)&machine_i2c_locals_dict, + .protocol = &mp_machine_soft_i2c_p, + .locals_dict = (mp_obj_dict_t*)&mp_machine_soft_i2c_locals_dict, }; #endif // MICROPY_PY_MACHINE_I2C diff --git a/extmod/machine_i2c.h b/extmod/machine_i2c.h index 03fa6422ad..d49ff01e46 100644 --- a/extmod/machine_i2c.h +++ b/extmod/machine_i2c.h @@ -29,6 +29,29 @@ #include "py/obj.h" +// I2C protocol +// the first 4 methods can be NULL, meaning operation is not supported +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); +} mp_machine_i2c_p_t; + +typedef struct _mp_machine_soft_i2c_obj_t { + mp_obj_base_t base; + uint32_t us_delay; + uint32_t us_timeout; + mp_hal_pin_obj_t scl; + mp_hal_pin_obj_t sda; +} 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); #endif // __MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H__ diff --git a/extmod/machine_mem.c b/extmod/machine_mem.c index 8151bab47f..88c176803f 100644 --- a/extmod/machine_mem.c +++ b/extmod/machine_mem.c @@ -31,7 +31,7 @@ // If you wish to override the functions for mapping the machine_mem read/write // address, then add a #define for MICROPY_MACHINE_MEM_GET_READ_ADDR and/or -// MICROPY_MACHINE_MEM_GET_WRITE_ADDR in yopur mpconfigport.h. Since the +// MICROPY_MACHINE_MEM_GET_WRITE_ADDR in your mpconfigport.h. Since the // prototypes are identical, it is allowable for both of the macros to evaluate // the to same function. // diff --git a/extmod/machine_spi.c b/extmod/machine_spi.c index e3d72ab588..6e76784988 100644 --- a/extmod/machine_spi.c +++ b/extmod/machine_spi.c @@ -32,6 +32,12 @@ #if MICROPY_PY_MACHINE_SPI +// if a port didn't define MSB/LSB constants then provide them +#ifndef MICROPY_PY_MACHINE_SPI_MSB +#define MICROPY_PY_MACHINE_SPI_MSB (0) +#define MICROPY_PY_MACHINE_SPI_LSB (1) +#endif + void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in; uint32_t delay_half = self->delay_half; @@ -93,6 +99,49 @@ void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint } } +/******************************************************************************/ +// MicroPython bindings for generic machine.SPI + +STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); + +mp_obj_t mp_machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // check the id argument, if given + if (n_args > 0) { + if (args[0] != MP_OBJ_NEW_SMALL_INT(-1)) { + #if defined(MICROPY_PY_MACHINE_SPI_MAKE_NEW) + // dispatch to port-specific constructor + extern mp_obj_t MICROPY_PY_MACHINE_SPI_MAKE_NEW(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); + return MICROPY_PY_MACHINE_SPI_MAKE_NEW(type, n_args, n_kw, args); + #else + mp_raise_ValueError("invalid SPI peripheral"); + #endif + } + --n_args; + ++args; + } + + // software SPI + return mp_machine_soft_spi_make_new(type, n_args, n_kw, args); +} + +STATIC mp_obj_t machine_spi_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]); + mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol; + spi_p->init(s, n_args - 1, args + 1, kw_args); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_init_obj, 1, machine_spi_init); + +STATIC mp_obj_t machine_spi_deinit(mp_obj_t self) { + mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self); + mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol; + if (spi_p->deinit != NULL) { + spi_p->deinit(s); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_spi_deinit_obj, machine_spi_deinit); + STATIC void mp_machine_spi_transfer(mp_obj_t self, size_t len, const void *src, void *dest) { mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self); mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol; @@ -138,4 +187,159 @@ STATIC mp_obj_t mp_machine_spi_write_readinto(mp_obj_t self, mp_obj_t wr_buf, mp } MP_DEFINE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj, mp_machine_spi_write_readinto); +STATIC const mp_rom_map_elem_t machine_spi_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_spi_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_spi_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_machine_spi_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_machine_spi_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_machine_spi_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&mp_machine_spi_write_readinto_obj) }, + + { MP_ROM_QSTR(MP_QSTR_MSB), MP_ROM_INT(MICROPY_PY_MACHINE_SPI_MSB) }, + { MP_ROM_QSTR(MP_QSTR_LSB), MP_ROM_INT(MICROPY_PY_MACHINE_SPI_LSB) }, +}; + +MP_DEFINE_CONST_DICT(mp_machine_spi_locals_dict, machine_spi_locals_dict_table); + +/******************************************************************************/ +// Implementation of soft SPI + +STATIC uint32_t baudrate_from_delay_half(uint32_t delay_half) { + #ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY + if (delay_half == MICROPY_PY_MACHINE_SPI_MIN_DELAY) { + return MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE; + } else + #endif + { + return 500000 / delay_half; + } +} + +STATIC uint32_t baudrate_to_delay_half(uint32_t baudrate) { + #ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY + if (baudrate >= MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE) { + return MICROPY_PY_MACHINE_SPI_MIN_DELAY; + } else + #endif + { + uint32_t delay_half = 500000 / baudrate; + // round delay_half up so that: actual_baudrate <= requested_baudrate + if (500000 % baudrate != 0) { + delay_half += 1; + } + return delay_half; + } +} + +STATIC void mp_machine_soft_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + mp_machine_soft_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "SoftSPI(baudrate=%u, polarity=%u, phase=%u," + " sck=" MP_HAL_PIN_FMT ", mosi=" MP_HAL_PIN_FMT ", miso=" MP_HAL_PIN_FMT ")", + baudrate_from_delay_half(self->delay_half), self->polarity, self->phase, + mp_hal_pin_name(self->sck), mp_hal_pin_name(self->mosi), mp_hal_pin_name(self->miso)); +} + +STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MICROPY_PY_MACHINE_SPI_MSB} }, + { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // create new object + mp_machine_soft_spi_obj_t *self = m_new_obj(mp_machine_soft_spi_obj_t); + self->base.type = &mp_machine_soft_spi_type; + + // set parameters + self->delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int); + self->polarity = args[ARG_polarity].u_int; + self->phase = args[ARG_phase].u_int; + if (args[ARG_bits].u_int != 8) { + mp_raise_ValueError("bits must be 8"); + } + if (args[ARG_firstbit].u_int != MICROPY_PY_MACHINE_SPI_MSB) { + mp_raise_ValueError("firstbit must be MSB"); + } + if (args[ARG_sck].u_obj == MP_OBJ_NULL + || args[ARG_mosi].u_obj == MP_OBJ_NULL + || args[ARG_miso].u_obj == MP_OBJ_NULL) { + mp_raise_ValueError("must specify all of sck/mosi/miso"); + } + self->sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj); + self->mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj); + self->miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj); + + // configure pins + mp_hal_pin_write(self->sck, self->polarity); + mp_hal_pin_output(self->sck); + mp_hal_pin_output(self->mosi); + mp_hal_pin_input(self->miso); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC void mp_machine_soft_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in; + + enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_sck, ARG_mosi, ARG_miso }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_polarity, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_phase, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if (args[ARG_baudrate].u_int != -1) { + self->delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int); + } + if (args[ARG_polarity].u_int != -1) { + self->polarity = args[ARG_polarity].u_int; + } + if (args[ARG_phase].u_int != -1) { + self->phase = args[ARG_phase].u_int; + } + if (args[ARG_sck].u_obj != MP_OBJ_NULL) { + self->sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj); + } + if (args[ARG_mosi].u_obj != MP_OBJ_NULL) { + self->mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj); + } + if (args[ARG_miso].u_obj != MP_OBJ_NULL) { + self->miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj); + } + + // configure pins + mp_hal_pin_write(self->sck, self->polarity); + mp_hal_pin_output(self->sck); + mp_hal_pin_output(self->mosi); + mp_hal_pin_input(self->miso); +} + +STATIC const mp_machine_spi_p_t mp_machine_soft_spi_p = { + .init = mp_machine_soft_spi_init, + .deinit = NULL, + .transfer = mp_machine_soft_spi_transfer, +}; + +const mp_obj_type_t mp_machine_soft_spi_type = { + { &mp_type_type }, + .name = MP_QSTR_SoftSPI, + .print = mp_machine_soft_spi_print, + .make_new = mp_machine_spi_make_new, // delegate to master constructor + .protocol = &mp_machine_soft_spi_p, + .locals_dict = (mp_obj_dict_t*)&mp_machine_spi_locals_dict, +}; + #endif // MICROPY_PY_MACHINE_SPI diff --git a/extmod/machine_spi.h b/extmod/machine_spi.h index 62f42a8851..88a3e19f41 100644 --- a/extmod/machine_spi.h +++ b/extmod/machine_spi.h @@ -32,6 +32,8 @@ // SPI protocol typedef struct _mp_machine_spi_p_t { + void (*init)(mp_obj_base_t *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); + void (*deinit)(mp_obj_base_t *obj); // can be NULL void (*transfer)(mp_obj_base_t *obj, size_t len, const uint8_t *src, uint8_t *dest); } mp_machine_spi_p_t; @@ -45,8 +47,13 @@ typedef struct _mp_machine_soft_spi_obj_t { mp_hal_pin_obj_t miso; } mp_machine_soft_spi_obj_t; +extern const mp_obj_type_t mp_machine_soft_spi_type; +extern const mp_obj_dict_t mp_machine_spi_locals_dict; + void mp_machine_soft_spi_transfer(mp_obj_base_t *self, size_t len, const uint8_t *src, uint8_t *dest); +mp_obj_t mp_machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); + MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj); diff --git a/extmod/modbtree.c b/extmod/modbtree.c index f5ec5bfca1..bb75845b1b 100644 --- a/extmod/modbtree.c +++ b/extmod/modbtree.c @@ -81,6 +81,12 @@ STATIC void btree_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind mp_printf(print, "<btree %p>", self->db); } +STATIC mp_obj_t btree_flush(mp_obj_t self_in) { + mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(__bt_sync(self->db, 0)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(btree_flush_obj, btree_flush); + STATIC mp_obj_t btree_close(mp_obj_t self_in) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in); return MP_OBJ_NEW_SMALL_INT(__bt_close(self->db)); @@ -314,6 +320,7 @@ STATIC mp_obj_t btree_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) STATIC const mp_rom_map_elem_t btree_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&btree_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&btree_flush_obj) }, { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&btree_get_obj) }, { MP_ROM_QSTR(MP_QSTR_put), MP_ROM_PTR(&btree_put_obj) }, { MP_ROM_QSTR(MP_QSTR_seq), MP_ROM_PTR(&btree_seq_obj) }, diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index cd7f1c5e4b..d6e686e076 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -35,18 +35,106 @@ #include "stmhal/font_petme128_8x8.h" -// 1-bit frame buffer, each byte is a column of 8 pixels -typedef struct _mp_obj_framebuf1_t { +typedef struct _mp_obj_framebuf_t { mp_obj_base_t base; - uint8_t *buf; + mp_obj_t buf_obj; // need to store this to prevent GC from reclaiming buf + void *buf; uint16_t width, height, stride; -} mp_obj_framebuf1_t; + uint8_t format; +} mp_obj_framebuf_t; -STATIC mp_obj_t framebuf1_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 3, 4, false); +typedef void (*setpixel_t)(const mp_obj_framebuf_t*, int, int, uint32_t); +typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t*, int, int); +typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, int, int, int, int, uint32_t); - mp_obj_framebuf1_t *o = m_new_obj(mp_obj_framebuf1_t); +typedef struct _mp_framebuf_p_t { + setpixel_t setpixel; + getpixel_t getpixel; + fill_rect_t fill_rect; +} mp_framebuf_p_t; + +// Functions for MVLSB format + +STATIC void mvlsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) { + size_t index = (y >> 3) * fb->stride + x; + uint8_t offset = y & 0x07; + ((uint8_t*)fb->buf)[index] = (((uint8_t*)fb->buf)[index] & ~(0x01 << offset)) | ((color != 0) << offset); +} + +STATIC uint32_t mvlsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { + return (((uint8_t*)fb->buf)[(y >> 3) * fb->stride + x] >> (y & 0x07)) & 0x01; +} + +STATIC void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { + while (h--) { + uint8_t *b = &((uint8_t*)fb->buf)[(y >> 3) * fb->stride + x]; + uint8_t offset = y & 0x07; + for (int ww = w; ww; --ww) { + *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset); + ++b; + } + ++y; + } +} + +// Functions for RGB565 format + +STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) { + ((uint16_t*)fb->buf)[x + y * fb->stride] = color; +} + +STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { + return ((uint16_t*)fb->buf)[x + y * fb->stride]; +} + +STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t colour) { + uint16_t *b = &((uint16_t*)fb->buf)[x + y * fb->stride]; + while (h--) { + for (int ww = w; ww; --ww) { + *b++ = colour; + } + b += fb->stride - w; + } +} + +// constants for formats +#define FRAMEBUF_MVLSB (0) +#define FRAMEBUF_RGB565 (1) + +STATIC mp_framebuf_p_t formats[] = { + [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect}, + [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, +}; + +static inline void setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) { + formats[fb->format].setpixel(fb, x, y, color); +} + +static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, int x, int y) { + return formats[fb->format].getpixel(fb, x, y); +} + +STATIC void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { + if (x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) { + // No operation needed. + return; + } + + // clip to the framebuffer + int xend = MIN(fb->width, x + w); + int yend = MIN(fb->height, y + h); + x = MAX(x, 0); + y = MAX(y, 0); + + formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col); +} + +STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 4, 5, false); + + mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t); o->base.type = type; + o->buf_obj = args[0]; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE); @@ -54,98 +142,263 @@ STATIC mp_obj_t framebuf1_make_new(const mp_obj_type_t *type, size_t n_args, siz o->width = mp_obj_get_int(args[1]); o->height = mp_obj_get_int(args[2]); - o->stride = o->width; - if (n_args >= 4) { - o->stride = mp_obj_get_int(args[3]); + o->format = mp_obj_get_int(args[3]); + if (n_args >= 5) { + o->stride = mp_obj_get_int(args[4]); + } else { + o->stride = o->width; + } + + switch (o->format) { + case FRAMEBUF_MVLSB: + case FRAMEBUF_RGB565: + break; + default: + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "invalid format")); } return MP_OBJ_FROM_PTR(o); } -STATIC mp_obj_t framebuf1_fill(mp_obj_t self_in, mp_obj_t col_in) { - mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(self_in); +STATIC mp_int_t framebuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + (void)flags; + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); + bufinfo->buf = self->buf; + bufinfo->len = self->stride * self->height * (self->format == FRAMEBUF_RGB565 ? 2 : 1); + bufinfo->typecode = 'B'; // view framebuf as bytes + return 0; +} + +STATIC mp_obj_t framebuf_fill(mp_obj_t self_in, mp_obj_t col_in) { + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t col = mp_obj_get_int(col_in); - if (col) { - col = 0xff; - } - int end = (self->height + 7) >> 3; - for (int y = 0; y < end; ++y) { - memset(self->buf + y * self->stride, col, self->width); - } + formats[self->format].fill_rect(self, 0, 0, self->width, self->height, col); return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf1_fill_obj, framebuf1_fill); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill); + +STATIC mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args) { + (void)n_args; -STATIC mp_obj_t framebuf1_pixel(size_t n_args, const mp_obj_t *args) { - mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(args[0]); + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); + mp_int_t x = mp_obj_get_int(args[1]); + mp_int_t y = mp_obj_get_int(args[2]); + mp_int_t width = mp_obj_get_int(args[3]); + mp_int_t height = mp_obj_get_int(args[4]); + mp_int_t color = mp_obj_get_int(args[5]); + + fill_rect(self, x, y, width, height, color); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect); + +STATIC mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args) { + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); mp_int_t x = mp_obj_get_int(args[1]); mp_int_t y = mp_obj_get_int(args[2]); if (0 <= x && x < self->width && 0 <= y && y < self->height) { - int index = (y / 8) * self->stride + x; if (n_args == 3) { // get - return MP_OBJ_NEW_SMALL_INT((self->buf[index] >> (y & 7)) & 1); + return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y)); } else { // set - if (mp_obj_get_int(args[3])) { - self->buf[index] |= (1 << (y & 7)); - } else { - self->buf[index] &= ~(1 << (y & 7)); - } + setpixel(self, x, y, mp_obj_get_int(args[3])); } } return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf1_pixel_obj, 3, 4, framebuf1_pixel); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 4, framebuf_pixel); -STATIC mp_obj_t framebuf1_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) { - mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(self_in); - mp_int_t xstep = mp_obj_get_int(xstep_in); - mp_int_t ystep = mp_obj_get_int(ystep_in); - int end = (self->height + 7) >> 3; - if (ystep > 0) { - for (int y = end; y > 0;) { - --y; - for (int x = 0; x < self->width; ++x) { - int prev = 0; - if (y > 0) { - prev = (self->buf[(y - 1) * self->stride + x] >> (8 - ystep)) & ((1 << ystep) - 1); - } - self->buf[y * self->stride + x] = (self->buf[y * self->stride + x] << ystep) | prev; - } +STATIC mp_obj_t framebuf_hline(size_t n_args, const mp_obj_t *args) { + (void)n_args; + + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); + mp_int_t x = mp_obj_get_int(args[1]); + mp_int_t y = mp_obj_get_int(args[2]); + mp_int_t w = mp_obj_get_int(args[3]); + mp_int_t col = mp_obj_get_int(args[4]); + + fill_rect(self, x, y, w, 1, col); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_hline_obj, 5, 5, framebuf_hline); + +STATIC mp_obj_t framebuf_vline(size_t n_args, const mp_obj_t *args) { + (void)n_args; + + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); + mp_int_t x = mp_obj_get_int(args[1]); + mp_int_t y = mp_obj_get_int(args[2]); + mp_int_t h = mp_obj_get_int(args[3]); + mp_int_t col = mp_obj_get_int(args[4]); + + fill_rect(self, x, y, 1, h, col); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_vline_obj, 5, 5, framebuf_vline); + +STATIC mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args) { + (void)n_args; + + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); + mp_int_t x = mp_obj_get_int(args[1]); + mp_int_t y = mp_obj_get_int(args[2]); + mp_int_t w = mp_obj_get_int(args[3]); + mp_int_t h = mp_obj_get_int(args[4]); + mp_int_t col = mp_obj_get_int(args[5]); + + fill_rect(self, x, y, w, 1, col); + fill_rect(self, x, y + h- 1, w, 1, col); + fill_rect(self, x, y, 1, h, col); + fill_rect(self, x + w- 1, y, 1, h, col); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 6, framebuf_rect); + +STATIC mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args) { + (void)n_args; + + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); + mp_int_t x1 = mp_obj_get_int(args[1]); + mp_int_t y1 = mp_obj_get_int(args[2]); + mp_int_t x2 = mp_obj_get_int(args[3]); + mp_int_t y2 = mp_obj_get_int(args[4]); + mp_int_t col = mp_obj_get_int(args[5]); + + mp_int_t dx = x2 - x1; + mp_int_t sx; + if (dx > 0) { + sx = 1; + } else { + dx = -dx; + sx = -1; + } + + mp_int_t dy = y2 - y1; + mp_int_t sy; + if (dy > 0) { + sy = 1; + } else { + dy = -dy; + sy = -1; + } + + bool steep; + if (dy > dx) { + mp_int_t temp; + temp = x1; x1 = y1; y1 = temp; + temp = dx; dx = dy; dy = temp; + temp = sx; sx = sy; sy = temp; + steep = true; + } else { + steep = false; + } + + mp_int_t e = 2 * dy - dx; + for (mp_int_t i = 0; i < dx; ++i) { + if (steep) { + setpixel(self, y1, x1, col); + } else { + setpixel(self, x1, y1, col); } - } else if (ystep < 0) { - for (int y = 0; y < end; ++y) { - for (int x = 0; x < self->width; ++x) { - int prev = 0; - if (y + 1 < end) { - prev = self->buf[(y + 1) * self->stride + x] << (8 + ystep); - } - self->buf[y * self->stride + x] = (self->buf[y * self->stride + x] >> -ystep) | prev; - } + while (e >= 0) { + y1 += sy; + e -= 2 * dx; } + x1 += sx; + e += 2 * dy; } - if (xstep < 0) { - for (int y = 0; y < end; ++y) { - for (int x = 0; x < self->width + xstep; ++x) { - self->buf[y * self->stride + x] = self->buf[y * self->stride + x - xstep]; + + setpixel(self, x2, y2, col); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_line); + +STATIC mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args) { + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); + mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(args[1]); + mp_int_t x = mp_obj_get_int(args[2]); + mp_int_t y = mp_obj_get_int(args[3]); + mp_int_t key = -1; + if (n_args > 4) { + key = mp_obj_get_int(args[4]); + } + + if ( + (x >= self->width) || + (y >= self->height) || + (-x >= source->width) || + (-y >= source->height) + ) { + // Out of bounds, no-op. + return mp_const_none; + } + + // Clip. + int x0 = MAX(0, x); + int y0 = MAX(0, y); + int x1 = MAX(0, -x); + int y1 = MAX(0, -y); + int x0end = MIN(self->width, x + source->width); + int y0end = MIN(self->height, y + source->height); + uint32_t color; + + for (; y0 < y0end; ++y0) { + int cx1 = x1; + for (int cx0 = x0; cx0 < x0end; ++cx0) { + color = getpixel(source, cx1, y1); + if (color != key) { + setpixel(self, cx0, y0, color); } + ++cx1; } - } else if (xstep > 0) { - for (int y = 0; y < end; ++y) { - for (int x = self->width - 1; x >= xstep; --x) { - self->buf[y * self->stride + x] = self->buf[y * self->stride + x - xstep]; - } + ++y1; + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 5, framebuf_blit); + +STATIC mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) { + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t xstep = mp_obj_get_int(xstep_in); + mp_int_t ystep = mp_obj_get_int(ystep_in); + int sx, y, xend, yend, dx, dy; + if (xstep < 0) { + sx = 0; + xend = self->width + xstep; + dx = 1; + } else { + sx = self->width - 1; + xend = xstep - 1; + dx = -1; + } + if (ystep < 0) { + y = 0; + yend = self->height + ystep; + dy = 1; + } else { + y = self->height - 1; + yend = ystep - 1; + dy = -1; + } + for (; y != yend; y += dy) { + for (int x = sx; x != xend; x += dx) { + setpixel(self, x, y, getpixel(self, x - xstep, y - ystep)); } } - // TODO: Should we clear the margin created by scrolling? return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf1_scroll_obj, framebuf1_scroll); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf_scroll_obj, framebuf_scroll); -STATIC mp_obj_t framebuf1_text(size_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args) { // extract arguments - mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(args[0]); + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); const char *str = mp_obj_str_get_str(args[1]); mp_int_t x0 = mp_obj_get_int(args[2]); mp_int_t y0 = mp_obj_get_int(args[3]); @@ -170,43 +423,67 @@ STATIC mp_obj_t framebuf1_text(size_t n_args, const mp_obj_t *args) { for (int y = y0; vline_data; vline_data >>= 1, y++) { // scan over vertical column if (vline_data & 1) { // only draw if pixel set if (0 <= y && y < self->height) { // clip y - uint byte_pos = x0 + self->stride * ((uint)y >> 3); - if (col == 0) { - // clear pixel - self->buf[byte_pos] &= ~(1 << (y & 7)); - } else { - // set pixel - self->buf[byte_pos] |= 1 << (y & 7); - } + setpixel(self, x0, y, col); } } } } } } - return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf1_text_obj, 4, 5, framebuf1_text); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 5, framebuf_text); -STATIC const mp_rom_map_elem_t framebuf1_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf1_fill_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf1_pixel_obj) }, - { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf1_scroll_obj) }, - { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf1_text_obj) }, +STATIC const mp_rom_map_elem_t framebuf_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf_fill_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&framebuf_fill_rect_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf_pixel_obj) }, + { MP_ROM_QSTR(MP_QSTR_hline), MP_ROM_PTR(&framebuf_hline_obj) }, + { MP_ROM_QSTR(MP_QSTR_vline), MP_ROM_PTR(&framebuf_vline_obj) }, + { MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&framebuf_rect_obj) }, + { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&framebuf_line_obj) }, + { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&framebuf_blit_obj) }, + { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf_scroll_obj) }, + { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf_text_obj) }, }; -STATIC MP_DEFINE_CONST_DICT(framebuf1_locals_dict, framebuf1_locals_dict_table); +STATIC MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table); -STATIC const mp_obj_type_t mp_type_framebuf1 = { +STATIC const mp_obj_type_t mp_type_framebuf = { { &mp_type_type }, - .name = MP_QSTR_FrameBuffer1, - .make_new = framebuf1_make_new, - .locals_dict = (mp_obj_t)&framebuf1_locals_dict, + .name = MP_QSTR_FrameBuffer, + .make_new = framebuf_make_new, + .buffer_p = { .get_buffer = framebuf_get_buffer }, + .locals_dict = (mp_obj_t)&framebuf_locals_dict, }; +// this factory function is provided for backwards compatibility with old FrameBuffer1 class +STATIC mp_obj_t legacy_framebuffer1(size_t n_args, const mp_obj_t *args) { + mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t); + o->base.type = &mp_type_framebuf; + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE); + o->buf = bufinfo.buf; + + o->width = mp_obj_get_int(args[1]); + o->height = mp_obj_get_int(args[2]); + o->format = FRAMEBUF_MVLSB; + if (n_args >= 4) { + o->stride = mp_obj_get_int(args[3]); + } else { + o->stride = o->width; + } + + return MP_OBJ_FROM_PTR(o); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(legacy_framebuffer1_obj, 3, 4, legacy_framebuffer1); + STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_framebuf) }, - { MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&mp_type_framebuf1) }, + { MP_ROM_QSTR(MP_QSTR_FrameBuffer), MP_ROM_PTR(&mp_type_framebuf) }, + { MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&legacy_framebuffer1_obj) }, + { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB) }, + { MP_ROM_QSTR(MP_QSTR_RGB565), MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565) }, }; STATIC MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table); diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 11ba6e4231..62699bd417 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -114,7 +114,7 @@ u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len) { } // constructor lwip.slip(device=integer, iplocal=string, ipremote=string) -STATIC mp_obj_t lwip_slip_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t lwip_slip_make_new(mp_obj_t type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 3, 3, false); lwip_slip_obj.base.type = &lwip_slip_type; @@ -578,8 +578,7 @@ STATIC void lwip_socket_print(const mp_print_t *print, mp_obj_t self_in, mp_prin } // FIXME: Only supports two arguments at present -STATIC mp_obj_t lwip_socket_make_new(const mp_obj_type_t *type, mp_uint_t n_args, - mp_uint_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t lwip_socket_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 0, 4, false); lwip_socket_obj_t *socket = m_new_obj_with_finaliser(lwip_socket_obj_t); @@ -1127,6 +1126,34 @@ STATIC mp_uint_t lwip_socket_write(mp_obj_t self_in, const void *buf, mp_uint_t return MP_STREAM_ERROR; } +STATIC mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + lwip_socket_obj_t *socket = self_in; + mp_uint_t ret; + + if (request == MP_STREAM_POLL) { + uintptr_t flags = arg; + ret = 0; + + if (flags & MP_STREAM_POLL_RD && socket->incoming.pbuf != NULL) { + ret |= MP_STREAM_POLL_RD; + } + + if (flags & MP_STREAM_POLL_WR && tcp_sndbuf(socket->pcb.tcp) > 0) { + ret |= MP_STREAM_POLL_WR; + } + + if (flags & MP_STREAM_POLL_HUP && socket->state == STATE_PEER_CLOSED) { + ret |= MP_STREAM_POLL_HUP; + } + + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + + return ret; +} + STATIC const mp_map_elem_t lwip_socket_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&lwip_socket_close_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&lwip_socket_close_obj }, @@ -1153,6 +1180,7 @@ STATIC MP_DEFINE_CONST_DICT(lwip_socket_locals_dict, lwip_socket_locals_dict_tab STATIC const mp_stream_p_t lwip_socket_stream_p = { .read = lwip_socket_read, .write = lwip_socket_write, + .ioctl = lwip_socket_ioctl, }; STATIC const mp_obj_type_t lwip_socket_type = { diff --git a/extmod/modurandom.c b/extmod/modurandom.c index 995b0a2665..12e56741e3 100644 --- a/extmod/modurandom.c +++ b/extmod/modurandom.c @@ -206,9 +206,11 @@ STATIC const mp_rom_map_elem_t mp_module_urandom_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_randrange), MP_ROM_PTR(&mod_urandom_randrange_obj) }, { MP_ROM_QSTR(MP_QSTR_randint), MP_ROM_PTR(&mod_urandom_randint_obj) }, { MP_ROM_QSTR(MP_QSTR_choice), MP_ROM_PTR(&mod_urandom_choice_obj) }, + #if MICROPY_PY_BUILTINS_FLOAT { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&mod_urandom_random_obj) }, { MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&mod_urandom_uniform_obj) }, #endif + #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_urandom_globals, mp_module_urandom_globals_table); diff --git a/extmod/moduselect.c b/extmod/moduselect.c new file mode 100644 index 0000000000..5b00f6badd --- /dev/null +++ b/extmod/moduselect.c @@ -0,0 +1,312 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mpconfig.h" +#if MICROPY_PY_USELECT + +#include <stdio.h> + +#include "py/runtime.h" +#include "py/obj.h" +#include "py/objlist.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "py/mphal.h" + +// Flags for poll() +#define FLAG_ONESHOT (1) + +/// \module select - Provides select function to wait for events on a stream +/// +/// This module provides the select function. + +typedef struct _poll_obj_t { + mp_obj_t obj; + mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, mp_uint_t arg, int *errcode); + mp_uint_t flags; + mp_uint_t flags_ret; +} poll_obj_t; + +STATIC void poll_map_add(mp_map_t *poll_map, const mp_obj_t *obj, mp_uint_t obj_len, mp_uint_t flags, bool or_flags) { + for (mp_uint_t i = 0; i < obj_len; i++) { + mp_map_elem_t *elem = mp_map_lookup(poll_map, mp_obj_id(obj[i]), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + if (elem->value == NULL) { + // object not found; get its ioctl and add it to the poll list + const mp_stream_p_t *stream_p = mp_get_stream_raise(obj[i], MP_STREAM_OP_IOCTL); + poll_obj_t *poll_obj = m_new_obj(poll_obj_t); + poll_obj->obj = obj[i]; + poll_obj->ioctl = stream_p->ioctl; + poll_obj->flags = flags; + poll_obj->flags_ret = 0; + elem->value = poll_obj; + } else { + // object exists; update its flags + if (or_flags) { + ((poll_obj_t*)elem->value)->flags |= flags; + } else { + ((poll_obj_t*)elem->value)->flags = flags; + } + } + } +} + +// poll each object in the map +STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, mp_uint_t *rwx_num) { + mp_uint_t n_ready = 0; + for (mp_uint_t i = 0; i < poll_map->alloc; ++i) { + if (!MP_MAP_SLOT_IS_FILLED(poll_map, i)) { + continue; + } + + poll_obj_t *poll_obj = (poll_obj_t*)poll_map->table[i].value; + int errcode; + mp_int_t ret = poll_obj->ioctl(poll_obj->obj, MP_STREAM_POLL, poll_obj->flags, &errcode); + poll_obj->flags_ret = ret; + + if (ret == -1) { + // error doing ioctl + mp_raise_OSError(errcode); + } + + if (ret != 0) { + // object is ready + n_ready += 1; + if (rwx_num != NULL) { + if (ret & MP_STREAM_POLL_RD) { + rwx_num[0] += 1; + } + if (ret & MP_STREAM_POLL_WR) { + rwx_num[1] += 1; + } + if ((ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) { + rwx_num[2] += 1; + } + } + } + } + return n_ready; +} + +/// \function select(rlist, wlist, xlist[, timeout]) +STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) { + // get array data from tuple/list arguments + mp_uint_t rwx_len[3]; + mp_obj_t *r_array, *w_array, *x_array; + mp_obj_get_array(args[0], &rwx_len[0], &r_array); + mp_obj_get_array(args[1], &rwx_len[1], &w_array); + mp_obj_get_array(args[2], &rwx_len[2], &x_array); + + // get timeout + mp_uint_t timeout = -1; + if (n_args == 4) { + if (args[3] != mp_const_none) { + #if MICROPY_PY_BUILTINS_FLOAT + float timeout_f = mp_obj_get_float(args[3]); + if (timeout_f >= 0) { + timeout = (mp_uint_t)(timeout_f * 1000); + } + #else + timeout = mp_obj_get_int(args[3]) * 1000; + #endif + } + } + + // merge separate lists and get the ioctl function for each object + mp_map_t poll_map; + mp_map_init(&poll_map, rwx_len[0] + rwx_len[1] + rwx_len[2]); + poll_map_add(&poll_map, r_array, rwx_len[0], MP_STREAM_POLL_RD, true); + poll_map_add(&poll_map, w_array, rwx_len[1], MP_STREAM_POLL_WR, true); + poll_map_add(&poll_map, x_array, rwx_len[2], MP_STREAM_POLL_ERR | MP_STREAM_POLL_HUP, true); + + mp_uint_t start_tick = mp_hal_ticks_ms(); + rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; + for (;;) { + // poll the objects + mp_uint_t n_ready = poll_map_poll(&poll_map, rwx_len); + + if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) { + // one or more objects are ready, or we had a timeout + mp_obj_t list_array[3]; + list_array[0] = mp_obj_new_list(rwx_len[0], NULL); + list_array[1] = mp_obj_new_list(rwx_len[1], NULL); + list_array[2] = mp_obj_new_list(rwx_len[2], NULL); + rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; + for (mp_uint_t i = 0; i < poll_map.alloc; ++i) { + if (!MP_MAP_SLOT_IS_FILLED(&poll_map, i)) { + continue; + } + poll_obj_t *poll_obj = (poll_obj_t*)poll_map.table[i].value; + if (poll_obj->flags_ret & MP_STREAM_POLL_RD) { + ((mp_obj_list_t*)list_array[0])->items[rwx_len[0]++] = poll_obj->obj; + } + if (poll_obj->flags_ret & MP_STREAM_POLL_WR) { + ((mp_obj_list_t*)list_array[1])->items[rwx_len[1]++] = poll_obj->obj; + } + if ((poll_obj->flags_ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) { + ((mp_obj_list_t*)list_array[2])->items[rwx_len[2]++] = poll_obj->obj; + } + } + mp_map_deinit(&poll_map); + return mp_obj_new_tuple(3, list_array); + } + MICROPY_EVENT_POLL_HOOK + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select); + +/// \class Poll - poll class + +typedef struct _mp_obj_poll_t { + mp_obj_base_t base; + mp_map_t poll_map; +} mp_obj_poll_t; + +/// \method register(obj[, eventmask]) +STATIC mp_obj_t poll_register(uint n_args, const mp_obj_t *args) { + mp_obj_poll_t *self = args[0]; + mp_uint_t flags; + if (n_args == 3) { + flags = mp_obj_get_int(args[2]); + } else { + flags = MP_STREAM_POLL_RD | MP_STREAM_POLL_WR; + } + poll_map_add(&self->poll_map, &args[1], 1, flags, false); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register); + +/// \method unregister(obj) +STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) { + mp_obj_poll_t *self = self_in; + mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP_REMOVE_IF_FOUND); + // TODO raise KeyError if obj didn't exist in map + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister); + +/// \method modify(obj, eventmask) +STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) { + mp_obj_poll_t *self = self_in; + mp_map_elem_t *elem = mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP); + if (elem == NULL) { + mp_raise_OSError(MP_ENOENT); + } + ((poll_obj_t*)elem->value)->flags = mp_obj_get_int(eventmask_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(poll_modify_obj, poll_modify); + +/// \method poll([timeout]) +/// Timeout is in milliseconds. +STATIC mp_obj_t poll_poll(uint n_args, const mp_obj_t *args) { + mp_obj_poll_t *self = args[0]; + + // work out timeout (its given already in ms) + mp_uint_t timeout = -1; + int flags = 0; + if (n_args >= 2) { + if (args[1] != mp_const_none) { + mp_int_t timeout_i = mp_obj_get_int(args[1]); + if (timeout_i >= 0) { + timeout = timeout_i; + } + } + if (n_args >= 3) { + flags = mp_obj_get_int(args[2]); + } + } + + mp_uint_t start_tick = mp_hal_ticks_ms(); + for (;;) { + // poll the objects + mp_uint_t n_ready = poll_map_poll(&self->poll_map, NULL); + + if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) { + // one or more objects are ready, or we had a timeout + mp_obj_list_t *ret_list = mp_obj_new_list(n_ready, NULL); + n_ready = 0; + for (mp_uint_t i = 0; i < self->poll_map.alloc; ++i) { + if (!MP_MAP_SLOT_IS_FILLED(&self->poll_map, i)) { + continue; + } + poll_obj_t *poll_obj = (poll_obj_t*)self->poll_map.table[i].value; + if (poll_obj->flags_ret != 0) { + mp_obj_t tuple[2] = {poll_obj->obj, MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret)}; + ret_list->items[n_ready++] = mp_obj_new_tuple(2, tuple); + if (flags & FLAG_ONESHOT) { + // Don't poll next time, until new event flags will be set explicitly + poll_obj->flags = 0; + } + } + } + return ret_list; + } + MICROPY_EVENT_POLL_HOOK + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 3, poll_poll); + +STATIC const mp_map_elem_t poll_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_register), (mp_obj_t)&poll_register_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_unregister), (mp_obj_t)&poll_unregister_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_modify), (mp_obj_t)&poll_modify_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_poll), (mp_obj_t)&poll_poll_obj }, +}; +STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table); + +STATIC const mp_obj_type_t mp_type_poll = { + { &mp_type_type }, + .name = MP_QSTR_poll, + .locals_dict = (mp_obj_t)&poll_locals_dict, +}; + +/// \function poll() +STATIC mp_obj_t select_poll(void) { + mp_obj_poll_t *poll = m_new_obj(mp_obj_poll_t); + poll->base.type = &mp_type_poll; + mp_map_init(&poll->poll_map, 0); + return poll; +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_select_poll_obj, select_poll); + +STATIC const mp_map_elem_t mp_module_select_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_uselect) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_select), (mp_obj_t)&mp_select_select_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_poll), (mp_obj_t)&mp_select_poll_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_POLLIN), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_RD) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_POLLOUT), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_WR) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_POLLERR), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_ERR) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_POLLHUP), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_HUP) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_select_globals, mp_module_select_globals_table); + +const mp_obj_module_t mp_module_uselect = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_select_globals, +}; + +#endif // MICROPY_PY_USELECT diff --git a/extmod/modussl_axtls.c b/extmod/modussl_axtls.c index 5bc69fe261..4169158216 100644 --- a/extmod/modussl_axtls.c +++ b/extmod/modussl_axtls.c @@ -142,7 +142,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, diff --git a/extmod/modussl_mbedtls.c b/extmod/modussl_mbedtls.c index 5a7a745d82..a7b8a14405 100644 --- a/extmod/modussl_mbedtls.c +++ b/extmod/modussl_mbedtls.c @@ -242,7 +242,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, diff --git a/extmod/modutimeq.c b/extmod/modutimeq.c new file mode 100644 index 0000000000..f73b39103d --- /dev/null +++ b/extmod/modutimeq.c @@ -0,0 +1,212 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * Copyright (c) 2016 Paul Sokolovsky + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <string.h> + +#include "py/nlr.h" +#include "py/objlist.h" +#include "py/runtime0.h" +#include "py/runtime.h" +#include "py/smallint.h" + +#if MICROPY_PY_UTIMEQ + +#define MODULO MICROPY_PY_UTIME_TICKS_PERIOD + +#define DEBUG 0 + +// the algorithm here is modelled on CPython's heapq.py + +struct qentry { + mp_uint_t time; + mp_obj_t callback; + mp_obj_t args; +}; + +typedef struct _mp_obj_utimeq_t { + mp_obj_base_t base; + mp_uint_t alloc; + mp_uint_t len; + struct qentry items[]; +} mp_obj_utimeq_t; + + +STATIC mp_obj_utimeq_t *get_heap(mp_obj_t heap_in) { + return MP_OBJ_TO_PTR(heap_in); +} + +STATIC bool time_less_than(struct qentry *item, struct qentry *parent) { + mp_uint_t item_tm = item->time; + mp_uint_t parent_tm = parent->time; + mp_uint_t res = parent_tm - item_tm; + if ((mp_int_t)res < 0) { + res += MODULO; + } + return res && res < (MODULO / 2); +} + +STATIC mp_obj_t utimeq_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + mp_uint_t alloc = mp_obj_get_int(args[0]); + mp_obj_utimeq_t *o = m_new_obj_var(mp_obj_utimeq_t, struct qentry, alloc); + o->base.type = type; + memset(o->items, 0, sizeof(*o->items) * alloc); + o->alloc = alloc; + o->len = 0; + return MP_OBJ_FROM_PTR(o); +} + +STATIC void heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t pos) { + struct qentry item = heap->items[pos]; + while (pos > start_pos) { + mp_uint_t parent_pos = (pos - 1) >> 1; + struct qentry *parent = &heap->items[parent_pos]; + bool lessthan = time_less_than(&item, parent); + if (lessthan) { + heap->items[pos] = *parent; + pos = parent_pos; + } else { + break; + } + } + heap->items[pos] = item; +} + +STATIC void heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) { + mp_uint_t start_pos = pos; + mp_uint_t end_pos = heap->len; + struct qentry item = heap->items[pos]; + for (mp_uint_t child_pos = 2 * pos + 1; child_pos < end_pos; child_pos = 2 * pos + 1) { + // choose right child if it's <= left child + if (child_pos + 1 < end_pos) { + bool lessthan = time_less_than(&heap->items[child_pos], &heap->items[child_pos + 1]); + if (!lessthan) { + child_pos += 1; + } + } + // bubble up the smaller child + heap->items[pos] = heap->items[child_pos]; + pos = child_pos; + } + heap->items[pos] = item; + heap_siftdown(heap, start_pos, pos); +} + +STATIC mp_obj_t mod_utimeq_heappush(size_t n_args, const mp_obj_t *args) { + (void)n_args; + mp_obj_t heap_in = args[0]; + mp_obj_utimeq_t *heap = get_heap(heap_in); + if (heap->len == heap->alloc) { + mp_raise_msg(&mp_type_IndexError, "queue overflow"); + } + mp_uint_t l = heap->len; + heap->items[l].time = MP_OBJ_SMALL_INT_VALUE(args[1]); + heap->items[l].callback = args[2]; + heap->items[l].args = args[3]; + heap_siftdown(heap, 0, heap->len); + heap->len++; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_utimeq_heappush_obj, 4, 4, mod_utimeq_heappush); + +STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) { + mp_obj_utimeq_t *heap = get_heap(heap_in); + if (heap->len == 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "empty heap")); + } + mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref); + if (!MP_OBJ_IS_TYPE(list_ref, &mp_type_list) || ret->len < 3) { + mp_raise_TypeError(""); + } + + struct qentry *item = &heap->items[0]; + ret->items[0] = MP_OBJ_NEW_SMALL_INT(item->time); + ret->items[1] = item->callback; + ret->items[2] = item->args; + heap->len -= 1; + heap->items[0] = heap->items[heap->len]; + heap->items[heap->len].callback = MP_OBJ_NULL; // so we don't retain a pointer + heap->items[heap->len].args = MP_OBJ_NULL; + if (heap->len) { + heap_siftup(heap, 0); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_utimeq_heappop_obj, mod_utimeq_heappop); + +#if DEBUG +STATIC mp_obj_t mod_utimeq_dump(mp_obj_t heap_in) { + mp_obj_utimeq_t *heap = get_heap(heap_in); + for (int i = 0; i < heap->len; i++) { + printf(UINT_FMT "\t%p\t%p\n", heap->items[i].time, + MP_OBJ_TO_PTR(heap->items[i].callback), MP_OBJ_TO_PTR(heap->items[i].args)); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_dump_obj, mod_utimeq_dump); +#endif + +STATIC mp_obj_t utimeq_unary_op(mp_uint_t op, mp_obj_t self_in) { + mp_obj_utimeq_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_BOOL: return mp_obj_new_bool(self->len != 0); + case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len); + default: return MP_OBJ_NULL; // op not supported + } +} + +STATIC const mp_rom_map_elem_t utimeq_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_push), MP_ROM_PTR(&mod_utimeq_heappush_obj) }, + { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&mod_utimeq_heappop_obj) }, + #if DEBUG + { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_utimeq_dump_obj) }, + #endif +}; + +STATIC MP_DEFINE_CONST_DICT(utimeq_locals_dict, utimeq_locals_dict_table); + +STATIC const mp_obj_type_t utimeq_type = { + { &mp_type_type }, + .name = MP_QSTR_utimeq, + .make_new = utimeq_make_new, + .unary_op = utimeq_unary_op, + .locals_dict = (void*)&utimeq_locals_dict, +}; + +STATIC const mp_rom_map_elem_t mp_module_utimeq_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utimeq) }, + { MP_ROM_QSTR(MP_QSTR_utimeq), MP_ROM_PTR(&utimeq_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_utimeq_globals, mp_module_utimeq_globals_table); + +const mp_obj_module_t mp_module_utimeq = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_utimeq_globals, +}; + +#endif //MICROPY_PY_UTIMEQ diff --git a/extmod/moduzlib.c b/extmod/moduzlib.c index c5d4c48120..a91405a93a 100644 --- a/extmod/moduzlib.c +++ b/extmod/moduzlib.c @@ -124,7 +124,6 @@ STATIC mp_uint_t decompio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *er STATIC const mp_rom_map_elem_t decompio_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, }; diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 6e827fc664..bd88bcf1b1 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -121,6 +121,12 @@ STATIC mp_obj_t fat_vfs_rename(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t path_ const char *old_path = mp_obj_str_get_str(path_in); const char *new_path = mp_obj_str_get_str(path_out); FRESULT res = f_rename(old_path, new_path); + if (res == FR_EXIST) { + // if new_path exists then try removing it (but only if it's a file) + fat_vfs_remove_internal(path_out, 0); // 0 == file attribute + // try to rename again + res = f_rename(old_path, new_path); + } if (res == FR_OK) { return mp_const_none; } else { diff --git a/extmod/vfs_fat_file.c b/extmod/vfs_fat_file.c index 76ac23685a..77848b5a53 100644 --- a/extmod/vfs_fat_file.c +++ b/extmod/vfs_fat_file.c @@ -108,15 +108,6 @@ STATIC mp_uint_t file_obj_write(mp_obj_t self_in, const void *buf, mp_uint_t siz return sz_out; } -STATIC mp_obj_t file_obj_flush(mp_obj_t self_in) { - pyb_file_obj_t *self = MP_OBJ_TO_PTR(self_in); - FRESULT res = f_sync(&self->fp); - if (res != FR_OK) { - mp_raise_OSError(fresult_to_errno_table[res]); - } - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(file_obj_flush_obj, file_obj_flush); STATIC mp_obj_t file_obj_close(mp_obj_t self_in) { pyb_file_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -164,6 +155,14 @@ STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, s->offset = f_tell(&self->fp); return 0; + } else if (request == MP_STREAM_FLUSH) { + FRESULT res = f_sync(&self->fp); + if (res != FR_OK) { + *errcode = fresult_to_errno_table[res]; + return MP_STREAM_ERROR; + } + return 0; + } else { *errcode = MP_EINVAL; return MP_STREAM_ERROR; @@ -239,12 +238,11 @@ STATIC mp_obj_t file_obj_make_new(const mp_obj_type_t *type, size_t n_args, size STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, - { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&file_obj_flush_obj) }, + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&file_obj_close_obj) }, { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) }, { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) }, diff --git a/extmod/vfs_fat_lexer.c b/extmod/vfs_fat_lexer.c deleted file mode 100644 index 91acdb830d..0000000000 --- a/extmod/vfs_fat_lexer.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "py/mpconfig.h" -// *_ADHOC part is for cc3200 port which doesn't use general uPy -// infrastructure and instead duplicates code. TODO: Resolve. -#if MICROPY_VFS_FAT || MICROPY_FSUSERMOUNT || MICROPY_FSUSERMOUNT_ADHOC - -#include "py/lexer.h" -#include "lib/fatfs/ff.h" - -typedef struct _mp_lexer_file_buf_t { - FIL fp; - byte buf[20]; - uint16_t len; - uint16_t pos; -} mp_lexer_file_buf_t; - -STATIC mp_uint_t file_buf_next_byte(mp_lexer_file_buf_t *fb) { - if (fb->pos >= fb->len) { - if (fb->len < sizeof(fb->buf)) { - return MP_LEXER_EOF; - } else { - UINT n; - f_read(&fb->fp, fb->buf, sizeof(fb->buf), &n); - if (n == 0) { - return MP_LEXER_EOF; - } - fb->len = n; - fb->pos = 0; - } - } - return fb->buf[fb->pos++]; -} - -STATIC void file_buf_close(mp_lexer_file_buf_t *fb) { - f_close(&fb->fp); - m_del_obj(mp_lexer_file_buf_t, fb); -} - -mp_lexer_t *fat_vfs_lexer_new_from_file(const char *filename); - -mp_lexer_t *fat_vfs_lexer_new_from_file(const char *filename) { - mp_lexer_file_buf_t *fb = m_new_obj_maybe(mp_lexer_file_buf_t); - if (fb == NULL) { - return NULL; - } - FRESULT res = f_open(&fb->fp, filename, FA_READ); - if (res != FR_OK) { - m_del_obj(mp_lexer_file_buf_t, fb); - return NULL; - } - UINT n; - f_read(&fb->fp, fb->buf, sizeof(fb->buf), &n); - fb->len = n; - fb->pos = 0; - return mp_lexer_new(qstr_from_str(filename), fb, (mp_lexer_stream_next_byte_t)file_buf_next_byte, (mp_lexer_stream_close_t)file_buf_close); -} - -#endif // MICROPY_VFS_FAT diff --git a/extmod/vfs_fat_reader.c b/extmod/vfs_fat_reader.c new file mode 100644 index 0000000000..7a00f18de3 --- /dev/null +++ b/extmod/vfs_fat_reader.c @@ -0,0 +1,88 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdio.h> +#include <assert.h> + +#include "py/mperrno.h" +#include "py/reader.h" + +#if MICROPY_READER_FATFS + +#include "lib/fatfs/ff.h" +#include "extmod/vfs_fat_file.h" + +typedef struct _mp_reader_fatfs_t { + FIL fp; + uint16_t len; + uint16_t pos; + byte buf[20]; +} mp_reader_fatfs_t; + +STATIC mp_uint_t mp_reader_fatfs_readbyte(void *data) { + mp_reader_fatfs_t *reader = (mp_reader_fatfs_t*)data; + if (reader->pos >= reader->len) { + if (reader->len < sizeof(reader->buf)) { + return MP_READER_EOF; + } else { + UINT n; + f_read(&reader->fp, reader->buf, sizeof(reader->buf), &n); + if (n == 0) { + return MP_READER_EOF; + } + reader->len = n; + reader->pos = 0; + } + } + return reader->buf[reader->pos++]; +} + +STATIC void mp_reader_fatfs_close(void *data) { + mp_reader_fatfs_t *reader = (mp_reader_fatfs_t*)data; + f_close(&reader->fp); + m_del_obj(mp_reader_fatfs_t, reader); +} + +int mp_reader_new_file(mp_reader_t *reader, const char *filename) { + mp_reader_fatfs_t *rf = m_new_obj_maybe(mp_reader_fatfs_t); + if (rf == NULL) { + return MP_ENOMEM; + } + FRESULT res = f_open(&rf->fp, filename, FA_READ); + if (res != FR_OK) { + return fresult_to_errno_table[res]; + } + UINT n; + f_read(&rf->fp, rf->buf, sizeof(rf->buf), &n); + rf->len = n; + rf->pos = 0; + reader->data = rf; + reader->readbyte = mp_reader_fatfs_readbyte; + reader->close = mp_reader_fatfs_close; + return 0; // success +} + +#endif |