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

nrf52832 SPI Code execution time

Hi,

I'm BLE-sending out Sensor values using a nrf52832. When the sensor gets a (new) value an interrupt is generated and the nrf then transfers the value in the gpiote inerrupt handler via SPI from the sensor. Therefore 2 SPI Commands like the following example are needed:

    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, 1, m_rx_buf, 2));
    while (!spi_xfer_done) { __WFE(); }		
    spi_xfer_done = false;

When the SPI Transfers are done the sensor resets the interrupt. However for my application it's important that this reset is as fast as possible, so in other words the SPI transfer has to be as fast as possible. I already optimized my code and tested with different Compiler Optimization Levels. The time between the falling and rising edge of the interrupt is measured with an oscilloscope. At the moment It takes ablut 65 microseconds to reset the Interrupt. Now I've noticed that when I add "NRF_LOG_HEXDUMP_INFO" to the int_handler after getting the sensor values the Interrupt time is reduced to 50 microseconds. So adding this line of code seems to speed up the execution time of the SPI commands. How is that possible? Is there a way to speed it up without the Hexdump_Info?

static void int_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
    uint8_t   sensor[2];
    getValues(sensor);

    NRF_LOG_HEXDUMP_INFO(sensor, 2);
}

Parents
  • Hi,

     

    So adding this line of code seems to speed up the execution time of the SPI commands

    Compilers optimize in the strangest ways.

     

    If you are triggering a SPI transaction from the GPIOTE irq vector, you should avoid to do this in the interrupt handler:

    * logging

    * sleeping

     

    Please also note that if you set the same priority on the two IRQs, you will get into a while(1) loop on the spi_xfer_done call.

    This will essentially take as long as it takes to:

    * Handle two interrupt vectors

    * Send and receive over SPI 

     

    If you want to make the ISR functions run quicker, disable logging, and move the while() loop to main context.

     

    Kind regards,

    Håkon

      

  • Hi Håkon,

    thank's very much for your detailed answer. I'm not sure how to implement the SPI-while() loop in main as I'm already using idle_state_handle(); in main like in the nordic BLE-Examples. Can you please tell me how to get theese things together in my code example?

    The IRQ-priorities are set as:

    GPIOTE_CONFIG_IRQ_PRIORITY 6
    SPI_DEFAULT_CONFIG_IRQ_PRIORITY 5

    Is this OK?

    Thank you very much in advance!

    void getValues(uint8_t sensor[])
    {
        APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, 1, m_rx_buf, 2));
        while (!spi_xfer_done) { __WFE(); }		
        spi_xfer_done = false;
    }
    
    
    static void int_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
    {
        uint8_t   sensor[2];
        getValues(sensor);
    }
    
    void spi_event_handler(nrf_drv_spi_evt_t const * p_event, void *p_context)
    {
        spi_xfer_done = true;
    }
    
    
    int main(void)
    {
        initialize_nrf;
        initialize_nrf_ble;
        initialize_sersor_via_spi;
    
        for (;;)
        {
            idle_state_handle();
        }
    }

Reply
  • Hi Håkon,

    thank's very much for your detailed answer. I'm not sure how to implement the SPI-while() loop in main as I'm already using idle_state_handle(); in main like in the nordic BLE-Examples. Can you please tell me how to get theese things together in my code example?

    The IRQ-priorities are set as:

    GPIOTE_CONFIG_IRQ_PRIORITY 6
    SPI_DEFAULT_CONFIG_IRQ_PRIORITY 5

    Is this OK?

    Thank you very much in advance!

    void getValues(uint8_t sensor[])
    {
        APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, 1, m_rx_buf, 2));
        while (!spi_xfer_done) { __WFE(); }		
        spi_xfer_done = false;
    }
    
    
    static void int_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
    {
        uint8_t   sensor[2];
        getValues(sensor);
    }
    
    void spi_event_handler(nrf_drv_spi_evt_t const * p_event, void *p_context)
    {
        spi_xfer_done = true;
    }
    
    
    int main(void)
    {
        initialize_nrf;
        initialize_nrf_ble;
        initialize_sersor_via_spi;
    
        for (;;)
        {
            idle_state_handle();
        }
    }

Children
  • Hi,

     

    hannes said:

    The IRQ-priorities are set as:

    GPIOTE_CONFIG_IRQ_PRIORITY 6
    SPI_DEFAULT_CONFIG_IRQ_PRIORITY 5

    Is this OK?

    Yes, that is fine. 

     

    hannes said:
    thank's very much for your detailed answer. I'm not sure how to implement the SPI-while() loop in main as I'm already using idle_state_handle(); in main like in the nordic BLE-Examples. Can you please tell me how to get theese things together in my code example?

     You are still handling this directly in two interrupt vectors, with no scheduling (ie. offloading processing) to main. If you want to speed up, remove the "__WFE()" call in getValues().

     

    Since the call "nrf_drv_spi_transfer()" is effectively a blocking call due to your next line while(!spi_xfer_done), the minimum time for those interrupts is equal to the time it takes to transfer those bytes over SPI.

     

    Kind regards,

    Håkon

  • Thanks Håkon,

    removing "__WFE()" speeded it up to 40 microseconds which is pretty good for my application. I'm still not sure If there would be an improvement in moving something to the main context?

  • Hi,

     

    hannes said:
    I'm still not sure If there would be an improvement in moving something to the main context?

    In terms of speed, it isn't an improvement, but in terms of reducing time spent in interrupt; it will be. However, 40 us is not that costly timing wise.

     

    Kind regards,

    Håkon 

Related