From 39799f7564d28737fa815a3a5d370c91414998ef Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Mon, 18 Jul 2016 18:35:33 +0200 Subject: extmod/modubinascii: implement binascii.crc32 --- extmod/modubinascii.c | 11 +++++++++++ extmod/modubinascii.h | 2 ++ tests/extmod/ubinascii_crc32.py | 14 ++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 tests/extmod/ubinascii_crc32.py diff --git a/extmod/modubinascii.c b/extmod/modubinascii.c index 3cceb991f..dc41d1dc9 100644 --- a/extmod/modubinascii.c +++ b/extmod/modubinascii.c @@ -33,6 +33,7 @@ #include "py/binary.h" #include "extmod/modubinascii.h" +#include "uzlib/tinf.h" mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args) { // Second argument is for an extension to allow a separator to be used @@ -203,6 +204,15 @@ mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) { } MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj, mod_binascii_b2a_base64); +mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + uint32_t crc = (n_args > 1) ? mp_obj_get_int(args[1]) : 0; + crc = uzlib_crc32(bufinfo.buf, bufinfo.len, crc ^ 0xffffffff); + return MP_OBJ_NEW_SMALL_INT(crc ^ 0xffffffff); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj, 1, 2, mod_binascii_crc32); + #if MICROPY_PY_UBINASCII STATIC const mp_rom_map_elem_t mp_module_binascii_globals_table[] = { @@ -211,6 +221,7 @@ STATIC const mp_rom_map_elem_t mp_module_binascii_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_unhexlify), MP_ROM_PTR(&mod_binascii_unhexlify_obj) }, { MP_ROM_QSTR(MP_QSTR_a2b_base64), MP_ROM_PTR(&mod_binascii_a2b_base64_obj) }, { MP_ROM_QSTR(MP_QSTR_b2a_base64), MP_ROM_PTR(&mod_binascii_b2a_base64_obj) }, + { MP_ROM_QSTR(MP_QSTR_crc32), MP_ROM_PTR(&mod_binascii_crc32_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_binascii_globals, mp_module_binascii_globals_table); diff --git a/extmod/modubinascii.h b/extmod/modubinascii.h index 0f9b82c1c..71dd8a693 100644 --- a/extmod/modubinascii.h +++ b/extmod/modubinascii.h @@ -31,10 +31,12 @@ extern mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args); extern mp_obj_t mod_binascii_unhexlify(mp_obj_t data); extern mp_obj_t mod_binascii_a2b_base64(mp_obj_t data); extern mp_obj_t mod_binascii_b2a_base64(mp_obj_t data); +extern mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args); MP_DECLARE_CONST_FUN_OBJ(mod_binascii_hexlify_obj); MP_DECLARE_CONST_FUN_OBJ(mod_binascii_unhexlify_obj); MP_DECLARE_CONST_FUN_OBJ(mod_binascii_a2b_base64_obj); MP_DECLARE_CONST_FUN_OBJ(mod_binascii_b2a_base64_obj); +MP_DECLARE_CONST_FUN_OBJ(mod_binascii_crc32_obj); #endif /* MICROPY_EXTMOD_MODUBINASCII */ diff --git a/tests/extmod/ubinascii_crc32.py b/tests/extmod/ubinascii_crc32.py new file mode 100644 index 000000000..cb86603d5 --- /dev/null +++ b/tests/extmod/ubinascii_crc32.py @@ -0,0 +1,14 @@ +try: + import ubinascii as binascii +except ImportError: + import binascii + +print(hex(binascii.crc32(b'The quick brown fox jumps over the lazy dog'))) +print(hex(binascii.crc32(b'\x00' * 32))) +print(hex(binascii.crc32(b'\xff' * 32))) +print(hex(binascii.crc32(bytes(range(32))))) + +print(hex(binascii.crc32(b' over the lazy dog', binascii.crc32(b'The quick brown fox jumps')))) +print(hex(binascii.crc32(b'\x00' * 16, binascii.crc32(b'\x00' * 16)))) +print(hex(binascii.crc32(b'\xff' * 16, binascii.crc32(b'\xff' * 16)))) +print(hex(binascii.crc32(bytes(range(16, 32)), binascii.crc32(bytes(range(16)))))) -- cgit v1.2.3 From c4283675439d549fac09c5695aa5f8d6c3319dd5 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 24 Aug 2016 18:28:43 +0300 Subject: extmod/modubinascii: Make crc32() support configurable. Disable by default, enable in unix port. --- extmod/modubinascii.c | 4 ++++ py/mpconfig.h | 5 +++++ tests/extmod/ubinascii_crc32.py | 6 ++++++ unix/mpconfigport.h | 1 + 4 files changed, 16 insertions(+) diff --git a/extmod/modubinascii.c b/extmod/modubinascii.c index dc41d1dc9..562c754b5 100644 --- a/extmod/modubinascii.c +++ b/extmod/modubinascii.c @@ -204,6 +204,7 @@ mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) { } MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj, mod_binascii_b2a_base64); +#if MICROPY_PY_UBINASCII_CRC32 mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); @@ -212,6 +213,7 @@ mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args) { return MP_OBJ_NEW_SMALL_INT(crc ^ 0xffffffff); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj, 1, 2, mod_binascii_crc32); +#endif #if MICROPY_PY_UBINASCII @@ -221,7 +223,9 @@ STATIC const mp_rom_map_elem_t mp_module_binascii_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_unhexlify), MP_ROM_PTR(&mod_binascii_unhexlify_obj) }, { MP_ROM_QSTR(MP_QSTR_a2b_base64), MP_ROM_PTR(&mod_binascii_a2b_base64_obj) }, { MP_ROM_QSTR(MP_QSTR_b2a_base64), MP_ROM_PTR(&mod_binascii_b2a_base64_obj) }, + #if MICROPY_PY_UBINASCII_CRC32 { MP_ROM_QSTR(MP_QSTR_crc32), MP_ROM_PTR(&mod_binascii_crc32_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_binascii_globals, mp_module_binascii_globals_table); diff --git a/py/mpconfig.h b/py/mpconfig.h index 3808df743..491a53746 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -887,6 +887,11 @@ typedef double mp_float_t; #define MICROPY_PY_UBINASCII (0) #endif +// Depends on MICROPY_PY_UZLIB +#ifndef MICROPY_PY_UBINASCII_CRC32 +#define MICROPY_PY_UBINASCII_CRC32 (0) +#endif + #ifndef MICROPY_PY_URANDOM #define MICROPY_PY_URANDOM (0) #endif diff --git a/tests/extmod/ubinascii_crc32.py b/tests/extmod/ubinascii_crc32.py index cb86603d5..2c4017751 100644 --- a/tests/extmod/ubinascii_crc32.py +++ b/tests/extmod/ubinascii_crc32.py @@ -2,6 +2,12 @@ try: import ubinascii as binascii except ImportError: import binascii +try: + binascii.crc32 +except AttributeError: + print("SKIP") + import sys + sys.exit() print(hex(binascii.crc32(b'The quick brown fox jumps over the lazy dog'))) print(hex(binascii.crc32(b'\x00' * 32))) diff --git a/unix/mpconfigport.h b/unix/mpconfigport.h index 9845e1ce5..56cfaa2ac 100644 --- a/unix/mpconfigport.h +++ b/unix/mpconfigport.h @@ -117,6 +117,7 @@ #define MICROPY_PY_UHASHLIB_SHA1 (1) #endif #define MICROPY_PY_UBINASCII (1) +#define MICROPY_PY_UBINASCII_CRC32 (1) #define MICROPY_PY_URANDOM (1) #ifndef MICROPY_PY_USELECT #define MICROPY_PY_USELECT (1) -- cgit v1.2.3 From 5af6184e7251e44c17714466981c624dc7bd225d Mon Sep 17 00:00:00 2001 From: Matt Brejza Date: Tue, 23 Aug 2016 23:13:03 +0100 Subject: stmhal: Make ADC channel 16 available on L4 MCUs. --- stmhal/adc.c | 25 ++++++++++++++++++++----- stmhal/boards/make-pins.py | 6 +++++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/stmhal/adc.c b/stmhal/adc.c index 39f036407..bcf895625 100644 --- a/stmhal/adc.c +++ b/stmhal/adc.c @@ -54,7 +54,16 @@ #define ADCx (ADC1) #define ADCx_CLK_ENABLE __ADC1_CLK_ENABLE #define ADC_NUM_CHANNELS (19) -#define ADC_NUM_GPIO_CHANNELS (16) + +#if defined(MCU_SERIES_F4) || defined(MCU_SERIES_F7) +#define ADC_FIRST_GPIO_CHANNEL (0) +#define ADC_LAST_GPIO_CHANNEL (15) +#elif defined(MCU_SERIES_L4) +#define ADC_FIRST_GPIO_CHANNEL (1) +#define ADC_LAST_GPIO_CHANNEL (16) +#else +#error Unsupported processor +#endif #if defined(STM32F405xx) || defined(STM32F415xx) || \ defined(STM32F407xx) || defined(STM32F417xx) || \ @@ -124,7 +133,7 @@ STATIC void adc_init_single(pyb_obj_adc_t *adc_obj) { return; } - if (adc_obj->channel < ADC_NUM_GPIO_CHANNELS) { + if (ADC_FIRST_GPIO_CHANNEL <= adc_obj->channel && adc_obj->channel <= ADC_LAST_GPIO_CHANNEL) { // Channels 0-16 correspond to real pins. Configure the GPIO pin in // ADC mode. const pin_obj_t *pin = pin_adc1[adc_obj->channel]; @@ -253,8 +262,14 @@ STATIC mp_obj_t adc_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uin if (!is_adcx_channel(channel)) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "not a valid ADC Channel: %d", channel)); } - if (pin_adc1[channel] == NULL) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "channel %d not available on this board", channel)); + + + if (ADC_FIRST_GPIO_CHANNEL <= channel && channel <= ADC_LAST_GPIO_CHANNEL) { + // these channels correspond to physical GPIO ports so make sure they exist + if (pin_adc1[channel] == NULL) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "channel %d not available on this board", channel)); + } } pyb_obj_adc_t *o = m_new_obj(pyb_obj_adc_t); @@ -423,7 +438,7 @@ void adc_init_all(pyb_adc_all_obj_t *adc_all, uint32_t resolution) { "resolution %d not supported", resolution)); } - for (uint32_t channel = 0; channel < ADC_NUM_GPIO_CHANNELS; channel++) { + for (uint32_t channel = ADC_FIRST_GPIO_CHANNEL; channel <= ADC_LAST_GPIO_CHANNEL; ++channel) { // Channels 0-16 correspond to real pins. Configure the GPIO pin in // ADC mode. const pin_obj_t *pin = pin_adc1[channel]; diff --git a/stmhal/boards/make-pins.py b/stmhal/boards/make-pins.py index b7f484214..ae614877d 100755 --- a/stmhal/boards/make-pins.py +++ b/stmhal/boards/make-pins.py @@ -303,7 +303,9 @@ class Pins(object): def print_adc(self, adc_num): print(''); print('const pin_obj_t * const pin_adc{:d}[] = {{'.format(adc_num)) - for channel in range(16): + for channel in range(17): + if channel == 16: + print('#if defined(MCU_SERIES_L4)') adc_found = False for named_pin in self.cpu_pins: pin = named_pin.pin() @@ -314,6 +316,8 @@ class Pins(object): break if not adc_found: print(' NULL, // {:d}'.format(channel)) + if channel == 16: + print('#endif') print('};') -- cgit v1.2.3 From 78bc31e29409b5e059de0641fc7df9ed58483187 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 25 Aug 2016 14:06:40 +1000 Subject: tests/run-tests: Disable thread/stress_heap.py when running on Travis. It has reliability issues and sometimes fails on Travis (reason currently unknown). --- tests/run-tests | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-tests b/tests/run-tests index f796f2863..80536f242 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -201,6 +201,7 @@ def run_tests(pyb, tests, args): if os.getenv('TRAVIS') == 'true': skip_tests.add('basics/memoryerror.py') skip_tests.add('thread/thread_lock4.py') # has reliability issues + skip_tests.add('thread/stress_heap.py') # has reliability issues if not has_complex: skip_tests.add('float/complex1.py') -- cgit v1.2.3 From d09b6b9aa1c2ed82a4795f5abbc02f50f00927d9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 25 Aug 2016 14:10:25 +1000 Subject: esp8266/modpybrtc: Use 64-bit arithmetic when computing alarm expiry. --- esp8266/modpybrtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp8266/modpybrtc.c b/esp8266/modpybrtc.c index 968524803..500b2bc54 100644 --- a/esp8266/modpybrtc.c +++ b/esp8266/modpybrtc.c @@ -212,7 +212,7 @@ STATIC mp_obj_t pyb_rtc_alarm(mp_obj_t self_in, mp_obj_t alarm_id, mp_obj_t time } // set expiry time (in microseconds) - pyb_rtc_alarm0_expiry = pyb_rtc_get_us_since_2000() + mp_obj_get_int(time_in) * 1000; + pyb_rtc_alarm0_expiry = pyb_rtc_get_us_since_2000() + (uint64_t)mp_obj_get_int(time_in) * 1000; return mp_const_none; -- cgit v1.2.3 From a589fa3e0bdb65f9aa9ad30f720bfc93788689e9 Mon Sep 17 00:00:00 2001 From: Dave Hylands Date: Mon, 22 Aug 2016 23:22:04 -0700 Subject: stmhal: Update pin print to print new constants This updates the print function for machine.Pin to print out the new constants from the Hardware API defintion rather than the legacy definitions. --- stmhal/pin.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stmhal/pin.c b/stmhal/pin.c index e3ae0be40..9bd4e7a6d 100644 --- a/stmhal/pin.c +++ b/stmhal/pin.c @@ -199,15 +199,15 @@ STATIC void pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t if (mode == GPIO_MODE_INPUT) { mode_qst = MP_QSTR_IN; } else if (mode == GPIO_MODE_OUTPUT_PP) { - mode_qst = MP_QSTR_OUT_PP; + mode_qst = MP_QSTR_OUT; } else if (mode == GPIO_MODE_OUTPUT_OD) { - mode_qst = MP_QSTR_OUT_OD; + mode_qst = MP_QSTR_OPEN_DRAIN; } else { af = true; if (mode == GPIO_MODE_AF_PP) { - mode_qst = MP_QSTR_AF_PP; + mode_qst = MP_QSTR_ALT; } else { - mode_qst = MP_QSTR_AF_OD; + mode_qst = MP_QSTR_ALT_OPEN_DRAIN; } } mp_print_str(print, qstr_str(mode_qst)); -- cgit v1.2.3 From 9897bcaa731d9dfe62cac61b56698799c8e6bac8 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 25 Aug 2016 15:22:25 +0300 Subject: lib/utils/stdout_helpers: Fix function signature to match py/mphal.h. --- lib/utils/stdout_helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/stdout_helpers.c b/lib/utils/stdout_helpers.c index 6efe10b92..5f7a17d32 100644 --- a/lib/utils/stdout_helpers.c +++ b/lib/utils/stdout_helpers.c @@ -11,7 +11,7 @@ // Send "cooked" string of given length, where every occurance of // LF character is replaced with CR LF. -void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) { +void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { while (len--) { if (*str == '\n') { mp_hal_stdout_tx_strn("\r", 1); -- cgit v1.2.3 From 891479e62a8db32d5a6351e9e3eb9e5dd570ba9c Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Thu, 25 Aug 2016 10:42:33 +0200 Subject: esp8266/hspi: Enable duplex operation of hardware SPI Without this, spi.read(1, 0xff) would use 16 clock cycles, first to send 0xff and then to receive one byte, as visible with a logic analyzer. --- esp8266/hspi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/esp8266/hspi.c b/esp8266/hspi.c index 7315dc8a1..436fb4f6f 100644 --- a/esp8266/hspi.c +++ b/esp8266/hspi.c @@ -199,7 +199,11 @@ uint32_t spi_transaction(uint8_t spi_no, uint8_t cmd_bits, uint16_t cmd_data, // This is rather inefficient but allows for a very generic function. // CMD ADDR and MOSI are set below to save on an extra if statement. if (din_bits) { - SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO); + if (dout_bits) { + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_DOUTDIN); + } else { + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO); + } } if (dummy_bits) { SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_DUMMY); -- cgit v1.2.3 From b0e2106fb89d108bd0249c85707b800d23d0273f Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 25 Aug 2016 21:30:20 +0300 Subject: lib/berkeley-db-1.xx: Update to upstream, fixes MacOSX build. --- lib/berkeley-db-1.xx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/berkeley-db-1.xx b/lib/berkeley-db-1.xx index 78a478794..dab957dac 160000 --- a/lib/berkeley-db-1.xx +++ b/lib/berkeley-db-1.xx @@ -1 +1 @@ -Subproject commit 78a4787948bb80cbfafcfd7910f95f61a4dd0d4c +Subproject commit dab957dacddcbf6cbc85d42df62e189e4877bb72 -- cgit v1.2.3 From d29ca28288581ca788e468528ea1680b99eb49e5 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Mon, 22 Aug 2016 16:10:34 +0200 Subject: esp8266/modous: Add os.umount method to unmount a filesystem. This is an object-oriented approach, where uos is only a proxy for the methods on the vfs object. Some internals had to be exposed (the STATIC keyword removed) for this to work. Fixes #2338. --- esp8266/moduos.c | 5 +++++ extmod/fsusermount.c | 2 +- extmod/fsusermount.h | 1 + extmod/vfs_fat.c | 8 ++++++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/esp8266/moduos.c b/esp8266/moduos.c index af149625d..bbbf2b6b8 100644 --- a/esp8266/moduos.c +++ b/esp8266/moduos.c @@ -123,6 +123,10 @@ STATIC mp_obj_t os_rename(mp_obj_t path_old, mp_obj_t path_new) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(os_rename_obj, os_rename); +STATIC mp_obj_t os_umount(void) { + return vfs_proxy_call(MP_QSTR_umount, 0, NULL); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_umount_obj, os_umount); #endif STATIC mp_obj_t os_urandom(mp_obj_t num) { @@ -166,6 +170,7 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&os_remove_obj) }, { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&os_rename_obj) }, { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&os_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&os_umount_obj) }, #endif }; diff --git a/extmod/fsusermount.c b/extmod/fsusermount.c index 9ddc98f3d..cbc1e3622 100644 --- a/extmod/fsusermount.c +++ b/extmod/fsusermount.c @@ -162,7 +162,7 @@ STATIC mp_obj_t fatfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k } MP_DEFINE_CONST_FUN_OBJ_KW(fsuser_mount_obj, 2, fatfs_mount); -STATIC mp_obj_t fatfs_umount(mp_obj_t bdev_or_path_in) { +mp_obj_t fatfs_umount(mp_obj_t bdev_or_path_in) { size_t i = 0; if (MP_OBJ_IS_STR(bdev_or_path_in)) { mp_uint_t mnt_len; diff --git a/extmod/fsusermount.h b/extmod/fsusermount.h index a25434b78..e1f26f2ce 100644 --- a/extmod/fsusermount.h +++ b/extmod/fsusermount.h @@ -55,6 +55,7 @@ typedef struct _fs_user_mount_t { } fs_user_mount_t; fs_user_mount_t *fatfs_mount_mkfs(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, bool mkfs); +mp_obj_t fatfs_umount(mp_obj_t bdev_or_path_in); MP_DECLARE_CONST_FUN_OBJ(fsuser_mount_obj); MP_DECLARE_CONST_FUN_OBJ(fsuser_umount_obj); diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index a691ee062..eea075f6b 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -250,6 +250,13 @@ STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_stat_obj, fat_vfs_stat); +// Unmount the filesystem +STATIC mp_obj_t fat_vfs_umount(mp_obj_t vfs_in) { + fatfs_umount(((fs_user_mount_t *)vfs_in)->readblocks[1]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_umount_obj, fat_vfs_umount); + STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&fat_vfs_mkfs_obj) }, { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&fat_vfs_open_obj) }, @@ -261,6 +268,7 @@ STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&fat_vfs_remove_obj) }, { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&fat_vfs_rename_obj) }, { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&fat_vfs_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&fat_vfs_umount_obj) }, }; STATIC MP_DEFINE_CONST_DICT(fat_vfs_locals_dict, fat_vfs_locals_dict_table); -- cgit v1.2.3 From 5ffe1d8dc07930818bbac6a88bec2aa5bd973402 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Aug 2016 15:35:26 +1000 Subject: py/gc: Add MICROPY_GC_CONSERVATIVE_CLEAR option to always zero memory. There can be stray pointers in memory blocks that are not properly zero'd after allocation. This patch adds a new config option to always zero all allocated memory (via gc_alloc and gc_realloc) and hence help to eliminate stray pointers. See issue #2195. --- py/gc.c | 10 ++++++++++ py/malloc.c | 3 +++ py/mpconfig.h | 9 +++++++++ 3 files changed, 22 insertions(+) diff --git a/py/gc.c b/py/gc.c index 97868c07f..7ed53cfc7 100644 --- a/py/gc.c +++ b/py/gc.c @@ -480,12 +480,17 @@ found: GC_EXIT(); + #if MICROPY_GC_CONSERVATIVE_CLEAR + // be conservative and zero out all the newly allocated blocks + memset((byte*)ret_ptr, 0, (end_block - start_block + 1) * BYTES_PER_BLOCK); + #else // zero out the additional bytes of the newly allocated blocks // This is needed because the blocks may have previously held pointers // to the heap and will not be set to something else if the caller // doesn't actually use the entire block. As such they will continue // to point to the heap and may prevent other blocks from being reclaimed. memset((byte*)ret_ptr + n_bytes, 0, (end_block - start_block + 1) * BYTES_PER_BLOCK - n_bytes); + #endif #if MICROPY_ENABLE_FINALISER if (has_finaliser) { @@ -713,8 +718,13 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { GC_EXIT(); + #if MICROPY_GC_CONSERVATIVE_CLEAR + // be conservative and zero out all the newly allocated blocks + memset((byte*)ptr_in + n_blocks * BYTES_PER_BLOCK, 0, (new_blocks - n_blocks) * BYTES_PER_BLOCK); + #else // zero out the additional bytes of the newly allocated blocks (see comment above in gc_alloc) memset((byte*)ptr_in + n_bytes, 0, new_blocks * BYTES_PER_BLOCK - n_bytes); + #endif #if EXTENSIVE_HEAP_PROFILING gc_dump_alloc_table(); diff --git a/py/malloc.c b/py/malloc.c index b0493d934..f48cb8da4 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -117,7 +117,10 @@ void *m_malloc0(size_t num_bytes) { if (ptr == NULL && num_bytes != 0) { return m_malloc_fail(num_bytes); } + // If this config is set then the GC clears all memory, so we don't need to. + #if !MICROPY_GC_CONSERVATIVE_CLEAR memset(ptr, 0, num_bytes); + #endif return ptr; } diff --git a/py/mpconfig.h b/py/mpconfig.h index 491a53746..23591e0da 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -107,6 +107,15 @@ #define MICROPY_ALLOC_GC_STACK_SIZE (64) #endif +// Be conservative and always clear to zero newly (re)allocated memory in the GC. +// This helps eliminate stray pointers that hold on to memory that's no longer +// used. It decreases performance due to unnecessary memory clearing. +// TODO Do analysis to understand why some memory is not properly cleared and +// find a more efficient way to clear it. +#ifndef MICROPY_GC_CONSERVATIVE_CLEAR +#define MICROPY_GC_CONSERVATIVE_CLEAR (1) +#endif + // Support automatic GC when reaching allocation threshold, // configurable by gc.threshold(). #ifndef MICROPY_GC_ALLOC_THRESHOLD -- cgit v1.2.3 From fc73c9b4b28e903641888a81a76169a929407383 Mon Sep 17 00:00:00 2001 From: Krzysztof Blazewicz Date: Fri, 26 Aug 2016 10:50:37 +0200 Subject: stmhal/modusocket: set self->nic to MP_OBJ_NULL after socket close This patch makes second and next calls to .close() a no-op. It prevents GC from closing the underlying resource after user already used .close() explicitly. fixes #2355 --- stmhal/modusocket.c | 1 + 1 file changed, 1 insertion(+) diff --git a/stmhal/modusocket.c b/stmhal/modusocket.c index fbd119644..c0e30ce05 100644 --- a/stmhal/modusocket.c +++ b/stmhal/modusocket.c @@ -83,6 +83,7 @@ STATIC mp_obj_t socket_close(mp_obj_t self_in) { mod_network_socket_obj_t *self = self_in; if (self->nic != MP_OBJ_NULL) { self->nic_type->close(self); + self->nic = MP_OBJ_NULL; } return mp_const_none; } -- cgit v1.2.3 From 24df30c1336fc72ad7dcd5cfc08a5e7f9a1f9808 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Aug 2016 22:28:22 +1000 Subject: py/compile: Don't compile assert statements when optimisations enabled. As per CPython. --- py/compile.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/py/compile.c b/py/compile.c index df6dab063..c8b4e5470 100644 --- a/py/compile.c +++ b/py/compile.c @@ -1200,6 +1200,11 @@ STATIC void compile_nonlocal_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) } STATIC void compile_assert_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + // with optimisations enabled we don't compile assertions + if (MP_STATE_VM(mp_optimise_value) != 0) { + return; + } + uint l_end = comp_next_label(comp); c_if_cond(comp, pns->nodes[0], true, l_end); EMIT_LOAD_GLOBAL(MP_QSTR_AssertionError); // we load_global instead of load_id, to be consistent with CPython -- cgit v1.2.3 From 3b5affa0d1f7c1e81dd4a48bba2742e8013804da Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Aug 2016 22:29:38 +1000 Subject: tests/cmdline: Add test for -O option to check optimisation value. --- tests/cmdline/cmd_optimise.py | 4 ++++ tests/cmdline/cmd_optimise.py.exp | 1 + 2 files changed, 5 insertions(+) create mode 100644 tests/cmdline/cmd_optimise.py create mode 100644 tests/cmdline/cmd_optimise.py.exp diff --git a/tests/cmdline/cmd_optimise.py b/tests/cmdline/cmd_optimise.py new file mode 100644 index 000000000..79d3bb44f --- /dev/null +++ b/tests/cmdline/cmd_optimise.py @@ -0,0 +1,4 @@ +# cmdline: -O +# test optimisation output +print(__debug__) +assert 0 diff --git a/tests/cmdline/cmd_optimise.py.exp b/tests/cmdline/cmd_optimise.py.exp new file mode 100644 index 000000000..bc59c12aa --- /dev/null +++ b/tests/cmdline/cmd_optimise.py.exp @@ -0,0 +1 @@ +False -- cgit v1.2.3 From fe3cc5bb53198cc1462fb6748137d57a68960933 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Aug 2016 23:42:40 +1000 Subject: py/modstruct: Use more compact mp_raise_ValueError function. Saves a few bytes of code size. --- py/modstruct.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/py/modstruct.c b/py/modstruct.c index 2016add17..be0d0110d 100644 --- a/py/modstruct.c +++ b/py/modstruct.c @@ -114,7 +114,7 @@ STATIC mp_obj_t struct_calcsize(mp_obj_t fmt_in) { mp_uint_t align; size_t sz = mp_binary_get_size(fmt_type, *fmt, &align); if (sz == 0) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "unsupported format")); + mp_raise_ValueError("unsupported format"); } while (cnt--) { // Apply alignment @@ -149,7 +149,7 @@ STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) { // negative offsets are relative to the end of the buffer offset = bufinfo.len + offset; if (offset < 0) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "buffer too small")); + mp_raise_ValueError("buffer too small"); } } p += offset; @@ -164,7 +164,7 @@ STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) { sz = get_fmt_num(&fmt); } if (p + sz > end_p) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "buffer too small")); + mp_raise_ValueError("buffer too small"); } mp_obj_t item; if (*fmt == 's') { @@ -197,7 +197,7 @@ STATIC void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, byte* end_p, siz sz = get_fmt_num(&fmt); } if (p + sz > end_p) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "buffer too small")); + mp_raise_ValueError("buffer too small"); } if (*fmt == 's') { @@ -240,7 +240,7 @@ STATIC mp_obj_t struct_pack_into(size_t n_args, const mp_obj_t *args) { // negative offsets are relative to the end of the buffer offset = (mp_int_t)bufinfo.len + offset; if (offset < 0) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "buffer too small")); + mp_raise_ValueError("buffer too small"); } } byte *p = (byte *)bufinfo.buf; -- cgit v1.2.3 From 26295e04ff878d821ff4fbb9a5d8176906907da5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 26 Aug 2016 23:45:07 +1000 Subject: py/emitglue: Use more compact mp_raise_ValueError function. --- py/emitglue.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/py/emitglue.c b/py/emitglue.c index 1b9992b8e..0b5903092 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -348,12 +348,10 @@ mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) { byte header[4]; read_bytes(reader, header, sizeof(header)); if (strncmp((char*)header, "M\x00", 2) != 0) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, - "invalid .mpy file")); + mp_raise_ValueError("invalid .mpy file"); } if (header[2] != MPY_FEATURE_FLAGS || header[3] > mp_small_int_bits()) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, - "incompatible .mpy file")); + mp_raise_ValueError("incompatible .mpy file"); } return load_raw_code(reader); } @@ -559,8 +557,7 @@ STATIC void save_bytecode_qstrs(mp_print_t *print, const byte *ip, const byte *i STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc) { if (rc->kind != MP_CODE_BYTECODE) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, - "can only save bytecode")); + mp_raise_ValueError("can only save bytecode"); } // save bytecode -- cgit v1.2.3 From fea7fe45ea4145fff7afc2f07f461613ad31e781 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Fri, 26 Aug 2016 15:22:53 +0200 Subject: tests/extmod/vfs_fat_ramdisk: Add tests for VFS.umount() Try to un-mount a file system and re-mount it again. --- tests/extmod/vfs_fat_ramdisk.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/extmod/vfs_fat_ramdisk.py b/tests/extmod/vfs_fat_ramdisk.py index 7860d6812..57c8eeba8 100644 --- a/tests/extmod/vfs_fat_ramdisk.py +++ b/tests/extmod/vfs_fat_ramdisk.py @@ -1,5 +1,6 @@ import sys import uos +import uerrno try: uos.VfsFat except AttributeError: @@ -84,3 +85,15 @@ assert vfs.listdir() == ["sub_file.txt"] vfs.chdir("..") print("getcwd:", vfs.getcwd()) + + +vfs.umount() +try: + vfs.listdir() +except OSError as e: + assert e.args[0] == uerrno.ENODEV +else: + raise AssertionError("expected OSError not thrown") + +vfs = uos.VfsFat(bdev, "/ramdisk") +assert vfs.listdir() == ['foo_dir', 'moved-to-root.txt'] -- cgit v1.2.3 From 531217a06b54d612ac49c341a358b3fca1d59985 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 27 Aug 2016 18:31:58 +1000 Subject: extmod/modframebuf: Fix pixel accessor to return a 1-bit result. --- extmod/modframebuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index d0ef23807..a1e7c079b 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -83,7 +83,7 @@ STATIC mp_obj_t framebuf1_pixel(size_t n_args, const mp_obj_t *args) { int index = (y / 8) * self->stride + x; if (n_args == 3) { // get - return MP_OBJ_NEW_SMALL_INT(self->buf[index] >> (y & 7)); + return MP_OBJ_NEW_SMALL_INT((self->buf[index] >> (y & 7)) & 1); } else { // set if (mp_obj_get_int(args[3])) { -- cgit v1.2.3 From 581a59a456654e881a228a99f135777e0335f1e1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 27 Aug 2016 23:21:00 +1000 Subject: py: Rename struct mp_code_state to mp_code_state_t. Also at _t to mp_exc_stack pre-declaration in struct typedef. --- py/bc.c | 2 +- py/bc.h | 12 ++++++------ py/emitnative.c | 6 +++--- py/objfun.c | 14 +++++++------- py/objgenerator.c | 2 +- py/vm.c | 10 +++++----- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/py/bc.c b/py/bc.c index 5cdaa4770..0d0a0b12f 100644 --- a/py/bc.c +++ b/py/bc.c @@ -89,7 +89,7 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) { // - code_state->ip should contain the offset in bytes from the start of // the bytecode chunk to just after n_state and n_exc_stack // - code_state->n_state should be set to the state size (locals plus stack) -void mp_setup_code_state(mp_code_state *code_state, mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) { +void mp_setup_code_state(mp_code_state_t *code_state, mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) { // This function is pretty complicated. It's main aim is to be efficient in speed and RAM // usage for the common case of positional only args. size_t n_state = code_state->n_state; diff --git a/py/bc.h b/py/bc.h index c2d95bedd..11f5bb632 100644 --- a/py/bc.h +++ b/py/bc.h @@ -60,7 +60,7 @@ // constN : obj // Exception stack entry -typedef struct _mp_exc_stack { +typedef struct _mp_exc_stack_t { const byte *handler; // bit 0 is saved currently_in_except_block value // bit 1 is whether the opcode was SETUP_WITH or SETUP_FINALLY @@ -69,7 +69,7 @@ typedef struct _mp_exc_stack { mp_obj_base_t *prev_exc; } mp_exc_stack_t; -typedef struct _mp_code_state { +typedef struct _mp_code_state_t { const byte *code_info; const byte *ip; const mp_uint_t *const_table; @@ -85,14 +85,14 @@ typedef struct _mp_code_state { mp_obj_t state[0]; // Variable-length, never accessed by name, only as (void*)(state + n_state) //mp_exc_stack_t exc_state[0]; -} mp_code_state; +} mp_code_state_t; mp_uint_t mp_decode_uint(const byte **ptr); -mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_obj_t inject_exc); -mp_code_state *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, size_t n_args, size_t n_kw, const mp_obj_t *args); +mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc); +mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, size_t n_args, size_t n_kw, const mp_obj_t *args); struct _mp_obj_fun_bc_t; -void mp_setup_code_state(mp_code_state *code_state, struct _mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args); +void mp_setup_code_state(mp_code_state_t *code_state, struct _mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args); void mp_bytecode_print(const void *descr, const byte *code, mp_uint_t len, const mp_uint_t *const_table); void mp_bytecode_print2(const byte *code, mp_uint_t len); const byte *mp_bytecode_print_str(const byte *ip); diff --git a/py/emitnative.c b/py/emitnative.c index 9adaabc11..2cf4711fe 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -628,7 +628,7 @@ STATIC void emit_post_push_reg(emit_t *emit, vtype_kind_t vtype, int reg); STATIC void emit_native_load_fast(emit_t *emit, qstr qst, mp_uint_t local_num); STATIC void emit_native_store_fast(emit_t *emit, qstr qst, mp_uint_t local_num); -#define STATE_START (sizeof(mp_code_state) / sizeof(mp_uint_t)) +#define STATE_START (sizeof(mp_code_state_t) / sizeof(mp_uint_t)) STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { DEBUG_printf("start_pass(pass=%u, scope=%p)\n", pass, scope); @@ -775,10 +775,10 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop // set code_state.ip (offset from start of this function to prelude info) // XXX this encoding may change size - ASM_MOV_IMM_TO_LOCAL_USING(emit->as, emit->prelude_offset, offsetof(mp_code_state, ip) / sizeof(mp_uint_t), REG_ARG_1); + ASM_MOV_IMM_TO_LOCAL_USING(emit->as, emit->prelude_offset, offsetof(mp_code_state_t, ip) / sizeof(mp_uint_t), REG_ARG_1); // set code_state.n_state - ASM_MOV_IMM_TO_LOCAL_USING(emit->as, emit->n_state, offsetof(mp_code_state, n_state) / sizeof(mp_uint_t), REG_ARG_1); + ASM_MOV_IMM_TO_LOCAL_USING(emit->as, emit->n_state, offsetof(mp_code_state_t, n_state) / sizeof(mp_uint_t), REG_ARG_1); // put address of code_state into first arg ASM_MOV_LOCAL_ADDR_TO_REG(emit->as, 0, REG_ARG_1); diff --git a/py/objfun.c b/py/objfun.c index 293d06427..3fd25fb22 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -165,7 +165,7 @@ STATIC void dump_args(const mp_obj_t *a, mp_uint_t sz) { #define VM_DETECT_STACK_OVERFLOW (0) #if MICROPY_STACKLESS -mp_code_state *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { MP_STACK_CHECK(); mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); @@ -178,8 +178,8 @@ mp_code_state *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args, // allocate state for locals and stack size_t state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t); - mp_code_state *code_state; - code_state = m_new_obj_var_maybe(mp_code_state, byte, state_size); + mp_code_state_t *code_state; + code_state = m_new_obj_var_maybe(mp_code_state_t, byte, state_size); if (!code_state) { return NULL; } @@ -220,12 +220,12 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const // allocate state for locals and stack mp_uint_t state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t); - mp_code_state *code_state = NULL; + mp_code_state_t *code_state = NULL; if (state_size > VM_MAX_STATE_ON_STACK) { - code_state = m_new_obj_var_maybe(mp_code_state, byte, state_size); + code_state = m_new_obj_var_maybe(mp_code_state_t, byte, state_size); } if (code_state == NULL) { - code_state = alloca(sizeof(mp_code_state) + state_size); + code_state = alloca(sizeof(mp_code_state_t) + state_size); state_size = 0; // indicate that we allocated using alloca } @@ -287,7 +287,7 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const // free the state if it was allocated on the heap if (state_size != 0) { - m_del_var(mp_code_state, byte, state_size, code_state); + m_del_var(mp_code_state_t, byte, state_size, code_state); } if (vm_return_kind == MP_VM_RETURN_NORMAL) { diff --git a/py/objgenerator.c b/py/objgenerator.c index 99d944537..8c32a3649 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -46,7 +46,7 @@ typedef struct _mp_obj_gen_wrap_t { typedef struct _mp_obj_gen_instance_t { mp_obj_base_t base; mp_obj_dict_t *globals; - mp_code_state code_state; + mp_code_state_t code_state; } mp_obj_gen_instance_t; STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { diff --git a/py/vm.c b/py/vm.c index 65801401d..f9bdedff8 100644 --- a/py/vm.c +++ b/py/vm.c @@ -127,7 +127,7 @@ typedef enum { // MP_VM_RETURN_NORMAL, sp valid, return value in *sp // MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp // MP_VM_RETURN_EXCEPTION, exception in fastn[0] -mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_obj_t inject_exc) { +mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc) { #define SELECTIVE_EXC_IP (0) #if SELECTIVE_EXC_IP #define MARK_EXC_IP_SELECTIVE() { code_state->ip = ip; } /* stores ip 1 byte past last opcode */ @@ -904,7 +904,7 @@ unwind_jump:; code_state->ip = ip; code_state->sp = sp; code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, currently_in_except_block); - mp_code_state *new_state = mp_obj_fun_bc_prepare_codestate(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1); + mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1); if (new_state) { new_state->prev = code_state; code_state = new_state; @@ -940,7 +940,7 @@ unwind_jump:; mp_call_args_t out_args; mp_call_prepare_args_n_kw_var(false, unum, sp, &out_args); - mp_code_state *new_state = mp_obj_fun_bc_prepare_codestate(out_args.fun, + mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(out_args.fun, out_args.n_args, out_args.n_kw, out_args.args); m_del(mp_obj_t, out_args.args, out_args.n_alloc); if (new_state) { @@ -976,7 +976,7 @@ unwind_jump:; mp_uint_t n_kw = (unum >> 8) & 0xff; int adjust = (sp[1] == MP_OBJ_NULL) ? 0 : 1; - mp_code_state *new_state = mp_obj_fun_bc_prepare_codestate(*sp, n_args + adjust, n_kw, sp + 2 - adjust); + mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(*sp, n_args + adjust, n_kw, sp + 2 - adjust); if (new_state) { new_state->prev = code_state; code_state = new_state; @@ -1011,7 +1011,7 @@ unwind_jump:; mp_call_args_t out_args; mp_call_prepare_args_n_kw_var(true, unum, sp, &out_args); - mp_code_state *new_state = mp_obj_fun_bc_prepare_codestate(out_args.fun, + mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(out_args.fun, out_args.n_args, out_args.n_kw, out_args.args); m_del(mp_obj_t, out_args.args, out_args.n_alloc); if (new_state) { -- cgit v1.2.3 From f4ee9f88539e64a88bd837c5877354f6b7141119 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 27 Aug 2016 23:23:51 +1000 Subject: py/bc.h: Rename _mp_code_state to _mp_code_state_t. This rename was missed in the previous patch. --- py/bc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/bc.h b/py/bc.h index 11f5bb632..4707da179 100644 --- a/py/bc.h +++ b/py/bc.h @@ -78,7 +78,7 @@ typedef struct _mp_code_state_t { mp_exc_stack_t *exc_sp; mp_obj_dict_t *old_globals; #if MICROPY_STACKLESS - struct _mp_code_state *prev; + struct _mp_code_state_t *prev; #endif size_t n_state; // Variable-length -- cgit v1.2.3 From 8f8f699eb72446e94253eaa73a3c893562eef82d Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 27 Aug 2016 23:41:16 +1000 Subject: tests/run-tests: Disable thread_gc1.py test on Travis. It has reliability issues (cause unknown at this time). --- tests/run-tests | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-tests b/tests/run-tests index 80536f242..5e8819729 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -200,6 +200,7 @@ def run_tests(pyb, tests, args): # Some tests shouldn't be run under Travis CI if os.getenv('TRAVIS') == 'true': skip_tests.add('basics/memoryerror.py') + skip_tests.add('thread/thread_gc1.py') # has reliability issues skip_tests.add('thread/thread_lock4.py') # has reliability issues skip_tests.add('thread/stress_heap.py') # has reliability issues -- cgit v1.2.3 From 263aaa70302aa60d0338abbe439be2a77eb49a52 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 28 Aug 2016 14:48:49 +0300 Subject: esp8266/modmachinewdt: Implement machine.WDT class. --- esp8266/Makefile | 1 + esp8266/modmachine.c | 3 ++ esp8266/modmachinewdt.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 esp8266/modmachinewdt.c diff --git a/esp8266/Makefile b/esp8266/Makefile index 0b498602f..7da83f1aa 100644 --- a/esp8266/Makefile +++ b/esp8266/Makefile @@ -78,6 +78,7 @@ SRC_C = \ modpybrtc.c \ modpybadc.c \ modpybuart.c \ + modmachinewdt.c \ modmachinespi.c \ modpybspi.c \ modpybhspi.c \ diff --git a/esp8266/modmachine.c b/esp8266/modmachine.c index 7672834aa..0972ce29f 100644 --- a/esp8266/modmachine.c +++ b/esp8266/modmachine.c @@ -49,6 +49,8 @@ //#define MACHINE_WAKE_SLEEP (0x02) #define MACHINE_WAKE_DEEPSLEEP (0x04) +extern const mp_obj_type_t esp_wdt_type; + STATIC mp_obj_t machine_freq(mp_uint_t n_args, const mp_obj_t *args) { if (n_args == 0) { // get @@ -246,6 +248,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&pyb_rtc_type) }, { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&esp_timer_type) }, + { MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&esp_wdt_type) }, { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pyb_pin_type) }, { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&pyb_pwm_type) }, { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&pyb_adc_type) }, diff --git a/esp8266/modmachinewdt.c b/esp8266/modmachinewdt.c new file mode 100644 index 000000000..e0b1ff5d7 --- /dev/null +++ b/esp8266/modmachinewdt.c @@ -0,0 +1,76 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * 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 +#include + +#include "py/nlr.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "user_interface.h" + +const mp_obj_type_t esp_wdt_type; + +typedef struct _machine_wdt_obj_t { + mp_obj_base_t base; +} machine_wdt_obj_t; + +STATIC machine_wdt_obj_t wdt_default = {{&esp_wdt_type}}; + +STATIC mp_obj_t machine_wdt_make_new(const mp_obj_type_t *type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + + mp_int_t id = 0; + if (n_args > 0) { + id = mp_obj_get_int(args[0]); + } + + switch (id) { + case 0: + return &wdt_default; + default: + mp_raise_ValueError(""); + } +} + +STATIC mp_obj_t machine_wdt_feed(mp_obj_t self_in) { + (void)self_in; + system_soft_wdt_feed(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_wdt_feed_obj, machine_wdt_feed); + +STATIC const mp_map_elem_t machine_wdt_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_feed), (mp_obj_t)&machine_wdt_feed_obj } +}; +STATIC MP_DEFINE_CONST_DICT(machine_wdt_locals_dict, machine_wdt_locals_dict_table); + +const mp_obj_type_t esp_wdt_type = { + { &mp_type_type }, + .name = MP_QSTR_WDT, + .make_new = machine_wdt_make_new, + .locals_dict = (mp_obj_t)&machine_wdt_locals_dict, +}; -- cgit v1.2.3 From ed0a06a93fc275e4f3980b81e4e5314ed63279c6 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Sat, 27 Aug 2016 19:07:14 +0200 Subject: docs/esp8266/quickref: Fix and update the SPI docs Use the `SPI` factory function in the examples, and use proper baud rate of 80 000 000. --- docs/esp8266/quickref.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index be58f9332..d8ec60372 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -165,14 +165,14 @@ Use the ``machine.ADC`` class:: SPI bus ------- -The SPI driver is implemented in software and works on all pins:: +There are two SPI drivers. One is implemented in software and works on all pins:: from machine import Pin, SPI # construct an SPI bus on the given pins # polarity is the idle state of SCK # phase=0 means sample on the first edge of SCK, phase=1 means the second - spi = SPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4)) + spi = SPI(-1, baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4)) spi.init(baudrate=200000) # set the baudrate @@ -194,13 +194,13 @@ Hardware SPI ------------ The hardware SPI is faster (up to 80Mhz), but only works on following pins: -``MISO`` is gpio2, ``MOSI`` is gpio13, and ``SCK`` is gpio14. It has the same +``MISO`` is gpio12, ``MOSI`` is gpio13, and ``SCK`` is gpio14. It has the same methods as SPI, except for the pin parameters for the constructor and init (as those are fixed). - from machine import Pin, HSPI + from machine import Pin, SPI - hspi = HSPI(baudrate=800000000, polarity=0, phase=0) + hspi = SPI(0, baudrate=80000000, polarity=0, phase=0) I2C bus -- cgit v1.2.3 From 9fba618356679b0eede00b7cd746d1a53e6b3772 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 29 Aug 2016 12:12:49 +1000 Subject: esp8266/modules: Split onewire.py into OneWire and DS18X20 driver. The OneWire class is now in its own onewire.py module, and the temperature sensor class is in its own ds18x20.py module. The latter is renamed to DS18X20 to reflect the fact that it will support both the "S" and "B" variants of the device. These files are moved to the modules/ subdirectory to take advantage of frozen bytecode. --- esp8266/modules/ds18x20.py | 37 +++++++++++++ esp8266/modules/onewire.py | 92 ++++++++++++++++++++++++++++++++ esp8266/scripts/onewire.py | 127 --------------------------------------------- 3 files changed, 129 insertions(+), 127 deletions(-) create mode 100644 esp8266/modules/ds18x20.py create mode 100644 esp8266/modules/onewire.py delete mode 100644 esp8266/scripts/onewire.py diff --git a/esp8266/modules/ds18x20.py b/esp8266/modules/ds18x20.py new file mode 100644 index 000000000..66983f5bc --- /dev/null +++ b/esp8266/modules/ds18x20.py @@ -0,0 +1,37 @@ +# DS18x20 temperature sensor driver for MicroPython. +# MIT license; Copyright (c) 2016 Damien P. George + +_CONVERT = const(0x44) +_RD_SCRATCH = const(0xbe) +_WR_SCRATCH = const(0x4e) + +class DS18X20: + def __init__(self, onewire): + self.ow = onewire + + def scan(self): + return [rom for rom in self.ow.scan() if rom[0] == 0x28] + + def convert_temp(self): + self.ow.reset(True) + self.ow.writebyte(self.ow.SKIP_ROM) + self.ow.writebyte(_CONVERT) + + def read_scratch(self, rom): + self.ow.reset(True) + self.ow.select_rom(rom) + self.ow.writebyte(_RD_SCRATCH) + buf = self.ow.read(9) + if self.ow.crc8(buf): + raise Exception('CRC error') + return buf + + def write_scratch(self, rom, buf): + self.ow.reset(True) + self.ow.select_rom(rom) + self.ow.writebyte(_WR_SCRATCH) + self.ow.write(buf) + + def read_temp(self, rom): + buf = self.read_scratch(rom) + return (buf[1] << 8 | buf[0]) / 16 diff --git a/esp8266/modules/onewire.py b/esp8266/modules/onewire.py new file mode 100644 index 000000000..af5675649 --- /dev/null +++ b/esp8266/modules/onewire.py @@ -0,0 +1,92 @@ +# 1-Wire driver for MicroPython on ESP8266 +# MIT license; Copyright (c) 2016 Damien P. George + +import _onewire as _ow + +class OneWireError(Exception): + pass + +class OneWire: + SEARCH_ROM = const(0xf0) + MATCH_ROM = const(0x55) + SKIP_ROM = const(0xcc) + + def __init__(self, pin): + self.pin = pin + self.pin.init(pin.OPEN_DRAIN) + + def reset(self, required=False): + reset = _ow.reset(self.pin) + if required and not reset: + raise OneWireError + return reset + + def readbit(self): + return _ow.readbit(self.pin) + + def readbyte(self): + return _ow.readbyte(self.pin) + + def read(self, count): + buf = bytearray(count) + for i in range(count): + buf[i] = _ow.readbyte(self.pin) + return buf + + def writebit(self, value): + return _ow.writebit(self.pin, value) + + def writebyte(self, value): + return _ow.writebyte(self.pin, value) + + def write(self, buf): + for b in buf: + _ow.writebyte(self.pin, b) + + def select_rom(self, rom): + self.reset() + self.writebyte(MATCH_ROM) + self.write(rom) + + def scan(self): + devices = [] + diff = 65 + rom = False + for i in range(0xff): + rom, diff = self._search_rom(rom, diff) + if rom: + devices += [rom] + if diff == 0: + break + return devices + + def _search_rom(self, l_rom, diff): + if not self.reset(): + return None, 0 + self.writebyte(SEARCH_ROM) + if not l_rom: + l_rom = bytearray(8) + rom = bytearray(8) + next_diff = 0 + i = 64 + for byte in range(8): + r_b = 0 + for bit in range(8): + b = self.readbit() + if self.readbit(): + if b: # there are no devices or there is an error on the bus + return None, 0 + else: + if not b: # collision, two devices with different bit meaning + if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i): + b = 1 + next_diff = i + self.writebit(b) + if b: + r_b |= 1 << bit + i -= 1 + rom[byte] = r_b + return rom, next_diff + + def crc8(self, data): + return _ow.crc8(data) diff --git a/esp8266/scripts/onewire.py b/esp8266/scripts/onewire.py deleted file mode 100644 index 686616950..000000000 --- a/esp8266/scripts/onewire.py +++ /dev/null @@ -1,127 +0,0 @@ -# 1-Wire driver for MicroPython on ESP8266 -# MIT license; Copyright (c) 2016 Damien P. George - -import _onewire as _ow - -class OneWireError(Exception): - pass - -class OneWire: - SEARCH_ROM = const(0xf0) - MATCH_ROM = const(0x55) - SKIP_ROM = const(0xcc) - - def __init__(self, pin): - self.pin = pin - self.pin.init(pin.OPEN_DRAIN) - - def reset(self): - return _ow.reset(self.pin) - - def readbit(self): - return _ow.readbit(self.pin) - - def readbyte(self): - return _ow.readbyte(self.pin) - - def read(self, count): - buf = bytearray(count) - for i in range(count): - buf[i] = _ow.readbyte(self.pin) - return buf - - def writebit(self, value): - return _ow.writebit(self.pin, value) - - def writebyte(self, value): - return _ow.writebyte(self.pin, value) - - def write(self, buf): - for b in buf: - _ow.writebyte(self.pin, b) - - def select_rom(self, rom): - self.reset() - self.writebyte(MATCH_ROM) - self.write(rom) - - def scan(self): - devices = [] - diff = 65 - rom = False - for i in range(0xff): - rom, diff = self._search_rom(rom, diff) - if rom: - devices += [rom] - if diff == 0: - break - return devices - - def _search_rom(self, l_rom, diff): - if not self.reset(): - return None, 0 - self.writebyte(SEARCH_ROM) - if not l_rom: - l_rom = bytearray(8) - rom = bytearray(8) - next_diff = 0 - i = 64 - for byte in range(8): - r_b = 0 - for bit in range(8): - b = self.readbit() - if self.readbit(): - if b: # there are no devices or there is an error on the bus - return None, 0 - else: - if not b: # collision, two devices with different bit meaning - if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i): - b = 1 - next_diff = i - self.writebit(b) - if b: - r_b |= 1 << bit - i -= 1 - rom[byte] = r_b - return rom, next_diff - - def crc8(self, data): - return _ow.crc8(data) - -class DS18B20: - CONVERT = const(0x44) - RD_SCRATCH = const(0xbe) - WR_SCRATCH = const(0x4e) - - def __init__(self, onewire): - self.ow = onewire - - def scan(self): - return [rom for rom in self.ow.scan() if rom[0] == 0x28] - - def convert_temp(self): - if not self.ow.reset(): - raise OneWireError - self.ow.writebyte(SKIP_ROM) - self.ow.writebyte(CONVERT) - - def read_scratch(self, rom): - if not self.ow.reset(): - raise OneWireError - self.ow.select_rom(rom) - self.ow.writebyte(RD_SCRATCH) - buf = self.ow.read(9) - if self.ow.crc8(buf): - raise OneWireError - return buf - - def write_scratch(self, rom, buf): - if not self.ow.reset(): - raise OneWireError - self.ow.select_rom(rom) - self.ow.writebyte(WR_SCRATCH) - self.ow.write(buf) - - def read_temp(self, rom): - buf = self.read_scratch(rom) - return (buf[1] << 8 | buf[0]) / 16 -- cgit v1.2.3 From 8e9b98e974ff141ba78918021f3203f620d1de8a Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 29 Aug 2016 12:27:21 +1000 Subject: esp8266/modules/onewire: Change onewire.read() to onewire.readinto(). This allows 1-wire drivers (eg DS18X20) to perform in-place operations and hence do less memory allocations. --- esp8266/modules/ds18x20.py | 7 ++++--- esp8266/modules/onewire.py | 6 ++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/esp8266/modules/ds18x20.py b/esp8266/modules/ds18x20.py index 66983f5bc..9c5bb8eb6 100644 --- a/esp8266/modules/ds18x20.py +++ b/esp8266/modules/ds18x20.py @@ -8,6 +8,7 @@ _WR_SCRATCH = const(0x4e) class DS18X20: def __init__(self, onewire): self.ow = onewire + self.buf = bytearray(9) def scan(self): return [rom for rom in self.ow.scan() if rom[0] == 0x28] @@ -21,10 +22,10 @@ class DS18X20: self.ow.reset(True) self.ow.select_rom(rom) self.ow.writebyte(_RD_SCRATCH) - buf = self.ow.read(9) - if self.ow.crc8(buf): + self.ow.readinto(self.buf) + if self.ow.crc8(self.buf): raise Exception('CRC error') - return buf + return self.buf def write_scratch(self, rom, buf): self.ow.reset(True) diff --git a/esp8266/modules/onewire.py b/esp8266/modules/onewire.py index af5675649..06b216a57 100644 --- a/esp8266/modules/onewire.py +++ b/esp8266/modules/onewire.py @@ -27,11 +27,9 @@ class OneWire: def readbyte(self): return _ow.readbyte(self.pin) - def read(self, count): - buf = bytearray(count) - for i in range(count): + def readinto(self, buf): + for i in range(len(buf)): buf[i] = _ow.readbyte(self.pin) - return buf def writebit(self, value): return _ow.writebit(self.pin, value) -- cgit v1.2.3 From 59a9509703c6e525b3419ffea1b69f3b6992e006 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 29 Aug 2016 12:45:07 +1000 Subject: esp8266/modules/ds18x20.py: Add support for DS18S20 devices. --- esp8266/modules/ds18x20.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/esp8266/modules/ds18x20.py b/esp8266/modules/ds18x20.py index 9c5bb8eb6..eb22e2ae3 100644 --- a/esp8266/modules/ds18x20.py +++ b/esp8266/modules/ds18x20.py @@ -11,7 +11,7 @@ class DS18X20: self.buf = bytearray(9) def scan(self): - return [rom for rom in self.ow.scan() if rom[0] == 0x28] + return [rom for rom in self.ow.scan() if rom[0] == 0x10 or rom[0] == 0x28] def convert_temp(self): self.ow.reset(True) @@ -35,4 +35,12 @@ class DS18X20: def read_temp(self, rom): buf = self.read_scratch(rom) - return (buf[1] << 8 | buf[0]) / 16 + if rom[0] == 0x10: + if buf[1]: + t = buf[0] >> 1 | 0x80 + t = -((~t + 1) & 0xff) + else: + t = buf[0] >> 1 + return t - 0.25 + (buf[7] - buf[6]) / buf[7] + else: + return (buf[1] << 8 | buf[0]) / 16 -- cgit v1.2.3 From 13c5a228c9ec9881087bdddd162f3b633edc3aa5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 29 Aug 2016 12:52:50 +1000 Subject: docs/esp8266: Update quickref and tutorial for OneWire/DS18X20 driver. --- docs/esp8266/quickref.rst | 7 +++---- docs/esp8266/tutorial/onewire.rst | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index d8ec60372..f15cb72bb 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -253,15 +253,14 @@ The OneWire driver is implemented in software and works on all pins:: ow.scan() # return a list of devices on the bus ow.reset() # reset the bus ow.readbyte() # read a byte - ow.read(5) # read 5 bytes ow.writebyte(0x12) # write a byte on the bus ow.write('123') # write bytes on the bus ow.select_rom(b'12345678') # select a specific device by its ROM code -There is a specific driver for DS18B20 devices:: +There is a specific driver for DS18S20 and DS18B20 devices:: - import time - ds = onewire.DS18B20(ow) + import time, ds18x20 + ds = ds18x20.DS18X20(ow) roms = ds.scan() ds.convert_temp() time.sleep_ms(750) diff --git a/docs/esp8266/tutorial/onewire.rst b/docs/esp8266/tutorial/onewire.rst index c90044b7a..c2cede9e3 100644 --- a/docs/esp8266/tutorial/onewire.rst +++ b/docs/esp8266/tutorial/onewire.rst @@ -6,19 +6,19 @@ The 1-wire bus is a serial bus that uses just a single wire for communication is a very popular 1-wire device, and here we show how to use the onewire module to read from such a device. -For the following code to work you need to have at least one DS18B20 temperature +For the following code to work you need to have at least one DS18S20 or DS18B20 temperature sensor with its data line connected to GPIO12. You must also power the sensors and connect a 4.7k Ohm resistor between the data pin and the power pin. :: import time import machine - import onewire + import onewire, ds18x20 # the device is on GPIO12 dat = machine.Pin(12) # create the onewire object - ds = onewire.DS18B20(onewire.OneWire(dat)) + ds = ds18x20.DS18X20(onewire.OneWire(dat)) # scan for devices on the bus roms = ds.scan() -- cgit v1.2.3 From 57c92d90b0be9f353c5f0501f4d78a408911f8b2 Mon Sep 17 00:00:00 2001 From: Philip Potter Date: Sat, 27 Aug 2016 20:57:22 +0100 Subject: docs/pyboard: Update USB mouse tutorial to use VCP instead of CDC. --- docs/pyboard/tutorial/usb_mouse.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/pyboard/tutorial/usb_mouse.rst b/docs/pyboard/tutorial/usb_mouse.rst index ac1de6e27..6e4feb622 100644 --- a/docs/pyboard/tutorial/usb_mouse.rst +++ b/docs/pyboard/tutorial/usb_mouse.rst @@ -13,23 +13,23 @@ will look something like this:: import pyb #pyb.main('main.py') # main script to run after this one - #pyb.usb_mode('CDC+MSC') # act as a serial and a storage device - #pyb.usb_mode('CDC+HID') # act as a serial device and a mouse + #pyb.usb_mode('VCP+MSC') # act as a serial and a storage device + #pyb.usb_mode('VCP+HID') # act as a serial device and a mouse To enable the mouse mode, uncomment the last line of the file, to make it look like:: - pyb.usb_mode('CDC+HID') # act as a serial device and a mouse + pyb.usb_mode('VCP+HID') # act as a serial device and a mouse If you already changed your ``boot.py`` file, then the minimum code it needs to work is:: import pyb - pyb.usb_mode('CDC+HID') + pyb.usb_mode('VCP+HID') -This tells the pyboard to configure itself as a CDC (serial) and HID -(human interface device, in our case a mouse) USB device when it boots -up. +This tells the pyboard to configure itself as a VCP (Virtual COM Port, +ie serial port) and HID (human interface device, in our case a mouse) +USB device when it boots up. Eject/unmount the pyboard drive and reset it using the RST switch. Your PC should now detect the pyboard as a mouse! @@ -121,9 +121,9 @@ If you leave your pyboard as-is, it'll behave as a mouse everytime you plug it in. You probably want to change it back to normal. To do this you need to first enter safe mode (see above), and then edit the ``boot.py`` file. In the ``boot.py`` file, comment out (put a # in front of) the line with the -``CDC+HID`` setting, so it looks like:: +``VCP+HID`` setting, so it looks like:: - #pyb.usb_mode('CDC+HID') # act as a serial device and a mouse + #pyb.usb_mode('VCP+HID') # act as a serial device and a mouse Save your file, eject/unmount the drive, and reset the pyboard. It is now back to normal operating mode. -- cgit v1.2.3 From f2da6467a9579c564f297a90a23c8b576c9cf6b1 Mon Sep 17 00:00:00 2001 From: Philip Potter Date: Sat, 27 Aug 2016 21:06:51 +0100 Subject: docs/pyboard: Update USB mouse tutorial to use pyb.USB_HID(). --- docs/pyboard/tutorial/usb_mouse.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/pyboard/tutorial/usb_mouse.rst b/docs/pyboard/tutorial/usb_mouse.rst index 6e4feb622..6f8831edb 100644 --- a/docs/pyboard/tutorial/usb_mouse.rst +++ b/docs/pyboard/tutorial/usb_mouse.rst @@ -41,7 +41,8 @@ To get the py-mouse to do anything we need to send mouse events to the PC. We will first do this manually using the REPL prompt. Connect to your pyboard using your serial program and type the following:: - >>> pyb.hid((0, 10, 0, 0)) + >>> hid = pyb.USB_HID() + >>> hid.send((0, 10, 0, 0)) Your mouse should move 10 pixels to the right! In the command above you are sending 4 pieces of information: button status, x, y and scroll. The @@ -52,7 +53,7 @@ Let's make the mouse oscillate left and right:: >>> import math >>> def osc(n, d): ... for i in range(n): - ... pyb.hid((0, int(20 * math.sin(i / 10)), 0, 0)) + ... hid.send((0, int(20 * math.sin(i / 10)), 0, 0)) ... pyb.delay(d) ... >>> osc(100, 50) @@ -100,9 +101,10 @@ In ``main.py`` put the following code:: switch = pyb.Switch() accel = pyb.Accel() + hid = pyb.USB_HID() while not switch(): - pyb.hid((0, accel.x(), accel.y(), 0)) + hid.send((0, accel.x(), accel.y(), 0)) pyb.delay(20) Save your file, eject/unmount your pyboard drive, and reset it using the RST @@ -112,7 +114,7 @@ the mouse around. Try it out, and see if you can make the mouse stand still! Press the USR switch to stop the mouse motion. You'll note that the y-axis is inverted. That's easy to fix: just put a -minus sign in front of the y-coordinate in the ``pyb.hid()`` line above. +minus sign in front of the y-coordinate in the ``hid.send()`` line above. Restoring your pyboard to normal -------------------------------- -- cgit v1.2.3 From 0f8b1ba8a2c3588f319f6b426aa07e97d2e17b24 Mon Sep 17 00:00:00 2001 From: Philip Potter Date: Sun, 28 Aug 2016 18:05:40 +0100 Subject: docs/library: Add reference for pyb.usb_mode and pyb.USB_HID. --- docs/library/pyb.USB_HID.rst | 28 ++++++++++++++++++++++++++++ docs/library/pyb.rst | 30 +++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 docs/library/pyb.USB_HID.rst diff --git a/docs/library/pyb.USB_HID.rst b/docs/library/pyb.USB_HID.rst new file mode 100644 index 000000000..65fb4014e --- /dev/null +++ b/docs/library/pyb.USB_HID.rst @@ -0,0 +1,28 @@ +.. currentmodule:: pyb + +class USB_HID -- USB Human Interface Device (HID) +================================================= + +The USB_HID class allows creation of an object representing the USB +Human Interface Device (HID) interface. It can be used to emulate +a peripheral such as a mouse or keyboard. + +Before you can use this class, you need to use :meth:`pyb.usb_mode()` to set the USB mode to include the HID interface. + +Constructors +------------ + +.. class:: pyb.USB_HID() + + Create a new USB_HID object. + + +Methods +------- + +.. method:: USB_HID.send(data) + + Send data over the USB HID interface: + + - ``data`` is the data to send (a tuple/list of integers, or a + bytearray). diff --git a/docs/library/pyb.rst b/docs/library/pyb.rst index 2f3e7d36b..910b2f45b 100644 --- a/docs/library/pyb.rst +++ b/docs/library/pyb.rst @@ -188,7 +188,7 @@ Miscellaneous functions Takes a 4-tuple (or list) and sends it to the USB host (the PC) to signal a HID mouse-motion event. - .. note:: This function is deprecated. Use pyb.USB_HID().send(...) instead. + .. note:: This function is deprecated. Use :meth:`pyb.USB_HID.send()` instead. .. function:: info([dump_alloc_table]) @@ -254,6 +254,33 @@ Miscellaneous functions Returns a string of 12 bytes (96 bits), which is the unique ID of the MCU. +.. function:: usb_mode([modestr], vid=0xf055, pid=0x9801, hid=pyb.hid_mouse) + + If called with no arguments, return the current USB mode as a string. + + If called with ``modestr`` provided, attempts to set USB mode. + This can only be done when called from ``boot.py`` before + :meth:`pyb.main()` has been called. The following values of + ``modestr`` are understood: + + - ``None``: disables USB + - ``'VCP'``: enable with VCP (Virtual COM Port) interface + - ``'VCP+MSC'``: enable with VCP and MSC (mass storage device class) + - ``'VCP+HID'``: enable with VCP and HID (human interface device) + + For backwards compatibility, ``'CDC'`` is understood to mean + ``'VCP'`` (and similarly for ``'CDC+MSC'`` and ``'CDC+HID'``). + + The ``vid`` and ``pid`` parameters allow you to specify the VID + (vendor id) and PID (product id). + + If enabling HID mode, you may also specify the HID details by + passing the ``hid`` keyword parameter. It takes a tuple of + (subclass, protocol, max packet length, polling interval, report + descriptor). By default it will set appropriate values for a USB + mouse. There is also a ``pyb.hid_keyboard`` constant, which is an + appropriate tuple for a USB keyboard. + Classes ------- @@ -277,4 +304,5 @@ Classes pyb.Switch.rst pyb.Timer.rst pyb.UART.rst + pyb.USB_HID.rst pyb.USB_VCP.rst -- cgit v1.2.3 From c777b6950eed1b48bb88fb8b343eeed85b13560f Mon Sep 17 00:00:00 2001 From: Philip Potter Date: Sun, 28 Aug 2016 18:19:08 +0100 Subject: stmhal: Update boot.py files to use VCP instead of CDC. --- examples/SDdatalogger/boot.py | 4 ++-- stmhal/main.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/SDdatalogger/boot.py b/examples/SDdatalogger/boot.py index aa943fde9..4ac94bbaa 100644 --- a/examples/SDdatalogger/boot.py +++ b/examples/SDdatalogger/boot.py @@ -16,10 +16,10 @@ pyb.LED(3).off() # indicate that we finished waiting for the swit pyb.LED(4).on() # indicate that we are selecting the mode if switch_value: - pyb.usb_mode('CDC+MSC') + pyb.usb_mode('VCP+MSC') pyb.main('cardreader.py') # if switch was pressed, run this else: - pyb.usb_mode('CDC+HID') + pyb.usb_mode('VCP+HID') pyb.main('datalogger.py') # if switch wasn't pressed, run this pyb.LED(4).off() # indicate that we finished selecting the mode diff --git a/stmhal/main.c b/stmhal/main.c index 30dddaf98..32baae532 100644 --- a/stmhal/main.c +++ b/stmhal/main.c @@ -137,8 +137,8 @@ static const char fresh_boot_py[] = "import machine\r\n" "import pyb\r\n" "#pyb.main('main.py') # main script to run after this one\r\n" -"#pyb.usb_mode('CDC+MSC') # act as a serial and a storage device\r\n" -"#pyb.usb_mode('CDC+HID') # act as a serial device and a mouse\r\n" +"#pyb.usb_mode('VCP+MSC') # act as a serial and a storage device\r\n" +"#pyb.usb_mode('VCP+HID') # act as a serial device and a mouse\r\n" ; static const char fresh_main_py[] = -- cgit v1.2.3 From d1a366fdd4a0f60e0487bd45a8b456804c75ea5d Mon Sep 17 00:00:00 2001 From: Krzysztof Blazewicz Date: Sat, 27 Aug 2016 10:15:00 +0200 Subject: .gitignore: Add *.pyc, because Python 2 doesn't use __pycache__. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 450bbd863..a6295928d 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ tests/*.out # Python cache files ###################### __pycache__/ +*.pyc # Customized Makefile/project overrides ###################### -- cgit v1.2.3 From 5c3a2f162ec08aa2df900f25e87abb5d74e39c7c Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 29 Aug 2016 17:32:38 +1000 Subject: docs/pyboard/quickref: Add links to pinouts for other pyboard variants. --- docs/pyboard/quickref.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/pyboard/quickref.rst b/docs/pyboard/quickref.rst index 2a1429cb0..140452a9b 100644 --- a/docs/pyboard/quickref.rst +++ b/docs/pyboard/quickref.rst @@ -3,6 +3,12 @@ Quick reference for the pyboard =============================== +The below pinout is for PYBv1.0. You can also view pinouts for +other versions of the pyboard: +`PYBv1.1 `__ +or `PYBLITEv1.0-AC `__ +or `PYBLITEv1.0 `__. + .. image:: http://micropython.org/resources/pybv10-pinout.jpg :alt: PYBv1.0 pinout :width: 700px -- cgit v1.2.3 From efc904c41d4acc631ec0781a7489c382d6d0b396 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 29 Aug 2016 17:33:02 +1000 Subject: docs/pyboard/quickref: Add section on "delay and timing" for utime mod. And remove reference to deprecated pyb.delay() and pyb.millis(). --- docs/pyboard/quickref.rst | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/pyboard/quickref.rst b/docs/pyboard/quickref.rst index 140452a9b..3d08ae910 100644 --- a/docs/pyboard/quickref.rst +++ b/docs/pyboard/quickref.rst @@ -20,14 +20,25 @@ See :mod:`pyb`. :: import pyb - pyb.delay(50) # wait 50 milliseconds - pyb.millis() # number of milliseconds since bootup pyb.repl_uart(pyb.UART(1, 9600)) # duplicate REPL on UART(1) pyb.wfi() # pause CPU, waiting for interrupt pyb.freq() # get CPU and bus frequencies pyb.freq(60000000) # set CPU freq to 60MHz pyb.stop() # stop CPU, waiting for external interrupt +Delay and timing +---------------- + +Use the :mod:`time ` module:: + + import time + + time.sleep(1) # sleep for 1 second + time.sleep_ms(500) # sleep for 500 milliseconds + time.sleep_us(10) # sleep for 10 microseconds + start = time.ticks_ms() # get value of millisecond counter + delta = time.ticks_diff(start, time.ticks_ms()) # compute time difference + LEDs ---- -- cgit v1.2.3 From 76dcaddc0f647bd597013980108b0b28bb6eb1c9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 29 Aug 2016 17:40:36 +1000 Subject: docs/esp8266/quickref: Add internal links to docs for some modules. --- docs/esp8266/quickref.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index f15cb72bb..d20987136 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -23,14 +23,14 @@ Tab-completion is useful to find out what methods an object has. Paste mode (ctrl-E) is useful to paste a large slab of Python code into the REPL. -The ``machine`` module:: +The :mod:`machine` module:: import machine machine.freq() # get the current frequency of the CPU machine.freq(160000000) # set the CPU frequency to 160 MHz -The ``esp`` module:: +The :mod:`esp` module:: import esp @@ -40,7 +40,7 @@ The ``esp`` module:: Networking ---------- -The ``network`` module:: +The :mod:`network` module:: import network @@ -69,13 +69,13 @@ A useful function for connecting to your local WiFi network is:: pass print('network config:', wlan.ifconfig()) -Once the network is established the ``socket`` module can be used +Once the network is established the :mod:`socket ` module can be used to create and use TCP/UDP sockets as usual. Delay and timing ---------------- -Use the ``time`` module:: +Use the :mod:`time ` module:: import time -- cgit v1.2.3 From bae62d9abe3db0f60f681f79b684ec7e5fb25fda Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 30 Aug 2016 00:58:42 +0300 Subject: lib/utils/pyexec: qstr_pool_info() requires size_t* parameters. --- lib/utils/pyexec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index 411ee969d..2bc7a00cc 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -112,7 +112,7 @@ STATIC int parse_compile_execute(void *source, mp_parse_input_kind_t input_kind, printf("took " UINT_FMT " ms\n", ticks); // qstr info { - mp_uint_t n_pool, n_qstr, n_str_data_bytes, n_total_bytes; + size_t n_pool, n_qstr, n_str_data_bytes, n_total_bytes; qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes); printf("qstr:\n n_pool=" UINT_FMT "\n n_qstr=" UINT_FMT "\n n_str_data_bytes=" UINT_FMT "\n n_total_bytes=" UINT_FMT "\n", n_pool, n_qstr, n_str_data_bytes, n_total_bytes); } -- cgit v1.2.3 From 1f61fe07a2bca5374fdb402a41785a81b193f5ed Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 30 Aug 2016 00:59:17 +0300 Subject: py/mkrules.mk: Allow to override name of libmicropython.a Or alternatively, refer to an exact library file, not just phony target "lib". --- py/mkrules.mk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/py/mkrules.mk b/py/mkrules.mk index 3cf0e3058..a3a408dc8 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -123,8 +123,9 @@ clean-prog: .PHONY: clean-prog endif -lib: $(OBJ) - $(AR) rcs libmicropython.a $^ +LIBMICROPYTHON = libmicropython.a +lib $(LIBMICROPYTHON): $(OBJ) + $(AR) rcs $(LIBMICROPYTHON) $^ clean: $(RM) -rf $(BUILD) $(CLEAN_EXTRA) -- cgit v1.2.3 From 9c04ef2a6772cabbd92abe6c826b56cc04a1a7b8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 31 Aug 2016 15:12:57 +1000 Subject: unix,stmhal,esp8266: When find'ing frozen files don't use extra slash. This extra forward slash for the starting-point directory is unnecessary and leads to additional slashes on Max OS X which mean that the frozen files cannot be imported. Fixes #2374. --- esp8266/Makefile | 2 +- stmhal/Makefile | 2 +- unix/Makefile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esp8266/Makefile b/esp8266/Makefile index 7da83f1aa..278651038 100644 --- a/esp8266/Makefile +++ b/esp8266/Makefile @@ -141,7 +141,7 @@ DRIVERS_SRC_C = $(addprefix drivers/,\ SRC_S = \ gchelper.s \ -FROZEN_MPY_PY_FILES := $(shell find $(FROZEN_MPY_DIR)/ -type f -name '*.py') +FROZEN_MPY_PY_FILES := $(shell find $(FROZEN_MPY_DIR) -type f -name '*.py') FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) OBJ = diff --git a/stmhal/Makefile b/stmhal/Makefile index 1ad2783fc..2c6ea806e 100644 --- a/stmhal/Makefile +++ b/stmhal/Makefile @@ -284,7 +284,7 @@ endif ifneq ($(FROZEN_MPY_DIR),) # To use frozen bytecode, put your .py files in a subdirectory (eg frozen/) and # then invoke make with FROZEN_MPY_DIR=frozen (be sure to build from scratch). -FROZEN_MPY_PY_FILES := $(shell find $(FROZEN_MPY_DIR)/ -type f -name '*.py') +FROZEN_MPY_PY_FILES := $(shell find $(FROZEN_MPY_DIR) -type f -name '*.py') FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) CFLAGS += -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool CFLAGS += -DMICROPY_MODULE_FROZEN_MPY diff --git a/unix/Makefile b/unix/Makefile index cb9a999f0..3afa80dfa 100644 --- a/unix/Makefile +++ b/unix/Makefile @@ -183,7 +183,7 @@ ifneq ($(FROZEN_MPY_DIR),) # then invoke make with FROZEN_MPY_DIR=frozen (be sure to build from scratch). MPY_CROSS = ../mpy-cross/mpy-cross MPY_TOOL = ../tools/mpy-tool.py -FROZEN_MPY_PY_FILES := $(shell find $(FROZEN_MPY_DIR)/ -type f -name '*.py') +FROZEN_MPY_PY_FILES := $(shell find $(FROZEN_MPY_DIR) -type f -name '*.py') FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) CFLAGS += -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool CFLAGS += -DMICROPY_MODULE_FROZEN_MPY -- cgit v1.2.3 From 0823c1baf864f2e8fd60e49a5e42a958fd89ff43 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Sep 2016 15:07:20 +1000 Subject: extmod: Add machine_spi with generic SPI C-protocol and helper methods. The idea is that all ports can use these helper methods and only need to provide initialisation of the SPI bus, as well as a single transfer function. The coding pattern follows the stream protocol and helper methods. --- extmod/machine_spi.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++ extmod/machine_spi.h | 42 ++++++++++++++++++++++++++ py/mpconfig.h | 4 +++ py/py.mk | 1 + 4 files changed, 132 insertions(+) create mode 100644 extmod/machine_spi.c create mode 100644 extmod/machine_spi.h diff --git a/extmod/machine_spi.c b/extmod/machine_spi.c new file mode 100644 index 000000000..6b6202a22 --- /dev/null +++ b/extmod/machine_spi.c @@ -0,0 +1,85 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 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 + +#include "py/runtime.h" +#include "extmod/machine_spi.h" + +#if MICROPY_PY_MACHINE_SPI + +STATIC void mp_machine_spi_transfer(mp_obj_t self, size_t slen, const uint8_t *src, size_t dlen, uint8_t *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; + spi_p->transfer(s, slen, src, dlen, dest); +} + +STATIC mp_obj_t mp_machine_spi_read(size_t n_args, const mp_obj_t *args) { + uint8_t write_byte = 0; + if (n_args == 3) { + write_byte = mp_obj_get_int(args[2]); + } + vstr_t vstr; + vstr_init_len(&vstr, mp_obj_get_int(args[1])); + mp_machine_spi_transfer(args[0], 1, &write_byte, vstr.len, (uint8_t*)vstr.buf); + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj, 2, 3, mp_machine_spi_read); + +STATIC mp_obj_t mp_machine_spi_readinto(size_t n_args, const mp_obj_t *args) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); + uint8_t write_byte = 0; + if (n_args == 3) { + write_byte = mp_obj_get_int(args[2]); + } + mp_machine_spi_transfer(args[0], 1, &write_byte, bufinfo.len, (uint8_t*)bufinfo.buf); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj, 2, 3, mp_machine_spi_readinto); + +STATIC mp_obj_t mp_machine_spi_write(mp_obj_t self, mp_obj_t wr_buf) { + mp_buffer_info_t src; + mp_get_buffer_raise(wr_buf, &src, MP_BUFFER_READ); + mp_machine_spi_transfer(self, src.len, (const uint8_t*)src.buf, 0, NULL); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj, mp_machine_spi_write); + +STATIC mp_obj_t mp_machine_spi_write_readinto(mp_obj_t self, mp_obj_t wr_buf, mp_obj_t rd_buf) { + mp_buffer_info_t src; + mp_get_buffer_raise(wr_buf, &src, MP_BUFFER_READ); + mp_buffer_info_t dest; + mp_get_buffer_raise(rd_buf, &dest, MP_BUFFER_WRITE); + if (src.len != dest.len) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "buffers must be the same length")); + } + mp_machine_spi_transfer(self, src.len, (const uint8_t*)src.buf, dest.len, (uint8_t*)dest.buf); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj, mp_machine_spi_write_readinto); + +#endif // MICROPY_PY_MACHINE_SPI diff --git a/extmod/machine_spi.h b/extmod/machine_spi.h new file mode 100644 index 000000000..26d716fc1 --- /dev/null +++ b/extmod/machine_spi.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 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. + */ + +#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H +#define MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H + +#include "py/obj.h" + +// SPI protocol +typedef struct _mp_machine_spi_p_t { + void (*transfer)(mp_obj_base_t *obj, size_t slen, const uint8_t *src, size_t dlen, uint8_t *dest); +} mp_machine_spi_p_t; + +MP_DECLARE_CONST_FUN_OBJ(mp_machine_spi_read_obj); +MP_DECLARE_CONST_FUN_OBJ(mp_machine_spi_readinto_obj); +MP_DECLARE_CONST_FUN_OBJ(mp_machine_spi_write_obj); +MP_DECLARE_CONST_FUN_OBJ(mp_machine_spi_write_readinto_obj); + +#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H diff --git a/py/mpconfig.h b/py/mpconfig.h index 23591e0da..455f870ac 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -923,6 +923,10 @@ typedef double mp_float_t; #define MICROPY_PY_MACHINE_I2C (0) #endif +#ifndef MICROPY_PY_MACHINE_SPI +#define MICROPY_PY_MACHINE_SPI (0) +#endif + #ifndef MICROPY_PY_USSL #define MICROPY_PY_USSL (0) #endif diff --git a/py/py.mk b/py/py.mk index a110c9f60..abea7215b 100644 --- a/py/py.mk +++ b/py/py.mk @@ -208,6 +208,7 @@ PY_O_BASENAME = \ ../extmod/machine_pinbase.o \ ../extmod/machine_pulse.o \ ../extmod/machine_i2c.o \ + ../extmod/machine_spi.o \ ../extmod/modussl_axtls.o \ ../extmod/modurandom.o \ ../extmod/modwebsocket.o \ -- cgit v1.2.3 From 5863e15a23910f46c716876e171a23008993db77 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Sep 2016 15:10:27 +1000 Subject: esp8266/modpybspi: Use generic SPI helper methods to implement SPI. --- esp8266/modpybspi.c | 66 +++++++++----------------------------------------- esp8266/mpconfigport.h | 1 + 2 files changed, 13 insertions(+), 54 deletions(-) diff --git a/esp8266/modpybspi.c b/esp8266/modpybspi.c index fafe3b2fa..4c4b843c5 100644 --- a/esp8266/modpybspi.c +++ b/esp8266/modpybspi.c @@ -35,6 +35,7 @@ #include "py/runtime.h" #include "py/stream.h" #include "py/mphal.h" +#include "extmod/machine_spi.h" typedef struct _pyb_spi_obj_t { mp_obj_base_t base; @@ -46,7 +47,8 @@ typedef struct _pyb_spi_obj_t { mp_hal_pin_obj_t miso; } pyb_spi_obj_t; -STATIC void mp_hal_spi_transfer(pyb_spi_obj_t *self, size_t src_len, const uint8_t *src_buf, size_t dest_len, uint8_t *dest_buf) { +STATIC void mp_hal_spi_transfer(mp_obj_base_t *self_in, size_t src_len, const uint8_t *src_buf, size_t dest_len, uint8_t *dest_buf) { + pyb_spi_obj_t *self = (pyb_spi_obj_t*)self_in; // only MSB transfer is implemented uint32_t delay_half = 500000 / self->baudrate + 1; for (size_t i = 0; i < src_len || i < dest_len; ++i) { @@ -154,69 +156,25 @@ STATIC mp_obj_t pyb_spi_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_a } MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_init_obj, 1, pyb_spi_init); -STATIC mp_obj_t pyb_spi_read(size_t n_args, const mp_obj_t *args) { - pyb_spi_obj_t *self = MP_OBJ_TO_PTR(args[0]); - uint8_t write_byte = 0; - if (n_args == 3) { - write_byte = mp_obj_get_int(args[2]); - } - vstr_t vstr; - vstr_init_len(&vstr, mp_obj_get_int(args[1])); - mp_hal_spi_transfer(self, 1, &write_byte, vstr.len, (uint8_t*)vstr.buf); - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_spi_read_obj, 2, 3, pyb_spi_read); - -STATIC mp_obj_t pyb_spi_readinto(size_t n_args, const mp_obj_t *args) { - pyb_spi_obj_t *self = MP_OBJ_TO_PTR(args[0]); - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); - uint8_t write_byte = 0; - if (n_args == 3) { - write_byte = mp_obj_get_int(args[2]); - } - mp_hal_spi_transfer(self, 1, &write_byte, bufinfo.len, (uint8_t*)bufinfo.buf); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_spi_readinto_obj, 2, 3, pyb_spi_readinto); - -STATIC mp_obj_t pyb_spi_write(mp_obj_t self_in, mp_obj_t wr_buf_in) { - pyb_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_buffer_info_t src_buf; - mp_get_buffer_raise(wr_buf_in, &src_buf, MP_BUFFER_READ); - mp_hal_spi_transfer(self, src_buf.len, (const uint8_t*)src_buf.buf, 0, NULL); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_2(pyb_spi_write_obj, pyb_spi_write); - -STATIC mp_obj_t pyb_spi_write_readinto(mp_obj_t self_in, mp_obj_t wr_buf_in, mp_obj_t rd_buf_in) { - pyb_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_buffer_info_t src_buf; - mp_get_buffer_raise(wr_buf_in, &src_buf, MP_BUFFER_READ); - mp_buffer_info_t dest_buf; - mp_get_buffer_raise(rd_buf_in, &dest_buf, MP_BUFFER_WRITE); - if (src_buf.len != dest_buf.len) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "buffers must be the same length")); - } - mp_hal_spi_transfer(self, src_buf.len, (const uint8_t*)src_buf.buf, dest_buf.len, (uint8_t*)dest_buf.buf); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_3(pyb_spi_write_readinto_obj, pyb_spi_write_readinto); - STATIC const mp_rom_map_elem_t pyb_spi_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_spi_init_obj) }, - { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&pyb_spi_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&pyb_spi_readinto_obj) }, - { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&pyb_spi_write_obj) }, - { MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&pyb_spi_write_readinto_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) }, }; STATIC MP_DEFINE_CONST_DICT(pyb_spi_locals_dict, pyb_spi_locals_dict_table); +STATIC const mp_machine_spi_p_t pyb_spi_p = { + .transfer = mp_hal_spi_transfer, +}; + const mp_obj_type_t pyb_spi_type = { { &mp_type_type }, .name = MP_QSTR_SoftSPI, .print = pyb_spi_print, .make_new = pyb_spi_make_new, + .protocol = &pyb_spi_p, .locals_dict = (mp_obj_dict_t*)&pyb_spi_locals_dict, }; diff --git a/esp8266/mpconfigport.h b/esp8266/mpconfigport.h index 63b9258a2..4fc8d856b 100644 --- a/esp8266/mpconfigport.h +++ b/esp8266/mpconfigport.h @@ -63,6 +63,7 @@ #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_WEBSOCKET (1) #define MICROPY_PY_WEBREPL (1) #define MICROPY_PY_WEBREPL_DELAY (20) -- cgit v1.2.3 From 3be8b688c04d7210872d099d9d65269bbe5c13c5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Sep 2016 15:29:20 +1000 Subject: esp8266/modpybhspi: Simplify HSPI driver by using 1 function for xfers. --- esp8266/modpybhspi.c | 179 ++++++++++++++++++--------------------------------- 1 file changed, 62 insertions(+), 117 deletions(-) diff --git a/esp8266/modpybhspi.c b/esp8266/modpybhspi.c index f80fbae00..c4d4dcee8 100644 --- a/esp8266/modpybhspi.c +++ b/esp8266/modpybhspi.c @@ -35,6 +35,7 @@ #include "py/runtime.h" #include "py/stream.h" #include "py/mphal.h" +#include "extmod/machine_spi.h" #include "hspi.h" @@ -47,6 +48,58 @@ typedef struct _pyb_hspi_obj_t { } pyb_hspi_obj_t; +STATIC void hspi_transfer(mp_obj_base_t *self_in, size_t src_len, const uint8_t *src_buf, size_t dest_len, uint8_t *dest_buf) { + (void)self_in; + + if (dest_len == 0) { + // fast case when we only need to write data + size_t chunk_size = 1024; + size_t count = src_len / chunk_size; + size_t i = 0; + for (size_t j = 0; j < count; ++j) { + for (size_t k = 0; k < chunk_size; ++k) { + spi_tx8fast(HSPI, src_buf[i]); + ++i; + } + ets_loop_iter(); + } + while (i < src_len) { + spi_tx8fast(HSPI, src_buf[i]); + ++i; + } + } else { + // we need to read and write data + + // Process data in chunks, let the pending tasks run in between + size_t chunk_size = 1024; // TODO this should depend on baudrate + size_t count = dest_len / chunk_size; + size_t i = 0; + for (size_t j = 0; j < count; ++j) { + for (size_t k = 0; k < chunk_size; ++k) { + uint32_t data_out; + if (src_len == 1) { + data_out = src_buf[0]; + } else { + data_out = src_buf[i]; + } + dest_buf[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8, data_out, 8, 0); + ++i; + } + ets_loop_iter(); + } + while (i < dest_len) { + uint32_t data_out; + if (src_len == 1) { + data_out = src_buf[0]; + } else { + data_out = src_buf[i]; + } + dest_buf[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8, data_out, 8, 0); + ++i; + } + } +} + /******************************************************************************/ // MicroPython bindings for HSPI @@ -126,133 +179,25 @@ STATIC mp_obj_t pyb_hspi_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_ } MP_DEFINE_CONST_FUN_OBJ_KW(pyb_hspi_init_obj, 1, pyb_hspi_init); - -STATIC mp_obj_t pyb_hspi_read(size_t n_args, const mp_obj_t *args) { - vstr_t dest_buf; - vstr_init_len(&dest_buf, mp_obj_get_int(args[1])); - uint8_t write_byte = 0; - if (n_args == 3) { - write_byte = mp_obj_get_int(args[2]); - } - // Process data in chunks, let the pending tasks run in between - size_t chunk_size = 1024; // TODO this should depend on baudrate - size_t count = dest_buf.len / chunk_size; - size_t i = 0; - for (size_t j = 0; j < count; ++j) { - for (size_t k = 0; k < chunk_size; ++k) { - ((uint8_t*)dest_buf.buf)[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8, - (uint32_t)write_byte, 8, 0); - ++i; - } - ets_loop_iter(); - } - while (i < dest_buf.len) { - ((uint8_t*)dest_buf.buf)[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8, - (uint32_t)write_byte, 8, 0); - ++i; - } - return mp_obj_new_str_from_vstr(&mp_type_bytes, &dest_buf); -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_hspi_read_obj, 2, 3, pyb_hspi_read); - - -STATIC mp_obj_t pyb_hspi_readinto(size_t n_args, const mp_obj_t *args) { - mp_buffer_info_t dest_buf; - mp_get_buffer_raise(args[1], &dest_buf, MP_BUFFER_WRITE); - uint8_t write_byte = 0; - if (n_args == 3) { - write_byte = mp_obj_get_int(args[2]); - } - - size_t chunk_size = 1024; - size_t count = dest_buf.len / chunk_size; - size_t i = 0; - for (size_t j = 0; j < count; ++j) { - for (size_t k = 0; k < chunk_size; ++k) { - ((uint8_t*)dest_buf.buf)[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8, - (uint32_t)write_byte, 8, 0); - ++i; - } - ets_loop_iter(); - } - while (i < dest_buf.len) { - ((uint8_t*)dest_buf.buf)[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8, - (uint32_t)write_byte, 8, 0); - ++i; - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_hspi_readinto_obj, 2, 3, pyb_hspi_readinto); - - -STATIC mp_obj_t pyb_hspi_write(mp_obj_t self_in, mp_obj_t wr_buf_in) { - mp_buffer_info_t src_buf; - mp_get_buffer_raise(wr_buf_in, &src_buf, MP_BUFFER_READ); - - size_t chunk_size = 1024; - size_t count = src_buf.len / chunk_size; - size_t i = 0; - for (size_t j = 0; j < count; ++j) { - for (size_t k = 0; k < chunk_size; ++k) { - spi_tx8fast(HSPI, ((const uint8_t*)src_buf.buf)[i]); - ++i; - } - ets_loop_iter(); - } - while (i < src_buf.len) { - spi_tx8fast(HSPI, ((const uint8_t*)src_buf.buf)[i]); - ++i; - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_2(pyb_hspi_write_obj, pyb_hspi_write); - - -STATIC mp_obj_t pyb_hspi_write_readinto(mp_obj_t self_in, mp_obj_t wr_buf_in, mp_obj_t rd_buf_in) { - mp_buffer_info_t src_buf; - mp_get_buffer_raise(wr_buf_in, &src_buf, MP_BUFFER_READ); - mp_buffer_info_t dest_buf; - mp_get_buffer_raise(rd_buf_in, &dest_buf, MP_BUFFER_WRITE); - if (src_buf.len != dest_buf.len) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "buffers must be the same length")); - } - - size_t chunk_size = 1024; - size_t count = src_buf.len / chunk_size; - size_t i = 0; - for (size_t j = 0; j < count; ++j) { - for (size_t k = 0; k < chunk_size; ++k) { - ((uint8_t*)dest_buf.buf)[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8, - (uint32_t)(((const uint8_t*)src_buf.buf)[i]), 8, 0); - ++i; - } - ets_loop_iter(); - } - while (i < src_buf.len) { - ((uint8_t*)dest_buf.buf)[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8, - (uint32_t)(((const uint8_t*)src_buf.buf)[i]), 8, 0); - ++i; - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_3(pyb_hspi_write_readinto_obj, pyb_hspi_write_readinto); - - STATIC const mp_rom_map_elem_t pyb_hspi_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_hspi_init_obj) }, - { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&pyb_hspi_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&pyb_hspi_readinto_obj) }, - { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&pyb_hspi_write_obj) }, - { MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&pyb_hspi_write_readinto_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) }, }; STATIC MP_DEFINE_CONST_DICT(pyb_hspi_locals_dict, pyb_hspi_locals_dict_table); +STATIC const mp_machine_spi_p_t pyb_hspi_p = { + .transfer = hspi_transfer, +}; + const mp_obj_type_t pyb_hspi_type = { { &mp_type_type }, .name = MP_QSTR_HSPI, .print = pyb_hspi_print, .make_new = pyb_hspi_make_new, + .protocol = &pyb_hspi_p, .locals_dict = (mp_obj_dict_t*)&pyb_hspi_locals_dict, }; -- cgit v1.2.3 From 9b64d1966b831be971e6f743a7d2a93047205c05 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Sep 2016 16:36:41 +1000 Subject: stmhal/spi: Factor out SPI transfer code to a single function. --- stmhal/spi.c | 147 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 77 insertions(+), 70 deletions(-) diff --git a/stmhal/spi.c b/stmhal/spi.c index fbc5f9aa4..c72c3662c 100644 --- a/stmhal/spi.c +++ b/stmhal/spi.c @@ -327,6 +327,79 @@ STATIC HAL_StatusTypeDef spi_wait_dma_finished(SPI_HandleTypeDef *spi, uint32_t return HAL_OK; } +STATIC void spi_transfer(mp_obj_base_t *self_in, size_t src_len, const uint8_t *src_buf, size_t dest_len, uint8_t *dest_buf, uint32_t timeout) { + // Note: there seems to be a problem sending 1 byte using DMA the first + // time directly after the SPI/DMA is initialised. The cause of this is + // unknown but we sidestep the issue by using polling for 1 byte transfer. + + pyb_spi_obj_t *self = (pyb_spi_obj_t*)self_in; + HAL_StatusTypeDef status; + + if (dest_len == 0) { + // send only + if (src_len == 1 || query_irq() == IRQ_STATE_DISABLED) { + status = HAL_SPI_Transmit(self->spi, (uint8_t*)src_buf, src_len, timeout); + } else { + DMA_HandleTypeDef tx_dma; + dma_init(&tx_dma, self->tx_dma_descr, self->spi); + self->spi->hdmatx = &tx_dma; + self->spi->hdmarx = NULL; + status = HAL_SPI_Transmit_DMA(self->spi, (uint8_t*)src_buf, src_len); + if (status == HAL_OK) { + status = spi_wait_dma_finished(self->spi, timeout); + } + dma_deinit(self->tx_dma_descr); + } + } else if (src_len == 0) { + // receive only + if (dest_len == 1 || query_irq() == IRQ_STATE_DISABLED) { + status = HAL_SPI_Receive(self->spi, dest_buf, dest_len, timeout); + } else { + DMA_HandleTypeDef tx_dma, rx_dma; + if (self->spi->Init.Mode == SPI_MODE_MASTER) { + // in master mode the HAL actually does a TransmitReceive call + dma_init(&tx_dma, self->tx_dma_descr, self->spi); + self->spi->hdmatx = &tx_dma; + } else { + self->spi->hdmatx = NULL; + } + dma_init(&rx_dma, self->rx_dma_descr, self->spi); + self->spi->hdmarx = &rx_dma; + + status = HAL_SPI_Receive_DMA(self->spi, dest_buf, dest_len); + if (status == HAL_OK) { + status = spi_wait_dma_finished(self->spi, timeout); + } + if (self->spi->hdmatx != NULL) { + dma_deinit(self->tx_dma_descr); + } + dma_deinit(self->rx_dma_descr); + } + } else { + // send and receive + // requires src_len==dest_len + if (src_len == 1 || query_irq() == IRQ_STATE_DISABLED) { + status = HAL_SPI_TransmitReceive(self->spi, (uint8_t*)src_buf, dest_buf, src_len, timeout); + } else { + DMA_HandleTypeDef tx_dma, rx_dma; + dma_init(&tx_dma, self->tx_dma_descr, self->spi); + self->spi->hdmatx = &tx_dma; + dma_init(&rx_dma, self->rx_dma_descr, self->spi); + self->spi->hdmarx = &rx_dma; + status = HAL_SPI_TransmitReceive_DMA(self->spi, (uint8_t*)src_buf, dest_buf, src_len); + if (status == HAL_OK) { + status = spi_wait_dma_finished(self->spi, timeout); + } + dma_deinit(self->tx_dma_descr); + dma_deinit(self->rx_dma_descr); + } + } + + if (status != HAL_OK) { + mp_hal_raise(status); + } +} + /******************************************************************************/ /* Micro Python bindings */ @@ -556,27 +629,7 @@ STATIC mp_obj_t pyb_spi_send(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_ pyb_buf_get_for_send(args[0].u_obj, &bufinfo, data); // send the data - // Note: there seems to be a problem sending 1 byte using DMA the first - // time directly after the SPI/DMA is initialised. The cause of this is - // unknown but we sidestep the issue by using polling for 1 byte transfer. - HAL_StatusTypeDef status; - if (bufinfo.len == 1 || query_irq() == IRQ_STATE_DISABLED) { - status = HAL_SPI_Transmit(self->spi, bufinfo.buf, bufinfo.len, args[1].u_int); - } else { - DMA_HandleTypeDef tx_dma; - dma_init(&tx_dma, self->tx_dma_descr, self->spi); - self->spi->hdmatx = &tx_dma; - self->spi->hdmarx = NULL; - status = HAL_SPI_Transmit_DMA(self->spi, bufinfo.buf, bufinfo.len); - if (status == HAL_OK) { - status = spi_wait_dma_finished(self->spi, args[1].u_int); - } - dma_deinit(self->tx_dma_descr); - } - - if (status != HAL_OK) { - mp_hal_raise(status); - } + spi_transfer((mp_obj_base_t*)self, bufinfo.len, bufinfo.buf, 0, NULL, args[1].u_int); return mp_const_none; } @@ -610,34 +663,7 @@ STATIC mp_obj_t pyb_spi_recv(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_ mp_obj_t o_ret = pyb_buf_get_for_recv(args[0].u_obj, &vstr); // receive the data - HAL_StatusTypeDef status; - if (vstr.len == 1 || query_irq() == IRQ_STATE_DISABLED) { - status = HAL_SPI_Receive(self->spi, (uint8_t*)vstr.buf, vstr.len, args[1].u_int); - } else { - DMA_HandleTypeDef tx_dma, rx_dma; - if (self->spi->Init.Mode == SPI_MODE_MASTER) { - // in master mode the HAL actually does a TransmitReceive call - dma_init(&tx_dma, self->tx_dma_descr, self->spi); - self->spi->hdmatx = &tx_dma; - } else { - self->spi->hdmatx = NULL; - } - dma_init(&rx_dma, self->rx_dma_descr, self->spi); - self->spi->hdmarx = &rx_dma; - - status = HAL_SPI_Receive_DMA(self->spi, (uint8_t*)vstr.buf, vstr.len); - if (status == HAL_OK) { - status = spi_wait_dma_finished(self->spi, args[1].u_int); - } - if (self->spi->hdmatx != NULL) { - dma_deinit(self->tx_dma_descr); - } - dma_deinit(self->rx_dma_descr); - } - - if (status != HAL_OK) { - mp_hal_raise(status); - } + spi_transfer((mp_obj_base_t*)self, 0, NULL, vstr.len, (uint8_t*)vstr.buf, args[1].u_int); // return the received data if (o_ret != MP_OBJ_NULL) { @@ -706,27 +732,8 @@ STATIC mp_obj_t pyb_spi_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, mp } } - // send and receive the data - HAL_StatusTypeDef status; - if (bufinfo_send.len == 1 || query_irq() == IRQ_STATE_DISABLED) { - status = HAL_SPI_TransmitReceive(self->spi, bufinfo_send.buf, bufinfo_recv.buf, bufinfo_send.len, args[2].u_int); - } else { - DMA_HandleTypeDef tx_dma, rx_dma; - dma_init(&tx_dma, self->tx_dma_descr, self->spi); - self->spi->hdmatx = &tx_dma; - dma_init(&rx_dma, self->rx_dma_descr, self->spi); - self->spi->hdmarx = &rx_dma; - status = HAL_SPI_TransmitReceive_DMA(self->spi, bufinfo_send.buf, bufinfo_recv.buf, bufinfo_send.len); - if (status == HAL_OK) { - status = spi_wait_dma_finished(self->spi, args[2].u_int); - } - dma_deinit(self->tx_dma_descr); - dma_deinit(self->rx_dma_descr); - } - - if (status != HAL_OK) { - mp_hal_raise(status); - } + // do the transfer + spi_transfer((mp_obj_base_t*)self, bufinfo_send.len, bufinfo_send.buf, bufinfo_recv.len, bufinfo_recv.buf, args[2].u_int); // return the received data if (o_ret != MP_OBJ_NULL) { -- cgit v1.2.3 From 49406b0ac6a10406e1505d3fda2447571649f853 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Sep 2016 16:37:15 +1000 Subject: stmhal/spi: Support new machine SPI methods in legacy SPI object. --- stmhal/mpconfigport.h | 1 + stmhal/spi.c | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/stmhal/mpconfigport.h b/stmhal/mpconfigport.h index 3122a9065..98d1e17e3 100644 --- a/stmhal/mpconfigport.h +++ b/stmhal/mpconfigport.h @@ -93,6 +93,7 @@ #define MICROPY_PY_UHASHLIB (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_FRAMEBUF (1) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) diff --git a/stmhal/spi.c b/stmhal/spi.c index c72c3662c..85f170920 100644 --- a/stmhal/spi.c +++ b/stmhal/spi.c @@ -30,6 +30,7 @@ #include "py/nlr.h" #include "py/runtime.h" #include "py/mphal.h" +#include "extmod/machine_spi.h" #include "irq.h" #include "pin.h" #include "genhdr/pins.h" @@ -400,6 +401,17 @@ STATIC void spi_transfer(mp_obj_base_t *self_in, size_t src_len, const uint8_t * } } +STATIC void spi_transfer_machine(mp_obj_base_t *self_in, size_t src_len, const uint8_t *src_buf, size_t dest_len, uint8_t *dest_buf) { + if (src_len == 1 && dest_len > 1) { + // this catches read and readinto + // copy the single output byte to the dest buffer and use that as source + memset(dest_buf, src_buf[0], dest_len); + src_len = dest_len; + src_buf = dest_buf; + } + spi_transfer(self_in, src_len, src_buf, dest_len, dest_buf, 100); +} + /******************************************************************************/ /* Micro Python bindings */ @@ -748,6 +760,13 @@ STATIC const mp_map_elem_t pyb_spi_locals_dict_table[] = { // instance methods { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_spi_init_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&pyb_spi_deinit_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_machine_spi_read_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), (mp_obj_t)&mp_machine_spi_readinto_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_machine_spi_write_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_write_readinto), (mp_obj_t)&mp_machine_spi_write_readinto_obj }, + + // legacy methods { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&pyb_spi_send_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&pyb_spi_recv_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_send_recv), (mp_obj_t)&pyb_spi_send_recv_obj }, @@ -773,10 +792,15 @@ STATIC const mp_map_elem_t pyb_spi_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(pyb_spi_locals_dict, pyb_spi_locals_dict_table); +STATIC const mp_machine_spi_p_t pyb_spi_p = { + .transfer = spi_transfer_machine, +}; + const mp_obj_type_t pyb_spi_type = { { &mp_type_type }, .name = MP_QSTR_SPI, .print = pyb_spi_print, .make_new = pyb_spi_make_new, + .protocol = &pyb_spi_p, .locals_dict = (mp_obj_t)&pyb_spi_locals_dict, }; -- cgit v1.2.3 From ce1c786297e69df4a82e83ac379f8ef068e709f6 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Thu, 25 Aug 2016 10:54:59 +0200 Subject: drivers/sdcard: Port the SDCard driver to new machine API. With backwards compatibility for pyboard. --- drivers/sdcard/sdcard.py | 139 +++++++++++++++++++++++++++-------------------- 1 file changed, 79 insertions(+), 60 deletions(-) diff --git a/drivers/sdcard/sdcard.py b/drivers/sdcard/sdcard.py index fba383ae3..fc7a8af7c 100644 --- a/drivers/sdcard/sdcard.py +++ b/drivers/sdcard/sdcard.py @@ -4,31 +4,41 @@ Micro Python driver for SD cards using SPI bus. Requires an SPI bus and a CS pin. Provides readblocks and writeblocks methods so the device can be mounted as a filesystem. -Example usage: +Example usage on pyboard: import pyb, sdcard, os sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5) pyb.mount(sd, '/sd2') os.listdir('/') +Example usage on ESP8266: + + import machine, sdcard, os + sd = sdcard.SDCard(machine.SPI(0), machine.Pin(15)) + os.umount() + os.VfsFat(sd, "") + os.listdir() + """ -import pyb +import time -class SDCard: - CMD_TIMEOUT = const(100) - - R1_IDLE_STATE = const(1 << 0) - #R1_ERASE_RESET = const(1 << 1) - R1_ILLEGAL_COMMAND = const(1 << 2) - #R1_COM_CRC_ERROR = const(1 << 3) - #R1_ERASE_SEQUENCE_ERROR = const(1 << 4) - #R1_ADDRESS_ERROR = const(1 << 5) - #R1_PARAMETER_ERROR = const(1 << 6) - TOKEN_CMD25 = const(0xfc) - TOKEN_STOP_TRAN = const(0xfd) - TOKEN_DATA = const(0xfe) +_CMD_TIMEOUT = const(100) + +_R1_IDLE_STATE = const(1 << 0) +#R1_ERASE_RESET = const(1 << 1) +_R1_ILLEGAL_COMMAND = const(1 << 2) +#R1_COM_CRC_ERROR = const(1 << 3) +#R1_ERASE_SEQUENCE_ERROR = const(1 << 4) +#R1_ADDRESS_ERROR = const(1 << 5) +#R1_PARAMETER_ERROR = const(1 << 6) +_TOKEN_CMD25 = const(0xfc) +_TOKEN_STOP_TRAN = const(0xfd) +_TOKEN_DATA = const(0xfe) + + +class SDCard: def __init__(self, spi, cs): self.spi = spi self.cs = cs @@ -42,30 +52,39 @@ class SDCard: # initialise the card self.init_card() + def init_spi(self, baudrate): + try: + master = self.spi.MASTER + except AttributeError: + # on ESP8266 + self.spi.init(baudrate=baudrate, phase=0, polarity=0) + else: + # on pyboard + self.spi.init(master, baudrate=baudrate, phase=0, polarity=0) + def init_card(self): # init CS pin - self.cs.high() - self.cs.init(self.cs.OUT_PP) + self.cs.init(self.cs.OUT, value=1) # init SPI bus; use low data rate for initialisation - self.spi.init(self.spi.MASTER, baudrate=100000, phase=0, polarity=0) + self.init_spi(100000) # clock card at least 100 cycles with cs high for i in range(16): - self.spi.send(0xff) + self.spi.write(b'\xff') - # CMD0: init card; should return R1_IDLE_STATE (allow 5 attempts) + # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts) for _ in range(5): - if self.cmd(0, 0, 0x95) == R1_IDLE_STATE: + if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE: break else: raise OSError("no SD card") # CMD8: determine card version r = self.cmd(8, 0x01aa, 0x87, 4) - if r == R1_IDLE_STATE: + if r == _R1_IDLE_STATE: self.init_card_v2() - elif r == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND): + elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND): self.init_card_v1() else: raise OSError("couldn't determine SD card version") @@ -86,10 +105,10 @@ class SDCard: raise OSError("can't set 512 block size") # set to high data rate now that it's initialised - self.spi.init(self.spi.MASTER, baudrate=1320000, phase=0, polarity=0) + self.init_spi(1320000) def init_card_v1(self): - for i in range(CMD_TIMEOUT): + for i in range(_CMD_TIMEOUT): self.cmd(55, 0, 0) if self.cmd(41, 0, 0) == 0: self.cdv = 512 @@ -98,8 +117,8 @@ class SDCard: raise OSError("timeout waiting for v1 card") def init_card_v2(self): - for i in range(CMD_TIMEOUT): - pyb.delay(50) + for i in range(_CMD_TIMEOUT): + time.sleep_ms(50) self.cmd(58, 0, 0, 4) self.cmd(55, 0, 0) if self.cmd(41, 0x40000000, 0) == 0: @@ -120,87 +139,87 @@ class SDCard: buf[3] = arg >> 8 buf[4] = arg buf[5] = crc - self.spi.send(buf) + self.spi.write(buf) # wait for the repsonse (response[7] == 0) - for i in range(CMD_TIMEOUT): - response = self.spi.send_recv(0xff)[0] + for i in range(_CMD_TIMEOUT): + response = self.spi.read(1, 0xff)[0] if not (response & 0x80): # this could be a big-endian integer that we are getting here for j in range(final): - self.spi.send(0xff) + self.spi.write(b'\xff') if release: self.cs.high() - self.spi.send(0xff) + self.spi.write(b'\xff') return response # timeout self.cs.high() - self.spi.send(0xff) + self.spi.write(b'\xff') return -1 def cmd_nodata(self, cmd): - self.spi.send(cmd) - self.spi.send_recv(0xff) # ignore stuff byte - for _ in range(CMD_TIMEOUT): - if self.spi.send_recv(0xff)[0] == 0xff: + self.spi.write(cmd) + self.spi.read(1, 0xff) # ignore stuff byte + for _ in range(_CMD_TIMEOUT): + if self.spi.read(1, 0xff)[0] == 0xff: self.cs.high() - self.spi.send(0xff) + self.spi.write(b'\xff') return 0 # OK self.cs.high() - self.spi.send(0xff) + self.spi.write(b'\xff') return 1 # timeout def readinto(self, buf): self.cs.low() # read until start byte (0xff) - while self.spi.send_recv(0xff)[0] != 0xfe: + while self.spi.read(1, 0xff)[0] != 0xfe: pass # read data mv = self.dummybuf_memoryview[:len(buf)] - self.spi.send_recv(mv, recv=buf) + self.spi.write_readinto(mv, buf) # read checksum - self.spi.send(0xff) - self.spi.send(0xff) + self.spi.write(b'\xff') + self.spi.write(b'\xff') self.cs.high() - self.spi.send(0xff) + self.spi.write(b'\xff') def write(self, token, buf): self.cs.low() # send: start of block, data, checksum - self.spi.send(token) - self.spi.send(buf) - self.spi.send(0xff) - self.spi.send(0xff) + self.spi.read(1, token) + self.spi.write(buf) + self.spi.write(b'\xff') + self.spi.write(b'\xff') # check the response - if (self.spi.send_recv(0xff)[0] & 0x1f) != 0x05: + if (self.spi.read(1, 0xff)[0] & 0x1f) != 0x05: self.cs.high() - self.spi.send(0xff) + self.spi.write(b'\xff') return # wait for write to finish - while self.spi.send_recv(0xff)[0] == 0: + while self.spi.read(1, 0xff)[0] == 0: pass self.cs.high() - self.spi.send(0xff) + self.spi.write(b'\xff') def write_token(self, token): self.cs.low() - self.spi.send(token) - self.spi.send(0xff) + self.spi.read(1, token) + self.spi.write(b'\xff') # wait for write to finish - while self.spi.send_recv(0xff)[0] == 0: + while self.spi.read(1, 0xff)[0] == 0x00: pass self.cs.high() - self.spi.send(0xff) + self.spi.write(b'\xff') def count(self): return self.sectors @@ -224,7 +243,7 @@ class SDCard: self.readinto(mv[offset : offset + 512]) offset += 512 nblocks -= 1 - return self.cmd_nodata(12) + return self.cmd_nodata(b'\x0c') # cmd 12 return 0 def writeblocks(self, block_num, buf): @@ -236,7 +255,7 @@ class SDCard: return 1 # send the data - self.write(TOKEN_DATA, buf) + self.write(_TOKEN_DATA, buf) else: # CMD25: set write address for first block if self.cmd(25, block_num * self.cdv, 0) != 0: @@ -245,8 +264,8 @@ class SDCard: offset = 0 mv = memoryview(buf) while nblocks: - self.write(TOKEN_CMD25, mv[offset : offset + 512]) + self.write(_TOKEN_CMD25, mv[offset : offset + 512]) offset += 512 nblocks -= 1 - self.write_token(TOKEN_STOP_TRAN) + self.write_token(_TOKEN_STOP_TRAN) return 0 -- cgit v1.2.3 From f98bb2ddcbfed7033ae93ae8fe98b0540a9fb42f Mon Sep 17 00:00:00 2001 From: Delio Brignoli Date: Sat, 20 Aug 2016 10:51:28 +0200 Subject: py/mpprint: Fail an assertion with unsupported format specifiers. Arguments of an unknown type cannot be skipped and continuing to parse a format string after encountering an unknown format specifier leads to undefined behaviour. This patch helps to find use of unsupported formats. --- py/mpprint.c | 4 +++- tests/unix/extra_coverage.py.exp | 1 - unix/coverage.c | 2 -- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/py/mpprint.c b/py/mpprint.c index cb49b1227..97ea33ad2 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -537,10 +537,12 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { chrs += mp_print_int(print, arg_value, *fmt == 'd', 10, 'a', flags, fill, width); break; } - // fall through to default case to print unknown format char + assert(!"unsupported fmt char"); } #endif default: + // if it's not %% then it's an unsupported format character + assert(*fmt == '%' || !"unsupported fmt char"); print->print_strn(print->data, fmt, 1); chrs += 1; break; diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index db282b152..44749c45e 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -7,7 +7,6 @@ ab abc false true (null) -t -2147483648 2147483648 80000000 diff --git a/unix/coverage.c b/unix/coverage.c index 9d5372554..ce1da50e2 100644 --- a/unix/coverage.c +++ b/unix/coverage.c @@ -12,7 +12,6 @@ STATIC mp_obj_t extra_coverage(void) { // mp_printf (used by ports that don't have a native printf) { mp_printf(&mp_plat_print, "# mp_printf\n"); - mp_printf(&mp_plat_print, "%"); // nothing after percent mp_printf(&mp_plat_print, "%d %+d % d\n", -123, 123, 123); // sign mp_printf(&mp_plat_print, "%05d\n", -123); // negative number with zero padding mp_printf(&mp_plat_print, "%ld\n", 123); // long @@ -21,7 +20,6 @@ STATIC mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%.*s\n", -1, "abc"); // negative string precision mp_printf(&mp_plat_print, "%b %b\n", 0, 1); // bools mp_printf(&mp_plat_print, "%s\n", NULL); // null string - mp_printf(&mp_plat_print, "%t\n"); // non-format char mp_printf(&mp_plat_print, "%d\n", 0x80000000); // should print signed mp_printf(&mp_plat_print, "%u\n", 0x80000000); // should print unsigned mp_printf(&mp_plat_print, "%x\n", 0x80000000); // should print unsigned -- cgit v1.2.3 From f127bef3e41f25eea6da73a52aab2fdc53be2464 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Sep 2016 14:32:47 +1000 Subject: py/makeqstrdata.py: Compute the qstr hash from bytes, not characters. --- py/makeqstrdata.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py index 8a3136b1f..7249769f4 100644 --- a/py/makeqstrdata.py +++ b/py/makeqstrdata.py @@ -14,11 +14,13 @@ import sys # - codepoint2name lives in a different module import platform if platform.python_version_tuple()[0] == '2': - ord_bytes = ord + bytes_cons = lambda val, enc=None: bytearray(val) from htmlentitydefs import codepoint2name elif platform.python_version_tuple()[0] == '3': - ord_bytes = lambda x:x + bytes_cons = bytes from html.entities import codepoint2name +# end compatibility code + codepoint2name[ord('-')] = 'hyphen'; # add some custom names to map characters that aren't in HTML @@ -52,8 +54,8 @@ codepoint2name[ord('~')] = 'tilde' # this must match the equivalent function in qstr.c def compute_hash(qstr, bytes_hash): hash = 5381 - for char in qstr: - hash = (hash * 33) ^ ord(char) + for b in qstr: + hash = (hash * 33) ^ b # Make sure that valid hash is never zero, zero means "hash not computed" return (hash & ((1 << (8 * bytes_hash)) - 1)) or 1 @@ -115,16 +117,15 @@ def parse_input_headers(infiles): return qcfgs, qstrs def make_bytes(cfg_bytes_len, cfg_bytes_hash, qstr): - qhash = compute_hash(qstr, cfg_bytes_hash) + qbytes = bytes_cons(qstr, 'utf8') + qlen = len(qbytes) + qhash = compute_hash(qbytes, cfg_bytes_hash) if all(32 <= ord(c) <= 126 and c != '\\' and c != '"' for c in qstr): # qstr is all printable ASCII so render it as-is (for easier debugging) - qlen = len(qstr) qdata = qstr else: # qstr contains non-printable codes so render entire thing as hex pairs - qbytes = qstr.encode('utf8') - qlen = len(qbytes) - qdata = ''.join(('\\x%02x' % ord_bytes(b)) for b in qbytes) + qdata = ''.join(('\\x%02x' % b) for b in qbytes) if qlen >= (1 << (8 * cfg_bytes_len)): print('qstr is too long:', qstr) assert False -- cgit v1.2.3 From 5f3bda422a18d49fb282a93968b658c568343b7d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Sep 2016 14:42:53 +1000 Subject: py: If str/bytes hash is 0 then explicitly compute it. --- py/objstr.c | 6 ++++++ py/objstr.h | 1 + py/runtime.c | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/py/objstr.c b/py/objstr.c index e83ff7c84..406ccf290 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -158,6 +158,9 @@ mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ if (MP_OBJ_IS_TYPE(args[0], &mp_type_bytes)) { GET_STR_DATA_LEN(args[0], str_data, str_len); GET_STR_HASH(args[0], str_hash); + if (str_hash == 0) { + str_hash = qstr_compute_hash(str_data, str_len); + } mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_of_type(type, NULL, str_len)); o->data = str_data; o->hash = str_hash; @@ -191,6 +194,9 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size } GET_STR_DATA_LEN(args[0], str_data, str_len); GET_STR_HASH(args[0], str_hash); + if (str_hash == 0) { + str_hash = qstr_compute_hash(str_data, str_len); + } mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_of_type(&mp_type_bytes, NULL, str_len)); o->data = str_data; o->hash = str_hash; diff --git a/py/objstr.h b/py/objstr.h index 07929156c..e14568dac 100644 --- a/py/objstr.h +++ b/py/objstr.h @@ -39,6 +39,7 @@ typedef struct _mp_obj_str_t { #define MP_DEFINE_STR_OBJ(obj_name, str) mp_obj_str_t obj_name = {{&mp_type_str}, 0, sizeof(str) - 1, (const byte*)str} // use this macro to extract the string hash +// warning: the hash can be 0, meaning invalid, and must then be explicitly computed from the data #define GET_STR_HASH(str_obj_in, str_hash) \ mp_uint_t str_hash; if (MP_OBJ_IS_QSTR(str_obj_in)) \ { str_hash = qstr_hash(MP_OBJ_QSTR_VALUE(str_obj_in)); } else { str_hash = ((mp_obj_str_t*)MP_OBJ_TO_PTR(str_obj_in))->hash; } diff --git a/py/runtime.c b/py/runtime.c index 04b3a34de..48e815f0f 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -217,6 +217,10 @@ mp_obj_t mp_unary_op(mp_uint_t op, mp_obj_t arg) { } else if (op == MP_UNARY_OP_HASH && MP_OBJ_IS_STR_OR_BYTES(arg)) { // fast path for hashing str/bytes GET_STR_HASH(arg, h); + if (h == 0) { + GET_STR_DATA_LEN(arg, data, len); + h = qstr_compute_hash(data, len); + } return MP_OBJ_NEW_SMALL_INT(h); } else { mp_obj_type_t *type = mp_obj_get_type(arg); -- cgit v1.2.3 From 58f386135813ab6e55641bec6eae32cc0fd35115 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Sep 2016 15:07:42 +1000 Subject: tests/unix/extra_coverage: Add test for str/bytes with invalid hash. --- tests/unix/extra_coverage.py | 9 ++++++++- tests/unix/extra_coverage.py.exp | 5 +++++ unix/coverage.c | 9 ++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/unix/extra_coverage.py b/tests/unix/extra_coverage.py index 8f330f1da..72bcc9994 100644 --- a/tests/unix/extra_coverage.py +++ b/tests/unix/extra_coverage.py @@ -5,4 +5,11 @@ except NameError: import sys sys.exit() -extra_coverage() +data = extra_coverage() + +# test hashing of str/bytes that have an invalid hash +print(data) +print(hash(data[0])) +print(hash(data[1])) +print(hash(bytes(data[0], 'utf8'))) +print(hash(str(data[1], 'utf8'))) diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index 44749c45e..ea73a54e4 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -35,3 +35,8 @@ ementation 12345678 0 0 +('0123456789', b'0123456789') +7300 +7300 +7300 +7300 diff --git a/unix/coverage.c b/unix/coverage.c index ce1da50e2..c84a653f7 100644 --- a/unix/coverage.c +++ b/unix/coverage.c @@ -1,12 +1,17 @@ #include #include "py/obj.h" +#include "py/objstr.h" #include "py/runtime.h" #include "py/repl.h" #include "py/mpz.h" #if defined(MICROPY_UNIX_COVERAGE) +// str/bytes objects without a valid hash +STATIC const mp_obj_str_t str_no_hash_obj = {{&mp_type_str}, 0, 10, (const byte*)"0123456789"}; +STATIC const mp_obj_str_t bytes_no_hash_obj = {{&mp_type_bytes}, 0, 10, (const byte*)"0123456789"}; + // function to run extra tests for things that can't be checked by scripts STATIC mp_obj_t extra_coverage(void) { // mp_printf (used by ports that don't have a native printf) @@ -109,7 +114,9 @@ STATIC mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%d\n", mpz_as_uint_checked(&mpz, &value)); } - return mp_const_none; + // return a tuple of data for testing on the Python side + mp_obj_t items[] = {(mp_obj_t)&str_no_hash_obj, (mp_obj_t)&bytes_no_hash_obj}; + return mp_obj_new_tuple(MP_ARRAY_SIZE(items), items); } MP_DEFINE_CONST_FUN_OBJ_0(extra_coverage_obj, extra_coverage); -- cgit v1.2.3 From b4790afdafaf243ca7a1ec250917f30ed00a2d2d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Sep 2016 15:09:21 +1000 Subject: tools/mpy-tool.py: Store qstr config values in global config object. Makes it easier to access them without passing around another dict of the config values. --- tools/mpy-tool.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index a7c3d8f91..c5bbf88e8 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -444,10 +444,7 @@ def dump_mpy(raw_codes): for rc in raw_codes: rc.dump() -def freeze_mpy(qcfgs, base_qstrs, raw_codes): - cfg_bytes_len = int(qcfgs['BYTES_IN_LEN']) - cfg_bytes_hash = int(qcfgs['BYTES_IN_HASH']) - +def freeze_mpy(base_qstrs, raw_codes): # add to qstrs new = {} for q in global_qstrs: @@ -505,7 +502,8 @@ def freeze_mpy(qcfgs, base_qstrs, raw_codes): print(' %u, // used entries' % len(new)) print(' {') for _, _, qstr in new: - print(' %s,' % qstrutil.make_bytes(cfg_bytes_len, cfg_bytes_hash, qstr)) + print(' %s,' + % qstrutil.make_bytes(config.MICROPY_QSTR_BYTES_IN_LEN, config.MICROPY_QSTR_BYTES_IN_HASH, qstr)) print(' },') print('};') @@ -549,10 +547,15 @@ def main(): }[args.mlongint_impl] config.MPZ_DIG_SIZE = args.mmpz_dig_size + # set config values for qstrs, and get the existing base set of qstrs if args.qstr_header: qcfgs, base_qstrs = qstrutil.parse_input_headers([args.qstr_header]) + config.MICROPY_QSTR_BYTES_IN_LEN = int(qcfgs['BYTES_IN_LEN']) + config.MICROPY_QSTR_BYTES_IN_HASH = int(qcfgs['BYTES_IN_HASH']) else: - qcfgs, base_qstrs = {'BYTES_IN_LEN':1, 'BYTES_IN_HASH':1}, {} + config.MICROPY_QSTR_BYTES_IN_LEN = 1 + config.MICROPY_QSTR_BYTES_IN_HASH = 1 + base_qstrs = {} raw_codes = [read_mpy(file) for file in args.files] @@ -560,7 +563,7 @@ def main(): dump_mpy(raw_codes) elif args.freeze: try: - freeze_mpy(qcfgs, base_qstrs, raw_codes) + freeze_mpy(base_qstrs, raw_codes) except FreezeError as er: print(er, file=sys.stderr) sys.exit(1) -- cgit v1.2.3 From b6bdf18debfd75e9d39c2e8452c7b4ade448bcc5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Sep 2016 15:10:45 +1000 Subject: tools/mpy-tool.py: Compute the hash value for str/bytes objects. This makes it more efficient at runtime to hash str/bytes objects. --- tools/mpy-tool.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index c5bbf88e8..bfb2a5da7 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -283,15 +283,15 @@ class RawCode: # generate constant objects for i, obj in enumerate(self.objs): obj_name = 'const_obj_%s_%u' % (self.escaped_name, i) - if is_str_type(obj): - obj = bytes_cons(obj, 'utf8') - print('STATIC const mp_obj_str_t %s = ' - '{{&mp_type_str}, 0, %u, (const byte*)"%s"};' - % (obj_name, len(obj), ''.join(('\\x%02x' % b) for b in obj))) - elif is_bytes_type(obj): - print('STATIC const mp_obj_str_t %s = ' - '{{&mp_type_bytes}, 0, %u, (const byte*)"%s"};' - % (obj_name, len(obj), ''.join(('\\x%02x' % b) for b in obj))) + if is_str_type(obj) or is_bytes_type(obj): + if is_str_type(obj): + obj = bytes_cons(obj, 'utf8') + obj_type = 'mp_type_str' + else: + obj_type = 'mp_type_bytes' + print('STATIC const mp_obj_str_t %s = {{&%s}, %u, %u, (const byte*)"%s"};' + % (obj_name, obj_type, qstrutil.compute_hash(obj, config.MICROPY_QSTR_BYTES_IN_HASH), + len(obj), ''.join(('\\x%02x' % b) for b in obj))) elif is_int_type(obj): if config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_NONE: # TODO check if we can actually fit this long-int into a small-int -- cgit v1.2.3 From 41ec22632da8f61ca5c841e13206258048d1be5b Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Sun, 28 Aug 2016 19:13:32 +0200 Subject: extmod/modframebuf: Fix fill and scroll when height not divisible by 8. There was a bug in `framebuf1_fill` function, that makes it leave a few lines unfilled at the bottom if the height is not divisible by 8. A similar bug is fixed in the scroll method. --- extmod/modframebuf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index a1e7c079b..84f1246fb 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -68,7 +68,8 @@ STATIC mp_obj_t framebuf1_fill(mp_obj_t self_in, mp_obj_t col_in) { if (col) { col = 0xff; } - for (int y = 0; y < self->height / 8; ++y) { + int end = (self->height + 7) >> 3; + for (int y = 0; y < end; ++y) { memset(self->buf + y * self->stride, col, self->width); } return mp_const_none; @@ -101,8 +102,9 @@ STATIC mp_obj_t framebuf1_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t y 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 (xstep == 0 && ystep > 0) { - for (int y = self->height / 8; y > 0;) { + for (int y = end; y > 0;) { --y; for (int x = 0; x < self->width; ++x) { int prev = 0; @@ -113,10 +115,10 @@ STATIC mp_obj_t framebuf1_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t y } } } else if (xstep == 0 && ystep < 0) { - for (int y = 0; y < self->height / 8; ++y) { + for (int y = 0; y < end; ++y) { for (int x = 0; x < self->width; ++x) { int prev = 0; - if (y + 1 < self->height / 8) { + 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; -- cgit v1.2.3 From c51c883cc84bb680dcc810a81e81dd1df879a07c Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 3 Sep 2016 00:19:02 +1000 Subject: tools/mpy-tool.py: Support freezing of complex numbers. --- tools/mpy-tool.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index bfb2a5da7..bc8ac4fbd 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -320,6 +320,9 @@ class RawCode: print('STATIC const mp_obj_float_t %s = {{&mp_type_float}, %.16g};' % (obj_name, obj)) print('#endif') + elif type(obj) is complex: + print('STATIC const mp_obj_complex_t %s = {{&mp_type_complex}, %.16g, %.16g};' + % (obj_name, obj.real, obj.imag)) else: # TODO raise FreezeError(self, 'freezing of object %r is not implemented' % (obj,)) @@ -485,6 +488,15 @@ def freeze_mpy(base_qstrs, raw_codes): print('#endif') print() + print('#if MICROPY_PY_BUILTINS_COMPLEX') + print('typedef struct _mp_obj_complex_t {') + print(' mp_obj_base_t base;') + print(' mp_float_t real;') + print(' mp_float_t imag;') + print('} mp_obj_complex_t;') + print('#endif') + print() + print('enum {') for i in range(len(new)): if i == 0: -- cgit v1.2.3 From a6864a13c7e82e182e36dab8fb350fb2f7ece766 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 3 Sep 2016 00:07:08 +0300 Subject: extmod/moduzlib: Implement zlib stream decompressor class, DecompIO. --- extmod/moduzlib.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/extmod/moduzlib.c b/extmod/moduzlib.c index b733ae2e0..7fc655fda 100644 --- a/extmod/moduzlib.c +++ b/extmod/moduzlib.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2014 Paul Sokolovsky + * Copyright (c) 2014-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 @@ -29,6 +29,7 @@ #include "py/nlr.h" #include "py/runtime.h" +#include "py/stream.h" #if MICROPY_PY_UZLIB @@ -40,6 +41,85 @@ #define DEBUG_printf(...) (void)0 #endif +typedef struct _mp_obj_decompio_t { + mp_obj_base_t base; + mp_obj_t src_stream; + TINF_DATA decomp; + bool eof; +} mp_obj_decompio_t; + +STATIC unsigned char read_src_stream(TINF_DATA *data) { + byte *p = (void*)data; + p -= offsetof(mp_obj_decompio_t, decomp); + mp_obj_decompio_t *self = (mp_obj_decompio_t*)p; + + const mp_stream_p_t *stream = mp_get_stream_raise(self->src_stream, MP_STREAM_OP_READ); + int err; + byte c; + mp_uint_t out_sz = stream->read(self->src_stream, &c, 1, &err); + if (out_sz == MP_STREAM_ERROR) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(err))); + } + if (out_sz == 0) { + nlr_raise(mp_obj_new_exception(&mp_type_EOFError)); + } + return c; +} + +#define DICT_SIZE 32768 + +STATIC mp_obj_t decompio_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_obj_decompio_t *o = m_new_obj(mp_obj_decompio_t); + o->base.type = type; + memset(&o->decomp, 0, sizeof(o->decomp)); + uzlib_uncompress_init(&o->decomp, m_new(byte, DICT_SIZE), DICT_SIZE); + o->decomp.readSource = read_src_stream; + o->src_stream = args[0]; + o->eof = false; + return MP_OBJ_FROM_PTR(o); +} + +STATIC mp_uint_t decompio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { + mp_obj_decompio_t *o = MP_OBJ_TO_PTR(o_in); + if (o->eof) { + return 0; + } + + o->decomp.dest = buf; + o->decomp.destSize = size; + int st = uzlib_uncompress_chksum(&o->decomp); + if (st == TINF_DONE) { + o->eof = true; + } + if (st < 0) { + *errcode = EINVAL; + return MP_STREAM_ERROR; + } + return o->decomp.dest - (byte*)buf; +} + +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) }, +}; + +STATIC MP_DEFINE_CONST_DICT(decompio_locals_dict, decompio_locals_dict_table); + +STATIC const mp_stream_p_t decompio_stream_p = { + .read = decompio_read, +}; + +STATIC const mp_obj_type_t decompio_type = { + { &mp_type_type }, + .name = MP_QSTR_DecompIO, + .make_new = decompio_make_new, + .protocol = &decompio_stream_p, + .locals_dict = (void*)&decompio_locals_dict, +}; + STATIC mp_obj_t mod_uzlib_decompress(size_t n_args, const mp_obj_t *args) { (void)n_args; mp_obj_t data = args[0]; @@ -102,6 +182,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_uzlib_decompress_obj, 1, 3, mod_u STATIC const mp_rom_map_elem_t mp_module_uzlib_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uzlib) }, { MP_ROM_QSTR(MP_QSTR_decompress), MP_ROM_PTR(&mod_uzlib_decompress_obj) }, + { MP_ROM_QSTR(MP_QSTR_DecompIO), MP_ROM_PTR(&decompio_type) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_uzlib_globals, mp_module_uzlib_globals_table); -- cgit v1.2.3 From fafd587514ece8ee394b9587c1494e35ec2634fb Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 3 Sep 2016 00:20:30 +0300 Subject: tests/extmod: Add test for uzlib.DecompIO. --- tests/extmod/uzlib_decompio.py | 19 +++++++++++++++++++ tests/extmod/uzlib_decompio.py.exp | 9 +++++++++ 2 files changed, 28 insertions(+) create mode 100644 tests/extmod/uzlib_decompio.py create mode 100644 tests/extmod/uzlib_decompio.py.exp diff --git a/tests/extmod/uzlib_decompio.py b/tests/extmod/uzlib_decompio.py new file mode 100644 index 000000000..ee3204d07 --- /dev/null +++ b/tests/extmod/uzlib_decompio.py @@ -0,0 +1,19 @@ +try: + import zlib +except ImportError: + import uzlib as zlib +import uio as io + + +# Raw DEFLATE bitstream +buf = io.BytesIO(b'\xcbH\xcd\xc9\xc9\x07\x00') +inp = zlib.DecompIO(buf) +print(buf.seek(0, 1)) +print(inp.read(1)) +print(buf.seek(0, 1)) +print(inp.read(2)) +print(inp.read()) +print(buf.seek(0, 1)) +print(inp.read(1)) +print(inp.read()) +print(buf.seek(0, 1)) diff --git a/tests/extmod/uzlib_decompio.py.exp b/tests/extmod/uzlib_decompio.py.exp new file mode 100644 index 000000000..6ef811d7d --- /dev/null +++ b/tests/extmod/uzlib_decompio.py.exp @@ -0,0 +1,9 @@ +0 +b'h' +2 +b'el' +b'lo' +7 +b'' +b'' +7 -- cgit v1.2.3 From f7c461152364650b71231387c3f80b37277c9161 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 3 Sep 2016 00:34:57 +0300 Subject: extmod/moduzlib: Use mperrno.h for error constants. --- extmod/moduzlib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extmod/moduzlib.c b/extmod/moduzlib.c index 7fc655fda..dbf513527 100644 --- a/extmod/moduzlib.c +++ b/extmod/moduzlib.c @@ -30,6 +30,7 @@ #include "py/nlr.h" #include "py/runtime.h" #include "py/stream.h" +#include "py/mperrno.h" #if MICROPY_PY_UZLIB @@ -93,7 +94,7 @@ STATIC mp_uint_t decompio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *er o->eof = true; } if (st < 0) { - *errcode = EINVAL; + *errcode = MP_EINVAL; return MP_STREAM_ERROR; } return o->decomp.dest - (byte*)buf; -- cgit v1.2.3