Setting up Deep Sleep and Wake Up interrupt

Hi

I have a nordic NRF52833 interfacing an IMU using I2C and an interrupt signal going to GPIO 13 on the NRF device. 

I am using Zephyr and I was able to integrate the IMU driver LISD2/3H including the wake up interrupt. However,  when trying to put the NRF to sleep and configuring the same pin as interrupt/GPIO, I don't get the interrupt. Wondering if I am missing a step.

Here is the main file:

/* main.c - Application main entry point */

/*
 * Copyright (c) 2015-2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <sys/printk.h>
#include <sys/util.h>
#include <device.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <drivers/sensor.h>
#include <drivers/gpio.h>
#include <stdio.h>
#include <device.h>
#include <init.h>
#include <pm/pm.h>
#include <pm/device.h>
#include <zephyr.h>
#include <hal/nrf_gpio.h>


#define DEVICE_NAME "VTTI_Beacon"
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)

/* TWI instance ID. */
#define TWI_INSTANCE_ID             0
#define MAX_PENDING_TRANSACTIONS    33
#define LIS2DH12_MIN_QUEUE_SIZE     32


#define CONSOLE_LABEL DT_LABEL(DT_CHOSEN(zephyr_console))

#define BUSY_WAIT_S 2U
#define SLEEP_S 2U
#define SLEEP_S1 5U

/* Prevent deep sleep (system off) from being entered on long timeouts
 * or `K_FOREVER` due to the default residency policy.
 *
 * This has to be done before anything tries to sleep, which means
 * before the threading system starts up between PRE_KERNEL_2 and
 * POST_KERNEL.  Do it at the start of PRE_KERNEL_2.
 */
static int disable_ds_1(const struct device *dev)
{
	ARG_UNUSED(dev);

	pm_constraint_set(PM_STATE_SOFT_OFF);
	return 0;
}

SYS_INIT(disable_ds_1, PRE_KERNEL_2, 0);


static int imu_flag = 0;

static void fetch_and_display(const struct device *sensor)
{
	static unsigned int count;
	struct sensor_value accel[3];
	struct sensor_value temperature;
        struct sensor_value value_x;
	const char *overrun = "";
	int rc = sensor_sample_fetch(sensor);
        struct sensor_trigger trig;


	++count;
	if (rc == -EBADMSG) {
		/* Sample overrun.  Ignore in polled mode. */
		if (IS_ENABLED(CONFIG_LIS2DH_TRIGGER)) {
			overrun = "[OVERRUN] ";
		}
		rc = 0;
	}
	if (rc == 0) {
		/*rc = sensor_channel_get(sensor,
					SENSOR_CHAN_ACCEL_XYZ,
					accel);*/

                rc = sensor_channel_get(sensor, SENSOR_CHAN_ACCEL_X, &value_x);
	}
	if (rc < 0) {
		printf("ERROR: Update failed: %d\n", rc);
	} else {
		/*printf("#%u @ %u ms: %sx %f , y %f , z %f",
		       count, k_uptime_get_32(), overrun,
		       value_x.val2,
		       sensor_value_to_double(&accel[1]),
		       sensor_value_to_double(&accel[2]));*/
                 if(imu_flag==0)
                 {
                	 printf("Accel X Data:  %d %d \n", value_x.val1, value_x.val2);
                   imu_flag = 1;
                 }	
	}


}

#ifdef CONFIG_LIS2DH_TRIGGER
static void trigger_handler(const struct device *dev,
			    const struct sensor_trigger *trig)
{
	fetch_and_display(dev);
}
#endif


/*
 * Set Advertisement data. Based on the Eddystone specification:
 * https://github.com/google/eddystone/blob/master/protocol-specification.md
 * https://github.com/google/eddystone/tree/master/eddystone-url
 */
static const struct bt_data ad[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
	BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0xaa, 0xfe),
	BT_DATA_BYTES(BT_DATA_SVC_DATA16,
		      0xaa, 0xfe, /* Eddystone UUID */
		      0x10, /* Eddystone-URL frame type */
		      0x00, /* Calibrated Tx power at 0m */
		      0x00, /* URL Scheme Prefix http://www. */
		      'v', 't', 't', 'i', '.', 'v','t','.',
		      'e', 'd', 'u',
		      0x08) /* .org */
};

/* Set Scan Response data */
static const struct bt_data sd[] = {
	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};

