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

What is the safe way to implement ready line for SPI?

I want to implement a ready line for SPI and I noticed nordic has similar code in your sdk: components/serialization/common/transport/ser_phy/ser_phy_spi_slave.c.

However the following comment concerns me: "     //toggle - this should go high - but toggle is unsafe"

So I want to do a safe version and I do the following change:

In spi_slave_gpiote_init(), I change the following line

>> nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
to

>> nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_HIGH;
>> config.init_state = NRF_GPIOTE_INITIAL_VALUE_HIGH;

And I also change the lines in set_ready_line()

from:

>> //toggle - this should go high - but toggle is unsafe
>> uint32_t rdy_task = nrf_drv_gpiote_out_task_addr_get(m_spi_slave_raw_config.pin_rdy)

>> *(uint32_t *)rdy_task = 1;

to

>> nrf_gpio_pin_clear(m_spi_slave_raw_config.pin_rdy);

But it doesn't work at all. The ready line keeps high and never becomes low which indicates ready. I don't know what I do wrongly and what is supposed to be the safe way instead of toggle?

  • Hello,

    This should not be set up like it is, as you say.

    The reason that the nrf_gpio_pin_clear() does not work is that the pin is already "taken" by the task.

    If you have set GPIOTE_CONFIG_OUT_TASK_HIGH, then you should use nrf_drv_gpiote_pit_task_addr_get() and *(uint32_t *)rdy_task  = 1; as it was. Have you tested that?

    I have not set this up and tested it myself, but let me know if that doesn't work, and I will look into it.

    Best regards,

    Edvin

  • I need to use gpio task so that my PPI can set the pin to HIGH when SPI transfer is done. Then I need to set the pin to LOW when my SPI slave is ready to receive. Then how should I achieve that? I can do it using toggle task but it doesn't seem to be safe.

  • Hello,

    You can use the PPI for this, but you would want to split this into two tasks.

    To set the pin HIGH when SPI transfer is done, you can set 

    in spi_slave_gpiote_init(void):

    NRF_GPIOTE->CONFIG[m_spi_slave_raw_config.ppi_rdy_ch]   = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | 
                                                GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos | 
                                                m_spi_slawe_raw_config.pin_rdy << GPIOTE_CONFIG_PSEL_Pos | 
                                                GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos;

    instead of 

        nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
        (void) nrf_drv_gpiote_out_init(m_spi_slave_raw_config.pin_rdy, &config);
        (void) nrf_drv_gpiote_out_task_enable(m_spi_slave_raw_config.pin_rdy);

    In spi_slave_ppi_init(void), change:

    NRF_PPI->CH[m_spi_slave_raw_config.ppi_rdy_ch].TEP = rdy_task; 

    to

    NRF_PPI->CH[m_spi_slave_raw_config.ppi_rdy_ch].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[some free channel_A];

    Then you must set up a task to set the pin to LOW. This should be done everywhere where you see the set_ready_line(); function (below "DEBUG_EVT_SPI_SLAVE_RAW_BUFFERS_SET(0);  )

    You can e.g. use the task:

    NRF_GPIOTE->TASKS_CLR[some free channel_A] = 1;

    I am sorry, but I have not tested this. I assume you can use the same setup for the gpiote in spi_slave_gpiote_init, and only change the rdy_task to TASKS_SET, and then use TASKS_CLEAR = 1 as described above, in the application where you want to clear the pin. 

    Best regards,

    Edvin

Related