Skip to content

Commit 1eb3c66

Browse files
committed
extmod/machine_spi: Provide reusable software SPI class.
So long as a port defines relevant mp_hal_pin_xxx functions (and delay) it can make use of this software SPI class without the need for additional code.
1 parent d9c8397 commit 1eb3c66

5 files changed

Lines changed: 216 additions & 0 deletions

File tree

esp8266/esp_mphal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,10 @@ void ets_event_poll(void);
7676
#include "etshal.h"
7777
#include "gpio.h"
7878
#include "esp8266/modmachine.h"
79+
#define MP_HAL_PIN_FMT "%u"
7980
#define mp_hal_pin_obj_t uint32_t
8081
#define mp_hal_get_pin_obj(o) mp_obj_get_pin(o)
82+
#define mp_hal_pin_name(p) (p)
8183
void mp_hal_pin_input(mp_hal_pin_obj_t pin);
8284
void mp_hal_pin_output(mp_hal_pin_obj_t pin);
8385
void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin);

extmod/machine_spi.c

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@
3232

3333
#if MICROPY_PY_MACHINE_SPI
3434

35+
// if a port didn't define MSB/LSB constants then provide them
36+
#ifndef MICROPY_PY_MACHINE_SPI_MSB
37+
#define MICROPY_PY_MACHINE_SPI_MSB (0)
38+
#define MICROPY_PY_MACHINE_SPI_LSB (1)
39+
#endif
40+
3541
void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
3642
mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in;
3743
uint32_t delay_half = self->delay_half;
@@ -93,6 +99,49 @@ void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint
9399
}
94100
}
95101

102+
/******************************************************************************/
103+
// MicroPython bindings for generic machine.SPI
104+
105+
STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
106+
107+
mp_obj_t mp_machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
108+
// check the id argument, if given
109+
if (n_args > 0) {
110+
if (args[0] != MP_OBJ_NEW_SMALL_INT(-1)) {
111+
#if defined(MICROPY_PY_MACHINE_SPI_MAKE_NEW)
112+
// dispatch to port-specific constructor
113+
extern mp_obj_t MICROPY_PY_MACHINE_SPI_MAKE_NEW(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
114+
return MICROPY_PY_MACHINE_SPI_MAKE_NEW(type, n_args, n_kw, args);
115+
#else
116+
mp_raise_ValueError("invalid SPI peripheral");
117+
#endif
118+
}
119+
--n_args;
120+
++args;
121+
}
122+
123+
// software SPI
124+
return mp_machine_soft_spi_make_new(type, n_args, n_kw, args);
125+
}
126+
127+
STATIC mp_obj_t machine_spi_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
128+
mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
129+
mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol;
130+
spi_p->init(s, n_args - 1, args + 1, kw_args);
131+
return mp_const_none;
132+
}
133+
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_init_obj, 1, machine_spi_init);
134+
135+
STATIC mp_obj_t machine_spi_deinit(mp_obj_t self) {
136+
mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self);
137+
mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol;
138+
if (spi_p->deinit != NULL) {
139+
spi_p->deinit(s);
140+
}
141+
return mp_const_none;
142+
}
143+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_spi_deinit_obj, machine_spi_deinit);
144+
96145
STATIC void mp_machine_spi_transfer(mp_obj_t self, size_t len, const void *src, void *dest) {
97146
mp_obj_base_t *s = (mp_obj_base_t*)MP_OBJ_TO_PTR(self);
98147
mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t*)s->type->protocol;
@@ -138,4 +187,159 @@ STATIC mp_obj_t mp_machine_spi_write_readinto(mp_obj_t self, mp_obj_t wr_buf, mp
138187
}
139188
MP_DEFINE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj, mp_machine_spi_write_readinto);
140189

