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.