Cortex-M微控制器的一个主要优势在于能耗效率和低功耗特性。能耗效率一般有一定的能量可以完成的工作来衡量,可以为DMIPS/uW或CoreMark/uW等形式,不过,低功耗的测量只会注重动态电流和休眠电流,其中动态电流的单位为uA/MHz, 休眠电流则为uA(时钟停止)。
目前,许多Cortex-M微控制器都具有许多可以提高电池寿命的系统特性。例如:
低功耗系统在设计时需要考虑的需求如下:
不同的嵌入式系统需要考虑的低功耗需求也是不同的,比如,对于一些电池供电的产品,能耗效率是最为关键的因素,但是对于一些追求实时特性的工控应用来说,唤醒等待时间会更为关键。中断驱动系统的工作流程如下:
若数据处理请求为周期性的,且每次的持续时间都相同,在无须考虑数据处理等待时间的情况下,可以再尽可能低的时钟频率下运行以降低功耗。
Cortex-M处理器支持两种休眠模式:休眠和深度休眠。有些情况下,深度休眠模式可利用状态保持功率门(SRPG)等高级芯片设计技术进一步降低功耗。休眠期间发生的情况取决于芯片的设计,多数情况下,可以停止部分时钟信号以降低功耗。不过芯片也可以设计为部分关掉。有些情况下,还可以将芯片全部掉电,将处于这种掉电模式下的系统唤醒的唯一方法为系统复位。不同模式的功耗对比如下:
SCR => System Control Register(0xE000ED10),利用这个寄存器可以选择进入什么睡眠模式,也可以选择是不是要发送事件。具体如下:
处理器提供了两个用于进入休眠模式的指令:WFI和WFE。具体的用法如下:
WFE可由事件唤醒,其中包括事件输入信号脉冲(在处理器中名为RXEV)及之前产生的事件。在处理器内部,之前产生的事件会体现在只有一位的事件寄存器中,该事件寄存器可由下列情况置位:
与WFI休眠类似,在WFE休眠期间,若中断的优先级高于当前等级,该中断可以将处理器唤醒,不管其优先级是否被BASEPRI屏蔽掉,或者SEV-ON-PEND如何设置。
多数情况下,中断(包括NMI和Systick定时中断)可以将Cortex-M3或M4从休眠模式中唤醒。不过,有些睡眠模式可能会关掉NVIC或外设的时钟信号,但是具体还是根据芯片而异,可能有的中断时无法唤醒MCU的,只有特定中断才能唤醒。WFI的具体唤醒条件如下:
WFE的唤醒和WFI稍有不同,其中一个SEV-ON-PEND的bit位对其影响很大,如果这个bit位置位,无论什么中断产生并被挂起,都会唤醒处在WFE的MCU。具体唤醒条件如下:
注:当SEV-ON-PEND置位,只会在挂起位从0变到1的上升沿才会产生唤醒事件唤醒WFE,如果新产生的中断挂起位已经置位,那么就不会产生唤醒事件了。
对于一些MCU,有些低功耗模式可能会通过某种手段极大地降低功耗,如减小对SRAM的供电(SRAM分区供电)并关掉Flash存储器的供电。有些情况下,中断请求产生后,硬件电路需要时间来恢复,因此中断实际执行的时间会被延迟。Cortex-M3/M4处理器提供了一组握手信号,利用这些信号,只有在其他部分准备好以后,处理器才会被唤醒。==该特性仅对芯片设计者可见,对软件则不可见。==使用该特性时候,会发觉中断等待时间变长了。
WIC => Wakeup Interrupt Controller。系统进入深度睡眠期间,当处理器的所有时钟信号都被关闭后,NVIC无法检测到新产生的中断请求。为了解决这个问题,Cortex-M3版本r2p0之后引入了一个WIC的硬件单元,这个WIC是没有可编程寄存器的。芯片设计者可以配置WIC的中断检测逻辑,以支持异步操作。WIC一端连着电源管理单元PMU,一端连着NVIC,中断屏蔽信息在刚进入深度睡眠模式前由NVIC发送到WIC中,当产生中断时,WIC检测到该请求并通知PMU恢复时钟,然后处理器唤醒并通知PMU恢复时钟,一切准备就绪再去处理中断。
SRPG => State Remain Power Gate。此高级技术可以极大地降低芯片的漏电流。在SRPG中,寄存器(在IC术语中通常被称为触发器)中状态保持单元是独立供电的,进入深度睡眠的时候,状态保持单元不掉电。具体的逻辑电路如下:
SRPG虽然可以保存掉电前的状态,但是深度睡眠的时候仍然无法检测中断,因此还是需要WIC。对于SRPG设计,由于处理器的状态得到了保留,它可以从程序暂停的地方继续执行,这样就不会每次上电都由代码来恢复寄存器。
WIC只会用在深度睡眠模式(此时系统控制寄存器中的SLEEPDEEP置位)。普通的休眠模式不会使能WIC操作,也不应该触发SRPG掉电操作。
另外还有一点需要注意,当使用调试器链接系统时,调试器可能会禁止一些低功耗操作。例如,微控制器可能会被设计为,当连接调试器时,时钟会在深度睡眠模式继续运行,这样即使应用代码试图使用深度睡眠模式,调试器也可以检测系统状态。
WFE指令可由Cortex-M处理器中一个名为RXEV(接收事件)输入信号唤醒。处理器中还有一个名为TXEV(发送事件)的输出信号,当执行SEV(发送事件)指令时,TXEV会输出一个周期的脉冲信号,此时RXEV就检测到输出信号,就会唤醒处于WFE的处理器。事件通信接口的目的就是,在特定事件发生前,处理器处于休眠状态,此时可以节省功耗,事件通信接口具有多种用法。
对于具有DMA的单处理器设计,处理器会利用DMA控制器进行存储器复制操作以提高性能。如果处理器用轮询来检测DMA是否完成,就会浪费能量且由于部分带宽被处理器占用而降低了性能。如果DMA操作完成之后,能够发送一个事件,这样在等待DMA结果的时候就可以用WFE进行休眠,从而节省功耗。当然,也可以将DMA输出的DMA_Done信号连接到NVIC,并利用中断机制来唤醒处理器。具体的代码如下:
setup_DMA_memcpy(src, dest, length);
while((get_DMA_status() & DMA_DONE_MASK) == 0)
{
WFE(); // 进入睡眠,会被事件或中断唤醒,如果是DMA结束的信号,就退出睡眠并退出循环,如果其他唤醒源,退出WFE但是依然会循环并在此进入WFE
}
多处理器间的通信同样可以基于事件机制,如果处理器A为了确定处理器B的任务处理是否结束,需要查询共用存储空间中的用一个变量,处理器A可能要等待较长的时间,而且这样还会浪费能量。如果A和B之间用事件机制来通信,那么就可在在等待的过程中进入WFE,这样就会减少功耗。
注:这种任务同步无法保证系统中的多处理器开始任务执行时的精确同步。
事件通信接口的另一个用途就是信号量和互斥体,如果信号量机制未使用事件通信特性,处理器可能会轮询检测锁定变量是否空闲,使用了事件接口就可用WFE来等。
// 获取MUTEX/信号量锁的函数
void get_lock_with_WFE(volatile int *Lock_Variable)
{
int status;
do
{
while(_LDREXW(&Lock_Variable) != 0) // 等待锁定,如果没拿到锁就WFE
{
_WFE();
}
status = _STREXW(1, &Lock_Variable); // 获取到锁之后,利用STREXW强制把Lock_Variable设为1
}while(status != 0);
_DMB();
return;
}
void free_lock(volatile int * Lock_Variable)
{
_DMB();
Lock_Variable = 0; // 释放锁住的变量
_SEV();
return;
}
对于具有嵌入式OS的系统,信号量操作可能会有很大不同, 这是因为在OS等待一个信号量时,它可能会将当前任务暂停并转而执行其他任务。
这个特性主要用在中断驱动的应用中,使能了这个特性,就会减少压栈和出栈。==这个特性应该在初始化阶段结束时使能,否则若在初始化阶段产生了中断事件且退出时休眠特性已使能,则处理器会在初始化阶段还没有完全完成时进入休眠。
WFI一般就只会用在中断驱动的应用中,如果中断事件的时序不可控,就无法确定是否能唤醒处理器了,具体参照P211页WFI的用法。
WFE指令长用于空循环,其中包括RTOS设计中的空循环。下面是WFE的使用示例:
volatile int timer0irq_flag;
timer0irq_flag = 0;
set_timer();
NVIC_EnableIRQ(Timer0_IRQn);
while(timer0irq_flag == 0) // 表示中断没有来,即等中断
{
_WFE();
};
Toggle_LDE();
==注意:对于Cortex-M3的r0p0~r2p0版本,处理器自身的缺陷会影响运行时内部事件寄存器的设置。要解决这个问题,可以在中断处理中插入一个SEV指令,这样中断就可以正确地设置中断寄存器了。
_SEV(); // 设置内部事件寄存器
_WFE(); // 由于事件寄存器已经置位,只是清除事件寄存器
_WFE(); // 这一次才是真正的进入休眠
若需要SEVONPEND特性,也应该使用WFE指令