SPI example for nRF52833 with nRF Connect SDK 2.3.0

Dear Support Team,

I just started evaluating a nRF52833 DK board with the nRF Connect SDK 2.3.0. At this point I would like to start playing with the SPI but unfortunately I can't find any working example that I could use.

Could you please point me to a working example without using the deprecated configuration parameters? 

Thank you very much in advance and all the best,

Viktor

Parents
  • Hi Viktor,

    One of my colleagues has made an SPI sample in v2.1.0 that you can find here: https://github.com/too1/ncs-spi-master-slave-example. This is using pinctrl and should not have any deprecated configurations.
    You need to make a few changes to the sample to migrate it to v2.3.0, such as adding the zephyr/ prefix to the include files, changing zephyr.h to kernel.h, and replacing spi_transceive_async() with spi_transceive_signal(). See the diff below for which changes to make.

    diff --git a/main.c b/main_v2-3-0.c
    index 2dc4978..11c6138 100644
    --- a/main.c
    +++ b/main_v2-3-0.c
    @@ -4,11 +4,11 @@
      * SPDX-License-Identifier: Apache-2.0
      */
    
    -#include <zephyr.h>
    -#include <device.h>
    -#include <devicetree.h>
    -#include <drivers/gpio.h>
    -#include <drivers/spi.h>
    +#include <zephyr/kernel.h>
    +#include <zephyr/device.h>
    +#include <zephyr/devicetree.h>
    +#include <zephyr/drivers/gpio.h>
    +#include <zephyr/drivers/spi.h>
    
     /* 1000 msec = 1 sec */
     #define SLEEP_TIME_MS   1000
    @@ -80,7 +80,7 @@ static int spi_write_test_msg(void)
            k_poll_signal_reset(&spi_done_sig);
    
            // Start transaction
    -       int error = spi_transceive_async(spi_dev, &spi_cfg, &tx, &rx, &spi_done_sig);
    +       int error = spi_transceive_signal(spi_dev, &spi_cfg, &tx, &rx, &spi_done_sig);
            if(error != 0){
                    printk("SPI transceive error: %i\n", error);
                    return error;
    @@ -147,7 +147,7 @@ static int spi_slave_write_test_msg(void)
            k_poll_signal_reset(&spi_slave_done_sig);
    
            // Start transaction
    -       int error = spi_transceive_async(spi_slave_dev, &spi_slave_cfg, &s_tx, &s_rx, &spi_slave_done_sig);
    +       int error = spi_transceive_signal(spi_slave_dev, &spi_slave_cfg, &s_tx, &s_rx, &spi_slave_done_sig);
            if(error != 0){
                    printk("SPI slave transceive error: %i\n", error);
                    return error;

    Best regards,
    Marte

Reply
  • Hi Viktor,

    One of my colleagues has made an SPI sample in v2.1.0 that you can find here: https://github.com/too1/ncs-spi-master-slave-example. This is using pinctrl and should not have any deprecated configurations.
    You need to make a few changes to the sample to migrate it to v2.3.0, such as adding the zephyr/ prefix to the include files, changing zephyr.h to kernel.h, and replacing spi_transceive_async() with spi_transceive_signal(). See the diff below for which changes to make.

    diff --git a/main.c b/main_v2-3-0.c
    index 2dc4978..11c6138 100644
    --- a/main.c
    +++ b/main_v2-3-0.c
    @@ -4,11 +4,11 @@
      * SPDX-License-Identifier: Apache-2.0
      */
    
    -#include <zephyr.h>
    -#include <device.h>
    -#include <devicetree.h>
    -#include <drivers/gpio.h>
    -#include <drivers/spi.h>
    +#include <zephyr/kernel.h>
    +#include <zephyr/device.h>
    +#include <zephyr/devicetree.h>
    +#include <zephyr/drivers/gpio.h>
    +#include <zephyr/drivers/spi.h>
    
     /* 1000 msec = 1 sec */
     #define SLEEP_TIME_MS   1000
    @@ -80,7 +80,7 @@ static int spi_write_test_msg(void)
            k_poll_signal_reset(&spi_done_sig);
    
            // Start transaction
    -       int error = spi_transceive_async(spi_dev, &spi_cfg, &tx, &rx, &spi_done_sig);
    +       int error = spi_transceive_signal(spi_dev, &spi_cfg, &tx, &rx, &spi_done_sig);
            if(error != 0){
                    printk("SPI transceive error: %i\n", error);
                    return error;
    @@ -147,7 +147,7 @@ static int spi_slave_write_test_msg(void)
            k_poll_signal_reset(&spi_slave_done_sig);
    
            // Start transaction
    -       int error = spi_transceive_async(spi_slave_dev, &spi_slave_cfg, &s_tx, &s_rx, &spi_slave_done_sig);
    +       int error = spi_transceive_signal(spi_slave_dev, &spi_slave_cfg, &s_tx, &s_rx, &spi_slave_done_sig);
            if(error != 0){
                    printk("SPI slave transceive error: %i\n", error);
                    return error;

    Best regards,
    Marte

Children
  • Hi Marte!

    Thank you very much for the quick reply!

    So, for example if I want to communicate with an external sensor (accelerometer) would this be correct config?

    Overlay:

    &pinctrl {
    	spi1_default: spi1_default {
    		group1 {
    			psels = <NRF_PSEL(SPIM_SCK, 0, 31)>,
    					<NRF_PSEL(SPIM_MOSI, 0, 30)>,
    					<NRF_PSEL(SPIM_MISO, 0, 29)>;
    		};
    	};
    
    	spi1_sleep: spi1_sleep {
    		group1 {
    			psels = <NRF_PSEL(SPIM_SCK, 0, 31)>,
    					<NRF_PSEL(SPIM_MOSI, 0, 30)>,
    					<NRF_PSEL(SPIM_MISO, 0, 29)>;
    			low-power-enable;
    		};
    	};
    };
    
    my_spi_master: &spi1 {
    	compatible = "nordic,nrf-spi";
    	status = "okay";
    	pinctrl-0 = <&spi1_default>;
    	pinctrl-1 = <&spi1_sleep>;
    	cs-gpios = <&gpio0 28 GPIO_ACTIVE_LOW>;
    	reg_my_spi_master: spi-dev-a@0 {
    		reg = <0>;
    	};
    };

    Initialize SPI master:

    #define MY_SPI_MASTER DT_NODELABEL(my_spi_master)
    
    // SPI master functionality
    const struct device *spi_dev;
    static struct k_poll_signal spi_done_sig = K_POLL_SIGNAL_INITIALIZER(spi_done_sig);
    
    struct spi_cs_control spim_cs = {
    	.gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(reg_my_spi_master)),
    	.delay = 0,
    };
    
    
    static void spi_init(void)
    {
    	spi_dev = DEVICE_DT_GET(MY_SPI_MASTER);
    	if(!device_is_ready(spi_dev)) {
    		printk("SPI master device not ready!\n");
    	}
    	if(!device_is_ready(spim_cs.gpio.port)){
    		printk("SPI master chip select device not ready!\n");
    	}
    }
    
    static const struct spi_config spi_cfg = {
    	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
    				 SPI_MODE_CPOL | SPI_MODE_CPHA,
    	.frequency = 4000000,
    	.slave = 0,
    	.cs = &spim_cs,
    };
    
    
    

    Where can I find documentation of the "spi_transceive_signal" function? The IDE can't find anything (warning: implicit declaration of function 'spi_transceive_signal')

    Is it correct that I should rather use the following function to read/write registers of the accelerometer in this case?:

    int error = spi_transceive_dt(spi_dev, &tx, &rx);

    All the best,

    Viktor

  • Hi Marte!

    I managed to make the communication to the sensor work! I post the solution here so others can use it as well.

    Thanks again for the help!

    Overlay:

    &pinctrl {
    	spi1_default: spi1_default {
    		group1 {
    			psels = <NRF_PSEL(SPIM_SCK, 0, 31)>,
    					<NRF_PSEL(SPIM_MOSI, 0, 30)>,
    					<NRF_PSEL(SPIM_MISO, 0, 29)>;
    		};
    	};
    
    	spi1_sleep: spi1_sleep {
    		group1 {
    			psels = <NRF_PSEL(SPIM_SCK, 0, 31)>,
    					<NRF_PSEL(SPIM_MOSI, 0, 30)>,
    					<NRF_PSEL(SPIM_MISO, 0, 29)>;
    			low-power-enable;
    		};
    	};
    };
    
    my_spi_master: &spi1 {
    	compatible = "nordic,nrf-spi";
    	status = "okay";
    	pinctrl-0 = <&spi1_default>;
    	pinctrl-1 = <&spi1_sleep>;
    	cs-gpios = <&gpio0 28 GPIO_ACTIVE_LOW>;
    	reg_my_spi_master: spi-dev-a@0 {
    		reg = <0>;
    	};
    };

    SPI initialization:

    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/spi.h>
    
    #define MY_SPI_MASTER DT_NODELABEL(my_spi_master)
    
    // SPI master functionality
    const struct device *spi_dev;
    
    struct spi_cs_control spim_cs = {
    	.gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(reg_my_spi_master)),
    	.delay = 0,
    };
    
    
    static const struct spi_config spi_cfg = {
    	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
    				 SPI_MODE_CPOL | SPI_MODE_CPHA,
    	.frequency = 4000000,
    	.slave = 0,
    	.cs = &spim_cs,
    };
    
    static void spi_init(void)
    {
    	spi_dev = DEVICE_DT_GET(MY_SPI_MASTER);
    	if(!device_is_ready(spi_dev)) {
    		printk("SPI master device not ready!\n");
    	}
    	if(!device_is_ready(spim_cs.gpio.port)){
    		printk("SPI master chip select device not ready!\n");
    	}
    }

    Test code that reads the accelerometer device ID:

    static int spi_test(void)
    {
    	static uint8_t tx_buffer[3];
    	static uint8_t rx_buffer[3];
    
    	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
    	};
    
    
    	/*
    	first 2 bytes of tx specify read/write action and on which register to perform
    	last byte of rx contains the respons from the accelerometer
    	*/
    	tx_buffer[0] = XL367_REG_READ;
    	tx_buffer[1] = XL367_DEVID_AD;
    
    	int error = spi_transceive(spi_dev, &spi_cfg, &tx, &rx);
    	if(error != 0){
    		printk("SPI transceive error: %i\n", error);
    		return error;
    	}
    
    
    	if(DEVICE_ID == rx_buffer[2])
    	{
    		printk("Successfull ACC communication\n");
    	}
    	else
    	{
    		printk("Faild ACC communication\n");
    	}
    
    	return 0;
    }

  • Hi Viktor,

    That is great to hear, and thank you for sharing the solution here for others to find!

    Best regards,
    Marte

  • Hi Marte,

    Just one more question related to this topic:

    What exactly this line is doing in the overlay SPI definition?

    reg_my_spi_master: spi-dev-a@0 {
    		reg = <0>;
    	};

    What exactly spi-dev-a@0 refers to and what is reg = <0>?

    Do I have to change anything to this part when trying to enable both SPI0 and SPI1 ? Does it relate to which SPI is beeing used? 

    Thank you in advance.

  • Hi Viktor,

    Spi-dev-a@-0 is the name of the devicetree node. As an example you can look at the example DTS file in Devicetree Guide - Syntax and structure:

    /dts-v1/;
    
    / {
            a-node {
                    subnode_nodelabel: a-sub-node {
                            foo = <3>;
                    };
            };
    };

    Here, a-sub-node is the same as spi-dev-a@0 in your case.

    For SPI devices, the 'reg' property is the chip select number. One SPI node (SPI0, SPI1, etc.) does not affect the reg property of another SPI node. If, however, you have more sub-nodes under an SPI node, then you need to make sure they have different chip select numbers. For example, the Thingy53 has three different SPI sub-nodes under SPI3, which must have different chip select numbers. This is from thingy53_nrf5340_common.dts:

    &spi3 {
    	compatible = "nordic,nrf-spim";
    	status = "okay";
    	cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>,
    		   <&gpio1 4  GPIO_ACTIVE_LOW>,
    		   <&gpio0 24 GPIO_ACTIVE_LOW>;
    
    	pinctrl-0 = <&spi3_default>;
    	pinctrl-1 = <&spi3_sleep>;
    	pinctrl-names = "default", "sleep";
    	adxl362: spi-dev-adxl362@0 {
    		compatible = "adi,adxl362";
    		spi-max-frequency = <8000000>;
    		reg = <0>;
    		int1-gpios = <&gpio0 19 0>;
    	};
    
    	bmi270: spi-dev-bmi270@1 {
    		compatible = "bosch,bmi270";
    		status = "disabled";
    		spi-max-frequency = <8000000>;
    		reg = <1>;
    	};
    
    	nrf_radio_fem_spi: fem_spi@2 {
    		compatible = "nordic,nrf21540-fem-spi";
    		status = "disabled";
    		reg = <2>;
    		spi-max-frequency = <8000000>;
    	};
    };

    The 'reg' property is used for different things based on the device. For more information see Devicetree Guide - Important properties.

    Best regards,
    Marte

Related