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

Large delay when using app scheduler in GPIOTE interrupt

Hello,

I have an external chip in SPI slave mode and connected to 52840 through SPI interface. When data is ready it will notify 52840 through its dedicated IRQ pin (connected to one of GPIO of 52840), and then 52840 will read the data back through SPI bus., using SPIM driver.

the software is composed like this:

1. Setup GPIOTE to sense the GPIO/IRQ pin (low to high)

2. in GPIOTE interrupt handler, use app_scheduler to transfer execution to main context.

3. in main context, use SPIM driver to read data back to 52840's RAM.

the above process can run fine and the data read back are all correct. However, a problem is Step 2. has a large delay about 2ms. that is, after app_sched_event_put is called, the data event handler is executed only after 2ms. My application can not tolerant such big delay.

Is this delay normal? if not, what could be the possible reason why it happen?

Major code snippet:

Main.c

int main(void)
{
    bool erase_bonds;
    
    // Initialize.
    log_init();

    NRF_LOG_INFO("Starting nrf52840...");
    
    timers_init();
    buttons_leds_init(&erase_bonds);
    power_management_init();

    //init event scheduler;
    APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
    
    
    // Enter main loop.
    for (;;)
    {
        //handle usbd event sperately;
        //app_usbd_event_queue_process();
        
        app_sched_execute();
        if (NRF_LOG_PROCESS() == false)
        {
			idle_state_handle();
            //power_manage();
        }
    }
}

GPIOTE init:

{
    ...
    ret_code_t err_code;
    UNUSED_VARIABLE(err_code);
    if(!nrf_drv_gpiote_is_init())
    {
        err_code = nrf_drv_gpiote_init();
        APP_ERROR_CHECK(err_code);
    }
    nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
    //in_config.pull = NRF_GPIO_PIN_PULLUP;
    UNUSED_VARIABLE(in_config);
    err_code = nrf_drv_gpiote_in_init(EXT_DATA_IRQ_PIN, &in_config, gpiote_irq_handler);
    //APP_ERROR_CHECK(err_code);
    nrf_drv_gpiote_in_event_enable(EXT_DATA_IRQ_PIN, true);
    ...
}

GPIO interrupt handler

void gpiote_irq_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
    
    if (pin == EXT_DATA_IRQ_PIN) {
     

        ex_data_event_t evt;
        //fill in data evt;
        ....
        app_sched_event_put(&evt, sizeof(evt), data_event_dispatch);

    }
    
}

app scheduler event hander:

void data_event_dispatch(void *p_event_data, uint16_t event_size) {
    ex_data_event_t* p_evt = (ex_data_event_t*)p_event_data;
    switch (p_evt->pa_ops) {
        case DATA_OPS_TYPE_EXT_PA_ON:
            //NRF_LOG_DEBUG("[ext]\tswitch PA on...");
            break;
    }
    ...

}

Parents
  • Hi,

    The app scheduler is just a queue that you pop ad process in the main loop. Your main loop does not do much else, and the queue is processed before logs (assuming you use deferred logging), so I do not see an explanation there. Could it be caused by other interrupts that are processed at the same time, and that takes some time? (they happen at the same time for some reason)?

  • One thing I forgot to mention is, I have an external PA(RF2411) connected to 52840, so it is also using GPIOTE channel to switch between PA/LNA. 

    52840 is also advertising while the code is running. Do you think it is possible that the PA/LNA GPIOTE task could be the reason of this delay?

  • Hi,

    As you suggested, I took some screenshots from a oscilloscope, toggling a GPIO pin.

    First one is taken when nrf_log is on (I use deferred log)

    Fig. 1

    Second one is taken when nrf_log is totally disabled. 

    Fig. 3

    As you can see when log is on, the delay is between 500us ~ 2.5ms,

    and when log is off, the delay is ~ 4us.

    I have to say that this result surprised me, as I did not expect logging can make such big difference, even in deferred mode. Is it normal? or did I miss some configuration to make it correct?

  • Maybe not directly relevant, but have a look at this nrf_log_frontend_dequeue-must-be-atomically-protected-against-re-entry-from-interrupt-context issue, where I just posted some code following the suggestions by the reporter

  • Another related problem about GPIOTE confused me during the test is, can scheduler event wake CPU up or not?

    To isolate the delay issue, I built a new app from scratch, with the same GPIOTE handling model as original app, but without irrelevant timers and other components.

    as shown above in my test application, the main loop is like:

        // Enter main loop.
        for (;;)
        {
            //handle usbd event sperately;
            //app_usbd_event_queue_process();
            
            app_sched_execute();
            if (NRF_LOG_PROCESS() == false)
            {
    			idle_state_handle();
            }
        }

    this is the regular recommended routine. It is expected when GPIOTE interrupt happening and the event is issued, the CPU wakeup from the sleep mode and process the event.

    However, I found that after my GPIOTE interrupt handler put the event into queue, the CPU seems go back again into sleep, instead of processing the event. So the program just stuck there after GPIOTE interrupt happened for one time.

    The event will only get processed if I commented out the 

    //idle_state_handle();

    line, but now this means CPU never go to sleep.

    So the question is scheduler event not able to wake up CPU? or it is because the GPIOTE interrupt handler somehow put CPU back to sleep again after completion?

  • Hi,

    rolandash said:
    So the question is scheduler event not able to wake up CPU? or it is because the GPIOTE interrupt handler somehow put CPU back to sleep again after completion?

    The schedule is just a queue that you pop in the main loop, so it does not wake the CPU from sleep. However, that is not necessary either. In this case, you call it from an interrupt/event handler, so the CPU is awake. Therefore, it will run until it executes sd_app_evt_wait(). And similarly, after any events/interrupts are processed, the main/thread will run from the last call to sd_app_evt_wait() (or WFE/WFI if called directly).

    Do you call sd_app_evt_wait(), WFE or WFI from anywhere other than a single place in your main loop (via idle_state_handle and possible nrf_pwr_mgmt_run())? If so, that could explain this behaviour.

Reply
  • Hi,

    rolandash said:
    So the question is scheduler event not able to wake up CPU? or it is because the GPIOTE interrupt handler somehow put CPU back to sleep again after completion?

    The schedule is just a queue that you pop in the main loop, so it does not wake the CPU from sleep. However, that is not necessary either. In this case, you call it from an interrupt/event handler, so the CPU is awake. Therefore, it will run until it executes sd_app_evt_wait(). And similarly, after any events/interrupts are processed, the main/thread will run from the last call to sd_app_evt_wait() (or WFE/WFI if called directly).

    Do you call sd_app_evt_wait(), WFE or WFI from anywhere other than a single place in your main loop (via idle_state_handle and possible nrf_pwr_mgmt_run())? If so, that could explain this behaviour.

Children
No Data
Related