190+
STATIC const mp_rom_map_elem_t machine_spi_locals_dict_table[] = {
191+
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_spi_init_obj) },
192+
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_spi_deinit_obj) },
193+
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_machine_spi_read_obj) },
194+
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_machine_spi_readinto_obj) },
195+
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_machine_spi_write_obj) },
196+
{ MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&mp_machine_spi_write_readinto_obj) },
197+
198+
{ MP_ROM_QSTR(MP_QSTR_MSB), MP_ROM_INT(MICROPY_PY_MACHINE_SPI_MSB) },
199+
{ MP_ROM_QSTR(MP_QSTR_LSB), MP_ROM_INT(MICROPY_PY_MACHINE_SPI_LSB) },
200+
};
201+
202+
MP_DEFINE_CONST_DICT(mp_machine_spi_locals_dict, machine_spi_locals_dict_table);
203+
204+
/******************************************************************************/
205+
// Implementation of soft SPI
206+
207+
STATIC uint32_t baudrate_from_delay_half(uint32_t delay_half) {
208+
#ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
209+
if (delay_half == MICROPY_PY_MACHINE_SPI_MIN_DELAY) {
210+
return MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE;
211+
} else
212+
#endif
213+
{
214+
return 500000 / delay_half;
215+
}
216+
}
217+
218+
STATIC uint32_t baudrate_to_delay_half(uint32_t baudrate) {
219+
#ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
220+
if (baudrate >= MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE) {
221+
return MICROPY_PY_MACHINE_SPI_MIN_DELAY;
222+
} else
223+
#endif
224+
{
225+
uint32_t delay_half = 500000 / baudrate;
226+
// round delay_half up so that: actual_baudrate <= requested_baudrate
227+
if (500000 % baudrate != 0) {
228+
delay_half += 1;
229+
}
230+
return delay_half;
231+
}
232+
}
233+
234+
STATIC void mp_machine_soft_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
235+
mp_machine_soft_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
236+
mp_printf(print, "SoftSPI(baudrate=%u, polarity=%u, phase=%u,"
237+
" sck=" MP_HAL_PIN_FMT ", mosi=" MP_HAL_PIN_FMT ", miso=" MP_HAL_PIN_FMT ")",
238+
baudrate_from_delay_half(self->delay_half), self->polarity, self->phase,
239+
mp_hal_pin_name(self->sck), mp_hal_pin_name(self->mosi), mp_hal_pin_name(self->miso));
240+
}
241+
242+
STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
243+
enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso };
244+
static const mp_arg_t allowed_args[] = {
245+
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} },
246+
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
247+
{ MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
248+
{ MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
249+
{ MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MICROPY_PY_MACHINE_SPI_MSB} },
250+
{ MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
251+
{ MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
252+
{ MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
253+
};
254+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
255+
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
256+
257+
// create new object
258+
mp_machine_soft_spi_obj_t *self = m_new_obj(mp_machine_soft_spi_obj_t);
259+
self->base.type = &mp_machine_soft_spi_type;
260+
261+
// set parameters
262+
self->delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
263+
self->polarity = args[ARG_polarity].u_int;
264+
self->phase = args[ARG_phase].u_int;
265+
if (args[ARG_bits].u_int != 8) {
266+
mp_raise_ValueError("bits must be 8");
267+
}
268+
if (args[ARG_firstbit].u_int != MICROPY_PY_MACHINE_SPI_MSB) {
269+
mp_raise_ValueError("firstbit must be MSB");
270+
}
271+
if (args[ARG_sck].u_obj == MP_OBJ_NULL
272+
|| args[ARG_mosi].u_obj == MP_OBJ_NULL
273+
|| args[ARG_miso].u_obj == MP_OBJ_NULL) {
274+
mp_raise_ValueError("must specify all of sck/mosi/miso");
275+
}
276+
self->sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj);
277+
self->mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj);
278+
self->miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj);
279+
280+
// configure pins
281+
mp_hal_pin_write(self->sck, self->polarity);
282+
mp_hal_pin_output(self->sck);
283+
mp_hal_pin_output(self->mosi);
284+
mp_hal_pin_input(self->miso);
285+
286+
return MP_OBJ_FROM_PTR(self);
287+
}
288+
289+
STATIC void mp_machine_soft_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
290+
mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in;
291+
292+
enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_sck, ARG_mosi, ARG_miso };
293+
static const mp_arg_t allowed_args[] = {
294+
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} },
295+
{ MP_QSTR_polarity, MP_ARG_INT, {.u_int = -1} },
296+
{ MP_QSTR_phase, MP_ARG_INT, {.u_int = -1} },
297+
{ MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
298+
{ MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
299+
{ MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
300+
};
301+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
302+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
303+
304+
if (args[ARG_baudrate].u_int != -1) {
305+
self->delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
306+
}
307+
if (args[ARG_polarity].u_int != -1) {
308+
self->polarity = args[ARG_polarity].u_int;
309+
}
310+
if (args[ARG_phase].u_int != -1) {
311+
self->phase = args[ARG_phase].u_int;
312+
}
313+
if (args[ARG_sck].u_obj != MP_OBJ_NULL) {
314+
self->sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj);
315+
}
316+
if (args[ARG_mosi].u_obj != MP_OBJ_NULL) {
317+
self->mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj);
318+
}
319+
if (args[ARG_miso].u_obj != MP_OBJ_NULL) {
320+
self->miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj);
321+
}
322+
323+
// configure pins
324+
mp_hal_pin_write(self->sck, self->polarity);
325+
mp_hal_pin_output(self->sck);
326+
mp_hal_pin_output(self->mosi);
327+
mp_hal_pin_input(self->miso);
328+
}
329+
330+
STATIC const mp_machine_spi_p_t mp_machine_soft_spi_p = {
331+
.init = mp_machine_soft_spi_init,
332+
.deinit = NULL,
333+
.transfer = mp_machine_soft_spi_transfer,
334+
};
335+
336+
const mp_obj_type_t mp_machine_soft_spi_type = {
337+
{ &mp_type_type },
338+
.name = MP_QSTR_SoftSPI,
339+
.print = mp_machine_soft_spi_print,
340+
.make_new = mp_machine_spi_make_new, // delegate to master constructor
341+
.protocol = &mp_machine_soft_spi_p,
342+
.locals_dict = (mp_obj_dict_t*)&mp_machine_spi_locals_dict,
343+
};
344+
141345
#endif // MICROPY_PY_MACHINE_SPI

