关注+星标公众号,不错过精彩内容
来源 | 野火电子
现在智能化设备越来越多,很多场景下都需要用到低功耗,比如:智能门锁、智能音箱、智能手表等。
早些年,我们做智能门锁时,用的MCU的低功耗,和现在都不是一个级别。当时用的MCU不仅价格贵,低功耗相对现在也差远了,关键开发也很费劲。

这些年,全世界的信息技术变化是真的很大。现在的MCU毕竟性价比提高了一个档次,就连开发也相对很简单了。
说的有点远了,今天结合瑞萨RA6M5单片机,给大家分享一下MCU几种常见低功耗模式及其应用。
一、 概述
1.1 降低功耗的方式
RA6M5 具有多种降低功耗的功能,如设置时钟分频器、EBCLK(External Bus Clock)输出控制、 停止模块、在正常模式下选择电源控制模式以及转换到低功耗模式等, 下表是对于各种低功耗功能的描述。

1.2 通过切换时钟信号降低功耗
可以通过修改SCKDIVCR(System Clock Division Control Register)寄存器修改时钟频率。寄存器如下图所示, 根据需求,在对应的位段设置分频值即可。设分频前时钟频率为CLKIN,分频后时钟频率为CLKOUT, 分频值为div。则CLKOUT=CLKIN/2div。例如,将SCKDIVCR寄存器的ICK修改为1, 则ICLK = PLL/21= 100MHz(RA6M5中ICLK默认时钟源是PLL,PLL = 200MHz)。

1.3 停止模块功能
停止模块功能可以停止各外设的时钟供应,通过设置MSTPCRn(Module Stop Control Register)(n=A~E)寄存器, 可以单独停止外设的时钟供应,此时CPU会继续运行,在寄存器对应位置1则停止对应的外设的时钟供应, 置0则恢复时钟供应,具体对应关系可以在《RA6M5 Gruop User’s Manual》的第10章的寄存器描述查看。 例如,要停止SCI4,则在MSTPCRB的第27位置1。当外设对应的位为1时,禁止访问该外设。
选择PLL作为时钟源时,每次只能修改一个位,修改位之后,需要等待至少250ns才能进行后续处理。
1.4 功率控制模式
根据工作频率选择合适的功率控制模式,确保频率范围等工作条件在正常范围内。 下图为不同功率模式下可用的时钟源。

二、低功耗模式
2.1 模式切换框图

2.2 睡眠模式(Sleep Mode)
上电时,默认的低功耗模式即睡眠模式。睡眠模式是最方便的低功耗模式,它不需要任何额外的配置,只需要配置好用于唤醒的中断源。 在睡眠模式下,SRAM、处理寄存器和外设状态都会被保留,片上外设可以继续工作,进入睡眠模式以及从睡眠模式唤醒所消耗的时间都是极少的。 任何中断或者复位都会将MCU从睡眠模式下唤醒,并开始处理中断,这也包括Systick系统计时器,因此读者如果用到了RTOS, 进入睡眠模式前需要暂停Systick。
2.3 软件待机模式(Software Standby Mode)
在软件待机模式下,CPU以及大部分片上外设功能和所有内部晶振都停止工作。但是会保留CPU内部寄存器和SRAM数据的内容, 片上外设以及IO口的状态。软件待机模式可以显著降低功耗,因为大多数振荡器在这种模式下停止。 与睡眠模式一样,待机模式需要配置一个中断,并使用它来唤醒MCU。退出软件待机模式时,所有内部晶振都会被启动, 待所有晶振稳定后,MCU返回正常模式。
2.4 贪睡模式(Snooze Mode)
贪睡模式与软件待机模式相似,但是在贪睡模式下,可以运行很多核心外设和所有时钟,可以执行一些比较简单的任务,与软件待机模式相比, 贪睡模式可以实现更加灵活的低功耗配置。
2.5 深度软件待机模式(Deep Software Standby Mode)
深度软件待机模式类似于设备关机,CPU状态、SRAM存储的数据以及大部分外设寄存器的数据都会丢失,只有少量外设, 如RTC和AGTn(n=0~3)可以运行。因此MCU无法保存上下文,只能以重置的状态唤醒。
下图是用于唤醒软件待机模式,贪睡模式,深度软件待机模式的的各中断源是否支持的对比。

三、低功耗模式需要使用的函数及指令
3.1 WFI和WFE指令
进入低功耗模式都需要调用WFI(Wait for Interrupt)和WFE(Wait for Event)指令,实际上就是一行汇编指令, 在cmsis_gcc.h中被封装为函数。只使用WFI指令可以直接进入睡眠模式。
/**
*@brief Wait For Interrupt
*@details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs.
*/
/**
*@brief Wait For Event
*@details Wait For Event is a hint instruction that permits the processor to enter
* a low-power state until one of a number of events occurs.
*/
3.2 FSP库函数
低功耗模式的库函数很少,功能也很简单,在下表中列举。

四、睡眠模式实验应用
在本次实验中,开发板将会执行一个流水灯任务,执行完毕后控制RA6M5进入睡眠状态,随后用任意的中断将其唤醒,再执行一次流水灯任务, 再次控制其进入睡眠状态,如此往复。
1.1 硬件设计
将按键Key1以及串口配置为中断源。
4.2 软件设计
4.2.1 正常模式与睡眠模式的切换框图

在SBYCR(Standby Control Register)寄存器的SSBY(Software Standby Mode Select)位等于0的时候, 只需要一个WFI指令就可以进入睡眠模式,任何中断都能将MCU从睡眠模式切换到正常模式。
4.2.2 新建工程
对于 e2 studio 开发环境: 拷贝一份我们之前的 e2s 工程模板 “05_Template”, 然后将工程文件夹重命名为 “38_LPM_Sleep_Mode”, 最后再将它导入到我们的 e2 studio 工作空间中。
对于 Keil 开发环境: 拷贝一份我们之前的 Keil 工程模板 “06_Template”, 然后将工程文件夹重命名为 “38_LPM_Sleep_Mode”, 并进入该文件夹里面双击 Keil 工程文件,打开该工程。
工程新建好之后,我们需要添加LED、按键与串口这三个模块就可以进行我们的实验, LED,按键与串口的配置读者可以翻看本教程第10章,16章和第19章,这里不再赘述, 添加好的工程文件结构如下。
文件结构
38_LPM_Sleep_Mode
├─ ......
└─ src
├─ led
│ ├─ bsp_led.c
│ └─ bsp_led.h
├─ debug_uart
│ ├─ bsp_debug_uart.c
│ └─ bsp_debug_uart.h
├─ key
│ ├─ key.c
│ └─ key.h
└─ hal_entry.c
4.2.3 FSP配置
在Stack中如下图加入LPM模块。

在刚刚加入的LPM模块中如下图进行配置,配置极其简单,需要修改的只有Name和Low Power Mode。


4.2.4. LED流水灯任务
代码清单38-1:LED流水灯任务
void LED_Task(void)
{
/*流水灯任务*/
LED1_ON;
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
LED2_ON;
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
LED3_ON;
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
LED1_OFF;
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
LED2_OFF;
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
LED3_OFF;
/*睡眠前打印*/
printf("MCU进入睡眠模式\r\n");
/*执行完流水灯任务,进入睡眠模式*/
R_LPM_LowPowerModeEnter(sleep.p_ctrl);
/*被唤醒后打印*/
printf("MCU已被唤醒\r\n");
}
这里LED灯依次亮起,随后依次熄灭,全部熄灭后进入睡眠模式,进入睡眠模式前用串口打印信息。
40.4.2.5. 按键中断服务函数
/*按键1中断回调函数,用于唤醒待机模式*/
void key1_irq_callback(external_irq_callback_args_t *p_args)
{
/*防止编译器产生没有使用形参的警告*/
FSP_PARAMETER_NOT_USED(p_args);
}
在本实验中,按键仅仅用于唤醒MCU,因此不需要在回调函数中放任何代码,只需添加一行“(void)p_args”使用一次回调函数的参数, 避免编译器警告即可。
40.4.2.6. hal_entry函数
hal_entry函数非常简单,先初始化外设并打开LPM,随后在while(1)中执行LED_Task函数。
hal_entry函数:
void hal_entry(void)
{
/* TODO: add your own code here */
Debug_UART4_Init(); //初始化debug串口
IRQ_Init(); //按键中断初始化
R_BSP_PinAccessEnable(); //启用对PFS寄存器的访问,因为后面写IO口都用BSP内联函数
R_LPM_Open(sleep.p_ctrl, sleep.p_cfg); //打开LPM
while(1)
{
LED_Task(); //流水灯任务
}
/* Enter non-secure code */
R_BSP_NonSecureEnter();
}
4.3. 下载验证
下载程序后,用Type-C线连接开发板“USB TO UART”接口跟电脑。本次实验配置了开发板Key1按键的外部中断和串口UART中断, 流水灯熄灭即表示MCU已进入睡眠模式。在睡眠模式下,MCU可以被任何中断唤醒,此时按下按键1或者向串口发送任何信息, 即可产生中断并唤醒MCU。

