This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

issues with implementing SPI slave on nrf9160DK

using nrf connect sdk 1.4.0 and nrf9160 DK

build: nrf9160dk_nrf9160ns

SES version: V5.10d

Windows 10 Pro 64bit

1. Please note I used https_client project to implement spi slave(but have removed all the https code.. ignore the cert file)

2. Used some details from tickets this and this.. Please trust me when I say i have searched the devzone and documents as much as I could

proj.conf looks like this

# SPI
CONFIG_SPI=y
## for slave only
CONFIG_SPI_SLAVE=y
CONFIG_SPI_3_OP_MODES=3

#commented these out as per some devzone discussions
#CONFIG_SPI_3=y
#CONFIG_SPI_NRFX=y
#CONFIG_SPI_3_NRF_SPIS=y #this gives an error while opening a project if uncommented


CONFIG_RESET_ON_FATAL_ERROR=n

overlay file

&spi3 {
compatible = "nordic,nrf-spis";
status = "okay";
mosi-pin = <10>;
miso-pin = <11>;
sck-pin = <12>;
csn-pin = <13>;
def-char = <0xde>;
};

main.c

/*
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
 */

#include <string.h>
#include <zephyr.h>
#include <stdlib.h>
#include <drivers/spi.h>
#include <nrfx.h>
#include <nrfx_uarte.h>
#include "nrfx_spis.h"

#define PIN_SCK 10
#define PIN_MOSI 11
#define PIN_MISO 12
#define PIN_CSN 13
#define SPIS_NR 3

nrfx_spis_t spis_t = NRFX_SPIS_INSTANCE(SPIS_NR);
nrfx_spis_config_t spis_config_t = NRFX_SPIS_DEFAULT_CONFIG(PIN_SCK,PIN_MOSI,PIN_MISO,PIN_CSN);

uint8_t rx_buffer[2] = {0x0};
uint8_t tx_buffer[2] = {0xa5};

void spis_event_handler_t(nrfx_spis_evt_t const *p_event, void *p_context){
    printk("handler\n");
    int err;
    switch(p_event->evt_type){
        case NRFX_SPIS_XFER_DONE:
                printk("received %x, %x\n",rx_buffer[0], rx_buffer[1]);
            err = nrfx_spis_buffers_set(&spis_t, tx_buffer, sizeof(tx_buffer), rx_buffer, sizeof(rx_buffer));
            if(err != NRFX_SUCCESS){
                printk("Error with setting.\n");
            }
            break;
        case NRFX_SPIS_BUFFERS_SET_DONE:
            printk("buffers set\n");
            break;
        case NRFX_SPIS_EVT_TYPE_MAX:
        
            break;
        default:
            ;
    }
}

static void manual_isr_setup()
{
    //IRQ_DIRECT_CONNECT(SPIM3_SPIS3_TWIM3_TWIS3_UARTE3_IRQn, 0, nrfx_spis_3_irq_handler, 0);
    //irq_enable(SPIM3_SPIS3_TWIM3_TWIS3_UARTE3_IRQn);
    IRQ_DIRECT_CONNECT(UARTE3_SPIM3_SPIS3_TWIM3_TWIS3_IRQn, 0, nrfx_spis_3_irq_handler, 0);
    irq_enable(UARTE3_SPIM3_SPIS3_TWIM3_TWIS3_IRQn);    
}

void init_spis(){
    int err;

    //change mode
    spis_config_t.mode = NRF_SPIS_MODE_3;
    err = nrfx_spis_init(&spis_t,&spis_config_t,spis_event_handler_t,NULL);
    if(err != NRFX_SUCCESS){
        printk("nrfx_spis_init - Error. %x\n",err);
    } else {
        printk("SPIS started.\n");
    }
}


void spi_slave_test(void) {
    int err;
    init_spis();
    manual_isr_setup();

    err = nrfx_spis_buffers_set(&spis_t, tx_buffer, sizeof(tx_buffer), rx_buffer, sizeof(rx_buffer));
    if(err != NRFX_SUCCESS){
        printk("nrfx_spis_buffers_set - Error. %x\n", err);
    }
}


