STM32超低功耗之移植 RT-Thread PM 组件原理分析

一, 认识 PM 组件

在上一篇的文章中,介绍了如何移植 RT-Thread PM 组件,PM 组件的作用是通过 RTOS 来统一的管理,超低功耗是一个细致的工作,所以在使用的时候必须要知道 PM 组件中每个 API 的实现及作用,才能最到功耗最优。

二,PM 组件的工作原理

PM 的工作原理,可以从 RT-Thread 的文档中心来获取到,其中最为核心的部分就是下图
STM32超低功耗之移植 RT-Thread PM 组件原理分析_第1张图片
用户的调用 PM 的组件的 API 之后,来申请进入低功耗模式,如果当前没有工作进行,在线程运行到空闲任务的时候就会去尝试切换到对应的低功耗模式,如果对前面的内容还记得话,通过 WFI 进入低功耗模式后是会被 SYSTICK 唤醒的,所以这里根据进入不同的低功耗模式的时候,还要考虑是否要关闭 SYSTICK,关闭 SYSTICK 之后到达线程需要唤醒的时间的时候,系统的 TICK 值需要去补偿,所以在关闭 SYSTICK 的时候之前要启动 LPTIM ,还要得出 LPTIM 运行多长时间之后去唤醒 MCU,唤醒 MCU 之后还要把 SYSTICK 没有运行的这段时间的 OSTICK 补偿上。

三,PM 组件的 API 实现

  1. 选择睡眠模式
static rt_uint8_t _pm_select_sleep_mode(struct rt_pm *pm)
{
    int index;
    rt_uint8_t mode;

    mode = _pm_default_deepsleep;
    for (index = PM_SLEEP_MODE_NONE; index < PM_SLEEP_MODE_MAX; index ++)
    {
        if (pm->modes[index])
        {
            mode = index;
            break;
        }
    }
    pm->sleep_mode = mode;

    return mode;
}

这个函数会在后面的源码分析时,经常出现,所以这里放在前面来说
这里主要时通过遍历的方式查找到本次申请将要进入的低功耗模式,这个结果会被记录在全局变量 pm 结构体中
如果遍历所有低功耗的类型都已经释放完毕则会进入 _pm_default_deepsleep 睡眠模式,这样将会导致某些外设无法使用

  1. 请求进入低功耗模式
void rt_pm_request(rt_uint8_t mode)
{
    rt_base_t level;
    struct rt_pm *pm;

    if (_pm_init_flag == 0)
        return;

    if (mode > (PM_SLEEP_MODE_MAX - 1))
        return;

    level = rt_hw_interrupt_disable();
    pm = &_pm;
    if (pm->modes[mode] < 255)
        pm->modes[mode] ++;
    _pm_select_sleep_mode(pm);
    rt_hw_interrupt_enable(level);
}

检查 PM 组件是否已经初始化,检查申请的模式是否超出了当前支持最大的低功耗模式种类,在关中断的状态下申请要进入低功耗的模式的次数加 1 .

  1. 释放休眠模式
void rt_pm_release(rt_uint8_t mode)
{
    rt_ubase_t level;
    struct rt_pm *pm;

    if (_pm_init_flag == 0)
        return;

    if (mode > (PM_SLEEP_MODE_MAX - 1))
        return;

    level = rt_hw_interrupt_disable();
    pm = &_pm;
    if (pm->modes[mode] > 0)
        pm->modes[mode] --;
    _pm_select_sleep_mode(pm);
    rt_hw_interrupt_enable(level);
}

检查 PM 组件是否已经初始化,检查申请的模式是否超出了当前支持最大的低功耗模式种类,在关中断的状态下申请要进入低功耗的模式的次数减 1 。
需要注意的是,PM 组件在初始化的时候会设置 pm->modes[mode] = 0 这样在 PM 组件启动之后,就会有一个默认的运行状态,如果这个时候直接使用 pm_release 0 将会导致进入深度睡眠模式,这个时候终端将无法使用。

  1. 驱动层请求进入低功耗模式
