嵌入式单片机开发的低功耗追求

前言:
  虽然我使用单片机开发也有几年时间了,由于我主要是做上层的功能开发,因此在单片机底层的性能上关注较少,有所欠缺。到M公司后它的产品都是用锂电池、干电池、超级电容,因此对功耗要求比较严格,而这恰巧是我的短板。虽然我作出的产品功能上没有什么问题,但在功耗性能上总觉得不够满意。而如今经过磨练也积攒了一点经验,希望将其记录分享给需要的人。

1 关于低功耗

1.1 为什么要追求低功耗

 &emps;追求低功耗是为了节省电量损耗,增加设备的使用时长。特别在一些使用电池供电的移动设备中,降低功耗将使得产品更具有竞争力。

1.2 如何计算用电设备使用时长

  电池最重要的参数之一就是电池容量,单位通常是 mAhAh。比如一个3000mAh的电池,当用电设备使用该电池以3000mA进行放电时可以持续放电1h。
  计算库仑:
$$1mAh = 0.001A \times 3600s = 3.6As = 3.6C$$ (库仑)
  计算功率:
$$P = UI = I^{2}R = \frac{U^{2}}{R}$$
  一点锂电池的容量是80mAh,用电设备的最大损耗电流是1mA,那么 $\frac{80mAh}{1mA} = 80h$,该设备理论上至少可以使用80小时。

2 如何降低功耗

  要降低功耗首先要知道在哪些地方消耗功耗,然后对症下药,针对性的降低每一块的功耗。功耗和硬件及软件都有关系,主要从六个方面进行优化:

  • 器件选型
  • 合理配置IO
  • 优化硬件电路
  • 选择合适主频
  • 充分利用低功耗模式
  • 节约使用外设资源和时钟

  降低功耗的秘诀总结起来就是一个字“抠”,尽可能的从各个方面去“节约”使用。

3 器件选型

  电路中往往会使用至少一个主控芯片及其他传感器,这些器件都具有一定的功耗损耗,在器件选型时在成本和性能需求允许的情况下应该尽量选择功耗较低的芯片或传感器,需要仔细阅读相关手册。
  器件选型是做低功耗的前提,比如选择一款M4内核的高性能单片机将无法在低功耗上获得较好的表现,器件决定了低功耗的下限。一般来说使用M0或M0+内核的低功耗单片机更适合作出低功耗产品,但在芯片资源上会较为“紧张”。对于一些外部传感器比如GSensor,其功耗还与配置的有关,性能配置得越高功耗也越高。

4 合理配置IO

  根据使用,我们可以将IO分为三类:

  • 特殊功能IO。如SWD口、接外部晶振的IO、复位IO等。
  • 根据电路设计所使用的IO。比如输出输出IO、IIC通讯IO等。
  • 未使用的剩余IO。一些电路设计未接,悬空的IO。

  另外,IO作为输出时通常还可配置驱动能力,驱动力越强功耗越大,所以在驱动能力足够情况下尽量选小的,一般驱动数字器件选择较低的驱动能力就足够了。

4.1 特殊功能IO

  特殊功能的IO配置是确定的,比如IIC口就需要配置为开漏输出且外部上拉,复位脚往往也要配置为输入并且外部上拉,而SWD口默认就配置好了,我们省功耗的操作空间较小。

4.2 根据电路设计所使用的IO

  当IO的外部电路接上拉电阻时,那么当对IO输入低电平将产生较大的电流损耗,常见的运用是按键电路。根据外部电路的设计,IO需要配置为合适的状态才能最省功耗。根据我的经验总结为以下几条:

  • 当IO作为输入时,如果输入信号有频繁的高低变化,将IO配置为浮空输入将更省功耗。
  • 当IO作为输出时,如果需要频繁输出高低电平,将IO配置为推挽输出将更省功耗。
  • 当IO间歇作为输出时,比如在采样电路中,不采样时可将IO口配置为高阻输出状态可降低电流损耗。高阻态相当于电阻很大,理论上等效为“断开”,从而降低流过电流。
  • 如果外部接了上拉电阻,要尽量减少IO处于低电平的时间。反过来如果是下拉电阻,则要尽量避免IO处于高电平的时间。

  总之,就是要避免压差,产生压差又有电阻形成通路,就会产生损耗电流。

4.3 未使用的剩余IO

  大多数单片机的IO,如果悬空时配置为上拉会比下拉更省功耗,通常可配置为上拉输入。芯片复位后IO口往往处于高阻输入状态,即浮空输入状态。目的是防止复位时IO的异常电平变化导致外部器件做出错误的动作,所以上电后需要对IO进行配置。当浮空脚悬空时电平是介于高低之间,会产生漏电流,因此通常的做法是对未使用的悬空IO口配置为上拉输入状态,也可以是高祖输出(指开漏输出高,外部无上拉),这样能达到最低电流功耗。不同的单片机可能存在差异,具体特性需要查阅手册。

5 优化硬件电路

  一些硬件工程师认为自己电路设计好了,功耗高是软件的问题,而软件工程师也