static void bt_ready(int err)
{
	char addr_s[BT_ADDR_LE_STR_LEN];
	bt_addr_le_t addr = {0};
	size_t count = 1;

	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}

	printk("Bluetooth initialized\n");

	/* Start advertising */
	err = bt_le_adv_start(BT_LE_ADV_NCONN_IDENTITY, ad, ARRAY_SIZE(ad),
			      sd, ARRAY_SIZE(sd));
	if (err) {
		printk("Advertising failed to start (err %d)\n", err);
		return;
	}


	/* For connectable advertising you would use
	 * bt_le_oob_get_local().  For non-connectable non-identity
	 * advertising an non-resolvable private address is used;
	 * there is no API to retrieve that.
	 */

	bt_id_get(&addr, &count);
	bt_addr_le_to_str(&addr, addr_s, sizeof(addr_s));

	printk("Beacon started, advertising as %s\n", addr_s);
}

void main(void)
{
	int err;

	printk("Starting Beacon Demo\n");

        const struct device *sensor = DEVICE_DT_GET_ANY(st_lis2dh);

	if (sensor == NULL) {
		printf("No device found\n");
		return;
	}
	if (!device_is_ready(sensor)) {
		printf("Device %s is not ready\n", sensor->name);
		return;
	}

        struct sensor_value value_x;
        
        sensor_sample_fetch(sensor);
        sensor_channel_get(sensor, SENSOR_CHAN_ACCEL_X, &value_x);

	/* Initialize the Bluetooth Subsystem */
	err = bt_enable(bt_ready);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
	}
        
       struct sensor_trigger trig;
       int rc;
        
        #if CONFIG_LIS2DH_TRIGGER
	{
		

		trig.type = SENSOR_TRIG_DATA_READY;
		trig.chan = SENSOR_CHAN_ACCEL_XYZ;

		if (IS_ENABLED(CONFIG_LIS2DH_ODR_RUNTIME)) {
			struct sensor_value odr = {
				.val1 = 1,
			};

			rc = sensor_attr_set(sensor, trig.chan,
					     SENSOR_ATTR_SAMPLING_FREQUENCY,
					     &odr);
			if (rc != 0) {
				printf("Failed to set odr: %d\n", rc);
				return;
			}
			printf("Sampling at %u Hz\n", odr.val1);
		}

		rc = sensor_trigger_set(sensor, &trig, trigger_handler);
		if (rc != 0) {
			printf("Failed to set trigger: %d\n", rc);
			return;
		}

		printf("Waiting for triggers\n");
		/*while (true) {
			k_sleep(K_MSEC(2000));
                        if(imu_flag==1)
                        {
                            rc = sensor_trigger_set(sensor, &trig, trigger_handler);
                            if (rc != 0) {
                                    printf("Failed to set trigger: %d\n", rc);
                                    return;
                            }
                            imu_flag = 0;
                        }
		}*/
	}

        //int rc;
        const struct device *cons = device_get_binding(CONSOLE_LABEL);

	/* Configure to generate PORT event (wakeup) on button 1 press. */
	nrf_gpio_cfg_input(DT_GPIO_PIN(DT_NODELABEL(button1), gpios),
			   NRF_GPIO_PIN_PULLUP);
	nrf_gpio_cfg_sense_set(DT_GPIO_PIN(DT_NODELABEL(button1), gpios),
			       NRF_GPIO_PIN_SENSE_LOW);

	printk("Busy-wait %u s\n", BUSY_WAIT_S);
	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);

	printk("Busy-wait %u s with UART off\n", BUSY_WAIT_S);
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);

	printk("Sleep %u s\n", SLEEP_S);
	k_sleep(K_SECONDS(SLEEP_S));

	printk("Sleep %u s with UART off\n", SLEEP_S);
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
	k_sleep(K_SECONDS(SLEEP_S));
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);

        printk("Sleep %u s\n", SLEEP_S1);
	k_sleep(K_SECONDS(SLEEP_S1));

	printk("Entering system off; press BUTTON1 to restart\n");
                trig.type = SENSOR_TRIG_DATA_READY;
		trig.chan = SENSOR_CHAN_ACCEL_XYZ;

		if (IS_ENABLED(CONFIG_LIS2DH_ODR_RUNTIME)) {
			struct sensor_value odr = {
				.val1 = 1,
			};

			rc = sensor_attr_set(sensor, trig.chan,
					     SENSOR_ATTR_SAMPLING_FREQUENCY,
					     &odr);
			if (rc != 0) {
				printf("Failed to set odr: %d\n", rc);
				return;
			}
			printf("Sampling at %u Hz\n", odr.val1);
		}

		rc = sensor_trigger_set(sensor, &trig, trigger_handler);
		if (rc != 0) {
			printf("Failed to set trigger: %d\n", rc);
			return;
		}

        /* Configure to generate PORT event (wakeup) on button 1 press. */
	nrf_gpio_cfg_input(DT_GPIO_PIN(DT_NODELABEL(button1), gpios),
			   NRF_GPIO_PIN_PULLUP);
	nrf_gpio_cfg_sense_set(DT_GPIO_PIN(DT_NODELABEL(button1), gpios),
			       NRF_GPIO_PIN_SENSE_LOW);
	/* Above we disabled entry to deep sleep based on duration of
	 * controlled delay.  Here we need to override that, then
	 * force entry to deep sleep on any delay.
	 */
	pm_power_state_force(0u, (struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});




	printk("ERROR: System off failed\n");
	while (true) {
            if(imu_flag==1)
            {
                rc = sensor_trigger_set(sensor, &trig, trigger_handler);
                if (rc != 0) {
                        printf("Failed to set trigger: %d\n", rc);
                        return;
                }
                imu_flag = 0;
            }
            /* spin to avoid fall-off behavior */
	}

