芯片数据手册写着低功耗,上面那些小的出奇的电流标准,只是用来摆设的一种无法工作的假死状态,工作功耗才是实实在在的。有时为了体现低功耗,还要在应用中设计所谓的低功耗模式,当系统确认没有任务时就休眠。于是,低功耗这种“既要马儿跑,又要马儿不吃草”的逻辑,就成为降低正常工作模式下系统功耗的常规选择。
从硬件角度来说,找到所有可能消耗电流的回路,确定哪些是可以通过软件控制的方式来优化,哪些是不可避免的,并给软件开发人员提供所有IO口状态对功耗影响的关系,用简单的表格说明一下高电平或低电平会怎样,悬浮会怎样。前期配置驱动软件,验证工作时最小电流满足标准,基本可确认硬件正常。剩下的就是软件开发人员的发挥空间,而基于软件的降功耗策略,正是本文所要讨论的重点。
首先确认复位后引脚的默认状态,该状态下是否漏电,是否会开启某些时钟源,是否内部上拉或下拉,软件再结合硬件或外围做相应配置。例如AD通道禁止内部上拉,普通GPIO设为输出低或者高避免漏电,悬空引脚避免中断输入模式。
固定连接控制的,可以推挽输出控制外设,或者外部带上拉时选开漏;特殊引脚如UART一般配置模式即可,硬件自动控制对应电平;间歇性工作如ADC,作为输入转换完成后,可以再设为输出或关闭AD。
有些外设是可以插拔的,如UART正常空闲时收发都是高,如外设关机或者移除,仍保持高电平则存在漏电,这种情况下需将引脚设为输出低。重点关注工作中一种状态,工作结束或异常时要及时切换状态,避免漏电或者电平不匹配。
如果外设支持中断尽量配置开启,而不是定时轮询通信。
芯片内部往往划分不同的电源域,硬件外设也分不同单元的供电,暂时不使用的部分,可以立刻关闭电源域、时钟域。在硬件成本允许或者功耗要求严苛的情况下,外设尽量独立供电,这样在非使用状态下软件控制断电。需要注意关闭电源域后,某些端口可能需要重新配置避免漏电,如上节所提到关闭外设的供电后,外设的UART端口变为低,主控的UART端口就不能继续维持高电平了。
在正常的工作模式下,频率越高功耗越高,完成同样工作的时间越短,也可更快进入休眠。如果单片机主要做控制,没有复杂运算,降频能实现需求就往更低的频率切换。如果有大量数学计算,可以空间换时间,或者先高频运行,尽快完成算法,运行结束后动态切换到低频。
同等时钟下,供电电压低功耗也低;定时采样、屏幕刷新也可在满足需求的情况下尽量低频处理。
查阅数据手册或者SDK官方文档,确定符合需求的、可被唤醒的最低功耗休眠模式,编写一个测试用例,关闭所有可能耗电的外设,进入休眠的状态,验证极限情况下的功耗。
可能还需要硬件排除电路板上无法优化的固有功耗,比如电压转换等固定消耗电流的部分,单纯看主芯片的工作电流,是否达到数据手册上对应模式下的理论值。如果不满足就需要继续关闭一些复位后自动开启的功能,比如时钟使能等;或者硬件工程师配合拆除可疑器件加快排查。这第一步非常关键,直接决定后期整机功耗能达到的最佳效果,同时在配置过程中,非常细微的认识到哪些外设和配置影响功耗,如何影响,有多大的影响。
休眠后有的是降频工作,有的是假死(软件未运行,内存可恢复;有的不能恢复,唤醒类似重启效果),或者直接关机(RTC关机闹钟唤醒),不同的硬件方案和软件需求,休眠模式的表现不同。单片机开发,确认其所有的休眠模式,以及对应休眠模式下哪些时钟源工作或休眠,结合具体应用的需求,明确系统对唤醒源以及唤醒模式的需求,由此便确定了系统的基础休眠模式。
注意有些芯片在休眠模式下仅少数端口维持唤醒的状态,只有特殊引脚才能唤醒,这需要硬件设计前考虑。
降低功耗是软件和硬件协同工作才能解决的问题。比如AD采样时候的分压电阻,如果直接接了地,那会一直消耗电流;增加分压电阻足够大,表面上静态电流小,但因为AD内阻分流,最终结果就存在较大偏差。如果通过一个IO口来控制其接地的方式,只在需要采样的时候接地,采样完成后悬浮或者拉高,就可以将静态损耗降到最低,虽然成本加了不少,但实实在在的省电了。具体是否采用,主要是看功耗的标准,如果略微可以接受持续的静态损耗,就没必要增加硬件成本。
系统实现最低功耗,有时需要在外设性能、硬件成本和功耗之间做妥协,CPU是否可以降频,硬件外设是否支持中断唤醒等,这些都会影响最终的待机功耗,降低功耗是硬件和软件配合的结果,软件配置驱动,硬件逐个确认电流是否在期望之内,这理论值定义就看原厂资料或者经验了,以及与产品定义的待机时长妥协解决。
低功耗从硬件上能够解决一部分,但单纯依靠硬件肯定是不行的,需要软件的密切配合,才能达到最好的效果。以上是从硬件驱动层面的,一般情况下都比较关注,但实际上软件业务层的灵活性高,发掘低功耗的效果比硬件低功耗本身的效果更加显著,通俗地讲,底层硬件辛辛苦苦地优化设计节省的效果,远远不如软件设计得好的表现。
从软件的业务逻辑、产品需求方面的设计,在功耗方面更有意想不到的效果,软件功耗优化简单总结就是“能睡就睡”。
一个嵌入式产品包括很多子功能、子任务,一个应用是对若干服务的调用实现的。这里服务可以是硬件服务,比如AD电压采样、UART串口通讯,也可以是软件服务,比如TCP/IP网络通信等。简单的功能如CRC校验,纯软件实现,函数运行立即获得结果,对功耗无所谓影响;复杂的功能,尽量使用任务的方式来实现,并不是特指操作系统的task或者thread,可以理解为一个流程,即一个子功能运行的完整过程。一件事有始有终就可以根据需要循环反复,周期运行的任务,明确运行的起止时间点,区分运行与非运行状态就能更好的优化,比如减少运行持续时间或者其中大电流的时间段,在功耗方面效果比较明显。
将整个嵌入式软件系统分成了很多周期性工作的小任务,它们可能是交错的或者毫无关系并行的。从本质上说,每个小任务只需关注自身的起止时间点。系统的功耗管理就是为每个任务的功耗进行管理,整体在一个有效的协调方式下才能做到功耗最小。基于任务的功耗管理实际上分成两个部分,微观角度单任务自身的功耗管理,和宏观角度多任务的休眠协调。
从微观角度来看,一个任务能独立完成自己的功能,任务中所有的步骤都是确定的,都是“自己说了算的”,对外界来说是“黑盒子”,对低功耗的要求,不外乎以下几种情形:
(1)、任务执行的过程中不允许休眠,因此任务的开头和结尾处要设置标志,告知协调系统,“只要我不同意,就不允许系统休眠”。
(2)、任务执行的过程中,某些阶段允许休眠,某些阶段不允许休眠;任务的执行过程中,不同阶段允许不同的休眠等级。
(3)、任务执行的过程中,不在乎是否休眠。
三类任务同时存在于系统中,第一类任务是相当霸道的,只要它在执行,根本不允许休眠;第二类任务既完成了任务,又兼顾了休眠;第三类任务基本上可当做空气无视。系统任务设计时应尽可能编写后两类任务,避免或者尝试拆分第一类任务。
从宏观角度来看,任意时刻可能有多个任务同时在执行,每个任务对休眠的需求都是不同的。如果要设立一个协调机制,该怎么办呢?每个任务按最低需求,随时来休眠协调机构签到投票,表明自身当前能够容忍的最低功耗对应的休眠等级,休眠协调机构的仲裁者定时或轮询检查所有任务的投票结果,找到最小的休眠等级,类似水桶的最矮一环作为“共识”,然后进入相应的休眠等级。
如果有人投了“不休眠”的票,仲裁就只能放弃休眠。所以,每一个任务都应该是一个负责的任务,都应该根据自己的不同步骤及时的更新自己对休眠的容忍度,从而保证投票能够达成有意义的结果。
这种机制实现也很容易,比如
uint16_t sleep_enable = 0xFFFF; //0xFFFF表示可以进入休眠
uint16_t sleep_enable=0xFFFF,表示系统可以进入休眠,每个任务独立的操作相应的一位,禁止休眠时清0,允许休眠则置1。休眠协调机制即定时查询sleep_enable是否为0xFFFF,可以在main轮询或RTOS的待机任务查询,进行休眠的进出。
任务的划分合理,尽量允许休眠,通过这种协商机制可以解决“能睡就睡”的问题。
因为环境或外设组合不同,可能在某些时间段无法实现需求,或者结合当前信息大概率无法实现,或者硬件部分故障,软件监测到这种异常后,需要及时止损,减少不必要的消耗。例如GNSS卫星定位,其属性就是必须在开阔区域才能定位,如果设备开启GNSS但发现信号很差,可初步判断当前位置可能在室内,即使继续工作也不能定位,可以立刻关闭GNSS节省电量;当然产品在需求层面需要考虑不定位的其他操作。或者通信中确认外设不存在或者损坏,就没必要继续供电定时交互,进行异常报警即可。
在需求定义时,充分考虑某个任务或外设工作的起止要求,避免长时间进行无效工作。例如可以根据加速度传感器判断设备是否处于运动状态在开启监控,或者通过红外或声控判断有人接近才开启工作。这种都是通过产品定义,在需求层面组合,满足要求才唤醒工作,不满足则及时停止进入休眠,当然也可能增加硬件成本。部分设备也可以考虑使用场地增加太阳能充电板,开源节流。
在实践低功耗系统设计前,必须要有一个有效的手段来检测或观察系统当前的工作模式,可直观知道系统什么时候工作,什么时候休眠,工作时长和休眠时长,高精度的电流表必不可少,如果可以记录电流-时间曲线更佳,针对电流曲线有针对性的优化软件流程或时间参数。