【MCU ATS323X】PM电源管理系统
目录
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
6.2..\application\...\MMI_SAVE_PWR.c
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 双向链表。由用户调用接口,线程会延时执行延迟操作。
更多推荐
所有评论(0)