Question: About SPIS of nRF54L15-DK


I am trying SPI Master/Slave communication between two nRF54L15-DK boards, using the nRF Connect SDK v2.8.0 and nRF54L15-DK 0.9.1 and 0.8.1. However, it seems that the SPIS (SPI Slave) is not working. I would appreciate your help.

For SPIM (SPI Master), I used "spi_bitbang" as a reference.


The `nrf54l15dk-nrf54l15.overlay` is written as follows:

---
```
&pinctrl {
spi21_default: spi21_default {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 1, 9)>,
<NRF_PSEL(SPIM_MOSI, 1, 10)>,
<NRF_PSEL(SPIM_MISO, 1, 11)>;
nordic,drive-mode = <NRF_DRIVE_H0H1>;
};
};

spi21_sleep: spi21_sleep {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 1, 9)>,
<NRF_PSEL(SPIM_MOSI, 1, 10)>,
<NRF_PSEL(SPIM_MISO, 1, 11)>;
low-power-enable;
};
};
};

&spi21 {
compatible = "nordic,nrf-spim";
status = "okay";
pinctrl-0 = <&spi21_default>;
pinctrl-1 = <&spi21_sleep>;
pinctrl-names = "default", "sleep";
cs-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
};

&uart30{
status = "disabled";
};
```
---

The `main.c` for SPIM is as follows:

---
```c
/*
* Copyright (c) 2021 Marc Reilly, Creative Product Design
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(app, LOG_LEVEL_INF);

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <stdio.h>
#include <string.h>
#include <zephyr/drivers/spi.h>

#define SPIBB_NODE DT_NODELABEL(spi21)

/*
* writes 5 9bit words, you can check the output with a logic analyzer
*/
void test_basic_write_9bit_words(const struct device *dev,
struct spi_cs_control *cs)
{
struct spi_config config;

config.frequency = 125000;
config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(9);
config.slave = 0;
config.cs = *cs;

uint16_t buff[5] = { 0x0101, 0x00ff, 0x00a5, 0x0000, 0x0102};
int len = 5 * sizeof(buff[0]);

struct spi_buf tx_buf = { .buf = buff, .len = len };
struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1 };

int ret = spi_write(dev, &config, &tx_bufs);

printf("basic_write_9bit_words; ret: %d\n", ret);
printf(" wrote %04x %04x %04x %04x %04x\n",
buff[0], buff[1], buff[2], buff[3], buff[4]);
}

/*
* A more complicated xfer, sends two words, then sends and receives another
* 3 words. Connect MOSI to MISO to test read
*/
void test_9bit_loopback_partial(const struct device *dev,
struct spi_cs_control *cs)
{
struct spi_config config;

config.frequency = 125000;
config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(9);
config.slave = 0;
config.cs = *cs;

enum { datacount = 5 };
uint16_t buff[datacount] = { 0x0101, 0x0102, 0x0003, 0x0004, 0x0105};
uint16_t rxdata[3];

const int stride = sizeof(buff[0]);

struct spi_buf tx_buf[2] = {
{.buf = buff, .len = (2) * stride},
{.buf = buff + (2), .len = (datacount - 2)*stride},
};
struct spi_buf rx_buf[2] = {
{.buf = 0, .len = (2) * stride},
{.buf = rxdata, .len = (datacount - 2) * stride},
};

struct spi_buf_set tx_set = { .buffers = tx_buf, .count = 2 };
struct spi_buf_set rx_set = { .buffers = rx_buf, .count = 2 };

int ret = spi_transceive(dev, &config, &tx_set, &rx_set);

printf("9bit_loopback_partial; ret: %d\n", ret);
printf(" tx (i) : %04x %04x\n", buff[0], buff[1]);
printf(" tx (ii) : %04x %04x %04x\n", buff[2], buff[3], buff[4]);
printf(" rx (ii) : %04x %04x %04x\n", rxdata[0], rxdata[1], rxdata[2]);
}

/*
* Tests 8 bit transfer at higher frequency, at this frequency there won't be
* any busy waits between clock edges, the rate is limited by gpio calls etc.
*/
void test_8bit_xfer(const struct device *dev, struct spi_cs_control *cs)
{
struct spi_config config;

config.frequency = 1000000;
config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8);
config.slave = 0;
config.cs = *cs;

enum { datacount = 5 };
uint8_t buff[datacount] = { 0x01, 0x02, 0x03, 0x04, 0x05};
uint8_t rxdata[datacount];

struct spi_buf tx_buf[1] = {
{.buf = buff, .len = datacount},
};
struct spi_buf rx_buf[1] = {
{.buf = rxdata, .len = datacount},
};

struct spi_buf_set tx_set = { .buffers = tx_buf, .count = 1 };
struct spi_buf_set rx_set = { .buffers = rx_buf, .count = 1 };

int ret = spi_transceive(dev, &config, &tx_set, &rx_set);

printf("8bit_loopback_partial; ret: %d\n", ret);
printf(" tx (i) : %02x %02x %02x %02x %02x\n",
buff[0], buff[1], buff[2], buff[3], buff[4]);
printf(" rx (i) : %02x %02x %02x %02x %02x\n",
rxdata[0], rxdata[1], rxdata[2], rxdata[3], rxdata[4]);
}

int main(void)
{
const struct device *const dev = DEVICE_DT_GET(SPIBB_NODE);

if (!device_is_ready(dev)) {
printk("%s: device not ready.\n", dev->name);
return 0;
}

struct spi_cs_control cs_ctrl = (struct spi_cs_control){
.gpio = GPIO_DT_SPEC_GET(SPIBB_NODE, cs_gpios),
.delay = 0u,
};

/*
* Loop through the various demo functions, the delays make it easier to
* locate on a scope/analyzer, the longer delay at the end helps discern
* where the pattern repeats.
*/
while (1) {
// test_basic_write_9bit_words(dev, &cs_ctrl);
k_sleep(K_MSEC(200));

// test_9bit_loopback_partial(dev, &cs_ctrl);
k_sleep(K_MSEC(200));

test_8bit_xfer(dev, &cs_ctrl);
k_sleep(K_MSEC(1000));
}
return 0;
}
```
---

I confirmed that the SPIM (SPI Master) communication works correctly when connecting the MISO and MOSI pins of the SPIM on the nRF54L15-DK.

Terminal output:

```
8bit_loopback_partial; ret: 0
tx (i) : 01 02 03 04 05
rx (i) : 01 02 03 04 05
8bit_loopback_partial; ret: 0
tx (i) : 01 02 03 04 05
rx (i) : 01 02 03 04 05
8bit_loopback_partial; ret: 0
tx (i) : 01 02 03 04 05
rx (i) : 01 02 03 04 05
```


Next, regarding SPIS, I am using the DK with version 0.9.1. I referred to the example at:

[https://github.com/simon-iversen/ncs_samples/tree/master/spi_slave_and_master_ncs_v200/spi_slave](github.com/.../spi_slave)

I wrote the `nrf54l15dk-nrf54l15.overlay` file as follows:

```dts
&pinctrl {
spis22_default: spis22_default {
group1 {
psels = <NRF_PSEL(SPIS_SCK, 2, 6)>,
<NRF_PSEL(SPIS_MOSI, 2, 9)>,
<NRF_PSEL(SPIS_MISO, 2, 8)>,
<NRF_PSEL(SPIS_CSN, 2, 10)>;
};
};

spis22_sleep: spis22_sleep {
group1 {
psels = <NRF_PSEL(SPIS_SCK, 2, 6)>,
<NRF_PSEL(SPIS_MOSI, 2, 9)>,
<NRF_PSEL(SPIS_MISO, 2, 8)>,
<NRF_PSEL(SPIS_CSN, 2, 10)>;
low-power-enable;
};
};
};

/* uart20 is being used for serial communication, so use spi22 */

&spi22 {
compatible = "nordic,nrf-spis";
status = "okay";
pinctrl-0 = <&spis22_default>;
pinctrl-1 = <&spis22_sleep>;
pinctrl-names = "default", "sleep";
def-char = <0x00>;
/delete-property/rx-delay-supported;
/delete-property/rx-delay;
};
```

I wrote the `main.c` file as follows:

```c
/*
* Copyright (c) 2012-2014 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(app, LOG_LEVEL_INF);

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <stdio.h>
#include <string.h>
#include <zephyr/drivers/spi.h>

#define DT_DRV_COMPAT nordic_nrf_spis

static const struct spi_config spi_cfg = {
.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_OP_MODE_SLAVE,
.frequency = 1000000,
.slave = 1,
};

const struct device *spi_dev;

//#define SPIS_STACK_SIZE 500
//#define SPIS_PRIORITY 5

//extern void spis_thread(void *, void *, void *);

//#define MY_SPI_SLAVE DT_LABEL(DT_NODELABEL(spi22))
#define MY_SPI_SLAVE DT_NODELABEL(spi22)

//K_THREAD_DEFINE(spis_tid, SPIS_STACK_SIZE, spis_thread, NULL, NULL, NULL,
// SPIS_PRIORITY, 0, 0);

static void spi_init(void)
{
spi_dev = DEVICE_DT_GET(MY_SPI_SLAVE);; //device_get_binding(MY_SPI_SLAVE);//DT_LABEL(DT_DRV_INST(0)));

//printk("Device name: %s\n", DT_LABEL(DT_DRV_INST(0)));

if (spi_dev == NULL) {
printk("SPI Device is Null!");
//printk("Could not get %s device\n", DT_LABEL(DT_DRV_INST(0)));
return;
}

/*printk("SPI CSN %d, MISO %d, MOSI %d, CLK %d\n",
DT_PROP(DT_DRV_INST(0), csn_pin),
DT_PROP(DT_DRV_INST(0), miso_pin),
DT_PROP(DT_DRV_INST(0), mosi_pin),
DT_PROP(DT_DRV_INST(0), sck_pin));*/
}

void spi_test_transceive(void)
{
int err;
static uint8_t tx_buffer[32] = { 's', 'p', 'i', 's', 'l',
'a', 'v', 'e', '\n' };
static uint8_t rx_buffer[32];

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 < 0) {
printk("SPI error: %d\n", err);
} else {
printk("byte received: %d\n", err);
printk("TX buffer [0]: %x\n", tx_buffer[0]);
printk("RX buffer [0]: %x\n", rx_buffer[0]);
tx_buffer[0]++;
}
}

//extern void spis_thread(void *unused1, void *unused2, void *unused3)
//{
// spi_init();
//
// while (1) {
// spi_test_transceive();
// }
//}

void main(void)
{
printk("SPIS Example\n");
//printk("Add any handling you'd like in the main thread\n");
//printk("SPIS handling will be performed in an own thread\n");

spi_init();

while (1) {
spi_test_transceive();
}
}
```

The wiring between the two DK boards for SPIM / SPIS is as follows:

- **SPIM_SCK (1, 9)** ----------- **SPIS_SCK (2, 6)**
- **SPIM_MOSI (1, 10)** ----------- **SPIS_MISO (2, 8)**
- **SPIM_MISO (1, 11)** ----------- **SPIS_MOSI (2, 9)**
- **SPIM_CS (1, 12)** ----------- **SPIS_CSN (2, 10)**


Best regards.

Parents
  • Hi Naeem.

    Sorry for the late reply.

    The SPI Slave was using Port 2, so I changed spi22 to spi00 in the overlay file and it worked.

    Thank you.
    Best regards.

  • I also changed like you to SPI00 and the same pins.

    only change is this:

    static const struct spi_config spi_slave_cfg = {
    	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
    				 SPI_MODE_CPHA | SPI_OP_MODE_SLAVE,
    	.frequency = 1000000,
    	.slave = 0,
    };

    but beside that i also want to use signals.

    My code goes like this:

    static int spi_slave_check_for_message(void)
    {
    	int signaled, result;
    	k_poll_signal_check(&spi_slave_done_sig, &signaled, &result);
    	if(signaled != 0){
            for(int i = 0; i < result; i++){
                LOG_INF("rx[%d] = %d", i, s_rx_buf[i]);
            }
    		return result;
    	}
    	else return -1;
    }
    
    //after all init in main
    //reset the signal, that is all in main
    k_poll_signal_reset(&spi_slave_done_sig);
    
    int ret = spi_transceive_signal(spi_slave_dev, &spi_slave_cfg, &tx_set, &tx_set, &spi_slave_done_sig);
    
    //end of main
    
    //function that is called in a loop
    void function(){
     int ret = spi_slave_check_for_message();
    
        if (ret < 0){
            LOG_DBG("SPI check for message result %d", ret);
            return;
        }
        LOG_INF("message result %d", ret);
        LOG_INF("buf size is %d", strlen(rx_set.buffers->buf));
        
        //process data and put data in tx buf if needed
        
            LOG_INF("reseting the signal");
        // Reset signal
    	k_poll_signal_reset(&spi_slave_done_sig);
        LOG_INF("signal reset");
        //start new transaction
    	spi_transceive_signal(spi_slave_dev, &spi_slave_cfg, &tx_set, &rx_set, &spi_slave_done_sig);
    
        LOG_INF("new spi function called");
        
    }
    
    
        
        
    


    now the main problem is data that should be inside rx_buffer is 0. But when checking for message i get that result variable is above zero so there was some data inside. Using debugger i also have not found any data inside rx_buffer so if somehow you know what is going on I would be grateful.

Reply
  • I also changed like you to SPI00 and the same pins.

    only change is this:

    static const struct spi_config spi_slave_cfg = {
    	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
    				 SPI_MODE_CPHA | SPI_OP_MODE_SLAVE,
    	.frequency = 1000000,
    	.slave = 0,
    };

    but beside that i also want to use signals.

    My code goes like this:

    static int spi_slave_check_for_message(void)
    {
    	int signaled, result;
    	k_poll_signal_check(&spi_slave_done_sig, &signaled, &result);
    	if(signaled != 0){
            for(int i = 0; i < result; i++){
                LOG_INF("rx[%d] = %d", i, s_rx_buf[i]);
            }
    		return result;
    	}
    	else return -1;
    }
    
    //after all init in main
    //reset the signal, that is all in main
    k_poll_signal_reset(&spi_slave_done_sig);
    
    int ret = spi_transceive_signal(spi_slave_dev, &spi_slave_cfg, &tx_set, &tx_set, &spi_slave_done_sig);
    
    //end of main
    
    //function that is called in a loop
    void function(){
     int ret = spi_slave_check_for_message();
    
        if (ret < 0){
            LOG_DBG("SPI check for message result %d", ret);
            return;
        }
        LOG_INF("message result %d", ret);
        LOG_INF("buf size is %d", strlen(rx_set.buffers->buf));
        
        //process data and put data in tx buf if needed
        
            LOG_INF("reseting the signal");
        // Reset signal
    	k_poll_signal_reset(&spi_slave_done_sig);
        LOG_INF("signal reset");
        //start new transaction
    	spi_transceive_signal(spi_slave_dev, &spi_slave_cfg, &tx_set, &rx_set, &spi_slave_done_sig);
    
        LOG_INF("new spi function called");
        
    }
    
    
        
        
    


    now the main problem is data that should be inside rx_buffer is 0. But when checking for message i get that result variable is above zero so there was some data inside. Using debugger i also have not found any data inside rx_buffer so if somehow you know what is going on I would be grateful.

Children
  • Hi, Ivan.

    I wrote the SPIS code like this:

    main.c

    /*
     * Copyright (c) 2016 Intel Corporation
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/spi.h>
    
    
    #define MY_SPI_SLAVE  DT_NODELABEL(spi00)
    
    // SPIS functionality
    const struct device *spis_dev;
    static struct k_poll_signal spi_slave_done_sig = K_POLL_SIGNAL_INITIALIZER(spi_slave_done_sig);
    
    // .operation = SPI_TRANSFER_MSB | SPI_WORD_SET(8), // spi mode 0
    // .operation = SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_MODE_CPHA, // spi mode 1
    // .operation = SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_MODE_CPOL, // spi mode 2
    // .operation = SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_MODE_CPOL | SPI_MODE_CPHA, // spi mode 3
    
    static const struct spi_config spi_slave_cfg = {
    	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_OP_MODE_SLAVE,
    	.frequency = 4000000,
    	.slave = 1,
    };
    
    static void spi_slave_init(void)
    {
    	spi_slave_dev = DEVICE_DT_GET(MY_SPI_SLAVE);
    	if(!device_is_ready(spi_slave_dev)) {
    		printk("SPI slave device not ready!\n");
    	}
    }
    
    static uint8_t slave_tx_buffer[10];
    static uint8_t slave_rx_buffer[10];
    static int spi_slave_write_test_msg(void)
    {
    	static uint8_t counter = 0;
    
    
    	const struct spi_buf s_tx_buf = {
    		.buf = slave_tx_buffer,
    		.len = sizeof(slave_tx_buffer)
    	};
    	const struct spi_buf_set s_tx = {
    		.buffers = &s_tx_buf,
    		.count = 1
    	};
    
    	struct spi_buf s_rx_buf = {
    		.buf = slave_rx_buffer,
    		.len = sizeof(slave_rx_buffer),
    	};
    	const struct spi_buf_set s_rx = {
    		.buffers = &s_rx_buf,
    		.count = 1
    	};
    
    	// Update the TX buffer with a rolling counter
    	slave_tx_buffer[1] = counter++;
    	//printk("SPI SLAVE TX: 0x%.2x, 0x%.2x\n", slave_tx_buffer[0], slave_tx_buffer[1]);
    
    	// Reset signal
    	k_poll_signal_reset(&spi_slave_done_sig);
    	
    	// Start transaction
    	int error = spi_transceive_signal(spi_slave_dev, &spi_slave_cfg, &s_tx, &s_rx, &spi_slave_done_sig);
    	if(error != 0){
    		printk("SPI slave transceive error: %i\n", error);
    		return error;
    	}
    	return 0;
    }
    
    static int spi_slave_check_for_message(void)
    {
        int signaled = 0;
        int result = 0;
        
    	k_poll_signal_check(&spi_slave_done_sig, &signaled, &result);
        
    	if(signaled != 0){
    		return 0;
        }else{
            return -1;
        }
    }
    
    /*
     * A build error on this line means your board is unsupported.
     * See the sample documentation for information on how to fix this.
     */
    //static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
    
    int main(void)
    {
    //	int ret;
    
    	spi_slave_init();
    
    	printk("SPI slave example started\n");
    	
    	spi_slave_write_test_msg();
    
    	while (1) {
    
    		if(spi_slave_check_for_message() == 0){
    			// Print the last received data
    			//printk("SPI SLAVE RX: 0x%.2x, 0x%.2x\n", slave_rx_buffer[0], slave_rx_buffer[1]);
    			printk("SPI SLAVE RX: %c%c%c%c%c%c\n", slave_rx_buffer[0], slave_rx_buffer[1],
    			slave_rx_buffer[2], slave_rx_buffer[3],
    			slave_rx_buffer[4], slave_rx_buffer[5]);
    
    			// Prepare the next SPI slave transaction
    			spi_slave_write_test_msg();
    		}
    	}
    
    	return 0;
    }
    

    prj.conf

    CONFIG_GPIO=y
    
    CONFIG_SPI=y
    CONFIG_SPI_ASYNC=y
    
    CONFIG_SPI_SLAVE=y
    

    nrf54l15dk-nrf54l15.overlay

    &pinctrl {
     
            spis00_default: spis00_default {
    		group1 {
    			psels = <NRF_PSEL(SPIS_SCK, 2, 6)>,
    				<NRF_PSEL(SPIS_MOSI, 2, 9)>,
    				<NRF_PSEL(SPIS_MISO, 2, 8)>,
    				<NRF_PSEL(SPIS_CSN,  2, 10)>;
    
    		};
    	};
    
    	spis00_sleep: spis00_sleep {
    		group1 {
    			psels = <NRF_PSEL(SPIS_SCK, 2, 6)>,
    				<NRF_PSEL(SPIS_MOSI, 2, 9)>,
    				<NRF_PSEL(SPIS_MISO, 2, 8)>,
    				<NRF_PSEL(SPIS_CSN,  2, 10)>;
                            low-power-enable;
    		};
    	};
    
    
    };
    
    /* uart20 is being used for serial communication, so use spi21*/
    &spi00 {
        /delete-property/rx-delay-supported;
        /delete-property/rx-delay;
        status = "okay";
        compatible = "nordic,nrf-spis";
        pinctrl-0 = <&spis00_default>;
        pinctrl-1 = <&spis00_sleep>;
        pinctrl-names = "default", "sleep";
        def-char = <0x00>;
    };
    
    
    /*
    &uart20 {
        status = "disabled";
    };
    */
    

    The SPIM has been changed from the nRF54L15 to the nRF52840, and the SPI sample from the nRF5 SDK has been modified accordingly.

    Best regards.

    Kenta.

Related