FreeRTOS 低功耗模式设计 STM32平台

这篇具有很好参考价值的文章主要介绍了FreeRTOS 低功耗模式设计 STM32平台。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. STM32F105RBT6 的三种低功耗模式

1.1 sleep睡眠模式、stop停机模式、standby 待机模式

FreeRTOS 低功耗模式设计 STM32平台,stm32,mongodb,嵌入式硬件,低功耗,FreeRTOS低功耗

1.2 STM32中文参考手册有介绍STM32 低功耗模式的介绍

FreeRTOS 低功耗模式设计 STM32平台,stm32,mongodb,嵌入式硬件,低功耗,FreeRTOS低功耗

2. FreeRTOS 采用的是时间片轮转的抢占式任务调度机制,其低功耗设计思路一般是:

① 当运行空闲任务( IDLE任务)的时候就进入低功耗模式
② 在合适的时机,通过中断或者外部事件再唤醒MCU,退出低功耗模式
③ 对于STM32 系列单片机而言,systick 时间片如果设置的是1 ms,那么每隔1 ms 会将产生一个系统中断,可能会将MCU 从低功耗模式唤醒,如果MCU 频繁的进入、退出low power mode,MCU无法进入深度睡眠deep sleep,这是不合理的

3. FreeRTOS 给出了一种低功耗设计模式:Tickless Idle Mode

4. Tickless Idle Mode 的原理及实现

低功耗就是让单片机睡觉,只不过睡觉有很多种睡法,讲究人睡觉也讲究

4.1 情景分析

FreeRTOS 低功耗模式设计 STM32平台,stm32,mongodb,嵌入式硬件,低功耗,FreeRTOS低功耗
上图是任务调度示意图,横轴是时间轴,T1,T2,T3,T4是FreeRTOS 的时间片基准,FreeRTOS时间片一般是 1ms,产生一个systick 中断,有四个任务,分别是Task A、B、C、D
Task A、B、D 是周期性任务,例如三个LED灯,每隔100ms 亮一次
Task C 是突发性任务,例如,按键事件,按一下按键就产生一个外部事件
在两两任务之间有一个间隙,这个时候会运行空闲任务,Idle1,Idle2,Idle3,Idle4

4.1.1 Idle1 期间有一个systick 中断T1

运行完Task A后立马运行空闲任务,过了一会来了T1 中断,把MCU 又给唤醒了,然后又立马进入idle状态,过了一会后运行Task B,这个时候的T1 就是不合理的,就好像你在睡觉,然后我一脚把你踢醒,然后我跑了,你又接着睡,睡的很不爽

4.1.2 Idle2 期间可以一直睡觉,我不打你,你就可以睡的很爽了
4.1.3 Idle3 也可以一直睡觉,我也不打你,但是睡的时间太短了,就没必要睡了

干完task C,立马睡觉,然后又立马干task D,还没睡一会就又要起来干活(运行Task D),就像有点公司午睡只有半个小时一样,那还睡个毛,所以是否睡觉,什么时候睡觉,睡多久,睡多深,眯一会还是睡死,需要设计一套策略

4.1.4 和 Idle1 一样情况

5. Tickless Idle Mode 的软件设计原理

设计思想在于尽可能的让MCU 在运行空闲任务的时候进入低功耗模式,没事做就睡觉,节省粮食,少呼吸点空气吧你
① 合理的进入低功耗模式,避免频繁的在低功耗模式和运行模式之间进行切换,睡一下起来干活,睡一下起来干活很烦的,可以通过调整系统定时器中断触发时间长短来调整(systick中断),调一下systick 的值,然后测试一下功耗,调一下,测一下功耗。
Tickless:减少systick,原来会产生systick 的位置,现在不产生systick 中断,往后挪,增大时间片长度(或减小)
② 当MCU 被唤醒时,通过某种方式为系统时钟提供补偿,唤醒的两种方式:系统时钟中断,外部事件中断,可以通过运行在低功耗模式下的某种定时器来计算出MCU处于低功耗模式下的时间,在MCU唤醒后对系统时间进行软件补偿
③ 低功耗时间补偿策略,根据mcu低功耗特性和具体应用场景而定,可参考STM32 中文参考手册的低功耗模式章节

