btcamel 发表于 2014-3-20 20:45:55

外行问嵌入式OS低功耗问题

单片机有低功耗模式,比如HALT,或者STANDBY模式
如果移植了FREE RTOS,那么是不是这些模式就不能进入了呢?
或者说,低功耗模式如何与OS良好协作呢? 谢谢

electrlife 发表于 2014-3-20 21:02:33

使用RTOS了也可以进入低功耗模式,但是必须有一个定时器可以唤醒MCU。
你可以在IDLE任务中使用MCU进入STANDY模式,但是需要处理一个时钟节拍,
如你进入睡眠的时间就是RTOS中延时列表中时间最长的那个,另外如果睡眠过程
中有其它的事件唤醒MCU,此时你还需要处理RTOS时钟节拍!{:lol:}

xiaoxu191 发表于 2014-3-20 21:08:04

2楼厉害,学习了。个人觉得处理起来还是有些难度的。

sunnyqd 发表于 2014-3-20 21:09:01

2楼正解

aozima 发表于 2014-3-20 22:07:52

2楼正解。

lyzhangxiang 发表于 2014-3-21 08:28:14

看下freertos 官方有相关文献

低功耗方面 有说怎么做的

Eric2013 发表于 2014-3-21 09:09:08

FreeRTOS就支持低功耗模式,不能说是完美支持,还是非常不错的,RTOS中的低功耗常用的方法叫tickless模式。

去年号称超低功耗的EMF32, 公司是Energy Micro ,专门给RTX做的超低功耗绝对不是盖的,有机会可以研究研究,
不过源码没找到。

Eric2013 发表于 2014-3-21 09:14:41

FreeRTOS的tickless模式在文件port.C里面,内容如下,看下大概的注释就懂是什么意思了。
#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();

                /* 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 + 1 ) * 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 */

btcamel 发表于 2014-3-21 17:57:39

非常感谢楼上各位的回复, 既然肯定是能实现,那我就不担心了。 谢谢各位!
页: [1]
查看完整版本: 外行问嵌入式OS低功耗问题