FreeRTOS-Tickless模式

FreeRTOS-Tickless模式

  • 很多场合中对功耗要求很严格,并且很多MCU都带有低功耗模式,合理地使用这些低功耗模式可以降低系统的功耗。同时,FreeRTOS也提供了Tickless功耗模式,这样软件和硬件相结合,可以更进一步降低系统功耗,并且配置也很简单。

STM32低功耗模式

  • STM32本身支持低功耗模式,它有休眠、停止、待机三种低功耗模式,相比来说,待机模式功耗最低,休眠模式功耗最高,但是相应地,睡眠模式唤醒延迟最低。下表给出三种模式的对比。
模式开启方式唤醒方式对1.2v域时钟影响对VDD域时钟影响调压器
休眠WFI或WFEWFI时,任意中断唤醒;WFE时,事件唤醒CPU时钟关闭,其他时钟无影响无影响开启
停止PDDS和LPDS位+SLEEPDEEP位+WFI位或WFE位任意EXTI线所有1.2v域时钟都关闭HSI和HSE振荡器关闭开启或处于低功耗模式
待机PDDS为+SLEEPDEEP位+WFI位或WFEWKUP引脚上升沿、RTC相关事件或中断所有1.2v域时钟关闭HSI和HSE振荡器关闭关闭

睡眠模式

  • 进入睡眠模式有两种指令:WFI(等待中断)和WFE(等待事件)。CMSIS中提供了两个函数来操作指令WFI和WFE,我们可以直接使用这两个函数,分别是__WFI和__WFE,FreeRTOS系统使用的是WFE进入休眠模式。
  • 如果使用WFI进入休眠模式,那么休眠期间 任意一个中断都会将MCU从休眠状态只用唤醒;使用WFE进入休眠模式,那么当有事件发生的话就会退出休眠模式。STM32进入休眠模式的时候,CM3内核停止运行,但是其他外设都正常。

Tickless模式

  • 在前面时间信息统计那一章节中,我们曾经统计了各个任务运行占用的时间,结果发现,处理器大部分时间都在处理空闲任务,而空闲任务一般都是不重要的任务,所以这期间可以进入低功耗模式。FreeRTOS就是通过这样来降低功耗的,一般会在空闲任务的相关钩子函数中进行一些相关低功耗处理,比如关闭外设时钟、降低系统主频等等。但是这里有个问题就是,既然FreeRTOS使用的是WFI,如果不关闭Systick只要有中断发生就会退出低功耗模式,这样对降低功耗极其不利。如果关闭SyStick那么会导致系统时钟停止运行,系统时钟都停止运行了,FreeRTOS系统就没办法正常工作了。所以,为了解决这个问题,我们还需要另一个定时器,该定时器只需要将系统节拍停止这段时间补偿上去就可以了,如果使用的MCU自带低功耗定时器,那么可以使用这个低功耗定时器来完成这项任务。不幸的是,STM32F1没有这个定时器,那是不是意味着就不能使用Tickless模式了?照样可以使用,只是我们会继续使用滴答定时器来完成这个工作,不过此时的滴答定时器工作模式有所变化。这时候当进入低功耗模式前,将会计算下一个任务的执行时间,根据这个计算时间,滴答定时器只需要在该时间点将系统唤醒,而不需要每个节拍都唤醒,然后再将这段时间补偿到系统实拍中就可以解决了。那么如何计算下一个任务的唤醒时间呢?值得庆幸的是,FreeRTOS已经帮我们提前做好了。我们只需要完成相关配置即可。下面讲述配置过程。

Tickless模式配置

  • 首先开启低功耗模式,也就是使能宏:configUSE_TICKLESS_IDLE 为1。
  • 开启低功耗模式以后,当满足空闲任务是唯一可运行的任务并且系统处于低功耗模式的时间大于configEXPECTED_IDLE_TIME_BEFORE_SLEEP个时钟节拍系统才会进入低功耗模式,为什么要有这个条件呢?其实容易想到,如果低功耗模式时间过短,刚进入低功耗模式就推出了,那就完全没有必要进入了。这个最小节拍可以有用户自己定义。系统默认定义如下
#ifndef configEXPECTED_IDLE_TIME_BEFORE_SLEEP
	#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2
#endif
#if configEXPECTED_IDLE_TIME_BEFORE_SLEEP < 2
	#error configEXPECTED_IDLE_TIME_BEFORE_SLEEP must not be less than 2
#endif
  • 从上面可以看出,低功耗持续时间最小是2个节拍。
  • 满足上述条件后,系统会调用vPortSuppressTicksAndSleep()这个函数,该函数会使系统进入睡眠模式,并进行任务补偿,需要注意的是,在进入低功耗模式前和退出低功耗模式后,系统都会调用两个函数来做一些低功耗的处理,这两个函数分别是configPRE_SLEEP_PROCESSING()和configPOST_SLEEP_PROCESSING(),这两个函数需要用户自己定义,其完成的功能可以是使能或除能相应外设,降低或回复系统时钟频率等。这里我们定义如下:
  • FreeRTOSConfig.h文件
extern void PreSleepProcessing(u32 xExpectedIdleTime);
extern void PostSleepProcessing(u32 xExpectedIdleTime);	

#define configPRE_SLEEP_PROCESSING  		PreSleepProcessing
#define configPOST_SLEEP_PROCESSING  		PostSleepProcessing
  • 函数定义
//进入低功耗模式前的工作
void PreSleepProcessing(TickType_t xExpectedIdleTime)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,DISABLE);
}

//退出低功耗模式后的工作
void PostSleepProcessing(TickType_t xExpectedIdleTime)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
}

  • 低功耗相关的配置就讲述完了,这一章只是讲了如何使用和配置Tickless模式,一些深入的内容这里没有分析。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值