五、软件待机模式实验应用
软件待机模式与睡眠模式相似,但是此时大部分外设是停止工作的,需要指定用于唤醒MCU的中断源,而且只有少量中断源可以用于唤醒。 本次实验与实验1基本相同,开发板将会执行一个流水灯任务,执行完毕后进入软件待机模式,随后用我们指定的中断将其唤醒, 再执行一次流水灯任务,进入软件待机模式,如此往复。
5.1. 硬件设计
5.1.1. 正常模式与软件待机模式的切换框图

SBYCR寄存器的SSBY位为1的时候,使用WFI指令即可进入软件待机模式,并通过指定的中断源进行唤醒。
5.2. 软件设计
5.2.1. 新建工程
由于我们实验2与实验1基本一致,所以我们可以直接利用刚刚写好的工程进行修改。
对于 e2 studio 开发环境: 拷贝一份我们刚刚的 e2s 工程 “38_LPM_Sleep_Mode”, 然后将工程文件夹重命名为 “38_LPM_Software_Standby_Mode”, 最后再将它导入到我们的 e2 studio 工作空间中。
对于 Keil 开发环境: 拷贝一份我们刚刚的 Keil 工程 “38_LPM_Sleep_Mode”, 然后将工程文件夹重命名为 “38_LPM_Software_Standby_Mode”, 并进入该文件夹里面双击 Keil 工程文件,打开该工程。
5.2.2. FSP配置
如下图所示。主要差别在LPM的属性窗口的配置,只需要配置低功耗的模式为 “Software Standby mode”, 为了体现软件待机模式与睡眠模式的区别,这里唤醒源只选择使用IRQ9,即Key1的外部中断,与上一节实验所使用中断相同即可,省去了重新配置的麻烦。

下面是对于Standby Options的属性的具体讲解。
属性 | 描述 |
---|---|
Wake Sources | 用于设置唤醒源,与睡眠模式不同,软件待机模式下只能使用少量中断唤醒 |
Snooze End Sources | 贪睡结束源,选择用于将贪睡模式切换至软件待机模式的事件 |
Snooze Request Source | 贪睡请求源,选择用于将软件待机模式切换至贪睡模式的事件 |
DTC state in Snooze Mode | 是否使用DTC将MCU从贪睡模式切换至软件待机模式 |
Snooze Cancel Source | 选择用于将MCU从贪睡模式下唤醒的事件 |
5.3. 代码
本实验代码与上面实验不同的地方只有“printf”打印信息与模块的名称需要修改,其他部分完全相同即可。
5.4. 下载验证
下载程序后,用Type-C线连接开发板“USB TO UART”接口跟电脑。流水灯熄灭即表示MCU已进入软件待机模式。 MCU进入软件待机模式前和唤醒后都会通过串口向调试助手发送对应信息,在本次实验中,我们配置的唤醒源为IRQ9,因此只有按下用Key1可以唤醒MCU, 我们可以尝试在待机模式下使用其它非指定中断,可以看到MCU是没有任何反应的。

注解
在待机模式下,我们是无法向开发板烧写程序的, 需要按RES复位或者通过设定的唤醒源将MCU唤醒才能继续烧写程序。注意不要上电复位或者唤醒后瞬间进入软件待机模式, 因为这时候只能使用串口烧录了。