From dd33956654589ded6644a75088e50069b1744ef9 Mon Sep 17 00:00:00 2001 From: tcsullivan Date: Sun, 10 Mar 2019 15:37:07 -0400 Subject: rtc, keeping time --- drivers_nrf/spi_master/nrf_drv_spi.c | 778 +++++++++++++++++++++++++++++++++ drivers_nrf/spi_master/nrf_drv_spi.h | 413 +++++++++++++++++ drivers_nrf/spi_master/spi_5W_master.c | 629 ++++++++++++++++++++++++++ drivers_nrf/spi_master/spi_5W_master.h | 206 +++++++++ 4 files changed, 2026 insertions(+) create mode 100644 drivers_nrf/spi_master/nrf_drv_spi.c create mode 100644 drivers_nrf/spi_master/nrf_drv_spi.h create mode 100644 drivers_nrf/spi_master/spi_5W_master.c create mode 100644 drivers_nrf/spi_master/spi_5W_master.h (limited to 'drivers_nrf/spi_master') diff --git a/drivers_nrf/spi_master/nrf_drv_spi.c b/drivers_nrf/spi_master/nrf_drv_spi.c new file mode 100644 index 0000000..3b4c912 --- /dev/null +++ b/drivers_nrf/spi_master/nrf_drv_spi.c @@ -0,0 +1,778 @@ +/** + * Copyright (c) 2015 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(SPI) +#define ENABLED_SPI_COUNT (SPI0_ENABLED+SPI1_ENABLED+SPI2_ENABLED) +#if ENABLED_SPI_COUNT + +#include "nrf_drv_spi.h" +#include "nrf_drv_common.h" +#include "nrf_gpio.h" +#include "nrf_assert.h" +#include "app_util_platform.h" + +#define NRF_LOG_MODULE_NAME "SPI" + +#if SPI_CONFIG_LOG_ENABLED +#define NRF_LOG_LEVEL SPI_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR SPI_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR SPI_CONFIG_DEBUG_COLOR +#else //SPI_CONFIG_LOG_ENABLED +#define NRF_LOG_LEVEL 0 +#endif //SPI_CONFIG_LOG_ENABLED +#include "nrf_log.h" + +#ifndef SPIM_PRESENT + // Make sure SPIx_USE_EASY_DMA is 0 for nRF51 (if a common + // "nrf_drv_config.h" file is provided for nRF51 and nRF52). + #undef SPI0_USE_EASY_DMA + #define SPI0_USE_EASY_DMA 0 + #undef SPI1_USE_EASY_DMA + #define SPI1_USE_EASY_DMA 0 + #undef SPI2_USE_EASY_DMA + #define SPI2_USE_EASY_DMA 0 +#endif + +#ifndef SPI0_USE_EASY_DMA +#define SPI0_USE_EASY_DMA 0 +#endif + +#ifndef SPI1_USE_EASY_DMA +#define SPI1_USE_EASY_DMA 0 +#endif + +#ifndef SPI2_USE_EASY_DMA +#define SPI2_USE_EASY_DMA 0 +#endif + +// This set of macros makes it possible to exclude parts of code when one type +// of supported peripherals is not used. +#if ((NRF_MODULE_ENABLED(SPI0) && SPI0_USE_EASY_DMA) || \ + (NRF_MODULE_ENABLED(SPI1) && SPI1_USE_EASY_DMA) || \ + (NRF_MODULE_ENABLED(SPI2) && SPI2_USE_EASY_DMA)) + #define SPIM_IN_USE +#endif +#if ((NRF_MODULE_ENABLED(SPI0) && !SPI0_USE_EASY_DMA) || \ + (NRF_MODULE_ENABLED(SPI1) && !SPI1_USE_EASY_DMA) || \ + (NRF_MODULE_ENABLED(SPI2) && !SPI2_USE_EASY_DMA)) + #define SPI_IN_USE +#endif +#if defined(SPIM_IN_USE) && defined(SPI_IN_USE) + // SPIM and SPI combined + #define CODE_FOR_SPIM(code) if (p_instance->use_easy_dma) { code } + #define CODE_FOR_SPI(code) else { code } +#elif defined(SPIM_IN_USE) && !defined(SPI_IN_USE) + // SPIM only + #define CODE_FOR_SPIM(code) { code } + #define CODE_FOR_SPI(code) +#elif !defined(SPIM_IN_USE) && defined(SPI_IN_USE) + // SPI only + #define CODE_FOR_SPIM(code) + #define CODE_FOR_SPI(code) { code } +#else + #error "Wrong configuration." +#endif + +#ifdef SPIM_IN_USE +#define END_INT_MASK NRF_SPIM_INT_END_MASK +#endif + +// Control block - driver instance local data. +typedef struct +{ + nrf_drv_spi_evt_handler_t handler; + void * p_context; + nrf_drv_spi_evt_t evt; // Keep the struct that is ready for event handler. Less memcpy. + nrf_drv_state_t state; + volatile bool transfer_in_progress; + + // [no need for 'volatile' attribute for the following members, as they + // are not concurrently used in IRQ handlers and main line code] + uint8_t ss_pin; + uint8_t orc; + uint8_t bytes_transferred; + +#if NRF_MODULE_ENABLED(SPIM_NRF52_ANOMALY_109_WORKAROUND) + uint8_t tx_length; + uint8_t rx_length; +#endif + + bool tx_done : 1; + bool rx_done : 1; + bool abort : 1; +} spi_control_block_t; +static spi_control_block_t m_cb[ENABLED_SPI_COUNT]; + +#if NRF_MODULE_ENABLED(PERIPHERAL_RESOURCE_SHARING) + #define IRQ_HANDLER_NAME(n) irq_handler_for_instance_##n + #define IRQ_HANDLER(n) static void IRQ_HANDLER_NAME(n)(void) + + #if NRF_MODULE_ENABLED(SPI0) + IRQ_HANDLER(0); + #endif + #if NRF_MODULE_ENABLED(SPI1) + IRQ_HANDLER(1); + #endif + #if NRF_MODULE_ENABLED(SPI2) + IRQ_HANDLER(2); + #endif + static nrf_drv_irq_handler_t const m_irq_handlers[ENABLED_SPI_COUNT] = { + #if NRF_MODULE_ENABLED(SPI0) + IRQ_HANDLER_NAME(0), + #endif + #if NRF_MODULE_ENABLED(SPI1) + IRQ_HANDLER_NAME(1), + #endif + #if NRF_MODULE_ENABLED(SPI2) + IRQ_HANDLER_NAME(2), + #endif + }; +#else + #define IRQ_HANDLER(n) void SPI##n##_IRQ_HANDLER(void) +#endif // NRF_MODULE_ENABLED(PERIPHERAL_RESOURCE_SHARING) + +ret_code_t nrf_drv_spi_init(nrf_drv_spi_t const * const p_instance, + nrf_drv_spi_config_t const * p_config, + nrf_drv_spi_evt_handler_t handler, + void * p_context) +{ + ASSERT(p_config); + spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + ret_code_t err_code; + + if (p_cb->state != NRF_DRV_STATE_UNINITIALIZED) + { + err_code = NRF_ERROR_INVALID_STATE; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + +#if NRF_MODULE_ENABLED(PERIPHERAL_RESOURCE_SHARING) + if (nrf_drv_common_per_res_acquire(p_instance->p_registers, + m_irq_handlers[p_instance->drv_inst_idx]) != NRF_SUCCESS) + { + err_code = NRF_ERROR_BUSY; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } +#endif + + p_cb->handler = handler; + p_cb->p_context = p_context; + + uint32_t mosi_pin; + uint32_t miso_pin; + // Configure pins used by the peripheral: + // - SCK - output with initial value corresponding with the SPI mode used: + // 0 - for modes 0 and 1 (CPOL = 0), 1 - for modes 2 and 3 (CPOL = 1); + // according to the reference manual guidelines this pin and its input + // buffer must always be connected for the SPI to work. + if (p_config->mode <= NRF_DRV_SPI_MODE_1) + { + nrf_gpio_pin_clear(p_config->sck_pin); + } + else + { + nrf_gpio_pin_set(p_config->sck_pin); + } + nrf_gpio_cfg(p_config->sck_pin, + NRF_GPIO_PIN_DIR_OUTPUT, + NRF_GPIO_PIN_INPUT_CONNECT, + NRF_GPIO_PIN_NOPULL, + NRF_GPIO_PIN_S0S1, + NRF_GPIO_PIN_NOSENSE); + // - MOSI (optional) - output with initial value 0, + if (p_config->mosi_pin != NRF_DRV_SPI_PIN_NOT_USED) + { + mosi_pin = p_config->mosi_pin; + nrf_gpio_pin_clear(mosi_pin); + nrf_gpio_cfg_output(mosi_pin); + } + else + { + mosi_pin = NRF_SPI_PIN_NOT_CONNECTED; + } + // - MISO (optional) - input, + if (p_config->miso_pin != NRF_DRV_SPI_PIN_NOT_USED) + { + miso_pin = p_config->miso_pin; + nrf_gpio_cfg_input(miso_pin, NRF_GPIO_PIN_NOPULL); + } + else + { + miso_pin = NRF_SPI_PIN_NOT_CONNECTED; + } + // - Slave Select (optional) - output with initial value 1 (inactive). + if (p_config->ss_pin != NRF_DRV_SPI_PIN_NOT_USED) + { + nrf_gpio_pin_set(p_config->ss_pin); + nrf_gpio_cfg_output(p_config->ss_pin); + } + m_cb[p_instance->drv_inst_idx].ss_pin = p_config->ss_pin; + + CODE_FOR_SPIM + ( + NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_registers; + nrf_spim_pins_set(p_spim, p_config->sck_pin, mosi_pin, miso_pin); + nrf_spim_frequency_set(p_spim, + (nrf_spim_frequency_t)p_config->frequency); + nrf_spim_configure(p_spim, + (nrf_spim_mode_t)p_config->mode, + (nrf_spim_bit_order_t)p_config->bit_order); + + nrf_spim_orc_set(p_spim, p_config->orc); + + if (p_cb->handler) + { + nrf_spim_int_enable(p_spim, END_INT_MASK); + } + + nrf_spim_enable(p_spim); + ) + CODE_FOR_SPI + ( + NRF_SPI_Type * p_spi = p_instance->p_registers; + nrf_spi_pins_set(p_spi, p_config->sck_pin, mosi_pin, miso_pin); + nrf_spi_frequency_set(p_spi, + (nrf_spi_frequency_t)p_config->frequency); + nrf_spi_configure(p_spi, + (nrf_spi_mode_t)p_config->mode, + (nrf_spi_bit_order_t)p_config->bit_order); + + m_cb[p_instance->drv_inst_idx].orc = p_config->orc; + + if (p_cb->handler) + { + nrf_spi_int_enable(p_spi, NRF_SPI_INT_READY_MASK); + } + + nrf_spi_enable(p_spi); + ) + + if (p_cb->handler) + { + nrf_drv_common_irq_enable(p_instance->irq, p_config->irq_priority); + } + + p_cb->transfer_in_progress = false; + p_cb->state = NRF_DRV_STATE_INITIALIZED; + + NRF_LOG_INFO("Init\r\n"); + + err_code = NRF_SUCCESS; + NRF_LOG_INFO("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; +} + +void nrf_drv_spi_uninit(nrf_drv_spi_t const * const p_instance) +{ + spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED); + + if (p_cb->handler) + { + nrf_drv_common_irq_disable(p_instance->irq); + } + + #define DISABLE_ALL 0xFFFFFFFF + + CODE_FOR_SPIM + ( + NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_registers; + if (p_cb->handler) + { + nrf_spim_int_disable(p_spim, DISABLE_ALL); + if (p_cb->transfer_in_progress) + { + // Ensure that SPI is not performing any transfer. + nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_STOP); + while (!nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_STOPPED)) {} + p_cb->transfer_in_progress = false; + } + } + nrf_spim_disable(p_spim); + ) + CODE_FOR_SPI + ( + NRF_SPI_Type * p_spi = p_instance->p_registers; + if (p_cb->handler) + { + nrf_spi_int_disable(p_spi, DISABLE_ALL); + } + nrf_spi_disable(p_spi); + ) + #undef DISABLE_ALL + +#if NRF_MODULE_ENABLED(PERIPHERAL_RESOURCE_SHARING) + nrf_drv_common_per_res_release(p_instance->p_registers); +#endif + + p_cb->state = NRF_DRV_STATE_UNINITIALIZED; +} + +ret_code_t nrf_drv_spi_transfer(nrf_drv_spi_t const * const p_instance, + uint8_t const * p_tx_buffer, + uint8_t tx_buffer_length, + uint8_t * p_rx_buffer, + uint8_t rx_buffer_length) +{ + nrf_drv_spi_xfer_desc_t xfer_desc; + xfer_desc.p_tx_buffer = p_tx_buffer; + xfer_desc.p_rx_buffer = p_rx_buffer; + xfer_desc.tx_length = tx_buffer_length; + xfer_desc.rx_length = rx_buffer_length; + + NRF_LOG_INFO("Transfer tx_len:%d, rx_len:%d.\r\n", tx_buffer_length, rx_buffer_length); + NRF_LOG_DEBUG("Tx data:\r\n"); + NRF_LOG_HEXDUMP_DEBUG((uint8_t *)p_tx_buffer, tx_buffer_length * sizeof(p_tx_buffer)); + return nrf_drv_spi_xfer(p_instance, &xfer_desc, 0); +} + +static void finish_transfer(spi_control_block_t * p_cb) +{ + // If Slave Select signal is used, this is the time to deactivate it. + if (p_cb->ss_pin != NRF_DRV_SPI_PIN_NOT_USED) + { + nrf_gpio_pin_set(p_cb->ss_pin); + } + + // By clearing this flag before calling the handler we allow subsequent + // transfers to be started directly from the handler function. + p_cb->transfer_in_progress = false; + p_cb->evt.type = NRF_DRV_SPI_EVENT_DONE; + NRF_LOG_INFO("Transfer rx_len:%d.\r\n", p_cb->evt.data.done.rx_length); + NRF_LOG_DEBUG("Rx data:\r\n"); + NRF_LOG_HEXDUMP_DEBUG((uint8_t *)p_cb->evt.data.done.p_rx_buffer, + p_cb->evt.data.done.rx_length * sizeof(p_cb->evt.data.done.p_rx_buffer)); + p_cb->handler(&p_cb->evt, p_cb->p_context); +} + +#ifdef SPI_IN_USE +// This function is called from IRQ handler or, in blocking mode, directly +// from the 'nrf_drv_spi_transfer' function. +// It returns true as long as the transfer should be continued, otherwise (when +// there is nothing more to send/receive) it returns false. +static bool transfer_byte(NRF_SPI_Type * p_spi, spi_control_block_t * p_cb) +{ + // Read the data byte received in this transfer and store it in RX buffer, + // if needed. + volatile uint8_t rx_data = nrf_spi_rxd_get(p_spi); + if (p_cb->bytes_transferred < p_cb->evt.data.done.rx_length) + { + p_cb->evt.data.done.p_rx_buffer[p_cb->bytes_transferred] = rx_data; + } + + ++p_cb->bytes_transferred; + + // Check if there are more bytes to send or receive and write proper data + // byte (next one from TX buffer or over-run character) to the TXD register + // when needed. + // NOTE - we've already used 'p_cb->bytes_transferred + 1' bytes from our + // buffers, because we take advantage of double buffering of TXD + // register (so in effect one byte is still being transmitted now); + // see how the transfer is started in the 'nrf_drv_spi_transfer' + // function. + uint16_t bytes_used = p_cb->bytes_transferred + 1; + + if (p_cb->abort) + { + if (bytes_used < p_cb->evt.data.done.tx_length) + { + p_cb->evt.data.done.tx_length = bytes_used; + } + if (bytes_used < p_cb->evt.data.done.rx_length) + { + p_cb->evt.data.done.rx_length = bytes_used; + } + } + + if (bytes_used < p_cb->evt.data.done.tx_length) + { + nrf_spi_txd_set(p_spi, p_cb->evt.data.done.p_tx_buffer[bytes_used]); + return true; + } + else if (bytes_used < p_cb->evt.data.done.rx_length) + { + nrf_spi_txd_set(p_spi, p_cb->orc); + return true; + } + + return (p_cb->bytes_transferred < p_cb->evt.data.done.tx_length || + p_cb->bytes_transferred < p_cb->evt.data.done.rx_length); +} + +static void spi_xfer(NRF_SPI_Type * p_spi, + spi_control_block_t * p_cb, + nrf_drv_spi_xfer_desc_t const * p_xfer_desc) +{ + p_cb->bytes_transferred = 0; + nrf_spi_int_disable(p_spi, NRF_SPI_INT_READY_MASK); + + nrf_spi_event_clear(p_spi, NRF_SPI_EVENT_READY); + + // Start the transfer by writing some byte to the TXD register; + // if TX buffer is not empty, take the first byte from this buffer, + // otherwise - use over-run character. + nrf_spi_txd_set(p_spi, + (p_xfer_desc->tx_length > 0 ? p_xfer_desc->p_tx_buffer[0] : p_cb->orc)); + + // TXD register is double buffered, so next byte to be transmitted can + // be written immediately, if needed, i.e. if TX or RX transfer is to + // be more that 1 byte long. Again - if there is something more in TX + // buffer send it, otherwise use over-run character. + if (p_xfer_desc->tx_length > 1) + { + nrf_spi_txd_set(p_spi, p_xfer_desc->p_tx_buffer[1]); + } + else if (p_xfer_desc->rx_length > 1) + { + nrf_spi_txd_set(p_spi, p_cb->orc); + } + + // For blocking mode (user handler not provided) wait here for READY + // events (indicating that the byte from TXD register was transmitted + // and a new incoming byte was moved to the RXD register) and continue + // transaction until all requested bytes are transferred. + // In non-blocking mode - IRQ service routine will do this stuff. + if (p_cb->handler) + { + nrf_spi_int_enable(p_spi, NRF_SPI_INT_READY_MASK); + } + else + { + do { + while (!nrf_spi_event_check(p_spi, NRF_SPI_EVENT_READY)) {} + nrf_spi_event_clear(p_spi, NRF_SPI_EVENT_READY); + NRF_LOG_DEBUG("SPI: Event: NRF_SPI_EVENT_READY.\r\n"); + } while (transfer_byte(p_spi, p_cb)); + if (p_cb->ss_pin != NRF_DRV_SPI_PIN_NOT_USED) + { + nrf_gpio_pin_set(p_cb->ss_pin); + } + } +} +#endif // SPI_IN_USE + +#ifdef SPIM_IN_USE +__STATIC_INLINE void spim_int_enable(NRF_SPIM_Type * p_spim, bool enable) +{ + if (!enable) + { + nrf_spim_int_disable(p_spim, END_INT_MASK); + } + else + { + nrf_spim_int_enable(p_spim, END_INT_MASK); + } +} + +__STATIC_INLINE void spim_list_enable_handle(NRF_SPIM_Type * p_spim, uint32_t flags) +{ + if (NRF_DRV_SPI_FLAG_TX_POSTINC & flags) + { + nrf_spim_tx_list_enable(p_spim); + } + else + { + nrf_spim_tx_list_disable(p_spim); + } + + if (NRF_DRV_SPI_FLAG_RX_POSTINC & flags) + { + nrf_spim_rx_list_enable(p_spim); + } + else + { + nrf_spim_rx_list_disable(p_spim); + } +} + +static ret_code_t spim_xfer(NRF_SPIM_Type * p_spim, + spi_control_block_t * p_cb, + nrf_drv_spi_xfer_desc_t const * p_xfer_desc, + uint32_t flags) +{ + ret_code_t err_code; + // EasyDMA requires that transfer buffers are placed in Data RAM region; + // signal error if they are not. + if ((p_xfer_desc->p_tx_buffer != NULL && !nrf_drv_is_in_RAM(p_xfer_desc->p_tx_buffer)) || + (p_xfer_desc->p_rx_buffer != NULL && !nrf_drv_is_in_RAM(p_xfer_desc->p_rx_buffer))) + { + p_cb->transfer_in_progress = false; + err_code = NRF_ERROR_INVALID_ADDR; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + +#if NRF_MODULE_ENABLED(SPIM_NRF52_ANOMALY_109_WORKAROUND) + p_cb->tx_length = 0; + p_cb->rx_length = 0; +#endif + + nrf_spim_tx_buffer_set(p_spim, p_xfer_desc->p_tx_buffer, p_xfer_desc->tx_length); + nrf_spim_rx_buffer_set(p_spim, p_xfer_desc->p_rx_buffer, p_xfer_desc->rx_length); + + nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END); + + spim_list_enable_handle(p_spim, flags); + + if (!(flags & NRF_DRV_SPI_FLAG_HOLD_XFER)) + { + nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_START); + } +#if NRF_MODULE_ENABLED(SPIM_NRF52_ANOMALY_109_WORKAROUND) + if (flags & NRF_DRV_SPI_FLAG_HOLD_XFER) + { + nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_STARTED); + p_cb->tx_length = p_xfer_desc->tx_length; + p_cb->rx_length = p_xfer_desc->rx_length; + nrf_spim_tx_buffer_set(p_spim, p_xfer_desc->p_tx_buffer, 0); + nrf_spim_rx_buffer_set(p_spim, p_xfer_desc->p_rx_buffer, 0); + nrf_spim_int_enable(p_spim, NRF_SPIM_INT_STARTED_MASK); + } +#endif + + if (!p_cb->handler) + { + while (!nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_END)){} + if (p_cb->ss_pin != NRF_DRV_SPI_PIN_NOT_USED) + { + nrf_gpio_pin_set(p_cb->ss_pin); + } + } + else + { + spim_int_enable(p_spim, !(flags & NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER)); + } + err_code = NRF_SUCCESS; + NRF_LOG_INFO("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; +} +#endif + +ret_code_t nrf_drv_spi_xfer(nrf_drv_spi_t const * const p_instance, + nrf_drv_spi_xfer_desc_t const * p_xfer_desc, + uint32_t flags) +{ + spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED); + ASSERT(p_xfer_desc->p_tx_buffer != NULL || p_xfer_desc->tx_length == 0); + ASSERT(p_xfer_desc->p_rx_buffer != NULL || p_xfer_desc->rx_length == 0); + + ret_code_t err_code = NRF_SUCCESS; + + if (p_cb->transfer_in_progress) + { + err_code = NRF_ERROR_BUSY; + NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + } + else + { + if (p_cb->handler && !(flags & (NRF_DRV_SPI_FLAG_REPEATED_XFER | + NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER))) + { + p_cb->transfer_in_progress = true; + } + } + + p_cb->evt.data.done = *p_xfer_desc; + p_cb->tx_done = false; + p_cb->rx_done = false; + p_cb->abort = false; + + if (p_cb->ss_pin != NRF_DRV_SPI_PIN_NOT_USED) + { + nrf_gpio_pin_clear(p_cb->ss_pin); + } + CODE_FOR_SPIM + ( + return spim_xfer(p_instance->p_registers, p_cb, p_xfer_desc, flags); + ) + CODE_FOR_SPI + ( + if (flags) + { + p_cb->transfer_in_progress = false; + err_code = NRF_ERROR_NOT_SUPPORTED; + } + else + { + spi_xfer(p_instance->p_registers, p_cb, p_xfer_desc); + } + NRF_LOG_INFO("Function: %s, error code: %s.\r\n", + (uint32_t)__func__, + (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code)); + return err_code; + ) +} + + +void nrf_drv_spi_abort(nrf_drv_spi_t const * p_instance) +{ + spi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx]; + ASSERT(p_cb->state != NRF_DRV_STATE_UNINITIALIZED); + + CODE_FOR_SPIM + ( + nrf_spim_task_trigger(p_instance->p_registers, NRF_SPIM_TASK_STOP); + while (!nrf_spim_event_check(p_instance->p_registers, NRF_SPIM_EVENT_STOPPED)) {} + p_cb->transfer_in_progress = false; + ) + CODE_FOR_SPI + ( + p_cb->abort = true; + ) +} + + +#ifdef SPIM_IN_USE +static void irq_handler_spim(NRF_SPIM_Type * p_spim, spi_control_block_t * p_cb) +{ + +#if NRF_MODULE_ENABLED(SPIM_NRF52_ANOMALY_109_WORKAROUND) + if ((nrf_spim_int_enable_check(p_spim, NRF_SPIM_INT_STARTED_MASK)) && + (nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_STARTED)) ) + { + /* Handle first, zero-length, auxiliary transmission. */ + nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_STARTED); + nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END); + + ASSERT(p_spim->TXD.MAXCNT == 0); + p_spim->TXD.MAXCNT = p_cb->tx_length; + + ASSERT(p_spim->RXD.MAXCNT == 0); + p_spim->RXD.MAXCNT = p_cb->rx_length; + + /* Disable STARTED interrupt, used only in auxiliary transmission. */ + nrf_spim_int_disable(p_spim, NRF_SPIM_INT_STARTED_MASK); + + /* Start the actual, glitch-free transmission. */ + nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_START); + return; + } +#endif + + if (nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_END)) + { + nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END); + ASSERT(p_cb->handler); + NRF_LOG_DEBUG("SPIM: Event: NRF_SPIM_EVENT_END.\r\n"); + finish_transfer(p_cb); + } +} + +uint32_t nrf_drv_spi_start_task_get(nrf_drv_spi_t const * p_instance) +{ + NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_registers; + return nrf_spim_task_address_get(p_spim, NRF_SPIM_TASK_START); +} + +uint32_t nrf_drv_spi_end_event_get(nrf_drv_spi_t const * p_instance) +{ + NRF_SPIM_Type * p_spim = (NRF_SPIM_Type *)p_instance->p_registers; + return nrf_spim_event_address_get(p_spim, NRF_SPIM_EVENT_END); +} +#endif // SPIM_IN_USE + +#ifdef SPI_IN_USE +static void irq_handler_spi(NRF_SPI_Type * p_spi, spi_control_block_t * p_cb) +{ + ASSERT(p_cb->handler); + + nrf_spi_event_clear(p_spi, NRF_SPI_EVENT_READY); + NRF_LOG_DEBUG("SPI: Event: NRF_SPI_EVENT_READY.\r\n"); + + if (!transfer_byte(p_spi, p_cb)) + { + finish_transfer(p_cb); + } +} +#endif // SPI_IN_USE + +#if NRF_MODULE_ENABLED(SPI0) +IRQ_HANDLER(0) +{ + spi_control_block_t * p_cb = &m_cb[SPI0_INSTANCE_INDEX]; + #if SPI0_USE_EASY_DMA + irq_handler_spim(NRF_SPIM0, p_cb); + #else + irq_handler_spi(NRF_SPI0, p_cb); + #endif +} +#endif // NRF_MODULE_ENABLED(SPI0) + +#if NRF_MODULE_ENABLED(SPI1) +IRQ_HANDLER(1) +{ + spi_control_block_t * p_cb = &m_cb[SPI1_INSTANCE_INDEX]; + #if SPI1_USE_EASY_DMA + irq_handler_spim(NRF_SPIM1, p_cb); + #else + irq_handler_spi(NRF_SPI1, p_cb); + #endif +} +#endif // NRF_MODULE_ENABLED(SPI1) + +#if NRF_MODULE_ENABLED(SPI2) +IRQ_HANDLER(2) +{ + spi_control_block_t * p_cb = &m_cb[SPI2_INSTANCE_INDEX]; + #if SPI2_USE_EASY_DMA + irq_handler_spim(NRF_SPIM2, p_cb); + #else + irq_handler_spi(NRF_SPI2, p_cb); + #endif +} +#endif // NRF_MODULE_ENABLED(SPI2) +#endif // ENABLED_SPI_COUNT +#endif // NRF_MODULE_ENABLED(SPI) diff --git a/drivers_nrf/spi_master/nrf_drv_spi.h b/drivers_nrf/spi_master/nrf_drv_spi.h new file mode 100644 index 0000000..c6d418c --- /dev/null +++ b/drivers_nrf/spi_master/nrf_drv_spi.h @@ -0,0 +1,413 @@ +/** + * Copyright (c) 2015 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file + * @addtogroup nrf_spi Serial peripheral interface (SPI/SPIM) + * @ingroup nrf_drivers + * @brief Serial peripheral interface (SPI/SPIM) APIs. + * + */ + +#ifndef NRF_DRV_SPI_H__ +#define NRF_DRV_SPI_H__ + +#include "nordic_common.h" +#include "sdk_config.h" +#include "nrf_peripherals.h" +#include "nrf_spi.h" +#ifdef SPIM_PRESENT +#include "nrf_spim.h" +#endif +#include "sdk_errors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(SPIM_PRESENT) + #define NRF_DRV_SPI_PERIPHERAL(id) \ + (CONCAT_3(SPI, id, _USE_EASY_DMA) == 1 ? \ + (void *)CONCAT_2(NRF_SPIM, id) \ + : (void *)CONCAT_2(NRF_SPI, id)) + #define SPI2_IRQ SPIM2_SPIS2_SPI2_IRQn + #define SPI2_IRQ_HANDLER SPIM2_SPIS2_SPI2_IRQHandler +#else + #define NRF_DRV_SPI_PERIPHERAL(id) (void *)CONCAT_2(NRF_SPI, id) +#endif +#define SPI0_IRQ SPI0_TWI0_IRQn +#define SPI0_IRQ_HANDLER SPI0_TWI0_IRQHandler +#define SPI1_IRQ SPI1_TWI1_IRQn +#define SPI1_IRQ_HANDLER SPI1_TWI1_IRQHandler + +/** + * @defgroup nrf_drv_spi SPI master driver + * @{ + * @ingroup nrf_spi + * + * @brief Multi-instance SPI master driver. + */ + +/** + * @brief SPI master driver instance data structure. + */ +typedef struct +{ + void * p_registers; ///< Pointer to the structure with SPI/SPIM peripheral instance registers. + IRQn_Type irq; ///< SPI/SPIM peripheral instance IRQ number. + uint8_t drv_inst_idx; ///< Driver instance index. + bool use_easy_dma; ///< True if the peripheral with EasyDMA (SPIM) shall be used. +} nrf_drv_spi_t; + +#define SPI0_INSTANCE_INDEX 0 +#define SPI1_INSTANCE_INDEX SPI0_INSTANCE_INDEX+SPI0_ENABLED +#define SPI2_INSTANCE_INDEX SPI1_INSTANCE_INDEX+SPI1_ENABLED + +/** + * @brief Macro for creating an SPI master driver instance. + */ +#define NRF_DRV_SPI_INSTANCE(id) \ +{ \ + .p_registers = NRF_DRV_SPI_PERIPHERAL(id), \ + .irq = CONCAT_3(SPI, id, _IRQ), \ + .drv_inst_idx = CONCAT_3(SPI, id, _INSTANCE_INDEX), \ + .use_easy_dma = CONCAT_3(SPI, id, _USE_EASY_DMA) \ +} + +/** + * @brief This value can be provided instead of a pin number for signals MOSI, + * MISO, and Slave Select to specify that the given signal is not used and + * therefore does not need to be connected to a pin. + */ +#define NRF_DRV_SPI_PIN_NOT_USED 0xFF + +/** + * @brief SPI data rates. + */ +typedef enum +{ + NRF_DRV_SPI_FREQ_125K = NRF_SPI_FREQ_125K, ///< 125 kbps. + NRF_DRV_SPI_FREQ_250K = NRF_SPI_FREQ_250K, ///< 250 kbps. + NRF_DRV_SPI_FREQ_500K = NRF_SPI_FREQ_500K, ///< 500 kbps. + NRF_DRV_SPI_FREQ_1M = NRF_SPI_FREQ_1M, ///< 1 Mbps. + NRF_DRV_SPI_FREQ_2M = NRF_SPI_FREQ_2M, ///< 2 Mbps. + NRF_DRV_SPI_FREQ_4M = NRF_SPI_FREQ_4M, ///< 4 Mbps. + NRF_DRV_SPI_FREQ_8M = NRF_SPI_FREQ_8M ///< 8 Mbps. +} nrf_drv_spi_frequency_t; + +/** + * @brief SPI modes. + */ +typedef enum +{ + NRF_DRV_SPI_MODE_0 = NRF_SPI_MODE_0, ///< SCK active high, sample on leading edge of clock. + NRF_DRV_SPI_MODE_1 = NRF_SPI_MODE_1, ///< SCK active high, sample on trailing edge of clock. + NRF_DRV_SPI_MODE_2 = NRF_SPI_MODE_2, ///< SCK active low, sample on leading edge of clock. + NRF_DRV_SPI_MODE_3 = NRF_SPI_MODE_3 ///< SCK active low, sample on trailing edge of clock. +} nrf_drv_spi_mode_t; + +/** + * @brief SPI bit orders. + */ +typedef enum +{ + NRF_DRV_SPI_BIT_ORDER_MSB_FIRST = NRF_SPI_BIT_ORDER_MSB_FIRST, ///< Most significant bit shifted out first. + NRF_DRV_SPI_BIT_ORDER_LSB_FIRST = NRF_SPI_BIT_ORDER_LSB_FIRST ///< Least significant bit shifted out first. +} nrf_drv_spi_bit_order_t; + +/** + * @brief SPI master driver instance configuration structure. + */ +typedef struct +{ + uint8_t sck_pin; ///< SCK pin number. + uint8_t mosi_pin; ///< MOSI pin number (optional). + /**< Set to @ref NRF_DRV_SPI_PIN_NOT_USED + * if this signal is not needed. */ + uint8_t miso_pin; ///< MISO pin number (optional). + /**< Set to @ref NRF_DRV_SPI_PIN_NOT_USED + * if this signal is not needed. */ + uint8_t ss_pin; ///< Slave Select pin number (optional). + /**< Set to @ref NRF_DRV_SPI_PIN_NOT_USED + * if this signal is not needed. The driver + * supports only active low for this signal. + * If the signal should be active high, + * it must be controlled externally. */ + uint8_t irq_priority; ///< Interrupt priority. + uint8_t orc; ///< Over-run character. + /**< This character is used when all bytes from the TX buffer are sent, + but the transfer continues due to RX. */ + nrf_drv_spi_frequency_t frequency; ///< SPI frequency. + nrf_drv_spi_mode_t mode; ///< SPI mode. + nrf_drv_spi_bit_order_t bit_order; ///< SPI bit order. +} nrf_drv_spi_config_t; + +/** + * @brief SPI master instance default configuration. + */ +#define NRF_DRV_SPI_DEFAULT_CONFIG \ +{ \ + .sck_pin = NRF_DRV_SPI_PIN_NOT_USED, \ + .mosi_pin = NRF_DRV_SPI_PIN_NOT_USED, \ + .miso_pin = NRF_DRV_SPI_PIN_NOT_USED, \ + .ss_pin = NRF_DRV_SPI_PIN_NOT_USED, \ + .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY, \ + .orc = 0xFF, \ + .frequency = NRF_DRV_SPI_FREQ_4M, \ + .mode = NRF_DRV_SPI_MODE_0, \ + .bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST, \ +} + +#define NRF_DRV_SPI_FLAG_TX_POSTINC (1UL << 0) /**< TX buffer address incremented after transfer. */ +#define NRF_DRV_SPI_FLAG_RX_POSTINC (1UL << 1) /**< RX buffer address incremented after transfer. */ +#define NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER (1UL << 2) /**< Interrupt after each transfer is suppressed, and the event handler is not called. */ +#define NRF_DRV_SPI_FLAG_HOLD_XFER (1UL << 3) /**< Set up the transfer but do not start it. */ +#define NRF_DRV_SPI_FLAG_REPEATED_XFER (1UL << 4) /**< Flag indicating that the transfer will be executed multiple times. */ + +/** + * @brief Single transfer descriptor structure. + */ +typedef struct +{ + uint8_t const * p_tx_buffer; ///< Pointer to TX buffer. + uint8_t tx_length; ///< TX buffer length. + uint8_t * p_rx_buffer; ///< Pointer to RX buffer. + uint8_t rx_length; ///< RX buffer length. +}nrf_drv_spi_xfer_desc_t; + +/** + * @brief Macro for setting up single transfer descriptor. + * + * This macro is for internal use only. + */ +#define NRF_DRV_SPI_SINGLE_XFER(p_tx, tx_len, p_rx, rx_len) \ + { \ + .p_tx_buffer = (uint8_t const *)(p_tx), \ + .tx_length = (tx_len), \ + .p_rx_buffer = (p_rx), \ + .rx_length = (rx_len), \ + } + +/** + * @brief Macro for setting duplex TX RX transfer. + */ +#define NRF_DRV_SPI_XFER_TRX(p_tx_buf, tx_length, p_rx_buf, rx_length) \ + NRF_DRV_SPI_SINGLE_XFER(p_tx_buf, tx_length, p_rx_buf, rx_length) + +/** + * @brief Macro for setting TX transfer. + */ +#define NRF_DRV_SPI_XFER_TX(p_buf, length) \ + NRF_DRV_SPI_SINGLE_XFER(p_buf, length, NULL, 0) + +/** + * @brief Macro for setting RX transfer. + */ +#define NRF_DRV_SPI_XFER_RX(p_buf, length) \ + NRF_DRV_SPI_SINGLE_XFER(NULL, 0, p_buf, length) + +/** + * @brief SPI master driver event types, passed to the handler routine provided + * during initialization. + */ +typedef enum +{ + NRF_DRV_SPI_EVENT_DONE, ///< Transfer done. +} nrf_drv_spi_evt_type_t; + +typedef struct +{ + nrf_drv_spi_evt_type_t type; ///< Event type. + union + { + nrf_drv_spi_xfer_desc_t done; ///< Event data for DONE event. + } data; +} nrf_drv_spi_evt_t; + +/** + * @brief SPI master driver event handler type. + */ +typedef void (* nrf_drv_spi_evt_handler_t)(nrf_drv_spi_evt_t const * p_event, + void * p_context); + +/** + * @brief Function for initializing the SPI master driver instance. + * + * This function configures and enables the specified peripheral. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] p_config Pointer to the structure with the initial configuration. + * If NULL, the default configuration is used. + * @param handler Event handler provided by the user. If NULL, transfers + * will be performed in blocking mode. + * @param p_context Context passed to event handler. + * + * @retval NRF_SUCCESS If initialization was successful. + * @retval NRF_ERROR_INVALID_STATE If the driver was already initialized. + * @retval NRF_ERROR_BUSY If some other peripheral with the same + * instance ID is already in use. This is + * possible only if PERIPHERAL_RESOURCE_SHARING_ENABLED + * is set to a value other than zero. + */ +ret_code_t nrf_drv_spi_init(nrf_drv_spi_t const * const p_instance, + nrf_drv_spi_config_t const * p_config, + nrf_drv_spi_evt_handler_t handler, + void * p_context); + +/** + * @brief Function for uninitializing the SPI master driver instance. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +void nrf_drv_spi_uninit(nrf_drv_spi_t const * const p_instance); + +/** + * @brief Function for starting the SPI data transfer. + * + * If an event handler was provided in the @ref nrf_drv_spi_init call, this function + * returns immediately and the handler is called when the transfer is done. + * Otherwise, the transfer is performed in blocking mode, which means that this function + * returns when the transfer is finished. + * + * @note Peripherals using EasyDMA (for example, SPIM) require the transfer buffers + * to be placed in the Data RAM region. If they are not and an SPIM instance is + * used, this function will fail with the error code NRF_ERROR_INVALID_ADDR. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] p_tx_buffer Pointer to the transmit buffer. Can be NULL + * if there is nothing to send. + * @param tx_buffer_length Length of the transmit buffer. + * @param[in] p_rx_buffer Pointer to the receive buffer. Can be NULL + * if there is nothing to receive. + * @param rx_buffer_length Length of the receive buffer. + * + * @retval NRF_SUCCESS If the operation was successful. + * @retval NRF_ERROR_BUSY If a previously started transfer has not finished + * yet. + * @retval NRF_ERROR_INVALID_ADDR If the provided buffers are not placed in the Data + * RAM region. + */ +ret_code_t nrf_drv_spi_transfer(nrf_drv_spi_t const * const p_instance, + uint8_t const * p_tx_buffer, + uint8_t tx_buffer_length, + uint8_t * p_rx_buffer, + uint8_t rx_buffer_length); + +/** + * @brief Function for starting the SPI data transfer with additional option flags. + * + * Function enables customizing the transfer by using option flags. + * + * Additional options are provided using the flags parameter: + * + * - @ref NRF_DRV_SPI_FLAG_TX_POSTINC and @ref NRF_DRV_SPI_FLAG_RX_POSTINC: + * Post-incrementation of buffer addresses. Supported only by SPIM. + * - @ref NRF_DRV_SPI_FLAG_HOLD_XFER: Driver is not starting the transfer. Use this + * flag if the transfer is triggered externally by PPI. Supported only by SPIM. Use + * @ref nrf_drv_spi_start_task_get to get the address of the start task. + * - @ref NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER: No user event handler after transfer + * completion. This also means no interrupt at the end of the transfer. Supported only by SPIM. + * If @ref NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER is used, the driver does not set the instance into + * busy state, so you must ensure that the next transfers are set up when SPIM is not active. + * @ref nrf_drv_spi_end_event_get function can be used to detect end of transfer. Option can be used + * together with @ref NRF_DRV_SPI_FLAG_REPEATED_XFER to prepare a sequence of SPI transfers + * without interruptions. + * - @ref NRF_DRV_SPI_FLAG_REPEATED_XFER: Prepare for repeated transfers. You can set + * up a number of transfers that will be triggered externally (for example by PPI). An example is + * a TXRX transfer with the options @ref NRF_DRV_SPI_FLAG_RX_POSTINC, + * @ref NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER, and @ref NRF_DRV_SPI_FLAG_REPEATED_XFER. After the + * transfer is set up, a set of transfers can be triggered by PPI that will read, for example, + * the same register of an external component and put it into a RAM buffer without any interrupts. + * @ref nrf_drv_spi_end_event_get can be used to get the address of the END event, which can be + * used to count the number of transfers. If @ref NRF_DRV_SPI_FLAG_REPEATED_XFER is used, + * the driver does not set the instance into busy state, so you must ensure that the next + * transfers are set up when SPIM is not active. Supported only by SPIM. + * @note Function is intended to be used only in non-blocking mode. + * + * @param p_instance Pointer to the driver instance structure. + * @param p_xfer_desc Pointer to the transfer descriptor. + * @param flags Transfer options (0 for default settings). + * + * @retval NRF_SUCCESS If the procedure was successful. + * @retval NRF_ERROR_BUSY If the driver is not ready for a new transfer. + * @retval NRF_ERROR_NOT_SUPPORTED If the provided parameters are not supported. + * @retval NRF_ERROR_INVALID_ADDR If the provided buffers are not placed in the Data + * RAM region. + */ +ret_code_t nrf_drv_spi_xfer(nrf_drv_spi_t const * const p_instance, + nrf_drv_spi_xfer_desc_t const * p_xfer_desc, + uint32_t flags); + +/** + * @brief Function for returning the address of a SPIM start task. + * + * This function should be used if @ref nrf_drv_spi_xfer was called with the flag @ref NRF_DRV_SPI_FLAG_HOLD_XFER. + * In that case, the transfer is not started by the driver, but it must be started externally by PPI. + * + * @param[in] p_instance Pointer to the driver instance structure. + * + * @return Start task address. + */ +uint32_t nrf_drv_spi_start_task_get(nrf_drv_spi_t const * p_instance); + +/** + * @brief Function for returning the address of a END SPIM event. + * + * A END event can be used to detect the end of a transfer if the @ref NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER + * option is used. + * + * @param[in] p_instance Pointer to the driver instance structure. + * + * @return END event address. + */ +uint32_t nrf_drv_spi_end_event_get(nrf_drv_spi_t const * p_instance); + +/** + * @brief Function for aborting ongoing transfer. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +void nrf_drv_spi_abort(nrf_drv_spi_t const * p_instance); + +#ifdef __cplusplus +} +#endif + +#endif // NRF_DRV_SPI_H__ + +/** @} */ diff --git a/drivers_nrf/spi_master/spi_5W_master.c b/drivers_nrf/spi_master/spi_5W_master.c new file mode 100644 index 0000000..c8ac416 --- /dev/null +++ b/drivers_nrf/spi_master/spi_5W_master.c @@ -0,0 +1,629 @@ +/** + * Copyright (c) 2014 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/**@file + * + * @defgroup ser_phy_spi_5W_hw_driver_master spi_5W_master.c + * @{ + * @ingroup ser_phy_spi_5W_hw_driver_master + * + * @brief SPI_5W_RAW hardware driver. + */ + +#include "app_error.h" +#include "app_util_platform.h" +#include "nrf_gpio.h" +#include "nrf.h" +#include "spi_5W_master.h" +#include "ser_config_5W_app.h" +#include "ser_phy_debug_app.h" +#include "sdk_common.h" + + +#define _static + +#define DOUBLE_BUFFERED /**< A flag for enabling double buffering. */ + +#define SPI_PIN_DISCONNECTED 0xFFFFFFFF /**< A value used to the PIN deinitialization. */ +#define SPI_DEFAULT_TX_BYTE 0x00 /**< Default byte (used to clock transmission + from slave to the master) */ + +typedef struct +{ + NRF_SPI_Type * p_nrf_spi; /**< A pointer to the NRF SPI master */ + IRQn_Type irq_type; /**< A type of NVIC IRQn */ + + uint8_t * p_tx_buffer; /**< A pointer to TX buffer. */ + uint16_t tx_length; /**< A length of TX buffer. */ + uint16_t tx_index; /**< A index of the current element in the TX buffer. */ + + uint8_t * p_rx_buffer; /**< A pointer to RX buffer. */ + uint16_t rx_length; /**< A length RX buffer. */ + uint16_t rx_index; /**< A index of the current element in the RX buffer. */ + + uint16_t max_length; /**< Max length (Max of the TX and RX length). */ + uint16_t bytes_count; + uint8_t pin_slave_select; /**< A pin for Slave Select. */ + + spi_master_event_handler_t callback_event_handler; /**< A handler for event callback function. */ + spi_master_state_t state; /**< A state of an instance of SPI master. */ + bool start_flag; + bool abort_flag; + +} spi_master_instance_t; + +#ifdef _SPI_5W_ +typedef enum +{ + HOOK_STATE_DISABLED, + HOOK_STATE_IDLE, + HOOK_STATE_GUARDED, + HOOK_STATE_ABORTED, + HOOK_STATE_RESTARTED, + HOOK_STATE_PASSING +} spi_hook_state_t; + + +_static spi_master_event_handler_t m_ser_phy_event_handler; +_static spi_master_hw_instance_t m_spi_master_hw_instance; +_static spi_hook_state_t m_hook_state = HOOK_STATE_DISABLED; +#endif + +#ifdef SER_PHY_DEBUG_APP_ENABLE +_static spi_master_raw_callback_t m_debug_callback; +#endif + +_static spi_master_instance_t m_spi_master_instances[SPI_MASTER_HW_ENABLED_COUNT]; + +static __INLINE spi_master_instance_t * spi_master_get_instance( + const spi_master_hw_instance_t spi_master_hw_instance); +static __INLINE void spi_master_send_recv_irq(spi_master_instance_t * const p_spi_instance); +static __INLINE void spi_master_signal_evt(spi_master_instance_t * const p_spi_instance, + spi_master_evt_type_t event_type, + const uint16_t data); + +#ifdef SPI_MASTER_0_ENABLE +/** + * @brief SPI0 interrupt handler. + */ +void SPI0_TWI0_IRQHandler(void) +{ + if (NRF_SPI0->EVENTS_READY != 0) + { + NRF_SPI0->EVENTS_READY = 0; + + spi_master_instance_t * p_spi_instance = spi_master_get_instance(SPI_MASTER_0); + + spi_master_send_recv_irq(p_spi_instance); + } +} +#endif //SPI_MASTER_0_ENABLE + +#ifdef SPI_MASTER_1_ENABLE +/** + * @brief SPI0 interrupt handler. + */ +void SPI1_TWI1_IRQHandler(void) +{ + if (NRF_SPI1->EVENTS_READY != 0) + { + NRF_SPI1->EVENTS_READY = 0; + + spi_master_instance_t * p_spi_instance = spi_master_get_instance(SPI_MASTER_1); + + spi_master_send_recv_irq(p_spi_instance); + } +} +#endif //SPI_MASTER_1_ENABLE + +#if defined(SPI_MASTER_0_ENABLE) || defined(SPI_MASTER_1_ENABLE) + +/**@brief Function for getting an instance of SPI master. */ +static __INLINE spi_master_instance_t * spi_master_get_instance( + const spi_master_hw_instance_t spi_master_hw_instance) +{ + return &(m_spi_master_instances[(uint8_t)spi_master_hw_instance]); +} + +/** @brief Function for initializing instance of SPI master by default values. */ +static __INLINE void spi_master_init_hw_instance(NRF_SPI_Type * p_nrf_spi, + IRQn_Type irq_type, + spi_master_instance_t * p_spi_instance) +{ + APP_ERROR_CHECK_BOOL(p_spi_instance != NULL); + + p_spi_instance->p_nrf_spi = p_nrf_spi; + p_spi_instance->irq_type = irq_type; + + p_spi_instance->p_tx_buffer = NULL; + p_spi_instance->tx_length = 0; + p_spi_instance->tx_index = 0; + + p_spi_instance->p_rx_buffer = NULL; + p_spi_instance->rx_length = 0; + p_spi_instance->rx_index = 0; + + p_spi_instance->bytes_count = 0; + p_spi_instance->max_length = 0; + p_spi_instance->pin_slave_select = 0; + + p_spi_instance->callback_event_handler = NULL; + + p_spi_instance->state = SPI_MASTER_STATE_DISABLED; + p_spi_instance->abort_flag = false; + p_spi_instance->start_flag = false; +} + +/**@brief Function for initializing TX or RX buffer. */ +static __INLINE void spi_master_buffer_init(uint8_t * const p_buf, + const uint16_t buf_len, + uint8_t * * pp_buf, + uint16_t * const p_buf_len, + uint16_t * const p_index) +{ + APP_ERROR_CHECK_BOOL(pp_buf != NULL); + APP_ERROR_CHECK_BOOL(p_buf_len != NULL); + APP_ERROR_CHECK_BOOL(p_index != NULL); + + *pp_buf = p_buf; + *p_buf_len = (p_buf != NULL) ? buf_len : 0; + *p_index = 0; +} + +/**@brief Function for releasing TX or RX buffer. */ +static __INLINE void spi_master_buffer_release(uint8_t * * const pp_buf, uint16_t * const p_buf_len) +{ + APP_ERROR_CHECK_BOOL(pp_buf != NULL); + APP_ERROR_CHECK_BOOL(p_buf_len != NULL); + + *pp_buf = NULL; + *p_buf_len = 0; +} + +/**@brief Function for sending events by callback. */ +static __INLINE void spi_master_signal_evt(spi_master_instance_t * const p_spi_instance, + spi_master_evt_type_t event_type, + const uint16_t data) +{ + APP_ERROR_CHECK_BOOL(p_spi_instance != NULL); + + if (p_spi_instance->callback_event_handler != NULL) + { + spi_master_evt_t event = {SPI_MASTER_EVT_TYPE_MAX, 0}; + event.type = event_type; + event.data = data; + p_spi_instance->callback_event_handler(event); + } +} + +/**@brief Function insert to a TX buffer another byte or two bytes (depends on flag @ref DOUBLE_BUFFERED). */ +static __INLINE void spi_master_send_initial_bytes(spi_master_instance_t * const p_spi_instance) +{ + APP_ERROR_CHECK_BOOL(p_spi_instance != NULL); + + p_spi_instance->p_nrf_spi->TXD = ((p_spi_instance->p_tx_buffer != NULL) && + (p_spi_instance->tx_index < p_spi_instance->tx_length)) ? + p_spi_instance->p_tx_buffer[p_spi_instance->tx_index] : + SPI_DEFAULT_TX_BYTE; + (p_spi_instance->tx_index)++; + + #ifdef DOUBLE_BUFFERED + + if (p_spi_instance->tx_index < p_spi_instance->max_length) + { + p_spi_instance->p_nrf_spi->TXD = ((p_spi_instance->p_tx_buffer != NULL) && + (p_spi_instance->tx_index < p_spi_instance->tx_length)) ? + p_spi_instance->p_tx_buffer[p_spi_instance->tx_index] : + SPI_DEFAULT_TX_BYTE; + (p_spi_instance->tx_index)++; + } + #endif +} + +/**@brief Function for receiving and sending data from IRQ. (The same for both IRQs). */ +static __INLINE void spi_master_send_recv_irq(spi_master_instance_t * const p_spi_instance) +{ + + uint8_t rx_byte; + + APP_ERROR_CHECK_BOOL(p_spi_instance != NULL); + APP_ERROR_CHECK_BOOL(p_spi_instance->state == SPI_MASTER_STATE_BUSY); + + p_spi_instance->bytes_count++; + rx_byte = p_spi_instance->p_nrf_spi->RXD; + + if (p_spi_instance->start_flag) + { + p_spi_instance->start_flag = false; + spi_master_signal_evt(p_spi_instance, SPI_MASTER_EVT_FIRST_BYTE_RECEIVED, (uint16_t)rx_byte); + } + else if (p_spi_instance->abort_flag ) //this is tricky, but callback for SPI_MASTER_EVT_FIRST_BYTE_RECEIVED will set this flag for a first byte, which is bad because there is still byte in a buffer + { //and for a single byte transaction you will get XFERDONE event to restart + p_spi_instance->abort_flag = false; + p_spi_instance->state = SPI_MASTER_STATE_ABORTED; + nrf_gpio_pin_set(p_spi_instance->pin_slave_select); + spi_master_signal_evt(p_spi_instance, SPI_MASTER_EVT_TRANSFER_ABORTED, 0); + return; + } + + if ((p_spi_instance->p_rx_buffer != NULL) && + (p_spi_instance->rx_index < p_spi_instance->rx_length)) + { + p_spi_instance->p_rx_buffer[p_spi_instance->rx_index++] = rx_byte; + } + + if ((p_spi_instance->tx_index < p_spi_instance->max_length) && (!(p_spi_instance->abort_flag))) //do not TX if you know that there is an abort to be done - this should work for a DOUBLE BUFFERING ??? + { + p_spi_instance->p_nrf_spi->TXD = ((p_spi_instance->p_tx_buffer != NULL) && + (p_spi_instance->tx_index < p_spi_instance->tx_length)) ? + p_spi_instance->p_tx_buffer[p_spi_instance->tx_index] : + SPI_DEFAULT_TX_BYTE; + (p_spi_instance->tx_index)++; + } + + if (p_spi_instance->bytes_count >= p_spi_instance->max_length) + { + APP_ERROR_CHECK_BOOL(p_spi_instance->bytes_count == p_spi_instance->max_length); + nrf_gpio_pin_set(p_spi_instance->pin_slave_select); + p_spi_instance->state = SPI_MASTER_STATE_IDLE; + spi_master_signal_evt(p_spi_instance, + SPI_MASTER_EVT_TRANSFER_COMPLETED, + p_spi_instance->tx_index); + } + return; +} +#endif //defined(SPI_MASTER_0_ENABLE) || defined(SPI_MASTER_1_ENABLE) + + +/** + * @brief Function for opening and initializing a SPI master driver. */ +uint32_t spi_master_open(const spi_master_hw_instance_t spi_master_hw_instance, + spi_master_config_t const * const p_spi_master_config) +{ + #if defined(SPI_MASTER_0_ENABLE) || defined(SPI_MASTER_1_ENABLE) + + + VERIFY_PARAM_NOT_NULL(p_spi_master_config); + + spi_master_instance_t * p_spi_instance = spi_master_get_instance(spi_master_hw_instance); + + switch (spi_master_hw_instance) + { + #ifdef SPI_MASTER_0_ENABLE + case SPI_MASTER_0: + spi_master_init_hw_instance(NRF_SPI0, SPI0_TWI0_IRQn, p_spi_instance); + break; + #endif //SPI_MASTER_0_ENABLE + + #ifdef SPI_MASTER_1_ENABLE + case SPI_MASTER_1: + spi_master_init_hw_instance(NRF_SPI1, SPI1_TWI1_IRQn, p_spi_instance); + break; + #endif //SPI_MASTER_1_ENABLE + + default: + break; + } + + //A Slave select must be set as high before setting it as output, + //because during connect it to the pin it causes glitches. + nrf_gpio_pin_set(p_spi_master_config->SPI_Pin_SS); + nrf_gpio_cfg_output(p_spi_master_config->SPI_Pin_SS); + nrf_gpio_pin_set(p_spi_master_config->SPI_Pin_SS); + + //Configure GPIO + nrf_gpio_cfg_output(p_spi_master_config->SPI_Pin_SCK); + nrf_gpio_cfg_output(p_spi_master_config->SPI_Pin_MOSI); + nrf_gpio_cfg_input(p_spi_master_config->SPI_Pin_MISO, NRF_GPIO_PIN_NOPULL); + p_spi_instance->pin_slave_select = p_spi_master_config->SPI_Pin_SS; + + /* Configure SPI hardware */ + p_spi_instance->p_nrf_spi->PSELSCK = p_spi_master_config->SPI_Pin_SCK; + p_spi_instance->p_nrf_spi->PSELMOSI = p_spi_master_config->SPI_Pin_MOSI; + p_spi_instance->p_nrf_spi->PSELMISO = p_spi_master_config->SPI_Pin_MISO; + + p_spi_instance->p_nrf_spi->FREQUENCY = p_spi_master_config->SPI_Freq; + + p_spi_instance->p_nrf_spi->CONFIG = + (uint32_t)(p_spi_master_config->SPI_CPHA << SPI_CONFIG_CPHA_Pos) | + (p_spi_master_config->SPI_CPOL << SPI_CONFIG_CPOL_Pos) | + (p_spi_master_config->SPI_ORDER << SPI_CONFIG_ORDER_Pos); + + + /* Clear waiting interrupts and events */ + p_spi_instance->p_nrf_spi->EVENTS_READY = 0; + + NVIC_ClearPendingIRQ(p_spi_instance->irq_type); + NVIC_SetPriority(p_spi_instance->irq_type, APP_IRQ_PRIORITY_MID); + + /* Clear event handler */ + p_spi_instance->callback_event_handler = NULL; + + /* Enable interrupt */ + p_spi_instance->p_nrf_spi->INTENSET = (SPI_INTENSET_READY_Set << SPI_INTENCLR_READY_Pos); + NVIC_EnableIRQ(p_spi_instance->irq_type); + + /* Enable SPI hardware */ + p_spi_instance->p_nrf_spi->ENABLE = (SPI_ENABLE_ENABLE_Enabled << SPI_ENABLE_ENABLE_Pos); + + /* Change state to IDLE */ + p_spi_instance->state = SPI_MASTER_STATE_IDLE; + + return NRF_SUCCESS; + #else + return NRF_ERROR_NOT_SUPPORTED; + #endif +} + +/** + * @brief Function for closing a SPI master driver. + */ +void spi_master_close(const spi_master_hw_instance_t spi_master_hw_instance) +{ + #if defined(SPI_MASTER_0_ENABLE) || defined(SPI_MASTER_1_ENABLE) + spi_master_instance_t * p_spi_instance = spi_master_get_instance(spi_master_hw_instance); + + /* Disable interrupt */ + NVIC_ClearPendingIRQ(p_spi_instance->irq_type); + NVIC_DisableIRQ(p_spi_instance->irq_type); + + p_spi_instance->p_nrf_spi->ENABLE = (SPI_ENABLE_ENABLE_Disabled << SPI_ENABLE_ENABLE_Pos); + + /* Set Slave Select pin as input with pull-up. */ + nrf_gpio_pin_set(p_spi_instance->pin_slave_select); + nrf_gpio_cfg_input(p_spi_instance->pin_slave_select, NRF_GPIO_PIN_PULLUP); + p_spi_instance->pin_slave_select = (uint8_t)0xFF; + + /* Disconnect pins from SPI hardware */ + p_spi_instance->p_nrf_spi->PSELSCK = (uint32_t)SPI_PIN_DISCONNECTED; + p_spi_instance->p_nrf_spi->PSELMOSI = (uint32_t)SPI_PIN_DISCONNECTED; + p_spi_instance->p_nrf_spi->PSELMISO = (uint32_t)SPI_PIN_DISCONNECTED; + + /* Reset to default values */ + spi_master_init_hw_instance(NULL, (IRQn_Type)0, p_spi_instance); + #else + return; + #endif +} + +/** + * @brief Function for getting current state of the SPI master driver. + */ +__INLINE spi_master_state_t spi_master_get_state( + const spi_master_hw_instance_t spi_master_hw_instance) +{ + #if defined(SPI_MASTER_0_ENABLE) || defined(SPI_MASTER_1_ENABLE) + spi_master_instance_t * spi_instance = spi_master_get_instance(spi_master_hw_instance); + return spi_instance->state; + #else + return SPI_MASTER_STATE_DISABLED; + #endif +} + +/** + * @brief Function for event handler registration. + */ +__INLINE void spi_master_evt_handler_reg(const spi_master_hw_instance_t spi_master_hw_instance, + spi_master_event_handler_t event_handler) +{ + #if defined(SPI_MASTER_0_ENABLE) || defined(SPI_MASTER_1_ENABLE) + spi_master_instance_t * spi_instance = spi_master_get_instance(spi_master_hw_instance); + spi_instance->callback_event_handler = event_handler; + #else + return; + #endif +} + +/** + * @brief Function for transmitting data between SPI master and SPI slave. + */ +uint32_t spi_master_send_recv(const spi_master_hw_instance_t spi_master_hw_instance, + uint8_t * const p_tx_buf, const uint16_t tx_buf_len, + uint8_t * const p_rx_buf, const uint16_t rx_buf_len) +{ + #if defined(SPI_MASTER_0_ENABLE) || defined(SPI_MASTER_1_ENABLE) + spi_master_instance_t * p_spi_instance = spi_master_get_instance(spi_master_hw_instance); + + uint32_t err_code = NRF_SUCCESS; + uint16_t max_length = 0; + + if (p_spi_instance->state == SPI_MASTER_STATE_IDLE) + { + NVIC_DisableIRQ(p_spi_instance->irq_type); + + max_length = (rx_buf_len > tx_buf_len) ? rx_buf_len : tx_buf_len; + + if (max_length > 0) + { + p_spi_instance->state = SPI_MASTER_STATE_BUSY; + p_spi_instance->start_flag = true; //abort_flag should set by abort and cleared only by restart + p_spi_instance->bytes_count = 0; + p_spi_instance->max_length = max_length; + spi_master_buffer_release(&(p_spi_instance->p_tx_buffer), &(p_spi_instance->tx_length)); + spi_master_buffer_release(&(p_spi_instance->p_rx_buffer), &(p_spi_instance->rx_length)); + /* Initialize buffers */ + spi_master_buffer_init(p_tx_buf, tx_buf_len, &(p_spi_instance->p_tx_buffer), + &(p_spi_instance->tx_length), &(p_spi_instance->tx_index)); + spi_master_buffer_init(p_rx_buf, rx_buf_len, &(p_spi_instance->p_rx_buffer), + &(p_spi_instance->rx_length), &(p_spi_instance->rx_index)); + nrf_gpio_pin_clear(p_spi_instance->pin_slave_select); + spi_master_send_initial_bytes(p_spi_instance); + spi_master_signal_evt(p_spi_instance, SPI_MASTER_EVT_TRANSFER_STARTED, max_length); + } + else + { + err_code = NRF_ERROR_INVALID_PARAM; + } + + NVIC_EnableIRQ(p_spi_instance->irq_type); + } + else + { + err_code = NRF_ERROR_BUSY; + } + + return err_code; + #else + return NRF_ERROR_NOT_SUPPORTED; + #endif +} + +#ifdef _SPI_5W_ + +/** + * @brief Function for aborting transfer + */ +uint32_t spi_master_abort(const spi_master_hw_instance_t spi_master_hw_instance) +{ + spi_master_instance_t * p_spi_instance = spi_master_get_instance(spi_master_hw_instance); + + NVIC_DisableIRQ(p_spi_instance->irq_type); + + if (p_spi_instance->state == SPI_MASTER_STATE_BUSY) + { + //set_flag - but only when there are events pending + //ignore when in IDLE - must be able to restart a completed transfer + p_spi_instance->abort_flag = true; + } + NVIC_EnableIRQ(p_spi_instance->irq_type); + return NRF_SUCCESS; +} + +/** + * @brief Function for restarting transfer + */ +uint32_t spi_master_restart(const spi_master_hw_instance_t spi_master_hw_instance) +{ + spi_master_instance_t * p_spi_instance = spi_master_get_instance(spi_master_hw_instance); + + NVIC_DisableIRQ(p_spi_instance->irq_type); + spi_master_signal_evt(p_spi_instance, SPI_MASTER_EVT_TRANSFER_RESTARTED, 0); + p_spi_instance->state = SPI_MASTER_STATE_BUSY; + p_spi_instance->bytes_count = 0; + p_spi_instance->tx_index = 0; + p_spi_instance->rx_index = 0; + p_spi_instance->start_flag = true; + p_spi_instance->abort_flag = false; //you should force clearing abort flag - no other way for 1 byte transfer + nrf_gpio_pin_clear(p_spi_instance->pin_slave_select); + spi_master_send_initial_bytes(p_spi_instance); + NVIC_EnableIRQ(p_spi_instance->irq_type); + + return NRF_SUCCESS; +} + +static void spi_5W_master_event_handler(spi_master_evt_t evt) +{ + + switch (m_hook_state) + { + case HOOK_STATE_IDLE: + + if (evt.type == SPI_MASTER_EVT_TRANSFER_STARTED) + { + DEBUG_EVT_SPI_MASTER_RAW_XFER_GUARDED(0); + m_hook_state = HOOK_STATE_GUARDED; + m_ser_phy_event_handler(evt); + } + break; + + case HOOK_STATE_GUARDED: + + if (evt.type == SPI_MASTER_EVT_FIRST_BYTE_RECEIVED) + { + if (evt.data == 0) + { + DEBUG_EVT_SPI_MASTER_RAW_XFER_PASSED(0); + m_hook_state = HOOK_STATE_PASSING; + } + else + { + DEBUG_EVT_SPI_MASTER_RAW_XFER_ABORTED(0); + m_hook_state = HOOK_STATE_ABORTED; + (void)spi_master_abort(m_spi_master_hw_instance); + } + } + break; + + case HOOK_STATE_ABORTED: + + if ((evt.type == SPI_MASTER_EVT_TRANSFER_ABORTED) || + (evt.type == SPI_MASTER_EVT_TRANSFER_COMPLETED)) + { + DEBUG_EVT_SPI_MASTER_RAW_XFER_RESTARTED(0); + m_hook_state = HOOK_STATE_RESTARTED; + (void)spi_master_restart(m_spi_master_hw_instance); + } + break; + + case HOOK_STATE_RESTARTED: + + if (evt.type == SPI_MASTER_EVT_TRANSFER_RESTARTED) + { + DEBUG_EVT_SPI_MASTER_RAW_XFER_GUARDED(0); + m_hook_state = HOOK_STATE_GUARDED; + } + break; + + case HOOK_STATE_PASSING: + + if (evt.type == SPI_MASTER_EVT_TRANSFER_COMPLETED) + { + m_hook_state = HOOK_STATE_IDLE; + m_ser_phy_event_handler(evt); //this is the only way to get a signal from complete transaction + } + break; + + default: + break; + } +} + +void spi_5W_master_evt_handler_reg(const spi_master_hw_instance_t spi_master_hw_instance, + spi_master_event_handler_t event_handler) +{ + m_ser_phy_event_handler = event_handler; + m_spi_master_hw_instance = spi_master_hw_instance; + m_hook_state = HOOK_STATE_IDLE; + spi_master_evt_handler_reg(spi_master_hw_instance, spi_5W_master_event_handler); + return; +} + +#endif + +/** @} */ diff --git a/drivers_nrf/spi_master/spi_5W_master.h b/drivers_nrf/spi_master/spi_5W_master.h new file mode 100644 index 0000000..7b827bf --- /dev/null +++ b/drivers_nrf/spi_master/spi_5W_master.h @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2014 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef APP_SPI_MASTER_H +#define APP_SPI_MASTER_H + +#include +#include +#include "boards.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define _SPI_5W_ + +/**@brief Struct containing configuration parameters of the SPI master. */ +typedef struct +{ + uint32_t SPI_Freq; /**< SPI frequency. */ + uint32_t SPI_Pin_SCK; /**< SCK pin number. */ + uint32_t SPI_Pin_MISO; /**< MISO pin number. */ + uint32_t SPI_Pin_MOSI; /**< MOSI pin number .*/ + uint32_t SPI_Pin_SS; /**< Slave select pin number. */ + uint8_t SPI_ORDER; /**< Bytes order MSBFIRST or LSBFIRST. */ + uint8_t SPI_CPOL; /**< Serial clock polarity ACTIVEHIGH or ACTIVELOW. */ + uint8_t SPI_CPHA; /**< Serial clock phase LEADING or TRAILING. */ + } spi_master_config_t; + +/**@brief SPI master driver events types. */ +typedef enum +{ + SPI_MASTER_EVT_TRANSFER_STARTED = 0, /**< An event indicating that transfer has been started */ + SPI_MASTER_EVT_TRANSFER_COMPLETED, /**< An event indicating that transfer has been completed */ + SPI_MASTER_EVT_TRANSFER_ABORTED, /**< An event indicating that transfer has been aborted */ + SPI_MASTER_EVT_TRANSFER_RESTARTED, /**< An event indicating that transfer has been resumed */ + SPI_MASTER_EVT_FIRST_BYTE_RECEIVED, /**< An event indicating end of one byte transfer */ + SPI_MASTER_EVT_TYPE_MAX /**< Enumeration upper bound. */ +} spi_master_evt_type_t; + +/**@brief Struct containing parameters of the SPI MASTER event */ + typedef struct + { + spi_master_evt_type_t type; /**< Type of an event */ + uint16_t data; /**< event data - context dependent */ + } spi_master_evt_t; + + /**@brief SPI MASTER internal states types. */ + typedef enum + { + SPI_MASTER_STATE_DISABLED, /**< A state indicating that SPI master is disabled. */ + SPI_MASTER_STATE_BUSY, /**< A state indicating that SPI master is sending now. */ + SPI_MASTER_STATE_ABORTED, + SPI_MASTER_STATE_IDLE /**< A state indicating that SPI master is idle now. */ + } spi_master_state_t; + + /**@brief Instances of SPI master module. */ + typedef enum + { + #ifdef SPI_MASTER_0_ENABLE + SPI_MASTER_0, /**< A instance of SPI master 0. */ + #endif + + #ifdef SPI_MASTER_1_ENABLE + SPI_MASTER_1, /**< A instance of SPI master 1. */ + #endif + + SPI_MASTER_HW_ENABLED_COUNT /**< A number of enabled instances of SPI master. */ + } spi_master_hw_instance_t; + +/**@brief Type of generic callback function handler to be used by all SPI MASTER driver events. + * + * @param[in] spi_master_evt SPI MASTER driver event. + */ +typedef void (*spi_master_event_handler_t) (spi_master_evt_t spi_master_evt); + + +/**@brief Function for opening and initializing a SPI master driver. + * + * @note Function initializes SPI master hardware and internal module states, unregister events callback. + * + * @warning If the function has been already called, the function @ref spi_master_close has to be + * called before spi_master_open can be called again. + * + * @param[in] spi_master_hw_instance Instance of SPI master module. + * @param[in] p_spi_master_config Pointer to configuration structure which will be used + * to initialize SPI MASTER hardware. + * + * @retval NRF_SUCCESS Operation success. + * @retval NRF_ERROR_INVALID_STATE Operation failure. The function has been already called. + * To call it again the function @ref spi_master_close + * has to be called previously. + * @retval NRF_ERROR_NULL Operation failure. NULL pointer supplied. + */ +uint32_t spi_master_open(const spi_master_hw_instance_t spi_master_hw_instance, + spi_master_config_t const * const p_spi_master_config); + + +/**@brief Function for closing a SPI MASTER driver. + * + * @note Function disable hardware, reset internal module states and unregister events callback + * function. + * + * @param[in] spi_master_hw_instance A instance of SPI master. + */ +void spi_master_close(const spi_master_hw_instance_t spi_master_hw_instance); + + +/**@brief Function for transferring data between SPI master and SPI slave + * + * @note Function registers buffers pointed by p_tx_buf and p_rx_buf parameters, after that starts transmission. + * Function generates an event of type @ref SPI_MASTER_EVT_TRANSFER_STARTED when transfer has been started + * and @ref SPI_MASTER_EVT_TRANSFER_COMPLETED when transfer has been completed. + * + * @param[in] spi_master_hw_instance Instance of SPI master module. + * @param[in] p_tx_buf Pointer to a transmit buffer. + * @param[in] tx_buf_len Number of octets to the transfer. + * @param[out] p_rx_buf Pointer to a receive buffer. + * @param[in] rx_buf_len Number of octets to be received. + * + * @retval NRF_SUCCESS Operation success. Packet was registered to the transmission + * and event will be send upon transmission completion. + * @retval NRF_ERROR_BUSY Operation failure. Transmitting of a data is in progress. + */ + uint32_t spi_master_send_recv(const spi_master_hw_instance_t spi_master_hw_instance, + uint8_t * const p_tx_buf, const uint16_t tx_buf_len, + uint8_t * const p_rx_buf, const uint16_t rx_buf_len); + + +/**@brief Function for registration event handler. +* +* @note Function registers a event handler to be used by SPI MASTER driver for sending events. +* @ref SPI_MASTER_EVT_TRANSFER_STARTED and @ref SPI_MASTER_EVT_TRANSFER_COMPLETED. +* +* @param[in] spi_master_hw_instance Instance of SPI master module. +* @param[in] event_handler Generic callback function handler to be used +* by all SPI master driver events. +*/ +void spi_master_evt_handler_reg(const spi_master_hw_instance_t spi_master_hw_instance, + spi_master_event_handler_t event_handler); + + +/**@brief Function for getting current state of the SPI master driver. + * + * @note Function gets current state of the SPI master driver. + * + * @param[in] spi_master_hw_instance Instance of SPI master module. + * + * @retval SPI_MASTER_STATE_DISABLED SPI MASTER is disabled. + * @retval SPI_MASTER_STATE_BUSY SPI_MASTER is sending now. + * @retval SPI_MASTER_STATE_IDLE SPI_MASTER is idle now. + */ +spi_master_state_t spi_master_get_state(const spi_master_hw_instance_t spi_master_hw_instance); + +#ifdef _SPI_5W_ + +uint32_t spi_master_abort(const spi_master_hw_instance_t spi_master_hw_instance); + +uint32_t spi_master_restart(const spi_master_hw_instance_t spi_master_hw_instance); + +void spi_5W_master_evt_handler_reg(const spi_master_hw_instance_t spi_master_hw_instance, + spi_master_event_handler_t event_handler); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif -- cgit v1.2.3