6. FreeRTOS 低功耗模式Tickless Mode 使能宏开关

置1 打开,置0 关闭

#define configUSE_TICKLESS_IDLE    1

FreeRTOS 低功耗模式设计 STM32平台,stm32,mongodb,嵌入式硬件,低功耗,FreeRTOS低功耗

7. FreeRTOS 创建空闲任务, 系统自动调度的,不需要人为调度

FreeRTOS 在启动任务调度的时候会创建一个空闲任务,没事干的时候就一直运行这个任务,摸鱼,老板来了就干活,老板一走就摸鱼任务
FreeRTOS 低功耗模式设计 STM32平台,stm32,mongodb,嵌入式硬件,低功耗,FreeRTOS低功耗

7.2 空闲任务宏定义 portTASK_FUNCTION

FreeRTOS 低功耗模式设计 STM32平台,stm32,mongodb,嵌入式硬件,低功耗,FreeRTOS低功耗

/*
 * The Idle task.
 *
 * The portTASK_FUNCTION() macro is used to allow port/compiler specific language extensions. The equivalent prototype for this
 function is:
 *
 * void prvIdleTask(void *pvParameters);
 *
 */
static portTASK_FUNCTION(prvIdleTask, pvParameters)
{
    (void)pvParameters;

    /*This is the FreeRTOS idle task, which is created automatically when the scheduler is started*/
    for(;;)
    {
        /* See if any tasks have deleted themselves - if so then the idle task is responsible for freeing the deleted
         * task's TCB and stack
         */
        prvCheckTasksWaitingTermination();

#if (0 == configUSE_PREEMPTION)
{
        /* If we are not using preemption we keep forcing a task switch to see if any other task has become available. If we are using
         * preemption we don't need to do this as any task becoming available will automatically get the processor anyway
         */
        taskYIELD();
}
#endif /* configUSE_PREEMPTION */

#if ((1 == configUSE_PREEMPTION) && (1 == configIDLE_SHOULD_YIELD))
{
        /* When using preemption tasks of equal priority will be timesliced. If a task that is sharing the idle priority is ready
         * to run then the idle task should yield before the end of the timeslice.

         * A critical region is not required here as we are just reading from the list, and an occasional incorrect value will not
         * matter. If the ready list at the idle priority contains more than one task then a task other than the idle task is ready
         * to execute.
         */
        if (listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[tskIDLE_PRIORITY])) > (UBaseType_t)1)
        {
            taskYIELD();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
}
#endif /* ((configUSE_PREEMPTION == 1) && (configIDLE_SHOULD_YIELD == 1)) */

#if (1 == configUSE_IDLE_HOOK)
{
        extern void vApplicationIdleHook(void);

        /* Call the user defined function from within the idle task. This allows the application designer to add background
         * functionality without the overhead of a separate task. NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,
        * CALL A FUNCTION THAT MIGHT BLOCK. */
        vApplicationIdleHook();
}
#endif /* configUSE_IDLE_HOOK */

/* This conditional compilation should use inequality to 0, not equality to 1.  This is to ensure portSUPPRESS_TICKS_AND_SLEEP()
 * is called when user defined low power mode	implementations require configUSE_TICKLESS_IDLE to be set to a value other than 1.
 */
#if (configUSE_TICKLESS_IDLE != 0)
{
        TickType_t xExpectedIdleTime;

        /* It is not desirable to suspend then resume the scheduler on each iteration of the idle task.  Therefore, a preliminary
         * test of the expected idle time is performed without the scheduler suspended.  The result here is not necessarily valid.
         */
        xExpectedIdleTime = prvGetExpectedIdleTime();

        if (xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP)
        {
            vTaskSuspendAll();
            {
                // Now the scheduler is suspended, the expected idle time can be sampled again, and this time its value can be used
                configASSERT(xNextTaskUnblockTime >= xTickCount);
                xExpectedIdleTime = prvGetExpectedIdleTime();

                if (xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP)
                {
                    traceLOW_POWER_IDLE_BEGIN();
                    portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime);
                    traceLOW_POWER_IDLE_END();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            (void) xTaskResumeAll();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
}
#endif /* configUSE_TICKLESS_IDLE */
    }
}

8. 低功耗模式处理 vPortSuppressTicksAndSleep,需要根据MCU的低功耗模式编写代码vPortSuppressTicksAndSleep

FreeRTOS 低功耗模式设计 STM32平台,stm32,mongodb,嵌入式硬件,低功耗,FreeRTOS低功耗

#if configUSE_TICKLESS_IDLE == 1
__weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
{
    uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL;
    TickType_t xModifiableIdleTime;

    /* Make sure the SysTick reload value does not overflow the counter. */
    if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
    {
        xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
    }

    /* Stop the SysTick momentarily.  The time the SysTick is stopped for is accounted for as best it can be, but using the tickless mode will
    inevitably result in some tiny drift of the time maintained by the kernel with respect to calendar time. */
    portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT;

    /* Calculate the reload value required to wait xExpectedIdleTime tick periods.  -1 is used because this code will execute part way
    through one of the tick periods. */
    ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
    if( ulReloadValue > ulStoppedTimerCompensation )
    {
        ulReloadValue -= ulStoppedTimerCompensation;
    }

    /* Enter a critical section but don't use the taskENTER_CRITICAL() method as that will mask interrupts that should exit sleep mode. */
    __disable_irq();
    __dsb( portSY_FULL_READ_WRITE );
    __isb( portSY_FULL_READ_WRITE );

    /* If a context switch is pending or a task is waiting for the scheduler to be unsuspended then abandon the low power entry. */
    if( eTaskConfirmSleepModeStatus() == eAbortSleep )
    {
       /* Restart from whatever is left in the count register to complete this tick period. */
        portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG;

       /* Restart SysTick. */
        portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;

        /* Reset the reload register to the value required for normal tick periods. */
        portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;

        /* Re-enable interrupts - see comments above __disable_irq() call above. */
        __enable_irq();
    }
    else
    {
        /* Set the new reload value. */
        portNVIC_SYSTICK_LOAD_REG = ulReloadValue;

        /* Clear the SysTick count flag and set the count value back to zero. */
        portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;

        /* Restart SysTick. */
        portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;

        /* Sleep until something happens.  configPRE_SLEEP_PROCESSING() can set its parameter to 0 to indicate that its implementation contains
        its own wait for interrupt or wait for event instruction, and so wfi should not be executed again.  However, the original expected idle
        time variable must remain unmodified, so a copy is taken. */
        xModifiableIdleTime = xExpectedIdleTime;
        configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
        if( xModifiableIdleTime > 0 )
        {
            __dsb( portSY_FULL_READ_WRITE );
            __wfi();
            __isb( portSY_FULL_READ_WRITE );
        }
        configPOST_SLEEP_PROCESSING( xExpectedIdleTime );

        /* Stop SysTick.  Again, the time the SysTick is stopped for is accounted for as best it can be, but using the tickless mode will
        inevitably result in some tiny drift of the time maintained by the kernel with respect to calendar time. */
        ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG;
        portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT );

        /* Re-enable interrupts - see comments above __disable_irq() call above. */
        __enable_irq();

        if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )
        {
            uint32_t ulCalculatedLoadValue;

            /* The tick interrupt has already executed, and the SysTick count reloaded with ulReloadValue.  Reset the
            portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick period. */
            ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );

            /* Don't allow a tiny value, or values that have somehow underflowed because the post sleep hook did something that took too long. */
            if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )
            {
                ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );
            }

            portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue;

            /* The tick interrupt handler will already have pended the tick processing in the kernel.  As the pending tick will be
            processed as soon as this function exits, the tick value maintained by the tick is stepped forward by one less than the
            time spent waiting. */
            ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
        }
        else
        {
            /* Something other than the tick interrupt ended the sleep. Work out how long the sleep lasted rounded to complete tick
            periods (not the ulReload value which accounted for part ticks). */
            ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;

            /* How many complete tick periods passed while the processor was waiting? */
            ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;

            /* The reload value is set to whatever fraction of a single tick period remains. */
            portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;
        }

        /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again, then set portNVIC_SYSTICK_LOAD_REG back to its standard
        value.  The critical section is used to ensure the tick interrupt can only execute once in the case that the reload register is near zero. */
        portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
        portENTER_CRITICAL();
        {
            portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
            vTaskStepTick( ulCompleteTickPeriods );
            portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
        }
        portEXIT_CRITICAL();
    }
}

