Cannot wakeup if DETECT asserted before entering SYSOFF

Hello

We have a product made with nrf52840 and powered with coin battery. There is a button for user to turn on/off (52840 entering and exiting from SYSOFF mode). In working mode, the button is configured with push event to trigger a shutdown process. In shutdown handler, the same button is configured as wakeup pin. The code is shown below

static bool app_shutdown_handler(nrf_pwr_mgmt_evt_t event)
{
    ret_code_t err_code;

    err_code = bsp_buttons_disable();
    APP_ERROR_CHECK(err_code);
    err_code = app_timer_stop(m_power_ctrl_timer);
    APP_ERROR_CHECK(err_code);

    if (NRF_PWR_MGMT_EVT_PREPARE_WAKEUP == event)
    {
        err_code = bsp_wakeup_button_enable(BTN_ID_WAKEUP);
        APP_ERROR_CHECK(err_code);
    }

    return true;
}

We have a LED blinking to indicate system ON/OFF state. Usually users push button to power on, wait for LED blinking once, then push button to power off, and wait for LED blinking twice. It works well with such procedure. However if user does not wait for LED blinking and just press button quickly, after repeating ON/OFF for several cycles, the CPU will enter SYSOFF state and cannot wake up anymore. 

We suspect the reason is that wakeup pin asserted before entering SYSOFF mode. And we made a faked wakeup pin asserted early case to confirm, by following code

    if (NRF_PWR_MGMT_EVT_PREPARE_WAKEUP == event)
    {
        err_code = bsp_wakeup_button_enable(BTN_ID_WAKEUP);
        APP_ERROR_CHECK(err_code);

        // fake early asserted wakeup signal
        nrf_gpio_pin_clear(BTN_ID_WAKEUP);
        nrf_gpio_pin_dir_set(bsp_board_button_idx_to_pin(BTN_ID_WAKEUP), NRF_GPIO_PIN_DIR_OUTPUT);
        nrf_gpio_cfg_watcher(bsp_board_button_idx_to_pin(BTN_ID_WAKEUP));
    }

We can see that after first time button pushed and released, the pin is pulled down (active low, BUTTONS_ACTIVE_STATE 0). But system stays in SYSOFF mode, did not wakeup when wakeup pin asserted before entering SYSOFF.

We need the product can wakeup no matter how the button is pressed. And we cannot modify PCB any more. Any suggestion to avoid such failure with firmware? 

Thanks in advanced.

Changchun

 

  • I have not tested this myself byt based on the description of the issue, it seems that there could be a race condition in how you are handling the events. It seems there there can be a situation where the pin is not configured with sense and input connected even after the button press. 

    You can verify this by reading into the GPIO register values for this pin configuration register to see that when the issue happens, the pin configuration register has its sense and input connect configured correctly.

  • Yes, I checked the pin configuration after calling bsp_wakeup_button_enable, nrf_gpio_pin_sense_get always return DISABLED if the button is pressed at around time, and wakeup pin does not work. I do not understand what is race condition you mentioned. It looks to me bsp_wakeup_button_enable just write to a register.

  • Anyway I can solve the problem. Just check the pin sense configuration, if it is not correct, suspend shutdown procedure with return false, and try to continue shutdown after 200 ms. My shutdown handler is as below

    static bool app_shutdown_handler(nrf_pwr_mgmt_evt_t event)
    {
        ret_code_t err_code;
        static bool has_called = false;
    
        if (!has_called)
        {
            err_code = bsp_buttons_disable();
            APP_ERROR_CHECK(err_code);
            err_code = app_timer_stop(m_power_ctrl_timer);
            APP_ERROR_CHECK(err_code);
    
            has_called = true;
        }
    
        if (NRF_PWR_MGMT_EVT_PREPARE_WAKEUP == event)
        {
            err_code = bsp_wakeup_button_enable(BTN_ID_WAKEUP);
            APP_ERROR_CHECK(err_code);
    
            nrf_gpio_pin_sense_t sense = nrf_gpio_pin_sense_get(bsp_board_button_idx_to_pin(BTN_ID_WAKEUP));
    
            if (NRF_GPIO_PIN_NOSENSE == sense)
            {
                err_code = app_timer_start(m_power_ctrl_timer, APP_TIMER_TICKS(200), NULL);
                APP_ERROR_CHECK(err_code);
    
                return false;
            }
        }
    
        return true;
    }

    In the timer callback just call nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_CONTINUE)

    Thanks for your help!

  • Update

    I understood where the race condition comes from. Bsp_buttons_disable only set the pin to NO_SENSE but did not prevent a pending event from being servered. nrf_gpio_cfg_sense_set is called in port event handler to flip sense level direction. Need to call nrfx_gpiote_in_uninit  to clear port_handlers_pins, then pending event will be ignored.

Related