diff --git a/examples/Makefile-nrf52840 b/examples/Makefile-nrf52840 index b753b811d..dee38d157 100644 --- a/examples/Makefile-nrf52840 +++ b/examples/Makefile-nrf52840 @@ -45,7 +45,6 @@ GCCVersion = $(shell expr `$(CC) -dumpversion | cut -f1 -d. configure_OPTIONS = \ --enable-cli-app=all \ --enable-ncp-app=all \ - --with-ncp-bus=uart \ --enable-diag \ --with-examples=nrf52840 \ --enable-linker-map \ @@ -56,6 +55,12 @@ ifdef SRC_PATH configure_OPTIONS += --srcdir="$(SRC_PATH)" endif +ifeq ($(NCP_SPI),1) +configure_OPTIONS += --with-ncp-bus=spi +else +configure_OPTIONS += --with-ncp-bus=uart +endif + TopSourceDir := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST)))).. AbsTopSourceDir := $(dir $(realpath $(firstword $(MAKEFILE_LIST)))).. @@ -99,6 +104,10 @@ ifeq ($(USB),1) COMMONCFLAGS += -DUSB_CDC_AS_SERIAL_TRANSPORT=1 endif +ifeq ($(DISABLE_SPI),1) +COMMONCFLAGS += -DSPIS_TRANSPORT_DISABLE=1 +endif + ifeq ($(shell expr $(GCCVersion) \>= 7), 1) COMMONCFLAGS += -Wno-expansion-to-defined endif diff --git a/examples/platforms/nrf52840/Makefile.am b/examples/platforms/nrf52840/Makefile.am index fb02711b1..34c6c2212 100644 --- a/examples/platforms/nrf52840/Makefile.am +++ b/examples/platforms/nrf52840/Makefile.am @@ -88,6 +88,7 @@ PLATFORM_COMMON_SOURCES radio.c \ random.c \ temp.c \ + spi-slave.c \ uart.c \ usb-cdc-uart.c \ $(NULL) @@ -312,6 +313,7 @@ noinst_HEADERS $(top_srcdir)/third_party/NordicSemiconductor/nrfx/hal/nrf_ppi.h \ $(top_srcdir)/third_party/NordicSemiconductor/nrfx/hal/nrf_power.h \ $(top_srcdir)/third_party/NordicSemiconductor/nrfx/hal/nrf_rng.h \ + $(top_srcdir)/third_party/NordicSemiconductor/nrfx/hal/nrf_spis.h \ $(top_srcdir)/third_party/NordicSemiconductor/nrfx/hal/nrf_uart.h \ $(top_srcdir)/third_party/NordicSemiconductor/nrfx/hal/nrf_usbd.h \ $(top_srcdir)/third_party/NordicSemiconductor/nrfx/mdk/compiler_abstraction.h \ diff --git a/examples/platforms/nrf52840/README.md b/examples/platforms/nrf52840/README.md index 6a770d79c..7801c1e87 100644 --- a/examples/platforms/nrf52840/README.md +++ b/examples/platforms/nrf52840/README.md @@ -48,6 +48,32 @@ $ make -f examples/Makefile-nrf52840 USB=1 Note, that if Windows 7 or earlier is used, an additional USB CDC driver has to be loaded. It can be found in third_party/NordicSemiconductor/libraries/usb/nordic_cdc_acm_example.inf +## Native SPI Slave support + +You can build the libraries with support for native SPI Slave. +To do so, build the libraries with the following parameter: +``` +$ make -f examples/Makefile-nrf52840 NCP_SPI=1 +``` + +With this option enabled, SPI communication between the NCP example and wpantund is possible +(provided that the wpantund host supports SPI Master). To achieve that, an appropriate SPI device +should be chosen in wpantund configuration file, `/etc/wpantund.conf`. You can find an example below. +``` +Config:NCP:SocketPath "system:/usr/bin/spi-hdlc-adapter --gpio-int /sys/class/gpio/gpio25 /dev/spidev0.0" +``` + +[spi-hdlc-adapter][spi-hdlc-adapter] +is a tool that can be used to perform communication between NCP and wpantund over SPI. +In the above example it is assumed that `spi-hdlc-adapter` is installed in `/usr/bin`. + +The default SPI Slave pin configuration for nRF52840 is defined in `examples/platforms/nrf52840/platform-config.h`. + +Note that the native SPI Slave support is not intended to be used with Engineering sample A of the nRF52840 chip due to +single transfer size limitation. + +[spi-hdlc-adapter]: https://github.com/openthread/openthread/tree/master/tools/spi-hdlc-adapter + ## Flashing the binaries Flash the compiled binaries onto nRF52840 using `nrfjprog` which is diff --git a/examples/platforms/nrf52840/platform-config.h b/examples/platforms/nrf52840/platform-config.h index 92a95b5d8..1fe17f7b9 100644 --- a/examples/platforms/nrf52840/platform-config.h +++ b/examples/platforms/nrf52840/platform-config.h @@ -317,6 +317,94 @@ #define LOG_TIMESTAMP_ENABLE 1 #endif +/******************************************************************************* + * @section SPI Slave configuration. + ******************************************************************************/ + +/** + * @def SPIS Instance. + */ +#ifndef SPIS_INSTANCE +#define SPIS_INSTANCE NRF_SPIS0 +#endif + +/** + * @def SPIS mode. + * + * @brief Possible values: + * \ref NRF_SPIS_MODE_0 - SCK active high, sample on leading edge of clock. + * \ref NRF_SPIS_MODE_1 - SCK active high, sample on trailing edge of clock. + * \ref NRF_SPIS_MODE_2 - SCK active low, sample on leading edge of clock. + * \ref NRF_SPIS_MODE_3 - SCK active low, sample on trailing edge of clock. + */ +#ifndef SPIS_MODE +#define SPIS_MODE NRF_SPIS_MODE_0 +#endif + +/** + * @def SPIS bit orders. + * + * @brief Possible values: + * \ref NRF_SPIS_BIT_ORDER_MSB_FIRST - Most significant bit shifted out first. + * \ref NRF_SPIS_BIT_ORDER_LSB_FIRST - Least significant bit shifted out first. + */ +#ifndef SPIS_BIT_ORDER +#define SPIS_BIT_ORDER NRF_SPIS_BIT_ORDER_MSB_FIRST +#endif + +/** + * @def SPIS Interrupt number. + */ +#ifndef SPIS_IRQN +#define SPIS_IRQN SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn +#endif + +/** + * @def SPIS Interrupt priority. + */ +#ifndef SPIS_IRQ_PRIORITY +#define SPIS_IRQ_PRIORITY 6 +#endif + +/** + * @def SPIS MOSI Pin. + */ +#ifndef SPIS_PIN_MOSI +#define SPIS_PIN_MOSI 4 +#endif + +/** + * @def SPIS MISO Pin. + */ +#ifndef SPIS_PIN_MISO +#define SPIS_PIN_MISO 28 +#endif + +/** + * @def SPIS SCK Pin. + */ +#ifndef SPIS_PIN_SCK +#define SPIS_PIN_SCK 3 +#endif + +/** + * @def SPIS CSN Pin. + */ +#ifndef SPIS_PIN_CSN +#define SPIS_PIN_CSN 29 +#endif + +/** + * @def SPIS Host IRQ Pin. + */ +#ifndef SPIS_PIN_HOST_IRQ +#define SPIS_PIN_HOST_IRQ 30 +#endif + +/******************************************************************************* + * @section USB driver configuration. + ******************************************************************************/ + /** * @def USB_HOST_UART_CONFIG_DELAY_MS * diff --git a/examples/platforms/nrf52840/platform-nrf5.h b/examples/platforms/nrf52840/platform-nrf5.h index 629793139..024eb7677 100644 --- a/examples/platforms/nrf52840/platform-nrf5.h +++ b/examples/platforms/nrf52840/platform-nrf5.h @@ -107,6 +107,23 @@ void nrf5LogInit(void); */ void nrf5LogDeinit(void); +/** + * Initialization of SPI Slave driver. + * + */ +void nrf5SpiSlaveInit(void); + +/** + * Deinitialization of SPI Slave driver. + * + */ +void nrf5SpiSlaveDeinit(void); + +/** + * Function for processing SPI Slave driver. + */ +void nrf5SpiSlaveProcess(void); + /** * Initialization of Misc module. * diff --git a/examples/platforms/nrf52840/platform.c b/examples/platforms/nrf52840/platform.c index 82858a809..9e23d30d6 100644 --- a/examples/platforms/nrf52840/platform.c +++ b/examples/platforms/nrf52840/platform.c @@ -79,6 +79,9 @@ void PlatformInit(int argc, char *argv[]) nrf5AlarmInit(); nrf5RandomInit(); nrf5UartInit(); +#ifndef SPIS_TRANSPORT_DISABLE + nrf5SpiSlaveInit(); +#endif nrf5MiscInit(); nrf5CryptoInit(); nrf5RadioInit(); @@ -91,6 +94,9 @@ void PlatformDeinit(void) nrf5RadioDeinit(); nrf5CryptoDeinit(); nrf5MiscDeinit(); +#ifndef SPIS_TRANSPORT_DISABLE + nrf5SpiSlaveDeinit(); +#endif nrf5UartDeinit(); nrf5RandomDeinit(); nrf5AlarmDeinit(); @@ -111,6 +117,9 @@ void PlatformProcessDrivers(otInstance *aInstance) nrf5RadioProcess(aInstance); nrf5UartProcess(); nrf5TempProcess(); +#ifndef SPIS_TRANSPORT_DISABLE + nrf5SpiSlaveProcess(); +#endif } __WEAK void PlatformEventSignalPending(void) diff --git a/examples/platforms/nrf52840/spi-slave.c b/examples/platforms/nrf52840/spi-slave.c new file mode 100644 index 000000000..1d46abe34 --- /dev/null +++ b/examples/platforms/nrf52840/spi-slave.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2018, The OpenThread Authors. + * 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 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 the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 + * This file implements the OpenThread platform abstraction for SPIS communication. + * + */ + +#include +#include +#include + +#include +#include +#include + +/** + * SPI Slave transaction variables. + */ +static void * sContext = NULL; +static uint8_t * sOutputBuf = NULL; +static uint16_t sOutputBufLen = 0; +static uint8_t * sInputBuf = NULL; +static uint16_t sInputBufLen = 0; +static bool sRequestTransactionFlag = false; +static bool sFurtherProcessingFlag = false; +static otPlatSpiSlaveTransactionProcessCallback sProcessCallback = NULL; +static otPlatSpiSlaveTransactionCompleteCallback sCompleteCallback = NULL; + +void nrf5SpiSlaveInit(void) +{ + /* Intentionally empty. */ +} + +void nrf5SpiSlaveDeinit(void) +{ + sOutputBuf = NULL; + sOutputBufLen = 0; + sInputBuf = NULL; + sInputBufLen = 0; + sRequestTransactionFlag = false; + + otPlatSpiSlaveDisable(); +} + +void nrf5SpiSlaveProcess(void) +{ + otEXPECT(sFurtherProcessingFlag == true); + + /* Clear further processing flag. */ + sFurtherProcessingFlag = false; + + /* Perform any further processing if necessary. */ + sProcessCallback(sContext); + +exit: + return; +} + +otError otPlatSpiSlaveEnable(otPlatSpiSlaveTransactionCompleteCallback aCompleteCallback, + otPlatSpiSlaveTransactionProcessCallback aProcessCallback, + void * aContext) +{ + otError error = OT_ERROR_NONE; + + /* Check if SPI Slave interface is already enabled. */ + otEXPECT_ACTION(sCompleteCallback == NULL, error = OT_ERROR_ALREADY); + + /* Set up MISO/MOSI/SCK/CSN and Host IRQ. */ + nrf_gpio_cfg_input(SPIS_PIN_MISO, NRF_GPIO_PIN_NOPULL); + nrf_gpio_cfg_input(SPIS_PIN_MOSI, NRF_GPIO_PIN_NOPULL); + nrf_gpio_cfg_input(SPIS_PIN_SCK, NRF_GPIO_PIN_NOPULL); + nrf_gpio_cfg_input(SPIS_PIN_CSN, NRF_GPIO_PIN_NOPULL); + nrf_gpio_pin_set(SPIS_PIN_HOST_IRQ); + nrf_gpio_cfg_output(SPIS_PIN_HOST_IRQ); + + nrf_spis_pins_set(SPIS_INSTANCE, SPIS_PIN_SCK, SPIS_PIN_MOSI, SPIS_PIN_MISO, SPIS_PIN_CSN); + + /* Set buffer pointers. */ + nrf_spis_rx_buffer_set(SPIS_INSTANCE, NULL, 0); + nrf_spis_tx_buffer_set(SPIS_INSTANCE, NULL, 0); + + /* Configure SPIS Mode and Bit order. */ + nrf_spis_configure(SPIS_INSTANCE, SPIS_MODE, SPIS_BIT_ORDER); + + /* Use 0xFF character to indicate that there is no transmit buffer set up or overflow occured + as described in API documentation. */ + nrf_spis_def_set(SPIS_INSTANCE, 0xFF); + nrf_spis_orc_set(SPIS_INSTANCE, 0xFF); + + /* Clear SPIS specific events. */ + nrf_spis_event_clear(SPIS_INSTANCE, NRF_SPIS_EVENT_END); + nrf_spis_event_clear(SPIS_INSTANCE, NRF_SPIS_EVENT_ACQUIRED); + + /* Enable interrupts for ACQUIRED and END events. */ + nrf_spis_int_enable(SPIS_INSTANCE, (NRF_SPIS_INT_ACQUIRED_MASK | NRF_SPIS_INT_END_MASK)); + + /* Configure NVIC to handle SPIS interrupts. */ + NVIC_SetPriority(SPIS_IRQN, SPIS_IRQ_PRIORITY); + NVIC_ClearPendingIRQ(SPIS_IRQN); + NVIC_EnableIRQ(SPIS_IRQN); + + /* Enable SPI slave device. */ + nrf_spis_enable(SPIS_INSTANCE); + + /* Set proper callback and context. */ + sProcessCallback = aProcessCallback; + sCompleteCallback = aCompleteCallback; + sContext = aContext; + +exit: + return error; +} + +void otPlatSpiSlaveDisable(void) +{ + /* Disable interrupts for ACQUIRED and END events. */ + nrf_spis_int_disable(SPIS_INSTANCE, (NRF_SPIS_INT_ACQUIRED_MASK | NRF_SPIS_INT_END_MASK)); + + /* Disable NVIC interrupt. */ + NVIC_DisableIRQ(SPIS_IRQN); + + /* Disable SPIS instance. */ + nrf_spis_disable(SPIS_INSTANCE); +} + +otError otPlatSpiSlavePrepareTransaction(uint8_t *aOutputBuf, + uint16_t aOutputBufLen, + uint8_t *aInputBuf, + uint16_t aInputBufLen, + bool aRequestTransactionFlag) +{ + otError error = OT_ERROR_NONE; + nrf_spis_semstat_t semaphore_status = nrf_spis_semaphore_status_get(SPIS_INSTANCE); + + otEXPECT_ACTION(sCompleteCallback != NULL, error = OT_ERROR_INVALID_STATE); + + otEXPECT_ACTION(((semaphore_status != NRF_SPIS_SEMSTAT_SPIS) && (semaphore_status != NRF_SPIS_SEMSTAT_CPUPENDING)), + error = OT_ERROR_BUSY); + + if (aOutputBuf != NULL) + { + sOutputBuf = aOutputBuf; + sOutputBufLen = aOutputBufLen; + } + + if (aInputBuf != NULL) + { + sInputBuf = aInputBuf; + sInputBufLen = aInputBufLen; + } + + sRequestTransactionFlag = aRequestTransactionFlag; + + /* Trigger acquired event. */ + nrf_spis_task_trigger(SPIS_INSTANCE, NRF_SPIS_TASK_ACQUIRE); + +exit: + return error; +} + +/** + * Interrupt handler of SPIS peripherial. + */ +void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) +{ + /* Check for SPI semaphore acquired event. */ + if (nrf_spis_event_check(SPIS_INSTANCE, NRF_SPIS_EVENT_ACQUIRED)) + { + nrf_spis_event_clear(SPIS_INSTANCE, NRF_SPIS_EVENT_ACQUIRED); + + /* Set actual TX/RX buffers. */ + nrf_spis_tx_buffer_set(SPIS_INSTANCE, sOutputBuf, sOutputBufLen); + nrf_spis_rx_buffer_set(SPIS_INSTANCE, sInputBuf, sInputBufLen); + + if (sRequestTransactionFlag) + { + /* Interrupt pin is active low. */ + nrf_gpio_pin_clear(SPIS_PIN_HOST_IRQ); + } + + /* Trigger RELEASE event immediately. */ + nrf_spis_task_trigger(SPIS_INSTANCE, NRF_SPIS_TASK_RELEASE); + } + + /* Check for SPI transaction complete event. */ + if (nrf_spis_event_check(SPIS_INSTANCE, NRF_SPIS_EVENT_END)) + { + nrf_spis_event_clear(SPIS_INSTANCE, NRF_SPIS_EVENT_END); + + if (sRequestTransactionFlag) + { + nrf_gpio_pin_set(SPIS_PIN_HOST_IRQ); + } + + /* Discard all transactions until buffers are updated. */ + nrf_spis_tx_buffer_set(SPIS_INSTANCE, sOutputBuf, 0); + nrf_spis_rx_buffer_set(SPIS_INSTANCE, sInputBuf, 0); + + /* Execute application callback. */ + if (sCompleteCallback(sContext, sOutputBuf, sOutputBufLen, sInputBuf, sInputBufLen, + nrf_spis_rx_amount_get(SPIS_INSTANCE))) + { + /* Further processing is required. */ + sFurtherProcessingFlag = true; + } + } +}