#endif /* #if configUSE_TICKLESS_IDLE */

9. STM32Lxx 系列是专门为了RTOS 低功耗设计的

STM32L系列微控制器是意法半导体(STMicroelectronics)推出的一款超低功耗微控制器产品系列,旨在满足电池供电和节能应用的需求。该系列具有优异的功耗特性,并提供了多种低功耗模式和技术,以减少功耗并延长电池寿命。

STM32L系列的主要特点包括:

① 低功耗执行核心:采用ARM Cortex-M0+/M3/M4内核,具有高性能和低功耗的特点。
② 丰富的低功耗模式:包括低功耗运行模式、低功耗睡眠模式、低功耗停止模式等,可以根据需要选择不同的模式以达到最佳功耗效果。
③ 电源管理单元(PMU):提供了灵活的电源管理功能,包括自动电源切换、可选电源监测等。
④专用硬件加速器:部分型号提供了专用的硬件加速器,如AES加密、CRC校验等,可以提高性能并降低功耗。
⑤ 丰富的外设接口:包括UART、SPI、I2C、GPIO等常用外设接口,可满足不同应用的需求。
⑥ 多样化的封装选项:提供了不同的封装选项,以适应不同的应用场景和空间限制。

总之,STM32L系列是专门为低功耗要求而设计的微控制器产品系列,适用于电池供电和节能应用,比如便携设备、智能家居、传感器节点等。

