简介
随着物联网(IoT)的兴起,产品对功耗的需求越来越强烈。作为数据采集的传感器节点通常需要在电池供电时长期工作,而作为联网的SOC也需要有快速的响应功能和较低的功耗。
在产品开发的起始阶段,首先考虑是尽快完成产品的功能开发。在产品功能逐步完善之后,就需要加入电源管理功能。为了适应IoT的这种需求,RT-Thread提供了电源管理框架。电源管理框架的理念是尽量透明,使得产品加入低功耗功能更加轻松。
本文的示例都是在潘多拉开发板下运行。
潘多拉开发板是RTT和正点原子联合推出的硬件平台,该平台上专门为IoT领域设计,并提供了丰富的例程和文档。
MCU通常提供了多种时钟源供用户选择。
例如潘多拉开发板上板载的STM32L475就可以选择LSI/MSI/HSI等内部时钟,还可以选择HSE/LSE等外部时钟。
MCU内通常也集成了PLL(Phase-lock loops),基于不同的时钟源,向MCU的其它模块提供更高频率的时钟。
为了支持低功耗功能,MCU里也会提供不同的休眠模式。
例如STM32L475里,可以分成SLEEP模式、STOP模式、STANDBY模式。
定时应用
在定时应用里,我们创建了一个周期性的软件定时器,定时器任务里周期性输出当前的OS Tick。
如果创建软件定时器成功之后,使用rt_pm_request(PM_SLEEP_MODE_DEEP)请求深度睡眠模式。
按键唤醒应用
在按键唤醒应用里,我们使用wakeup按键来唤醒处于休眠模式的MCU。
一般情况下,在MCU处于比较深度的休眠模式,只能通过特定的方式唤醒。
#include <board.h>
#include <rtthread.h>
#include <rtdevice.h>
#ifdef RT_USING_PM
#define WAKEUP_EVENT_BUTTON (1 << 0)
#define PIN_LED_R GET_PIN(E, 7)
#define WAKEUP_PIN GET_PIN(C, 13)
#define WAKEUP_APP_THREAD_STACK_SIZE 1024
static rt_event_t wakeup_event;
static void wakeup_callback(void *args)
{
rt_event_send(wakeup_event, WAKEUP_EVENT_BUTTON);
}
static void wakeup_init(void)
{
rt_pin_mode(WAKEUP_PIN, PIN_MODE_INPUT_PULLUP);
rt_pin_attach_irq(WAKEUP_PIN, PIN_IRQ_MODE_FALLING, wakeup_callback, RT_NULL);
rt_pin_irq_enable(WAKEUP_PIN, 1);
}
static void wakeup_app_entry(void *parameter)
{
wakeup_init();
rt_pm_request(PM_SLEEP_MODE_DEEP);
while (1)
{
if (rt_event_recv(wakeup_event,
WAKEUP_EVENT_BUTTON,
RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
RT_WAITING_FOREVER, RT_NULL) == RT_EOK)
{
rt_pm_request(PM_SLEEP_MODE_NONE);
rt_pin_mode(PIN_LED_R, PIN_MODE_OUTPUT);
rt_pin_write(PIN_LED_R, 0);
rt_thread_delay(rt_tick_from_millisecond(500));
rt_pin_write(PIN_LED_R, 1);
rt_pm_release(PM_SLEEP_MODE_NONE);
}
}
}
static int wakeup_app(void)
{
rt_thread_t tid;
wakeup_event = rt_event_create("wakup", RT_IPC_FLAG_PRIO);
RT_ASSERT(wakeup_event != RT_NULL);
tid = rt_thread_create("wakeup_app", wakeup_app_entry, RT_NULL,
WAKEUP_APP_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
RT_ASSERT(tid != RT_NULL);
rt_thread_startup(tid);
return 0;
}
INIT_APP_EXPORT(wakeup_app);
#endif
线程里注册了按键中断回调函数,接着请求深度睡眠模式,当唤醒中断之后就会触发回调。回调函数里会发送时间。
线程在接收事件之后,完成LED闪烁功能后,再去释放None。
STM32L4的低功耗模式简介
STM32L476是ST公司推出的一款超低功耗的Cortex-M4内核的MCU,支持多个电源管理模式,其中最低功耗shutdown模式下,待机电流仅30nA。
ST公司把L476的电源管理分为很多种,但各个模式并非功耗逐级递减的特点,下面是各个模式之间的状态转换图:
仅管L476的低功耗模式很多,但本质上并不复杂,理解它的原理有助于我们移植驱动,同时更好的在产品中选择合适的模式。
最终决定STM32L476系统功耗的主要是三个因素:稳压器(voltage regulator)、CPU工作频率、芯片自身低功耗的处理,下面分别对三个因素进行阐述。
稳压器
L4使用两个嵌入式线性稳压器为所有数字电路、待机电路以及备份时钟域供电,分别是主稳压器(main regulator,MR)和低功耗稳压器(low power regulator,简称LPR)。
稳压器在复位后处于使能状态,根据应用模式,选择不同的稳压器对Vcore域供电。
其中,MR的输出电压可以由软件配置为不同的范围(Range1 和 Range2)。
CPU工作频率
通过降低CPU的主频达到降低功耗的目的:MR工作中Range1正常模式时,SYSCLK最高可以工作在80M;MR工作在Range 2时,SYSCLK最高不能超过26M。
低功耗运行模式和低功耗休眠模式,即Vcore域由LPR供电,SYSCLK必须小于2M。
芯片本身的低功耗处理
芯片本身定义了一系列的休眠模式,如Sleep、Stop、Standby和Shutdown,前面的四种模式功耗逐渐降低,实质是芯片内部通过关闭外设和时钟来实现。
移植具体实现
RTT低功耗管理系统从设计上分离运行模式和休眠模式,独立管理,运行模式用于变频和变电压,休眠调用芯片的休眠特性。
对于多数芯片和开发来说,可能并不需要考虑变频和变电压,仅需关注休眠模式。
STM32 L4系列的芯片有运行模式和低功耗运行模式的概念,同时MR还有Range 2模式,可用于变频场景。
PM组件的底层功能都是通过struct rt_pm_ops结构体里的函数完成:
/**
* low power mode operations
*/
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);
};
移植休眠模式
移植休眠模式仅需关注sleep接口,根据PM用户手册相关介绍,首先将RTT的休眠模式和STM32的模式做一个转换:
- NONE——Run:正常运行模式,不进行任何降功耗的措施
- IDLE——Run:正常运行模式,可选择WFI(等待中断唤醒)和WFE(等待事件唤醒),此处暂不处理
- LIGHT——Sleep:轻度睡眠模式,执行ST的Sleep模式
- PM_SLEEP_MODE_DEEP——Stop2:深度睡眠模式,执行ST的Stop2模式
- STANDBY——Standby:待机模式,执行ST的Standby模式
- SHUTDOWN——Shutdown:停止模式,执行ST的Shutdown模式
#include <board.h>
#include <rtthread.h>
#include <rtdevice.h>
static void sleep(struct rt_pm *pm,uint8_t mode)
{
}