• 【FreeRTOS】14 Tickless低功耗模式


    本节我们来讲讲FreeRTOS的低功耗模式——tickless的相关知识。

    Tickless在许多小型RTOS中都有应用,是一种通用的实现低功耗的方法;需要注意的是,Tickless只是操作系统层面的实现手段,实质上是帮助用户识别可以进入低功耗的时间段,真正进入低功耗模式还需要MCU的硬件支持。

    学习本节内容前,最好了解stm32的低功耗相关知识,可以浏览笔者以前的文章:STM32编程HAL库开发的第14篇《电源控制(三种低功耗模式:sleep、stop、standby)》

    1)tickless模式的原理

    我们知道,FreeRTOS的任务,是以系统滴答时钟节拍来运行的,执行完一段工作后,任务可以调用系统的延时函数(如vTaskDelay)来延时一定的时钟节拍周期,之后再次运行。

    假如所有的任务都暂时执行完毕,进入了阻塞态,那么此时就可以进入CPU的低功耗模式来减少能量的消耗;只要在最近的需要运行任务的那个时钟节拍唤醒CPU,就不会对各个任务的执行产生任何影响。

    举一个例子便于理解,见下图:

    在时间0~2时,任务A、B、C有可能运行,会一直有用户任务占用CPU;

    到时刻2时,任务A、B、C都进入阻塞态,空闲任务得到运行;

    到时刻4时,又有任务进入到了运行态,那么CPU会退出空闲任务,执行运行态的任务。

    在这一段过程中,时刻2~4之间的两个时钟节拍,就可以使MCU进入低功耗模式,只要在时刻4把MCU唤醒,那么用户任务的执行就不受任何影响。

    FreeRTOS中的tickless模式的功能,就是在空闲任务执行时(代表没有用户任务在执行),进入MCU的低功耗模式,然后在最近的需要唤醒MCU的那个时钟节拍时,把MCU唤醒,再更新时钟节拍的计数(使得时钟不会因为进入低功耗而漏计节拍数)。

    从tickless的原理上来看,进入低功耗模式的时间点很好确定,因为所有的用户任务都不执行时,就会进入空闲任务,所以我们只需要在空闲任务中执行进入MCU低功耗模式的操作就可以了。

    退出低功耗模式时,如果是被中断唤醒,那么不需要做特殊的处理,只需要在唤醒后更新时钟节拍数;而如果是任务延时后被唤醒,就有点麻烦了,因为需要计算所有任务的延时,选出离运行时刻最短的那个,在这个时钟节拍来唤醒MCU,并更新时钟节拍数。由于多个任务同时运行,它们的延时、阻塞都不确定,而且是动态变化的,它们之间的延时还有交叉、重叠,所以让MCU休眠多长时间是很难确定的。

    而FreeRTOS的tickless模式的强大之处在于它将这个需要唤醒的时长帮我们计算好了,我们甚至不需要知道这个时长是多少,也不需要知道它在哪起的作用,只要开启了tickless模式,它就会在空闲任务中让MCU进入低功耗模式,并且在最近的需要运行任务的那个时钟节拍将MCU唤醒;顺便把更新时钟节拍的工作也干了。

    2)FreeRTOS中低功耗模式的使用

    使用Tickless模式几个重要的宏定义和函数:

    a) 要想使用tickless模式,要先将FreeRTOSConfig.h文件中的宏configUSE_TICKLESS_IDLE定义为1。

    b) 另外一个重要的宏,是在FreeRTOS.h文件中定义的configEXPECTED_IDLE_TIME_BEFORE_SLEEP,这个宏定义了一个周期数,只有空闲任务连续执行的时间大于这个周期,才会进入低功耗模式。这个宏定义是不能小于2的,也就是说,只有系统计算出有两个或以上的周期只运行空闲任务时,才会进入低功耗模式。

    c) portmacro.h文件中的portSUPPRESS_TICKS_AND_SLEEP()宏,是进入和退出低功耗模式的关键,它会被空闲任务调用,完成进入和退出低功耗的工作,并更新系统时钟节拍计数;这是tickless模式工作的最主要的一个函数。使用STM32的话,FreeRTOS已经帮我们把这个宏实现好了。如果是HAL库生成的,一般在port.c文件中,由vPortSuppressTicksAndSleep这个函数实现,可以看到其中有这么几行:

    中间的__wfi();这句,其实就是嵌入的汇编指令WFI,即进入低功耗模式。

    如果是其他硬件平台,可能需要自己编写这个宏或函数,并且configUSE_TICKLESS_IDLE宏定义要改为2,即使用自定义的低功耗处理函数。

    d) configPRE_SLEEP_PROCESSING()和configPOST_SLEEP_PROCESSING()宏,会在portSUPPRESS_TICKS_AND_SLEEP()实现的宏里面被调用,见上图,它们是需要用户编写的。这两个宏是用在MCU进入低功耗模式前、退出低功耗模式后执行的操作。一般在进入低功耗模式前,可以设置降低时钟主频、改为内部RC时钟、关闭一些无必要的外设等等,以降低硬件功耗;而退出低功耗模式后,需要将这些设置恢复。

    默认情况下,这两个函数是空函数,用户可以自行添加需要的操作,如关闭部分外设的时钟,等等:

    设置好以上几项,就可以使用低功耗模式了。

    3)编程试验

    下面我们来编程试验一下tickless低功耗模式的使用。

    Cubemx中建立工程的时候,注意使能tickless模式,如下图,选择Built in模式:

    注意,这里使能有两个选项,Built in表示的是使能内置的低功耗模式;User defined表示的是使能用户自定义的模式。这里的这个设置是改变宏configUSE_TICKLESS_IDLE的值,Built in是将其设置为1,User defined是将其设置为2。

    我们可以在这里修改,也可以在生成keil工程后,直接在源文件FreeRTOSConfig.h中修改configUSE_TICKLESS_IDLE的宏定义为1:

    时钟基准要设置为systick:

    因为cubemx里生成的低功耗代码,是以systick作为freeRTOS的节拍时钟的,如果不这么设置,自动生成的代码会无法在低功耗模式时关闭时钟节拍。

    再确认一下,没有意外的中断会打断低功耗模式:

    生成keil代码,在keil工程中编写代码。首先是三个任务的处理函数。

    DefaultTask和Task03,都是延时2000ms后打印运行的消息:

    Task02,先占用CPU时间1000ms,再延时1000ms运行,期间打印提示信息:

    另外,在进入低功耗之前,打印即将睡眠的节拍数:

    这个程序运行结果如下:

    由后面的时间戳可以观察打印信息执行的大致时刻。

    首先Task02占用了1s的CPU时间,然后开始延时;

    在延时的1000ms期间,低功耗模式开始运行了,它连续几次进入睡眠状态,共睡眠了233*4+65 = 997个周期;

    之后被唤醒,Task03开始运行了;

    Task03运行完之后,又进入了低功耗模式,睡眠了3个节拍;

    之后Task02完成了一个循环;睡眠时间总共997 + 3 = 1000个周期。

    可以观察到,这个程序的输出,前几次都是睡眠233个节拍,这是系统为了防止超过systick计时的最大值自动做的限制。

    另外,可以观察到优先级较高的Task03任务,每次运行都相对Task02提早了几个节拍,这可能是因为进、出低功耗模式时,以及打印输出时,都会占用一点时间。

    好了,本节的内容就到这里了。

    如果觉得有用可以关注作者微信号“小白白学电子”,在公众号可以找到代码和资料下载地址:

  • 相关阅读:
    Vue太难啦!从入门到放弃day06——Vue前端路由
    LeetCode 3. 无重复字符的最长子串
    Ionic4 生命周期钩子函数和angular生命周期钩子函数介绍
    Vue2.0+Vue3.0复习
    Hugging News #0626: 音频课程更新、在线体验 baichuan-7B 模型、ChatGLM2-6B 重磅发
    慢 SQL 的致胜法宝
    Glide源码解析四(解码和转码)
    vue2导航守卫
    机器学习之查准率、查全率与F1
    小程序制作(超详解!!!)第十四节 计时器
  • 原文地址:https://blog.csdn.net/little_grapes/article/details/126276221