10. 降低功耗的措施

为了节省功耗,可以将未使用的 GPIO 口设置为低功耗状态。以下是一些常见的方法:
① 输入模式:将未使用的 GPIO 口设置为输入模式,不使用推挽输出或开漏输出。这样可以减少功耗,因为输入模式下的电流消耗较低。
② 禁用上拉和下拉:如果未使用的 GPIO 口被设置为输入模式,确保禁用了上拉电阻和下拉电阻。禁用这些电阻可以降低静态电流消耗。
③ 关闭输入缓冲器:对于不需要读取外部信号的 GPIO 口,可以关闭输入缓冲器以减少功耗。这可以通过配置寄存器来实现,具体方法可能因芯片型号而有所不同。
④ 关闭输出驱动器:如果未使用的 GPIO 口被设置为输出模式,可以将输出驱动器关闭或调整为较低的驱动能力。这可以降低功耗并减少与其他电路的干扰。
⑤ 动态切换电源:如果可能,将未使用的 GPIO 口连接到外部电源开关或电源选择电路上。通过动态切换电源,可以完全断开或切换到更低功耗的电源,从而实现更高的功耗节省效果。
⑥ 将未使用的模块时钟关闭
⑦ 降低主频,需要综合考虑,主频降低,性能会下将,数据处理会变慢
⑧ 降低性能,这个需要综合考虑,需要在性能要求和功耗要求之间取得平衡
⑨ 选择不同的硬件,这个需要硬件攻城狮参与选型
每个芯片的 GPIO 管理方式和功耗优化方法可能会有所不同,查阅芯片制造商提供的技术参考手册和数据手册,以获得更详细和针对性的操作指南。文章来源地址https://www.toymoban.com/news/detail-551259.html

