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!
Parents
  • Ok, I have the log information now going.  The -EINVAL is from this error message:   spi_nrfx_spis: CS control via GPIO is not supported

    I am assuming at this point that something in my overlay is not quite right with regards to the CS definition.  This information should be in the previous post.

    Thanks in advance for any thoughts.

    -Keith

  • As I understand this now, the SPI Slave code does not support monitoring a GPIO line assigned as a CS control line.  This is still a bit opaque from the zephyr and other documentation.

    Passing a NULL value into spi_cfg.cs allows the spi_transceive_cb to be called without an immediate error being thrown.

    I'm in the process of manually checking for the state of a cs line and should have some more code ready test later today.  

    Does anyone know if there is a decent write-up on the SPI support software and its functionality for NRF running zephyr? 

  • Thank you for confirming this.

    I have code that now manually monitors a cs line and triggers a call to spi_transceive_cb.  (see below).  The first SPI transaction calls spi_transceive_cb and it returns a zero.  The second transaction throws up some debug messages from the MPU module (see further below).  In both cases I never see output from my callback function.

    I'll continue to debug on this, but if there is something obvious that I'm not doing correctly, please let me know.

    I have verified with a logic analyzer that the SPI transmission to the NRF are happening correctly.

    struct spi_config spi_cfg = {
    	.operation = SPI_WORD_SET(8) | SPI_OP_MODE_SLAVE | SPI_TRANSFER_MSB |
    				 SPI_LINES_SINGLE,
    	.frequency = 4000000,
    	.slave = 0,
    };
    
    ...................
    
    while (1)
    	{
    		//printf("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,
    				};
    		*/
    		// receive data packet
    		//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)
    		{
    			printf("error in reading cs pin state.\n"); /* handle error */
    		}
    		else if (cs_state == 0)
    		{
    			__asm__ volatile("nop");
    			err = spi_transceive_cb(spi_dev, &spi_cfg, NULL, &rx, spi_slave_callback, NULL);
    			__asm__ volatile("nop");
    			printf("Out of spi_transceive_cb\n RET: %d\n", err);
    			/* CS line is active, ready to transceive data */
    		}
    		else
    		{
    			/* CS line is not active */
    			__asm__ volatile("nop");
    		}
    
    		if (err < 0)
    		{
    			printf("SPI transceive error: %d\n", err);
    		}
    	}

    Here is the callback:

    void spi_slave_callback(const struct device *dev, int result, void *data)
    {
    	// Check for errors in the result
    	if (result < 0)
    	{
    		printf("SPI transaction failed with error code %d\n", result);
    		return;
    	}
    
    	// Check if the buffer contains data
    	if (buffer == NULL || BUFFER_SIZE == 0)
    	{
    		printf("Buffer is empty or not initialized\n");
    		return;
    	}
    
    	// Print the received data
    	printf("Received data: ");
    	for (int i = 0; i < BUFFER_SIZE; i++)
    	{
    		printf("%02x ", buffer[i]);
    	}
    
    	printf("\n");
    
    	// Print the result of the SPI transaction
    	printf("SPI transaction completed with status %d\n", result);
    }

    Here is the console output:

    Out of spi_transceive_cb

     RET: 0

    [00:00:35.624,877] <dbg> mpu: mpu_configure_region: Configure MPU region at index 0x2

    [00:00:35.633,087] <dbg> mpu: region_allocate_and_init: Program MPU region at index 0x2

    [00:00:35.641,510] <dbg> mpu: region_init: [2] 0x200015c0 0x150b000a

  • More information.....

    I changed my code to operate similar to the master/slave SPI example that is referenced in various Nordic SPI threads: NCS spi master/slave example

    In particular, adding in the kpoll method to watch for spi transaction completion.  Here are a couple of code snippets:

    	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");
    	printf("Out of spi_transceive_cb\n RET: %d\n", err);
    	return (err);
    
    .................
    
    			printf("waiting... ");
    			do
    			{
    				err = spi_slave_check_for_message();
    			    printf(".");
    			} while (err < 0);
    			printf("\n");
    			
    .......................
    
    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;
    }

    The code/device sits and waits for the cs line to drop.  Once that happens the kpoll reset occurs and spi_transceive_cb is called as shown.  Then the code goes into the do while loop to look for completion of the SPI reception.  The completion never occurs and the device keeps printing '.' 's to the console.  It doesn't matter if I underflow the transmission, have it match the buffer definition exactly (8 bytes) or overflow, the spi reception never 'completes'.  

    It seems as though I'm missing something very fundamental here.  Thank you in advance for any insights.

  • Hi, 

    I am not sure why it is happening like this, while the code looks pretty much same like the SPI Master/Slave Sample.

    Have you tested the sample? Can you start with that and then modify / incorporate the changes you want.

  • Ok, here are some interesting results.

    I started a new project and pulled in the files from the master/slave GitHub repository (including the proj.conf file).  Modify nothing, build, and flash.  I get a toggling LED, as expected from the code.  However, I can not see any of the printk messages.

    As soon as I modify the proj.conf file so that I can see the printk output, I start having issues.  Here is the output:

    [00:00:00.249,93[00:00:00.250,244] <dbg> mpu: region_init: [2] 0x20012000 0x150b000a
    SPI master/slave example started
    --- 37 messages dropped ---
    SPI SLAVE TX: 0x00, 0x00
    SPI TX: 0x00, 0x00
    [00:00:00.250,427] <dbg> spi_nrfx_spim: spi_context_buffers_setup: tx_bufs 0x20012fc8 - rx_bufs 0x20012fb8 - 1
    [00:00:00.250,457] <dbg> spi_nrfx_spim: spi_context_buffers_setup: current_tx 0x20012fd0 (1), current_rx 0x20012fc0 (1), tx buf/len 0x200007e4/2, rx buf/len 0x200007e0/2
    [00:00:00.250,488] <dbg> spi_nrfx_spim: spi_context_update_tx: tx buf/len (nil)/0
    [00:00:00.250,518] <dbg> spi_nrfx_spim: spi_context_update_rx: rx buf/len (nil)/0
    [00:00:00.250,549] <dbg> spi_nrfx_spim: finish_transaction: Transaction finished with status 0
    SPI RX: 0x00, 0x00
    [00:00:00.250,640] <dbg> os: z_tick_sleep: thread 0x20000600 for 32768 ticks
    [00:00:00.250,671] <dbg> mpu: z_arm_configure_dynamic_mpu_regions: configure thread 0x20000000's domain
    [00:00:00.250,701] <dbg> mpu: z_arm_configure_dynamic_mpu_regions: configure domain: 0x20001448
    [00:00:00.250,701] <dbg> mpu: z_arm_configure_dynamic_mpu_regions: configure domain: 0x20001448
    [00:00:00.250,732] <dbg> mpu: z_arm_configure_dynamic_mpu_regions: configure user thread 0x20000000's context
    [00:00:00.250,762] <dbg> mpu: mpu_configure_region: Configure MPU region at index 0x2
    [00:00:00.250,793] <dbg> mpu: region_allocate_and_init: Program MPU region at index 0x2
    [00:00:00.250,793] <dbg> mpu: region_init: [2] 0x20002000 0x150b000a
    [00:00:00.249,938] <dbg> mpu: mpu_configure_region: Configure MPU region at index 0x2
    [00:00:00.250,976] <err> os: ***** MPU FAULT *****
    [00:00:00.251,007] <err> os:   Data Access Violation
    [00:00:00.251,007] <err> os:   MMFAR Address: 0x20002010
    [00:00:00.251,037] <err> os: r0/a1:  0x200020e0  r1/a2:  0x0000001c  r2/a3:  0x00000000
    [00:00:00.251,068] <err> os: r3/a4:  0x20002094 r12/ip:  0x0000000e r14/lr:  0x0000258f
    [00:00:00.251,068] <err> os:  xpsr:  0x01000000
    [00:00:00.251,098] <err> os: Faulting instruction address (r15/pc): 0x00000bda
    [00:00:00.251,159] <err> os: >>> ZEPHYR FATAL ERROR 2: Stack overflow on CPU 0
    [00:00:00.251,190] <err> os: Current thread: 0x20000000 (logging)
    [00:00:01.086,059] <err> os: Halting system

    Here is the modified proj.conf file:

    CONFIG_GPIO=y
    
    CONFIG_SPI=y
    CONFIG_SPI_ASYNC=y
    
    CONFIG_SPI_SLAVE=y
    
    CONFIG_DEBUG=y
    CONFIG_PRINTK=y
    #CONFIG_NRFX_SPIS1=y
    CONFIG_USERSPACE=y
    CONFIG_UART_CONSOLE=n
    CONFIG_LOG=y
    CONFIG_LOG_BACKEND_RTT=y
    CONFIG_LOG_BACKEND_UART=n
    CONFIG_LOG_PRINTK=y
    CONFIG_LOG_DEFAULT_LEVEL=4 
    CONFIG_USE_SEGGER_RTT=y
    CONFIG_RTT_CONSOLE=y
    CONFIG_SERIAL=y
    
    CONFIG_HEAP_MEM_POOL_SIZE=50512
    CONFIG_IDLE_STACK_SIZE=4096
    CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
    CONFIG_MAIN_STACK_SIZE=4096

    I had also tried a version to route the console to the UART, but was still not seeing output (am connected to UART0, UART0 is enabled and connected to the console per the dts file, UART1 is disabled per the overlay).

  • espian said:
    As soon as I modify the proj.conf file so that I can see the printk output, I start having issues.

    I am not sure why such is happening. 

    can you send your minimal project, just enough to show what you are facing. Please strip it down to minimal version just to show the functionality and error.

Reply Children
  • My last set of comments were with the GitHub master slave code with the only change being 1) changing several of the printk's to printf's and 2) the above changes to the proj.conf file.  

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

Related