Missing transactions on SPI slave

Hi,

We are using two nRF54l15-PDF to communicate as SPI, one is in master mode and one is in slave mode. The master device sends a simple array of bytes, the first one being incremented at each transaction. The other one simply receive the data using the blocking spi_read_dt function.

The slave receives the data correctly exactly when the master writes, we can see it by having two RTT connected at the same time on each. However, it looks like the slave can either lose a message or get the same one between successives calls to spi_read_dt function.

Examples shown:

master

00> [00:57:45.720,154] <inf> main: sending a message with byte 0xb6
00> [00:57:50.720,554] <inf> main: sending a message with byte 0xb7
00> [00:57:55.720,954] <inf> main: sending a message with byte 0xb8
00> [00:58:00.721,354] <inf> main: sending a message with byte 0xb9
00> [00:58:05.721,754] <inf> main: sending a message with byte 0xba
00> [00:58:10.722,154] <inf> main: sending a message with byte 0xbb
00> [00:58:15.722,554] <inf> main: sending a message with byte 0xbc

slave

[00:04:24.112,272] <inf> main: first bytes received: 0xb6, 0x0
[00:04:24.112,422] <inf> main: client waiting for data...
[00:04:29.112,578] <inf> main: first bytes received: 0xb6, 0x0
[00:04:29.112,729] <inf> main: client waiting for data...
[00:04:34.112,885] <inf> main: first bytes received: 0xb8, 0x0
[00:04:34.113,035] <inf> main: client waiting for data...
[00:04:39.113,191] <inf> main: first bytes received: 0xb8, 0x0
[00:04:39.113,341] <inf> main: client waiting for data...
[00:04:44.113,497] <inf> main: first bytes received: 0xba, 0x0
[00:04:44.113,646] <inf> main: client waiting for data...
[00:04:49.113,804] <inf> main: first bytes received: 0xba, 0x0
[00:04:49.113,954] <inf> main: client waiting for data...
[00:04:54.114,111] <inf> main: first bytes received: 0xbc, 0x0
[00:04:54.114,261] <inf> main: client waiting for data...

This is the master code

#include <stdint.h>
#include <string.h>

#include <zephyr/drivers/spi.h>
#include <zephyr/logging/log.h>

#define LEN     130

#define M_NODE  DT_NODELABEL(sub)
#define M_OP    SPI_WORD_SET(8) | SPI_TRANSFER_LSB | SPI_OP_MODE_MASTER

static const struct spi_dt_spec spi = SPI_DT_SPEC_GET(M_NODE, M_OP, 0);

LOG_MODULE_REGISTER(main);

int main(void)
{
    uint8_t rx_data[LEN], tx_data[LEN];
    uint8_t byte = 0;
    int rv;

    struct spi_buf rx_buf = {
        .buf = rx_data,
        .len = sizeof (rx_data)
    };
    struct spi_buf_set rx_set = {
        .buffers = &rx_buf,
        .count = 1
    };

    struct spi_buf tx_buf = {
        .buf = tx_data,
        .len = sizeof (tx_data)
    };
    struct spi_buf_set tx_set = {
        .buffers = &tx_buf,
        .count = 1
    };

    if (!spi_is_ready_dt(&spi)) {
        LOG_ERR("SPI device not ready");
        return 1;
    }

    for (;;) {
        memset(rx_data, 0, sizeof (rx_data));
        memset(tx_data, 0, sizeof (tx_data));

        tx_data[0] = ++byte;

        LOG_INF("sending a message with byte 0x%x", (unsigned)tx_data[0]);
        rv = spi_transceive_dt(&spi, &tx_set, &rx_set);

        if (rv != 0)
            LOG_WRN("SPI error: %s", strerror(rv));

        k_msleep(5000);
    }
}

And this is the slave code

#include <stdint.h>
#include <string.h>

#include <zephyr/drivers/spi.h>
#include <zephyr/logging/log.h>

#define LEN     130

#define S_NODE  DT_NODELABEL(main)
#define S_OP    SPI_WORD_SET(8) | SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE

