目录

1、概述

2、功耗状态

3、zephyr 功耗模式切换

4、pm_system_suspend()

4.1..\zephyr\subsys\pm\pm.c

4.2..\hal\actions\soc\arm\actions\falcon\power.c

4.3..\hal\actions\soc\arm\actions\falcon\soc_sleep.c

4.4..\hal\actions\soc\arm\actions\falcon\soc_sleep.s

5、投票与仲裁

6、other

6.1..\application\...\main.c

6.2..\application\...\MMI_SAVE_PWR.c

6.3 应用唤醒过程

7、other 底层实现

7.1、framework

7.2、zephyr - work(延时队列)


1、概述

       本文的电源管理系统是比较狭义的定义主要包含:设备功耗模式申明(多少种);不同功耗模式如何切换;投票与仲裁;功能模式切换回调。

        ATS323X的SDK使用OS是zephyr分析电源管会涉及到zyphyr子系统pm的内容。

2、功耗状态

        zephyr是一个支持多核心的系统所有使用一个数组来记录多核的状态。为每一个CPU实现独立控制提供的基础。..\zephyr\subsys\pm\state.c内容如下:

/** CPU power states information for each CPU */
static const struct pm_state_info *cpus_states[] = {
	DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PATH(cpus), CPU_STATE_REF, (,))
};

/** Number of states for each CPU */
static const uint8_t states_per_cpu[] = {
	DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PATH(cpus), DT_NUM_CPU_POWER_STATES, (,))
};

uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states)
{
	if (cpu >= ARRAY_SIZE(cpus_states)) {
		return 0;
	}
	*states = cpus_states[cpu];
	return states_per_cpu[cpu];
}

3、zephyr 功耗模式切换

        所有有OS的功耗模式切换都是发生在idlex线程。如代码中我们可以看到如果没有PM子系统进入idle线程是直接调用k_cpu_idle()此接口不同架构的实现是不一样。此接口只是让CPU停下来并没有让外设也进入低功耗所以功耗降低不是很明显,实践中这种方式不常使用。

        如果PM子系统被开启则会调用pm_system_suspend()接口,pm_system_suspend()作用一个PM模块的接口是很方便用户扩展而且对底层OS影响。当pm_system_suspend()进入低功耗失败时OS则调用k_cpu_idle()仅让CPU进入低功耗。

        WFI 是一条CPU指令 Wait For Interrupt​ 的缩写,中文意思是“等待中断”。当CPU执行到这条指令时,它会进入一种低功耗的“睡眠”或“待机”状态。在这种状态下,CPU的核心时钟可能会被停止,从而显著降低功耗。这个“睡眠”不是永久的。一旦发生任何中断(例如定时器中断、按键中断、数据接收中断等),CPU就会立即“醒来”,退出低功耗状态,恢复正常运行,并去处理那个中断。

void idle(void *unused1, void *unused2, void *unused3)
{
	ARG_UNUSED(unused1);
	ARG_UNUSED(unused2);
	ARG_UNUSED(unused3);
	__ASSERT_NO_MSG(_current->base.prio >= 0);

	while (true) {
		if (IS_ENABLED(CONFIG_SMP) && !IS_ENABLED(CONFIG_SCHED_IPI_SUPPORTED)) {
			for (volatile int i = 0; i < 100000; i++) {
				/* Empty loop */
			}
			z_swap_unlocked();
		}

		(void) arch_irq_lock();

#ifdef CONFIG_PM
		_kernel.idle = z_get_next_timeout_expiry();
		if (k_is_pre_kernel() || !pm_system_suspend(_kernel.idle)) {
			k_cpu_idle();
		}
#else
		k_cpu_idle();
#endif /* CONFIG_PM */

#if !defined(CONFIG_PREEMPT_ENABLED)
# if !defined(CONFIG_USE_SWITCH) || defined(CONFIG_SPARC)

		if (_kernel.ready_q.cache != _current) {
			z_swap_unlocked();
		}
# endif /* !defined(CONFIG_USE_SWITCH) || defined(CONFIG_SPARC) */
#endif /* !defined(CONFIG_PREEMPT_ENABLED) */
	}
}

4、pm_system_suspend()

        入参为将要空闲的时长。接口的调用过程过如下:

