nRF5340 SPI slave doesnt seem to receive

Hi,

I have ported my already working nRF52840 SPI slave code to a nRF5340, but it doesnt seem to receive any data. I have verified the CS, SCK and MOSI signals, but no MISO from nRF5340.

.
.
static const struct spi_config spis_cfg = {
	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_OP_MODE_SLAVE | SPI_MODE_CPOL | SPI_MODE_CPHA,
	.slave = 1,
};
.
.
static void spi_slave_init(void)
{
	spis_dev = DEVICE_DT_GET(MY_SPI_SLAVE);
	
	if (!device_is_ready(spis_dev)) {
		printk("SPI slave device not ready!\n");
	}
	else
		LOG_INF ("SPI slave ready");
}
.
.
static int spi_slave_check_for_message(void)
{
	int signaled, result;

	k_poll_signal_check(&spi_slave_done_sig, &signaled, &result);
	
	if (signaled != 0){
		slave_tx_buffer [0] = 0x01;	// init slave tx buffer
		return 0;
	}
	
	else return -1;
}
.
.
static int spi_slave_wait_msg(uint8_t txlen, uint8_t rxlen)
{
	const struct spi_buf s_tx_buf = {
		.buf = slave_tx_buffer,
		.len = txlen 
	};
	const struct spi_buf_set s_tx = {
		.buffers = &s_tx_buf,
		.count = 1
	};

	struct spi_buf s_rx_buf = {
		.buf = slave_rx_buffer,
		.len = rxlen
	};
	const struct spi_buf_set s_rx = {
		.buffers = &s_rx_buf,
		.count = 1
	};

	// Reset signal - this flag the next msg check 
	k_poll_signal_reset(&spi_slave_done_sig);
	
	// Start transaction
	int error = spi_transceive_signal(spis_dev, &spis_cfg, &s_tx, &s_rx, &spi_slave_done_sig);	// once a cycle is complete, the 'spi_slave_done_sig' will get signelled

	if(error != 0){
		LOG_ERR("SPI slave transceive error: %i\n", error);
		return error;
	}
	
	return 0;
}
.
.
static void spis_handler (struct k_work *item) {

static uint8_t txlen, rxlen;
int8_t i=0, loopCnt=0;
static uint8_t startRec=0;
uint16_t hostFWRev;

	if (spi_slave_check_for_message() == 0){
		
		LOG_INF ("---- Received SPI msg -----");

		switch (slave_rx_buffer [0]) {
			case 'V':	// Feature 7
				hostFWRev = EncVerString ();
				LOG_INF ("Host radio FW : %04x", hostFWRev);
				
				slave_tx_buffer[0] = 'A';
				slave_tx_buffer[1] = (uint8_t)(hostFWRev >> 8);
				slave_tx_buffer[2] = (uint8_t)(hostFWRev & 0x00ff);
				txlen = 3;
				rxlen = 1;
				break;
			

			case 'A':	// ACK/NAK request. This is the second byte send by host after the command. Only thing we need is to send the ACK/NAK of last command
				//LOG_INF ("ACK/NAK command");
				break;

			default :
				LOG_ERR ("default %x, %c", slave_rx_buffer [0], slave_rx_buffer [0]);
				slave_tx_buffer[0] = 'K';
				txlen = 1;
				rxlen = 1;
		}
		// Prepare the next SPI slave transaction -  this means the ACK/NAK doesnt get send immediately, but in next rx cycle. 
		// This means we have to either send same command twice or have a ACK/NAK request command, which is send after each command
		spi_slave_wait_msg(txlen, rxlen);	
	}

	k_msleep (1);
	k_work_submit (item);
}
.
.
void main(void)
{
.
.
    k_poll_signal_reset(&spi_slave_done_sig);
	spi_slave_init ();
	spi_slave_wait_msg (1, 1);	// this starts the k_poll event to wait for next spi rx

	k_work_init(&spis_work, spis_handler);		// spis handling is done in system queue to avoid issues with spi async transfers
	k_work_submit (&spis_work);
	.
	.
}

I get 'SPI slave ready' as a result of invoking spi_slave_init () function.

I can see the spis_handler () is invoked repeatedly, but 'spi_slave_check_for_message() == 0' seems to fail all the time.

I use nordic SDK 2.9.0.

I saw in the nRF5340 user guide that there is a hardware semaphore for accessing spi buffers, which was not available in nRF52840. Is there anything I need to implement in the application level to do that?

