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

SPI Slave SPIS rx via ENDRX event (no CS toggle) and re-config buffers for tx ACK/NAK without interim CS toggle

Hi

My application requires that SPI CS is NOT TOGGLED within a transaction, only at either side

 My Transaction consists of:

1 - Slave sets up buffer ready for Rx and waits: nrf_drv_spis_buffers_set(&spis, NULL, 0, (uint8_t*)&sProteusMsg, 8); while(!spis_xfer_done) {}

2 - Master Sets CS Low

3 - Master clocks in 8 bytes to Slave, slave knows RX complete via ENDRX, NOT via CS toggle

4 - Slave re-configures buffers for a single byte Tx/Rx: nrf_drv_spis_buffers_set(&spis, &status, 1,&dummy,1);

5 - Master reads the single byte from the slave

6 - Master Sets CS High

The above FAILS - it looks like it fails on the SPI slave at step 4 - the re-configure of the buffer does not 'take'

If I flip CS High/Low on the master between steps 4 and 5, everything works. I am guessing this is down to the acquisition of the SPIS semaphore, I need to 'mimic' whatever it is that is occurring on the slave when CS is toggled - I need to do this when I receive the ENDRX event for the initial 8 bytes transferred master-->slave.

I have tried many many things, all to no avail, and am hoping for some assistance. I need to know what happens on the slave when CS is flipped high/low between the Tx and the Rx so that I can mimic this behaviour somehow?

NB: To make the ENDRX event work for SPIS, I had to modify the code. My mods are in this thread: https://devzone.nordicsemi.com/f/nordic-q-a/41249/spi-slave-comms-using-byte-count-eg-rx-buffer-full-rather-csn-chip-select-to-indicate-end-of-spi-transaction

Kind Regards

Nigel

My SPIS callback function below:

