This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF9160 and SPI (CS control inhibited (no GPIO device)

I'm struggling with SPI and nRF9160. I've used an old sample (which didn't compile right of the bat):
github.com/.../spi

Seems the CS pin isn't passed on and I'm getting the "spi_nrfx_spim: CS control inhibited (no GPIO device)" error. It fails the check:
if (ctx->config->cs && ctx->config->cs->gpio_dev)
since
ctx->config->cs = 0
ctx->config->cs->gpio_dev = 536877096

If i "fake it" in spi_context.h and hardcode in the cs config:
const struct device *port;
port = device_get_binding(DT_LABEL(DT_NODELABEL(gpio0)));
gpio_pin_configure(port, 13, GPIO_OUTPUT_INACTIVE);

It throws the CS line low (since initial state of GPIO_OUTPUT_INACTIVE is low) and it stays low. If i set it GPIO_OUTPUT_INIT_HIGH, CS stays high. Which is not so strange, since the driver isn't able to hook into the cs pin.

What am I missing?
I read somewhere that SPI only works non-secure, but I see correct data with my Saleae Logic both when building secure and non-secure.


NCS v1.5.0.
West version: v0.10.1

Here is my main.c:

/*
* Copyright (c) 2012-2014 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr.h>
#include <sys/printk.h>
#include <drivers/spi.h>


#define DT_DRV_COMPAT bosch_bmi160 //just for experimenting with DT macros

static const struct spi_config spi_cfg = {
	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
		     SPI_MODE_CPOL | SPI_MODE_CPHA,
	.frequency = 1000000,
	.slave = 0,
	.cs = NULL,
};

struct device * spi_dev;

static void spi_init(void)
{
	const char* const spiName = "SPI_3";
	spi_dev = device_get_binding(spiName);

	if (spi_dev == NULL) {
		printk("Could not get %s device\n", spiName);
		return;
	}
}

void spi_test_send(void)
{
	int err;
	static int8_t tx_buffer[1];
	static int8_t rx_buffer[1];

	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
	};

	err = spi_transceive(spi_dev, &spi_cfg, &tx, &rx);
	if (err) {
		printk("SPI error: %d\n", err);
	} else {
		/* Connect MISO to MOSI for loopback */
		printk("TX sent: %x\n", tx_buffer[0]);
		printk("RX recv: %x\n", rx_buffer[0]);
		tx_buffer[0]++;
	}
}

void main(void)
{
	spi_init();

	while (1) {
        	spi_test_send();
		k_sleep(K_MSEC(1000));
	}
}

nrf9160dk_nrf9160.overlay and nrf9160dk_nrf9160ns.overlay are identical:


&uart3{
	status = "disabled";
};

&spi3{
	compatible = "nordic,nrf-spim";
	status = "okay";
	sck-pin = <10>;
	mosi-pin = <11>;
	miso-pin = <12>;
	cs-gpios =	<&gpio0 13 GPIO_ACTIVE_LOW>,
			<&gpio0 7 GPIO_ACTIVE_LOW>,
			<&gpio0 8 GPIO_ACTIVE_LOW>,
			<&gpio0 9 GPIO_ACTIVE_LOW>;

	accelerometer: bmi160@0 {
		compatible = "bosch,bmi160";
		status = "okay";
		reg = <0>;
	        spi-max-frequency = <1000000>;
		label = "BMI160";
		int-gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>;
	};

	pressure: lps22hb-press@1 {
		compatible = "st,lps22hb-press";
		spi-max-frequency = <1000000>;
		status = "okay";
		reg = <1>;
		label = "LPS22HB";
	};

	flashchip: at25sf321bsshbb@2 {
		compatible = "jedec,spi-nor";
		label = "AT25SF321BSSHBB";
		status = "okay";
		reg = <2>;
		spi-max-frequency = <1000000>;
		size = <0x10000000>;
		has-dpd;
		t-enter-dpd = <1000>;
		t-exit-dpd = <30000>;
		jedec-id = [1F 47 01];
	};


        memorycard: sdhc@3 {
                compatible = "zephyr,mmc-spi-slot";
                reg = <3>;
                status = "okay";
                label = "SDHC0";
                spi-max-frequency = <1000000>;
        };
};

