RT-Thread PM2.0 应用 -- 平台适配篇

开题

  • PM2.0 已经合入最新的RT-Thread,以后RT-Thread 组件使用,以2.0版本为基线。
  • 目前PM 组件的平台适配,主要在STM32L4系列上得到适配。
  • 工程师想在各个平台上使用PM 组件进行功耗的管理。

 

PM组件介绍

2021-01-27_094147.png

  • 当我们只开启PM 组件后,发现对功耗没有任何的影响!

2021-01-27_090429.png

  • PM组件大概分为:【PM组件】、【平台适配】、【业务管理】(引脚、电源管理业务逻辑)三个部分。
  • 【备注】PM组件不只能用于STM32L4系列,或低功耗L系列MCU上,可以用于RT-Thread的任何MCU平台上,只是平台适配不同而已。

 

开启组件

2021-01-27_085532.png

RT-Thread Components
    Device Drivers
        [*] Using Power Management device drivers

平台适配方法

  • 新建平台适配文件(或根据现有的STM32L4系列拷贝一份):
drv_pm.c 【必要】
drv_lptim.c (可选)
  • drv_pm 实现的主要函数:
rt_system_pm_init /* 初始化pm组件,【必要】 */

/* 主要实现的ops操作: sleep */
    static const struct rt_pm_ops _ops =
    {
        sleep, /* 【必要】 */
        run, /* 可选 */
        pm_timer_start, /* 可选 */
        pm_timer_stop, /* 可选 */
        pm_timer_get_tick /* 可选 */
    };
  • 必要函数的实现:
/**
 * This function will put apollo3 into sleep mode.
 *
 * @param pm pointer to power manage structure
 */
static void sleep(struct rt_pm *pm, uint8_t mode)
{
    switch (mode)
    {
    case PM_SLEEP_MODE_NONE:
        break;

    case PM_SLEEP_MODE_IDLE:
        pm_bsp_enter_idle();
        break;

    case PM_SLEEP_MODE_LIGHT:
        pm_bsp_enter_light();
        break;

    case PM_SLEEP_MODE_DEEP:
        pm_bsp_enter_deepsleep();
        break;

    case PM_SLEEP_MODE_STANDBY:
        /* Enter STANDBY mode */
        break;

    case PM_SLEEP_MODE_SHUTDOWN:
        /* Enter SHUTDOWNN mode */
        pm_bsp_enter_shutdown();
        break;

    default:
        RT_ASSERT(0);
        break;
    }
}
  • 必要的BSP函数实现
void pm_bsp_enter_deepsleep(void)
{
/* 引脚处理:反初始化,gpio_pins_deinit 【必要】*/
/* 外设处理:反初始化,peripherals_deinit 【可选】*/
/* 进入睡眠 【必要】 */
/* 外设唤醒处理:反初始化,peripherals_init */
/* 唤醒后引脚处理:gpio_pins_init */
}
  • drv_lptim.c 适配【可选】
  • lptimer用于tickless的实现:MCU需要有一个可以在深睡眠情况下,可以唤醒MCU的定时器。
  • lptimer用于系统深睡眠下的定时唤醒业务,时钟补偿。
pm_timer_start, //pm_timer_start,
pm_timer_stop, //pm_timer_stop,
pm_timer_get_tick //pm_timer_get_tick

如下为apollo3p 平台的lp_tim.c实现:

#include <board.h>
#include <drv_lptim.h>

/**
 * This function get current count value of LPTIM
 *
 * @return the count vlaue
 */
rt_uint32_t apollo3_lptim_get_current_counter(void)
{
    return am_hal_stimer_counter_get();
}

/**
 * This function get the max value that LPTIM can count
 *
 * @return the max count
 */
rt_uint32_t apollo3_lptim_get_max_counter(void)
{
    return 0xffffffff;
}

/**
 * This function start LPTIM with reload value
 *
 * @param reload The value that LPTIM count down from
 *
 * @return RT_EOK
 */
rt_err_t apollo3_lptim_start(rt_uint32_t reload)
{
    am_hal_stimer_compare_delta_set(0, reload);
    return (RT_EOK);
}

/**
 * This function stop LPTIM
 */
void apollo3_lptim_stop(void)
{
    uint32_t lptim_cout = 32768 / RT_TICK_PER_SECOND;
    am_hal_stimer_compare_delta_set(0, lptim_cout);
    return;
}

/**
 * This function get the count clock of LPTIM
 *
 * @return the count clock frequency in Hz
 */
rt_uint32_t apollo3_get_lptim_freq(void)
{
    return 32768;
}

对接drv_pm.c的函数实现:

/**
 * This function caculate the PM timer counter from OS tick
 *
 * @param tick OS tick
 *
 * @return the PM counter
 */
static rt_tick_t apollo3_pm_counter_from_os_tick(rt_tick_t tick)
{
    rt_uint32_t freq = apollo3_get_lptim_freq();

    return (tick * freq / RT_TICK_PER_SECOND);
}

