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:
- 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.
- 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: