RT-thread之PM

本文详细介绍了STM32L4系列的电源管理(PM)架构和低功耗模式的实现,包括如何配置PM、理解关键函数`_pm_change_sleep_mode`的作用、休眠模式的时间补偿、唤醒机制以及STM32L4的功耗控制策略。在应用实践中,通过PM组件在空闲时进入Stop模式降低功耗,并讨论了唤醒后与MP3通信的潜在问题。此外,还提到了RTC唤醒、时钟恢复和功耗优化等关键点。
摘要由CSDN通过智能技术生成

项目基本完成了,就剩下降功耗了。参考官方的文档,首先理解下pm管理架构。

配置

1、先建立个最简单的工程。

2、打开组件层的电源管理选项。

3、新加了三个文件:

4、编译会出错: error: unknown type name 'LPTIM_HandleTypeDef'; did you mean 'EXTI_HandleTypeDef'?

这里其实是没有定义Lptime或RTC导致的,系统需要补偿对模式变化敏感的设备的时钟。具体看官网。

开启低功耗时钟,即可。如下图:

到这里为止,基本的官网的PM管理框架基本完成。

使用:

1、增加IDLE空闲线程的栈空间:

通过ENV配置:

具体函数的接口,参见官方教程。

https://www.rt-thread.org/document/site/programming-manual/pm/pm/

剩下的就是根据自己业务需求,编写程序了。

如:项目基本情况:

雷达,配置成了外部中断触发,有断电引脚,恢复需要一定时间

红外,与雷达一样,有外部中断。

MP3,有断电引脚,电流大概20MA,恢复需要一定的时间

功放,有断电引脚

4g通信模块,只有在特定的时间需要工作,电源也有引脚控制

温湿度,虽没有专用的管脚,但可以通过命令方式进入低功耗

光感,也是通过命令方式进低功耗

超声波,是通过PWM方式控制

激光,也是通过PWM方式控制

前期先实现关闭MP3与雷达,最后实现关闭单片机,关闭雷达整机功耗就可以降到9MA以下,所以系统还是比较省电的。需要一定的算法支持,因为恢复雷达,需要等待一段时间 。先这样了,调通后再做一下总结吧。

已经实现关闭雷达与mp3,关掉后,功耗可以降到9ma左右,比上一代产品降了算是不少了,心理也更有底气了。接下来利用PM来实现单片机的关闭。

要点还是要记录下的:

1、何时进入低功耗状态:

当任务进入到空闲线程,执行 rt_system_power_manager() 最终是调用函数   static void _pm_change_sleep_mode(struct rt_pm *pm, rt_uint8_t mode) 进入低功耗和唤醒,也就是说这个函数就有唤醒的部分代码,具体是何时唤醒?

static void _pm_change_sleep_mode(struct rt_pm *pm, rt_uint8_t mode)
{
    rt_tick_t timeout_tick, delta_tick;
    rt_base_t level;
    int ret = RT_EOK;

    if (mode == PM_SLEEP_MODE_NONE)
    {
        pm->sleep_mode = mode;
        pm->ops->sleep(pm, PM_SLEEP_MODE_NONE);
    }
    else
    {
        level = rt_pm_enter_critical(mode);//关掉全局中断

        /* Notify app will enter sleep mode */
        if (_pm_notify.notify)
            _pm_notify.notify(RT_PM_ENTER_SLEEP, mode, _pm_notify.data);//调用进入休眠通知

        /* Suspend all peripheral device */
        ret = _pm_device_suspend(mode);//挂起所有的休眠外设
        if (ret != RT_EOK)
        {
            _pm_device_resume(mode);
            if (_pm_notify.notify)
                _pm_notify.notify(RT_PM_EXIT_SLEEP, mode, _pm_notify.data);
            rt_pm_exit_critical(level, mode);

            return;
        }

        /* Tickless*/
        if (pm->timer_mask & (0x01 << mode))
        {
            timeout_tick = rt_timer_next_timeout_tick();//这里就是唤醒时间的设置
            if (timeout_tick == RT_TICK_MAX)
            {
                if (pm->ops->timer_start)//定义了开始记时的函数才会调用??????没有定义
                {
                    pm->ops->timer_start(pm, RT_TICK_MAX);
                }
            }
            else
            {
                timeout_tick = timeout_tick - rt_tick_get();
                if (timeout_tick < RT_PM_TICKLESS_THRESH)//得大于2个tickless,否则模式切换为空闲
                {
                    mode = PM_SLEEP_MODE_IDLE;
                }
                else
                {
                    pm->ops->timer_start(pm, timeout_tick);//执行它
                }
            }
        }

        /* enter lower power state */
        pm->ops->sleep(pm, mode);

        /* wake up from lower power state*/
        if (pm->timer_mask & (0x01 << mode))
        {
            delta_tick = pm->ops->timer_get_tick(pm);
            pm->ops->timer_stop(pm);
            if (delta_tick)
            {
                rt_tick_set(rt_tick_get() + delta_tick);
                rt_timer_check();
            }
        }

        /* resume all device */
        _pm_device_resume(pm->sleep_mode);

        if (_pm_notify.notify)
            _pm_notify.notify(RT_PM_EXIT_SLEEP, mode, _pm_notify.data);

        rt_pm_exit_critical(level, mode);
    }
}

这个函数对于使用PM框架的核心,理解了这个函数就一定会用PM,可以看到唤醒函数是哪个呢?其实是在初始化时定义的。

关于何时唤醒的问题:其实是通过rt_timer_next_timeout_tick();从系统其他定时器中来实现的,其他定时器定了个时间,但由于进入DEEPSLEEP后就没法跑了,所以传递到lptime里面跑。这样间接实现了定时唤醒。并且其他有通过延时挂起的线程,也能唤醒lptime.如下:这样的话,要想实现真正的定时的话,只能是通过阻塞线程来实现,消除掉对时间的定时。这样才能惟一的指向自己所希望的定时时间。

2、休眠模式通过 void stm32_sleep(struct rt_pm *pm, rt_uint8_t mode)实现。

系统在空闲时进入 Stop 模式,以达到更低的降功耗效果。根据手册可知,Stop 2 模式会关闭系统时钟,当前的 OS Tick 基于内核的 Systick 定时器。那么在系统时钟停止后,OS Tick 也会停止,对于某些依赖 OS Tick 的应用,在进入 Stop 2 模式,又被中断唤醒后,就会出现问题,因此需要在系统唤醒后,对 OS Tick 进行补偿。Stop 2 模式下,绝大多数外设都停止工作,仅低功耗定时器 1(LP_TIM1)和RTC,选择 LSI 作为时钟源后,仍然能正常运行,所以可以选择 LP_TIM1 或者RTC 作为 Stop 2 模式的时间补偿定时器。

struct rt_pm_ops
{
   void (*sleep)(struct rt_pm *pm, uint8_t mode);
   void (*run)(struct rt_pm *pm, uint8_t mode);
   void (*timer_start)(struct rt_pm *pm, rt_uint32_t timeout);
   void (*timer_stop)(struct rt_pm *pm);
   rt_tick_t (*timer_get_tick)(struct rt_pm *pm);
};

后面三个timer_start 、timer_stop、timer_get_tick三个接口实现,根据 Tick 配置低功耗定时器超时,唤醒后获取实际休眠时间并转换为OS Tick,告知 PM 组件即可。

休眠模式的时间补偿需要在初始化阶段通过设置 timer_mask 的对应模式的 bit 控制开启。在 int drv_pm_hw_init(void)中timer_mask设置。在这个函数中,最后是通过void rt_system_pm_init(const struct rt_pm_ops *ops,                       rt_uint8_t              timer_mask,                       void                   *user_data)这个函数实现最终的初始化。

3、关于stml4的功耗控制:STM32L4系列 系统功耗的主要是三个因素:稳压器(voltage regulator)、CPU 工作频率、芯片自身低功耗的处理。

  • 稳压器

L4 使用两个嵌入式线性稳压器为所有数字电路、待机电路以及备份时钟域供电,分别是主稳压器(main regulator,下文简称 MR)和低功耗稳压器(low-power regulator,下文简称 LPR)。稳压器在复位后处于使能状态,根据应用模式,选择不同的稳压器对 Vcore 域供电。其中,MR 的输出电压可以由软件配置为不同的范围(Range 1 和 Rnage 2)。

稳压器应用场合
MR(Range 1)Vcore = 1.2V,用于运行模式、睡眠模式和停止模式0,MR 未 Vcore 域提供全功率
MR(Range 2)Vcore = 1.0V,使用的场景同上
LPR用于低功耗运行模式、低功耗休眠模式、停止模式 1、停止模式2
OFFStandby 和 Shutdown 模式下,MR 和 LPR 都被关闭
  • CPU 工作频率

通过降低 CPU 的主频达到降低功耗的目的:MR 工作在 Range 1 正常模式时,SYSCLK 最高可以工作在 80M;MR 工作在 Range 2 时,SYSCLK 最高不能超过 26 M;低功耗运行模式和低功耗休眠模式,即 Vcore 域由 LPR 供电,SYSCLK 必须小于 2M。 

  • 芯片本身的低功耗处理

芯片本身定义了一系列的休眠模式,如 Sleeep、Stop、Standby 和 Shutdown,前面的四种模式功耗逐渐降低,实质是芯片内部通过关闭外设和时钟来实现。

4、理解PM管理思路

任何有高功耗的请求在,那就切换不到更低功耗。

5、PM对具体平台应用思路:

使用PM组件,进入睡眠前,若有控制管脚那么先通过线程关掉所有外设,再阻塞掉所有的线程,然后对管脚进行反初始化,最后打开想要唤醒的中断源。

6、遇到的问题,醒来后与mp3的通信不正常。下面是一些参考资料,具体的设置,再补充

1) 时钟问题:STM32被唤醒以后的时钟自动切换到内部HIS RC振荡器,大家都是知道的,RC振荡器的精度是不高的。而且,睡觉前对于时钟的设置都是恢复到复位状态,只是时钟这个地方复位,其他的没有。这也会带来一个问题,可能你睡觉前使用的是内部时钟,可是睡觉后,时钟却变了,带来的问题就是UART和定时器。或许你想不使用PLL,就是8M,这样醒来后的时钟HIS也是8M,这样虽然在时钟上没有差别了,但是时钟却不稳定了。UART波特率肯定不能太高,否则通信会有问题。 
2) 醒来时间:这个问题也是个非常大的问题,datasheet上给出的醒来时间是7us,这个可能真的不假,但是醒来,不能马上干_你的活,为什么。初始化IO,你可能问,我不初始化不行吗,回答应该是否定的。因为,如果你想使用低功耗的话,睡觉前IO口都应该设置为模拟输入,这样才能达到datasheet上的14uA,但是这样也带来一个问题,那就是初始化IO,醒来必须要初始化IO。如果你还想把时钟切换到外部时钟,耗时会更加长,接近200ms,因为STM32会等待外部时钟稳定后才能工作,然后还要在重新初始化所有IO,这个非常的耗时。可能我只需要醒来10ms,但是这些活干完就需要100ms。 
3) RTC唤醒:RTC这个也是个问题,为什么?大家需要注意的是RTC只能使用报警才能唤醒MCU,秒中断是不可以唤醒的。并且报警中断必须不停的设置,设置一次只生效一次,中断完了,还需要设置下次中断的时间。并且还有个问题,报警中断必须等待到秒中断到了之后才能设置,也就是正好秒寄存器更新了一次的时候设置,这就带来一个问题,等待秒中断。如果睡前还想再能被报警唤醒的话必须重新设置报警中断,而且设置报警中断的时候需要等到秒中断才能设置新的值。这个等待的时间是不定的。可能会几百个毫秒。说以要空空的耗费几百个毫秒等到秒中断标志来设置报警中断。可能我的MCU只需要执行10ms就需要睡觉了。还是要空空的耗费掉几百个毫秒.

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guangod

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值