From 5bb28c7f10ebd1036302cf7ac0b24a7a233de2aa Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 3 Oct 2016 12:39:31 +1100 Subject: extmod/machine_spi: Simplify SPI xfer function to only take one buf len. There is no need to take src_len and dest_len arguments. The case of reading-only with a single output byte (originally src_len=1, dest_len>1) is now handled by using the output buffer as the input buffer, and using memset to fill the output byte into this buffer. This simplifies the implementations of the spi_transfer protocol function. --- extmod/machine_spi.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) (limited to 'extmod/machine_spi.c') diff --git a/extmod/machine_spi.c b/extmod/machine_spi.c index 6b6202a221..1c64cf511b 100644 --- a/extmod/machine_spi.c +++ b/extmod/machine_spi.c @@ -25,26 +25,24 @@ */ #include +#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) { +STATIC void mp_machine_spi_transfer(mp_obj_t self, size_t len, const void *src, void *dest) { mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self); mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol; - spi_p->transfer(s, slen, src, dlen, dest); + spi_p->transfer(s, len, src, 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); + memset(vstr.buf, n_args == 3 ? mp_obj_get_int(args[2]) : 0, vstr.len); + mp_machine_spi_transfer(args[0], vstr.len, vstr.buf, 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); @@ -52,11 +50,8 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj, 2, 3, mp_machine_sp 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); + memset(bufinfo.buf, n_args == 3 ? mp_obj_get_int(args[2]) : 0, bufinfo.len); + mp_machine_spi_transfer(args[0], bufinfo.len, bufinfo.buf, bufinfo.buf); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj, 2, 3, mp_machine_spi_readinto); @@ -64,7 +59,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj, 2, 3, mp_machin 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); + mp_machine_spi_transfer(self, src.len, (const uint8_t*)src.buf, NULL); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj, mp_machine_spi_write); @@ -75,9 +70,9 @@ STATIC mp_obj_t mp_machine_spi_write_readinto(mp_obj_t self, mp_obj_t wr_buf, mp 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_raise_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); + mp_machine_spi_transfer(self, src.len, src.buf, dest.buf); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj, mp_machine_spi_write_readinto); -- cgit v1.2.3 From d434ce3fca088959c4a4bf525b9d730ec9bdb7b4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 3 Oct 2016 16:43:44 +1100 Subject: extmod/machine_spi: Factor out software SPI code from esp8266 to extmod. --- esp8266/modpybspi.c | 55 ++++------------------------------------------------ extmod/machine_spi.c | 37 +++++++++++++++++++++++++++++++++++ extmod/machine_spi.h | 13 +++++++++++++ 3 files changed, 54 insertions(+), 51 deletions(-) (limited to 'extmod/machine_spi.c') diff --git a/esp8266/modpybspi.c b/esp8266/modpybspi.c index b9650954f3..acc3c25125 100644 --- a/esp8266/modpybspi.c +++ b/esp8266/modpybspi.c @@ -28,68 +28,21 @@ #include #include -#include "ets_sys.h" -#include "etshal.h" -#include "ets_alt_task.h" - #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; - uint32_t baudrate; - uint8_t polarity; - uint8_t phase; - mp_hal_pin_obj_t sck; - mp_hal_pin_obj_t mosi; - mp_hal_pin_obj_t miso; -} pyb_spi_obj_t; - -STATIC void mp_hal_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { - 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 < len; ++i) { - uint8_t data_out = src[i]; - uint8_t data_in = 0; - for (int j = 0; j < 8; ++j, data_out <<= 1) { - mp_hal_pin_write(self->mosi, (data_out >> 7) & 1); - if (self->phase == 0) { - ets_delay_us(delay_half); - mp_hal_pin_write(self->sck, 1 - self->polarity); - } else { - mp_hal_pin_write(self->sck, 1 - self->polarity); - ets_delay_us(delay_half); - } - data_in = (data_in << 1) | mp_hal_pin_read(self->miso); - if (self->phase == 0) { - ets_delay_us(delay_half); - mp_hal_pin_write(self->sck, self->polarity); - } else { - mp_hal_pin_write(self->sck, self->polarity); - ets_delay_us(delay_half); - } - } - if (dest != NULL) { - dest[i] = data_in; - } - // make sure pending tasks have a chance to run - ets_loop_iter(); - } -} - /******************************************************************************/ // MicroPython bindings for SPI STATIC void pyb_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - pyb_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_machine_soft_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "SPI(baudrate=%u, polarity=%u, phase=%u, sck=%u, mosi=%u, miso=%u)", self->baudrate, self->polarity, self->phase, self->sck, self->mosi, self->miso); } -STATIC void pyb_spi_init_helper(pyb_spi_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +STATIC void pyb_spi_init_helper(mp_machine_soft_spi_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_sck, ARG_mosi, ARG_miso }; static const mp_arg_t allowed_args[] = { { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, @@ -130,7 +83,7 @@ STATIC void pyb_spi_init_helper(pyb_spi_obj_t *self, size_t n_args, const mp_obj mp_obj_t pyb_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, true); - pyb_spi_obj_t *self = m_new_obj(pyb_spi_obj_t); + mp_machine_soft_spi_obj_t *self = m_new_obj(mp_machine_soft_spi_obj_t); self->base.type = &pyb_spi_type; // set defaults self->baudrate = 500000; @@ -162,7 +115,7 @@ STATIC const mp_rom_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 = mp_hal_spi_transfer, + .transfer = mp_machine_soft_spi_transfer, }; const mp_obj_type_t pyb_spi_type = { diff --git a/extmod/machine_spi.c b/extmod/machine_spi.c index 1c64cf511b..3a34b7fb08 100644 --- a/extmod/machine_spi.c +++ b/extmod/machine_spi.c @@ -32,6 +32,43 @@ #if MICROPY_PY_MACHINE_SPI +void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { + mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in; + // only MSB transfer is implemented + uint32_t delay_half = 500000 / self->baudrate + 1; + for (size_t i = 0; i < len; ++i) { + uint8_t data_out = src[i]; + uint8_t data_in = 0; + for (int j = 0; j < 8; ++j, data_out <<= 1) { + mp_hal_pin_write(self->mosi, (data_out >> 7) & 1); + if (self->phase == 0) { + mp_hal_delay_us_fast(delay_half); + mp_hal_pin_write(self->sck, 1 - self->polarity); + } else { + mp_hal_pin_write(self->sck, 1 - self->polarity); + mp_hal_delay_us_fast(delay_half); + } + data_in = (data_in << 1) | mp_hal_pin_read(self->miso); + if (self->phase == 0) { + mp_hal_delay_us_fast(delay_half); + mp_hal_pin_write(self->sck, self->polarity); + } else { + mp_hal_pin_write(self->sck, self->polarity); + mp_hal_delay_us_fast(delay_half); + } + } + if (dest != NULL) { + dest[i] = data_in; + } + + // Some ports need a regular callback, but probably we don't need + // to do this every byte, or even at all. + #ifdef MICROPY_EVENT_POLL_HOOK + MICROPY_EVENT_POLL_HOOK; + #endif + } +} + STATIC void mp_machine_spi_transfer(mp_obj_t self, size_t len, const void *src, void *dest) { mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self); mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol; diff --git a/extmod/machine_spi.h b/extmod/machine_spi.h index 35a911d919..e1922c6e8b 100644 --- a/extmod/machine_spi.h +++ b/extmod/machine_spi.h @@ -28,12 +28,25 @@ #define MICROPY_INCLUDED_EXTMOD_MACHINE_SPI_H #include "py/obj.h" +#include "py/mphal.h" // SPI protocol typedef struct _mp_machine_spi_p_t { void (*transfer)(mp_obj_base_t *obj, size_t len, const uint8_t *src, uint8_t *dest); } mp_machine_spi_p_t; +typedef struct _mp_machine_soft_spi_obj_t { + mp_obj_base_t base; + uint32_t baudrate; + uint8_t polarity; + uint8_t phase; + mp_hal_pin_obj_t sck; + mp_hal_pin_obj_t mosi; + mp_hal_pin_obj_t miso; +} mp_machine_soft_spi_obj_t; + +void mp_machine_soft_spi_transfer(mp_obj_base_t *self, size_t len, const uint8_t *src, uint8_t *dest); + 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); -- cgit v1.2.3 From b932b2dd1faaf3091b13af7d213041a737e40fac Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 4 Oct 2016 13:43:02 +1100 Subject: extmod/machine_spi: Use delay_half, not baudrate, for internal timing. The delay_half parameter must be specified by the port to set up the timing of the software SPI. This allows the port to adjust the timing value to better suit its timing characteristics, as well as provide a more accurate printing of the baudrate. --- esp8266/modpybspi.c | 20 +++++++++++++++++--- extmod/machine_spi.c | 4 +++- extmod/machine_spi.h | 2 +- stmhal/spi.c | 19 ++++++++++++++++--- 4 files changed, 37 insertions(+), 8 deletions(-) (limited to 'extmod/machine_spi.c') diff --git a/esp8266/modpybspi.c b/esp8266/modpybspi.c index acc3c25125..e974547111 100644 --- a/esp8266/modpybspi.c +++ b/esp8266/modpybspi.c @@ -36,10 +36,24 @@ /******************************************************************************/ // MicroPython bindings for SPI +STATIC uint32_t baudrate_from_delay_half(uint32_t delay_half) { + return 500000 / delay_half; +} + +STATIC uint32_t baudrate_to_delay_half(uint32_t baudrate) { + uint32_t delay_half = 500000 / baudrate; + // round delay_half up so that: actual_baudrate <= requested_baudrate + if (500000 % baudrate != 0) { + delay_half += 1; + } + return delay_half; +} + STATIC void pyb_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { mp_machine_soft_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "SPI(baudrate=%u, polarity=%u, phase=%u, sck=%u, mosi=%u, miso=%u)", - self->baudrate, self->polarity, self->phase, self->sck, self->mosi, self->miso); + baudrate_from_delay_half(self->delay_half), + self->polarity, self->phase, self->sck, self->mosi, self->miso); } STATIC void pyb_spi_init_helper(mp_machine_soft_spi_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -56,7 +70,7 @@ STATIC void pyb_spi_init_helper(mp_machine_soft_spi_obj_t *self, size_t n_args, mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); if (args[ARG_baudrate].u_int != -1) { - self->baudrate = args[ARG_baudrate].u_int; + self->delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int); } if (args[ARG_polarity].u_int != -1) { self->polarity = args[ARG_polarity].u_int; @@ -86,7 +100,7 @@ mp_obj_t pyb_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, mp_machine_soft_spi_obj_t *self = m_new_obj(mp_machine_soft_spi_obj_t); self->base.type = &pyb_spi_type; // set defaults - self->baudrate = 500000; + self->delay_half = baudrate_to_delay_half(500000); self->polarity = 0; self->phase = 0; self->sck = 14; diff --git a/extmod/machine_spi.c b/extmod/machine_spi.c index 3a34b7fb08..b0bd76faf9 100644 --- a/extmod/machine_spi.c +++ b/extmod/machine_spi.c @@ -34,8 +34,10 @@ void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in; + uint32_t delay_half = self->delay_half; + // only MSB transfer is implemented - uint32_t delay_half = 500000 / self->baudrate + 1; + for (size_t i = 0; i < len; ++i) { uint8_t data_out = src[i]; uint8_t data_in = 0; diff --git a/extmod/machine_spi.h b/extmod/machine_spi.h index e1922c6e8b..316d06646e 100644 --- a/extmod/machine_spi.h +++ b/extmod/machine_spi.h @@ -37,7 +37,7 @@ typedef struct _mp_machine_spi_p_t { typedef struct _mp_machine_soft_spi_obj_t { mp_obj_base_t base; - uint32_t baudrate; + uint32_t delay_half; // microsecond delay for half SCK period uint8_t polarity; uint8_t phase; mp_hal_pin_obj_t sck; diff --git a/stmhal/spi.c b/stmhal/spi.c index 46da05facf..8ec6f86275 100644 --- a/stmhal/spi.c +++ b/stmhal/spi.c @@ -944,10 +944,23 @@ STATIC MP_DEFINE_CONST_DICT(machine_spi_locals_dict, machine_spi_locals_dict_tab /* code for soft implementation ***********************************************/ +STATIC uint32_t baudrate_from_delay_half(uint32_t delay_half) { + return 500000 / delay_half; +} + +STATIC uint32_t baudrate_to_delay_half(uint32_t baudrate) { + uint32_t delay_half = 500000 / baudrate; + // round delay_half up so that: actual_baudrate <= requested_baudrate + if (500000 % baudrate != 0) { + delay_half += 1; + } + return delay_half; +} + STATIC void machine_soft_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { mp_machine_soft_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "SPI(-1, baudrate=%u, polarity=%u, phase=%u, sck=%q, mosi=%q, miso=%q)", - self->baudrate, self->polarity, self->phase, + baudrate_from_delay_half(self->delay_half), self->polarity, self->phase, self->sck->name, self->mosi->name, self->miso->name); } @@ -957,7 +970,7 @@ STATIC mp_obj_t machine_soft_spi_make_new(mp_arg_val_t *args) { self->base.type = &machine_soft_spi_type; // set parameters - self->baudrate = args[ARG_NEW_baudrate].u_int; + self->delay_half = baudrate_to_delay_half(args[ARG_NEW_baudrate].u_int); self->polarity = args[ARG_NEW_polarity].u_int; self->phase = args[ARG_NEW_phase].u_int; if (args[ARG_NEW_bits].u_int != 8) { @@ -989,7 +1002,7 @@ STATIC void machine_soft_spi_init(mp_obj_t self_in, mp_arg_val_t *args) { // update parameters if (args[ARG_INIT_baudrate].u_int != -1) { - self->baudrate = args[ARG_INIT_baudrate].u_int; + self->delay_half = baudrate_to_delay_half(args[ARG_INIT_baudrate].u_int); } if (args[ARG_INIT_polarity].u_int != -1) { self->polarity = args[ARG_INIT_polarity].u_int; -- cgit v1.2.3 From b0eb0d6153d63cf823065792db35d2858fbb9406 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 4 Oct 2016 13:46:40 +1100 Subject: extmod/machine_spi: Add optional support for fast software SPI. If a port defines MICROPY_PY_MACHINE_SPI_MIN_DELAY then it can use a faster software SPI loop that does not make calls to the delay_us function. --- extmod/machine_spi.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'extmod/machine_spi.c') diff --git a/extmod/machine_spi.c b/extmod/machine_spi.c index b0bd76faf9..e3d72ab588 100644 --- a/extmod/machine_spi.c +++ b/extmod/machine_spi.c @@ -38,6 +38,28 @@ void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint // only MSB transfer is implemented + // If a port defines MICROPY_PY_MACHINE_SPI_MIN_DELAY, and the configured + // delay_half is equal to this value, then the software SPI implementation + // will run as fast as possible, limited only by CPU speed and GPIO time. + #ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY + if (delay_half == MICROPY_PY_MACHINE_SPI_MIN_DELAY) { + for (size_t i = 0; i < len; ++i) { + uint8_t data_out = src[i]; + uint8_t data_in = 0; + for (int j = 0; j < 8; ++j, data_out <<= 1) { + mp_hal_pin_write(self->mosi, (data_out >> 7) & 1); + mp_hal_pin_write(self->sck, 1 - self->polarity); + data_in = (data_in << 1) | mp_hal_pin_read(self->miso); + mp_hal_pin_write(self->sck, self->polarity); + } + if (dest != NULL) { + dest[i] = data_in; + } + } + return; + } + #endif + for (size_t i = 0; i < len; ++i) { uint8_t data_out = src[i]; uint8_t data_in = 0; -- cgit v1.2.3