extmod/machine_spi.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
// SPI protocol
3434
typedef struct _mp_machine_spi_p_t {
35+
void (*init)(mp_obj_base_t *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
36+
void (*deinit)(mp_obj_base_t *obj); // can be NULL
3537
void (*transfer)(mp_obj_base_t *obj, size_t len, const uint8_t *src, uint8_t *dest);
3638
} mp_machine_spi_p_t;
3739

@@ -45,8 +47,13 @@ typedef struct _mp_machine_soft_spi_obj_t {
4547
mp_hal_pin_obj_t miso;
4648
} mp_machine_soft_spi_obj_t;
4749

50+
extern const mp_obj_type_t mp_machine_soft_spi_type;
51+
extern const mp_obj_dict_t mp_machine_spi_locals_dict;
52+
4853
void mp_machine_soft_spi_transfer(mp_obj_base_t *self, size_t len, const uint8_t *src, uint8_t *dest);
4954

55+
mp_obj_t mp_machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);
56+
5057
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj);
5158
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj);
5259
MP_DECLARE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj);

stmhal/mpconfigport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
#define MICROPY_PY_MACHINE_I2C_MAKE_NEW machine_hard_i2c_make_new
107107
#define MICROPY_PY_MACHINE_SPI (1)
108108
#define MICROPY_PY_MACHINE_SPI_MIN_DELAY (0)
109+
#define MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE (HAL_RCC_GetSysClockFreq() / 48)
109110
#define MICROPY_PY_FRAMEBUF (1)
110111

111112
#ifndef MICROPY_PY_USOCKET

stmhal/mphalport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ static inline mp_uint_t mp_hal_ticks_cpu(void) {
4848

4949
#include "stmhal/pin.h"
5050

51+
#define MP_HAL_PIN_FMT "%q"
5152
#define MP_HAL_PIN_MODE_INPUT (0)
5253
#define MP_HAL_PIN_MODE_OUTPUT (1)
5354
#define MP_HAL_PIN_MODE_ALT (2)
@@ -60,6 +61,7 @@ static inline mp_uint_t mp_hal_ticks_cpu(void) {
6061

6162
#define mp_hal_pin_obj_t const pin_obj_t*
6263
#define mp_hal_get_pin_obj(o) pin_find(o)
64+
#define mp_hal_pin_name(p) ((p)->name)
6365
#define mp_hal_pin_input(p) mp_hal_pin_config((p), MP_HAL_PIN_MODE_INPUT, MP_HAL_PIN_PULL_NONE, 0)
6466
#define mp_hal_pin_output(p) mp_hal_pin_config((p), MP_HAL_PIN_MODE_OUTPUT, MP_HAL_PIN_PULL_NONE, 0)
6567
#define mp_hal_pin_open_drain(p) mp_hal_pin_config((p), MP_HAL_PIN_MODE_OPEN_DRAIN, MP_HAL_PIN_PULL_NONE, 0)

0 commit comments

Comments
 (0)