I have tried all I can think of and need some help urgently.

Thanks,

Kaushalya

Parents
  • Hello,

    I suggest to look at the driver test examples for spi, which also include spis:
    \zephyr\tests\drivers\spi\spi_controller_peripheral\src\main.c

    By default I can see there is no overlay for nRF5340, but I would expect you can rename nrf52840dk_nrf52840.overlay to nrf5340dk_nrf5340_cpuapp.overlay, and update the pins used for spi and spis, and then it should work.

    Kenneth

  • Hi,

    We need some help urgently as we are stuck at this point.

    After refering bit more on NORA B1 user guide, I come across this.

    "SPIM4: For the fastest SPI mode, the special purpose GPIO pins are enabled using the Peripheral setting of the MCUSEL pin of the PIN_CNF register. When activated, the SPIM PSEL settings are ignored, and the dedicated pins are used. The GPIO must use the extra high drive E0E1 configuration in the DRIVE field of the PIN_CNF GPIO register."

    I tried setting the MCUSEL to peripheral as follows.

    	#define SPIM4_SCK  NRF_GPIO_PIN_MAP(0,8)
    	#define SPIM4_MOSI NRF_GPIO_PIN_MAP(0,9)
    	#define SPIM4_MISO NRF_GPIO_PIN_MAP(0,10)
    
    	nrf_gpio_cfg(SPIM4_SCK, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
    	nrf_gpio_cfg(SPIM4_MOSI, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
    	nrf_gpio_cfg(SPIM4_MISO, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
    
    	nrf_gpio_pin_control_select(SPIM4_SCK, NRF_GPIO_PIN_SEL_PERIPHERAL);
    	nrf_gpio_pin_control_select(SPIM4_MOSI, NRF_GPIO_PIN_SEL_PERIPHERAL);
    	nrf_gpio_pin_control_select(SPIM4_MISO, NRF_GPIO_PIN_SEL_PERIPHERAL);

    But when I debug and check register at GPIO0-Pin 8, I see it is still set as APPMCU.

    So I have following questions.

    1. Can P0.8, P0.9 and P0.10 only be used as SPI4 in master mode, not as a slave?

    2. If SPI4 on the above pins can be used as a slave, how to do it?

    Cheers,

    Kaushalya

Reply
  • Hi,

    We need some help urgently as we are stuck at this point.

    After refering bit more on NORA B1 user guide, I come across this.

    "SPIM4: For the fastest SPI mode, the special purpose GPIO pins are enabled using the Peripheral setting of the MCUSEL pin of the PIN_CNF register. When activated, the SPIM PSEL settings are ignored, and the dedicated pins are used. The GPIO must use the extra high drive E0E1 configuration in the DRIVE field of the PIN_CNF GPIO register."

    I tried setting the MCUSEL to peripheral as follows.

    	#define SPIM4_SCK  NRF_GPIO_PIN_MAP(0,8)
    	#define SPIM4_MOSI NRF_GPIO_PIN_MAP(0,9)
    	#define SPIM4_MISO NRF_GPIO_PIN_MAP(0,10)
    
    	nrf_gpio_cfg(SPIM4_SCK, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
    	nrf_gpio_cfg(SPIM4_MOSI, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
    	nrf_gpio_cfg(SPIM4_MISO, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
    
    	nrf_gpio_pin_control_select(SPIM4_SCK, NRF_GPIO_PIN_SEL_PERIPHERAL);
    	nrf_gpio_pin_control_select(SPIM4_MOSI, NRF_GPIO_PIN_SEL_PERIPHERAL);
    	nrf_gpio_pin_control_select(SPIM4_MISO, NRF_GPIO_PIN_SEL_PERIPHERAL);

    But when I debug and check register at GPIO0-Pin 8, I see it is still set as APPMCU.

    So I have following questions.

    1. Can P0.8, P0.9 and P0.10 only be used as SPI4 in master mode, not as a slave?

    2. If SPI4 on the above pins can be used as a slave, how to do it?

    Cheers,

    Kaushalya

Children
  • Hello,

    The driver should handle everything, so as long as you use the driver in the same way as the test examples that is all you need to do.

    The SPIM4 do not share hardware resources with other instances, ref:
    https://docs.nordicsemi.com/bundle/ps_nrf5340/page/chapters/memory/appmem.html#ariaid-title3 

    spim3 share with spis3, twim3, twis3 and uart3:

    spim4 don't share with other functionality:

    The pin assignment tables show recommended usage:
    https://docs.nordicsemi.com/bundle/ps_nrf5340/page/chapters/pin.html 

    If there is no specific recommended usage for a pin, then it can be used for anything. The spi pins can use those pins that have labeled qspi usage, if qspi is not used.

    Edit: I can see that you write P0.9 and P0.10, make sure that these pins are not configured to nfc by using:
    CONFIG_NFCT_PINS_AS_GPIOS=y (that might be deprecated, you may need to use in devicetree &uicr { nfct-pins-as-gpios; };.

    Kenneth

  • Thanks Kenneth,

    I figured out that SPI4 has only master mode, but what I cant figure out is why cant I assign P0.08, P0.09, P0.10 and P0.11 to any other SPI interface like SPI2. I guess if we dont use them as NFC or tracedata, they could be assigned to SPI function. Please confirm.

    Even after I assigned NFC as GPIO, my SPI comms were not working. I was running out of options (and time) so wired the SPI to different set of pins (P0.13, P0.14, P0.15 and P1.01) and immediately it was working!! So there is something with P0.8-P0.11 that prevented them from being used as SPI slave.

    Also one other observation is when I debugged it and check the MCUSEL bits of corresponding pins, I saw them as assigned to APPCPU. Even my new pin assignment is same. I thing for the SPI to work, they should be connected to 'Peripheral', not 'APPCPU'. Can you please confirm?

    Cheers,

    Kaushaya

  • Hi,

    Think I found the problem. I compiled hello world for nRF5340 and looked at the zephyr.dts file in the build folder. I can find 4 pins are assigned by default to the network core, like this:

    	gpio_fwd: nrf-gpio-forwarder {
    		compatible = "nordic,nrf-gpio-forwarder";
    		status = "okay";
    		uart {
    			gpios = < &gpio1 0x1 0x0 >, < &gpio1 0x0 0x0 >, < &gpio0 0xb 0x0 >, < &gpio0 0xa 0x0 >;
    		};
    	};
    Amongst those are for instance P0.10 and P0.11, I assume that is the cause of your problems. To answer your questions:
    kaushalyasat said:
    I thing for the SPI to work, they should be connected to 'Peripheral', not 'APPCPU'. Can you please confirm?

    Looks like you are right, for SPIM4 and 32Mbps you need to configure MCUSEL to "Peripheral" yes.

    Kenneth

  • Hi Kenneth,

    Thanks again. I have also noticed this but when I check from the GUI devicetree view, the nrf-gpio-forwarder was not visible. But as you point out in the zephyr.dts I can see the relevant pins being used.

    1. Why wouldn't the devicetree compiler complains when the same pins is assigned to two interfaces?

    2. I have given up using P0.8 - P0.11 and picked up other four GPIOs as SPI2S and I selected them as following.

    nCS - P0.13

    SCLK - P0.14 

    MOSI - P1.1

    MISO - P0.15

    Then I could see the spi handler gets invoked but the received character was always 0x00. Then I changed MOSI to P0.18 and all was good. 

    Now after seeing your comment, I see P1.1 is also used in nrf-gpio-forwarder and remapped all my spi2 related pins from nrf-gpio-forwarder. I verified it in zephyr.dts, but P1.1 doesnt seem to work!! Any ideas?

    3.

    for SPIM4 and 32Mbps you need to configure MCUSEL to "Peripheral"

    This means if I map P0.8, P0.9 etc as SPI2S, it doesn't need to be mapped as a peripheral? I thought SPI2S is also acts as a peripheral. Even after SPI2S is working fully, the CNF register indicates that each pin belongs to APPCPU, not Peripheral.

    Cheers,

    Kaushalya

  • kaushalyasat said:
    1. Why wouldn't the devicetree compiler complains when the same pins is assigned to two interfaces?

    In an ideal world it would, but it doesn't.

    kaushalyasat said:
    I verified it in zephyr.dts, but P1.1 doesnt seem to work!! Any ideas?

    If you are using the DK maybe check out:
    https://docs.nordicsemi.com/bundle/ug_nrf5340_dk/page/UG/dk/dyn_hw_flow_control.html#d10e117 

    kaushalyasat said:
    This means if I map P0.8, P0.9 etc as SPI2S, it doesn't need to be mapped as a peripheral? I

    Correct.

    Kenneth

  • Related