prj.conf:

CONFIG_GPIO=y
CONFIG_SERIAL=y

#Debug Specific
CONFIG_DEBUG=y
CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=4
CONFIG_LOG_MAX_LEVEL=4
CONFIG_LOG_BACKEND_UART=y


# SPI
CONFIG_SPI=y
CONFIG_SPI_NRFX=y
CONFIG_SPI_3=y
CONFIG_NRFX_SPIM3=y
CONFIG_MAIN_STACK_SIZE=4096

  • Another problem (and this continues along many of my code samples), when building for non-secure, it doesn't produce merged.hex, only zephyr.hex. When I build the "samples/nrf9160/download", it creates a merged.hex. I've compared my files to the ones in "samples/nrf9160/download", but I can't find anything that would make the merged.hex.

  • Hi,

    From your main.c I see that you have .cs = NULL. This will result in ctx->config->cs = 0, as you stated. When using CS control and controlling a CS line via a GPIO line, both gpio_dev and cs should be valid pointers (gpio_dev to an actual GPIO device, and cs is used for the SPI Chip Select control structure). Therefore, you should instead set .cs = &spi_cs, and define spi_cs as:

    struct spi_cs_control spi_cs = {
    	.gpio_pin = CONFIG_SPI_CS_CTRL_GPIO_PIN,
    	.gpio_dt_flags = GPIO_ACTIVE_LOW,
    	.delay = 0,
    };

    CONFIG_SPI_CS_CTRL_GPIO_PIN is a number representing the GPIO PIN that will be used to act as a CS line. You can rename it to something else, but you must remember to define this in your project.

    spi_cfg will then be:

    static const struct spi_config spi_cfg = {
    	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
    		     SPI_MODE_CPOL | SPI_MODE_CPHA,
    	.frequency = 1000000,
    	.slave = 0,
    	.cs = &spi_cs,
    };

    Best regards,

    Marte

  • Hi,

    When building your application as non-secure, the SPM sample should get built automatically, and it should be automatically included as a child image in merged.hex. I will look into this more to figure out what the problem might be and come back to you.

    Best regards,

    Marte

  • That took care of the cs (now 13), but now ctx->config->cs->gpio_dev: 0, so it still failed. I had to changed it to:

      struct spi_cs_control spi_cs = {
        .gpio_dev = NULL,
        .gpio_pin = 13,
        .gpio_dt_flags = GPIO_ACTIVE_LOW,
        .delay = 0,
      };
    
      static const struct spi_config spi_cfg = {
        .operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
        SPI_MODE_CPOL | SPI_MODE_CPHA,
        .frequency = 1000000,
        .slave = 0,
        .cs = &spi_cs,
      };

    And add this in static void spi_init(void):

    spi_cs.gpio_dev = device_get_binding("GPIO_0");
    

    Seems to work now. 

    We have 3 SPI devices on the bus (one accelerometer which shall be read very often (10-100kHz), and two other sensors with low read (1-5 Hz)). Each sensor will be running in their own thread, and put values in a cue, then a message handler and pick from the cue.

    Is there a more elegant way to handle the SPI initialisation than the one I did above (considering we have 3 devices on the bus)? Maybe one that picks each property from the DT? Or is this fine (set up 3 individual spi instances)?

  • Hi,

    Good to hear that it seems to work now! I have not seen examples of having three devices on the bus, as most SPI implementations are only using one device, so I cannot say much about more elegant ways to do it. I do not see any reason why it would cause problems as you have it now, so if it is working with the way you have implemented it you should be fine.

    I am still looking into your problem regarding merged.hex, and I still have not found any reasons why you are only getting zephyr.hex when building. Could you check the .config file that is generated when you build your project? It can be found in <build_folder>/zephyr/.config. When using nrf9160dk_nrf9160ns as the build target, the following configurations should be set in .config:

    CONFIG_TRUSTED_EXECUTION_NONSECURE=y
    CONFIG_SPM=y

    If these are set, then you were able to build it as non-secure. If not, there was something wrong with building it as non-secure, and that might be the reason why you only get zephyr.hex.

    Have you tested using both west and SES, or only one of the options?

    Best regards,

    Marte

Related