在工作过程中,遇到这样一个产品,它基于 Cortex-M7 内核的 STM32F769 芯片,同时使用了 FreeRTOS 实时操作系统。
由于该产品使用电池供电,因此有着低功耗的需求。
接下来,我将简单描述一下 STM32 与 FreeRTOS 各自的低功耗特性,以及在配合使用时如何去实现产品的低功耗。
一、STM32F769 芯片的三种低功耗模式[1]
STM32F769 支持三种低功耗模式,它们分别是:SLEEP、STOP和STANDBY,其省电能力依次增强。
SLEEP
在 SLEEP 模式下,只有 Cortex-M7 内核停止了工作,而外设仍然在运行。
在进入 SLEEP 模式后,所有中断均可唤醒 MCU,从而退出 SLEEP 模式。
- STOP
在 STOP 模式下,内核停止工作,并且所有的时钟(如 HCLK, PCLK1, PCLK2 等)也停止工作,即所有外设停止工作,这里有一点要特别注意,此时 SYSTICK 也会被停掉。当然,我们产品中的 RTC 还在继续运行,因为它的时钟源为外部的 32.768K 晶振。
在进入 STOP 模式后,只有外部中断(EXTI)才能唤醒 MCU(由于 RTC 中断挂在外部中断线上,所以 RTC 中断也能唤醒 MCU)。
- STANDBY
在 STANDBY 模式下,内核、所有的时钟、以及后备 1.2V 电源全部停止工作。
从 STANDBY 模式中唤醒后,系统相当于执行了一次复位操作,程序会从头来过。
综上所述,很明显地,在STM32 提供的这三种低功耗模式中,我们只能使用其中的 SLEEP 和 STOP 这两种,STANDBY 不适用。
关于 STM32769 更详细的低功耗内容介绍,请查看 Reference Manual 的4.3节 – Low-power modes.
二、FreeRTOS 的低功耗实现[2]
在启动任务调度器时,FreeRTOS 会创建一个 IDLE 任务,其任务优先级最低,当且仅当所有其它任务均被阻塞时,IDLE 任务才会获得 CPU 使用权。
因此,可以很容易想到在 IDLE 任务里去实现进入与退出 STM32F769 的低功耗模式,即在切入 IDLE 任务后,让 STM32 也进入低功耗模式,而在即将切换出 IDLE 任务之前,去唤醒 STM32。
另外,较新版本的 FreeRTOS 中,增加了 Tickless mode,更详细的介绍请查看参考文献[2].
三、整个产品的低功耗实现
那么在 IDLE 任务里,要如何去确定当前 STM32 应该是进入 SLEEP 还是 STOP 模式呢?
考虑到 SLEEP 和 STOP 两者之间的差异,即 SLEEP 下任何中断均可唤醒 STM32,而在 STOP 下,只能通过外部中断去唤醒,所以,我们的产品采用了如下的机制:
在可确定的将来的一段时间内,如果程序员知道这期间会发生一个非外部中断,这时,就不能让 STM32 进入 STOP 模式。因为,一旦进入了 STOP,STM32 就只能响应外部中断,而不能对非外部中断(如串口、I2C 等外设中断)作出响应。
举个例子会更便于理解。假设这样一个场景 —— 通过中断去读取 I2C 数据。在程序员配置好 I2C 读取数据中断后,系统就处于等待 I2C 中断的状态。之后如果产生了 I2C 中断,就代表数据已经读取完毕,程序员接下来就可以去处理数据了。
接上面的,在配置好 I2C 读取数据中断后,如果此时 IDLE 任务得到执行,那么,这种情况下就不能让 STM32 进入 STOP 模式,而只能进入 SLEEP 模式。一旦产生了 I2C 中断,则 STM32 就会从 SLEEP 中被唤醒。而如果之前 STM32 进入了 STOP 模式,那么这个 I2C 中断就会被略掉了。
所以,在这个产品中,我们提供了两个接口,disable_enter_stop_mode 和 enable_enter_stop_mode,分别用来告知,当前不能进入 STOP 模式和当前可以进入 STOP 模式了。
整理一下,我们可以得到如下的流程图:
如果当前可以进入 STOP 模式,在真正进入 STOP 之前,还有一件事要做——配置 RTC 唤醒定时器,让其在某一时刻来唤醒 STM32。具体能在 STOP 模式下睡多长时间,由 FreeRTOS 中的 prvGetExpectedIdleTime 接口计算得出。
RTC 唤醒定时器配置完成后,即可通过调用 HAL_PWR_EnterSTOPMode 让 STM32 进入 STOP 模式了。如果此时没有任何中断处于 PENDING 状态,则 STM32 会立即进入 STOP 模式,如果此时有中断处于 PENDING 状态,则 STM32 不会进入 STOP 模式,代码会继续往下执行。
在 STM32 处于 STOP 模式期间,如果产生了任何外部中断(EXTI 中断),则 STM32 会被立马唤醒,不管 RTC 唤醒定时器有没有超时。如果期间一直没有外部中断,那么 STM32 会一直处于 STOP 模式,直到 RTC 唤醒定时器超时,从而将 STM32 唤醒。
另外,由于在 STOP 下,为 FreeRTOS 提供心跳时钟的 SYSTICK 也停止了工作,所以,在被唤醒之后,还需要将在 STOP 下流逝的时间告诉 FreeRTOS。
总之,降低整个产品功耗的基本思想,就是让 FreeRTOS 仅可能多的时间处于 IDLE 任务,让 STM32 尽可能多的时间处于 STOP 模式,最终达到尽可能多的降低功耗的目的。
过段时间有空了,再把相关的代码整理一下贴出来,以供参考。
参考文献: