Unable to get asynchronous behavior when using two SPI controllers simultaneously.

Hello,

I am running into an issue when I try to use two SPI controllers simultaneously on the nrf5340dk. I am using ncs v2.6.0.

What I am trying to achieve, is a system that will use two separate SPI controllers, and be able to run them in a non-blocking fashion. For example, I want to be able to start a data transmission on SPI4, and then start another data transmission on SPI3 before SPI4 is finished with its transmission (before SPI4's CS line goes back to high).

I tried to achieve this using the function spi_transcieve_signal, because the documentation says that it is asynchronous. I have two issues when I try this:

  1. The Chip-select line does not remain active-low for the entire duration of the data transmission; it goes back high before all of the data is sent.
  2. I am not seeing the asynchronous behavior that I was expecting; the second SPI controller waits until the first one is finished before it starts its own transmission.

Here is a screenshot of the signals to show these two issues, measured using a logic analyzer.

Here is my source code:

/* main.c */

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

LOG_MODULE_REGISTER(spi_example);

#define SPI1_NODE DT_NODELABEL(spi_0)
#define SPI2_NODE DT_NODELABEL(spi_1)

static const struct spi_dt_spec spispec1 = SPI_DT_SPEC_GET(SPI1_NODE, SPI_WORD_SET(8) | SPI_TRANSFER_MSB, 0);
static const struct spi_dt_spec spispec2 = SPI_DT_SPEC_GET(SPI2_NODE, SPI_WORD_SET(8) | SPI_TRANSFER_MSB, 0);

int main(void)
{
    if (!device_is_ready(spispec1.bus)) {
        LOG_ERR("SPI1 device not ready");
        return;
    }
    LOG_INF("SPI1 device ready");

    if (!device_is_ready(spispec2.bus)) {
        LOG_ERR("SPI2 device not ready");
        return;
    }
    LOG_INF("SPI2 device ready");

    uint8_t tx_buffer1[3] = {0x01, 0x80, 0x00}; // Example command
    uint8_t rx_buffer1[3] = {0};

    uint8_t tx_buffer2[1] = {0x55}; // Example command
    uint8_t rx_buffer2[1] = {0};

    // initialize buffers
    struct spi_buf tx_buf1 = {
        .buf = tx_buffer1,
        .len = 3,
    };
    struct spi_buf_set tx1 = {
        .buffers = &tx_buf1,
        .count = 1,
    };
    struct spi_buf tx_buf2 = {
        .buf = tx_buffer2,
        .len = 1,
    };
    struct spi_buf_set tx2 = {
        .buffers = &tx_buf2,
        .count = 1,
    };
    struct spi_buf rx_buf1 = {
        .buf = rx_buffer1,
        .len = 3,
    };
    struct spi_buf_set rx1 = {
        .buffers = &rx_buf1,
        .count = 1,
    };
    struct spi_buf rx_buf2 = {
        .buf = rx_buffer2,
        .len = 1,
    };
    struct spi_buf_set rx2 = {
        .buffers = &rx_buf2,
        .count = 1,
    };


    while (1) {

        int ret1;
        int ret2;



        ret1 = spi_transceive_signal(spispec1.bus, &(spispec1.config), &tx1, &rx1, NULL);

        ret2 = spi_transceive_signal(spispec2.bus, &(spispec2.config), &tx2, &rx2, NULL);


        if (ret1) {
            LOG_ERR("SPI1 transceive failed: %d", ret1);
        }
        if (ret2) {
            LOG_ERR("SPI2 transceive failed: %d", ret2);
        }

        

        k_msleep(100);
    }

    return 0;
}

I have tried using both spi_transceive_signal and spi_transceive_async with no success. 

And my overlay file:

/* nrf5340dk_nrf5340_cpuapp.overlay */

// Disable unused peripheral controllers
&i2c0 { status = "disabled"; };
&i2c1 { status = "disabled"; };
&spi1 { status = "disabled"; };
&spi0 { status = "disabled"; };
&spi2 { status = "disabled"; };

// SPI0 configuration
&spi3 {
    compatible = "nordic,nrf-spim";
    status = "okay";
    pinctrl-0 = <&spi0_default>;
    pinctrl-1 = <&spi0_sleep>;
    pinctrl-names = "default", "sleep";

    cs-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>; // CS pin configuration

    spi_0: spi_0@0 {
        compatible = "bosch,bme280";
        reg = <0>;
        spi-max-frequency = <8000000>; 
    };
};

// SPI1 configuration
&spi4 {
    compatible = "nordic,nrf-spim";
    status = "okay";
    pinctrl-0 = <&spi1_default>;
    pinctrl-1 = <&spi1_sleep>;
    pinctrl-names = "default", "sleep";

    cs-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>; // CS pin configuration

    spi_1: spi_1@0 {
        compatible = "bosch,bme280";
        reg = <0>;
        spi-max-frequency = <8000000>; 
    };
};

&pinctrl {
    spi0_default: spi0_default {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 6)>,   // SCK on P0.06
                    <NRF_PSEL(SPIM_MOSI, 0, 25)>, // MOSI on P0.25
                    <NRF_PSEL(SPIM_MISO, 0, 7)>;  // MISO on P0.07
        };
    };
    spi0_sleep: spi0_sleep {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 6)>,
                    <NRF_PSEL(SPIM_MOSI, 0, 25)>,
                    <NRF_PSEL(SPIM_MISO, 0, 7)>;
            low-power-enable;
        };
    };
    spi1_default: spi1_default {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 8)>,   // SCK on P0.08
                    <NRF_PSEL(SPIM_MOSI, 0, 9)>, // MOSI on P0.09
                    <NRF_PSEL(SPIM_MISO, 0, 10)>;  // MISO on P0.10
        };
    };
    spi1_sleep: spi1_sleep {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 8)>,
                    <NRF_PSEL(SPIM_MOSI, 0, 9)>,
                    <NRF_PSEL(SPIM_MISO, 0, 10)>;
            low-power-enable;
        };
    };
};

Within my prj.conf, I believe that I have all of the necessary configurations to use SPI asynchronously:

CONFIG_SPI=y
CONFIG_LOG=y

CONFIG_SPI_ASYNC=y
CONFIG_POLL=y
Any help to resolve this issue would be greatly appreciated.
Thanks,
Taylor
Related