From 05712250eafe73f6a7568fa245189a8bc0781dbd Mon Sep 17 00:00:00 2001 From: Guillaume Gautier Date: Tue, 7 Nov 2023 09:33:57 +0100 Subject: [PATCH] samples: boards: stm32: pm: suspend_to_ram: add wba standby sample Add a sample for STM32WBA standby power management. Signed-off-by: Guillaume Gautier --- .../power_mgmt/suspend_to_ram/CMakeLists.txt | 7 + .../power_mgmt/suspend_to_ram/README.rst | 42 +++++ .../boards/nucleo_wba55cg.overlay | 48 +++++ .../stm32/power_mgmt/suspend_to_ram/prj.conf | 8 + .../power_mgmt/suspend_to_ram/sample.yaml | 17 ++ .../power_mgmt/suspend_to_ram/src/main.c | 164 ++++++++++++++++++ 6 files changed, 286 insertions(+) create mode 100644 samples/boards/stm32/power_mgmt/suspend_to_ram/CMakeLists.txt create mode 100644 samples/boards/stm32/power_mgmt/suspend_to_ram/README.rst create mode 100644 samples/boards/stm32/power_mgmt/suspend_to_ram/boards/nucleo_wba55cg.overlay create mode 100644 samples/boards/stm32/power_mgmt/suspend_to_ram/prj.conf create mode 100644 samples/boards/stm32/power_mgmt/suspend_to_ram/sample.yaml create mode 100644 samples/boards/stm32/power_mgmt/suspend_to_ram/src/main.c diff --git a/samples/boards/stm32/power_mgmt/suspend_to_ram/CMakeLists.txt b/samples/boards/stm32/power_mgmt/suspend_to_ram/CMakeLists.txt new file mode 100644 index 00000000000..8e813b2d175 --- /dev/null +++ b/samples/boards/stm32/power_mgmt/suspend_to_ram/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(stm32_pm_suspend_to_ram) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/boards/stm32/power_mgmt/suspend_to_ram/README.rst b/samples/boards/stm32/power_mgmt/suspend_to_ram/README.rst new file mode 100644 index 00000000000..246ac96ea10 --- /dev/null +++ b/samples/boards/stm32/power_mgmt/suspend_to_ram/README.rst @@ -0,0 +1,42 @@ +.. _stm32-pm-suspend-to-ram-sample: + +STM32 PM Suspend to RAM +####################### + +Overview +******** + +This sample is a minimum application to demonstrate basic power management +behavior in a basic blinking LED set up using the :ref:`GPIO API ` in +low power context + ADC measurements and entropy. + +.. _stm32-pm-suspend-to-ram-sample-requirements: + +Requirements +************ + +The board should support enabling PM. For a STM32 based target, it means that +it should support a clock source alternative to Cortex Systick that can be used +in core sleep states, as LPTIM (:dtcompatible:`st,stm32-lptim`). +The board shall have an RTC to use it during the standby mode as a replacement +for LPTIM (which is disabled). The board shall also have RAM retention to be +able to restore context after standby. + +Building and Running +******************** + +Build and flash Blinky as follows, changing ``stm32wba55cg`` for your board: + +.. zephyr-app-commands:: + :zephyr-app: samples/boards/stm32/power_mgmt/suspend_to_ram + :board: stm32wba55cg + :goals: build flash + :compact: + +After flashing, the LED starts to blink. + +PM configurations +***************** + +By default, :kconfig:option:`CONFIG_PM_DEVICE` and :kconfig:option:`CONFIG_PM_DEVICE_RUNTIME` +are enabled. diff --git a/samples/boards/stm32/power_mgmt/suspend_to_ram/boards/nucleo_wba55cg.overlay b/samples/boards/stm32/power_mgmt/suspend_to_ram/boards/nucleo_wba55cg.overlay new file mode 100644 index 00000000000..3ed3dab7a88 --- /dev/null +++ b/samples/boards/stm32/power_mgmt/suspend_to_ram/boards/nucleo_wba55cg.overlay @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2023 STMicroelectronics + */ + +/ { + /* Change min residency time to ease power consumption measurement */ + cpus { + power-states { + stop0: state0 { + min-residency-us = <500000>; + exit-latency-us = <50>; + }; + stop1: state1 { + min-residency-us = <1000000>; + exit-latency-us = <100>; + }; + standby: state2 { + min-residency-us = <2000000>; + exit-latency-us = <1000>; + }; + }; + }; + + zephyr,user { + /* adjust channel number according to pinmux in board.dts */ + io-channels = <&adc4 8>; + }; +}; + +&lptim1 { + status = "okay"; +}; + +&adc4 { + pinctrl-0 = <&adc4_in8_pa1>; + #address-cells = <1>; + #size-cells = <0>; + + channel@8 { + reg = <8>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + }; +}; diff --git a/samples/boards/stm32/power_mgmt/suspend_to_ram/prj.conf b/samples/boards/stm32/power_mgmt/suspend_to_ram/prj.conf new file mode 100644 index 00000000000..acef64bb9a8 --- /dev/null +++ b/samples/boards/stm32/power_mgmt/suspend_to_ram/prj.conf @@ -0,0 +1,8 @@ +CONFIG_PM=y +CONFIG_PM_DEVICE=y +CONFIG_PM_DEVICE_RUNTIME=y +CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE=n +CONFIG_PM_S2RAM=y +CONFIG_ADC=y +CONFIG_ENTROPY_GENERATOR=y +#CONFIG_DEBUG=y diff --git a/samples/boards/stm32/power_mgmt/suspend_to_ram/sample.yaml b/samples/boards/stm32/power_mgmt/suspend_to_ram/sample.yaml new file mode 100644 index 00000000000..f7eb4383664 --- /dev/null +++ b/samples/boards/stm32/power_mgmt/suspend_to_ram/sample.yaml @@ -0,0 +1,17 @@ +sample: + name: STM32 PM Standby Power Management +tests: + sample.boards.stm32.power_mgmt.suspend_to_ram: + tags: + - power + harness: console + harness_config: + type: one_line + regex: + - "Exit Standby" + filter: dt_compat_enabled("zephyr,power-state") and + dt_enabled_alias_with_parent_compat("led0", "gpio-leds") and + dt_compat_enabled("st,stm32-lptim") + extra_args: "CONFIG_DEBUG=y" + platform_allow: + - nucleo_wba55cg diff --git a/samples/boards/stm32/power_mgmt/suspend_to_ram/src/main.c b/samples/boards/stm32/power_mgmt/suspend_to_ram/src/main.c new file mode 100644 index 00000000000..bb1ca9dbb55 --- /dev/null +++ b/samples/boards/stm32/power_mgmt/suspend_to_ram/src/main.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2024 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SLEEP_TIME_STOP0_MS 800 +#define SLEEP_TIME_STOP1_MS 1500 +#define SLEEP_TIME_STANDBY_MS 3000 +#define SLEEP_TIME_BUSY_MS 2000 + +static const struct gpio_dt_spec led = + GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); + +#if !DT_NODE_EXISTS(DT_PATH(zephyr_user)) || \ + !DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels) +#error "No suitable devicetree overlay specified" +#endif + +#define DT_SPEC_AND_COMMA(node_id, prop, idx) \ + ADC_DT_SPEC_GET_BY_IDX(node_id, idx), + +/* Data of ADC io-channels specified in devicetree. */ +static const struct adc_dt_spec adc_channels[] = { + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, + DT_SPEC_AND_COMMA) +}; + +const struct device *rng_dev; + +#define BUFFER_LENGTH 3 + +static uint8_t entropy_buffer[BUFFER_LENGTH] = {0}; + +static int adc_test(void) +{ + int err; + static uint32_t count; + uint16_t buf; + struct adc_sequence sequence = { + .buffer = &buf, + /* buffer size in bytes, not number of samples */ + .buffer_size = sizeof(buf), + }; + + /* Configure channels individually prior to sampling. */ + for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) { + if (!adc_is_ready_dt(&adc_channels[i])) { + printk("ADC controller device %s not ready\n", adc_channels[i].dev->name); + return 0; + } + + err = adc_channel_setup_dt(&adc_channels[i]); + if (err < 0) { + printk("Could not setup channel #%d (%d)\n", i, err); + return 0; + } + } + + printk("ADC reading[%u]:\n", count++); + for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) { + int32_t val_mv; + + printk("- %s, channel %d: ", + adc_channels[i].dev->name, + adc_channels[i].channel_id); + + (void)adc_sequence_init_dt(&adc_channels[i], &sequence); + + err = adc_read_dt(&adc_channels[i], &sequence); + if (err < 0) { + printk("Could not read (%d)\n", err); + continue; + } + + /* + * If using differential mode, the 16 bit value + * in the ADC sample buffer should be a signed 2's + * complement value. + */ + if (adc_channels[i].channel_cfg.differential) { + val_mv = (int32_t)((int16_t)buf); + } else { + val_mv = (int32_t)buf; + } + printk("%"PRId32, val_mv); + err = adc_raw_to_millivolts_dt(&adc_channels[i], + &val_mv); + /* conversion to mV may not be supported, skip if not */ + if (err < 0) { + printk(" (value in mV not available)\n"); + } else { + printk(" = %"PRId32" mV\n", val_mv); + } + } + + return 0; +} + +void print_buf(uint8_t *buffer) +{ + int i; + int count = 0; + + for (i = 0; i < BUFFER_LENGTH; i++) { + printk(" 0x%02x", buffer[i]); + if (buffer[i] == 0x00) { + count++; + } + } + printk("\n"); +} + +int main(void) +{ + __ASSERT_NO_MSG(gpio_is_ready_dt(&led)); + + rng_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_entropy)); + if (!device_is_ready(rng_dev)) { + printk("error: random device not ready"); + } + + printk("Device ready\n"); + + while (true) { + gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); + adc_test(); + k_busy_wait(SLEEP_TIME_BUSY_MS*1000); + gpio_pin_set_dt(&led, 0); + k_msleep(SLEEP_TIME_STOP0_MS); + printk("Exit Stop0\n"); + + gpio_pin_set_dt(&led, 1); + adc_test(); + k_busy_wait(SLEEP_TIME_BUSY_MS*1000); + gpio_pin_set_dt(&led, 0); + k_msleep(SLEEP_TIME_STOP1_MS); + printk("Exit Stop1\n"); + + (void)memset(entropy_buffer, 0x00, BUFFER_LENGTH); + entropy_get_entropy(rng_dev, (char *)entropy_buffer, BUFFER_LENGTH); + printk("Sync entropy: "); + print_buf(entropy_buffer); + + gpio_pin_set_dt(&led, 1); + adc_test(); + k_busy_wait(SLEEP_TIME_BUSY_MS*1000); + gpio_pin_configure_dt(&led, GPIO_DISCONNECTED); + k_msleep(SLEEP_TIME_STANDBY_MS); + printk("Exit Standby\n"); + } + return 0; +}