【MCU ATS323X】PM电源管理系统
目录
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、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进入低功耗。
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();}
#elsek_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_MANAGEDif (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;}}}
#endifif ((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、zephyr - work(延时队列)
..\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() -> k_work_reschedule()
..\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);
}
int k_work_reschedule(struct k_work_delayable *dwork, k_timeout_t delay)//加入到static sys_slist_t work_pending 双向链表。