4.1..\zephyr\subsys\pm\pm.c

        void pm_notifier_register(struct pm_notifier *notifier)//供用户注册切换回调

        pm_system_suspend() -> pm_state_set()//进低功耗

        pm_state_notify(true)//在进入低功耗前通知要外设。

        pm_system_resume() -> pm_state_notify(false)//在唤醒后通知注册

4.2..\hal\actions\soc\arm\actions\falcon\power.c

        pm_state_set() -> soc_enter_deep_sleep() /soc_enter_light_sleep()

4.3..\hal\actions\soc\arm\actions\falcon\soc_sleep.c

         soc_enter_deep_sleep() /soc_enter_light_sleep() -> cpu_enter_sleep() -> __cpu_enter_sleep() ->__cpu_suspend()

4.4..\hal\actions\soc\arm\actions\falcon\soc_sleep.s

        SECTION_FUNC(sleepfunc, __cpu_suspend)

bool pm_system_suspend(int32_t ticks)
{
	uint8_t id = _current_cpu->id;
	k_spinlock_key_t key;
	SYS_PORT_TRACING_FUNC_ENTER(pm, system_suspend, ticks);
	key = k_spin_lock(&pm_forced_state_lock);
	if (z_cpus_pm_forced_state[id].state != PM_STATE_ACTIVE) {
		z_cpus_pm_state[id] = z_cpus_pm_forced_state[id];
		z_cpus_pm_forced_state[id].state = PM_STATE_ACTIVE;
	} else {
		const struct pm_state_info *info;

		info = pm_policy_next_state(id, ticks);
		if (info != NULL) {
			z_cpus_pm_state[id] = *info;
		} else {
			z_cpus_pm_state[id].state = PM_STATE_ACTIVE;
		}
	}
	k_spin_unlock(&pm_forced_state_lock, key);

	if (z_cpus_pm_state[id].state == PM_STATE_ACTIVE) {
		LOG_DBG("No PM operations done.");
		SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks,
				   z_cpus_pm_state[id].state);
		return false;
	}

#ifdef CONFIG_PM_DEVICE_SYSTEM_MANAGED
	if (atomic_sub(&_cpus_active, 1) == 1) {
		if ((z_cpus_pm_state[id].state != PM_STATE_RUNTIME_IDLE) &&
		    !z_cpus_pm_state[id].pm_device_disabled) {
			if (!pm_suspend_devices()) {
				pm_resume_devices();
				z_cpus_pm_state[id].state = PM_STATE_ACTIVE;
				(void)atomic_add(&_cpus_active, 1);
				SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks,
							   z_cpus_pm_state[id].state);
				return false;
			}
		}
	}
#endif

	if ((z_cpus_pm_state[id].exit_latency_us != 0) &&
	    (ticks != K_TICKS_FOREVER)) {
		sys_clock_set_timeout(ticks -
		     k_us_to_ticks_ceil32(
			     z_cpus_pm_state[id].exit_latency_us),
				     true);
	}

	k_sched_lock();
	pm_stats_start();
	/* Enter power state */
	pm_state_notify(true);
	atomic_set_bit(z_post_ops_required, id);
	pm_state_set(z_cpus_pm_state[id].state, z_cpus_pm_state[id].substate_id);//调用CPU停止接口
	pm_stats_stop();

	/* Wake up sequence starts here */
	pm_stats_update(z_cpus_pm_state[id].state);
	pm_system_resume();
	k_sched_unlock();
	SYS_PORT_TRACING_FUNC_EXIT(pm, system_suspend, ticks,
				   z_cpus_pm_state[id].state);

	return true;
}

5、投票与仲裁

6、other

唤醒后有些是需要延时执行的操作,介绍一个延时执行模块。延时模块还可以用串口通信超时等场景。

6.1..\application\...\main.c

        user_work_mode() -> mmi_save_pwr()

6.2..\application\...\MMI_SAVE_PWR.c

        mmi_save_pwr() -> pf_stop_radio_work_timer() //关定时器停任务

6.3 应用唤醒过程

依赖消息中心与pf_rpmsg_send_task线程,s_rpmsg_send_queue其实是线程的句柄。消息中心与普通队列是两明显区别,直接作用于线程有点类似于线程的事件。

..\application\...\system_init.c

wakelock_init() -> sys_wakelock_init(app_wakelock_ctx.standby,  WAKELOCK_STATE_NUM,  _wakelock_callback);
        wakelock_context.user_callback = cb;//初始化时注册进回调函数。

_wakelock_callback() -> //有唤醒源时执行些回调函数
_sys_wakelock_enter_normal -> rpmsg_comm_send_wakeup_dsp() -> OS_SendMsg( s_rpmsg_send_queue, &msg)//发送唤醒信号到pf_rpmsg_send_task线程。


pf_rpmsg_send_task//收到唤醒事件线程

        OS_GetMsg(s_rpmsg_send_queue, &rpmsg_comm)

        pf_start_radio_work_timer(10);//开定时器。

7、other 底层实现

7.1、framework

framework是SDK的一个可裁剪大块,framework\porting为framework提供一个完整的适配层(操作系统抽象层Operating System Abstract Layer(osal))。为其中的模块提供统一接口方便framework移植。

..\framework\porting\include\os_common_api.h

#define os_delayed_work_init(work, handler)  k_work_init_delayable(work, handler)//一般都是一直使用zephyr的k_work_init_delayable()接口。

..\framework\porting\osal\os_wrapper.c

int os_delayed_work_submit(os_work_delayable *work, s32_t delay)

{        return k_work_reschedule(work, SYS_TIMEOUT_MS(delay));        }

..\framework\misc\sys_manager\wakelock.c

sys_wakelock_init()执行如下初始化:

os_delayed_work_init(&wakelock_context.work_handle_up_state, _sys_wakelock_up_state);

os_delayed_work_init(&wakelock_context.work_handle_down_state, _sys_wakelock_down_state);

_sys_wakelock_up_state()/_sys_wakelock_down_state() -> 延迟回调函数触发线程。

os_delayed_work_submit(&wakelock_context.work_handle_down_state, state->remain_time) -> k_work_reschedule()

7.2、zephyr - work(延时队列)

..\zephyr\kernel\work.c中创建一个线程管理延时队列。

void k_work_queue_start(struct k_work_q *queue,
			k_thread_stack_t *stack,
			size_t stack_size,
			int prio,
			const struct k_work_queue_config *cfg)
{
	__ASSERT_NO_MSG(queue);
	__ASSERT_NO_MSG(stack);
	__ASSERT_NO_MSG(!flag_test(&queue->flags, K_WORK_QUEUE_STARTED_BIT));
	uint32_t flags = K_WORK_QUEUE_STARTED;

	SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_work_queue, start, queue);

	sys_slist_init(&queue->pending);
	z_waitq_init(&queue->notifyq);
	z_waitq_init(&queue->drainq);

	if ((cfg != NULL) && cfg->no_yield) {
		flags |= K_WORK_QUEUE_NO_YIELD;
	}

	flags_set(&queue->flags, flags);

	(void)k_thread_create(&queue->thread, stack, stack_size,
			      work_queue_main, queue, NULL, NULL,
			      prio, 0, K_FOREVER);

	if ((cfg != NULL) && (cfg->name != NULL)) {
		k_thread_name_set(&queue->thread, cfg->name);
	}

	if ((cfg != NULL) && (cfg->essential)) {
		queue->thread.base.user_options |= K_ESSENTIAL;
	}

	k_thread_start(&queue->thread);
	SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_work_queue, start, queue);
}

..\zephyr\kernel\system_work_q.c将work启动想关加入到系统初始化段。

static int k_sys_work_q_init(void)//..\zephyr\kernel\system_work_q.c
{
	struct k_work_queue_config cfg = {
		.name = "sysworkq",
		.no_yield = IS_ENABLED(CONFIG_SYSTEM_WORKQUEUE_NO_YIELD),
		.essential = true,
	};

	k_work_queue_start(&k_sys_work_q,
			    sys_work_q_stack,
			    K_KERNEL_STACK_SIZEOF(sys_work_q_stack),
			    CONFIG_SYSTEM_WORKQUEUE_PRIORITY, &cfg);
	return 0;
}

SYS_INIT(k_sys_work_q_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);//

int k_work_reschedule(struct k_work_delayable *dwork, k_timeout_t delay)//加入到static sys_slist_t work_pending 双向链表。由用户调用接口,线程会延时执行延迟操作。

更多推荐