Feedback
Feedback

If you are having issues with the exercises, please create a ticket on DevZone: devzone.nordicsemi.com
Click or drag files to this area to upload. You can upload up to 2 files.

Exercise 2 – Adding power management to a custom driver

In this exercise, we will add power management to the custom driver created in Exercise 1, as described in Device power management.

We will enable and implement device runtime power management in our custom driver. We will also enable this strategy for the SPI driver so that both the BME280 driver and the SPI bus driver will be suspended when inactive.

Open the code base of the exercise by navigating to Create a new application in the nRF Connect for VS Code extension, select Copy a sample, and search for Lesson 7 – Exercise 2.

Alternatively, in the GitHub repository for this course, go to the base code for this exercise, found in l7/l7_e2.

Exercise steps

1. Define the power callback.

Firstly, we need to declare the power states for our driver. In these, we will define what action will be taken on a power state request. The BME280 sensor supports sleep mode; we can look into the documentation to figure out how to enable it. It will be used when our driver goes into suspend mode. When it needs to be resumed, we will initialize the BME280 sensor to make it ready to provide data.

Modify custom_bme280.c in the custom driver module by adding the following code snippet

static int custom_bme280_pm_action(const struct device *dev,
			    enum pm_device_action action)
{
	int ret = 0;

	switch (action) {
	case PM_DEVICE_ACTION_RESUME:
		LOG_INF("Resuming BME280 sensor");
		/* Re-initialize the chip */
		ret = custom_bme280_init(dev);
		break;
	case PM_DEVICE_ACTION_SUSPEND:
		LOG_INF("Suspending BME280 sensor");
		/* Put the chip into sleep mode */
		ret = bme280_reg_write(dev,
			CTRLMEAS,
			0x93);

		if (ret < 0) {
			LOG_DBG("CTRL_MEAS write failed: %d", ret);
		}
		break;
	default:
		return -ENOTSUP;
	}

	return ret;
}
C

We want to observe the behavior of our driver on the console. Every time the device driver changes the power state, it prints out its name.

2. Modify custom_bme280_sample_fetch() to let the power management subsystem know when the driver needs to be used.

Now we want to use pm_device_runtime_get() and pm_device_runtime_put() to let the power management subsystem know when the driver needs to be used. As this makes sense only when device runtime power management is enabled, we will check it during the sampling procedure by using IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME).

The whole function should look like this:

	static int custom_bme280_sample_fetch(const struct device *dev,
				      enum sensor_channel chan)
{
	struct custom_bme280_data *data = dev->data;

	uint8_t buf[8];
	int32_t adc_press, adc_temp, adc_humidity;
	int size = 8;
	int err;

	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);

  /* let power management system know that driver needs device to be active */

	if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)){
  /* Check if device runtime power managemeng is enabled */
		pm_device_runtime_get(dev);
	}

	err = bme280_wait_until_ready(dev);
	if (err < 0) {
	  /* Check if device runtime power managemeng is enabled */
		if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME))
		{
		 /* let power management system know that device is no longer needed needed  */
			pm_device_runtime_put(dev);
		}
		return err;
	}

	err = bme280_reg_read(dev, PRESSMSB, buf, size);
	if (err < 0) {
	   /* Check if device runtime power managemeng is enabled */
		if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME))
		{
		  /* let power management system know that device is no longer needed needed  */
			pm_device_runtime_put(dev);
		}
		return err;
	}

	LOG_INF("Sensor Data acquired");

	adc_press = (buf[0] << 12) | (buf[1] << 4) | (buf[2] >> 4);
	adc_temp = (buf[3] << 12) | (buf[4] << 4) | (buf[5] >> 4);
	adc_humidity = (buf[6] << 8) | buf[7];

	bme280_compensate_temp(data, adc_temp);
	bme280_compensate_press(data, adc_press);
	bme280_compensate_humidity(data, adc_humidity);

  /* Check if device runtime power managemeng is enabled */
	if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)){
	
	  /* let power management system know that device is no longer needed needed  */
		pm_device_runtime_put(dev);
	}

	return 0;
}
C

Recall

We need to be aware that custom_bme280_sample_fetch() function uses the SPI driver for acquiring sensor data. As a consequence of that, the SPI driver applies a device runtime power management strategy to suspend the bus when not needed. Thanks to that, both drivers act in a hierarchical relation to provide lower power consumption

3 Add the power management structures into device definition

3.1 Attach the power management function to the device node.

Now we will attach the power management function custom_bme280_pm_action() to the device node using PM_DEVICE_DT_INST_DEFINE().

PM_DEVICE_DT_INST_DEFINE(inst, custom_bme280_pm_action);					\
C

3.2 Use the power management resources in the device definition.

Next, we will use these power management resources in the device definition. We will use PM_DEVICE_DT_INST_GET() as the pm parameter, and now, the device driver definition looks like this:

DEVICE_DT_INST_DEFINE(inst,													  \
				custom_bme280_init,												    \
				PM_DEVICE_DT_INST_GET(inst),									\
				&custom_bme280_data_##inst,										\
				&custom_bme280_config_##inst,									\
				POST_KERNEL, 													        \
				CONFIG_SENSOR_INIT_PRIORITY, 									\
				&custom_bme280_api);
C

4 Enable device power management in the overlay file.

4.1 Enable device runtime power management for the bme280 node.

Open the proper overlay file for your board. In the bme280 node definition, enable the device runtime pm flag by adding the following line:

		zephyr,pm-device-runtime-auto;
Devicetree

4.2 Enable device runtime pm for the spi bus.

We will add the same flag to the spi bus to which the BME280 sensor is connected. As a result, the SPI driver will also use pm_device_runtime_get() and pm_device_runtime_put() to put the bus into suspended/resumed mode.

		zephyr,pm-device-runtime-auto;
Devicetree

Note

If you take a closer look at the spi node, we can identify sleep and default variants of the pin configuration. As we mentioned in power management todo:link, they are used by the SPI driver to reconfigure the pins during power mode changes.

5. Enable the power management subsystem in the project.

Finally, our custom driver with power management is ready to use. Now, we just need to enable power management in the application by setting the necessary configuration parameters.

Enable these options in the prj.conf file

CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
Kconfig

6. Build and flash the application to your board.

6.1 Connect BME280 sensor using Lesson5 – Exercise1 instructions.

Important

To build the project properly, make sure to create a build configuration for the app directory, not the root of the exercise directory.

When building from the command line, the command should look like this: west build app -b <board>

6.2 Build and flash the application

7. Observe the device driver’s behavior.

Observe the output in the terminal. Notice that the sensor data acquisition is done after resume and before suspend power state changes.

*** Booting nRF Connect SDK ***
*** Using Zephyr OS ***
[00:00:00.579,353] Lesson7_Exercise1: Lesson 7 - Exercise 1 started
[00:00:00.579,357] Lesson7_Exercise1: Sample fetching…
[00:00:00.579,364] custom_bme280: Resuming BME280 sensor
[00:00:00.766,496] custom_bme280: Sensor Data acquired
[00:00:00.766,512] custom_bme280: Suspending BME280 sensor
[00:00:00.766,881] Lesson7_Exercise1: Compensated temperature value: 23
[00:00:00.766,887] Lesson7_Exercise1: Compensated pressure value: 98
[00:00:00.766,891] Lesson7_Exercise1: Compensated humidity value: 34
Terminal
Register an account
Already have an account? Log in
(All fields are required unless specified optional)

  • 8 or more characters
  • Upper and lower case letters
  • At least one number or special character

Forgot your password?
Enter the email associated with your account, and we will send you a link to reset your password.