Skip to content

Commit 38c6e55

Browse files
MrSurlydpgeorge
authored andcommitted
esp32: Add support for hardware SPI peripheral (block 1 and 2).
Can be accessed via: machine.SPI(id, sck=sck, mosi=mosi, miso=miso) Where id is 1 or 2 and sck/mosi/miso are the pins to use (can be any pin or None to not use that pin).
1 parent 18d23b3 commit 38c6e55

File tree

4 files changed

+376
-0
lines changed

4 files changed

+376
-0
lines changed

esp32/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ SRC_C = \
128128
modsocket.c \
129129
modesp.c \
130130
espneopixel.c \
131+
machine_hw_spi.c \
131132
$(SRC_MOD)
132133

133134
STM_SRC_C = $(addprefix stmhal/,\
@@ -201,6 +202,7 @@ ESPIDF_DRIVER_O = $(addprefix $(ESPCOMP)/driver/,\
201202
ledc.o \
202203
gpio.o \
203204
timer.o \
205+
spi_master.o \
204206
rtc_module.o \
205207
)
206208

esp32/machine_hw_spi.c

Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2017 "Eric Poulsen" <eric@zyxod.com>
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include <stdio.h>
28+
#include <stdint.h>
29+
#include <string.h>
30+
31+
#include "py/runtime.h"
32+
#include "py/stream.h"
33+
#include "py/mphal.h"
34+
#include "extmod/machine_spi.h"
35+
#include "modmachine.h"
36+
37+
#include "driver/spi_master.h"
38+
39+
typedef struct _machine_hw_spi_obj_t {
40+
mp_obj_base_t base;
41+
spi_host_device_t host;
42+
uint32_t baudrate;
43+
uint8_t polarity;
44+
uint8_t phase;
45+
uint8_t bits;
46+
uint8_t firstbit;
47+
int8_t sck;
48+
int8_t mosi;
49+
int8_t miso;
50+
spi_device_handle_t spi;
51+
enum {
52+
MACHINE_HW_SPI_STATE_NONE,
53+
MACHINE_HW_SPI_STATE_INIT,
54+
MACHINE_HW_SPI_STATE_DEINIT
55+
} state;
56+
} machine_hw_spi_obj_t;
57+
58+
STATIC void machine_hw_spi_deinit_internal(machine_hw_spi_obj_t *self) {
59+
switch (spi_bus_remove_device(self->spi)) {
60+
case ESP_ERR_INVALID_ARG:
61+
mp_raise_msg(&mp_type_OSError, "invalid configuration");
62+
return;
63+
64+
case ESP_ERR_INVALID_STATE:
65+
mp_raise_msg(&mp_type_OSError, "SPI device already freed");
66+
return;
67+
}
68+
69+
switch (spi_bus_free(self->host)) {
70+
case ESP_ERR_INVALID_ARG:
71+
mp_raise_msg(&mp_type_OSError, "invalid configuration");
72+
return;
73+
74+
case ESP_ERR_INVALID_STATE:
75+
mp_raise_msg(&mp_type_OSError, "SPI bus already freed");
76+
return;
77+
}
78+
79+
int8_t pins[3] = {self->miso, self->mosi, self->sck};
80+
81+
for (int i = 0; i < 3; i++) {
82+
if (pins[i] != -1) {
83+
gpio_pad_select_gpio(pins[i]);
84+
gpio_matrix_out(pins[i], SIG_GPIO_OUT_IDX, false, false);
85+
gpio_set_direction(pins[i], GPIO_MODE_INPUT);
86+
}
87+
}
88+
}
89+
90+
STATIC void machine_hw_spi_init_internal(
91+
machine_hw_spi_obj_t *self,
92+
int8_t host,
93+
int32_t baudrate,
94+
int8_t polarity,
95+
int8_t phase,
96+
int8_t bits,
97+
int8_t firstbit,
98+
int8_t sck,
99+
int8_t mosi,
100+
int8_t miso) {
101+
102+
// if we're not initialized, then we're
103+
// implicitly 'changed', since this is the init routine
104+
bool changed = self->state != MACHINE_HW_SPI_STATE_INIT;
105+
106+
esp_err_t ret;
107+
108+
machine_hw_spi_obj_t old_self = *self;
109+
110+
if (host != -1 && host != self->host) {
111+
self->host = host;
112+
changed = true;
113+
}
114+
115+
if (baudrate != -1 && baudrate != self->baudrate) {
116+
self->baudrate = baudrate;
117+
changed = true;
118+
}
119+
120+
if (polarity != -1 && polarity != self->polarity) {
121+
self->polarity = polarity;
122+
changed = true;
123+
}
124+
125+
if (phase != -1 && phase != self->phase) {
126+
self->phase = phase;
127+
changed = true;
128+
}
129+
130+
if (bits != -1 && bits != self->bits) {
131+
self->bits = bits;
132+
changed = true;
133+
}
134+
135+
if (firstbit != -1 && firstbit != self->firstbit) {
136+
self->firstbit = firstbit;
137+
changed = true;
138+
}
139+
140+
if (sck != -2 && sck != self->sck) {
141+
self->sck = sck;
142+
changed = true;
143+
}
144+
145+
if (mosi != -2 && mosi != self->mosi) {
146+
self->mosi = mosi;
147+
changed = true;
148+
}
149+
150+
if (miso != -2 && miso != self->miso) {
151+
self->miso = miso;
152+
changed = true;
153+
}
154+
155+
if (self->host != HSPI_HOST && self->host != VSPI_HOST) {
156+
mp_raise_ValueError("SPI ID must be either HSPI(1) or VSPI(2)");
157+
}
158+
159+
if (changed) {
160+
if (self->state == MACHINE_HW_SPI_STATE_INIT) {
161+
self->state = MACHINE_HW_SPI_STATE_DEINIT;
162+
machine_hw_spi_deinit_internal(&old_self);
163+
}
164+
} else {
165+
return; // no changes
166+
}
167+
168+
spi_bus_config_t buscfg = {
169+
.miso_io_num = self->miso,
170+
.mosi_io_num = self->mosi,
171+
.sclk_io_num = self->sck,
172+
.quadwp_io_num = -1,
173+
.quadhd_io_num = -1
174+
};
175+
176+
spi_device_interface_config_t devcfg = {
177+
.clock_speed_hz = self->baudrate,
178+
.mode = self->phase | (self->polarity << 1),
179+
.spics_io_num = -1, // No CS pin
180+
.queue_size = 1,
181+
.flags = self->firstbit == MICROPY_PY_MACHINE_SPI_LSB ? SPI_DEVICE_TXBIT_LSBFIRST | SPI_DEVICE_RXBIT_LSBFIRST : 0,
182+
.pre_cb = NULL
183+
};
184+
185+
//Initialize the SPI bus
186+
// FIXME: Does the DMA matter? There are two
187+
188+
ret = spi_bus_initialize(self->host, &buscfg, 1);
189+
switch (ret) {
190+
case ESP_ERR_INVALID_ARG:
191+
mp_raise_msg(&mp_type_OSError, "invalid configuration");
192+
return;
193+
194+
case ESP_ERR_INVALID_STATE:
195+
mp_raise_msg(&mp_type_OSError, "SPI device already in use");
196+
return;
197+
}
198+
199+
ret = spi_bus_add_device(self->host, &devcfg, &self->spi);
200+
switch (ret) {
201+
case ESP_ERR_INVALID_ARG:
202+
mp_raise_msg(&mp_type_OSError, "invalid configuration");
203+
spi_bus_free(self->host);
204+
return;
205+
206+
case ESP_ERR_NO_MEM:
207+
mp_raise_msg(&mp_type_OSError, "out of memory");
208+
spi_bus_free(self->host);
209+
return;
210+
211+
case ESP_ERR_NOT_FOUND:
212+
mp_raise_msg(&mp_type_OSError, "no free slots");
213+
spi_bus_free(self->host);
214+
return;
215+
}
216+
self->state = MACHINE_HW_SPI_STATE_INIT;
217+
}
218+
219+
STATIC void machine_hw_spi_deinit(mp_obj_base_t *self_in) {
220+
machine_hw_spi_obj_t *self = (machine_hw_spi_obj_t *) self_in;
221+
if (self->state == MACHINE_HW_SPI_STATE_INIT) {
222+
self->state = MACHINE_HW_SPI_STATE_DEINIT;
223+
machine_hw_spi_deinit_internal(self);
224+
}
225+
}
226+
227+
STATIC void machine_hw_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
228+
machine_hw_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
229+
230+
int bits_to_send = len * self->bits;
231+
bool shortMsg = len <= 4;
232+
233+
if (self->state == MACHINE_HW_SPI_STATE_DEINIT) {
234+
mp_raise_msg(&mp_type_OSError, "transfer on deinitialized SPI");
235+
return;
236+
}
237+
238+
struct spi_transaction_t transaction = {
239+
.flags = 0,
240+
.length = bits_to_send,
241+
.tx_buffer = NULL,
242+
.rx_buffer = NULL,
243+
};
244+
245+
if (shortMsg) {
246+
if (src != NULL) {
247+
memcpy(&transaction.tx_data, src, len);
248+
}
249+
transaction.flags |= (SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA);
250+
} else {
251+
transaction.tx_buffer = src;
252+
transaction.rx_buffer = dest;
253+
}
254+
255+
spi_device_transmit(self->spi, &transaction);
256+
257+
if (shortMsg && dest != NULL) {
258+
memcpy(dest, &transaction.rx_data, len);
259+
}
260+
}
261+
262+
/******************************************************************************/
263+
// MicroPython bindings for hw_spi
264+
265+
STATIC void machine_hw_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
266+
machine_hw_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
267+
mp_printf(print, "SPI(id=%u, baudrate=%u, polarity=%u, phase=%u, bits=%u, firstbit=%u, sck=%d, mosi=%d, miso=%d)",
268+
self->host, self->baudrate, self->polarity,
269+
self->phase, self->bits, self->firstbit,
270+
self->sck, self->mosi, self->miso);
271+
}
272+
273+
STATIC void machine_hw_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
274+
machine_hw_spi_obj_t *self = (machine_hw_spi_obj_t *) self_in;
275+
276+
enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso };
277+
static const mp_arg_t allowed_args[] = {
278+
{ MP_QSTR_id, MP_ARG_INT , {.u_int = -1} },
279+
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} },
280+
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
281+
{ MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
282+
{ MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
283+
{ MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
284+
{ MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
285+
{ MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
286+
{ MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
287+
};
288+
289+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
290+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args),
291+
allowed_args, args);
292+
int8_t sck, mosi, miso;
293+
294+
if (args[ARG_sck].u_obj == MP_OBJ_NULL) {
295+
sck = -2;
296+
} else if (args[ARG_sck].u_obj == mp_const_none) {
297+
sck = -1;
298+
} else {
299+
sck = machine_pin_get_id(args[ARG_sck].u_obj);
300+
}
301+
302+
if (args[ARG_miso].u_obj == MP_OBJ_NULL) {
303+
miso = -2;
304+
} else if (args[ARG_miso].u_obj == mp_const_none) {
305+
miso = -1;
306+
} else {
307+
miso = machine_pin_get_id(args[ARG_miso].u_obj);
308+
}
309+
310+
if (args[ARG_mosi].u_obj == MP_OBJ_NULL) {
311+
mosi = -2;
312+
} else if (args[ARG_mosi].u_obj == mp_const_none) {
313+
mosi = -1;
314+
} else {
315+
mosi = machine_pin_get_id(args[ARG_mosi].u_obj);
316+
}
317+
318+
machine_hw_spi_init_internal(self, args[ARG_id].u_int, args[ARG_baudrate].u_int,
319+
args[ARG_polarity].u_int, args[ARG_phase].u_int, args[ARG_bits].u_int,
320+
args[ARG_firstbit].u_int, sck, mosi, miso);
321+
}
322+
323+
mp_obj_t machine_hw_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
324+
enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso };
325+
static const mp_arg_t allowed_args[] = {
326+
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT , {.u_int = -1} },
327+
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} },
328+
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
329+
{ MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
330+
{ MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
331+
{ MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MICROPY_PY_MACHINE_SPI_MSB} },
332+
{ MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
333+
{ MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
334+
{ MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
335+
};
336+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
337+
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
338+
339+
machine_hw_spi_obj_t *self = m_new_obj(machine_hw_spi_obj_t);
340+
self->base.type = &machine_hw_spi_type;
341+
342+
machine_hw_spi_init_internal(
343+
self,
344+
args[ARG_id].u_int,
345+
args[ARG_baudrate].u_int,
346+
args[ARG_polarity].u_int,
347+
args[ARG_phase].u_int,
348+
args[ARG_bits].u_int,
349+
args[ARG_firstbit].u_int,
350+
args[ARG_sck].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_sck].u_obj),
351+
args[ARG_mosi].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_mosi].u_obj),
352+
args[ARG_miso].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_miso].u_obj));
353+
354+
return MP_OBJ_FROM_PTR(self);
355+
}
356+
357+
STATIC const mp_machine_spi_p_t machine_hw_spi_p = {
358+
.init = machine_hw_spi_init,
359+
.deinit = machine_hw_spi_deinit,
360+
.transfer = machine_hw_spi_transfer,
361+
};
362+
363+
const mp_obj_type_t machine_hw_spi_type = {
364+
{ &mp_type_type },
365+
.name = MP_QSTR_SPI,
366+
.print = machine_hw_spi_print,
367+
.make_new = machine_hw_spi_make_new,
368+
.protocol = &machine_hw_spi_p,
369+
.locals_dict = (mp_obj_dict_t *) &mp_machine_spi_locals_dict,
370+
};

esp32/modmachine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ extern const mp_obj_type_t machine_pin_type;
77
extern const mp_obj_type_t machine_touchpad_type;
88
extern const mp_obj_type_t machine_adc_type;
99
extern const mp_obj_type_t machine_dac_type;
10+
extern const mp_obj_type_t machine_hw_spi_type;
1011

1112
void machine_pins_init(void);
1213
void machine_pins_deinit(void);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy