stm32L0 +freertos tickless低功耗

嵌入式实时操作系统运行中都会有一个系统时钟节拍,类似于人的心脏来驱动整个系统的运行。运行在arm cortex-m内核的单片机上的系统时钟用的最多的就是systick(属于arm内核带的)。使用stm32cubemx可以直接生成移植好的freertos工程,默认也会使用systick为系统时钟,时钟周期为1ms。

基于MCU的低功耗核心思想也很简单,就是让mcu尽可能的处于低功耗的状态。需要处理一些事情的时候唤醒做完了再进入低功耗。那么和freertos结合起来进行低功耗的处理又该如何处理?

首先freertos相比裸机就是有系统时钟节拍的概念,就按照默认的1ms时钟周期来举例:每1ms时间到就会执行一次任务调度器,任务调度器就会去查看是否有任务需要切换,是否阻塞状态的任务已经变成了就绪态。如果不需要切换或者全部任务都处于阻塞态的话,freertos就会切换到IDLE空闲任务中运行。

既然进了空闲任务就代表该做的事情做完了。所以处理低功耗的最简单的思路就是在空闲任务里让MCU进入低功耗状态,直到下次时钟节拍到来的时候又被唤醒起来。这样就是每1ms需要被唤醒一次。如下图所示:

红色的区域为时钟节拍到唤醒后运行的状态,绿色区域为进入IDLE空闲任务再进入低功耗的状态。

该思路应该是在freertos下处理低功耗最简单的方式,但是每1ms 单片机还是需要唤醒一下,可能大部分被唤醒以后所有任务还是处于阻塞状态,就又直接进入低功耗了。所以这次唤醒就是无意义的,是浪费电量的。

至此就又引出了更高级的玩法,让那些在唤醒以后又直接进低功耗的直接干脆就不要唤醒了,就等到需要任务切换的时候再次唤醒。这样子系统时钟节拍就已经不像原来这样有节奏的1ms来一次了,感觉像是没有tick时钟,所以这种模式就叫做tickless。示意图如下面这种样子:

可以看到mcu将大部分时间处于绿色的低功耗区域,只有再真正需要有任务切换的时候再唤醒起来。核心思路明白了,其实还有很多细节疑问,我怎么知道这次唤醒距离下次需要任务切换的时间间隔?原来的systick都是配置好的1ms中断一次,现在又该怎么处理?

实际上这两个问题在freertos下都不是问题:因为freertos已经帮我们做好了。它会计算下次需要唤醒的时间间隔wake_time,然后重新配置systick过 wake_time 时间以后再次唤醒。而真正需要我们做的就是,修改配置文件,使能tickless。

在freertosConfig.h文件中添加如下宏定义:

关键性进入低功耗和配置systick的代码在port.c文件中。可以自行阅读参考。

烧录运行,再使用仪器测量下电流发现电流已经降了不少,可是仍然在ma级别。这个对于电池供电的设备想要运行一年肯定是不行的,所以还需要进一步优化。要优化就要先明白现在没有降下去的原因在哪里。打开port.c文件看针对低功耗的部分如何处理,如下为关键性的代码:

进入低功耗最关键的一句代码就是__wfi(); 查看stm32L0的参考手册描述:

WFI指令只是让单片机进入了sleep模式,在该模式下也只有ARM-COREX 内核的时钟关闭了,但是其他比如GPIO、ADC、UART这些外设时钟都是打开的,也就是还处于耗电状态。

stm32L0提供5种低功耗模式。其中综合考虑STOP模式可以做到ua级别,同时又可以保存ram内容,唤醒以后继续运行。所以接下来的主要工作就是要把原来进入sleep低功耗模式换成进入STOP模式。

低功耗模式确定了,还有个最关键也是最复杂的一个问题。当进入STOP模式以后systick时钟彻底停止了,这样tickless计算出来下次该唤醒的时间到了,该如何唤醒?

systick用不了了,那么只能寻找替代的定时器,该定时器还要满足一个要求就是在单片机进入到STOP模式以后还可以继续运行。好在stm32L0系列有一个LPTIMER,正好满足要求,在进入低功耗以后也可以把单片机唤醒。那接下来的思路就是彻底抛弃systick,使用LPTIMER来产生系统时钟节拍。

在stm32cubemx中使能LPTIMER并配置,记好要使能中断。

在clock configuration中还有个选择LPTIMER时钟源的地方,我这里配置为LSI

生成代码,修改stm32l0xx_it.c文件,注释掉原来sytick中断中的freertos处理代码:

再添加如下代码,把os系统节拍处理放在LPTIMER的中断回调中:

这个步骤完成了,当然还有个地方要处理,原来进入低功耗模式部分的代码。实际上freertos是可以自定义低功耗处理部分的,记得前面在freertosConfig.h文件中添加了的宏定义改为2 :define configUSE_TICKLESS_IDLE 2

除此之外,还有自定义个 vPortSuppressTicksAndSleep 函数来替代原来port.c中移植好的。具体这个函数该怎么写freertos有参考: https://www.freertos.org/low-power-tickless-rtos.html

如下为我自己写的处理代码:

这里面有两个地方需要注意:假如开了ADC的话进入STOP之前关掉,还有进入STOP使用PWR_LOWPOWERREGULATOR_ON参数可以进一步降低功耗。

基于以上修改之后再次测量单片机低功耗时的电流可以做到30ua左右了。

您可能还喜欢...

1 条回复

  1. 电子创客营说道:

    也可以使用RTC Periodic auto-wakeup功能来替代LPTIMER.LPTIMER只有在stm32低功耗系列中才有,所以F系列也只能使用RTC