diff options
| author | Dan Halbert <halbert@halwitz.org> | 2020-02-11 19:19:31 -0500 |
|---|---|---|
| committer | Dan Halbert <halbert@halwitz.org> | 2020-02-11 19:22:14 -0500 |
| commit | 2e029d55fc0396a33fcf8ac73124f571aea34eee (patch) | |
| tree | 95002ac5feb5d55eee1c0776b1a6d4393006bcb0 | |
| parent | 8364c60eb28fa9afffb2a2fac96bd71a18faa6de (diff) | |
nrf: add SPIM3 support
| -rw-r--r-- | locale/circuitpython.pot | 2 | ||||
| -rw-r--r-- | ports/nrf/boards/common.template.ld | 8 | ||||
| -rw-r--r-- | ports/nrf/common-hal/busio/SPI.c | 72 | ||||
| -rw-r--r-- | ports/nrf/common-hal/busio/SPI.h | 2 | ||||
| -rw-r--r-- | ports/nrf/nrfx_config.h | 18 | ||||
| -rw-r--r-- | shared-bindings/busio/SPI.c | 7 |
6 files changed, 79 insertions, 30 deletions
diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 3e4413068..1a378e331 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-02-07 10:02-0500\n" +"POT-Creation-Date: 2020-02-11 19:18-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/ports/nrf/boards/common.template.ld b/ports/nrf/boards/common.template.ld index 607a1dc93..427b6b35b 100644 --- a/ports/nrf/boards/common.template.ld +++ b/ports/nrf/boards/common.template.ld @@ -22,7 +22,9 @@ MEMORY /* SoftDevice 6.1.0 with 5 connections and various increases takes just under 64kiB. /* To measure the minimum required amount of memory for given configuration, set this number high enough to work and then check the mutation of the value done by sd_ble_enable. */ - RAM (xrw) : ORIGIN = 0x20000000 + 64K, LENGTH = 256K - 64K + SPIM3_RAM (rw) : ORIGIN = 0x20000000 + 64K, LENGTH = 8K + RAM (xrw) : ORIGIN = 0x20000000 + 64K + 8K, LENGTH = 256K - 64K -8K + } /* produce a link error if there is not this amount of RAM available */ @@ -37,6 +39,10 @@ _estack = ORIGIN(RAM) + LENGTH(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_end = 0x20020000; /* tunable */ +/* nrf52840 SPIM3 needs its own area to work around hardware problems. Nothing else may use this space. */ +_spim3_ram = ORIGIN(SPIM3_RAM); +_spim3_ram_end = ORIGIN(SPIM3_RAM) + LENGTH(RAM); + /* define output sections */ SECTIONS { diff --git a/ports/nrf/common-hal/busio/SPI.c b/ports/nrf/common-hal/busio/SPI.c index 8e1d73bf0..fe943a221 100644 --- a/ports/nrf/common-hal/busio/SPI.c +++ b/ports/nrf/common-hal/busio/SPI.c @@ -22,6 +22,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <string.h> + #include "shared-bindings/busio/SPI.h" #include "py/mperrno.h" #include "py/runtime.h" @@ -29,33 +31,34 @@ #include "nrfx_spim.h" #include "nrf_gpio.h" +// These are in order from ighest available frequency to lowest (32MHz first, then 8MHz). STATIC spim_peripheral_t spim_peripherals[] = { #if NRFX_CHECK(NRFX_SPIM3_ENABLED) // SPIM3 exists only on nRF52840 and supports 32MHz max. All other SPIM's are only 8MHz max. // Allocate SPIM3 first. { .spim = NRFX_SPIM_INSTANCE(3), - .max_frequency_MHz = 32, + .max_frequency = 32000000, .max_xfer_size = SPIM3_EASYDMA_MAXCNT_SIZE, }, #endif #if NRFX_CHECK(NRFX_SPIM2_ENABLED) // SPIM2 is not shared with a TWIM, so allocate before the shared ones. { .spim = NRFX_SPIM_INSTANCE(2), - .max_frequency_MHz = 8, + .max_frequency = 8000000, .max_xfer_size = SPIM2_EASYDMA_MAXCNT_SIZE, }, #endif #if NRFX_CHECK(NRFX_SPIM1_ENABLED) // SPIM1 and TWIM1 share an address. { .spim = NRFX_SPIM_INSTANCE(1), - .max_frequency_MHz = 8, + .max_frequency = 8000000, .max_xfer_size = SPIM1_EASYDMA_MAXCNT_SIZE, }, #endif #if NRFX_CHECK(NRFX_SPIM0_ENABLED) // SPIM0 and TWIM0 share an address. { .spim = NRFX_SPIM_INSTANCE(0), - .max_frequency_MHz = 8, + .max_frequency = 8000000, .max_xfer_size = SPIM0_EASYDMA_MAXCNT_SIZE, }, #endif @@ -63,6 +66,11 @@ STATIC spim_peripheral_t spim_peripherals[] = { STATIC bool never_reset[MP_ARRAY_SIZE(spim_peripherals)]; +// Separate RAM area for SPIM3 transmit buffer to avoid SPIM3 hardware errata. +// https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52840_Rev2%2FERR%2FnRF52840%2FRev2%2Flatest%2Fanomaly_840_198.html +extern uint32_t _spim3_ram; +STATIC uint8_t *spim3_transmit_buffer = (uint8_t *) &_spim3_ram; + void spi_reset(void) { for (size_t i = 0 ; i < MP_ARRAY_SIZE(spim_peripherals); i++) { if (never_reset[i]) { @@ -122,7 +130,7 @@ static nrf_spim_frequency_t baudrate_to_spim_frequency(const uint32_t baudrate) } 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) { - // Find a free instance. + // Find a free instance, with most desirable (highest freq and not shared) allocated first. self->spim_peripheral = NULL; for (size_t i = 0 ; i < MP_ARRAY_SIZE(spim_peripherals); i++) { 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 * nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG(NRFX_SPIM_PIN_NOT_USED, NRFX_SPIM_PIN_NOT_USED, NRFX_SPIM_PIN_NOT_USED, NRFX_SPIM_PIN_NOT_USED); - config.frequency = NRF_SPIM_FREQ_8M; + + config.frequency = baudrate_to_spim_frequency(self->spim_peripheral->max_frequency); config.sck_pin = clock->number; 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 // Set desired frequency, rounding down, and don't go above available frequency for this SPIM. nrf_spim_frequency_set(self->spim_peripheral->spim.p_reg, - baudrate_to_spim_frequency(MIN(baudrate, - self->spim_peripheral->max_frequency_MHz * 1000000))); + baudrate_to_spim_frequency(MIN(baudrate, self->spim_peripheral->max_frequency))); nrf_spim_mode_t mode = NRF_SPIM_MODE_0; if (polarity) { @@ -224,21 +232,36 @@ void common_hal_busio_spi_unlock(busio_spi_obj_t *self) { } bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len) { - if (len == 0) + if (len == 0) { return true; + } + + const bool is_spim3 = self->spim_peripheral->spim.p_reg == NRF_SPIM3; const uint32_t max_xfer_size = self->spim_peripheral->max_xfer_size; const uint32_t parts = len / max_xfer_size; const uint32_t remainder = len % max_xfer_size; for (uint32_t i = 0; i < parts; ++i) { - const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(data + i * max_xfer_size, max_xfer_size); + uint8_t *start = (uint8_t *) (data + i * max_xfer_size); + if (is_spim3) { + // If SPIM3, copy into unused RAM block, and do DMA from there. + memcpy(spim3_transmit_buffer, start, max_xfer_size); + start = spim3_transmit_buffer; + } + const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(start, max_xfer_size); if (nrfx_spim_xfer(&self->spim_peripheral->spim, &xfer, 0) != NRFX_SUCCESS) return false; } if (remainder > 0) { - const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(data + parts * max_xfer_size, remainder); + uint8_t *start = (uint8_t *) (data + parts * max_xfer_size); + if (is_spim3) { + // If SPIM3, copy into unused RAM block, and do DMA from there. + memcpy(spim3_transmit_buffer, start, remainder); + start = spim3_transmit_buffer; + } + const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(start, remainder); if (nrfx_spim_xfer(&self->spim_peripheral->spim, &xfer, 0) != NRFX_SUCCESS) return false; } @@ -247,8 +270,9 @@ bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size } bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value) { - if (len == 0) + if (len == 0) { return true; + } const uint32_t max_xfer_size = self->spim_peripheral->max_xfer_size; 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, } bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, uint8_t *data_out, uint8_t *data_in, size_t len) { - if (len == 0) + if (len == 0) { return true; + } + const bool is_spim3 = self->spim_peripheral->spim.p_reg == NRF_SPIM3; const uint32_t max_xfer_size = self->spim_peripheral->max_xfer_size; const uint32_t parts = len / max_xfer_size; const uint32_t remainder = len % max_xfer_size; for (uint32_t i = 0; i < parts; ++i) { - const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(data_out + i * max_xfer_size, max_xfer_size, + uint8_t *out_start = (uint8_t *) (data_out + i * max_xfer_size); + if (is_spim3) { + // If SPIM3, copy into unused RAM block, and do DMA from there. + memcpy(spim3_transmit_buffer, out_start, max_xfer_size); + out_start = spim3_transmit_buffer; + } + const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(out_start, max_xfer_size, data_in + i * max_xfer_size, max_xfer_size); if (nrfx_spim_xfer(&self->spim_peripheral->spim, &xfer, 0) != NRFX_SUCCESS) return false; } if (remainder > 0) { - const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(data_out + parts * max_xfer_size, remainder, + uint8_t *out_start = (uint8_t *) (data_out + parts * max_xfer_size); + if (is_spim3) { + // If SPIM3, copy into unused RAM block, and do DMA from there. + memcpy(spim3_transmit_buffer, out_start, remainder); + out_start = spim3_transmit_buffer; + } + const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(out_start, remainder, data_in + parts * max_xfer_size, remainder); if (nrfx_spim_xfer(&self->spim_peripheral->spim, &xfer, 0) != NRFX_SUCCESS) return false; @@ -325,9 +363,9 @@ uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t* self) { } uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t* self) { - return 0; + return (self->spim_peripheral->spim.p_reg->CONFIG & SPIM_CONFIG_CPHA_Msk) >> SPIM_CONFIG_CPHA_Pos; } uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t* self) { - return 0; + return (self->spim_peripheral->spim.p_reg->CONFIG & SPIM_CONFIG_CPOL_Msk) >> SPIM_CONFIG_CPOL_Pos; } diff --git a/ports/nrf/common-hal/busio/SPI.h b/ports/nrf/common-hal/busio/SPI.h index 1b0de8acf..738a1de78 100644 --- a/ports/nrf/common-hal/busio/SPI.h +++ b/ports/nrf/common-hal/busio/SPI.h @@ -32,7 +32,7 @@ typedef struct { nrfx_spim_t spim; - uint8_t max_frequency_MHz; + uint32_t max_frequency; uint8_t max_xfer_size; } spim_peripheral_t; diff --git a/ports/nrf/nrfx_config.h b/ports/nrf/nrfx_config.h index 82f6514df..e37c60934 100644 --- a/ports/nrf/nrfx_config.h +++ b/ports/nrf/nrfx_config.h @@ -26,10 +26,9 @@ // CIRCUITPY_NRF_NUM_I2C is 1 or 2 to choose how many I2C (TWIM) peripherals // to provide. -// This can go away once we have SPIM3 working: then we can have two -// I2C and two SPI. +// With SPIM3 working we can have two I2C and two SPI. #ifndef CIRCUITPY_NRF_NUM_I2C -#define CIRCUITPY_NRF_NUM_I2C 1 +#define CIRCUITPY_NRF_NUM_I2C 2 #endif #if CIRCUITPY_NRF_NUM_I2C != 1 && CIRCUITPY_NRF_NUM_I2C != 2 @@ -42,13 +41,12 @@ #define NRFX_SPIM1_ENABLED 1 #endif #define NRFX_SPIM2_ENABLED 1 -// DON'T ENABLE SPIM3 DUE TO ANOMALY WORKAROUND FAILURE (SEE ABOVE). -// #ifdef NRF52840_XXAA -// #define NRFX_SPIM_EXTENDED_ENABLED 1 -// #define NRFX_SPIM3_ENABLED 1 -// #else -// #define NRFX_SPIM3_ENABLED 0 -// #endif +#ifdef NRF52840_XXAA + #define NRFX_SPIM_EXTENDED_ENABLED 1 + #define NRFX_SPIM3_ENABLED 1 +#else + #define NRFX_SPIM3_ENABLED 0 +#endif #define NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY 7 diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c index d1791bef3..2ae312a35 100644 --- a/shared-bindings/busio/SPI.c +++ b/shared-bindings/busio/SPI.c @@ -56,6 +56,13 @@ //| .. class:: SPI(clock, MOSI=None, MISO=None) //| //| Construct an SPI object on the given pins. + +//| ..note:: The SPI peripherals allocated in order of desirability, if possible, +//| such as highest speed and not shared use first. For instance, on the nRF52840, +//| there is a single 32MHz SPI peripheral, and multiple 8MHz peripherals, +//| some of which may also be used for I2C. The 32MHz SPI peripheral is returned +//| first, then the exclusive 8MHz SPI peripheral, and finally the shared 8MHz +//| peripherals. //| //| .. seealso:: Using this class directly requires careful lock management. //| Instead, use :class:`~adafruit_bus_device.spi_device.SPIDevice` to |
