SPIS buffer cannot receive more than 64 bytes

Hi all,

I have a SPIS implementation on an nRF52840 running smoothly. Recently I'v had the need to receive large packets (up to 270 bytes) in one single transaction. However, the slave only receives 64 bytes. I am making sure that my internal SPI buffers (defined in the application to use the SPI APIs) are large enough to receive this. Also, the easydma-maxcnt-bits is set to 16. I have tried digging on the kconfig reference to see if there is a config that I am missing but I did not find anything that explicitly sets the SPIS buffer size. I know SPIS is implemented with EasyDMA and the buffers length is defined by the TXD.MAXCNT and RXD.MACNT registers. How can I set the values of these? I am using NCS v2.5.0 and the SPI driver in 

<zephyr/drivers/spi.h>.
Best regards,
Kevin 
  • Hi Kevin,

    There should not be any limitation of 64 bytes. Can you share more details about what you are doing and what you have found by debugging and testing?

    Br,

    Einar

  • Hi Einar,

    Yes. I based my routine on this example. The routine is basically the same, I only changed the data that has been exchanged. Inside the function spi_slave_check_for_message(), I print the value of "result" to the console and it shows 64. To my understanding, this is the number opf bytes received. I then tested sending smaller messages in one transaction (45 bytes) and the value of the "result" variable changes to 45. But anytime I try to send buffers larger than 64 bytes, "result" stays at 64 bytes and the rest of the bytes are lost. 

    I made sure that the master I am using (an ESP32) has the same SPI config (speed, mode, word size, bit order. etc..). 

    Best,

    Kevin 

  • Hi Kevin,

    I see. I Have still not been able to reproduce, though. I took the sample you refered to and modified it a bit to send 270 bytes and the full 270 byte message is received on the slave side. This is the code I tested with on 2.5.2:

    /*
     * Copyright (c) 2016 Intel Corporation
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/spi.h>
    
    
    #define MSG_LEN 270
    
    
    /* 1000 msec = 1 sec */
    #define SLEEP_TIME_MS   1000
    
    /* The devicetree node identifier for the "led0" alias. */
    #define LED0_NODE DT_ALIAS(led0)
    
    #define MY_SPI_MASTER DT_NODELABEL(my_spi_master)
    #define MY_SPI_MASTER_CS_DT_SPEC SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(reg_my_spi_master))
    
    #define MY_SPI_SLAVE  DT_NODELABEL(my_spi_slave)
    
    // SPI master functionality
    const struct device *spi_dev;
    static struct k_poll_signal spi_done_sig = K_POLL_SIGNAL_INITIALIZER(spi_done_sig);
    
    
    static void print_array(const uint8_t *in_data, uint32_t in_len)
    {
    	printk(" (size %lu):", (unsigned long)in_len);
    	if (NULL != in_data) {
    		for (uint32_t i = 0; i < in_len; i++) {
    			if (i % 16 == 0)
    				printk("\n\t%02X ", in_data[i]);
    			else
    				printk("%02X ", in_data[i]);
    		}
    	}
    	printk("\n");
    }
    
    
    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");
    	}
    	struct gpio_dt_spec spim_cs_gpio = MY_SPI_MASTER_CS_DT_SPEC;
    	if(!device_is_ready(spim_cs_gpio.port)){
    		printk("SPI master chip select device not ready!\n");
    	}
    }
    
    static struct spi_config spi_cfg = {
    	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_MODE_CPOL | SPI_MODE_CPHA,
    	.frequency = 4000000,
    	.slave = 0,
    	.cs = {.gpio = MY_SPI_MASTER_CS_DT_SPEC, .delay = 0},
    };
    
    static int spi_write_test_msg(void)
    {
    	static uint8_t tx_buffer[MSG_LEN];
    	static uint8_t rx_buffer[MSG_LEN];
    
    	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
    	};
    
    	// Updagte tx_buffer with dummy data
    	for (int i = 0; i < MSG_LEN; i++) {
    		tx_buffer[i]= (i & 0xFF);
    	}
    
    	// Reset signal
    	k_poll_signal_reset(&spi_done_sig);
    	
    	// Start transaction
    	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;
    	}
    
    	// Wait for the done signal to be raised and log the rx buffer
    	int spi_signaled, spi_result;
    	do{
    		k_poll_signal_check(&spi_done_sig, &spi_signaled, &spi_result);
    	} while(spi_signaled == 0);
    	printk("SPI RX: 0x%.2x, 0x%.2x\n", rx_buffer[0], rx_buffer[1]);
    	return 0;
    }
    
    // SPI slave functionality
    const struct device *spi_slave_dev;
    static struct k_poll_signal spi_slave_done_sig = K_POLL_SIGNAL_INITIALIZER(spi_slave_done_sig);
    
    static const struct spi_config spi_slave_cfg = {
    	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
    				 SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_OP_MODE_SLAVE,
    	.frequency = 4000000,
    	.slave = 0,
    };
    
    static void spi_slave_init(void)
    {
    	spi_slave_dev = DEVICE_DT_GET(MY_SPI_SLAVE);
    	if(!device_is_ready(spi_dev)) {
    		printk("SPI slave device not ready!\n");
    	}
    }
    
    static uint8_t slave_tx_buffer[MSG_LEN];
    static uint8_t slave_rx_buffer[MSG_LEN];
    static int spi_slave_write_test_msg(void)
    {
    	static uint8_t counter = 0;
    
    
    	const struct spi_buf s_tx_buf = {
    		.buf = slave_tx_buffer,
    		.len = sizeof(slave_tx_buffer)
    	};
    	const struct spi_buf_set s_tx = {
    		.buffers = &s_tx_buf,
    		.count = 1
    	};
    
    	struct spi_buf s_rx_buf = {
    		.buf = slave_rx_buffer,
    		.len = sizeof(slave_rx_buffer),
    	};
    	const struct spi_buf_set s_rx = {
    		.buffers = &s_rx_buf,
    		.count = 1
    	};
    
    	// Reset signal
    	k_poll_signal_reset(&spi_slave_done_sig);
    	
    	// Start transaction
    	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;
    	}
    	return 0;
    }
    /*
     * Return length of received message. If no message has been received, return -1.
     */
    static int spi_slave_check_for_message(void)
    {
    	int signaled, result;
    	k_poll_signal_check(&spi_slave_done_sig, &signaled, &result);
    	printk("received %i bytes\n", result);
    	if(signaled != 0){
    		return result;
    	}
    	else return -1;
    }
    
    /*
     * A build error on this line means your board is unsupported.
     * See the sample documentation for information on how to fix this.
     */
    static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
    
    int main(void)
    {
    	int ret;
    
    	if (!device_is_ready(led.port)) {
    		return 0;
    	}
    
    	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return 0;
    	}
    
    	spi_init();
    
    	spi_slave_init();
    
    	printk("SPI master/slave example started\n");
    	
    	spi_slave_write_test_msg();
    
    	while (1) {
    		spi_write_test_msg();
    		ret = gpio_pin_toggle_dt(&led);
    		if (ret < 0) {
    			return 0;
    		}
    		k_msleep(SLEEP_TIME_MS);
    
    		int received = spi_slave_check_for_message();
    		if(received > 0){
    			// Print the last received data
    			printk("SPI SLAVE RX:\n");
    			print_array(slave_rx_buffer, received);
    			
    			// Prepare the next SPI slave transaction
    			spi_slave_write_test_msg();
    		}
    	}
    
    	return 0;
    }
    

    If you don't make progress, can you share a logic analyzer trace of the SPI lines so that we can see if the master is actually sendign all the data (and if there are other things that stick out)?

  • Hi Einar,

    thank you so much for your help! All this time I thought maybe the isuue was on the nRF side, but it's actually on the master side (ESP32). It looks like the SPI FIFO on the ESP32 only allows for 64 bytes per transaction. Thanks for making me think about that! I will try to use another master or even use the nRF52840 as both master and slave (like in the example) to test this.

    Best regards,

    Kevin

Related