Skip to content

Commit fb8fbbf

Browse files
authored
Merge pull request #2613 from dhalbert/spim3
nrf: add SPIM3 support
2 parents 8364c60 + c57ccd5 commit fb8fbbf

File tree

6 files changed

+79
-30
lines changed

6 files changed

+79
-30
lines changed

locale/circuitpython.pot

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ msgid ""
88
msgstr ""
99
"Project-Id-Version: PACKAGE VERSION\n"
1010
"Report-Msgid-Bugs-To: \n"
11-
"POT-Creation-Date: 2020-02-07 10:02-0500\n"
11+
"POT-Creation-Date: 2020-02-11 19:18-0500\n"
1212
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414
"Language-Team: LANGUAGE <[email protected]>\n"

ports/nrf/boards/common.template.ld

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ MEMORY
2222
/* SoftDevice 6.1.0 with 5 connections and various increases takes just under 64kiB.
2323
/* To measure the minimum required amount of memory for given configuration, set this number
2424
high enough to work and then check the mutation of the value done by sd_ble_enable. */
25-
RAM (xrw) : ORIGIN = 0x20000000 + 64K, LENGTH = 256K - 64K
25+
SPIM3_RAM (rw) : ORIGIN = 0x20000000 + 64K, LENGTH = 8K
26+
RAM (xrw) : ORIGIN = 0x20000000 + 64K + 8K, LENGTH = 256K - 64K -8K
27+
2628
}
2729

2830
/* produce a link error if there is not this amount of RAM available */
@@ -37,6 +39,10 @@ _estack = ORIGIN(RAM) + LENGTH(RAM);
3739
_ram_end = ORIGIN(RAM) + LENGTH(RAM);
3840
_heap_end = 0x20020000; /* tunable */
3941

42+
/* nrf52840 SPIM3 needs its own area to work around hardware problems. Nothing else may use this space. */
43+
_spim3_ram = ORIGIN(SPIM3_RAM);
44+
_spim3_ram_end = ORIGIN(SPIM3_RAM) + LENGTH(RAM);
45+
4046
/* define output sections */
4147
SECTIONS
4248
{

ports/nrf/common-hal/busio/SPI.c

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,47 +22,55 @@
2222
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2323
*/
2424

25+
#include <string.h>
26+
2527
#include "shared-bindings/busio/SPI.h"
2628
#include "py/mperrno.h"
2729
#include "py/runtime.h"
2830

2931
#include "nrfx_spim.h"
3032
#include "nrf_gpio.h"
3133

34+
// These are in order from ighest available frequency to lowest (32MHz first, then 8MHz).
3235
STATIC spim_peripheral_t spim_peripherals[] = {
3336
#if NRFX_CHECK(NRFX_SPIM3_ENABLED)
3437
// SPIM3 exists only on nRF52840 and supports 32MHz max. All other SPIM's are only 8MHz max.
3538
// Allocate SPIM3 first.
3639
{ .spim = NRFX_SPIM_INSTANCE(3),
37-
.max_frequency_MHz = 32,
40+
.max_frequency = 32000000,
3841
.max_xfer_size = SPIM3_EASYDMA_MAXCNT_SIZE,
3942
},
4043
#endif
4144
#if NRFX_CHECK(NRFX_SPIM2_ENABLED)
4245
// SPIM2 is not shared with a TWIM, so allocate before the shared ones.
4346
{ .spim = NRFX_SPIM_INSTANCE(2),
44-
.max_frequency_MHz = 8,
47+
.max_frequency = 8000000,
4548
.max_xfer_size = SPIM2_EASYDMA_MAXCNT_SIZE,
4649
},
4750
#endif
4851
#if NRFX_CHECK(NRFX_SPIM1_ENABLED)
4952
// SPIM1 and TWIM1 share an address.
5053
{ .spim = NRFX_SPIM_INSTANCE(1),
51-
.max_frequency_MHz = 8,
54+
.max_frequency = 8000000,
5255
.max_xfer_size = SPIM1_EASYDMA_MAXCNT_SIZE,
5356
},
5457
#endif
5558
#if NRFX_CHECK(NRFX_SPIM0_ENABLED)
5659
// SPIM0 and TWIM0 share an address.
5760
{ .spim = NRFX_SPIM_INSTANCE(0),
58-
.max_frequency_MHz = 8,
61+
.max_frequency = 8000000,
5962
.max_xfer_size = SPIM0_EASYDMA_MAXCNT_SIZE,
6063
},
6164
#endif
6265
};
6366

6467
STATIC bool never_reset[MP_ARRAY_SIZE(spim_peripherals)];
6568

69+
// Separate RAM area for SPIM3 transmit buffer to avoid SPIM3 hardware errata.
70+
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52840_Rev2%2FERR%2FnRF52840%2FRev2%2Flatest%2Fanomaly_840_198.html
71+
extern uint32_t _spim3_ram;
72+
STATIC uint8_t *spim3_transmit_buffer = (uint8_t *) &_spim3_ram;
73+
6674
void spi_reset(void) {
6775
for (size_t i = 0 ; i < MP_ARRAY_SIZE(spim_peripherals); i++) {
6876
if (never_reset[i]) {
@@ -122,7 +130,7 @@ static nrf_spim_frequency_t baudrate_to_spim_frequency(const uint32_t baudrate)
122130
}
123131

124132
void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t * clock, const mcu_pin_obj_t * mosi, const mcu_pin_obj_t * miso) {
125-
// Find a free instance.
133+
// Find a free instance, with most desirable (highest freq and not shared) allocated first.
126134
self->spim_peripheral = NULL;
127135
for (size_t i = 0 ; i < MP_ARRAY_SIZE(spim_peripherals); i++) {
128136
if ((spim_peripherals[i].spim.p_reg->ENABLE & SPIM_ENABLE_ENABLE_Msk) == 0) {
@@ -137,7 +145,8 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *
137145

138146
nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG(NRFX_SPIM_PIN_NOT_USED, NRFX_SPIM_PIN_NOT_USED,
139147
NRFX_SPIM_PIN_NOT_USED, NRFX_SPIM_PIN_NOT_USED);
140-
config.frequency = NRF_SPIM_FREQ_8M;
148+
149+
config.frequency = baudrate_to_spim_frequency(self->spim_peripheral->max_frequency);
141150

142151
config.sck_pin = clock->number;
143152
self->clock_pin_number = clock->number;
@@ -189,8 +198,7 @@ bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, ui
189198

190199
// Set desired frequency, rounding down, and don't go above available frequency for this SPIM.
191200
nrf_spim_frequency_set(self->spim_peripheral->spim.p_reg,
192-
baudrate_to_spim_frequency(MIN(baudrate,
193-
self->spim_peripheral->max_frequency_MHz * 1000000)));
201+
baudrate_to_spim_frequency(MIN(baudrate, self->spim_peripheral->max_frequency)));
194202

195203
nrf_spim_mode_t mode = NRF_SPIM_MODE_0;
196204
if (polarity) {
@@ -224,21 +232,36 @@ void common_hal_busio_spi_unlock(busio_spi_obj_t *self) {
224232
}
225233

226234
bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len) {
227-
if (len == 0)
235+
if (len == 0) {
228236
return true;
237+
}
238+
239+
const bool is_spim3 = self->spim_peripheral->spim.p_reg == NRF_SPIM3;
229240

230241
const uint32_t max_xfer_size = self->spim_peripheral->max_xfer_size;
231242
const uint32_t parts = len / max_xfer_size;
232243
const uint32_t remainder = len % max_xfer_size;
233244

234245
for (uint32_t i = 0; i < parts; ++i) {
235-
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(data + i * max_xfer_size, max_xfer_size);
246+
uint8_t *start = (uint8_t *) (data + i * max_xfer_size);
247+
if (is_spim3) {
248+
// If SPIM3, copy into unused RAM block, and do DMA from there.
249+
memcpy(spim3_transmit_buffer, start, max_xfer_size);
250+
start = spim3_transmit_buffer;
251+
}
252+
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(start, max_xfer_size);
236253
if (nrfx_spim_xfer(&self->spim_peripheral->spim, &xfer, 0) != NRFX_SUCCESS)
237254
return false;
238255
}
239256

240257
if (remainder > 0) {
241-
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(data + parts * max_xfer_size, remainder);
258+
uint8_t *start = (uint8_t *) (data + parts * max_xfer_size);
259+
if (is_spim3) {
260+
// If SPIM3, copy into unused RAM block, and do DMA from there.
261+
memcpy(spim3_transmit_buffer, start, remainder);
262+
start = spim3_transmit_buffer;
263+
}
264+
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(start, remainder);
242265
if (nrfx_spim_xfer(&self->spim_peripheral->spim, &xfer, 0) != NRFX_SUCCESS)
243266
return false;
244267
}
@@ -247,8 +270,9 @@ bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size
247270
}
248271

249272
bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value) {
250-
if (len == 0)
273+
if (len == 0) {
251274
return true;
275+
}
252276

253277
const uint32_t max_xfer_size = self->spim_peripheral->max_xfer_size;
254278
const uint32_t parts = len / max_xfer_size;
@@ -270,23 +294,37 @@ bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len,
270294
}
271295

272296
bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, uint8_t *data_out, uint8_t *data_in, size_t len) {
273-
if (len == 0)
297+
if (len == 0) {
274298
return true;
299+
}
275300

301+
const bool is_spim3 = self->spim_peripheral->spim.p_reg == NRF_SPIM3;
276302

277303
const uint32_t max_xfer_size = self->spim_peripheral->max_xfer_size;
278304
const uint32_t parts = len / max_xfer_size;
279305
const uint32_t remainder = len % max_xfer_size;
280306

281307
for (uint32_t i = 0; i < parts; ++i) {
282-
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(data_out + i * max_xfer_size, max_xfer_size,
308+
uint8_t *out_start = (uint8_t *) (data_out + i * max_xfer_size);
309+
if (is_spim3) {
310+
// If SPIM3, copy into unused RAM block, and do DMA from there.
311+
memcpy(spim3_transmit_buffer, out_start, max_xfer_size);
312+
out_start = spim3_transmit_buffer;
313+
}
314+
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(out_start, max_xfer_size,
283315
data_in + i * max_xfer_size, max_xfer_size);
284316
if (nrfx_spim_xfer(&self->spim_peripheral->spim, &xfer, 0) != NRFX_SUCCESS)
285317
return false;
286318
}
287319

288320
if (remainder > 0) {
289-
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(data_out + parts * max_xfer_size, remainder,
321+
uint8_t *out_start = (uint8_t *) (data_out + parts * max_xfer_size);
322+
if (is_spim3) {
323+
// If SPIM3, copy into unused RAM block, and do DMA from there.
324+
memcpy(spim3_transmit_buffer, out_start, remainder);
325+
out_start = spim3_transmit_buffer;
326+
}
327+
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(out_start, remainder,
290328
data_in + parts * max_xfer_size, remainder);
291329
if (nrfx_spim_xfer(&self->spim_peripheral->spim, &xfer, 0) != NRFX_SUCCESS)
292330
return false;
@@ -325,9 +363,9 @@ uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t* self) {
325363
}
326364

327365
uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t* self) {
328-
return 0;
366+
return (self->spim_peripheral->spim.p_reg->CONFIG & SPIM_CONFIG_CPHA_Msk) >> SPIM_CONFIG_CPHA_Pos;
329367
}
330368

331369
uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t* self) {
332-
return 0;
370+
return (self->spim_peripheral->spim.p_reg->CONFIG & SPIM_CONFIG_CPOL_Msk) >> SPIM_CONFIG_CPOL_Pos;
333371
}

ports/nrf/common-hal/busio/SPI.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
typedef struct {
3434
nrfx_spim_t spim;
35-
uint8_t max_frequency_MHz;
35+
uint32_t max_frequency;
3636
uint8_t max_xfer_size;
3737
} spim_peripheral_t;
3838

ports/nrf/nrfx_config.h

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,9 @@
2626

2727
// CIRCUITPY_NRF_NUM_I2C is 1 or 2 to choose how many I2C (TWIM) peripherals
2828
// to provide.
29-
// This can go away once we have SPIM3 working: then we can have two
30-
// I2C and two SPI.
29+
// With SPIM3 working we can have two I2C and two SPI.
3130
#ifndef CIRCUITPY_NRF_NUM_I2C
32-
#define CIRCUITPY_NRF_NUM_I2C 1
31+
#define CIRCUITPY_NRF_NUM_I2C 2
3332
#endif
3433

3534
#if CIRCUITPY_NRF_NUM_I2C != 1 && CIRCUITPY_NRF_NUM_I2C != 2
@@ -42,13 +41,12 @@
4241
#define NRFX_SPIM1_ENABLED 1
4342
#endif
4443
#define NRFX_SPIM2_ENABLED 1
45-
// DON'T ENABLE SPIM3 DUE TO ANOMALY WORKAROUND FAILURE (SEE ABOVE).
46-
// #ifdef NRF52840_XXAA
47-
// #define NRFX_SPIM_EXTENDED_ENABLED 1
48-
// #define NRFX_SPIM3_ENABLED 1
49-
// #else
50-
// #define NRFX_SPIM3_ENABLED 0
51-
// #endif
44+
#ifdef NRF52840_XXAA
45+
#define NRFX_SPIM_EXTENDED_ENABLED 1
46+
#define NRFX_SPIM3_ENABLED 1
47+
#else
48+
#define NRFX_SPIM3_ENABLED 0
49+
#endif
5250

5351

5452
#define NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY 7

shared-bindings/busio/SPI.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@
5757
//|
5858
//| Construct an SPI object on the given pins.
5959
//|
60+
//| ..note:: The SPI peripherals allocated in order of desirability, if possible,
61+
//| such as highest speed and not shared use first. For instance, on the nRF52840,
62+
//| there is a single 32MHz SPI peripheral, and multiple 8MHz peripherals,
63+
//| some of which may also be used for I2C. The 32MHz SPI peripheral is returned
64+
//| first, then the exclusive 8MHz SPI peripheral, and finally the shared 8MHz
65+
//| peripherals.
66+
//|
6067
//| .. seealso:: Using this class directly requires careful lock management.
6168
//| Instead, use :class:`~adafruit_bus_device.spi_device.SPIDevice` to
6269
//| manage locks.

0 commit comments

Comments
 (0)