Cannot wake up from System OFF

I want to control the device status System ON and OFF.

I'm working on nRF52 DK, nRF Connect SDK v2.4.2.

The buttons are initialized with dk_buttons_init(), and works well.

Then

pm_state_force(0u, &(struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});

can make the device System OFF.

But after that, pushing the button cannot work to wake up the device.

Is some setting lack to make the button work as GPIO  signal detection.

According to the sample /zephyr/samples/boards/nrf/system_off,

I have added the following code. But it could not wake up the device.

/* Configure to generate PORT event (wakeup) on button 1 press. */
nrf_gpio_cfg_input(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios), NRF_GPIO_PIN_PULLUP);
nrf_gpio_cfg_sense_set(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), pios), NRF_GPIO_PIN_SENSE_LOW);


  • Hi,

    The two lines you have there is what you need for configuring the wakeup-pin. I tested now, and this still works on my end when I add the DK library and initialize the buttons with dk_buttons_init() beforehand. Can you share your project so that I can test on my end?

  • Hi, Einar

    Thanks for your reply.

    This is my source code, main.c;

    #include <stdio.h>
    #include <zephyr/kernel.h>
    #include <dk_buttons_and_leds.h>        /* Include DK library for buttons and LEDs */
    #include <zephyr/pm/pm.h>
    #include <zephyr/pm/device.h>
    #include <zephyr/pm/policy.h>
    #include <hal/nrf_gpio.h>
    
    #define SLEEP_TIME_MS 1000      // 1000 ms = 1 sec
    
    // LED Control Flags
    #define LED1    0
    #define LED2    1
    #define LED3    2
    #define LED4    3
    #define LED_CONTROL_STATUS_MASK         0x01
    #define LED_CONTROL_BLINKY_MASK         0x04
    #define LED_CONTROL_BLINK_STATUS_MASK   0x08
    #define LED_CONTROL_LIGHT_ON_DEFAULT    850     // ms
    #define LED_CONTROL_LIGHT_OFF_DEFAULT   650     // ms
    
    typedef struct {
        uint8_t status;                 // Status flags
        uint16_t light_on_interval_ms;  // interval time at blinking light on (ms)
        uint16_t light_off_interval_ms; // interval time at blinking light off (ms)
    } LED_control_information_t;
    
    static LED_control_information_t led_ctl_info;
    
    void LED_lighting(uint8_t led_idx, bool led_on)
    {
        if (led_on) {
            // Turn a LED on.
            dk_set_led_on(led_idx);
        } else {
            // Turn a LED off.
            dk_set_led_off(led_idx);
        }
    }
    
    void button_changed(uint32_t button_state, uint32_t has_changed)
    {
          if (has_changed & 0x01) {
            // Button 0
            if (button_state & has_changed) {
    
                if ( (led_ctl_info.status & LED_CONTROL_STATUS_MASK) == LED_CONTROL_STATUS_MASK ) {
                    // LED off
                    LED_lighting(LED1, false);
    
                    // stop blinky
                    led_ctl_info.status ^= LED_CONTROL_BLINKY_MASK;
                    led_ctl_info.status ^= LED_CONTROL_BLINK_STATUS_MASK;
    
                    led_ctl_info.status ^= LED_CONTROL_STATUS_MASK;
    
                    // Syste OFF
                    // Force usae of given power state.
                    // This function overrides decision made by PM policy forcing usage of given power state upon next entry of the idle thread.
                    // Parameters: CPU index = 0, Power state: substate_id = PM_STATE_SOFT_OFF, min_residency_us = 0, exit_latency_us = 0 
                    bool rc = pm_state_force(0u, &(struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
                    if (!rc) {
                        printk("Fail to system off \n");
                    }
                    k_msleep(SLEEP_TIME_MS);
                    LED_lighting(LED3, true);
                    
                }
                else {
                    // LED on
                    LED_lighting(LED1, true);
    
                    // start blinky
                    led_ctl_info.status |= LED_CONTROL_BLINKY_MASK;
                    led_ctl_info.status |= LED_CONTROL_BLINK_STATUS_MASK;
    
                    led_ctl_info.status |= LED_CONTROL_STATUS_MASK;
                }
            }
            else {
                // button released
            }
          }
    }
    
    
    int main(void)
    {
            int err = 0;
    
            // RTT console
            printk("Hello World! %s\n", CONFIG_BOARD);
    
            // Buttons
            err = dk_buttons_init(button_changed);
            if (err) {
                printk("Cannot init buttons (err: %d)\n", err);
            }
    
    	/* Configure to generate PORT event (wakeup) on button 1 press. */
    	nrf_gpio_cfg_input(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios), NRF_GPIO_PIN_PULLUP);
    	nrf_gpio_cfg_sense_set(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios), NRF_GPIO_PIN_SENSE_LOW);
    
            // LEDs
            err = dk_leds_init(); 
            if (err) {
                printk("Cannot init leds (err: %d)\n", err);
            }
            led_ctl_info.light_on_interval_ms = LED_CONTROL_LIGHT_ON_DEFAULT;
            led_ctl_info.light_off_interval_ms = LED_CONTROL_LIGHT_OFF_DEFAULT;
    
            // Power on
            LED_lighting(LED2, true);
            LED_lighting(LED4, true);
            k_msleep(SLEEP_TIME_MS);
            LED_lighting(LED4, false);
    
    
            while(1) {
                if ( (led_ctl_info.status & LED_CONTROL_BLINKY_MASK) == LED_CONTROL_BLINKY_MASK ) {
                    if ( (led_ctl_info.status & LED_CONTROL_BLINK_STATUS_MASK) == LED_CONTROL_BLINK_STATUS_MASK ) {
                        // blinking light off
                        led_ctl_info.status ^= LED_CONTROL_BLINK_STATUS_MASK;
                        LED_lighting(LED1, false);
    
                        k_msleep(led_ctl_info.light_off_interval_ms);
                    } else {
                        // blinking light on
                        led_ctl_info.status |= LED_CONTROL_BLINK_STATUS_MASK;
                        LED_lighting(LED1, true);
    
                        k_msleep(led_ctl_info.light_on_interval_ms);
                    }
                } else {
                    k_msleep(SLEEP_TIME_MS);
                }
            }
            return 0;
    }

    and proj.conf

    # Enable RTT console 
    CONFIG_RTT_CONSOLE=y
    CONFIG_UART_CONSOLE=n
    # CONFIG_USE_SEGGER_RTT=y  // defined in nrf52dk_nrf52832_defconfig
    
    # Enable DK Library (Buttons and LEDs)
    CONFIG_DK_LIBRARY=y
    
    # Enable power management
    CONFIG_PM=y
    CONFIG_PM_DEVICE=y
    # CONFIG_POWEROFF=y

  • Hi,

    It seems the issue is that you configure the wakeup button too early so that the configuration register gets overwritten at a later point. You can see this by debugging and setting a breakpoint before going to system off. The CNF registe for P0.13 (button 0), should look like this with sense enabled:

    But when I test your application, it is not. The reason turns out to be that in the implementation of the DK buttons and LEDs library, interrupts are disabled, and with that, sense is also disabled (this happens in the static button_pressed() function in dk_buttons_and_leds.c). So you should enable sense as the last thing you do before entering system off mode, moving the two relevant lines to right before the call to pm_state_force().

    Note that even with this change there is a theoretical chance for getting a button interrupt between enabling sense and entering system off mode, so it could be good to disable interrupt with irq_lock() before enabling sense and entering system off. This way there is no risk of getting the wakeup configuration modified by the button library (or anything else) before entering system OFF.

    Therefore I suggest you change your code slightly and enter system OFF doing something like this:

    /* Lock interrupts to prevent anything else from happening when preparing for system OFF. */
    int key = irq_lock();
    
    /* Configure to generate PORT event (wakeup) on button 1 press. */
    
    nrf_gpio_cfg_input(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios), NRF_GPIO_PIN_PULLUP);
    nrf_gpio_cfg_sense_set(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios), NRF_GPIO_PIN_SENSE_LOW);
    
    /* Enter system OFF. On success, pm_state_force will not return() */
    bool rc = pm_state_force(0u, &(struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
    
    /* Unlock IRQ (Though execution should never reach here unless there is an error) */
    irq_unlock(key);
    
    if (!rc) {
        printk("Fail to enter system OFF.\n");
    }

  • Hi, Einar

    Thank you for your test and kind explanation.

    It works.

Related