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: