|
本帖最后由 10xjzheng 于 2015-9-29 21:42 编辑
首先我们先来看下结果,如下图所示,我建立了两个LED翻转的任务,一个是200ms间隔,一个是500ms间隔,如第一二个波形所示,
第三波形如果是高电平,则处于STOP模式,如果是低电平,则处于run模式。
将波形进行放大,可以看到芯片及时地在任务1、2执行之前进行翻转。
首先我们来讨论下嵌入式系统的低功耗,简单地思路是干完活进入空闲任务的时候进入,然后醒来查看是否有任务需要执行,没有的话就继续进入空闲任务,这样子看来看来似乎还是可以的。
下面我们来看一个案例,只有一个任务,运行频率时1Hz,下面中我们可以看到在这1s内CPU不断地醒来,睡去,那么我们不禁回想,
既然已经知道任务地执行频率时1Hz,为什么还要频繁地查询?频繁地查询其实是很耗时的,因为进入和退出都需要一定的时间。
因此Tickless应运而生,在操作系统的管理之下,计算下一次应该醒来的准确时间,以实现长而少的睡眠-唤醒,而不是跟着tick的频
率无谓地醒来检查。正如我放上来的第一张图,可以看到需要翻转的时候,CPU就醒来才进行翻转。
接着我们需要结合CPU的低功耗模式来看看那种模式比较合适。
有三种模式,这三种模式工作的单元越来越少,而功耗也越来越低,首先standby不符合我的系统,醒来相当于复位。sleep和stop的
主要区别是stop模式下HSI、HSE时钟也会被关掉(这就是为什么系统醒来之后会好像慢了一样),stop模式的功耗也更低,粗略测
试时几mA,关掉HSI、HSE对我的系统没有什么影响,最多醒来的时候再配置一下。但是唤醒源不能再是任何的时钟了,只能是
EXTI Line,包括了RTC wake up定时器,于是我得首先将systick转变成RTC wake up timer,让它来担任其系统心跳的职责,修改的
步骤如下。
freeRTOS官方已经意识到担任心跳的定时器有可能限制于低功耗、在tickless模式溢出等因素不能进行工作,因此有文章介绍了怎么
修改为别的定时器来担任这个职责。
http://www.freertos.org/low-power-ARM-cortex-rtos.html
主要分为几个步骤:
1.配置新的定时器,然后将其覆盖掉其中的定时器配置。这里面有个很重要的点,将定时器的优先级配置为最低,至于为什么,见Cortex-M3中的图。
- #if configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0
- void vPortSetupTimerInterrupt( void )
- {
- /* Calculate the constants required to configure the tick interrupt. */
- #if configUSE_TICKLESS_IDLE == 1
- {
- ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
- xMaximumPossibleSuppressedTicks = (portMAX_16_BIT_NUMBER+1) / ulTimerCountsForOneTick;
- // ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
- ulStoppedTimerCompensation=0;
- }
- #endif /* configUSE_TICKLESS_IDLE */
- /* Configure RTC to interrupt at the requested rate. */
- RTC_Config();
- // portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
- // portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
- }
- #endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */
复制代码
2.删除掉Systick中断函数调用的osSystickHandler();,给RTC的wake up 定时器中断。
接着我们就要开始配置tickless模式了。
1.首先将configUSE_TICKLESS_IDLE这个宏置1 or 2,1表示使用systick,2表示使用另外的定时器。
这个宏为1,则使用官方在port.c中的移植。
这个宏为2,可以使用自己的一些移植。
实际上我是使能为1,使用非systick中断,但是对应修改官方移植好的函数。
2.配置下面几个宏
这个宏是你定时器计数的频率,我的RTC配置为2KHz,所以这里配置为2000
#define configCPU_CLOCK_HZ ( 2000 )
这个宏是心跳的频率,我设置为1000
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
这几个设置的作用见前面的函数vPortSetupTimerInterrupt,这个函数中间还有变量ulStoppedTimerCompensation,这个变量是在
配置tickless的过程中暂时关掉定时器需要设置的偏移,至于多少要用逻辑分析仪测试一下,然后配置下就可以,上面直接配置为0。
2.修改函数vPortSuppressTicksAndSleep,这个函数就是系统调用进入stop模式之前的配置,这里涉及到一些定时器最大可以定时
的时间等等参数的配合。这个函数有一个参数,这个参数表示要延时的tick数,函数中我们可以将其转化为定时器要延时的时间个数。
这个函数在系统的管理之下,只有满足两个条件才运行。
1.首先所有的任务都是挂起或者阻塞的状态。
2.下一次系统应该醒来的时间应该大于configEXPECTED_IDLE_TIME_BEFORE_SLEEP,这个宏是用户可以定义的,默认配置为2。
不足:
由于唤醒定时器位数是有限的,可能有的时候tickless要延时很久,而定时器无法延时比较久的时间。
这个当然是通过分次睡眠唤醒来解决咯。
问题:
如果只有tick中断还好,如果是其他的中断让CPU醒来呢?这个时候该怎么处理?
portSUPPRESS_TICKS_AND_SLEEP是在空闲任务中被调用的,发生外部中断之后,首先会执行中断服务函数,中断服务函数运行过
程中如果没有任务就绪的话,那么很简单,程序会回到进入睡眠的那个地方,在空闲任务中,又重新执行vPortSuppressTicksAndSleep
进入睡眠模式。在外部中断中也有可能有一些任务会被就绪,中断出来之后会先进行任务切换到高优先级任务,等到所有的任务都执行
完毕了,就进入空闲任务中,也就是我们之前进入睡眠模式的那个地方,空闲任务又重新执行了函数vPortSuppressTicksAndSleep进入
tickless。CPU执行了一大圈才回到进入到空闲任务中的睡眠模式的那个点,貌似没有问题。有几个小细节不知道大家有没有注意到,我
们的tick计数值还没有更新,tick定时器也没有重新配置,唤醒之后需要配置外设启动那些也没有做。因为之前我们假设了从哪里休眠就
从哪里唤醒,但是这次不是这样子的!这是需要注意的点,我现在想到的解决办法就是不在中断中让任务就绪。你可能会说,那延时的
那些任务不是也会影响到吗?no,no,no!如果没有进入休眠之前的那个点,那么tick数还没有更新呢。由tick管理的这些任务包括软
件定时器的回调函数那些都不会被就绪的,所以这个是没有问题的。
问题:
下面这两个在进入休眠的前后是干什么的呢?
__dsb( portSY_FULL_READ_WRITE );
__isb( portSY_FULL_READ_WRITE );
见下面网友回复。
问题:
宏configPRE_SLEEP_PROCESSING、configPOST_SLEEP_PROCESSING给的参数xModifiableIdleTime是干什么的呢?
见下面网友回复。
tickless的一些资料:
http://mcuoneclipse.com/2013/07/06/low-power-with-freertos-tickless-idle-mode/
http://www.freertos.org/low-power-ARM-cortex-rtos.html
http://www.freertos.org/low-power-tickless-rtos.html
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
|