void rt_pm_module_request(uint8_t module_id, rt_uint8_t mode)
{
    rt_base_t level;
    struct rt_pm *pm;

    if (_pm_init_flag == 0)
        return;

    if (mode > (PM_SLEEP_MODE_MAX - 1))
        return;

    if (module_id > (PM_MODULE_MAX_ID - 1))
        return;

    level = rt_hw_interrupt_disable();
    pm = &_pm;
    pm->module_status[module_id].req_status = 0x01;
    if (pm->modes[mode] < 255)
        pm->modes[mode] ++;
    _pm_select_sleep_mode(pm);
    rt_hw_interrupt_enable(level);
}

检查 PM 组件是否已经初始化,检查申请的模式是否超出当前支持的最大的低功耗模式种类,在关中断的状态下标记当前模式下已经请求了当前要进入的低功耗模式。设置当前将要进入的低功耗模式次数加 1

  1. 驱动层释放低功耗模式
void rt_pm_module_release(uint8_t module_id, rt_uint8_t mode)
{
    rt_ubase_t level;
    struct rt_pm *pm;

    if (_pm_init_flag == 0)
        return;

    if (mode > (PM_SLEEP_MODE_MAX - 1))
        return;

    if (module_id > (PM_MODULE_MAX_ID - 1))
        return;

    level = rt_hw_interrupt_disable();
    pm = &_pm;
    if (pm->modes[mode] > 0)
        pm->modes[mode] --;
    if (pm->modes[mode] == 0)
        pm->module_status[module_id].req_status = 0x00;
    _pm_select_sleep_mode(pm);
    rt_hw_interrupt_enable(level);
}

检查 PM 组件是否已经初始化,检查释放的模式是否已经超出当前支持的最大的功耗模式种类,在关中断的状态下,请求进入低功耗模式的次数减少一次,设置当前模块请求状态为 0

  1. 设置低功耗模块需要进入低功耗的时间
void rt_pm_module_delay_sleep(rt_uint8_t module_id, rt_tick_t timeout)
{
    rt_base_t level;
    struct rt_pm *pm;

    if (_pm_init_flag == 0)
        return;

    if (module_id > (PM_MODULE_MAX_ID - 1))
        return;

    level = rt_hw_interrupt_disable();
    pm = &_pm;
    pm->module_status[module_id].busy_flag = RT_TRUE;
    pm->module_status[module_id].timeout = timeout;
    pm->module_status[module_id].start_time = rt_tick_get();
    rt_hw_interrupt_enable(level);
}

设置低功耗模块进入低功耗的时间,单位是 tick

  1. 获取当前模块的运行模式
rt_uint32_t rt_pm_module_get_status(void)
{
    rt_uint8_t index = 0;
    struct rt_pm *pm;
    rt_uint32_t req_status = 0x00;
    pm = &_pm;

    for (index = 0; index < 32; index ++)
    {
        if (pm->module_status[index].req_status == 0x01)
            req_status |= 1<= PM_MODULE_MAX_ID)
            break;
    }

    return req_status;
}

遍历查找当前模块为 1 的模式,然后返回

  1. 获取当前的低功耗模式
rt_uint8_t rt_pm_get_sleep_mode(void)
{
    struct rt_pm *pm;

    pm = &_pm;
    return pm->sleep_mode;
}

返回当前的低功耗模式

四,总结

主要讲解了 PM 组件在常用 API 及实现原理,可以看到更多 PM 组件仅仅是取到了标记的作用,真正的切换功耗模式是在空闲线程中进行的。这里也就切合了 RTOS 的使用习惯,RTOS 运行到了 IDEL 说明已经没有就绪任务了,说明任务的工作都已经结束,只有保证所有的任务的工作都执行完毕了,才可以降低系统的功耗,这里降低功耗才是有价值的,避免降低功耗导致执行效率的下降。

你可能感兴趣的:(STM32超低功耗,stm32)