简介
当内核没有要调度的内容时,它会进入空闲状态。如果通过CONFIG_PM选项启用,则电源管理子系统可以根据所选的电源管理策略和内核分配的空闲时间持续时间,将空闲系统置于受支持的电源状态之一。设置唤醒事件是应用程序的责任。唤醒事件通常是由其中一个SoC外设模块如SysTick、RTC、计数器或GPIO触发的中断。
进入休眠的原理实现
在zephyr/kernel/idle.c下:
void idle(void *unused1, void *unused2, void *unused3){ARG_UNUSED(unused1);ARG_UNUSED(unused2);ARG_UNUSED(unused3);while (true) {/* SMP systems without a working IPI can't* actual enter an idle state, because they* can't be notified of scheduler changes* (i.e. threads they should run). They just* spin in a yield loop. This is intended as* a fallback configuration for new platform* bringup.*/if (IS_ENABLED(CONFIG_SMP) &&!IS_ENABLED(CONFIG_SCHED_IPI_SUPPORTED)) {k_busy_wait(100);k_yield();continue;}/* Note weird API: k_cpu_idle() is called with local* CPU interrupts masked, and returns with them* unmasked. It does not take a spinlock or other* higher level construct.*/(void) arch_irq_lock();// 判定是否启动了电源管理if (IS_ENABLED(CONFIG_PM)) {pm_save_idle();} else {k_cpu_idle();}/* It is possible to (pathologically) configure the* idle thread to have a non-preemptible priority.* You might think this is an API bug, but we actually* have a test that exercises this. Handle the edge* case when that happens.*/if (K_IDLE_PRIO < 0) {k_yield();}}}/*** @brief Indicate that kernel is idling in tickless mode** Sets the kernel data structure idle field to either a positive value or* K_FOREVER.*/static void pm_save_idle(void){#ifdef CONFIG_PM// 获取下次系统调度时间int32_t ticks = z_get_next_timeout_expiry();_kernel.idle = ticks;/** Call the suspend hook function of the soc interface to allow* entry into a low power state. The function returns* PM_STATE_ACTIVE if low power state was not entered, in which* case, kernel does normal idle processing.** This function is entered with interrupts disabled. If a low power* state was entered, then the hook function should enable inerrupts* before exiting. This is because the kernel does not do its own idle* processing in those cases i.e. skips k_cpu_idle(). The kernel's* idle processing re-enables interrupts which is essential for* the kernel's scheduling logic.*/// 启动电源管理if (pm_system_suspend(ticks) == PM_STATE_ACTIVE) {k_cpu_idle();}#endif}enum pm_state pm_system_suspend(int32_t ticks){// 根据电源管理策略计算出电源管理状态z_power_state = pm_policy_next_state(ticks);if (z_power_state.state == PM_STATE_ACTIVE) {LOG_DBG("No PM operations done.");return z_power_state.state;}post_ops_done = 0;if (ticks != K_TICKS_FOREVER) {/** Just a sanity check in case the policy manager does not* handle this error condition properly.*/__ASSERT(z_power_state.min_residency_us >=z_power_state.exit_latency_us,"min_residency_us < exit_latency_us");/** We need to set the timer to interrupt a little bit early to* accommodate the time required by the CPU to fully wake up.*/// 设置下次系统调度的超时定时器z_set_timeout_expiry(ticks -k_us_to_ticks_ceil32(z_power_state.exit_latency_us), true);}// 这部分我们之后会在设备电源里讲解#if CONFIG_PM_DEVICEbool should_resume_devices = true;switch (z_power_state.state) {case PM_STATE_RUNTIME_IDLE:__fallthrough;case PM_STATE_SUSPEND_TO_IDLE:__fallthrough;case PM_STATE_STANDBY:/* low power peripherals. */if (pm_low_power_devices()) {return _handle_device_abort(z_power_state);} break;case PM_STATE_SUSPEND_TO_RAM:__fallthrough;case PM_STATE_SUSPEND_TO_DISK:if (pm_suspend_devices()) {return _handle_device_abort(z_power_state);}break;default:should_resume_devices = false;break;}#endif/** This function runs with interruptions locked but it is* expected the SoC to unlock them in* pm_power_state_exit_post_ops() when returning to active* state. We don't want to be scheduled out yet, first we need* to send a notification about leaving the idle state. So,* we lock the scheduler here and unlock just after we have* sent the notification in pm_system_resume().*/k_sched_lock();//启动电源管理的调试定时器pm_debug_start_timer();/* Enter power state */// 通知要进入系统休眠pm_state_notify(true);// 进入系统休眠,这是一个和体系架构相关的函数pm_power_state_set(z_power_state);// 停止电源管理的调试定时器pm_debug_stop_timer();/* Wake up sequence starts here */// 这部分在之后设备电源管理讲解#if CONFIG_PM_DEVICEif (should_resume_devices) {/* Turn on peripherals and restore device states as necessary */pm_resume_devices();}#endif// 输出系统电源管理的调试信息pm_log_debug_info(z_power_state.state);//退出系统休眠,这是一个和体系架构相关的函数pm_system_resume();k_sched_unlock();return z_power_state.state;}
系统电源管理的执行步骤:
- 系统进入
idle任务 - 获取下次系统调度的时间
- 通过电源管理策略根据时间获取电源管理状态
- 根据电源管理状态进行休眠
电源管理策略解析
//从设备树中获取电源管理策略的结构体static const struct pm_state_info pm_min_residency[] =PM_STATE_INFO_DT_ITEMS_LIST(DT_NODELABEL(cpu0));struct pm_state_info pm_policy_next_state(int32_t ticks){int i;for (i = ARRAY_SIZE(pm_min_residency) - 1; i >= 0; i--) {uint32_t min_residency, exit_latency;// 判定改电源管理状态是否被约束if (!pm_constraint_get(pm_min_residency[i].state)) {continue;}min_residency = k_us_to_ticks_ceil32(pm_min_residency[i].min_residency_us);exit_latency = k_us_to_ticks_ceil32(pm_min_residency[i].exit_latency_us);__ASSERT(min_residency > exit_latency,"min_residency_us < exit_latency_us");// 通过时间来确定要进行的电源管理状态if ((ticks == K_TICKS_FOREVER) ||(ticks >= (min_residency + exit_latency))) {LOG_DBG("Selected power state %d ""(ticks: %d, min_residency: %u)",pm_min_residency[i].state, ticks,pm_min_residency[i].min_residency_us);return pm_min_residency[i];}}LOG_DBG("No suitable power state found!");return (struct pm_state_info){PM_STATE_ACTIVE, 0, 0};}
电源管理策略定义
想要定义电源管理策略,目前从源码上分析,我们只能通过设备树来定义。
电源管理策略设备树绑定文件
description: Properties for power management statecompatible: "zephyr,power-state"properties:power-state-name:type: stringrequired: truedescription: indicates a power stateenum:- "active"- "runtime-idle"- "suspend-to-idle"- "standby"- "suspend-to-ram"- "suspend-to-disk"- "soft-off"substate-id:type: intrequired: falsedescription: Platform specific identification.min-residency-us:type: intrequired: falsedescription: |Minimum residency duration in microseconds. It is the minimum time for agiven idle state to be worthwhile energywise. It includes the time to enterin this state.exit-latency-us:type: intrequired: falsedescription: |Worst case latency in microseconds required to exit the idle state.
- power-state-name: 定义电源状态
- substate-id: 改状态的唯一id
- min-residency-us: 进入改电源状态的最小时间/us。
- exit-latency-us: 退出改电源状态的最大时间/us。
电源管理策略示例
/ {power-states {stop0: state0 {compatible = "zephyr,power-state";power-state-name = "suspend-to-idle";substate-id = <1>;min-residency-us = <100>;};stop1: state1 {compatible = "zephyr,power-state";power-state-name = "suspend-to-idle";substate-id = <2>;min-residency-us = <500>;};stop2: state2 {compatible = "zephyr,power-state";power-state-name = "suspend-to-idle";substate-id = <3>;min-residency-us = <900>;};};}
电源管理示例
Kconfig定义:
CONFIG_GPIO=y//启动电源管理CONFIG_PM=y
设备树:
/{power-states {stop0: state0 {compatible = "zephyr,power-state";power-state-name = "suspend-to-idle";substate-id = <1>;min-residency-us = <500>;};stop1: state1 {compatible = "zephyr,power-state";power-state-name = "suspend-to-idle";substate-id = <2>;min-residency-us = <700>;};stop2: state2 {compatible = "zephyr,power-state";power-state-name = "suspend-to-idle";substate-id = <3>;min-residency-us = <1000>;};};leds {compatible = "gpio-leds";green_led_2: led_2 {gpios = <&gpioa 5 GPIO_ACTIVE_HIGH>;label = "User LD2";};};aliases {led0 = &green_led_2;sw0 = &user_button;};}
代码:
/** Copyright (c) 2021 Linaro Limited** SPDX-License-Identifier: Apache-2.0*/#include <zephyr.h>#include <device.h>#include <devicetree.h>#include <drivers/gpio.h>#include <sys/printk.h>#define SLEEP_TIME_MS 2000static const struct gpio_dt_spec led =GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);void main(void){bool led_is_on = true;__ASSERT_NO_MSG(device_is_ready(led.port));printk("Device ready\n");while (true) {gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);gpio_pin_set(led.port, led.pin, (int)led_is_on);if (led_is_on == false) {/* Release resource to release device clock */gpio_pin_configure(led.port, led.pin, GPIO_DISCONNECTED);}k_msleep(SLEEP_TIME_MS);if (led_is_on == true) {/* Release resource to release device clock */gpio_pin_configure(led.port, led.pin, GPIO_DISCONNECTED);}led_is_on = !led_is_on;}}
