How to do SPIM with handshakes

I'm using SDK15.3 on an nRF52840 with FreeRTOS and specifically non-preemptive scheduling.

I want to so some transfers on SPIM (it's configure with DMA but that's not a requirement)... I've got a part I'm talking to that has a READY handshake signal coming back from it that kicks in during long transfers... basically it's implementing XON/XOFF for the regular SPI transaction.

Got any hints at how to handle that?

  • Hi

    I'm sorry, but I'm not sure what you're asking how to handle here. Is the READY handshake interrupting your transfers when you're not expecting it to, or what exactly is the issue here? There could be buffer that reaches its limit for example, but please share some more information here on what the issue here is.

    Best regards,

    Simon

  • nuts,,,, wrong button here.. let me see if I can clear the idea.

    Think of RS232 XON/XOFF functionality.  In this case we're using an outside signal to hold off the transmission of bytes.  When it asserts, I need to not send the next byte (while keeping the select signal asserted).  Once it de-asserts, I can resume sending bytes via SPI

  • Yes, there is a similar XON/XOFF functionality in the SPIM, use PPI to trigger hardware SPIM TASKS_SUSPEND and TASKS_RESUME from the active and inactive edges of the external hold signal.

    "A transaction can be suspended and resumed using the SUSPEND and RESUME tasks. When the SUSPEND task is triggered, the SPI master will complete transmitting and receiving the current ongoing byte before it is suspended"

  • Very interesting indeed.... sounds like that's the ticket for sure.  I don't suppose someone has some example code of how to do it?

  • I got involved just because I saw FreeRTOS here but the code that you seek does not necessarily depend on FreeRTOS.

    After you initialize the SPI using nrf_drv_spim_init, I think you can use something like below code as template to have the connections you see through PPI to the tasks.

    static nrf_ppi_channel_t ppi_channel_suspend;
    static nrf_ppi_channel_t ppi_channel_resume;
    
    void gpio_ppi_init(void)
    {
        APP_ERROR_CHECK(nrf_drv_gpiote_init());
    
        // Set up the READY_PIN as an input that can detect both active and inactive edges.
        // This will let us sense when the external device signals us to pause or resume.
        nrf_drv_gpiote_in_config_t ready_pin_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(false);
        ready_pin_config.pull = NRF_GPIO_PIN_NOPULL; // adjust if your circuit requires it.
        APP_ERROR_CHECK(nrf_drv_gpiote_in_init(READY_PIN, &ready_pin_config, NULL));
    
        // Reserve two PPI channels: one for pausing SPI (SUSPEND) and one for resuming it (RESUME).
        APP_ERROR_CHECK(nrf_drv_ppi_channel_alloc(&ppi_channel_suspend));
        APP_ERROR_CHECK(nrf_drv_ppi_channel_alloc(&ppi_channel_resume));
    
        // Link the READY_PIN's "active edge" event to the SPIM's SUSPEND task.
        // This ensures the SPI pauses when the device signals us to stop.
        APP_ERROR_CHECK(nrf_drv_ppi_channel_assign(
            ppi_channel_suspend,
            nrf_drv_gpiote_in_event_addr_get(READY_PIN),
            (uint32_t)&NRF_SPIM0->TASKS_SUSPEND
        ));
    
        // Link the READY_PIN's "inactive edge" event to the SPIM's RESUME task.
        // This ensures the SPI resumes when the device signals it's ready to continue.
        APP_ERROR_CHECK(nrf_drv_ppi_channel_enable(ppi_channel_suspend));
        APP_ERROR_CHECK(nrf_drv_ppi_channel_enable(ppi_channel_resume));
    }

    This is uncompiled code trying to show you the logic. Please use that as template.

Related