委屈,明明功能很简单但功耗就是降不下去。事实上功耗和软硬件都脱不了干系。
&emps; 当你器件选型好,拿到板子后先别急着调试功能,先把把IO口配置到最佳状态,然后将主芯片及所用的传感器设置到一个手册上有对应功耗数据的确定状态,比如关闭单片机所有的外设并进入sleep模式,然后测试整板的功耗,如果明显比手册上记录的功耗大,那就是硬件电路设计的问题,存在漏电流。
  我自己就被坑过,软件也要懂点硬件知识不然被坑的时候想破头也不明白。硬件设计的电路中存在功耗损耗大的常见就是上拉电阻的地方,比如IO外部上拉,IIC总线的上拉等。如果硬件设计的上拉电阻过小,则电路电平为低时将产生较大的电流损耗,比如上拉电阻4.7kΩ接1.8v,当IO输出低电平时将产生损耗电流为 $\frac{1.8v}{4.7kΩ} = 383uA$,这对低功耗产品太说功耗太过大,如果将上拉电阻改为100kΩ呢 $\frac{1.8v}{100kΩ} = 18uA$,这功耗差异非常明显。IIC总线上拉电阻也不宜过大,否则会导致驱动能力不足上升沿过缓,但是其他普通IO口则对上升沿要求不高,选择较大的上拉电阻能降低电流损耗。
  除了上面提到的上拉电阻,还有硬件的采样电路、滤波电路、放大电路等,这些电路往往是硬件增加功耗的大头,我们可以先从软件上关闭其他功能,单独对这些硬件电路进行“试用”,检测是否存在缺陷而导致功耗过大。

6 选择合适主频

 &emps;手册上都会给出单片机在不同主频下的电流损耗,不难看出主频越高电流损耗越大,但这里有个误区。在实际使用中并不是一味的追求将主频降到最低越好,因为手册上给出的数据是cpu跑空循环的数据,而实际使用中我们往往会在运算完成后进入低功耗模式,主频+低功耗模式对功耗的影响就像一辆汽车跑100公里用什么速度跑最省油,结论不是最高速度也不会是龟速,而是中间某一个达到优劣势平衡的速度。高的主频能尽快将有限的运算完成,使得有更多处于低功耗模式的时间。而降低主频虽然运算变慢,但是同比条件下低主频损耗更小。在实际使用中我们需要综合考虑运算量和芯片低功耗特性,通过实际调试去选择一个最优功耗的主频,不要盲目降低主频。

7 充分利用低功耗模式

  不同的单片机低工作模式不同,常见的有这五类:

  • Active mode: cpu及所有外设都可正常工作。
  • Sleep mode: 一般cpu停止,但外设都可正常工作,任意中断源可唤醒cpu。
  • Deep sleep mode: 一般cpu和大部分外设都停止工作,除了一些专用的低功耗外设。仅一些特殊的中断源可唤醒cpu。
  • Power down mode: 一般cpu和外设都不能使用,仅一些特殊的中断源可唤醒cpu。
  • Deep power down mode: cpu和外设都不能使用,mcu功耗达到最低,仅少数外部IO中断可唤醒,唤醒后相当于复位运行。唤醒时间较长,不用于实时性要求较高的情况。

  以上几种功耗模式从上到下电流损耗减小,唤醒从上到下增大。

  为了节省功耗,在满足功和能性能的前提下尽可能进入功耗更为低的模式。根据我的经验总结有如下规律:

  • 需要cpu运算时使用Active mode。
  • 不需要cpu但需要外设,且实时性要求高需要立即唤醒时使用Sleep mode。
  • 不需要cpu和一般外设,只留特殊的低功耗外设时使用Deep sleep mode。
  • 不需要cpu和外设,仅在需要时通过特殊中断源唤醒时使用Power down mode。
  • 停止mcu所有功能,追求最低功耗,仅在外部中断时唤醒且不追求高实时性时用Deep power down mode。

  功耗模式越多,对功耗优化的操作空间越大,合理的在不同功耗模式下切换将利于获得最低电流损耗。mcu休眠和唤醒时间一般手册上都会给出,一般都是us级的,即使在1ms内我们也可以在不同功耗模式下多次切换来追求更低的电流损耗。也要避免过于频繁的切换功耗模式,在功耗模式切换时(进入或退出),功耗大于维持在某个模式下,因此高频的切换功耗模式可能会导致电流损耗反而增大。
  程序运行中有时候会用到delay函数进行延迟,delay通常是cpu跑无意义的计算进行粗略延迟等待,如果延迟时间较长,我们可以将delay函数优化为在低功耗模式下等待并由定时器唤醒。
  一些对计时精度要求不高的情况下,可以不适用低功耗定时器来唤醒低功耗模式下的mcu,而通过看门狗唤醒,虽然计时不准但可以少用一个外设从而节省功耗。

8 节约使用外设资源和时钟

  外设时mcu资源,使用mcu的外设当然会增加功耗。很多外设都有自己的控制时钟,甚至一些特殊的寄存器进行操作,也有相应的时钟需要使能,而使能这些时钟也会增加功耗。我根据经验总结出以下几条建议:

  • 尽量避免使用功能过于高级的外设,优先考虑低性能外设或只是用外设的部分所需功能。
  • 关闭不使用的外设。比如从未使用的外设不应该配置使能,使用到的外设在不需要时也尽量关闭,在需要时才使能。
  • 关闭不使用的时钟,比如从未使用的时钟不应该配置使能,使用到的时钟在不需要时也尽量关闭,在需要时才使能。