static const struct spi_dt_spec spi = SPI_DT_SPEC_GET(S_NODE, S_OP, 0);

LOG_MODULE_REGISTER(main);

int main(void)
{
    uint8_t rx_data[LEN], tx_data[LEN] = {0xaa, 0xbb};
    int rv;

    struct spi_buf rx_buf = {
        .buf = rx_data,
        .len = sizeof (rx_data)
    };
    struct spi_buf_set rx_set = {
        .buffers = &rx_buf,
        .count = 1
    };

    struct spi_buf tx_buf = {
        .buf = tx_data,
        .len = sizeof (tx_data)
    };
    struct spi_buf_set tx_set = {
        .buffers = &tx_buf,
        .count = 1
    };

    if (!spi_is_ready_dt(&spi)) {
        LOG_ERR("SPI device not ready");
        return 1;
    }

    for (;;) {
        memset(rx_data, 0, sizeof (rx_data));

        LOG_INF("client waiting for data...");
        rv = spi_transceive_dt(&spi, &tx_set, &rx_set);

        if (rv < 0 && rv != -EAGAIN)
            LOG_WRN("SPI error: %s", strerror(rv));
        else
            LOG_INF("first bytes received: 0x%x, 0x%x", (unsigned)rx_data[0], rx_data[1]);
    }
}

Finally, master and slave devicetree overlays respectively:

master

/ {
    aliases {
        spi = &spi;
    };
};

&pinctrl {
    spi_default: spi_default {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 2, 6)>,
                    <NRF_PSEL(SPIM_MOSI, 2, 8)>,
                    <NRF_PSEL(SPIM_MISO, 2, 9)>;
            bias-pull-down;
        };
    };

    spi_sleep: spi_sleep {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 2, 6)>,
                    <NRF_PSEL(SPIM_MOSI, 2, 8)>,
                    <NRF_PSEL(SPIM_MISO, 2, 9)>;
            low-power-enable;
        };
    };
};

spi: &spi00 {
    compatible = "nordic,nrf-spim";
    status = "okay";
    pinctrl-0 = <&spi_default>;
    pinctrl-1 = <&spi_sleep>;
    pinctrl-names = "default", "sleep";
    cs-gpios = <&gpio2 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
    sub: sub@0 {
        status = "okay";
        compatible = "vnd,spi-device";
        reg = <0>;
        spi-max-frequency = <10000000>;
        label = "sub";
    };
};

&uart20 {
    status = "disabled";
};

slave

/ {
    aliases {
        spi = &spidev;
    };
};

&pinctrl {
    spidev_default: spidev_default {
        group1 {
            psels = <NRF_PSEL(SPIS_MOSI, 1, 4)>,
                    <NRF_PSEL(SPIS_MISO, 1, 6)>,
                    <NRF_PSEL(SPIS_SCK, 1, 11)>,
                    <NRF_PSEL(SPIS_CSN, 1, 12)>;
        };
    };

    spidev_sleep: spidev_sleep {
        group1 {
            psels = <NRF_PSEL(SPIS_MOSI, 1, 4)>,
                    <NRF_PSEL(SPIS_MISO, 1, 6)>,
                    <NRF_PSEL(SPIS_SCK, 1, 11)>,
                    <NRF_PSEL(SPIS_CSN, 1, 12)>;
            low-power-enable;
        };
    };
};

spidev: &spi20 {
    /delete-property/ rx-delay;
    /delete-property/ rx-delay-supported;

    status = "okay";
    compatible = "nordic,nrf-spis";
    pinctrl-0 = <&spidev_default>;
    pinctrl-1 = <&spidev_sleep>;
    pinctrl-names = "default", "sleep";
    def-char = <0x00>;
    main: main@0 {
        compatible = "vnd,spi-device";
        label = "main";
        reg = <0>;
        spi-max-frequency = <10000000>;
        status = "okay";
    };
};

&uart20 {
    status = "disabled";
};

Are we missing something obvious? Is this behavior caused by the SPI experimental slave support in zephyr? I also have seen that CS GPIO control is not supported by the nRF slave SPI driver, but CSN is used though.

Parents Reply Children
No Data
Related