void spis_read_event_handler(nrf_drv_spis_event_t event)
{
		if (event.evt_type == NRF_DRV_SPIS_BUFFERS_SET_DONE)
		{
				NRF_LOG_INFO("NRF_DRV_SPIS_BUFFERS_SET_DONE");
		}
		
	
		if (event.evt_type == NRF_DRV_SPIS_ENDRX_DONE)
		{
				if (event.rx_amount > 0 || event.tx_amount > 0)
				{
						NRF_LOG_INFO("ENDRX --> Rx Byte Count: %d",event.rx_amount);
						NRF_LOG_INFO("ENDRX --> Tx Byte Count: %d",event.tx_amount);
						
						spis_xfer_done = true;
				}
		}
	
		if (event.evt_type == NRF_DRV_SPIS_XFER_DONE)
    {
				if (event.rx_amount > 0 || event.tx_amount > 0)
				{
						NRF_LOG_INFO("Via CS LO-->HI Rx Byte Count: %d",event.rx_amount);
						NRF_LOG_INFO("Via CS LO-->HI Tx Byte Count: %d",event.tx_amount);
						spis_xfer_done = true;
				}
    }
}

  • My SPI Xfer code for Slave and Master is below:

    //code at the slave end
    
    void WaitForNextSpiMessage(void)
    {
    		NRF_LOG_INFO("Entered WaitForNextSpiMessage");
    			
    		uint8_t status;
    		uint8_t dummy;
    	
    		memset(&sIncommingProteusMessage,0,sizeof(str_ProteusMsgWithPayload_t));
    		nrf_drv_spis_buffers_set(&spis, NULL, 0, (uint8_t*)&sIncommingProteusMessage.sProteusMsg, ProteusMsgLen);
    	
    		AssertNordicReady(); //<--Sets IO line to tell other end we are ready
    		
    		spis_xfer_done = false;
            while (!spis_xfer_done)
            {
    				
    		}
    		
    		//We exit above loop via recept of 8 bytes notified by ENDRX interrupt
    		
    		//The code below is me trying to fix my issue
    		
    		nrf_spis_task_trigger(spis.p_reg, NRF_SPIS_TASK_RELEASE);
    		while (nrf_spis_event_check(spis.p_reg, NRF_SPIS_EVENT_ACQUIRED))
    		{}
    		
    		//Code above is me trying to fix my issue
    		
    		NRF_LOG_INFO("Rx Transfer Done - preparing ACK");		
    		
    		//Prepare the ACK/NAK response
    		bool fCsumGood = MessageCsumIsOK(&sIncommingProteusMessage.sProteusMsg);
    		if (fCsumGood)	
    			status = (uint8_t)eRxStatus_SUCCESS;
    		else
    			status = (uint8_t)eRxStatus_CHECKSUM_ERROR;
    			
    		//Tried a variety of stuff to try and fix this issue!!
    		//spis.p_reg->INTENCLR = 0xFFFFFFFF;
    		//spis.p_reg->RXD.MAXCNT = 1;
    		//spis.p_reg->TXD.MAXCNT = 1;
    		//spis.p_reg->TXD.PTR = (uint32_t)&dummy;
    		//spis.p_reg->RXD.PTR = (uint32_t)&status;
    		//spis.p_reg->INTENSET = NRF_SPIS_INT_ACQUIRED_MASK | NRF_SPIS_INT_ENDRX_MASK | NRF_SPIS_INT_END_MASK;
    		//nrf_spis_task_trigger(spis.p_reg, NRF_SPIS_TASK_RELEASE);
    		//NRF_SPIS1->TASKS_RELEASE = 1;
    		//NRF_SPIS0->TASKS_RELEASE = 1;
    		//while (NRF_SPIS0->EVENTS_ACQUIRED == 1){}
    		//while (NRF_SPIS1->EVENTS_ACQUIRED == 1){}
    		//nrf_spis_event_clear(spis.p_reg, NRF_SPIS_EVENT_ACQUIRED);
    		
    		//Config buffers for RX <-- This config does not 'take' but it reports success
    		uint32_t error = nrf_drv_spis_buffers_set(&spis, &status, 1,&dummy,1);
    		
    		DeAssertNordicReady();   //<-- Tells master via IO line to read ACK
    		spis_xfer_done = false;
    		while (!spis_xfer_done)  
    		{
    			
    		}
    		
    		//Should get here via ENDRX - 1 byte clocked in/out, but actually don't get here
    		//until SPI Master de-asserts CS.
    				
    		NRF_LOG_INFO("DONE Send ACK/NAK");		
    		
    		//If CSUM good then process the message
    		if (fCsumGood)
    		{
    				NRF_LOG_INFO("Message Is Good - Process");
    				ProcessTheIncommingMessage(&sIncommingProteusMessage);
    				return;
    		}
    		
    		NRF_LOG_INFO("fCSUM BAD - Cant process message");
    }
    
    //Code at the master end
    
    void SendProteusMessage(p_str_ProteusMsgWithPayload_t psOutgoingProteusMessage)
    {
    		AssertSpiCS();      //<-- Master has CS controlled manually
    		
    		//Wait for slave to be ready
    		while (!IsNordicReadyToRecieve()){}
    			
    		SEGGER_RTT_printf(0, "SPIM Received READY signal\r\n");
    
    		APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi,ByteArrayFromProteusMessage(&sOutgoingProteusMessage.sProteusMsg,true), ProteusMsgLen, NULL,0));
    		spi_xfer_done = false;
    		while (!spi_xfer_done)  
    		{
    				
    		}
    		
    		SEGGER_RTT_printf(0, "SPIM Tx Done\r\n");
    		
    		SEGGER_RTT_WriteString(0, "Wait For ACK/NAK\r\n");
    		
    		//DeAssertSpiCS();    //<-- These three lines are what I CANNOT do
    		//nrf_delay_ms(50);   //<-- Not in pre existing implementation
    		//AssertSpiCS();      //<-- but doing this magically makes slave work properly
    		
            uint8_t dummy;				
            uint8_t AckNak;
            APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi,&dummy,1,&AckNak,1));
    		spi_xfer_done = false;
    		while (!spi_xfer_done || !IsNordicReadyToRespondWithAckNak()) 
    		{
    				
    		}
    		
    		SEGGER_RTT_printf(0, "SPIM Rx Done - AckNak: %X\r\n",AckNak);
    		
    		DeAssertSpiCS();   //<-- Master has CS controlled manually
    }

  • Log output...

    -- Below is the SPIS log output when not using an interim CS between tx and 
    -- rx on the master
    
    <info> app: Entered WaitForNextSpiMessage
    <info> app: NRF_DRV_SPIS_BUFFERS_SET_DONE
    <info> app: ENDRX --> Rx Byte Count: 8
    <info> app: ENDRX --> Tx Byte Count: 0
    <info> app: Rx Transfer Done - preparing ACK
    <info> app: NRF_DRV_SPIS_BUFFERS_SET_DONE
    <info> app: Via CS LO-->HI Rx Byte Count: 8  //this happens on the DeAssertSpiCS() at the end of the master tx/rx function
    <info> app: Via CS LO-->HI Tx Byte Count: 0
    <info> app: DONE Send ACK/NAK
    <info> app: Message Is Good - Process
    <info> app: Recieved Command: eBleMcuCommand_GetCurrentState
    <info> app: Murata State: eMurataStatus_BleDisabled
    
    -- Log below is where an interim CS toggle (low to high, high to low)
    -- is performed on master between Tx and Rx
    
    <info> app: Entered WaitForNextSpiMessage
    <info> app: NRF_DRV_SPIS_BUFFERS_SET_DONE
    <info> app: ENDRX --> Rx Byte Count: 8
    <info> app: ENDRX --> Tx Byte Count: 0
    <info> app: Rx Transfer Done - preparing ACK
    <info> app: NRF_DRV_SPIS_BUFFERS_SET_DONE      //Interim SPIM CS toggle between Tx and Rx makes this buffer setup actually work.
    <info> app: ENDRX --> Rx Byte Count: 1         //Called via ENDRX once 1 byte recieved
    <info> app: ENDRX --> Tx Byte Count: 0     
    <info> app: DONE Send ACK/NAK
    <info> app: Message Is Good - Process
    <info> app: Recieved Command: eBleMcuCommand_GetCurrentState
    <info> app: Murata State: eMurataStatus_BleDisabled
    
    <info> app: Entered WaitForNextSpiMessage
    <info> app: NRF_DRV_SPIS_BUFFERS_SET_DONE     //<-- fixed buffers for next Message
    
    NB: NRF_DRV_SPIS_BUFFERS_SET_DONE event fires at correct place each time, but in fact
        the buffers were not reconfigured correctly when the interim CS toggle is not used.
    

  • Have you tried to just prepare for one spis transfer, where the buffer is  (8 + 2) bytes, then you may just directly manipulate/update the ram location of the last byte in the rx buffer (10th byte in this case). I added 2 bytes, as I believe the 9th byte is fetched (in hardware to the spis) when the 8th byte is transmitted, thereby it's the 10th you need to update.

Related