/**
 * This function caculate the OS tick from PM counter
 *
 * @param tick PM counter
 *
 * @return the OS tick
 */
static rt_tick_t apollo3_os_tick_from_pm_counter(rt_uint32_t counter)
{
    static rt_uint32_t os_tick_remain = 0;
    rt_uint32_t ret, freq;

    freq = apollo3_get_lptim_freq();
    ret = (counter * RT_TICK_PER_SECOND + os_tick_remain) / freq;

    os_tick_remain += (counter * RT_TICK_PER_SECOND);
    os_tick_remain %= freq;

    return ret;
}

/**
 * This function start the timer of pm
 *
 * @param pm Pointer to power manage structure
 * @param timeout How many OS Ticks that MCU can sleep
 */
static void pm_timer_start(struct rt_pm *pm, rt_uint32_t timeout)
{
    RT_ASSERT(pm != RT_NULL);
    RT_ASSERT(timeout > 0);

    if (timeout != RT_TICK_MAX)
    {
        /* Convert OS Tick to pmtimer timeout value */
        timeout = apollo3_pm_counter_from_os_tick(timeout);
        if (timeout > apollo3_lptim_get_max_counter())
        {
            timeout = apollo3_lptim_get_max_counter();
        }

        /* Enter PM_TIMER_MODE */
        g_os_old_tick = rt_tick_get();
        apollo3_lptim_start(timeout);
    }
}

/**
 * This function stop the timer of pm
 *
 * @param pm Pointer to power manage structure
 */
static void pm_timer_stop(struct rt_pm *pm)
{
    RT_ASSERT(pm != RT_NULL);

    /* Reset pmtimer status */
    apollo3_lptim_stop();
}

/**
 * This function calculate how many OS Ticks that MCU have suspended
 *
 * @param pm Pointer to power manage structure
 *
 * @return OS Ticks
 */
static rt_tick_t pm_timer_get_tick(struct rt_pm *pm)
{
    rt_uint32_t timer_counter;
    rt_tick_t os_tick;

    RT_ASSERT(pm != RT_NULL);

    timer_counter = apollo3_lptim_get_current_counter();

    os_tick = (apollo3_os_tick_from_pm_counter(timer_counter) - g_os_old_tick);
    g_os_old_tick = 0x00;

    return os_tick;
}

 

运行效果:

  • idle 线程栈过小编译问题:

【解决方法】增加idle 线程栈大小,如1024或更大。

2021-01-27_092628.png

  • 解决上电初始化后,功耗模式为:None,不能自动进入深睡眠的问题:
rt_pm_module_release(PM_POWER_ID, RT_PM_DEFAULT_SLEEP_MODE);
  • 上电后想延时一段时间再进入睡眠,保证驱动初始化:
rt_pm_module_delay_sleep(PM_POWER_ID, 5000); /* 5秒后,再进入睡眠 */
  • PM组件的控制台命令:
pm_release
pm_release_all
pm_request
pm_module_release
pm_module_release_all
pm_module_request
pm_module_delay
pm_dump
  • 功耗相关API接口
void rt_pm_request(rt_uint8_t sleep_mode);  /* 请求(不睡眠) */
void rt_pm_release(rt_uint8_t sleep_mode);  /* 释放(允许睡眠) */
void rt_pm_release_all(rt_uint8_t sleep_mode); /* 释放此模式的所有请求(清除引用计数) */
void rt_system_pm_init(const struct rt_pm_ops *ops,
                       rt_uint8_t              timer_mask,
                       void                 *user_data);  /* 初始化PM组件,注册sleep,lptim的操作函数 */
void rt_pm_module_request(uint8_t module_id, rt_uint8_t sleep_mode); /* 请求(不睡眠),带模块id */
void rt_pm_module_release(uint8_t module_id, rt_uint8_t sleep_mode); /* 释放(允许睡眠),带模块id */
void rt_pm_module_release_all(uint8_t module_id, rt_uint8_t sleep_mode); /* 释放此模式的所有请求(清除引用计数),带模块id */
void rt_pm_module_delay_sleep(rt_uint8_t module_id, rt_tick_t timeout); /* 延时睡眠 */
rt_uint32_t rt_pm_module_get_status(void); /* 获取当前的模块id 请求释放状态(用于功耗日志管理,多线程功耗情况的拆解)*/
rt_uint8_t rt_pm_get_sleep_mode(void); /* 获取当前决策的睡眠状态 */

以上为PM2.0平台适配需要注意的,多实践验证,可以用于其他的MCU平台。

 

总结

  • 功耗管理牵涉的工作与基础知识点较多,多实践,才能真正的管理好功耗。
  • PM组件使用并不复杂,不代表功耗管理也不复杂(需要详细的功耗拆解、业务优化、整体考虑)
  • 更新rt-thread 源码为最新,以便很好的配合操作系统,完善功耗的管理

猜你喜欢

转载自blog.csdn.net/tcjy1000/article/details/113280160