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

Calling app_sched_event_put inside interrupt

Hi, I'm debugging a small app that is using the LPCOMP to trigger an interrupt when the BLE device is turned off. We have a capacitor to maintain alive the nRF for a while after the power is turned off (the idea is to let the nRF store some data in flash before die).

This is the code I use to configure and use the LPCOMP:

void LPCOMP_init(void) {
/* Enable interrupt on LPCOMP CROSS event */		
NRF_LPCOMP->INTENSET = LPCOMP_INTENSET_DOWN_Msk;
NVIC_EnableIRQ(LPCOMP_IRQn);

//  5 : 5V,  ref. 1/8 VDD
// 12 : 12V, ref. 3/8 VDD
// 24 : 24V, ref. 6/8 VDD

/* Configure LPCOMP */
if (power == 5) 
	NRF_LPCOMP->REFSEL = (LPCOMP_REFSEL_REFSEL_SupplyOneEighthPrescaling << LPCOMP_REFSEL_REFSEL_Pos);
else if (power == 12)
	NRF_LPCOMP->REFSEL = (LPCOMP_REFSEL_REFSEL_SupplyThreeEighthsPrescaling << LPCOMP_REFSEL_REFSEL_Pos);
else
	NRF_LPCOMP->REFSEL = (LPCOMP_REFSEL_REFSEL_SupplySixEighthsPrescaling << LPCOMP_REFSEL_REFSEL_Pos);

/* Configure LPCOMP - set reference input source to AIN2 */
NRF_LPCOMP->PSEL |= (LPCOMP_PSEL_PSEL_AnalogInput2 << LPCOMP_PSEL_PSEL_Pos);

/* Enable and start the low power comparator */
NRF_LPCOMP->ENABLE = LPCOMP_ENABLE_ENABLE_Enabled;	
NRF_LPCOMP->TASKS_START = 1;
}

/* Interrupt handler for LPCOMP */
void LPCOMP_COMP_IRQHandler(void)
{
uint32_t err_code;

// Clear event
NRF_LPCOMP->EVENTS_DOWN = 0;
// Stop LPCOMP
NRF_LPCOMP->TASKS_STOP = 1;
NRF_LPCOMP->ENABLE = LPCOMP_ENABLE_ENABLE_Disabled;

// Debug
nrf_gpio_pin_set(GPIO_PIN);

// Append store task in scheduler
err_code = app_sched_event_put(NULL, 0, config_store);
APP_ERROR_CHECK(err_code);

// Debug
nrf_gpio_pin_clear(GPIO_PIN);
}

void config_store(void *p_data, uint16_t p_size)
{
// Stop Advertising
sd_ble_gap_adv_stop();

// Store current configuration to flash
ps_config_store(&m_config);
}


int main(void)
{

...

// Enter main loop.
for (;;) {
    app_sched_execute();
	sd_app_evt_wait();
}
}

The problem I'm facing with this configuration is that when I call the app_sched_event_put() the code hangs and stops working. Looking inside at the app_sched_event_put() I see that there are two calls to CRITICAL_REGION_ENTER() and CRITICAL_REGION_EXIT(), I tried to comment out those calls and now the code works as expected. So my question is, why the CRITICAL_REGION_ENTER/EXIT macros are causing this problem?

The software is compiled with -DSOFTDEVICE_PRESENT and SoftDevice is enabled and inited.

  • I doubt the code hangs and stops working - I expect it goes to the hardfault handler, which a debugger would tell you.

    If that's so, most likely because your interrupt handler is running at HIGH priority and the critical region macros call a softdevice function and you can't call softdevice functions from APP_HIGH priority interrupt handlers, only from LOW or main thread context.

  • Hi, thanks for your answer. I understand what you are saying, but I think the CRITICAL_REGION macro was defined like this:

    #define CRITICAL_REGION_ENTER()                                                             \
    {                                                                                       \
        uint8_t IS_NESTED_CRITICAL_REGION = 0;                                              \
        uint32_t CURRENT_INT_PRI = current_int_priority_get();                              \
        if (CURRENT_INT_PRI != APP_IRQ_PRIORITY_HIGH)                                       \
        {                                                                                   \
            uint32_t ERR_CODE = sd_nvic_critical_region_enter(&IS_NESTED_CRITICAL_REGION);  \
            if (ERR_CODE == NRF_ERROR_SOFTDEVICE_NOT_ENABLED)                               \
            {                                                                               \
                __disable_irq();                                                            \
            }                                                                               \
            else                                                                            \
            {                                                                               \
                APP_ERROR_CHECK(ERR_CODE);                                                  \
            }                                                                               \
        }
    

    So I thought that if the macro was called inside a HIGH priority interrupt it doesn't call the sd_nvic_critical_region_enter().

  • Hi, I think the problem is in the macro. To debug my code I put a

    uint32_t pri = current_int_priority_get();
    printf("pri = %u\r\n",(unsigned int)pri);
    

    and I've found that the interrupt priority value is 0 (APP_IRQ_PRIORITY_HIGHEST). The macro executes sd_nvic_critical_region_enter() if (CURRENT_INT_PRI != APP_IRQ_PRIORITY_HIGH), but the interrupt priority is APP_IRQ_PRIORITY_HIGHEST and the macro calls sd_nvic_critical_region_enter() causing the hardfault. I think that the macro should check if (CURRENT_INT_PRI == APP_IRQ_PRIORITY_LOW) instead of (CURRENT_INT_PRI != APP_IRQ_PRIORITY_HIGH).

  • But the app priority shouldn't be APP_PRIORITY_HIGHEST because only the softdevice uses that, unless you never set the priority of your LPCOMP interrupt and you started it after you started the softdevice (which checks interrupt priorities on startup and fails if they are wrong).

  • Yes, in this test software the LPCOMP was inited after the SoftDevice and the priority of the interrupt wasn't set. I will try to use the nrf_drv_lpcomp driver and set the interrupt priority to LOW in order to use the app_sched_event_put().

Related