SPIS on NRF52840

I've been struggling bringing up a working SPIS test application and would appreciate any help I can get.  I'm much more of a HW person, so this hyper abstracted Zephyr stuff is not easy for me.

First Question:

In my overlay (see below) for the reg: attribute, I get the warning:  

Node spi-dev-a should have "compatible" property
Node should only occur on the undefined bus.

/soc/spi@40004000/spi-dev-a@0/

I've seen in https://devzone.nordicsemi.com/f/nordic-q-a/93484/struggling-to-use-spi-with-zephyr  

the following response:  "The overlay warnings I can see here also, but they don't seem to affect the example. I will try to find some time later to look into this."  referring to a similar warning, so I'm not sure if this actually an issue or not.

my_spis: &spi1 {
    compatible = "nordic,nrf-spis";
    status = "okay";
    pinctrl-0 = <&spi1_default>;
    pinctrl-1 = <&spi1_sleep>;
    pinctrl-names = "default", "sleep";
    cs-gpios = <&gpio0 29 GPIO_ACTIVE_LOW>;
    def-char = <0x00>;

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

};


&spi1_default {
    group1 {
        psels = <NRF_PSEL(SPIS_SCK, 0, 31)>,
                <NRF_PSEL(SPIS_MOSI, 0, 30)>,
                <NRF_PSEL(SPIS_MISO, 1, 8)>;

    };
};

Are there any issues that are easily seen with this?

if (!device_is_ready(spi_cs.gpio.port)) and  if (!device_is_ready(spi_dev))  both report back that the SPI instance and the CS are ready.  So, I'm inclined to think that this is a non-issue.  Thoughts?
Second Question:
I am calling:  err = spi_transceive_cb(spi_dev, &spi_cfg, NULL, &rx, spi_slave_callback, NULL); which is constantly returning -22 (EINVAL).
Besides the overlay, here are the other relevant bits:
CONFIG_SPI=y
CONFIG_GPIO=y
CONFIG_SPI_SLAVE=y
CONFIG_SPI_ASYNC=y
CONFIG_DEBUG=y
CONFIG_PRINTK=y
#CONFIG_NRFX_SPIS1=y
CONFIG_USERSPACE=n
CONFIG_UART_CONSOLE=y
CONFIG_LOG=y
#CONFIG_LOG_BACKEND_RTT=n
CONFIG_LOG_BACKEND_UART=y
CONFIG_LOG_PRINTK=y
CONFIG_LOG_DEFAULT_LEVEL=4 


CONFIG_HEAP_MEM_POOL_SIZE=50512
#CONFIG_IDLE_STACK_SIZE=4096
#CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_MAIN_STACK_SIZE=4096


CONFIG_USE_SEGGER_RTT=n
#CONFIG_RTT_CONSOLE=n
CONFIG_SERIAL=y
struct spi_config spi_cfg = {
	.operation = SPI_WORD_SET(8) | SPI_OP_MODE_SLAVE | SPI_TRANSFER_MSB |
				 SPI_LINES_SINGLE,
	.frequency = 4000000,
	.slave = 0,
};
After the CS line is tested as ready:
	spi_cfg.cs = &spi_cs;
Here is setting the spi_cs_control struct:
static struct spi_cs_control spi_cs = {
	//	.gpio.pin = cs_gpio_pin,
	//	.gpio.dt_flags = cs_gpio_flags,
	.gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(reg_my_spis)),
	.delay = 0,
};
Here is setting up the rx buffer:
		const struct spi_buf rx_buf = {
			.buf = buffer,
			.len = BUFFER_SIZE,
		};
		const struct spi_buf_set rx = {
			.buffers = &rx_buf,
			.count = 1,
		};
Here is the prototype for the CB:
void spi_slave_callback(const struct device *dev, int result, void *data)
There is a print statement upon enter the CB, so it's obviously not getting that far.
Does anything look obviously amiss here?  Does anyone have any suggestions on figuring out what is triggering the -EINVAL?  
Thank you for any guidance!
  • espian said:
    I get a toggling LED, as expected from the code.  However, I can not see any of the printk messages.

    I am using the same sample for 52840DK and I can see the messages on console (printing both messages on the same line).

    espian said:
    1) changing several of the printk's to printf's

    What do you mean by this? printk() is zephyr kernel function, and is supposed to be used with ncs/zephyr for console messages. 

    It is also important that you connect to the correct serial port.

  • I'm quite sure that the sample code is running properly.  However, I'm not going to see the UART output.

    I'm using a Fanstel eval kit (which is almost a carbon copy of the NRF52840DK), however the Fanstel module that is mounted on my board (BT840X) does not expose UART0 because it uses that on-module to control a PA.  This was very non-obvious and only mentioned in one line in their data sheet.

    I'm going to go back up in this thread to the actual issue/question as my CB is never being called.  I'll post more code related to that soon. 

  • I did fix a couple of items in my original code.  However, the issue remains that the call back function is never called.  I have changed the buffer size up and down to check for impact there and nothing.  It doesn't seem to matter if I underflow, overflow, or perfectly match the buffer size with the spi incoming data, the cb is never called.

    I'm still not clear as to what triggers the 'end' of an spi transaction in the driver and I suspect that is the fundamental issue that I'm having.

    I've included everything that I can think of below.  The GPIO registers, the code properly waits for the cs line to go active (low) and then beings the spi call back setup, pauses and then waits.

    Thanks for having a look.

    Here is my full current code:

    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/dt-bindings/gpio/gpio.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/spi.h>
    #include <nrfx_spis.h>
    #include <zephyr/logging/log.h>
    
    // #define NRFX_LOG_MODULE MY_CODE
    // #include <nrfx_log.h>
    
    LOG_MODULE_REGISTER(my_module);
    
    #define SPI_DEVICE_NAME "my_spis"
    #define BUFFER_SIZE 1
    static uint8_t buffer[BUFFER_SIZE] = {0};
    // static uint8_t ack_message[] = "ACK";
    
    static struct k_poll_signal spi_slave_done_sig = K_POLL_SIGNAL_INITIALIZER(spi_slave_done_sig);
    
    static struct device *spi_dev;
    
    static struct spi_config spi_cfg = {
    	.operation = SPI_WORD_SET(8) | SPI_OP_MODE_SLAVE | SPI_TRANSFER_MSB |
    				 SPI_LINES_SINGLE,
    	.frequency = 1000000,
    	.slave = 0,
    };
    
    #define MY_SPIS DT_NODELABEL(my_spis)
    const struct device *gpio_dev;
    #define CS_GPIO_NODE DT_NODELABEL(cs_gpio)
    const struct device *gpio_dev = DEVICE_DT_GET(CS_GPIO_NODE);
    const uint32_t cs_pin = 29;
    
    void spi_slave_callback(const struct device *dev, int result, void *data)
    {
    	// Check for errors in the result
    	if (result < 0)
    	{
    		printk("SPI transaction failed with error code %d\n", result);
    		return;
    	}
    
    	// Print the received data
    	printk("Received data: ");
    	for (int i = 0; i < BUFFER_SIZE; i++)
    	{
    		printk("%02x ", buffer[i]);
    	}
    
    	printk("\n");
    
    	// Print the result of the SPI transaction
    	k_poll_signal_raise(&spi_slave_done_sig, result);
    	printk("SPI transaction completed with status %d\n", result);
    }
    
    static int spi_begin_transaction(struct device *spi_dev, const struct spi_buf_set *rx)
    {
    	int err;
    	printk("In spi_check_for_message. \n");
    	k_poll_signal_reset(&spi_slave_done_sig);
    	__asm__ volatile("nop");
    	err = spi_transceive_cb(spi_dev, &spi_cfg, NULL, rx, spi_slave_callback, &spi_slave_done_sig);
    	__asm__ volatile("nop");
    	printk("Out of spi_transceive_cb\n RET: %d\n", err);
    	return (err);
    }
    
    static int spi_slave_check_for_message(void)
    {
    	int signaled, result;
    	k_poll_signal_check(&spi_slave_done_sig, &signaled, &result);
    	if (signaled != 0)
    	{
    		return 0;
    	}
    	else
    		return -1;
    }
    
    void main(void)
    {
    
    	static int err, loop_count;
    
    	// log_init();
    
    	printk("Entering Main\n");
    
    
    	if (!device_is_ready(gpio_dev))
    	{
    		printk("Device not ready.\n");
    		return;
    	}
    
    	spi_dev = DEVICE_DT_GET(DT_NODELABEL(my_spis));
    	if (!spi_dev)
    	{
    		printk("Could not find %s!\n", SPI_DEVICE_NAME);
    		return;
    	}
    
    	printk("Found %s!\n", SPI_DEVICE_NAME);
    
    	spi_cfg.cs = NULL;
    
    	/* configure the pin as input */
    	int ret = gpio_pin_configure(gpio_dev, cs_pin, GPIO_INPUT);
    	if (ret != 0)
    	{
    		printk("Didn't configure pin.\n");
    		/* handle error */
    	}
    
    	if (!device_is_ready(spi_dev))
    	{
    		printk("SPI device not ready \n");
    	}
    	else
    	{
    		printk("SPI device ready \n");
    	}
    
    	LOG_ERR("Test Error Message to confirm log works\n");
    	loop_count = 0;
    	while (1)
    	{
    		// printk("Loop %d\n", loop_count++);
    		const struct spi_buf rx_buf = {
    			.buf = buffer,
    			.len = BUFFER_SIZE,
    		};
    		const struct spi_buf_set rx = {
    			.buffers = &rx_buf,
    			.count = 1,
    		};
    		/*
    				const struct spi_buf tx_buf = {
    					.buf = buffer,
    					.len = BUFFER_SIZE,
    				};
    				const struct spi_buf_set tx = {
    					.buffers = &tx_buf,
    					.count = 1,
    				};
    		*/
    
    		// LOG_ERR("About to Call pin_get\n");
    		int cs_state = gpio_pin_get(gpio_dev, cs_pin);
    		// LOG_ERR("Just after calling pin_get\n");
    		if (cs_state < 0)
    		{
    			printk("error in reading cs pin state.\n"); /* handle error */
    		}
    		else if (cs_state == 0)
    		{
    			//cs is active so, begin transaction and setup callback and then wait for callback to be called.
    			err = spi_begin_transaction(spi_dev, &rx);
    			printk("Starting Wait. \n");
    
    			k_msleep(1);
    			printk("Done waiting. \n");
    			/* CS line is active, ready to transceive data */
    			printk("waiting... ");
    			do
    			{
    				err = spi_slave_check_for_message();
    				printk(".");
    			} while (err < 0);
    			printk("\n");
    			printk("Received data: ");
    			for (int i = 0; i < BUFFER_SIZE; i++)
    			{
    				printk("%02x ", buffer[i]);
    			}
    
    			printk("\n");
    		}
    
    		else
    		{
    
    			__asm__ volatile("nop");
    		}
    
    		if (err < 0)
    		{
    			printk("SPI transceive error: %d\n", err);
    		}
    	}
    }
    

    Here is the current proj.conf:


    CONFIG_GPIO=y
    
    CONFIG_SPI=y
    CONFIG_SPI_ASYNC=y
    
    CONFIG_SPI_SLAVE=y
    # nothing here
    #CONFIG_SPI=y
    #CONFIG_GPIO=y
    #CONFIG_GPIO_NRFX=y
    #CONFIG_SPI_SLAVE=y
    #CONFIG_SPI_ASYNC=y
    CONFIG_DEBUG=y
    #CONFIG_PRINTK=y
    #CONFIG_LOG_PRINTK=y
    CONFIG_LOG_MODE_IMMEDIATE=y
    #CONFIG_NRFX_SPIS1=y
    #CONFIG_USERSPACE=n
    CONFIG_LOG=y
    #CONFIG_LOG_BACKEND_RTT=n
    CONFIG_LOG_BACKEND_UART=y
    CONFIG_LOG_PRINTK=y
    CONFIG_LOG_DEFAULT_LEVEL=4 
    CONFIG_UART_CONSOLE=y
    #CONFIG_PM=n
    
    #CONFIG_STACK_SENTINEL=y
    
    
    
    
    #CONFIG_HEAP_MEM_POOL_SIZE=50512
    #CONFIG_IDLE_STACK_SIZE=2048
    #CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
    #CONFIG_MAIN_STACK_SIZE=8192
    
    
    
    #CONFIG_USE_SEGGER_RTT=n
    #CONFIG_RTT_CONSOLE=n
    CONFIG_SERIAL=y
    
    
    
    
    
    
    
    
    
    
    
    
    

    Here is a copy of the console output:

    Here is a screen shot of the logic analyzer capture feeding the dk:

  • Naeem,

    Any further thoughts?  

    Thanks,

    Keith

  • Hi Keith,

    Sorry for the delayed response

    I am note able to reproduce the issue that you are having.

    Are you able to compile and run the sample correctly?

    Can you send a complete zip folder which I could build for nrf52840dk to reproduce the error you are having?

    If there are any other special considerations, do let me know as well.

Related