到了这里,关于FreeRTOS 低功耗模式设计 STM32平台的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32--低功耗模式详解

    正常模式与睡眠模式耗电是mA级,停机模式与待机模式是uA级。 供电区域有三处,分别是 模拟部分供电(VDDA) ,数字部分供电,包括 VDD供电区域和1.8V供电区域 , 后备供电(VBAT) 。 PDDS 位用来区分停机还是待机模式。 PDDS = 0,进入停机模式,PDDS = 1,进入待机模式 ; LPD

    2024年03月26日
    浏览(44)
  • STM32 低功耗-待机模式

    在 STM32 的正常工作中,具有四种工作模式: 运行、睡眠、停止和待机模式 。 在系统或电源复位以后,微控制器处于运行状态,当CPU不需继续运行时,可以利用多种低功耗模式来节省功耗。这些低功耗模式电源消耗不同、唤醒时间不同和唤醒源不同。 例如等待某个外部事件

    2024年02月14日
    浏览(34)
  • STM32速成笔记—低功耗模式

    🎀 文章作者:二土电子 🌸 关注文末公众号获取其他资料和工程文件! 🐸 期待大家一起学习交流! STM32提供了一些低功耗模式。默认情况下,系统复位或上电复位后,微控制器进入运行模式。在运行模式下,HCLK 为CPU提供时钟,并执行程序代码。当 CPU 不需要继续运行(例如

    2024年02月12日
    浏览(52)
  • 【STM32CubeMX】低功耗模式

      本文讲解STM32F10X的低功耗模式,部分资料参考自STM32手册。STM32F10X提供了三种低功耗模式:睡眠模式(Sleep mode)、停机模式(Stop mode)和待机模式(Standby mode)。这些低功耗模式可以有效减少系统功耗,延长电池寿命,对于需要长时间运行的电池供电设备尤为重要。 ST

    2024年02月12日
    浏览(88)
  • 理解STM32的低功耗模式

    TM32的低功耗模式是特别设计来减少微控制器在不活跃状态下的能耗。这些模式允许STM32在保持核心功能的同时尽可能减少电力消耗,适合用在电池供电或需长期运行的场景。理解各种低功耗模式如何节能,主要包括以下几个方面: 关闭时钟信号 :在微控制器非活跃阶段关闭

    2024年03月18日
    浏览(46)
  • STM32F4+FreeRTOS+LVGL实现嵌入式快速开发(缝合怪)

    极速进行项目开发,只需要懂一款芯片架构+一个操作系统+一个GUI。各种部件程序全靠抄 ,成为究极缝合怪。本文用stm32f407+FreeRTOS+lvgl演示一些demo。 原文链接:STM32F4+FreeRTOS+LVGL实现快速开发(缝合怪) lvgl官方的音乐播放器demo: 百问网的2048小游戏: STM32F407这款芯片就不多介绍

    2024年02月08日
    浏览(65)
  • 【STM32】基于HAL库建立自己的低功耗模式配置库(STM32L4系列低功耗所有配置汇总)

    【STM32】基于HAL库建立自己的低功耗模式配置库(STM32L4系列低功耗所有配置汇总) 此文章是讨论将先前所有的低功耗配置功能整合起来的一个库(适用于STM32L4系列) 目前除了普通唤醒方式外 加入了UART唤醒和RTC唤醒配置 如果后续有更多唤醒加入(如I2C等 将直接在后续的文章

    2024年02月19日
    浏览(46)
  • (第48-59讲)STM32F4单片机,FreeRTOS【事件标志、任务通知、软件定时器、Tickless低功耗】【纯文字讲解】【】

    【吐血总结】FreeRTOS难点、Systick中断-滴答定时器、PendSV中断-任务切换、SVC中断-系统底层、时间片调度-时钟节拍【已完结】 (第1-8讲)STM32F4单片机,FreeRTOS基础知识总结【视频笔记、代码讲解】【正点原子】【原创】 (第9-10讲)STM32F4单片机,FreeRTOS任务创建和删除(动态方

    2024年02月01日
    浏览(65)
  • 【STM32笔记】低功耗模式配置及避坑汇总

    【STM32】低功耗模式配置及配置汇总 自己做的低功耗库函数: 【STM32】基于HAL库建立自己的低功耗模式配置库(STM32L4系列低功耗所有配置汇总) gitee库 文章总结:(后续更新以相关文章为准) 【STM32笔记】低功耗模式、WFI命令等进入不了休眠的可能原因(系统定时器SysTick一

    2024年02月06日
    浏览(108)
  • 【运行在freertos上的microros 任何stm32平台】

    `本文主要分享一下如何快速在stm32f103或stm32系列的任意板子上运行microros的具体步骤。(所需要的文件已经生成好了)。 生成的步骤极其踩坑,是一段令人痛苦的回忆。 生成静态链接库的踩坑主要内容就是 所有资源都是外网,需要代理,如果网络不通畅所有的步骤都得重新

    2024年03月16日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包