void main(void)
{
	printk("SPIS sample started \n\r");
        spi_slave_test();
        while (1) {
          //printk("alive \n");
          k_sleep(Z_TIMEOUT_TICKS(50000));
        }
}


Notes:

I have closed the solution and opened after changing proj.conf. I am using SPIS3

Issue is that nrfx_spis_init fails with error 0xbad0005.. and the reason for that is this line in nrfx_spi.c fails

    if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)

I am just confused to see that p_cb->state is set to NRFX_DRV_STATE for some reason
m_cb[p_instance->drv_inst_idx] has valid data... that array has ONLY ONE element (given only spi3 is available)  and drv_inst_idx=0;

It is clear that I a missing some configuration.. I was told to make some spis related changes using the SES ->project->configure nrf sdk project.. I tried to enable spis2 instance and spis3 driver and that made no difference.. I am sure you have some document in there somewhere in your ocean of documents explaining this but unfortunately I couldn't find any details

3. The SPI master worked without any issue.. Not that it helps but just FYI

4. attached the entire project in zip file

4331.https_client.zip

  • Corrections: 
    p_cb->state is set to NRFX_DRV_STATE_INITIALIZED
    I tried to enable spis3 instance and spis3 driver

  • Hi,

     

    Thank you for providing the full project. This makes it 

    I am just confused to see that p_cb->state is set to NRFX_DRV_STATE for some reason
    m_cb[p_instance->drv_inst_idx] has valid data... that array has ONLY ONE element (given only spi3 is available)  and drv_inst_idx=0;

     The problem is that the nrfx_spis and zephyr's spi slave do not play well together, so you have to only have one of these enabled to be able to use the SPIS properly.

     

    It is clear that I a missing some configuration.. I was told to make some spis related changes using the SES ->project->configure nrf sdk project.. I tried to enable spis2 instance and spis3 driver and that made no difference.. I am sure you have some document in there somewhere in your ocean of documents explaining this but unfortunately I couldn't find any details

     As your project is setup now, the zephyr spi driver will be initialized "post kernel" (see here: https://github.com/nrfconnect/sdk-zephyr/blob/master/drivers/spi/spi_nrfx_spis.c#L290-L297), and before main() is called. This means that the spis_nrfx_spis.c implementation will then claim the SPIS3 peripheral before you can via the nrfx_spis API in main.

     

    The fix is to disable the zephyr SPI driver and enable the NRFX_SPIS configs:

    # SPI
    #CONFIG_SPI=y
    ## for slave only
    #CONFIG_SPI_SLAVE=y
    #CONFIG_SPI_3_OP_MODES=3
    
    #commented these out as per some devzone discussions
    #CONFIG_SPI_3=y
    #CONFIG_SPI_NRFX=y
    #CONFIG_SPI_3_NRF_SPIS=y #this gives an error while opening a project if uncomm>
    
    CONFIG_NRFX_SPIS=y
    CONFIG_NRFX_SPIS3=y
    
    CONFIG_RESET_ON_FATAL_ERROR=n
    

     

    After these changes, the firmware should print this:

    *** Booting Zephyr OS build v2.4.0-ncs1  ***
    SPIS sample started
    SPIS started.
    handler
    buffers set
    

     

    Could you see if this is the case on your end as well?

     

    Kind regards,

    Håkon

  • you are a life saver sir.. thank you very much

    This works fine.. I can raise another ticket but I will try my luck here and I am sure i won't be the only one who will thanking this ticket :-)
    I have a nrf5340 acting like a master and sending data to nrf9160 which is acting like a slave.. the master is very simple (and loop back works as well).. For every time I send data from the master (I use spi_transceive),  the handler spis_event_handler_t in the slave gets called way too many times. You already have the slave code that I had attached.. any clue as to why that may happen ?

    #include <zephyr.h>
    #include <sys/printk.h>
    #include <drivers/spi.h>
    #include <nrfx.h>
    #include <nrfx_uarte.h>
    
    static const struct spi_config spi_cfg = {
    	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
    		     SPI_MODE_CPOL | SPI_MODE_CPHA,
    	.frequency = 800000,//4000000,
    	.slave = 0,
    };
    
    struct device * spi_dev;
    
    static void spi_init(void)
    {
    	const char* const spiName = "SPI_1";
    	spi_dev = device_get_binding(spiName);
    
    	if (spi_dev == NULL) {
    		printk("Could not get %s device\n", spiName);
    		return;
    	}
    }
    
    void spi_test_send(void)
    {
    	int err;
    	static uint8_t tx_buffer[]="Hello from Abcdefgh!";
    	static uint8_t rx_buffer[]="IFMMP GSPN BCDEFGHI!";
    
    	const struct spi_buf tx_buf = {
    		.buf = tx_buffer,
    		.len = sizeof(tx_buffer)
    	};
    	const struct spi_buf_set tx = {
    		.buffers = &tx_buf,
    		.count = 1
    	};
    
    	struct spi_buf rx_buf = {
    		.buf = rx_buffer,
    		.len = sizeof(rx_buffer),
    	};
    	const struct spi_buf_set rx = {
    		.buffers = &rx_buf,
    		.count = 1
    	};
    
    	err = spi_transceive(spi_dev, &spi_cfg, &tx, &rx);
    	if (err) {
    		printk("SPI error: %d\n", err);
    	} else {
    		/* Connect MISO to MOSI for loopback */
    		printk("TX sent: %s\n", tx_buffer);
    		printk("RX recv: %s\n", rx_buffer);
    		tx_buffer[0]++;
    	}	
    }
    
    
    void main(void)
    {
    	printk("Hello sheshu - take 2 %s\n", CONFIG_BOARD);
    	spi_init();
    
    	while (1) {
    		spi_test_send();
    		//k_sleep(1000);
                    //printk("alive \n");
                    k_sleep(Z_TIMEOUT_TICKS(50000));
    	}
    }

    proj.conf

    CONFIG_SERIAL=y

    CONFIG_UART_INTERRUPT_DRIVEN=y

    CONFIG_LOG=y
    CONFIG_LOG_IMMEDIATE=y

    # SPI
    CONFIG_SPI=y
    CONFIG_SPI_1=y

    CONFIG_MAIN_STACK_SIZE=4096

    CONFIG_NRF5340_CPUAPP_ERRATUM19=y

    CONFIG_RESET_ON_FATAL_ERROR=n

    overlay

    &spi1 {
    compatible = "nordic,nrf-spim";
    status = "okay";
    mosi-pin = <4>;
    miso-pin = <10>; //<5>;
    sck-pin = <6>;
    csn-pin = <7>;
    };


    &i2c1 {
    status = "disabled";
    };

    the entire project is also attachedhello_world2.zip

  • Hi,

      

    sshenoy105 said:
    you are a life saver sir.. thank you very much

    This works fine.. I can raise another ticket but I will try my luck here and I am sure i won't be the only one who will thanking this ticket :-)

    I'm glad to hear that it worked! Always happy to help out.

      

    sshenoy105 said:
    For every time I send data from the master (I use spi_transceive),  the handler spis_event_handler_t in the slave gets called way too many times. You already have the slave code that I had attached.. any clue as to why that may happen ?

    The slave code currently has set buffers of 2 byte (unless you have altered this from the original post), but the master sends a larger string (21 bytes inc. zero termination).

    If you peek into the NRF_SPIS1->INTENSET register, you can see which events are interrupt-enabled:

    https://infocenter.nordicsemi.com/topic/ps_nrf52840/spis.html?cp=4_0_0_5_25_4_6#register.INTENSET

     

    When I tested this, the content was 0x402, meaning EVENTS_ACQUIRED and EVENTS_END are interrupt enabled. You should then get two interrupts per transaction. However; if the slave doesn't have enough buffer space, so it'll only receive its max configured size, and the rest will be transferred back as a default overread character. You should set the size higher on your SPIS.

     

    How many times does your spi slave give an interrupt? You should get two per SPI transaction.

     

    Kind regards,

    Håkon

  • sorry.. I had changed the buffer sizes since the time I attached the spis code.. here is the latest (it's the same as before except buffer sizes and may be a print or two)..

    I will check the register like you suggested but I can clearly see handler getting called dozens of times (from prints).. 

    pasting the code here (inserting it using code tag doesn't seem to work when I tried just now)..

    /*
    * Copyright (c) 2020 Nordic Semiconductor ASA
    *
    * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
    */

    #include <string.h>
    #include <zephyr.h>
    #include <stdlib.h>
    #include <drivers/spi.h>
    #include <nrfx.h>
    #include <nrfx_uarte.h>
    #include "nrfx_spis.h"

    #define PIN_SCK 10
    #define PIN_MOSI 11
    #define PIN_MISO 12
    #define PIN_CSN 13
    #define SPIS_NR 3

    nrfx_spis_t spis_t = NRFX_SPIS_INSTANCE(SPIS_NR);
    nrfx_spis_config_t spis_config_t = NRFX_SPIS_DEFAULT_CONFIG(PIN_SCK,PIN_MOSI,PIN_MISO,PIN_CSN);

    uint8_t rx_buffer[] = "IFMMP GSPN BMJWFDPS!";
    uint8_t tx_buffer[] = "Hello back AliveCor!";

    void spis_event_handler_t(nrfx_spis_evt_t const *p_event, void *p_context){
    printk("handler\n");
    int err;
    switch(p_event->evt_type){
    case NRFX_SPIS_XFER_DONE:
    printk("received %s(%d) and sending %s(%d)\n",rx_buffer, sizeof(rx_buffer),tx_buffer, sizeof(tx_buffer));
    err = nrfx_spis_buffers_set(&spis_t, tx_buffer, sizeof(tx_buffer), rx_buffer, sizeof(rx_buffer));
    if(err != NRFX_SUCCESS){
    printk("Error with setting.\n");
    }
    tx_buffer[0]++;
    break;
    case NRFX_SPIS_BUFFERS_SET_DONE:
    printk("buffers set\n");
    break;
    case NRFX_SPIS_EVT_TYPE_MAX:

    break;
    default:
    ;
    }
    }

    static void manual_isr_setup()
    {
    //IRQ_DIRECT_CONNECT(SPIM3_SPIS3_TWIM3_TWIS3_UARTE3_IRQn, 0, nrfx_spis_3_irq_handler, 0);
    //irq_enable(SPIM3_SPIS3_TWIM3_TWIS3_UARTE3_IRQn);
    IRQ_DIRECT_CONNECT(UARTE3_SPIM3_SPIS3_TWIM3_TWIS3_IRQn, 0, nrfx_spis_3_irq_handler, 0);
    irq_enable(UARTE3_SPIM3_SPIS3_TWIM3_TWIS3_IRQn);
    }

    void init_spis(){
    int err;

    //change mode
    spis_config_t.mode = NRF_SPIS_MODE_3;
    err = nrfx_spis_init(&spis_t,&spis_config_t,spis_event_handler_t,NULL);
    if(err != NRFX_SUCCESS){
    printk("nrfx_spis_init - Error. %x\n",err);
    } else {
    printk("SPIS started.\n");
    }
    }


    void spi_slave_test(void) {
    int err;
    init_spis();
    manual_isr_setup();

    printk("rx_buffer %s(%d) and tx_buffer %s(%d)\n",rx_buffer, sizeof(rx_buffer),tx_buffer, sizeof(tx_buffer));
    err = nrfx_spis_buffers_set(&spis_t, tx_buffer, sizeof(tx_buffer), rx_buffer, sizeof(rx_buffer));
    if(err != NRFX_SUCCESS){
    printk("nrfx_spis_buffers_set - Error. %x\n", err);
    }
    }


    void main(void)
    {
    printk("SPIS sample started \n\r");
    spi_slave_test();
    while (1) {
    //printk("alive \n");
    //k_sleep(Z_TIMEOUT_TICKS(50000));
    }
    }

Related