Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Is it possible to get an event/interrupt on CS falling edge using the SPI slave?

Hi,

I'm developing on this setup:

  • nRF52833 DK, emulating an nRF52820. 
  • Soft Device 113 version 7.2.0
  • SDK 17.1.0, with updated MDK (see link)
  • IAR EW ARM 9.30

I need to get an event on the CS falling edge, with the nRF configured as a SPI Slave. I'm able to configure the GPIOTE interrupt but I believe this is messing up with the SPI peripheral event handling, because I stop getting the NRF_DRV_SPIS_XFER_DONE event after that. I read somewhere (I can't seem to find it now) that the SPIS module uses the GPIOTE to monitor the CS falling edge and start the transaction, which could explain why this doesn't work.

The way we setup the protocol, we expect for the CS falling edge to call nrf_drv_spis_buffers_set and load the highest priority message at that point in time. We have a different GPIO line to signal that data is ready, so the master lowers CS, nRF calls nrf_drv_spis_buffers_set and raises the other signal and then the master starts the transaction.

It'd be great if there was a way to get an event for the CS falling edge without messing with the SPI peripheral. Or a way to send a "fake" event to the SPI, so that my GPIOTE interrupt handles the falling edge and forwards it to the other module. Is this possible? Can I generate a fake CS falling edge from software?

Thank you and happy holidays!

  • Hi there,

    . I read somewhere (I can't seem to find it now) that the SPIS module uses the GPIOTE to monitor the CS falling edge and start the transaction

    You're correct.

    But I can't see exactly how this would mess up with the SPIS driver. Can you share how you're configuring the GPIOTE interrupt?

    It'd be great if there was a way to get an event for the CS falling edge without messing with the SPI peripheral. Or a way to send a "fake" event to the SPI, so that my GPIOTE interrupt handles the falling edge and forwards it to the other module. Is this possible? Can I generate a fake CS falling edge from software?

    I haven tried it myself but you can toggle a GPIO from sw that is connected to the CS pin trough GPIOTE + PPI.

    regards

    Jared 

  • Hi Jared, 

    The way I'm configuring the SPI is with the spis_init function, as follows. This function runs once when fw boots.

    static void enable_spi_pin_handler( nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action )
    {
    	nrf_spis_enable( spis.p_reg );
    	nrf_drv_spis_buffers_set( &spis, tx_buf, tx_length, rx_buf, SPIS_BUF_LEN );
    }
    
    
    static void init_enable_spi_interrupt( void )
    {
    	ret_code_t err_code;
    
    	err_code = nrf_drv_gpiote_init();
    	APP_ERROR_CHECK( err_code );
    
    	nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO( false );
    	in_config.pull = NRF_GPIO_PIN_PULLDOWN;
    
    	err_code = nrf_drv_gpiote_in_init( SPI_CS_PIN, &in_config, enable_spi_pin_handler );
    	APP_ERROR_CHECK( err_code );
    
    	nrf_drv_gpiote_in_event_enable( SPI_CS_PIN, true );
    }
    
    void spis_init( void )
    {
    	ret_code_t err_code;
    
    	nrf_drv_spis_config_t spis_config	= NRF_DRV_SPIS_DEFAULT_CONFIG;
    	spis_config.sck_pin					= SPI_SCK_PIN;
    	spis_config.miso_pin				= SPI_MISO_PIN;
    	spis_config.mosi_pin				= SPI_MOSI_PIN;
    	spis_config.csn_pin					= SPI_CS_PIN;
    	spis_config.irq_priority			= APP_IRQ_PRIORITY_LOWEST;
    
    	err_code = nrf_drv_spis_init( &spis, &spis_config, spis_event_handler );
    	APP_ERROR_CHECK( err_code );
    
    	nrf_spis_disable( spis.p_reg );
    	init_enable_spi_interrupt();
    
    	spi_incoming_parser_reset( &spi_incoming_parser );
    }

    The way this is intended to work is for SPI to be off all the time, until the master pulls the CS line low. At that point, the nRF gets the CS event, enables the SPI peripheral and calls nrf_drv_spis_buffers_set. On the NRF_DRV_SPIS_BUFFERS_SET_DONE event, the nRF raises another GPIO to tell the master it's ready (not included in this piece of code). After that, the master clocks all bits into the nRF and pulls CS high. This causes the NRF_DRV_SPIS_XFER_DONE event to be generated, the nRF processes the packet and disables the SPI again.

    Note that the GPIO event is configured after initializing the SPI, so presumably the GPIO will override the SPI behavior. And this sort of happens, because on certain occasions I don't get the  NRF_DRV_SPIS_XFER_DONE event when CS line goes high again. If I switch the lines and call init_enable_spi_interrupt before nrf_drv_spis_init, then I never get the CS falling edge interrupt and SPI is never enabled. So they seem to be overriding each other.

    I'd assume this is the expected behavior right? Is there a way to work around this?

    I haven tried it myself but you can toggle a GPIO from sw that is connected to the CS pin trough GPIOTE + PPI.

    Would this require an external hardware connection or is it all internal? I'm not familiar with PPI yet, could you elaborate please?

    Thank you!

  • Hi,

    I've been thinking about this and after some thought I think you can scratch my previous comment as it would require you to connect the two pins externally and would thus over-complicate this. 

    Here is what I suggest:

    1. Use the CS signal and send a wakeup signal x ms before a transfer using the GPIOTE IN event on slave
    2. Wakeup on slave and de initialize GPIOTE and configure SPIS peripheral and use another GPIO to signalize ready to master and wait for SPI transfer in IDLE sleep. 
    3. Initialize SPI on Master and start transfer. 
    4. After Master has finished transfer, de initialize SPIS peripheral and initialize GPIOTE and go back to system off sleep.

    The difference here is that you

    1. Send a wakeup signal with the CS pin from master before you initialize the SPI and SPIS on the master and slave respectively. 
    2. Only initialize the SPIS peripheral once you know a transfer is coming.

    Yes, there will be some added latency due to waiting for SPI and SPIS peripheral to initialize on master and slave respectively, but I think this is the best approach as the master will still have full control of the CS line. 

    regards

    Jared 

  • Hi Jared,

    We have tried this, but it doesn't seem to work. I think it's because once the SPI peripheral is configured on the slave (nRF) the CS line is already low, so there's no CS falling edge to tell the SPI peripheral that the transfer is incoming.

    Does the SPI peripheral work if it's initialized with CS low? Or do I need to raise CS and lower it again to give it a new falling edge? If that's the case, it looks like a quick pulse on CS could be used to initilize the SPI peripheral and the transfer should start after the nRF indicates it's ready (with the other GPIO), lowering CS again. 

    Thanks!

  • Why does the master assert the CS pin before the SPIS has signalized that it's ready and the SPIS is initialized? 

    Federico said:
    Does the SPI peripheral work if it's initialized with CS low? Or do I need to raise CS and lower it again to give it a new falling edge? If that's the case, it looks like a quick pulse on CS could be used to initilize the SPI peripheral and the transfer should start after the nRF indicates it's ready (with the other GPIO), lowering CS again. 

    You would need to raise the CS pin so that it can detect the falling edge, if not then the SPIS peripheral will never try to acquire the semaphore resulting in the transaction being ignored as explained here.

    regards
    Jared 

Related