diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/config/sdk_config.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/config/sdk_config.h index 4bef366d3f..09ea50b494 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/config/sdk_config.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/config/sdk_config.h @@ -3218,37 +3218,39 @@ // +// #define NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED 1 + // NRFX_SPIM_ENABLED - nrfx_spim - SPIM peripheral driver //========================================================== #ifndef NRFX_SPIM_ENABLED -#define NRFX_SPIM_ENABLED 0 +#define NRFX_SPIM_ENABLED 1 #endif // NRFX_SPIM0_ENABLED - Enable SPIM0 instance #ifndef NRFX_SPIM0_ENABLED -#define NRFX_SPIM0_ENABLED 0 +#define NRFX_SPIM0_ENABLED 1 #endif // NRFX_SPIM1_ENABLED - Enable SPIM1 instance #ifndef NRFX_SPIM1_ENABLED -#define NRFX_SPIM1_ENABLED 0 +#define NRFX_SPIM1_ENABLED 1 #endif // NRFX_SPIM2_ENABLED - Enable SPIM2 instance #ifndef NRFX_SPIM2_ENABLED -#define NRFX_SPIM2_ENABLED 0 +#define NRFX_SPIM2_ENABLED 1 #endif // NRFX_SPIM3_ENABLED - Enable SPIM3 instance #ifndef NRFX_SPIM3_ENABLED -#define NRFX_SPIM3_ENABLED 0 +#define NRFX_SPIM3_ENABLED 1 #endif // NRFX_SPIM_EXTENDED_ENABLED - Enable extended SPIM features @@ -3448,27 +3450,27 @@ //========================================================== #ifndef NRFX_SPI_ENABLED -#define NRFX_SPI_ENABLED 1 +#define NRFX_SPI_ENABLED 0 #endif // NRFX_SPI0_ENABLED - Enable SPI0 instance #ifndef NRFX_SPI0_ENABLED -#define NRFX_SPI0_ENABLED 1 +#define NRFX_SPI0_ENABLED 0 #endif // NRFX_SPI1_ENABLED - Enable SPI1 instance #ifndef NRFX_SPI1_ENABLED -#define NRFX_SPI1_ENABLED 1 +#define NRFX_SPI1_ENABLED 0 #endif // NRFX_SPI2_ENABLED - Enable SPI2 instance #ifndef NRFX_SPI2_ENABLED -#define NRFX_SPI2_ENABLED 1 +#define NRFX_SPI2_ENABLED 0 #endif // NRFX_SPI_MISO_PULL_CFG - MISO pin pull configuration. diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h index 916e75985d..13dbe946b7 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h @@ -43,7 +43,11 @@ #include "PortNames.h" #include "PeripheralNames.h" #include "PinNames.h" -#include "nrfx_spi.h" +#if NRFX_SPIM_ENABLED +#include "nrfx_spim.h" +#elif NRFX_SPI_ENABLED +#include "nrfx_spim.h" +#endif #include "nrf_twi.h" #include "nrf_pwm.h" @@ -90,7 +94,11 @@ struct serial_s { struct spi_s { int instance; PinName cs; +#if NRFX_SPIM_ENABLED + nrfx_spim_config_t config; +#elif NRFX_SPI_ENABLED nrfx_spi_config_t config; +#endif bool update; #if DEVICE_SPI_ASYNCH diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/spi_api.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/spi_api.c index 47965dce2b..0cbf2dce2b 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/spi_api.c +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/spi_api.c @@ -44,6 +44,835 @@ #include "pinmap_ex.h" #include "PeripheralPins.h" +#if NRFX_CHECK(NRFX_SPIM_ENABLED) +#include "nrfx_spim.h" + +#if 1 +#define DEBUG_PRINTF(...) printf(__VA_ARGS__) +#else +#define DEBUG_PRINTF(...) +#endif + +/* Pre-allocate instances and share them globally. */ +static const nrfx_spim_t nordic_nrf5_spim_instance[4] = { + NRFX_SPIM_INSTANCE(0), + NRFX_SPIM_INSTANCE(1), + NRFX_SPIM_INSTANCE(2), + NRFX_SPIM_INSTANCE(3) +}; + +/* Keep track of which instance has been initialized. */ +static bool nordic_nrf5_spi_initialized[4] = { false, false, false, false }; + +/* Forware declare interrupt handler. */ +#if DEVICE_SPI_ASYNCH +static void nordic_nrf5_spi_event_handler(nrfx_spim_evt_t const *p_event, void *p_context); +#endif + +/* Forward declaration. These functions are implemented in the driver but not + * set up in the NVIC due to it being relocated. + */ +void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void); +void SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler(void); +void SPIM2_SPIS2_SPI2_IRQHandler(void); + +/** + * Brief Reconfigure peripheral. + * + * If the peripheral has changed ownership clear old configuration and + * re-initialize the peripheral with the new settings. + * + * Parameter obj The object + * Parameter handler Optional callback handler. + * Parameter force_change Force change regardless of ownership. + */ +static void spi_configure_driver_instance(spi_t *obj) +{ +#if DEVICE_SPI_ASYNCH + struct spi_s *spi_inst = &obj->spi; +#else + struct spi_s *spi_inst = obj; +#endif + + int instance = spi_inst->instance; + + /* Get pointer to object of the current owner of the peripheral. */ + void *current_owner = object_owner_spi2c_get(instance); + + /* Check if reconfiguration is actually necessary. */ + if ((obj != current_owner) || spi_inst->update) { + + /* Update applied, reset flag. */ + spi_inst->update = false; + + /* Clean up and uninitialize peripheral if already initialized. */ + if (nordic_nrf5_spi_initialized[instance]) { + nrfx_spim_uninit(&nordic_nrf5_spim_instance[instance]); + } + +#if DEVICE_SPI_ASYNCH + /* Set callback handler in asynchronous mode. */ + if (spi_inst->handler) { + nrfx_spim_init(&nordic_nrf5_spim_instance[instance], &(spi_inst->config), nordic_nrf5_spi_event_handler, obj); + } else { + nrfx_spim_init(&nordic_nrf5_spim_instance[instance], &(spi_inst->config), NULL, NULL); + } +#else + /* Set callback handler to NULL in synchronous mode. */ + nrfx_spim_init(&nordic_nrf5_spim_instance[instance], &(spi_inst->config), NULL, NULL); +#endif + + /* Mark instance as initialized. */ + nordic_nrf5_spi_initialized[instance] = true; + + /* Claim ownership of peripheral. */ + object_owner_spi2c_set(instance, obj); + } +} + +void spi_get_capabilities(PinName ssel, bool slave, spi_capabilities_t *cap) +{ + if (slave) { + cap->minimum_frequency = 200000; // 200 kHz + cap->maximum_frequency = 2000000; // 2 MHz + cap->word_length = 0x00000080; // 8 bit symbols + cap->support_slave_mode = false; // to be determined later based on ssel + cap->hw_cs_handle = false; // irrelevant in slave mode + cap->slave_delay_between_symbols_ns = 2500; // 2.5 us + cap->clk_modes = 0x0f; // all clock modes + cap->tx_rx_buffers_equal_length = false; // rx/tx buffers can have different sizes +#if DEVICE_SPI_ASYNCH + cap->async_mode = true; +#else + cap->async_mode = false; +#endif + } else { + cap->minimum_frequency = 200000; // 200 kHz + cap->maximum_frequency = 2000000; // 2 MHz + cap->word_length = 0x00000080; // 8 bit symbols + cap->support_slave_mode = false; // to be determined later based on ssel + cap->hw_cs_handle = false; // to be determined later based on ssel + cap->slave_delay_between_symbols_ns = 0; // irrelevant in master mode + cap->clk_modes = 0x0f; // all clock modes + cap->tx_rx_buffers_equal_length = false; // rx/tx buffers can have different sizes +#if DEVICE_SPI_ASYNCH + cap->async_mode = true; +#else + cap->async_mode = false; +#endif + } + + // check if given ssel pin is in the cs pinmap + const PinMap *cs_pins = spi_master_cs_pinmap(); + PinName pin = NC; + while (cs_pins->pin != NC) { + if (cs_pins->pin == ssel) { +#if DEVICE_SPISLAVE + cap->support_slave_mode = true; +#endif + cap->hw_cs_handle = true; + break; + } + cs_pins++; + } +} + +/** Initialize the SPI peripheral + * + * Configures the pins used by SPI, sets a default format and frequency, and enables the peripheral + * Parameter obj The SPI object to initialize + * Parameter mosi The pin to use for MOSI + * Parameter miso The pin to use for MISO + * Parameter sclk The pin to use for SCLK + * Parameter ssel The pin to use for SSEL + */ +void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel) +{ + DEBUG_PRINTF("spi_api: spi_init - ENTER\r\n"); +#if DEVICE_SPI_ASYNCH + struct spi_s *spi_inst = &obj->spi; +#else + struct spi_s *spi_inst = obj; +#endif + + /* Get instance based on requested pins. */ + spi_inst->instance = pin_instance_spi(mosi, miso, sclk); + MBED_ASSERT(spi_inst->instance < NRFX_SPIM_ENABLED_COUNT); + DEBUG_PRINTF("spi_api: spi_init - spi_inst->instance=%d\r\n", spi_inst->instance); + + /* Store chip select separately for manual enabling. */ + spi_inst->cs = ssel; + + /* Store pins except chip select. */ + spi_inst->config.sck_pin = sclk; + spi_inst->config.mosi_pin = mosi; + spi_inst->config.miso_pin = miso; + spi_inst->config.ss_pin = NRFX_SPIM_PIN_NOT_USED; + + /* Use the default config. */ + spi_inst->config.irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY; + spi_inst->config.orc = SPI_FILL_CHAR; + spi_inst->config.frequency = NRF_SPIM_FREQ_4M; + spi_inst->config.mode = NRF_SPIM_MODE_0; + spi_inst->config.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST; + +#if DEVICE_SPI_ASYNCH + /* Set default values for asynchronous variables. */ + spi_inst->handler = 0; + spi_inst->mask = 0; + spi_inst->event = 0; +#endif + + /* Configure peripheral. This is called on each init to ensure all pins are set correctly + * according to the SPI mode before calling CS for the first time. + */ + spi_configure_driver_instance(obj); + + /* Configure GPIO pin if chip select has been set. */ + if (ssel != NC) { + nrf_gpio_pin_set(ssel); + nrf_gpio_cfg_output(ssel); + } + + static bool first_init = true; + + if (first_init) { + first_init = false; + + /* Register interrupt handlers in driver with the NVIC. */ + NVIC_SetVector(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn, (uint32_t) SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler); + NVIC_SetVector(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQn, (uint32_t) SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler); + NVIC_SetVector(SPIM2_SPIS2_SPI2_IRQn, (uint32_t) SPIM2_SPIS2_SPI2_IRQHandler); + } + DEBUG_PRINTF("spi_api: spi_init - EXIT\r\n"); +} + +/** Release a SPI object + * + * TODO: spi_free is currently unimplemented + * This will require reference counting at the C++ level to be safe + * + * Return the pins owned by the SPI object to their reset state + * Disable the SPI peripheral + * Disable the SPI clock + * Parameter obj The SPI object to deinitialize + */ +void spi_free(spi_t *obj) +{ +#if DEVICE_SPI_ASYNCH + struct spi_s *spi_inst = &obj->spi; +#else + struct spi_s *spi_inst = obj; +#endif + + int instance = spi_inst->instance; + + /* Use driver uninit to free instance. */ + nrfx_spim_uninit(&nordic_nrf5_spim_instance[instance]); + + /* Mark instance as uninitialized. */ + nordic_nrf5_spi_initialized[instance] = false; +} + +/** Configure the SPI format + * + * Set the number of bits per frame, configure clock polarity and phase, shift order and master/slave mode. + * The default bit order is MSB. + * Parameter obj The SPI object to configure + * Parameter bits The number of bits per frame + * Parameter mode The SPI mode (clock polarity, phase, and shift direction) + * Parameter slave Zero for master mode or non-zero for slave mode + */ +void spi_format(spi_t *obj, int bits, int mode, int slave) +{ + /* SPI module only supports 8 bit transfers. */ + MBED_ASSERT(bits == 8); + /* SPI module doesn't support Mbed HAL Slave API. */ + MBED_ASSERT(slave == 0); + +#if DEVICE_SPI_ASYNCH + struct spi_s *spi_inst = &obj->spi; +#else + struct spi_s *spi_inst = obj; +#endif + + nrf_spim_mode_t new_mode = NRF_SPIM_MODE_0; + + /* Convert Mbed HAL mode to Nordic mode. */ + if (mode == 0) { + new_mode = NRF_SPIM_MODE_0; + } else if (mode == 1) { + new_mode = NRF_SPIM_MODE_1; + } else if (mode == 2) { + new_mode = NRF_SPIM_MODE_2; + } else if (mode == 3) { + new_mode = NRF_SPIM_MODE_3; + } + + /* Check if configuration has changed. */ + if (spi_inst->config.mode != new_mode) { + spi_inst->config.mode = new_mode; + + /* Set flag to force update. */ + spi_inst->update = true; + } + + /* Configure peripheral if necessary. Must be called on each format to ensure the pins are set + * correctly according to the SPI mode. + */ + spi_configure_driver_instance(obj); +} + +/** Set the SPI baud rate + * + * Actual frequency may differ from the desired frequency due to available dividers and bus clock + * Configures the SPI peripheral's baud rate + * Parameter obj The SPI object to configure + * Parameter hz The baud rate in Hz + */ +void spi_frequency(spi_t *obj, int hz) +{ +#if DEVICE_SPI_ASYNCH + struct spi_s *spi_inst = &obj->spi; +#else + struct spi_s *spi_inst = obj; +#endif + + nrf_spim_frequency_t new_frequency = NRF_SPIM_FREQ_1M; + + /* Convert frequency to Nordic enum type. */ + if (hz < 250000) { + new_frequency = NRF_SPIM_FREQ_125K; + } else if (hz < 500000) { + new_frequency = NRF_SPIM_FREQ_250K; + } else if (hz < 1000000) { + new_frequency = NRF_SPIM_FREQ_500K; + } else if (hz < 2000000) { + new_frequency = NRF_SPIM_FREQ_1M; + } else if (hz < 4000000) { + new_frequency = NRF_SPIM_FREQ_2M; + } else if (hz < 8000000) { + new_frequency = NRF_SPIM_FREQ_4M; + } else { + new_frequency = NRF_SPIM_FREQ_8M; + } + + /* Check if configuration has changed. */ + if (spi_inst->config.frequency != new_frequency) { + spi_inst->config.frequency = new_frequency; + + /* Set flag to force update. */ + spi_inst->update = true; + } +} + +/** Write a byte out in master mode and receive a value + * + * Parameter obj The SPI peripheral to use for sending + * Parameter value The value to send + * Return Returns the value received during send + */ +int spi_master_write(spi_t *obj, int value) +{ + nrfx_err_t ret; + nrfx_spim_xfer_desc_t desc; +#if DEVICE_SPI_ASYNCH + struct spi_s *spi_inst = &obj->spi; +#else + struct spi_s *spi_inst = obj; +#endif + + int instance = spi_inst->instance; + + /* Local variables used in transfer. */ + const uint8_t tx_buff = (uint8_t) value; + uint8_t rx_buff; + + /* Configure peripheral if necessary. */ + spi_configure_driver_instance(obj); + + /* Manually clear chip select pin if defined. */ + if (spi_inst->cs != NC) { + nrf_gpio_pin_clear(spi_inst->cs); + } + + /* Transfer 1 byte. */ + desc.p_tx_buffer = &tx_buff; + desc.p_rx_buffer = &rx_buff; + desc.tx_length = 1; + desc.rx_length = 1; + ret = nrfx_spim_xfer(&nordic_nrf5_spim_instance[instance], &desc, 0); + + if (ret != NRFX_SUCCESS) { + DEBUG_PRINTF("%d error returned from nrf_spi_xfer\n\r"); + } + + /* Manually set chip select pin if defined. */ + if (spi_inst->cs != NC) { + nrf_gpio_pin_set(spi_inst->cs); + } + + return rx_buff; +} + +/** Write a block out in master mode and receive a value + * + * The total number of bytes sent and recieved will be the maximum of + * tx_length and rx_length. The bytes written will be padded with the + * value 0xff. + * + * Parameter obj The SPI peripheral to use for sending + * Parameter tx_buffer Pointer to the byte-array of data to write to the device + * Parameter tx_length Number of bytes to write, may be zero + * Parameter rx_buffer Pointer to the byte-array of data to read from the device + * Parameter rx_length Number of bytes to read, may be zero + * Parameter write_fill Default data transmitted while performing a read + * @returns + * The number of bytes written and read from the device. This is + * maximum of tx_length and rx_length. + */ +int spi_master_block_write(spi_t *obj, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, char write_fill) +{ + nrfx_spim_xfer_desc_t desc; +#if DEVICE_SPI_ASYNCH + struct spi_s *spi_inst = &obj->spi; +#else + struct spi_s *spi_inst = obj; +#endif + + int instance = spi_inst->instance; + + /* Check if overflow character has changed. */ + if (spi_inst->config.orc != write_fill) { + + /* Store new overflow character and force reconfiguration. */ + spi_inst->update = true; + spi_inst->config.orc = write_fill; + } + + /* Configure peripheral if necessary. */ + spi_configure_driver_instance(obj); + + /* Manually clear chip select pin if defined. */ + if (spi_inst->cs != NC) { + nrf_gpio_pin_clear(spi_inst->cs); + } + + /* The Nordic SPI driver is only able to transfer 255 bytes at a time. + * The following code will write/read the data 255 bytes at a time and + * ensure that asymmetrical transfers are handled properly. + */ + int tx_offset = 0; + int rx_offset = 0; + + ret_code_t result = NRFX_SUCCESS; + + /* Loop until all data is sent and received. */ + while (((tx_length > 0) || (rx_length > 0)) && (result == NRFX_SUCCESS)) { + + /* Check if tx_length is larger than 255 and if so, limit to 255. */ + int tx_actual_length = (tx_length > 255) ? 255 : tx_length; + + /* Set tx buffer pointer. Set to NULL if no data is going to be transmitted. */ + const uint8_t *tx_actual_buffer = (tx_actual_length > 0) ? + (const uint8_t *)(tx_buffer + tx_offset) : + NULL; + + /* Check if rx_length is larger than 255 and if so, limit to 255. */ + int rx_actual_length = (rx_length > 255) ? 255 : rx_length; + + /* Set rx buffer pointer. Set to NULL if no data is going to be received. */ + uint8_t *rx_actual_buffer = (rx_actual_length > 0) ? + (uint8_t *)(rx_buffer + rx_offset) : + NULL; + + /* Blocking transfer. */ + desc.p_tx_buffer = tx_actual_buffer; + desc.p_rx_buffer = rx_actual_buffer; + desc.tx_length = tx_actual_length; + desc.rx_length = rx_actual_length; + result = nrfx_spim_xfer(&nordic_nrf5_spim_instance[instance], + &desc, 0); + + /* Update loop variables. */ + tx_length -= tx_actual_length; + tx_offset += tx_actual_length; + + rx_length -= rx_actual_length; + rx_offset += rx_actual_length; + } + + /* Manually set chip select pin if defined. */ + if (spi_inst->cs != NC) { + nrf_gpio_pin_set(spi_inst->cs); + } + + return (rx_offset < tx_offset) ? tx_offset : rx_offset; +} + +/** Checks if the specified SPI peripheral is in use + * + * Parameter obj The SPI peripheral to check + * Return non-zero if the peripheral is currently transmitting + */ +int spi_busy(spi_t *obj) +{ + /* Legacy API call. Always return zero. */ + return 0; +} + +/** Get the module number + * + * Parameter obj The SPI peripheral to check + * Return The module number + */ +uint8_t spi_get_module(spi_t *obj) +{ +#if DEVICE_SPI_ASYNCH + struct spi_s *spi_inst = &obj->spi; +#else + struct spi_s *spi_inst = obj; +#endif + + return spi_inst->instance; +} + +const PinMap *spi_master_mosi_pinmap() +{ + return PinMap_SPI_testing; +} + +const PinMap *spi_master_miso_pinmap() +{ + return PinMap_SPI_testing; +} + +const PinMap *spi_master_clk_pinmap() +{ + return PinMap_SPI_testing; +} + +const PinMap *spi_master_cs_pinmap() +{ + return PinMap_SPI_testing; +} + +const PinMap *spi_slave_mosi_pinmap() +{ + return PinMap_SPI_testing; +} + +const PinMap *spi_slave_miso_pinmap() +{ + return PinMap_SPI_testing; +} + +const PinMap *spi_slave_clk_pinmap() +{ + return PinMap_SPI_testing; +} + +const PinMap *spi_slave_cs_pinmap() +{ + return PinMap_SPI_testing; +} + +#if DEVICE_SPISLAVE + +/** Check if a value is available to read + * + * Parameter obj The SPI peripheral to check + * Return non-zero if a value is available + */ +int spi_slave_receive(spi_t *obj) +{ + return 0; +} + +/** Get a received value out of the SPI receive buffer in slave mode + * + * Blocks until a value is available + * Parameter obj The SPI peripheral to read + * Return The value received + */ +int spi_slave_read(spi_t *obj) +{ + return 0; +} + +/** Write a value to the SPI peripheral in slave mode + * + * Blocks until the SPI peripheral can be written to + * Parameter obj The SPI peripheral to write + * Parameter value The value to write + */ +void spi_slave_write(spi_t *obj, int value) +{ + return; +} + +#endif + +#if DEVICE_SPI_ASYNCH + +/*** + * _____ _____ + * /\ /\ | __ \_ _| + * / \ ___ _ _ _ __ ___ / \ | |__) || | + * / /\ \ / __| | | | '_ \ / __| / /\ \ | ___/ | | + * / ____ \\__ \ |_| | | | | (__ / ____ \| | _| |_ + * /_/ \_\___/\__, |_| |_|\___| /_/ \_\_| |_____| + * __/ | + * |___/ + */ + +static ret_code_t spi_master_transfer_async_continue(spi_t *obj) +{ + nrfx_spim_xfer_desc_t desc; + /* Remaining data to be transferred. */ + size_t tx_length = obj->tx_buff.length - obj->tx_buff.pos; + size_t rx_length = obj->rx_buff.length - obj->rx_buff.pos; + + /* Cap TX length to 255 bytes. */ + if (tx_length > 255) { + tx_length = 255; + } + + /* Cap RX length to 255 bytes. */ + if (rx_length > 255) { + rx_length = 255; + } + + desc.p_tx_buffer = ((const uint8_t *)(obj->tx_buff.buffer) + obj->tx_buff.pos); + desc.p_rx_buffer = ((uint8_t *)(obj->rx_buff.buffer) + obj->rx_buff.pos); + desc.tx_length = tx_length; + desc.rx_length = rx_length; + + ret_code_t result = nrfx_spim_xfer(&nordic_nrf5_spim_instance[obj->spi.instance], + &desc, 0); + return result; +} + +/* Callback function for driver calls. This is called from ISR context. */ +static void nordic_nrf5_spi_event_handler(nrfx_spim_evt_t const *p_event, void *p_context) +{ + // Only safe to use with mbed-printf. + //DEBUG_PRINTF("nordic_nrf5_twi_event_handler: %d %p\r\n", p_event->type, p_context); + + bool signal_complete = false; + bool signal_error = false; + + spi_t *obj = (spi_t *) p_context; + struct spi_s *spi_inst = &obj->spi; + + if (p_event->type == NRFX_SPIM_EVENT_DONE) { + + /* Update buffers with new positions. */ + obj->tx_buff.pos += p_event->xfer_desc.tx_length; + obj->rx_buff.pos += p_event->xfer_desc.rx_length; + + /* Setup a new transfer if more data is pending. */ + if ((obj->tx_buff.pos < obj->tx_buff.length) || (obj->rx_buff.pos < obj->tx_buff.length)) { + + /* Initiate SPI transfer. */ + ret_code_t result = spi_master_transfer_async_continue(obj); + + /* Abort if transfer wasn't accepted. */ + if (result != NRFX_SUCCESS) { + + /* Signal callback handler that transfer failed. */ + signal_error = true; + } + + } else { + + /* Signal callback handler that transfer is complete. */ + signal_complete = true; + } + } else { + + /* Unexpected event, signal callback handler that transfer failed. */ + signal_error = true; + } + + /* Transfer complete, signal success if mask is set.*/ + if (signal_complete) { + + /* Signal success if event mask matches and event handler is set. */ + if ((spi_inst->mask & SPI_EVENT_COMPLETE) && spi_inst->handler) { + + /* Cast handler to callback function pointer. */ + void (*callback)(void) = (void (*)(void)) spi_inst->handler; + + /* Reset object. */ + spi_inst->handler = 0; + spi_inst->update = true; + + /* Store event value so it can be read back. */ + spi_inst->event = SPI_EVENT_COMPLETE; + + /* Signal callback handler. */ + callback(); + } + + /* Transfer failed, signal error if mask is set. */ + } else if (signal_error) { + + /* Signal error if event mask matches and event handler is set. */ + if ((spi_inst->mask & SPI_EVENT_ERROR) && spi_inst->handler) { + + /* Cast handler to callback function pointer. */ + void (*callback)(void) = (void (*)(void)) spi_inst->handler; + + /* Reset object. */ + spi_inst->handler = 0; + spi_inst->update = true; + + /* Store event value so it can be read back. */ + spi_inst->event = SPI_EVENT_ERROR; + + /* Signal callback handler. */ + callback(); + } + } + + /* Transfer completed one way or another. Set chip select manually if defined. */ + if (signal_complete || signal_error) { + + if (spi_inst->cs != NC) { + nrf_gpio_pin_set(spi_inst->cs); + } + } +} + +/** Begin the SPI transfer. Buffer pointers and lengths are specified in tx_buff and rx_buff + * + * Parameter obj The SPI object that holds the transfer information + * Parameter tx The transmit buffer + * Parameter tx_length The number of bytes to transmit + * Parameter rx The receive buffer + * Parameter rx_length The number of bytes to receive + * Parameter bit_width The bit width of buffer words + * Parameter event The logical OR of events to be registered + * Parameter handler SPI interrupt handler + * Parameter hint A suggestion for how to use DMA with this transfer + */ +void spi_master_transfer(spi_t *obj, + const void *tx, + size_t tx_length, + void *rx, + size_t rx_length, + uint8_t bit_width, + uint32_t handler, + uint32_t mask, + DMAUsage hint) +{ + /* SPI peripheral only supports 8 bit transfers. */ + MBED_ASSERT(bit_width == 8); + + /* Setup buffers for transfer. */ + struct buffer_s *buffer_pointer; + + buffer_pointer = &obj->tx_buff; + buffer_pointer->buffer = (void *) tx; + buffer_pointer->length = tx_length; + buffer_pointer->pos = 0; + buffer_pointer->width = 8; + + buffer_pointer = &obj->rx_buff; + buffer_pointer->buffer = rx; + buffer_pointer->length = rx_length; + buffer_pointer->pos = 0; + buffer_pointer->width = 8; + + /* Save event handler and event mask so they can be called from interrupt handler. */ + struct spi_s *spi_inst = &obj->spi; + spi_inst->handler = handler; + spi_inst->mask = mask; + + /* Clear event flag. */ + spi_inst->event = 0; + + /* Force reconfiguration. */ + spi_inst->update = true; + + /* Configure peripheral if necessary. */ + spi_configure_driver_instance(obj); + + /* Manually clear chip select pin if defined. */ + if (spi_inst->cs != NC) { + nrf_gpio_pin_clear(spi_inst->cs); + } + + /* Initiate SPI transfer. */ + ret_code_t result = spi_master_transfer_async_continue(obj); + + /* Signal error if event mask matches and event handler is set. */ + if ((result != NRFX_SUCCESS) && (mask & SPI_EVENT_ERROR) && handler) { + + /* Cast handler to callback function pointer. */ + void (*callback)(void) = (void (*)(void)) handler; + + /* Reset object. */ + spi_inst->handler = 0; + spi_inst->update = true; + + /* Store event value so it can be read back. */ + spi_inst->event = SPI_EVENT_ERROR; + + /* Signal callback handler. */ + callback(); + } +} + +/** The asynchronous IRQ handler + * + * Reads the received values out of the RX FIFO, writes values into the TX FIFO and checks for transfer termination + * conditions, such as buffer overflows or transfer complete. + * Parameter obj The SPI object that holds the transfer information + * Return Event flags if a transfer termination condition was met; otherwise 0. + */ +uint32_t spi_irq_handler_asynch(spi_t *obj) +{ + /* Return latest event. */ + return obj->spi.event; +} + +/** Attempts to determine if the SPI peripheral is already in use + * + * If a temporary DMA channel has been allocated, peripheral is in use. + * If a permanent DMA channel has been allocated, check if the DMA channel is in use. If not, proceed as though no DMA + * channel were allocated. + * If no DMA channel is allocated, check whether tx and rx buffers have been assigned. For each assigned buffer, check + * if the corresponding buffer position is less than the buffer length. If buffers do not indicate activity, check if + * there are any bytes in the FIFOs. + * Parameter obj The SPI object to check for activity + * Return Non-zero if the SPI port is active or zero if it is not. + */ +uint8_t spi_active(spi_t *obj) +{ + /* Callback handler is non-zero when a transfer is in progress. */ + return (obj->spi.handler != 0); +} + +/** Abort an SPI transfer + * + * Parameter obj The SPI peripheral to stop + */ +void spi_abort_asynch(spi_t *obj) +{ + int instance = obj->spi.instance; + + /* Abort transfer. */ + nrfx_spim_abort(&nordic_nrf5_spim_instance[instance]); + + /* Force reconfiguration. */ + object_owner_spi2c_set(instance, NULL); +} + +#endif // DEVICE_SPI_ASYNCH + +#elif NRFX_CHECK(NRFX_SPI_ENABLED) #include "nrfx_spi.h" #if 0 @@ -867,4 +1696,6 @@ void spi_abort_asynch(spi_t *obj) #endif // DEVICE_SPI_ASYNCH +#endif // NRFX_SPI_ENABLED + #endif // DEVICE_SPI