#else /* CONFIG_LIS2DH_TRIGGER */
        while (true) {
        
        sensor_sample_fetch(sensor);
        sensor_channel_get(sensor, SENSOR_CHAN_ACCEL_X, &value_x);
        printf("Accel X Data:  %d %d \n", value_x.val1, value_x.val2);
        k_sleep(K_MSEC(100));

	}
       
    #endif /* CONFIG_LIS2DH_TRIGGER */

	

}

  • Hi,

    Yes, the pin appears to be configured correctly, but judging from the power consumption, it seems that the device is not entering System OFF mode. Did you disconnect the debugger before taking the measurement? If not, it will enter Emulated System OFF mode and keep the CPU active.

  • I removed the debugger from my board so wondering what else it could be impacting on the system  current consumption and not going into power save mode. Any suggestions how to debug this?

  • Does the power consumption remain high if you power cycle the board too?

  • I tried pressing the reset button. There is a small time window when current goes low but not enough to consider deep sleep

  • Here is the main.c file that I am using

    .

    /* main.c - Application main entry point */
    
    /*
     * Copyright (c) 2015-2016 Intel Corporation
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/types.h>
    #include <stddef.h>
    #include <sys/printk.h>
    #include <sys/util.h>
    #include <device.h>
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/hci.h>
    #include <drivers/sensor.h>
    #include <drivers/gpio.h>
    #include <stdio.h>
    #include <device.h>
    #include <init.h>
    #include <pm/pm.h>
    #include <pm/device.h>
    #include <zephyr.h>
    #include <hal/nrf_gpio.h>
    
    
    #define DEVICE_NAME "VTTI_Beacon_3"
    #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
    
    /* TWI instance ID. */
    #define TWI_INSTANCE_ID             0
    #define MAX_PENDING_TRANSACTIONS    33
    #define LIS2DH12_MIN_QUEUE_SIZE     32
    
    
    #define CONSOLE_LABEL DT_LABEL(DT_CHOSEN(zephyr_console))
    
    #define BUSY_WAIT_S 2U
    #define SLEEP_S 2U
    #define SLEEP_S1 5U
    
    
    /* 1000 msec = 1 sec */
    #define SLEEP_TIME	1000
    
    
    /* 1000 msec = 1 sec */
    #define SLEEP_TIME	1000
    
    #define BT_LE_ADV_CONN_NAME_SLOW BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \
    					    BT_LE_ADV_OPT_USE_NAME, \
    					    BT_GAP_ADV_SLOW_INT_MIN, \
    					    BT_GAP_ADV_SLOW_INT_MAX)
    
    
    #define BT_LE_ADV_CONN_NAME_SLOW BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \
                                                BT_LE_ADV_OPT_USE_NAME, \
                                                BT_GAP_ADV_SLOW_INT_MIN, \
                                                BT_GAP_ADV_SLOW_INT_MAX)
    
    
    static uint8_t mfg_data[] = { 0xff, 0xff, 0xAB };
    
    static const struct bt_data ad[] = {
            BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, 3),
    };
    
    
    
    /* Prevent deep sleep (system off) from being entered on long timeouts
     * or `K_FOREVER` due to the default residency policy.
     *
     * This has to be done before anything tries to sleep, which means
     * before the threading system starts up between PRE_KERNEL_2 and
     * POST_KERNEL.  Do it at the start of PRE_KERNEL_2.
     */
    static int disable_ds_1(const struct device *dev)
    {
    	ARG_UNUSED(dev);
    
    	pm_constraint_set(PM_STATE_SOFT_OFF);
    	return 0;
    }
    
    SYS_INIT(disable_ds_1, PRE_KERNEL_2, 0);
    
    
    static int imu_flag = 0;
    
    static void fetch_and_display(const struct device *sensor)
    {
    	static unsigned int count;
    	struct sensor_value accel[3];
    	struct sensor_value temperature;
            struct sensor_value value_x;
    	const char *overrun = "";
    	int rc = sensor_sample_fetch(sensor);
            struct sensor_trigger trig;
    
    
    	++count;
    	if (rc == -EBADMSG) {
    		/* Sample overrun.  Ignore in polled mode. */
    		if (IS_ENABLED(CONFIG_LIS2DH_TRIGGER)) {
    			overrun = "[OVERRUN] ";
    		}
    		rc = 0;
    	}
    	if (rc == 0) {
    		/*rc = sensor_channel_get(sensor,
    					SENSOR_CHAN_ACCEL_XYZ,
    					accel);*/
    
                    rc = sensor_channel_get(sensor, SENSOR_CHAN_ACCEL_X, &value_x);
    	}
    	if (rc < 0) {
    		printf("ERROR: Update failed: %d\n", rc);
    	} else {
    		/*printf("#%u @ %u ms: %sx %f , y %f , z %f",
    		       count, k_uptime_get_32(), overrun,
    		       value_x.val2,
    		       sensor_value_to_double(&accel[1]),
    		       sensor_value_to_double(&accel[2]));*/
                     if(imu_flag==0)
                     {
                    	 printf("Accel X Data:  %d %d \n", value_x.val1, value_x.val2);
                       imu_flag = 1;
                     }	
    	}
    
    
    }
    
    #ifdef CONFIG_LIS2DH_TRIGGER
    static void trigger_handler(const struct device *dev,
    			    const struct sensor_trigger *trig)
    {
    	fetch_and_display(dev);
    }
    #endif
    
    
    /*
     * Set Advertisement data. Based on the Eddystone specification:
     * https://github.com/google/eddystone/blob/master/protocol-specification.md
     * https://github.com/google/eddystone/tree/master/eddystone-url
     */
    /*static const struct bt_data ad[] = {
    	BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
    	BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0xaa, 0xfe),
    	BT_DATA_BYTES(BT_DATA_SVC_DATA16,
    		      0xaa, 0xfe, 
    		      0x10, 
    		      0x00, 
    		      0x00, 
    		      'v', 't', 't', 'i', '.', 'v','t','.',
    		      'e', 'd', 'u',
    		      0x08) 
    };*/
    
    /* Set Scan Response data */
    static const struct bt_data sd[] = {
    	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
    };
    
    static void bt_ready(int err)
    {
    	char addr_s[BT_ADDR_LE_STR_LEN];
    	bt_addr_le_t addr = {0};
    	size_t count = 1;
    
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return;
    	}
    
    	printk("Bluetooth initialized\n");
    
    	/* Start advertising */
    	err = bt_le_adv_start(BT_LE_ADV_NCONN_IDENTITY, ad, ARRAY_SIZE(ad),
    			      sd, ARRAY_SIZE(sd));
    	if (err) {
    		printk("Advertising failed to start (err %d)\n", err);
    		return;
    	}
    
    
    	/* For connectable advertising you would use
    	 * bt_le_oob_get_local().  For non-connectable non-identity
    	 * advertising an non-resolvable private address is used;
    	 * there is no API to retrieve that.
    	 */
    
    	bt_id_get(&addr, &count);
    	bt_addr_le_to_str(&addr, addr_s, sizeof(addr_s));
    
    	printk("Beacon started, advertising as %s\n", addr_s);
    }
    
    
    
    
    void main(void)
    {
    	int err;
    
    	printk("Starting Beacon Demo\n");
    
            const struct device *sensor = DEVICE_DT_GET_ANY(st_lis2dh);
    
    	if (sensor == NULL) {
    		printf("No device found\n");
    		return;
    	}
    	if (!device_is_ready(sensor)) {
    		printf("Device %s is not ready\n", sensor->name);
    		return;
    	}
    
            struct sensor_value value_x;
            
            sensor_sample_fetch(sensor);
            sensor_channel_get(sensor, SENSOR_CHAN_ACCEL_X, &value_x);
    
    
    	err = bt_enable(bt_ready);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    	}
            
           struct sensor_trigger trig;
           int rc;
            
            #if CONFIG_LIS2DH_TRIGGER
    	{
    		
    
    		trig.type = SENSOR_TRIG_DATA_READY;
    		trig.chan = SENSOR_CHAN_ACCEL_XYZ;
    
    		if (IS_ENABLED(CONFIG_LIS2DH_ODR_RUNTIME)) {
    			struct sensor_value odr = {
    				.val1 = 1,
    			};
    
    			rc = sensor_attr_set(sensor, trig.chan,
    					     SENSOR_ATTR_SAMPLING_FREQUENCY,
    					     &odr);
    			if (rc != 0) {
    				printf("Failed to set odr: %d\n", rc);
    				return;
    			}
    			printf("Sampling at %u Hz\n", odr.val1);
    		}
    
    		rc = sensor_trigger_set(sensor, &trig, trigger_handler);
    		if (rc != 0) {
    			printf("Failed to set trigger: %d\n", rc);
    			return;
    		}
    
    		printf("Waiting for triggers\n");
    		/*while (true) {
    			k_sleep(K_MSEC(2000));
                            if(imu_flag==1)
                            {
                                rc = sensor_trigger_set(sensor, &trig, trigger_handler);
                                if (rc != 0) {
                                        printf("Failed to set trigger: %d\n", rc);
                                        return;
                                }
                                imu_flag = 0;
                            }
    		}*/
    	}
    
            //int rc;
            const struct device *cons = device_get_binding(CONSOLE_LABEL);
    
    	
    	nrf_gpio_cfg_input(DT_GPIO_PIN(DT_NODELABEL(button1), gpios),
    			   NRF_GPIO_PIN_PULLUP);
    	nrf_gpio_cfg_sense_set(DT_GPIO_PIN(DT_NODELABEL(button1), gpios),
    			       NRF_GPIO_PIN_SENSE_LOW);
    
    	printk("Busy-wait %u s\n", BUSY_WAIT_S);
    	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);
    
    	printk("Busy-wait %u s with UART off\n", BUSY_WAIT_S);
    	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
    	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);
    	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);
    
    	printk("Sleep %u s\n", SLEEP_S);
    	k_sleep(K_SECONDS(SLEEP_S));
    
    	printk("Sleep %u s with UART off\n", SLEEP_S);
    	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
    	k_sleep(K_SECONDS(SLEEP_S));
    	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);
    
            printk("Sleep %u s\n", SLEEP_S1);
    	k_sleep(K_SECONDS(SLEEP_S1));
    
    	printk("Entering system off; press BUTTON1 to restart\n");
                    trig.type = SENSOR_TRIG_DATA_READY;
    		trig.chan = SENSOR_CHAN_ACCEL_XYZ;
    
    		if (IS_ENABLED(CONFIG_LIS2DH_ODR_RUNTIME)) {
    			struct sensor_value odr = {
    				.val1 = 1,
    			};
    
    			rc = sensor_attr_set(sensor, trig.chan,
    					     SENSOR_ATTR_SAMPLING_FREQUENCY,
    					     &odr);
    			if (rc != 0) {
    				printf("Failed to set odr: %d\n", rc);
    				return;
    			}
    			printf("Sampling at %u Hz\n", odr.val1);
    		}
    
    		rc = sensor_trigger_set(sensor, &trig, trigger_handler);
    		if (rc != 0) {
    			printf("Failed to set trigger: %d\n", rc);
    			return;
    		}
    
            
    	nrf_gpio_cfg_input(DT_GPIO_PIN(DT_NODELABEL(button1), gpios),
    			   NRF_GPIO_PIN_PULLUP);
    	nrf_gpio_cfg_sense_set(DT_GPIO_PIN(DT_NODELABEL(button1), gpios),
    			       NRF_GPIO_PIN_SENSE_LOW);
    	
    	pm_power_state_force(0u, (struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
    
    
    
    
    	printk("ERROR: System off failed\n");
    	while (true) {
                if(imu_flag==1)
                {
                    rc = sensor_trigger_set(sensor, &trig, trigger_handler);
                    if (rc != 0) {
                            printf("Failed to set trigger: %d\n", rc);
                            return;
                    }
                    imu_flag = 0;
                }
               
    	}
    
    #else 
            while (true) {
            
            sensor_sample_fetch(sensor);
            sensor_channel_get(sensor, SENSOR_CHAN_ACCEL_X, &value_x);
            printf("Accel X Data:  %d %d \n", value_x.val1, value_x.val2);
            k_sleep(K_MSEC(100));
    
    	}
           
        #endif 
    
    	
    
    }
    
    

Related